Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Remix debugging of EVM call #1496

Closed
wants to merge 54 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
9bdb94b
wip move middleware to seperate package
Sriep Sep 23, 2019
414db24
implement
Sriep Sep 24, 2019
2b94ec3
wip implement
Sriep Sep 24, 2019
00743d8
implement
Sriep Sep 24, 2019
239fb4b
use ethapi go-ethereum branch
Sriep Sep 25, 2019
04dd303
use config transfer gateway branch
Sriep Sep 25, 2019
79536ea
touch
Sriep Sep 25, 2019
6002221
touch
Sriep Sep 25, 2019
c7af3a8
add version reader
Sriep Sep 25, 2019
343e039
wip use correct chain id
Sriep Sep 27, 2019
af517e5
Merge remote-tracking branch 'remotes/origin/appstate' into debug-evm
Sriep Sep 27, 2019
3d71a4f
move nocetxhandler into loom.go
Sriep Sep 27, 2019
7665fdf
evm debug
Sriep Sep 30, 2019
96b74f6
move tx_handler
Sriep Sep 30, 2019
0e3986c
Merge remote-tracking branch 'remotes/origin/appstate' into debug-evm
Sriep Oct 10, 2019
63368c5
merge master changes
Sriep Oct 10, 2019
91f266f
newMultiWriterVersionReader, unit test
Sriep Oct 14, 2019
9bda227
Merge remote-tracking branch 'remotes/origin/appstate' into debug-evm
Sriep Oct 18, 2019
1c59869
merge-wip
Sriep Oct 21, 2019
fbcfa43
merge-wip
Sriep Oct 21, 2019
7489a31
merge-wip
Sriep Oct 21, 2019
cf0ced2
Add version paramter to GetSnapshot
Sriep Oct 21, 2019
048040c
Modify stores
Sriep Oct 22, 2019
f7682fe
wip
Sriep Oct 22, 2019
d9664f1
merge iAVLVersionReader into iavlStoreSnapshot
Sriep Oct 23, 2019
0053ff4
versionedCachingStore
Sriep Oct 23, 2019
23746d5
PruningIAVLStore
Sriep Oct 23, 2019
c3c0ee4
PruningIAVLStore
Sriep Oct 25, 2019
e7d01f8
PruningIAVLStore
Sriep Oct 30, 2019
cff6ba6
reiview issues store changes
Sriep Oct 30, 2019
f2323ae
use loomchain.Application
Sriep Oct 30, 2019
3f0d006
loomchain.Application
Sriep Oct 30, 2019
39d948d
Add valiadtor functions, fix range
Sriep Oct 31, 2019
5de9514
fx deploy fixes
Sriep Oct 31, 2019
de3313f
fix version number
Sriep Oct 31, 2019
9c3daa1
copy factory, truffle test
Sriep Oct 31, 2019
ba19cf4
revert
Sriep Oct 31, 2019
323fa14
unit test fixes
Sriep Oct 31, 2019
8f3a7ad
unit test fixes
Sriep Nov 1, 2019
ce35dd1
test jenkins
Sriep Nov 1, 2019
5e10bd1
test jenkins
Sriep Nov 1, 2019
882ee71
Merge remote-tracking branch 'remotes/origin/appstate' into debug-evm
Sriep Nov 4, 2019
37838df
merge in master changes
Sriep Nov 4, 2019
66aaf08
makefile
Sriep Nov 4, 2019
48e3738
touch
Sriep Nov 4, 2019
fdfcb2f
Merge remote-tracking branch 'remotes/origin/appstate' into debug-evm
Sriep Nov 4, 2019
4203aaf
touch
Sriep Nov 4, 2019
fff1634
touch
Sriep Nov 4, 2019
3b8d97e
touch
Sriep Nov 4, 2019
9d2b808
touch
Sriep Nov 4, 2019
6d95f13
lint fix
Sriep Nov 4, 2019
6cf49dd
lint fixes
Sriep Nov 4, 2019
4b609de
touch
Sriep Nov 6, 2019
a1d93af
range fix
Sriep Nov 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jenkinsfile-PR.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ disabled['windows'] = {
}
}

builders['osx'] = {
disabled['osx'] = {
node('osx-any') {
timestamps {
def thisBuild = null
Expand Down
41 changes: 24 additions & 17 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@ package loomchain
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"time"

"github.com/go-kit/kit/metrics"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
cctypes "github.com/loomnetwork/go-loom/builtin/types/chainconfig"
"github.com/pkg/errors"

//"github.com/pkg/errors"
stdprometheus "github.com/prometheus/client_golang/prometheus"
abci "github.com/tendermint/tendermint/abci/types"
ttypes "github.com/tendermint/tendermint/types"

"github.com/loomnetwork/loomchain/eth/utils"
"github.com/loomnetwork/loomchain/features"
"github.com/loomnetwork/loomchain/registry"
"github.com/loomnetwork/loomchain/replay"
"github.com/loomnetwork/loomchain/txhandler"
"github.com/loomnetwork/loomchain/txhandler/middleware"

"github.com/loomnetwork/loomchain/log"
appstate "github.com/loomnetwork/loomchain/state"
Expand Down Expand Up @@ -368,7 +371,7 @@ func (a *Application) CheckTx(txBytes []byte) abci.ResponseCheckTx {

_, err = a.TxHandler.ProcessTx(state, txBytes, true)
if err != nil {
log.Error("CheckTx", "tx", ttypes.Tx(txBytes).Hash(), "err", err)
log.Error("CheckTx", "tx", hex.EncodeToString(ttypes.Tx(txBytes).Hash()), "err", err)
return abci.ResponseCheckTx{Code: 1, Log: err.Error()}
}

Expand Down Expand Up @@ -416,7 +419,7 @@ func (a *Application) DeliverTx(txBytes []byte) abci.ResponseDeliverTx {
func (a *Application) deliverTx(storeTx store.KVStoreTx, txBytes []byte) abci.ResponseDeliverTx {
r, err := a.processTx(storeTx, txBytes, false)
if err != nil {
log.Error("DeliverTx", "tx", ttypes.Tx(txBytes).Hash(), "err", err)
log.Error("DeliverTx", "tx", hex.EncodeToString(ttypes.Tx(txBytes).Hash()), "err", err)
return abci.ResponseDeliverTx{Code: 1, Log: err.Error()}
}
return abci.ResponseDeliverTx{Code: abci.CodeTypeOK, Data: r.Data, Tags: r.Tags, Info: r.Info}
Expand Down Expand Up @@ -507,7 +510,7 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R
}

if txErr != nil {
log.Error("DeliverTx", "tx", ttypes.Tx(txBytes).Hash(), "err", txErr)
log.Error("DeliverTx", "tx", hex.EncodeToString(ttypes.Tx(txBytes).Hash()), "err", txErr)
// FIXME: Really shouldn't be using r.Data if txErr != nil, but need to refactor TxHandler.ProcessTx
// so it only returns r with the correct status code & log fields.
// Pass the EVM tx hash (if any) back to Tendermint so it stores it in block results
Expand Down Expand Up @@ -595,41 +598,45 @@ func (a *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
func (a *Application) height() int64 {
return a.Store.Version() + 1
}

func (a *Application) ReadOnlyState() appstate.State {
// TODO: the store snapshot should be created atomically, otherwise the block header might
// not match the state... need to figure out why this hasn't spectacularly failed already
return appstate.NewStoreStateSnapshot(
nil,
a.Store.GetSnapshot(),
a.Store.GetSnapshot(0),
a.lastBlockHeader,
nil, // TODO: last block hash!
a.GetValidatorSet,
)
}

func (a *Application) InMemoryApp(blockNumber uint64, blockstore store.BlockStore) (middleware.InMemoryApp, error) {
startVersion := int64(blockNumber) - 1
func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.BlockStore) (replay.ReplayApplication, int64, error) {
startVersion := int64(blockNumber)
if startVersion < 0 {
return nil, errors.Errorf("invalid block number %d", blockNumber)
return nil, 0, errors.Errorf("invalid block number %d", blockNumber)
}
for ; !a.Store.VersionExists(startVersion) || startVersion <= int64(0); startVersion-- {
var snapshot store.Snapshot
for ; snapshot == nil && startVersion > 0; snapshot = a.Store.GetSnapshot(startVersion) {
startVersion--
}
if startVersion == 0 {
return nil, errors.Errorf("no saved version for height %d", blockNumber)
}
startStore, err := a.Store.RetrieveVersion(startVersion)
if err != nil {
return nil, err
return nil, 0, errors.Errorf("no saved version for height %d", blockNumber)
}

return middleware.NewInMemoryApp(
startStore := store.NewSplitStore(snapshot, store.NewMemStore())
newApp, err := replay.NewReplayApplication(
startVersion,
blockstore,
startStore,
a.TxHandler,
a.ReceiptsVersion,
a.GetValidatorSet,
a.config,
a.TxHandlerFactory,
), nil
nil,
)
if err != nil {
return nil, 0, err
}
return newApp, startVersion, nil
}
65 changes: 62 additions & 3 deletions auth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@
package auth

import (
"bytes"

etypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/golang/protobuf/proto"
"github.com/loomnetwork/go-loom/auth"
"github.com/loomnetwork/go-loom/common/evmcompat"
"github.com/loomnetwork/go-loom/types"
"github.com/loomnetwork/go-loom/vm"
sha3 "github.com/miguelmota/go-solidity-sha3"
"github.com/pkg/errors"
)

func verifySolidity66Byte(tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
func verifySolidity66Byte(chainID string, tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
ethAddr, err := evmcompat.RecoverAddressFromTypedSig(sha3.SoliditySHA3(tx.Inner), tx.Signature, allowSigTypes)
if err != nil {
return nil, errors.Wrap(err, "verify solidity key")
Expand All @@ -17,18 +25,69 @@ func verifySolidity66Byte(tx SignedTx, allowSigTypes []evmcompat.SignatureType)
return ethAddr.Bytes(), nil
}

func verifyTron(tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
func verifyTron(chainID string, tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
tronAddr, err := evmcompat.RecoverAddressFromTypedSig(sha3.SoliditySHA3(tx.Inner), tx.Signature, allowSigTypes)
if err != nil {
return nil, err
}
return tronAddr.Bytes(), nil
}

func verifyBinance(tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
func verifyBinance(chainID string, tx SignedTx, allowSigTypes []evmcompat.SignatureType) ([]byte, error) {
addr, err := evmcompat.RecoverAddressFromTypedSig(evmcompat.GenSHA256(tx.Inner), tx.Signature, allowSigTypes)
if err != nil {
return nil, err
}
return addr.Bytes(), nil
}

func VerifyWrappedEthTx(chainID string, signedTx SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
if len(signedTx.Signature) != 0 {
return nil, errors.New("unexpected signature in SignedTx")
}

var nonceTx auth.NonceTx
if err := proto.Unmarshal(signedTx.Inner, &nonceTx); err != nil {
return nil, err
}

var loomTx types.Transaction
if err := proto.Unmarshal(nonceTx.Inner, &loomTx); err != nil {
return nil, err
}

var msgTx vm.MessageTx
if err := proto.Unmarshal(loomTx.Data, &msgTx); err != nil {
return nil, err
}

var ethTx etypes.Transaction
if err := rlp.DecodeBytes(msgTx.Data, &ethTx); err != nil {
return nil, errors.Wrap(err, "failed to decode EthereumTx")
}

if ethTx.To() != nil && !bytes.Equal(ethTx.To().Bytes(), msgTx.To.Local) {
return nil, errors.Errorf(
"EthereumTx.To (%s) doesn't match MessageTx.To (%s)",
ethTx.To().String(), msgTx.To.String(),
)
}
if ethTx.Nonce() != nonceTx.Sequence {
return nil, errors.Errorf(
"EthereumTx.Nonce (%d) doesn't match NonceTx.Sequence (%d)",
ethTx.Nonce(), nonceTx.Sequence,
)
}

ethChainID, err := evmcompat.ToEthereumChainID(chainID)
if err != nil {
return nil, err
}
ethSigner := etypes.NewEIP155Signer(ethChainID)
from, err := etypes.Sender(ethSigner, &ethTx)
if err != nil {
return nil, errors.Wrap(err, "failed to recover signer from EthereumTx")
}

return from.Bytes(), err
}
49 changes: 37 additions & 12 deletions auth/multi_chain_sigtx_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"fmt"

"github.com/gogo/protobuf/proto"
"github.com/loomnetwork/go-loom"
loom "github.com/loomnetwork/go-loom"
"github.com/loomnetwork/go-loom/common/evmcompat"
"github.com/loomnetwork/go-loom/plugin/contractpb"
"github.com/loomnetwork/go-loom/types"
Expand All @@ -22,14 +22,20 @@ import (
"github.com/loomnetwork/loomchain/txhandler"
)

var originRecoveryFuncs = map[keys.SignedTxType]originRecoveryFunc{
keys.LoomSignedTxType: verifyEd25519,
keys.EthereumSignedTxType: verifySolidity66Byte,
keys.TronSignedTxType: verifyTron,
keys.BinanceSignedTxType: verifyBinance,
}
// AccountType is used to specify which address should be used on-chain to identify a tx sender.
type AccountType int

const (
// NativeAccountType indicates that the tx sender address should be passed through to contracts
// as is, with the original chain ID intact.
NativeAccountType AccountType = iota
// MappedAccountType indicates that the tx sender address should be mapped to a DAppChain
// address before being passed through to contracts.
MappedAccountType
)

type originRecoveryFunc func(tx SignedTx, allowedSigTypes []evmcompat.SignatureType) ([]byte, error)
// Recovers the signer address from a signed tx.
type originRecoveryFunc func(chainID string, tx SignedTx, allowedSigTypes []evmcompat.SignatureType) ([]byte, error)

// NewMultiChainSignatureTxMiddleware returns tx signing middleware that supports a set of chain
// specific signing algos.
Expand Down Expand Up @@ -76,12 +82,14 @@ func NewMultiChainSignatureTxMiddleware(
return r, fmt.Errorf("unknown chain ID %s", msgSender.ChainID)
}

recoverOrigin, found := originRecoveryFuncs[chain.TxType]
if !found {
recoverOrigin := getOriginRecoveryFunc(state, types.TxID(tx.Id), chain.TxType)
if recoverOrigin == nil {
return r, fmt.Errorf("recovery function for Tx type %v not found", chain.TxType)
}

recoveredAddr, err := recoverOrigin(signedTx, getAllowedSignatureTypes(state, msgSender.ChainID))
recoveredAddr, err := recoverOrigin(
state.Block().ChainID, signedTx, getAllowedSignatureTypes(state, msgSender.ChainID),
)
if err != nil {
return r, errors.Wrapf(err, "failed to recover origin (tx type %v, chain ID %s)",
chain.TxType, msgSender.ChainID,
Expand Down Expand Up @@ -132,6 +140,23 @@ func NewMultiChainSignatureTxMiddleware(
})
}

func getOriginRecoveryFunc(state appstate.State, txID types.TxID, txType keys.SignedTxType) originRecoveryFunc {
switch txType {
case keys.LoomSignedTxType:
return verifyEd25519
case keys.EthereumSignedTxType:
if (txID == types.TxID_ETHEREUM) && state.FeatureEnabled(features.EthTxFeature, false) {
return VerifyWrappedEthTx
}
return verifySolidity66Byte
case keys.TronSignedTxType:
return verifyTron
case keys.BinanceSignedTxType:
return verifyBinance
}
return nil
}

func getMappedAccountAddress(
state appstate.State,
addr loom.Address,
Expand Down Expand Up @@ -159,7 +184,7 @@ func getMappedAccountAddress(
return mappedAddr, nil
}

func verifyEd25519(tx SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
func verifyEd25519(chainID string, tx SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
if len(tx.PublicKey) != ed25519.PublicKeySize {
return nil, errors.New("invalid public key length")
}
Expand Down
13 changes: 7 additions & 6 deletions auth/multi_chain_sigtx_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/loomnetwork/go-loom/common/evmcompat"
goloomplugin "github.com/loomnetwork/go-loom/plugin"
"github.com/loomnetwork/go-loom/plugin/contractpb"
"github.com/loomnetwork/go-loom/types"
gltypes "github.com/loomnetwork/go-loom/types"
"github.com/loomnetwork/go-loom/vm"
sha3 "github.com/miguelmota/go-solidity-sha3"
Expand All @@ -34,8 +35,9 @@ import (
)

const (
sequence = uint64(4)
callId = uint32(2)
seq = uint64(4)
seq = uint64(4) // todo
defaultLoomChainId = "default"
)

Expand Down Expand Up @@ -218,7 +220,7 @@ func TestEthAddressMappingVerification(t *testing.T) {
ethPublicAddr := loom.Address{ChainID: "eth", Local: ethLocalAdr}

// tx using address mapping from eth account. Gives error.
txSigned = mockSignedTx(t, "eth", &auth.EthSigner66Byte{ethKey})
txSigned = mockSignedTx(t, "eth", &auth.EthSigner66Byte{PrivateKey: ethKey})
_, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx)
require.Error(t, err)

Expand Down Expand Up @@ -278,7 +280,6 @@ func TestBinanceAddressMappingVerification(t *testing.T) {
require.NoError(t, am.Init(amCtx, &address_mapper.InitRequest{}))

// generate eth key
// ethKey, err := crypto.GenerateKey()
privKey, err := crypto.HexToECDSA(ethPrivateKey)
require.NoError(t, err)
signer := auth.NewBinanceSigner(crypto.FromECDSA(privKey))
Expand Down Expand Up @@ -365,15 +366,15 @@ func TestChainIdVerification(t *testing.T) {
// to contracts will be eth:xxxxxx, i.e. the eth account is passed through without being mapped
// to a DAppChain account.
// Don't try this in production.
txSigned = mockSignedTx(t, "eth", &auth.EthSigner66Byte{ethKey})
txSigned = mockSignedTx(t, "eth", &auth.EthSigner66Byte{PrivateKey: ethKey})
_, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx)
require.NoError(t, err)

// Tx signed with Ethereum key, address mapping disabled, the caller address passed through
// to contracts will be tron:xxxxxx, i.e. the eth account is passed through without being mapped
// to a DAppChain account.
// Don't try this in production.
txSigned = mockSignedTx(t, "tron", &auth.TronSigner{ethKey})
txSigned = mockSignedTx(t, "tron", &auth.TronSigner{PrivateKey: ethKey})
_, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx)
require.NoError(t, err)

Expand Down Expand Up @@ -466,7 +467,7 @@ func mockNonceTx(t *testing.T, from loom.Address, sequence uint64) []byte {
})
require.NoError(t, err)
tx, err := proto.Marshal(&gltypes.Transaction{
Id: callId,
Id: uint32(types.TxID_CALL),
Data: messageTx,
})
require.Nil(t, err)
Expand Down
10 changes: 7 additions & 3 deletions auth/noeth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import (
"github.com/loomnetwork/go-loom/common/evmcompat"
)

func verifySolidity66Byte(_ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
func verifySolidity66Byte(_ string, _ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}

func verifyTron(_ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
func verifyTron(_ string, _ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}

func verifyBinance(_ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
func verifyBinance(_ string, _ SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}

func VerifyWrappedEthTx(_ string, signedTx SignedTx, _ []evmcompat.SignatureType) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}
4 changes: 4 additions & 0 deletions cmd/loom/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ func defaultGenesis(cfg *config.Config, validator *loom.Validator) (*config.Gene
Name: features.AuthSigTxFeaturePrefix + "eth",
Status: chainconfig.FeatureWaiting,
},
&cctypes.Feature{
Name: features.EthTxFeature,
Status: chainconfig.FeatureWaiting,
},
&cctypes.Feature{
Name: features.CheckTxValueFeature,
Status: chainconfig.FeatureWaiting,
Expand Down
Loading