From a443c1701ec9777a68af95943143536969275fc5 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 12:51:28 +0000 Subject: [PATCH 01/39] debug_traceTransaction --- app.go | 147 +++++++++---- cmd/loom/db/evm.go | 4 +- cmd/loom/loom.go | 285 +++++++++++++------------- evm/evm.go | 45 +++- evm/loomevm.go | 28 ++- evm/noevm.go | 10 +- plugin/fake_context.go | 4 +- plugin/vm.go | 4 +- rpc/debug/jsonrpc_conversion.go | 45 ++++ rpc/debug/noevmtrace.go | 19 ++ rpc/debug/trace.go | 118 +++++++++++ rpc/instrumenting.go | 14 ++ rpc/query_server.go | 31 ++- rpc/query_service.go | 5 + store/block_store.go | 1 + store/iavlstore.go | 7 + store/logstore.go | 4 + store/memstore.go | 4 + store/multi_writer_app_store.go | 26 ++- store/pruning_iavlstore.go | 17 +- store/splitstore.go | 93 +++++++++ store/store.go | 1 + store/store_test.go | 26 ++- store/versioned_cachingstore.go | 39 ++-- store/versioned_cachingstore_test.go | 10 +- tx_handler/factory/handler_factory.go | 273 ++++++++++++++++++++++++ 26 files changed, 1012 insertions(+), 248 deletions(-) create mode 100644 rpc/debug/jsonrpc_conversion.go create mode 100644 rpc/debug/noevmtrace.go create mode 100644 rpc/debug/trace.go create mode 100644 store/splitstore.go create mode 100644 tx_handler/factory/handler_factory.go diff --git a/app.go b/app.go index 64211fa79b..c233f4d943 100644 --- a/app.go +++ b/app.go @@ -8,8 +8,11 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/core/vm" "github.com/loomnetwork/go-loom/config" "github.com/loomnetwork/go-loom/util" + "github.com/pkg/errors" + "github.com/loomnetwork/loomchain/eth/utils" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/registry" @@ -20,14 +23,15 @@ import ( cctypes "github.com/loomnetwork/go-loom/builtin/types/chainconfig" "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain/log" - "github.com/loomnetwork/loomchain/store" - blockindex "github.com/loomnetwork/loomchain/store/block_index" - evmaux "github.com/loomnetwork/loomchain/store/evm_aux" stdprometheus "github.com/prometheus/client_golang/prometheus" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/common" ttypes "github.com/tendermint/tendermint/types" + + "github.com/loomnetwork/loomchain/log" + "github.com/loomnetwork/loomchain/store" + blockindex "github.com/loomnetwork/loomchain/store/block_index" + evmaux "github.com/loomnetwork/loomchain/store/evm_aux" ) type ReadOnlyState interface { @@ -302,6 +306,11 @@ type TxHandler interface { ProcessTx(state State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) } +type TxHandlerFactory interface { + TxHandler(tracer vm.Tracer, metrics bool) (TxHandler, error) + Copy(newStore store.VersionedKVStore) TxHandlerFactory +} + type TxHandlerFunc func(state State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) type TxHandlerResult struct { @@ -348,6 +357,7 @@ type Application struct { Store store.VersionedKVStore Init func(State) error TxHandler + TxHandlerFactory QueryHandler EventHandler ReceiptHandlerProvider @@ -577,17 +587,19 @@ func (a *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { } // TODO: receiptHandler.CommitBlock() should be moved to Application.Commit() - storeTx := store.WrapAtomic(a.Store).BeginTx() - receiptHandler := a.ReceiptHandlerProvider.Store() - if err := receiptHandler.CommitBlock(a.height()); err != nil { - storeTx.Rollback() - // TODO: maybe panic instead? - log.Error(fmt.Sprintf("aborted committing block receipts, %v", err.Error())) - } else { - storeTx.Commit() + if a.ReceiptHandlerProvider != nil { + storeTx := store.WrapAtomic(a.Store).BeginTx() + receiptHandler := a.ReceiptHandlerProvider.Store() + if err := receiptHandler.CommitBlock(a.height()); err != nil { + storeTx.Rollback() + // TODO: maybe panic instead? + log.Error(fmt.Sprintf("aborted committing block receipts, %v", err.Error())) + } else { + storeTx.Commit() + } } - storeTx = store.WrapAtomic(a.Store).BeginTx() + storeTx := store.WrapAtomic(a.Store).BeginTx() state := NewStoreState( context.Background(), storeTx, @@ -774,15 +786,18 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R a.GetValidatorSet, ).WithOnChainConfig(a.config) - receiptHandler := a.ReceiptHandlerProvider.Store() - defer receiptHandler.DiscardCurrentReceipt() - defer a.EventHandler.Rollback() + if a.ReceiptHandlerProvider != nil { + defer a.ReceiptHandlerProvider.Store().DiscardCurrentReceipt() + } + if a.EventHandler != nil { + defer a.EventHandler.Rollback() + } r, txErr := a.TxHandler.ProcessTx(state, txBytes, false) // Store the receipt even if the tx itself failed var receiptTxHash []byte - if a.ReceiptHandlerProvider.Reader().GetCurrentReceipt() != nil { + if a.ReceiptHandlerProvider != nil && a.ReceiptHandlerProvider.Reader().GetCurrentReceipt() != nil { receiptTxHash = a.ReceiptHandlerProvider.Reader().GetCurrentReceipt().TxHash txHash := ttypes.Tx(txBytes).Hash() // If a receipt was generated for an EVM tx add a link between the TM tx hash and the EVM tx hash @@ -793,7 +808,7 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R ChildTxHash: receiptTxHash, }) } - receiptHandler.CommitCurrentReceipt() + a.ReceiptHandlerProvider.Store().CommitCurrentReceipt() } if txErr != nil { @@ -804,13 +819,15 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R return abci.ResponseDeliverTx{Code: 1, Data: r.Data, Log: txErr.Error()} } - a.EventHandler.Commit(uint64(a.curBlockHeader.GetHeight())) storeTx.Commit() - // FIXME: Really shouldn't be sending out events until the whole block is committed because - // the state changes from the tx won't be visible to queries until after Application.Commit() - if err := a.EventHandler.LegacyEthSubscriptionSet().EmitTxEvent(r.Data, r.Info); err != nil { - log.Error("Emit Tx Event error", "err", err) + if a.EventHandler != nil { + a.EventHandler.Commit(uint64(a.curBlockHeader.GetHeight())) + // FIXME: Really shouldn't be sending out events until the whole block is committed because + // the state changes from the tx won't be visible to queries until after Application.Commit() + if err := a.EventHandler.LegacyEthSubscriptionSet().EmitTxEvent(r.Data, r.Info); err != nil { + log.Error("Emit Tx Event error", "err", err) + } } if len(receiptTxHash) > 0 { @@ -837,23 +854,28 @@ func (a *Application) Commit() abci.ResponseCommit { height := a.curBlockHeader.GetHeight() - if err := a.EvmAuxStore.SaveChildTxRefs(a.childTxRefs); err != nil { - // TODO: consider panic instead - log.Error("Failed to save Tendermint -> EVM tx hash refs", "height", height, "err", err) + if a.EvmAuxStore != nil { + if err := a.EvmAuxStore.SaveChildTxRefs(a.childTxRefs); err != nil { + // TODO: consider panic instead + log.Error("Failed to save Tendermint -> EVM tx hash refs", "height", height, "err", err) + } } a.childTxRefs = nil - go func(height int64, blockHeader abci.Header) { - if err := a.EventHandler.EmitBlockTx(uint64(height), blockHeader.Time); err != nil { - log.Error("Emit Block Event error", "err", err) - } - if err := a.EventHandler.LegacyEthSubscriptionSet().EmitBlockEvent(blockHeader); err != nil { - log.Error("Emit Block Event error", "err", err) - } - if err := a.EventHandler.EthSubscriptionSet().EmitBlockEvent(blockHeader); err != nil { - log.Error("Emit Block Event error", "err", err) - } - }(height, a.curBlockHeader) + if a.EventHandler != nil { + go func(height int64, blockHeader abci.Header) { + if err := a.EventHandler.EmitBlockTx(uint64(height), blockHeader.Time); err != nil { + log.Error("Emit Block Event error", "err", err) + } + if err := a.EventHandler.LegacyEthSubscriptionSet().EmitBlockEvent(blockHeader); err != nil { + log.Error("Emit Block Event error", "err", err) + } + if err := a.EventHandler.EthSubscriptionSet().EmitBlockEvent(blockHeader); err != nil { + log.Error("Emit Block Event error", "err", err) + } + }(height, a.curBlockHeader) + } + a.lastBlockHeader = a.curBlockHeader if err := a.Store.Prune(); err != nil { @@ -885,6 +907,7 @@ func (a *Application) Query(req abci.RequestQuery) abci.ResponseQuery { func (a *Application) height() int64 { return a.Store.Version() + 1 } + func (a *Application) ReadOnlyState() 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 @@ -896,3 +919,53 @@ func (a *Application) ReadOnlyState() State { a.GetValidatorSet, ) } + +func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.BlockStore) (*Application, int64, error) { + startVersion := int64(blockNumber) - 1 + if startVersion < 0 { + return nil, 0, errors.Errorf("invalid block number %d", blockNumber) + } + var snapshot store.Snapshot + for err := error(nil); (snapshot == nil || err != nil) && startVersion > 0; startVersion-- { + snapshot, err = a.Store.GetSnapshotAt(startVersion) + } + if startVersion == 0 { + return nil, 0, errors.Errorf("no saved version for height %d", blockNumber) + } + + splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) + factory := a.TxHandlerFactory.Copy(splitStore) + txHandle, err := factory.TxHandler(nil, false) + if err != nil { + return nil, 0, err + } + newApp := &Application{ + Store: splitStore, + Init: func(state State) error { + panic("init should not be called") + }, + TxHandler: txHandle, + TxHandlerFactory: factory, + BlockIndexStore: nil, + EventHandler: nil, + ReceiptHandlerProvider: nil, + CreateValidatorManager: a.CreateValidatorManager, + CreateChainConfigManager: a.CreateChainConfigManager, + CreateContractUpkeepHandler: a.CreateContractUpkeepHandler, + EventStore: nil, + GetValidatorSet: a.GetValidatorSet, + EvmAuxStore: nil, + ReceiptsVersion: a.ReceiptsVersion, + config: a.config, + } + return newApp, startVersion, nil +} + +func (a *Application) SetTracer(tracer vm.Tracer, metrics bool) error { + newTxHandle, err := a.TxHandlerFactory.TxHandler(tracer, metrics) + if err != nil { + return errors.Wrap(err, "making transaction handle") + } + a.TxHandler = newTxHandle + return nil +} diff --git a/cmd/loom/db/evm.go b/cmd/loom/db/evm.go index fdda84a2d9..2d1bb0969a 100644 --- a/cmd/loom/db/evm.go +++ b/cmd/loom/db/evm.go @@ -108,7 +108,7 @@ func newDumpEVMStateCommand() *cobra.Command { } } - vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false) + vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false, nil) if err != nil { return err } @@ -221,7 +221,7 @@ func newDumpEVMStateMultiWriterAppStoreCommand() *cobra.Command { } } - vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false) + vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false, nil) if err != nil { return err } diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index 596166c9cf..f4d6406817 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -29,17 +29,24 @@ import ( "github.com/loomnetwork/go-loom/crypto" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/util" + "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/abci/backend" - "github.com/loomnetwork/loomchain/auth" "github.com/loomnetwork/loomchain/builtin/plugins/dposv2" "github.com/loomnetwork/loomchain/builtin/plugins/dposv3" plasmaConfig "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/config" plasmaOracle "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/oracle" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/receipts/leveldb" + "github.com/loomnetwork/loomchain/tx_handler/factory" + "github.com/prometheus/client_golang/prometheus" + "github.com/pkg/errors" + stdprometheus "github.com/prometheus/client_golang/prometheus" + "github.com/spf13/cobra" + "golang.org/x/crypto/ed25519" + "github.com/loomnetwork/loomchain/chainconfig" chaincfgcmd "github.com/loomnetwork/loomchain/cmd/loom/chainconfig" "github.com/loomnetwork/loomchain/cmd/loom/common" @@ -57,7 +64,6 @@ import ( "github.com/loomnetwork/loomchain/fnConsensus" karma_handler "github.com/loomnetwork/loomchain/karma" "github.com/loomnetwork/loomchain/log" - "github.com/loomnetwork/loomchain/migrations" "github.com/loomnetwork/loomchain/plugin" "github.com/loomnetwork/loomchain/receipts" "github.com/loomnetwork/loomchain/receipts/handler" @@ -67,13 +73,7 @@ import ( "github.com/loomnetwork/loomchain/store" blockindex "github.com/loomnetwork/loomchain/store/block_index" evmaux "github.com/loomnetwork/loomchain/store/evm_aux" - "github.com/loomnetwork/loomchain/throttle" - "github.com/loomnetwork/loomchain/tx_handler" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/spf13/cobra" - "golang.org/x/crypto/ed25519" ) var ( @@ -834,35 +834,35 @@ func loadApp( return nil, err } } - return evm.NewLoomVm(state, eventHandler, receiptHandlerProvider.Writer(), createABM, cfg.EVMDebugEnabled), nil + return evm.NewLoomVm(state, receiptHandlerProvider.Writer(), createABM, cfg.EVMDebugEnabled, nil), nil }) } evm.LogEthDbBatch = cfg.LogEthDbBatch - deployTxHandler := &vm.DeployTxHandler{ - Manager: vmManager, - CreateRegistry: createRegistry, - AllowNamedEVMContracts: cfg.AllowNamedEvmContracts, - } - - callTxHandler := &vm.CallTxHandler{ - Manager: vmManager, - } - - ethTxHandler := &tx_handler.EthTxHandler{ - Manager: vmManager, - CreateRegistry: createRegistry, - } - - migrationTxHandler := &tx_handler.MigrationTxHandler{ - Manager: vmManager, - CreateRegistry: createRegistry, - Migrations: map[int32]tx_handler.MigrationFunc{ - 1: migrations.DPOSv3Migration, - 2: migrations.GatewayMigration, - 3: migrations.GatewayMigration, - }, - } + //deployTxHandler := &vm.DeployTxHandler{ + // Manager: vmManager, + // CreateRegistry: createRegistry, + // AllowNamedEVMContracts: cfg.AllowNamedEvmContracts, + //} + + //callTxHandler := &vm.CallTxHandler{ + // Manager: vmManager, + //} + + //ethTxHandler := &tx_handler.EthTxHandler{ + // Manager: vmManager, + // CreateRegistry: createRegistry, + //} + + //migrationTxHandler := &tx_handler.MigrationTxHandler{ + // Manager: vmManager, + // CreateRegistry: createRegistry, + // Migrations: map[int32]tx_handler.MigrationFunc{ + // 1: migrations.DPOSv3Migration, + // 2: migrations.GatewayMigration, + // 3: migrations.GatewayMigration, + // }, + //} gen, err := config.ReadGenesis(cfg.GenesisPath()) if err != nil { @@ -897,106 +897,106 @@ func loadApp( return nil } - router := loomchain.NewTxRouter() - - isEvmTx := func(txID uint32, state loomchain.State, txBytes []byte, isCheckTx bool) bool { - var msg vm.MessageTx - err := proto.Unmarshal(txBytes, &msg) - if err != nil { - return false - } - - switch txID { - case 1: - var tx vm.DeployTx - err = proto.Unmarshal(msg.Data, &tx) + //router := loomchain.NewTxRouter() + /* + isEvmTx := func(txID uint32, state loomchain.State, txBytes []byte, isCheckTx bool) bool { + var msg vm.MessageTx + err := proto.Unmarshal(txBytes, &msg) if err != nil { - // In case of error, let's give safest response, - // let's TxHandler down the line, handle it. return false } - return tx.VmType == vm.VMType_EVM - case 2: - var tx vm.CallTx - err = proto.Unmarshal(msg.Data, &tx) - if err != nil { - // In case of error, let's give safest response, - // let's TxHandler down the line, handle it. + + switch txID { + case 1: + var tx vm.DeployTx + err = proto.Unmarshal(msg.Data, &tx) + if err != nil { + // In case of error, let's give safest response, + // let's TxHandler down the line, handle it. + return false + } + return tx.VmType == vm.VMType_EVM + case 2: + var tx vm.CallTx + err = proto.Unmarshal(msg.Data, &tx) + if err != nil { + // In case of error, let's give safest response, + // let's TxHandler down the line, handle it. + return false + } + return tx.VmType == vm.VMType_EVM + case 3: + return false + default: return false } - return tx.VmType == vm.VMType_EVM - case 3: - return false - default: - return false } - } - - router.HandleDeliverTx(1, loomchain.GeneratePassthroughRouteHandler(deployTxHandler)) - router.HandleDeliverTx(2, loomchain.GeneratePassthroughRouteHandler(callTxHandler)) - router.HandleDeliverTx(3, loomchain.GeneratePassthroughRouteHandler(migrationTxHandler)) - router.HandleDeliverTx(4, loomchain.GeneratePassthroughRouteHandler(ethTxHandler)) - - // TODO: Write this in more elegant way - router.HandleCheckTx(1, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, deployTxHandler)) - router.HandleCheckTx(2, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, callTxHandler)) - router.HandleCheckTx(3, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, migrationTxHandler)) - router.HandleCheckTx(4, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, ethTxHandler)) - - txMiddleWare := []loomchain.TxMiddleware{ - loomchain.LogTxMiddleware, - loomchain.RecoveryTxMiddleware, - } - - postCommitMiddlewares := []loomchain.PostCommitMiddleware{ - loomchain.LogPostCommitMiddleware, - } - txMiddleWare = append(txMiddleWare, auth.NewChainConfigMiddleware( - cfg.Auth, - getContractStaticCtx("addressmapper", vmManager), - )) + router.HandleDeliverTx(1, loomchain.GeneratePassthroughRouteHandler(deployTxHandler)) + router.HandleDeliverTx(2, loomchain.GeneratePassthroughRouteHandler(callTxHandler)) + router.HandleDeliverTx(3, loomchain.GeneratePassthroughRouteHandler(migrationTxHandler)) + router.HandleDeliverTx(4, loomchain.GeneratePassthroughRouteHandler(ethTxHandler)) + + // TODO: Write this in more elegant way + router.HandleCheckTx(1, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, deployTxHandler)) + router.HandleCheckTx(2, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, callTxHandler)) + router.HandleCheckTx(3, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, migrationTxHandler)) + router.HandleCheckTx(4, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, ethTxHandler)) + */ + //txMiddleWare := []loomchain.TxMiddleware{ + // loomchain.LogTxMiddleware, + // loomchain.RecoveryTxMiddleware, + //} + + //postCommitMiddlewares := []loomchain.PostCommitMiddleware{ + // loomchain.LogPostCommitMiddleware, + //} + + //txMiddleWare = append(txMiddleWare, auth.NewChainConfigMiddleware( + // cfg.Auth, + // getContractStaticCtx("addressmapper", vmManager), + //)) createKarmaContractCtx := getContractCtx("karma", vmManager) - if cfg.Karma.Enabled { - txMiddleWare = append(txMiddleWare, throttle.GetKarmaMiddleWare( - cfg.Karma.Enabled, - cfg.Karma.MaxCallCount, - cfg.Karma.SessionDuration, - createKarmaContractCtx, - )) - } - - if cfg.TxLimiter.Enabled { - txMiddleWare = append(txMiddleWare, throttle.NewTxLimiterMiddleware(cfg.TxLimiter)) - } - - if cfg.ContractTxLimiter.Enabled { - contextFactory := getContractCtx("user-deployer-whitelist", vmManager) - txMiddleWare = append( - txMiddleWare, throttle.NewContractTxLimiterMiddleware(cfg.ContractTxLimiter, contextFactory), - ) - } - - if cfg.DeployerWhitelist.ContractEnabled { - contextFactory := getContractCtx("deployerwhitelist", vmManager) - dwMiddleware, err := throttle.NewDeployerWhitelistMiddleware(contextFactory) - if err != nil { - return nil, err - } - txMiddleWare = append(txMiddleWare, dwMiddleware) - - } - - if cfg.UserDeployerWhitelist.ContractEnabled { - contextFactory := getContractCtx("user-deployer-whitelist", vmManager) - evmDeployRecorderMiddleware, err := throttle.NewEVMDeployRecorderPostCommitMiddleware(contextFactory) - if err != nil { - return nil, err - } - postCommitMiddlewares = append(postCommitMiddlewares, evmDeployRecorderMiddleware) - } + //if cfg.Karma.Enabled { + // txMiddleWare = append(txMiddleWare, throttle.GetKarmaMiddleWare( + // cfg.Karma.Enabled, + // cfg.Karma.MaxCallCount, + // cfg.Karma.SessionDuration, + // createKarmaContractCtx, + // )) + //} + + //if cfg.TxLimiter.Enabled { + // txMiddleWare = append(txMiddleWare, throttle.NewTxLimiterMiddleware(cfg.TxLimiter)) + //} + + //if cfg.ContractTxLimiter.Enabled { + // contextFactory := getContractCtx("user-deployer-whitelist", vmManager) + // txMiddleWare = append( + // txMiddleWare, throttle.NewContractTxLimiterMiddleware(cfg.ContractTxLimiter, contextFactory), + // ) + //} + + //if cfg.DeployerWhitelist.ContractEnabled { + // contextFactory := getContractCtx("deployerwhitelist", vmManager) + // dwMiddleware, err := throttle.NewDeployerWhitelistMiddleware(contextFactory) + // if err != nil { + // return nil, err + // } + // txMiddleWare = append(txMiddleWare, dwMiddleware) + + //} + + //if cfg.UserDeployerWhitelist.ContractEnabled { + // contextFactory := getContractCtx("user-deployer-whitelist", vmManager) + // evmDeployRecorderMiddleware, err := throttle.NewEVMDeployRecorderPostCommitMiddleware(contextFactory) + // if err != nil { + // return nil, err + // } + // postCommitMiddlewares = append(postCommitMiddlewares, evmDeployRecorderMiddleware) + //} createContractUpkeepHandler := func(state loomchain.State) (loomchain.KarmaHandler, error) { // TODO: This setting should be part of the config stored within the Karma contract itself, @@ -1045,18 +1045,18 @@ func loadApp( return loom.NewValidatorSet(b.GenesisValidators()...), nil } - nonceTxHandler := auth.NewNonceHandler() - txMiddleWare = append(txMiddleWare, nonceTxHandler.TxMiddleware(appStore)) + //nonceTxHandler := auth.NewNonceHandler() + //txMiddleWare = append(txMiddleWare, nonceTxHandler.TxMiddleware(appStore)) - if cfg.GoContractDeployerWhitelist.Enabled { - goDeployers, err := cfg.GoContractDeployerWhitelist.DeployerAddresses(chainID) - if err != nil { - return nil, errors.Wrapf(err, "getting list of users allowed go deploys") - } - txMiddleWare = append(txMiddleWare, throttle.GetGoDeployTxMiddleWare(goDeployers)) - } + //if cfg.GoContractDeployerWhitelist.Enabled { + // goDeployers, err := cfg.GoContractDeployerWhitelist.DeployerAddresses(chainID) + // if err != nil { + // return nil, errors.Wrapf(err, "getting list of users allowed go deploys") + // } + // txMiddleWare = append(txMiddleWare, throttle.GetGoDeployTxMiddleWare(goDeployers)) + //} - txMiddleWare = append(txMiddleWare, loomchain.NewInstrumentingTxMiddleware()) + //txMiddleWare = append(txMiddleWare, loomchain.NewInstrumentingTxMiddleware()) createValidatorsManager := func(state loomchain.State) (loomchain.ValidatorsManager, error) { pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) @@ -1114,16 +1114,19 @@ func loadApp( // We need to make sure nonce post commit middleware is last // as it doesn't pass control to other middlewares after it. - postCommitMiddlewares = append(postCommitMiddlewares, nonceTxHandler.PostCommitMiddleware()) + //postCommitMiddlewares = append(postCommitMiddlewares, nonceTxHandler.PostCommitMiddleware()) + + txHandlerFactory := factory.NewTxHandlerFactory(*cfg, vmManager, chainID, appStore, createRegistry) + chainTxHandler, err := txHandlerFactory.TxHandler(nil, true) + if err != nil { + return nil, err + } return &loomchain.Application{ - Store: appStore, - Init: init, - TxHandler: loomchain.MiddlewareTxHandler( - txMiddleWare, - router, - postCommitMiddlewares, - ), + Store: appStore, + Init: init, + TxHandler: chainTxHandler, + TxHandlerFactory: txHandlerFactory, BlockIndexStore: blockIndexStore, EventHandler: eventHandler, ReceiptHandlerProvider: receiptHandlerProvider, diff --git a/evm/evm.go b/evm/evm.go index f33de2a01a..09e235f1e3 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -15,10 +15,10 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/go-kit/kit/metrics" kitprometheus "github.com/go-kit/kit/metrics/prometheus" + "github.com/loomnetwork/go-loom" "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/loomnetwork/go-loom" "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/log" @@ -147,7 +147,9 @@ type Evm struct { gasLimit uint64 } -func NewEvm(sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManager, debug bool) *Evm { +func NewEvm( + sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManager, debug bool, tracer vm.Tracer, +) (*Evm, error) { p := new(Evm) p.sdb = sdb p.gasLimit = lstate.Config().GetEvm().GetGasLimit() @@ -157,7 +159,11 @@ func NewEvm(sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManage p.chainConfig = defaultChainConfig(lstate.FeatureEnabled(features.EvmConstantinopleFeature, false)) - p.vmConfig = defaultVmConfig(debug) + var err error + p.vmConfig, err = createVmConfig(debug, tracer) + if err != nil { + return nil, errors.Wrap(err, "creating vm.Config") + } p.validateTxValue = lstate.FeatureEnabled(features.CheckTxValueFeature, false) p.context = vm.Context{ CanTransfer: core.CanTransfer, @@ -180,7 +186,7 @@ func NewEvm(sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManage abm.Transfer(from, to, amount) } } - return p + return p, nil } func (e Evm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]byte, loom.Address, error) { @@ -335,3 +341,34 @@ func defaultVmConfig(evmDebuggingEnabled bool) vm.Config { //JumpTable: [256]operation, } } + +func createVmConfig(evmDebuggingEnabled bool, tracer vm.Tracer) (vm.Config, error) { + if evmDebuggingEnabled { + log.Error("WARNING!!!! EVM Debug mode enabled, do NOT run this on a production server!!!") + } + + if tracer == nil { + logCfg := vm.LogConfig{ + DisableMemory: true, // disable memory capture + DisableStack: true, // disable stack capture + DisableStorage: true, // disable storage capture + Limit: 0, // maximum length of output, but zero means unlimited + } + tracer = vm.NewStructLogger(&logCfg) + } + return vm.Config{ + // Debug enabled debugging Interpreter options + Debug: evmDebuggingEnabled || (tracer != nil), + // Tracer is the op code logger + Tracer: tracer, + // NoRecursion disabled Interpreter call, callcode, + // delegate call and create. + NoRecursion: false, + // Enable recording of SHA3/keccak preimages + EnablePreimageRecording: true, //TODO: make this optional, [MGC] I don't think we need to keep this + // JumpTable contains the EVM instruction table. This + // may be left uninitialised and will be set to the default + // table. + //JumpTable: [256]operation, + }, nil +} diff --git a/evm/loomevm.go b/evm/loomevm.go index 3d3ae3ff3f..79ed0ac416 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -53,8 +53,11 @@ type LoomEvm struct { // TODO: this doesn't need to be exported, rename to newLoomEvmWithState func NewLoomEvm( - loomState loomchain.State, accountBalanceManager AccountBalanceManager, - logContext *ethdbLogContext, debug bool, + loomState loomchain.State, + accountBalanceManager AccountBalanceManager, + logContext *ethdbLogContext, + debug bool, + tracer ethvm.Tracer, ) (*LoomEvm, error) { p := new(LoomEvm) p.db = NewLoomEthdb(loomState, logContext) @@ -74,7 +77,10 @@ func NewLoomEvm( return nil, err } - p.Evm = NewEvm(p.sdb, loomState, abm, debug) + p.Evm, err = NewEvm(p.sdb, loomState, abm, debug, tracer) + if err != nil { + return nil, errors.Wrap(err, "creating tracer") + } return p, nil } @@ -111,7 +117,7 @@ var LoomVmFactory = func(state loomchain.State) (vm.VM, error) { nil, ) receiptHandler := receiptHandlerProvider.Writer() - return NewLoomVm(state, eventHandler, receiptHandler, nil, debug), nil + return NewLoomVm(state, receiptHandler, nil, debug, nil), nil } // LoomVm implements the loomchain/vm.VM interface using the EVM. @@ -121,20 +127,22 @@ type LoomVm struct { receiptHandler loomchain.WriteReceiptHandler createABM AccountBalanceManagerFactoryFunc debug bool + tracer ethvm.Tracer } func NewLoomVm( loomState loomchain.State, - eventHandler loomchain.EventHandler, receiptHandler loomchain.WriteReceiptHandler, createABM AccountBalanceManagerFactoryFunc, debug bool, + tracer ethvm.Tracer, ) vm.VM { return &LoomVm{ state: loomState, receiptHandler: receiptHandler, createABM: createABM, debug: debug, + tracer: tracer, } } @@ -151,7 +159,7 @@ func (lvm LoomVm) Create(caller loom.Address, code []byte, value *loom.BigUInt) contractAddr: loom.Address{}, callerAddr: caller, } - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug, lvm.tracer) if err != nil { return nil, loom.Address{}, err } @@ -214,7 +222,7 @@ func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigU contractAddr: addr, callerAddr: caller, } - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug, lvm.tracer) if err != nil { return nil, err } @@ -262,7 +270,7 @@ func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigU } func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(true), nil, lvm.debug) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(true), nil, lvm.debug, lvm.tracer) if err != nil { return nil, err } @@ -270,7 +278,7 @@ func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, e } func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug) + levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug, lvm.tracer) if err != nil { return nil, err } @@ -278,7 +286,7 @@ func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { } func (lvm LoomVm) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug) + levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug, lvm.tracer) if err != nil { return nil, err } diff --git a/evm/noevm.go b/evm/noevm.go index 80427c9327..2d5a263c2f 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -15,11 +15,11 @@ var ( const EVMEnabled = false func NewLoomVm( - loomState loomchain.State, - eventHandler loomchain.EventHandler, - receiptHandler loomchain.WriteReceiptHandler, - createABM AccountBalanceManagerFactoryFunc, - debug bool, + _ state.State, + _ loomchain.WriteReceiptHandler, + _ AccountBalanceManagerFactoryFunc, + _ bool, + _ *eth.TraceConfig, ) lvm.VM { return nil } diff --git a/plugin/fake_context.go b/plugin/fake_context.go index 84ddb384fe..5acaec3315 100644 --- a/plugin/fake_context.go +++ b/plugin/fake_context.go @@ -103,7 +103,7 @@ func (c *FakeContextWithEVM) CallEVM(addr loom.Address, input []byte, value *loo if c.useAccountBalanceManager { createABM = c.AccountBalanceManager } - vm := levm.NewLoomVm(c.State, nil, nil, createABM, false) + vm := levm.NewLoomVm(c.State, nil, createABM, false, nil) return vm.Call(c.ContractAddress(), addr, input, value) } @@ -112,7 +112,7 @@ func (c *FakeContextWithEVM) StaticCallEVM(addr loom.Address, input []byte) ([]b if c.useAccountBalanceManager { createABM = c.AccountBalanceManager } - vm := levm.NewLoomVm(c.State, nil, nil, createABM, false) + vm := levm.NewLoomVm(c.State, nil, createABM, false, nil) return vm.StaticCall(c.ContractAddress(), addr, input) } diff --git a/plugin/vm.go b/plugin/vm.go index 4a12c17d47..cca4c95715 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -196,7 +196,7 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.EventHandler, vm.receiptWriter, createABM, false) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) return evm.Call(caller, addr, input, value) } @@ -209,7 +209,7 @@ func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]by return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.EventHandler, vm.receiptWriter, createABM, false) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) return evm.StaticCall(caller, addr, input) } diff --git a/rpc/debug/jsonrpc_conversion.go b/rpc/debug/jsonrpc_conversion.go new file mode 100644 index 0000000000..050fd896c2 --- /dev/null +++ b/rpc/debug/jsonrpc_conversion.go @@ -0,0 +1,45 @@ +// +build evm + +package debug + +import ( + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" +) + +type JsonTraceConfig struct { + LogConfig *JsonLogConfig `json:"logconfig,omitempty"` + Tracer string `json:"tracer,omitempty"` + Timeout string `json:"address,omitempty"` +} + +type JsonLogConfig struct { + DisableStorage bool `json:"disableStorage,omitempty"` + DisableMemory bool `json:"disableMemory,omitempty"` + DisableStack bool `json:"disableStack,omitempty"` +} + +func DecTraceConfig(jcfg *JsonTraceConfig) eth.TraceConfig { + var logConfig *vm.LogConfig + if jcfg == nil { + return eth.TraceConfig{ + LogConfig: logConfig, + Tracer: nil, + Timeout: nil, + Reexec: nil, + } + } + if jcfg.LogConfig != nil { + logConfig = &vm.LogConfig{ + DisableMemory: jcfg.LogConfig.DisableMemory, + DisableStack: jcfg.LogConfig.DisableStack, + DisableStorage: jcfg.LogConfig.DisableStorage, + } + } + return eth.TraceConfig{ + LogConfig: logConfig, + Tracer: &jcfg.Tracer, + Timeout: &jcfg.Timeout, + Reexec: nil, + } +} diff --git a/rpc/debug/noevmtrace.go b/rpc/debug/noevmtrace.go new file mode 100644 index 0000000000..5781d01c97 --- /dev/null +++ b/rpc/debug/noevmtrace.go @@ -0,0 +1,19 @@ +// +build !evm + +package debug + +import ( + "github.com/ethereum/go-ethereum/eth" + + "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/store" +) + +func TraceTransaction( + _ loomchain.Application, + _ store.BlockStore, + _, _, _ int64, + _ eth.TraceConfig, +) (trace interface{}, err error) { + return nil, nil +} diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go new file mode 100644 index 0000000000..01f8f1fe51 --- /dev/null +++ b/rpc/debug/trace.go @@ -0,0 +1,118 @@ +// +build evm + +package debug + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethapi" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/pkg/errors" + abci "github.com/tendermint/tendermint/abci/types" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + ttypes "github.com/tendermint/tendermint/types" + + "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/store" +) + +func TraceTransaction( + app loomchain.Application, + blockstore store.BlockStore, + startBlockNumber, targetBlockNumber, txIndex int64, + config eth.TraceConfig, +) (trace interface{}, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("loomchain panicked %v", r) + } + }() + + if err := runUpTo(&app, blockstore, startBlockNumber, targetBlockNumber, txIndex); err != nil { + return nil, err + } + + block, err := blockstore.GetBlockByHeight(&targetBlockNumber) + if err != nil { + return nil, errors.Wrapf(err, "getting block information at height %v", targetBlockNumber) + } + + tracer, err := createTracer(config) + if err != nil { + return nil, err + } + if err := app.SetTracer(tracer, false); err != nil { + return nil, err + } + + result := app.DeliverTx(block.Block.Data.Txs[txIndex]) + + switch tracer := tracer.(type) { + case *vm.StructLogger: + return ðapi.ExecutionResult{ + Gas: 5, + Failed: result.Code != abci.CodeTypeOK, + ReturnValue: fmt.Sprintf("%x", result), + StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + }, nil + case *tracers.Tracer: + return tracer.GetResult() + default: + return nil, errors.New(fmt.Sprintf("bad tracer type %T", tracer)) + } +} + +func runUpTo(app *loomchain.Application, blockstore store.BlockStore, startHeight, height, index int64) error { + for h := startHeight; h <= height; h++ { + resultBlock, err := blockstore.GetBlockByHeight(&h) + if err != nil { + return errors.Wrapf(err, "getting block information at height %v", h) + } + + _ = app.BeginBlock(requestBeginBlock(*resultBlock)) + + if h != height { + for i := 0; i < len(resultBlock.Block.Data.Txs); i++ { + _ = app.DeliverTx(resultBlock.Block.Data.Txs[i]) + } + } else { + for i := 0; i < len(resultBlock.Block.Data.Txs); i++ { + if i != int(index) { + _ = app.DeliverTx(resultBlock.Block.Data.Txs[i]) + } else { + return nil + } + } + } + + _ = app.EndBlock(requestEndBlock(h)) + _ = app.Commit() + } + return errors.Errorf("cannot find transaction at height %d index %d", height, index) +} + +func requestBeginBlock(resultBlock ctypes.ResultBlock) abci.RequestBeginBlock { + return abci.RequestBeginBlock{ + Header: ttypes.TM2PB.Header(&resultBlock.BlockMeta.Header), + Hash: resultBlock.BlockMeta.BlockID.Hash, + } +} + +func requestEndBlock(height int64) abci.RequestEndBlock { + return abci.RequestEndBlock{ + Height: height, + } +} + +func createTracer(traceCfg eth.TraceConfig) (vm.Tracer, error) { + if traceCfg.Tracer == nil || len(*traceCfg.Tracer) == 0 { + return vm.NewStructLogger(traceCfg.LogConfig), nil + } + tracer, err := tracers.New(*traceCfg.Tracer) + if err != nil { + return nil, err + } + return tracer, nil +} diff --git a/rpc/instrumenting.go b/rpc/instrumenting.go index 97caffb15e..25f5631094 100755 --- a/rpc/instrumenting.go +++ b/rpc/instrumenting.go @@ -8,6 +8,7 @@ import ( "github.com/gorilla/websocket" "github.com/loomnetwork/go-loom/plugin/types" "github.com/loomnetwork/loomchain/config" + "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/rpc/eth" "github.com/loomnetwork/loomchain/vm" rpctypes "github.com/tendermint/tendermint/rpc/lib/types" @@ -569,3 +570,16 @@ func (m InstrumentingMiddleware) EthGetTransactionCount( resp, err = m.next.EthGetTransactionCount(local, block) return } + +func (m InstrumentingMiddleware) DebugTraceTransaction( + hash eth.Data, config *debug.JsonTraceConfig, +) (resp interface{}, err error) { + defer func(begin time.Time) { + lvs := []string{"method", "DebugTraceTransaction", "error", fmt.Sprint(err != nil)} + m.requestCount.With(lvs...).Add(1) + m.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) + }(time.Now()) + + resp, err = m.next.DebugTraceTransaction(hash, config) + return +} diff --git a/rpc/query_server.go b/rpc/query_server.go index 2a704b7be9..6d6eedc2a0 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -38,6 +38,7 @@ import ( "github.com/loomnetwork/loomchain/receipts/common" "github.com/loomnetwork/loomchain/registry" registryFac "github.com/loomnetwork/loomchain/registry/factory" + "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/rpc/eth" "github.com/loomnetwork/loomchain/store" blockindex "github.com/loomnetwork/loomchain/store/block_index" @@ -59,6 +60,7 @@ const ( // StateProvider interface is used by QueryServer to access the read-only application state type StateProvider interface { ReadOnlyState() loomchain.State + ReplayApplication(uint64, store.BlockStore) (*loomchain.Application, int64, error) } // QueryServer provides the ability to query the current state of the DAppChain via RPC. @@ -268,7 +270,7 @@ func (s *QueryServer) queryEvm(state loomchain.State, caller, contract loom.Addr return nil, err } } - vm := levm.NewLoomVm(state, nil, nil, createABM, false) + vm := levm.NewLoomVm(state, nil, createABM, false, nil) return vm.StaticCall(callerAddr, contract, query) } @@ -313,7 +315,7 @@ func (s *QueryServer) GetEvmCode(contract string) ([]byte, error) { snapshot := s.StateProvider.ReadOnlyState() defer snapshot.Release() - vm := levm.NewLoomVm(snapshot, nil, nil, nil, false) + vm := levm.NewLoomVm(snapshot, nil, nil, false, nil) return vm.GetCode(contractAddr) } @@ -327,7 +329,7 @@ func (s *QueryServer) EthGetCode(address eth.Data, block eth.BlockHeight) (eth.D snapshot := s.StateProvider.ReadOnlyState() defer snapshot.Release() - evm := levm.NewLoomVm(snapshot, nil, nil, nil, false) + evm := levm.NewLoomVm(snapshot, nil, nil, false, nil) code, err := evm.GetCode(addr) if err != nil { return "", errors.Wrapf(err, "getting evm code for %v", address) @@ -1102,7 +1104,7 @@ func (s *QueryServer) EthGetStorageAt(local eth.Data, position string, block eth return "", errors.Wrapf(err, "unable to get storage at height %v", block) } - evm := levm.NewLoomVm(snapshot, nil, nil, nil, false) + evm := levm.NewLoomVm(snapshot, nil, nil, false, nil) storage, err := evm.GetStorageAt(address, ethcommon.HexToHash(position).Bytes()) if err != nil { return "", errors.Wrapf(err, "failed to get EVM storage at %v", address.Local.String()) @@ -1129,6 +1131,27 @@ func (s *QueryServer) EthAccounts() ([]eth.Data, error) { return []eth.Data{}, nil } +func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTraceConfig) (interface{}, error) { + receipt, err := s.EthGetTransactionReceipt(hash) + if err != nil || receipt == nil { + return nil, errors.Wrap(err, "cant find transaction matching hash") + } + blockNumber, err := eth.DecQuantityToUint(receipt.BlockNumber) + if err != nil { + return nil, errors.Wrapf(err, "cant parse block number %v", receipt.BlockNumber) + } + txIndex, err := eth.DecQuantityToUint(receipt.TransactionIndex) + if err != nil { + return nil, errors.Wrapf(err, "cant parse transaction index %v", receipt.TransactionIndex) + } + cfg := debug.DecTraceConfig(config) + replayApp, startBlockNumber, err := s.ReplayApplication(blockNumber, s.BlockStore) + if err != nil { + return nil, err + } + return debug.TraceTransaction(*replayApp, s.BlockStore, startBlockNumber, int64(blockNumber), int64(txIndex), cfg) +} + func (s *QueryServer) getBlockHeightFromHash(hash []byte) (uint64, error) { if nil != s.BlockIndexStore { return s.BlockIndexStore.GetBlockHeightByHash(hash) diff --git a/rpc/query_service.go b/rpc/query_service.go index 77900c181e..6f1ef58759 100755 --- a/rpc/query_service.go +++ b/rpc/query_service.go @@ -17,6 +17,7 @@ import ( "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/eth/subs" "github.com/loomnetwork/loomchain/log" + "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/rpc/eth" "github.com/loomnetwork/loomchain/vm" ) @@ -65,6 +66,9 @@ type QueryService interface { GetContractRecord(contractAddr string) (*types.ContractRecordResponse, error) + // debug transactions. + DebugTraceTransaction(hash eth.Data, config *debug.JsonTraceConfig) (interface{}, error) + // deprecated function EvmTxReceipt(txHash []byte) ([]byte, error) GetEvmCode(contract string) ([]byte, error) @@ -188,6 +192,7 @@ func createDefaultEthRoutes(svc QueryService, chainID string) map[string]eth.RPC routes["net_version"] = eth.NewRPCFunc(svc.EthNetVersion, "") routes["eth_getTransactionCount"] = eth.NewRPCFunc(svc.EthGetTransactionCount, "local,block") routes["eth_sendRawTransaction"] = NewSendRawTransactionRPCFunc(chainID, rpccore.BroadcastTxSync) + routes["debug_traceTransaction"] = eth.NewRPCFunc(svc.DebugTraceTransaction, "hash,config") return routes } diff --git a/store/block_store.go b/store/block_store.go index 2cb2d3d39d..9f0443be89 100644 --- a/store/block_store.go +++ b/store/block_store.go @@ -87,6 +87,7 @@ func (s *TendermintBlockStore) GetBlockByHeight(height *int64) (*ctypes.ResultBl LastBlockID: blockResult.Block.Header.LastBlockID, Time: blockResult.Block.Header.Time, ProposerAddress: blockResult.Block.Header.ProposerAddress, + ChainID: blockResult.Block.Header.ChainID, } blockMeta := types.BlockMeta{ BlockID: blockResult.BlockMeta.BlockID, diff --git a/store/iavlstore.go b/store/iavlstore.go index 253d6f5a00..87cf976e60 100755 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -216,6 +216,13 @@ func (s *IAVLStore) GetSnapshot() Snapshot { } } +func (s *IAVLStore) GetSnapshotAt(version int64) (Snapshot, error) { + // This isn't an actual snapshot obviously, and never will be, but lets pretend... + return &iavlStoreSnapshot{ + IAVLStore: s, + }, nil +} + // NewIAVLStore creates a new IAVLStore. // maxVersions can be used to specify how many versions should be retained, if set to zero then // old versions will never been deleted. diff --git a/store/logstore.go b/store/logstore.go index 2e14a6b981..4de507b068 100644 --- a/store/logstore.go +++ b/store/logstore.go @@ -129,3 +129,7 @@ func (s *LogStore) Prune() error { func (s *LogStore) GetSnapshot() Snapshot { return s.store.GetSnapshot() } + +func (s *LogStore) GetSnapshotAt(version int64) (Snapshot, error) { + return s.store.GetSnapshotAt(version) +} diff --git a/store/memstore.go b/store/memstore.go index badf02b59f..dfe89e143c 100644 --- a/store/memstore.go +++ b/store/memstore.go @@ -79,3 +79,7 @@ func (m *MemStore) Prune() error { func (m *MemStore) GetSnapshot() Snapshot { panic("not implemented") } + +func (m *MemStore) GetSnapshotAt(version int64) (Snapshot, error) { + panic("not implemented") +} diff --git a/store/multi_writer_app_store.go b/store/multi_writer_app_store.go index 598da8a296..db45b7b06e 100644 --- a/store/multi_writer_app_store.go +++ b/store/multi_writer_app_store.go @@ -119,7 +119,9 @@ func NewMultiWriterAppStore( store.onlySaveEvmStateToEvmStore = bytes.Equal(store.appStore.Get(evmDBFeatureKey), []byte{1}) } - store.setLastSavedTreeToVersion(appStore.Version()) + if err := store.setLastSavedTreeToVersion(appStore.Version()); err != nil { + return nil, err + } return store, nil } @@ -262,12 +264,30 @@ func (s *MultiWriterAppStore) Prune() error { } func (s *MultiWriterAppStore) GetSnapshot() Snapshot { + snapshot, err := s.GetSnapshotAt(0) + if err != nil { + panic(err) + } + return snapshot +} + +func (s *MultiWriterAppStore) GetSnapshotAt(version int64) (Snapshot, error) { defer func(begin time.Time) { getSnapshotDuration.Observe(time.Since(begin).Seconds()) }(time.Now()) - appStoreTree := (*iavl.ImmutableTree)(atomic.LoadPointer(&s.lastSavedTree)) + + var err error + var appStoreTree *iavl.ImmutableTree + if version == 0 { + appStoreTree = (*iavl.ImmutableTree)(atomic.LoadPointer(&s.lastSavedTree)) + } else { + appStoreTree, err = s.appStore.tree.GetImmutable(version) + if err != nil { + return nil, errors.Wrapf(err, "failed to load immutable tree for version %v", version) + } + } evmDbSnapshot := s.evmStore.GetSnapshot(appStoreTree.Version()) - return newMultiWriterStoreSnapshot(evmDbSnapshot, appStoreTree) + return newMultiWriterStoreSnapshot(evmDbSnapshot, appStoreTree), nil } type multiWriterStoreSnapshot struct { diff --git a/store/pruning_iavlstore.go b/store/pruning_iavlstore.go index 4e73cbad31..fe58887efc 100644 --- a/store/pruning_iavlstore.go +++ b/store/pruning_iavlstore.go @@ -178,10 +178,11 @@ func (s *PruningIAVLStore) Prune() error { } func (s *PruningIAVLStore) GetSnapshot() Snapshot { - // This isn't an actual snapshot obviously, and never will be, but lets pretend... - return &pruningIAVLStoreSnapshot{ - PruningIAVLStore: s, - } + return s.GetSnapshot() +} + +func (s *PruningIAVLStore) GetSnapshotAt(version int64) (Snapshot, error) { + return s.store.GetSnapshotAt(version) } func (s *PruningIAVLStore) prune() error { @@ -261,11 +262,3 @@ func (s *PruningIAVLStore) loopWithInterval(step func() error, interval time.Dur time.Sleep(interval) } } - -type pruningIAVLStoreSnapshot struct { - *PruningIAVLStore -} - -func (s *pruningIAVLStoreSnapshot) Release() { - // noop -} diff --git a/store/splitstore.go b/store/splitstore.go new file mode 100644 index 0000000000..493ed643cc --- /dev/null +++ b/store/splitstore.go @@ -0,0 +1,93 @@ +package store + +import ( + "github.com/loomnetwork/go-loom/plugin" +) + +type splitStore struct { + KVReader + VersionedKVStore + deleted map[string]bool + version int64 +} + +func NewSplitStore(full KVReader, empty VersionedKVStore, version int64) VersionedKVStore { + return &splitStore{ + KVReader: full, + VersionedKVStore: empty, + deleted: make(map[string]bool), + version: version, + } +} + +func (ss *splitStore) Get(key []byte) []byte { + if ss.VersionedKVStore.Has(key) { + return ss.KVReader.Get(key) + } + if ss.deleted[string(key)] { + return nil + } + return ss.KVReader.Get(key) +} + +func (ss *splitStore) Range(prefix []byte) plugin.RangeData { + readerRange := ss.KVReader.Range(prefix) + updateRange := ss.VersionedKVStore.Range(prefix) + for _, re := range updateRange { + if !ss.KVReader.Has(re.Key) && !ss.deleted[string(re.Key)] { + readerRange = append(readerRange, re) + } + } + return readerRange +} + +func (ss *splitStore) Has(key []byte) bool { + if ss.VersionedKVStore.Has(key) { + return true + } + if ss.deleted[string(key)] { + return false + } + return ss.KVReader.Has(key) +} + +func (ss *splitStore) Set(key, value []byte) { + ss.VersionedKVStore.Set(key, value) + ss.deleted[string(key)] = false +} + +func (ss *splitStore) Delete(key []byte) { + ss.VersionedKVStore.Delete(key) + ss.deleted[string(key)] = true +} + +func (ss *splitStore) Hash() []byte { + return []byte{} +} + +func (ss *splitStore) Version() int64 { + return ss.version +} + +func (ss *splitStore) SaveVersion() ([]byte, int64, error) { + ss.version++ + return ss.Hash(), ss.version, nil +} + +func (ss *splitStore) Prune() error { + return nil +} + +func (ss *splitStore) GetSnapshot() Snapshot { + return &splitStoreSnapShot{*ss} +} + +func (ss *splitStore) GetSnapshotAt(version int64) (Snapshot, error) { + panic("should not be called") +} + +type splitStoreSnapShot struct { + splitStore +} + +func (ss *splitStoreSnapShot) Release() {} diff --git a/store/store.go b/store/store.go index f2aaae4320..85e0c7fe72 100644 --- a/store/store.go +++ b/store/store.go @@ -54,6 +54,7 @@ type VersionedKVStore interface { // Delete old version of the store Prune() error GetSnapshot() Snapshot + GetSnapshotAt(version int64) (Snapshot, error) } type cacheItem struct { diff --git a/store/store_test.go b/store/store_test.go index a2c3841ef5..1d51792ed5 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -255,7 +255,7 @@ func (ts *StoreTestSuite) VerifyRange(s KVReader, prefixes [][]byte, entries []* } actual := s.Range(prefixes[0]) require.Len(actual, len(expected), ts.StoreName) - if ts.StoreName != "MemStore" { + if ts.StoreName != "MemStore" && ts.StoreName != "SplitStore" { for i := range expected { require.EqualValues(expected[i], actual[i], ts.StoreName) } @@ -279,7 +279,7 @@ func (ts *StoreTestSuite) VerifyRange(s KVReader, prefixes [][]byte, entries []* require.Len(actual, len(expected), ts.StoreName) // TODO: MemStore keys should be iterated in ascending order - if ts.StoreName != "MemStore" { + if ts.StoreName != "MemStore" && ts.StoreName != "SplitStore" { for i := range expected { require.EqualValues(expected[i], actual[i], ts.StoreName) } @@ -297,7 +297,7 @@ func (ts *StoreTestSuite) VerifyRange(s KVReader, prefixes [][]byte, entries []* } actual = s.Range(prefixes[2]) require.Len(actual, len(expected), ts.StoreName) - if ts.StoreName != "MemStore" { + if ts.StoreName != "MemStore" && ts.StoreName != "SplitStore" { for i := range expected { require.EqualValues(expected[i], actual[i], ts.StoreName) } @@ -438,6 +438,26 @@ func (ts *MemStoreTestSuite) SetupSuite() { ts.supportsSnapshots = false } +func TestSplitStoreTestSuite(t *testing.T) { + suite.Run(t, &SplitStoreTestSuite{}) +} + +type SplitStoreTestSuite struct { + KVReader + VersionedKVStore + StoreTestSuite +} + +// runs before each test in this suite +func (ts *SplitStoreTestSuite) SetupTest() { + ts.store = NewSplitStore(NewMemStore(), NewMemStore(), 1) +} + +func (ts *SplitStoreTestSuite) SetupSuite() { + ts.StoreName = "SplitStore" + ts.supportsSnapshots = false +} + // // PruningIAVLStore // diff --git a/store/versioned_cachingstore.go b/store/versioned_cachingstore.go index 8f9575efff..e7ad896228 100644 --- a/store/versioned_cachingstore.go +++ b/store/versioned_cachingstore.go @@ -12,7 +12,6 @@ import ( "github.com/go-kit/kit/metrics" kitprometheus "github.com/go-kit/kit/metrics/prometheus" loom "github.com/loomnetwork/go-loom" - "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" ) @@ -371,11 +370,23 @@ func (c *versionedCachingStore) SaveVersion() ([]byte, int64, error) { return hash, version, err } -func (c *versionedCachingStore) GetSnapshot() Snapshot { - return newVersionedCachingStoreSnapshot( - c.VersionedKVStore.GetSnapshot(), - c.cache, c.version-1, c.logger, - ) +func (s *versionedCachingStore) GetSnapshot() Snapshot { + snapshot, err := s.GetSnapshotAt(0) + if err != nil { + panic(err) + } + return snapshot +} + +func (c *versionedCachingStore) GetSnapshotAt(version int64) (Snapshot, error) { + if version == 0 { + return newVersionedCachingStoreSnapshot( + c.VersionedKVStore.GetSnapshot(), + c.cache, c.version-1, c.logger, + ), nil + } else { + return c.VersionedKVStore.GetSnapshotAt(version) + } } // CachingStoreSnapshot is a read-only CachingStore with specified version @@ -396,14 +407,6 @@ func newVersionedCachingStoreSnapshot(snapshot Snapshot, cache *versionedBigCach } } -func (c *versionedCachingStoreSnapshot) Delete(key []byte) { - panic("[versionedCachingStoreSnapshot] Delete() not implemented") -} - -func (c *versionedCachingStoreSnapshot) Set(key, val []byte) { - panic("[versionedCachingStoreSnapshot] Set() not implemented") -} - func (c *versionedCachingStoreSnapshot) Has(key []byte) bool { var err error @@ -489,14 +492,6 @@ func (c *versionedCachingStoreSnapshot) Get(key []byte) []byte { return data } -func (c *versionedCachingStoreSnapshot) SaveVersion() ([]byte, int64, error) { - return nil, 0, errors.New("[VersionedCachingStoreSnapshot] SaveVersion() not implemented") -} - -func (c *versionedCachingStoreSnapshot) Prune() error { - return errors.New("[VersionedCachingStoreSnapshot] Prune() not implemented") -} - func (c *versionedCachingStoreSnapshot) Release() { c.Snapshot.Release() } diff --git a/store/versioned_cachingstore_test.go b/store/versioned_cachingstore_test.go index 5345b77e5d..e00095e1a5 100644 --- a/store/versioned_cachingstore_test.go +++ b/store/versioned_cachingstore_test.go @@ -59,6 +59,14 @@ func (m *MockStore) Prune() error { } func (m *MockStore) GetSnapshot() Snapshot { + snapshot, err := m.GetSnapshotAt(0) + if err != nil { + panic(err) + } + return snapshot +} + +func (m *MockStore) GetSnapshotAt(version int64) (Snapshot, error) { snapshotStore := make(map[string][]byte) for k, v := range m.storage { snapshotStore[k] = v @@ -68,7 +76,7 @@ func (m *MockStore) GetSnapshot() Snapshot { } return &mockStoreSnapshot{ MockStore: mstore, - } + }, nil } type mockStoreSnapshot struct { diff --git a/tx_handler/factory/handler_factory.go b/tx_handler/factory/handler_factory.go new file mode 100644 index 0000000000..e629889435 --- /dev/null +++ b/tx_handler/factory/handler_factory.go @@ -0,0 +1,273 @@ +package factory + +import ( + ethvm "github.com/ethereum/go-ethereum/core/vm" + "github.com/gogo/protobuf/proto" + "github.com/loomnetwork/go-loom/plugin/contractpb" + "github.com/pkg/errors" + + "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/config" + "github.com/loomnetwork/loomchain/evm" + "github.com/loomnetwork/loomchain/migrations" + "github.com/loomnetwork/loomchain/plugin" + "github.com/loomnetwork/loomchain/registry/factory" + "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/throttle" + "github.com/loomnetwork/loomchain/tx_handler" + "github.com/loomnetwork/loomchain/vm" +) + +func NewTxHandlerFactory( + cfg config.Config, + vmManager *vm.Manager, + chainID string, + store store.VersionedKVStore, + createRegistry factory.RegistryFactoryFunc, +) loomchain.TxHandlerFactory { + return txHandleFactory{ + cfg: cfg, + vmManager: vmManager, + chainID: chainID, + store: store, + createRegistry: createRegistry, + } +} + +type txHandleFactory struct { + cfg config.Config + vmManager *vm.Manager + chainID string + store store.VersionedKVStore + createRegistry factory.RegistryFactoryFunc +} + +func (f txHandleFactory) Copy(newStore store.VersionedKVStore) loomchain.TxHandlerFactory { + return txHandleFactory{ + cfg: f.cfg, + vmManager: f.vmManager, + chainID: f.chainID, + store: newStore, + createRegistry: f.createRegistry, + } +} + +func (f txHandleFactory) TxHandler(tracer ethvm.Tracer, metrics bool) (loomchain.TxHandler, error) { + vmManager := createVmManager(f.vmManager, tracer) + nonceTxHandler := auth.NewNonceHandler() + + txMiddleware, err := txMiddleWare(f.cfg, vmManager, nonceTxHandler, f.chainID, f.store, metrics) + if err != nil { + return nil, err + } + postCommitMiddlewares, err := postCommitMiddleWAre(f.cfg, vmManager, nonceTxHandler) + if err != nil { + return nil, err + } + + return loomchain.MiddlewareTxHandler( + txMiddleware, + router(f.cfg, vmManager, f.createRegistry), + postCommitMiddlewares, + ), nil +} + +func createVmManager(vmManager *vm.Manager, tracer ethvm.Tracer) vm.Manager { + if tracer == nil && vmManager != nil { + return *vmManager + } + managerWithTracer := vm.NewManager() + managerWithTracer.Register(vm.VMType_EVM, func(_state loomchain.State) (vm.VM, error) { + var createABM evm.AccountBalanceManagerFactoryFunc + return evm.NewLoomVm(_state, nil, createABM, false, tracer), nil + }) + return *managerWithTracer +} + +func txMiddleWare( + cfg config.Config, + vmManager vm.Manager, + nonceTxHandler *auth.NonceHandler, + chainID string, + appStore store.VersionedKVStore, + metrics bool, +) ([]loomchain.TxMiddleware, error) { + txMiddleWare := []loomchain.TxMiddleware{ + loomchain.LogTxMiddleware, + loomchain.RecoveryTxMiddleware, + } + + txMiddleWare = append(txMiddleWare, auth.NewChainConfigMiddleware( + cfg.Auth, + getContractStaticCtx("addressmapper", vmManager), + )) + + if cfg.Karma.Enabled { + txMiddleWare = append(txMiddleWare, throttle.GetKarmaMiddleWare( + cfg.Karma.Enabled, + cfg.Karma.MaxCallCount, + cfg.Karma.SessionDuration, + getContractCtx("karma", vmManager), + )) + } + + if cfg.TxLimiter.Enabled { + txMiddleWare = append(txMiddleWare, throttle.NewTxLimiterMiddleware(cfg.TxLimiter)) + } + + if cfg.ContractTxLimiter.Enabled { + udwCtxFactory := getContractCtx("user-deployer-whitelist", vmManager) + txMiddleWare = append( + txMiddleWare, throttle.NewContractTxLimiterMiddleware(cfg.ContractTxLimiter, udwCtxFactory), + ) + } + + if cfg.DeployerWhitelist.ContractEnabled { + dwMiddleware, err := throttle.NewDeployerWhitelistMiddleware(getContractCtx("deployerwhitelist", vmManager)) + if err != nil { + return nil, err + } + txMiddleWare = append(txMiddleWare, dwMiddleware) + + } + + txMiddleWare = append(txMiddleWare, nonceTxHandler.TxMiddleware(appStore)) + + if cfg.GoContractDeployerWhitelist.Enabled { + goDeployers, err := cfg.GoContractDeployerWhitelist.DeployerAddresses(chainID) + if err != nil { + return nil, errors.Wrapf(err, "getting list of users allowed go deploys") + } + txMiddleWare = append(txMiddleWare, throttle.GetGoDeployTxMiddleWare(goDeployers)) + } + + if metrics { + txMiddleWare = append(txMiddleWare, loomchain.NewInstrumentingTxMiddleware()) + } + + return txMiddleWare, nil +} + +func router( + cfg config.Config, + vmManager vm.Manager, + createRegistry factory.RegistryFactoryFunc, +) loomchain.TxHandler { + router := loomchain.NewTxRouter() + isEvmTx := func(txID uint32, _ loomchain.State, txBytes []byte, isCheckTx bool) bool { + var msg vm.MessageTx + err := proto.Unmarshal(txBytes, &msg) + if err != nil { + return false + } + + switch txID { + case 1: + var tx vm.DeployTx + err = proto.Unmarshal(msg.Data, &tx) + if err != nil { + // In case of error, let's give safest response, + // let's TxHandler down the line, handle it. + return false + } + return tx.VmType == vm.VMType_EVM + case 2: + var tx vm.CallTx + err = proto.Unmarshal(msg.Data, &tx) + if err != nil { + // In case of error, let's give safest response, + // let's TxHandler down the line, handle it. + return false + } + return tx.VmType == vm.VMType_EVM + case 3: + return false + default: + return false + } + } + + deployTxHandler := &vm.DeployTxHandler{ + Manager: &vmManager, + CreateRegistry: createRegistry, + AllowNamedEVMContracts: cfg.AllowNamedEvmContracts, + } + + callTxHandler := &vm.CallTxHandler{ + Manager: &vmManager, + } + + ethTxHandler := &tx_handler.EthTxHandler{ + Manager: &vmManager, + CreateRegistry: createRegistry, + } + + migrationTxHandler := &tx_handler.MigrationTxHandler{ + Manager: &vmManager, + CreateRegistry: createRegistry, + Migrations: map[int32]tx_handler.MigrationFunc{ + 1: migrations.DPOSv3Migration, + 2: migrations.GatewayMigration, + 3: migrations.GatewayMigration, + }, + } + + router.HandleDeliverTx(1, loomchain.GeneratePassthroughRouteHandler(deployTxHandler)) + router.HandleDeliverTx(2, loomchain.GeneratePassthroughRouteHandler(callTxHandler)) + router.HandleDeliverTx(3, loomchain.GeneratePassthroughRouteHandler(migrationTxHandler)) + router.HandleDeliverTx(4, loomchain.GeneratePassthroughRouteHandler(ethTxHandler)) + + // TODO: Write this in more elegant way + router.HandleCheckTx(1, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, deployTxHandler)) + router.HandleCheckTx(2, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, callTxHandler)) + router.HandleCheckTx(3, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, migrationTxHandler)) + router.HandleCheckTx(4, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, ethTxHandler)) + + return router +} + +func postCommitMiddleWAre( + cfg config.Config, + vmManager vm.Manager, + nonceTxHandler *auth.NonceHandler, +) ([]loomchain.PostCommitMiddleware, error) { + postCommitMiddlewares := []loomchain.PostCommitMiddleware{ + loomchain.LogPostCommitMiddleware, + } + + if cfg.UserDeployerWhitelist.ContractEnabled { + udwContractFactory := getContractCtx("user-deployer-whitelist", vmManager) + evmDeployRecorderMiddleware, err := throttle.NewEVMDeployRecorderPostCommitMiddleware(udwContractFactory) + if err != nil { + return nil, err + } + postCommitMiddlewares = append(postCommitMiddlewares, evmDeployRecorderMiddleware) + } + + // We need to make sure nonce post commit middleware is last as + // it doesn't pass control to other middlewares after it. + postCommitMiddlewares = append(postCommitMiddlewares, nonceTxHandler.PostCommitMiddleware()) + + return postCommitMiddlewares, nil +} + +func getContractCtx(pluginName string, vmManager vm.Manager) func(_ loomchain.State) (contractpb.Context, error) { + return func(_state loomchain.State) (contractpb.Context, error) { + pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, _state) + if err != nil { + return nil, err + } + return plugin.NewInternalContractContext(pluginName, pvm.(*plugin.PluginVM), false) + } +} + +func getContractStaticCtx(pluginName string, vmManager vm.Manager) func(_ loomchain.State) (contractpb.StaticContext, error) { + return func(_state loomchain.State) (contractpb.StaticContext, error) { + pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, _state) + if err != nil { + return nil, err + } + return plugin.NewInternalContractContext(pluginName, pvm.(*plugin.PluginVM), true) + } +} From d2f26563d7b207f51c632f1d453cb53d877b1b21 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 13:02:47 +0000 Subject: [PATCH 02/39] external branch changes --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1342d60436..491a7a2cd5 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ GO_LOOM_GIT_REV = HEAD # Specifies the loomnetwork/transfer-gateway branch/revision to use. TG_GIT_REV = HEAD # loomnetwork/go-ethereum loomchain branch -ETHEREUM_GIT_REV = 1fb6138d017a4309105d91f187c126cf979c93f9 +ETHEREUM_GIT_REV = ethapi # use go-plugin we get 'timeout waiting for connection info' error HASHICORP_GIT_REV = f4c3476bd38585f9ec669d10ed1686abd52b9961 LEVIGO_GIT_REV = c42d9e0ca023e2198120196f842701bb4c55d7b9 @@ -44,7 +44,7 @@ BTCD_GIT_REV = 7d2daa5bfef28c5e282571bc06416516936115ee # google.golang.org/genproto seems to be pulled in by the grpc package. GENPROTO_GIT_REV = b515fa19cec88c32f305a962f34ae60068947aea # Specifies the loomnetwork/binance-tgoracle branch/revision to use. -BINANCE_TG_GIT_REV = HEAD +BINANCE_TG_GIT_REV = debugeth # Lock down certusone/yubihsm-go revision YUBIHSM_REV = 892fb9b370f3cbb486fc1f53d4a1d89e9f552af0 From ba6c0c5d36a6a5a53987cd73e39928be0db3e95a Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 13:34:35 +0000 Subject: [PATCH 03/39] remove comment, noevem --- cmd/loom/loom.go | 141 ----------------------------------------------- evm/noevm.go | 3 +- 2 files changed, 2 insertions(+), 142 deletions(-) diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index f4d6406817..df55c9db0a 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -839,31 +839,6 @@ func loadApp( } evm.LogEthDbBatch = cfg.LogEthDbBatch - //deployTxHandler := &vm.DeployTxHandler{ - // Manager: vmManager, - // CreateRegistry: createRegistry, - // AllowNamedEVMContracts: cfg.AllowNamedEvmContracts, - //} - - //callTxHandler := &vm.CallTxHandler{ - // Manager: vmManager, - //} - - //ethTxHandler := &tx_handler.EthTxHandler{ - // Manager: vmManager, - // CreateRegistry: createRegistry, - //} - - //migrationTxHandler := &tx_handler.MigrationTxHandler{ - // Manager: vmManager, - // CreateRegistry: createRegistry, - // Migrations: map[int32]tx_handler.MigrationFunc{ - // 1: migrations.DPOSv3Migration, - // 2: migrations.GatewayMigration, - // 3: migrations.GatewayMigration, - // }, - //} - gen, err := config.ReadGenesis(cfg.GenesisPath()) if err != nil { return nil, err @@ -897,107 +872,8 @@ func loadApp( return nil } - //router := loomchain.NewTxRouter() - /* - isEvmTx := func(txID uint32, state loomchain.State, txBytes []byte, isCheckTx bool) bool { - var msg vm.MessageTx - err := proto.Unmarshal(txBytes, &msg) - if err != nil { - return false - } - - switch txID { - case 1: - var tx vm.DeployTx - err = proto.Unmarshal(msg.Data, &tx) - if err != nil { - // In case of error, let's give safest response, - // let's TxHandler down the line, handle it. - return false - } - return tx.VmType == vm.VMType_EVM - case 2: - var tx vm.CallTx - err = proto.Unmarshal(msg.Data, &tx) - if err != nil { - // In case of error, let's give safest response, - // let's TxHandler down the line, handle it. - return false - } - return tx.VmType == vm.VMType_EVM - case 3: - return false - default: - return false - } - } - - router.HandleDeliverTx(1, loomchain.GeneratePassthroughRouteHandler(deployTxHandler)) - router.HandleDeliverTx(2, loomchain.GeneratePassthroughRouteHandler(callTxHandler)) - router.HandleDeliverTx(3, loomchain.GeneratePassthroughRouteHandler(migrationTxHandler)) - router.HandleDeliverTx(4, loomchain.GeneratePassthroughRouteHandler(ethTxHandler)) - - // TODO: Write this in more elegant way - router.HandleCheckTx(1, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, deployTxHandler)) - router.HandleCheckTx(2, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, callTxHandler)) - router.HandleCheckTx(3, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, migrationTxHandler)) - router.HandleCheckTx(4, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, ethTxHandler)) - */ - //txMiddleWare := []loomchain.TxMiddleware{ - // loomchain.LogTxMiddleware, - // loomchain.RecoveryTxMiddleware, - //} - - //postCommitMiddlewares := []loomchain.PostCommitMiddleware{ - // loomchain.LogPostCommitMiddleware, - //} - - //txMiddleWare = append(txMiddleWare, auth.NewChainConfigMiddleware( - // cfg.Auth, - // getContractStaticCtx("addressmapper", vmManager), - //)) - createKarmaContractCtx := getContractCtx("karma", vmManager) - //if cfg.Karma.Enabled { - // txMiddleWare = append(txMiddleWare, throttle.GetKarmaMiddleWare( - // cfg.Karma.Enabled, - // cfg.Karma.MaxCallCount, - // cfg.Karma.SessionDuration, - // createKarmaContractCtx, - // )) - //} - - //if cfg.TxLimiter.Enabled { - // txMiddleWare = append(txMiddleWare, throttle.NewTxLimiterMiddleware(cfg.TxLimiter)) - //} - - //if cfg.ContractTxLimiter.Enabled { - // contextFactory := getContractCtx("user-deployer-whitelist", vmManager) - // txMiddleWare = append( - // txMiddleWare, throttle.NewContractTxLimiterMiddleware(cfg.ContractTxLimiter, contextFactory), - // ) - //} - - //if cfg.DeployerWhitelist.ContractEnabled { - // contextFactory := getContractCtx("deployerwhitelist", vmManager) - // dwMiddleware, err := throttle.NewDeployerWhitelistMiddleware(contextFactory) - // if err != nil { - // return nil, err - // } - // txMiddleWare = append(txMiddleWare, dwMiddleware) - - //} - - //if cfg.UserDeployerWhitelist.ContractEnabled { - // contextFactory := getContractCtx("user-deployer-whitelist", vmManager) - // evmDeployRecorderMiddleware, err := throttle.NewEVMDeployRecorderPostCommitMiddleware(contextFactory) - // if err != nil { - // return nil, err - // } - // postCommitMiddlewares = append(postCommitMiddlewares, evmDeployRecorderMiddleware) - //} - createContractUpkeepHandler := func(state loomchain.State) (loomchain.KarmaHandler, error) { // TODO: This setting should be part of the config stored within the Karma contract itself, // that will allow us to switch the upkeep on & off via a tx. @@ -1045,19 +921,6 @@ func loadApp( return loom.NewValidatorSet(b.GenesisValidators()...), nil } - //nonceTxHandler := auth.NewNonceHandler() - //txMiddleWare = append(txMiddleWare, nonceTxHandler.TxMiddleware(appStore)) - - //if cfg.GoContractDeployerWhitelist.Enabled { - // goDeployers, err := cfg.GoContractDeployerWhitelist.DeployerAddresses(chainID) - // if err != nil { - // return nil, errors.Wrapf(err, "getting list of users allowed go deploys") - // } - // txMiddleWare = append(txMiddleWare, throttle.GetGoDeployTxMiddleWare(goDeployers)) - //} - - //txMiddleWare = append(txMiddleWare, loomchain.NewInstrumentingTxMiddleware()) - createValidatorsManager := func(state loomchain.State) (loomchain.ValidatorsManager, error) { pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) if err != nil { @@ -1112,10 +975,6 @@ func loadApp( } } - // We need to make sure nonce post commit middleware is last - // as it doesn't pass control to other middlewares after it. - //postCommitMiddlewares = append(postCommitMiddlewares, nonceTxHandler.PostCommitMiddleware()) - txHandlerFactory := factory.NewTxHandlerFactory(*cfg, vmManager, chainID, appStore, createRegistry) chainTxHandler, err := txHandlerFactory.TxHandler(nil, true) if err != nil { diff --git a/evm/noevm.go b/evm/noevm.go index 2d5a263c2f..1984cf3335 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -3,6 +3,7 @@ package evm import ( + "github.com/ethereum/go-ethereum/eth" "github.com/loomnetwork/loomchain" lvm "github.com/loomnetwork/loomchain/vm" ) @@ -15,7 +16,7 @@ var ( const EVMEnabled = false func NewLoomVm( - _ state.State, + _ loomchain.State, _ loomchain.WriteReceiptHandler, _ AccountBalanceManagerFactoryFunc, _ bool, From b24ae1f0b17d71c64571ec0c3d74aa924d0faf0b Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 13:36:07 +0000 Subject: [PATCH 04/39] noevem --- evm/noevm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/noevm.go b/evm/noevm.go index 1984cf3335..b08f6a3174 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -20,7 +20,7 @@ func NewLoomVm( _ loomchain.WriteReceiptHandler, _ AccountBalanceManagerFactoryFunc, _ bool, - _ *eth.TraceConfig, + _ interface{}, ) lvm.VM { return nil } From 9a17eba6df8e56a1478f16380d08c313e9fc0358 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 13:36:17 +0000 Subject: [PATCH 05/39] noevem --- evm/noevm.go | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/noevm.go b/evm/noevm.go index b08f6a3174..ce88d22501 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -3,7 +3,6 @@ package evm import ( - "github.com/ethereum/go-ethereum/eth" "github.com/loomnetwork/loomchain" lvm "github.com/loomnetwork/loomchain/vm" ) From 31b9eaf33f6c24320f385585b7d9c22428ee330d Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 14:20:05 +0000 Subject: [PATCH 06/39] touch --- rpc/debug/trace.go | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 01f8f1fe51..569fc21b0f 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -33,7 +33,6 @@ func TraceTransaction( if err := runUpTo(&app, blockstore, startBlockNumber, targetBlockNumber, txIndex); err != nil { return nil, err } - block, err := blockstore.GetBlockByHeight(&targetBlockNumber) if err != nil { return nil, errors.Wrapf(err, "getting block information at height %v", targetBlockNumber) From 25931d9d2d5eb6913c6909ab3de1ed87c5a901e0 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 14:42:36 +0000 Subject: [PATCH 07/39] tg branch --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 491a7a2cd5..975ae22e6f 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ BINANCE_TGORACLE_DIR=$(GOPATH)/src/$(PKG_BINANCE_TGORACLE) # specific commit. GO_LOOM_GIT_REV = HEAD # Specifies the loomnetwork/transfer-gateway branch/revision to use. -TG_GIT_REV = HEAD +TG_GIT_REV = debugeth # loomnetwork/go-ethereum loomchain branch ETHEREUM_GIT_REV = ethapi # use go-plugin we get 'timeout waiting for connection info' error From 5dad4a272ceafdde31e00f76c0bf095dec26baed Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 14:43:43 +0000 Subject: [PATCH 08/39] tg branch --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 975ae22e6f..c5bb295410 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ BTCD_GIT_REV = 7d2daa5bfef28c5e282571bc06416516936115ee # google.golang.org/genproto seems to be pulled in by the grpc package. GENPROTO_GIT_REV = b515fa19cec88c32f305a962f34ae60068947aea # Specifies the loomnetwork/binance-tgoracle branch/revision to use. -BINANCE_TG_GIT_REV = debugeth +BINANCE_TG_GIT_REV = HEAD # Lock down certusone/yubihsm-go revision YUBIHSM_REV = 892fb9b370f3cbb486fc1f53d4a1d89e9f552af0 From 621b9ef9305de56a69145ed39dddaa91ade063b3 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 15:27:54 +0000 Subject: [PATCH 09/39] unit tests, truffle test --- .../sample_go_contract_test.go | 2 +- e2e/tests/truffle/test/DebugFunctions.js | 57 +++++++++++++++++++ integration_tests/ethcoin_evm_test.go | 6 +- plugin/vm_test.go | 2 +- 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 e2e/tests/truffle/test/DebugFunctions.js diff --git a/builtin/plugins/sample_go_contract/sample_go_contract_test.go b/builtin/plugins/sample_go_contract/sample_go_contract_test.go index b125518992..5defad796f 100644 --- a/builtin/plugins/sample_go_contract/sample_go_contract_test.go +++ b/builtin/plugins/sample_go_contract/sample_go_contract_test.go @@ -59,7 +59,7 @@ func deployContractToEVM(ctx *plugin.FakeContextWithEVM, filename string, caller byteCode := common.FromHex(string(hexByteCode)) byteCode, err = hex.DecodeString(string(hexByteCode)) - vm := evm.NewLoomVm(ctx.State, nil, nil, nil, false) + vm := evm.NewLoomVm(ctx.State, nil, nil, false, nil) _, contractAddr, err = vm.Create(caller, byteCode, loom.NewBigUIntFromInt(0)) if err != nil { return contractAddr, err diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js new file mode 100644 index 0000000000..b63b046a0c --- /dev/null +++ b/e2e/tests/truffle/test/DebugFunctions.js @@ -0,0 +1,57 @@ +const Web3 = require('web3'); +const fs = require('fs'); +const path = require('path'); +const EthereumTx = require('ethereumjs-tx').Transaction; +const { getLoomEvmTxHash, waitForXBlocks } = require('./helpers'); +const { + SpeculativeNonceTxMiddleware, SignedTxMiddleware, Client, + LocalAddress, CryptoUtils, LoomProvider +} = require('loom-js'); +const TxHashTestContract = artifacts.require('TxHashTestContract'); + +contract('debug_traceTransaction', async (accounts) => { + let contract, fromAddr, nodeAddr, txHashTestContract, web3, web3js; + const StructLogsLength = 87; + beforeEach(async () => { + nodeAddr = fs.readFileSync(path.join(process.env.CLUSTER_DIR, '0', 'node_rpc_addr'), 'utf-8').trim() + const chainID = 'default'; + const writeUrl = `ws://${nodeAddr}/websocket`; + const readUrl = `ws://${nodeAddr}/queryws`; + + const privateKey = CryptoUtils.generatePrivateKey(); + const publicKey = CryptoUtils.publicKeyFromPrivateKey(privateKey); + + fromAddr = LocalAddress.fromPublicKey(publicKey); + const from = fromAddr.toString(); + + var client = new Client(chainID, writeUrl, readUrl); + client.on('error', msg => { + console.error('Error on connect to client', msg); + console.warn('Please verify if loom cluster is running') + }); + const setupMiddlewareFn = function(client, privateKey) { + const publicKey = CryptoUtils.publicKeyFromPrivateKey(privateKey); + return [new SpeculativeNonceTxMiddleware(publicKey, client), new SignedTxMiddleware(privateKey)] + }; + var loomProvider = new LoomProvider(client, privateKey, setupMiddlewareFn); + + web3 = new Web3(loomProvider); + web3js = new Web3(new Web3.providers.HttpProvider(`http://${nodeAddr}/eth`)); + + txHashTestContract = await TxHashTestContract.deployed(); + contract = new web3.eth.Contract(TxHashTestContract._json.abi, txHashTestContract.address, {from}); + }); + + it('Test debug_traceTransaction', async () => { + const txResult = await contract.methods.set(1111).send(); + await web3js.currentProvider.send({ + method: "debug_traceTransaction", + params: [txResult.transactionHash,{"disableStorage":true,"disableMemory":false,"disableStack":false,"fullStorage":false}], + jsonrpc: "2.0", + id: new Date().getTime() + }, function (error, result) { + assert.equal(StructLogsLength, result.result.structLogs.length); + }); + await waitForXBlocks(nodeAddr, 1) + }) +}); \ No newline at end of file diff --git a/integration_tests/ethcoin_evm_test.go b/integration_tests/ethcoin_evm_test.go index fbbd24b9f2..07557fee96 100644 --- a/integration_tests/ethcoin_evm_test.go +++ b/integration_tests/ethcoin_evm_test.go @@ -208,7 +208,7 @@ func (c *ethCoinIntegrationTestHelper) callEVM( if err != nil { return err } - vm := evm.NewLoomVm(ctx.State, nil, nil, ctx.AccountBalanceManager, false) + vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager, false, nil) _, err = vm.Call(ctx.Message().Sender, c.Address, input, loom.NewBigUIntFromInt(0)) if err != nil { return err @@ -223,7 +223,7 @@ func (c *ethCoinIntegrationTestHelper) staticCallEVM( if err != nil { return err } - vm := evm.NewLoomVm(ctx.State, nil, nil, ctx.AccountBalanceManager, false) + vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager, false, nil) output, err := vm.StaticCall(ctx.Message().Sender, c.Address, input) if err != nil { return err @@ -239,7 +239,7 @@ func deployContractToEVM(ctx *plugin.FakeContextWithEVM, filename string, caller } byteCode := common.FromHex(string(hexByteCode)) - vm := evm.NewLoomVm(ctx.State, nil, nil, nil, false) + vm := evm.NewLoomVm(ctx.State, nil, nil, false, nil) _, contractAddr, err = vm.Create(caller, byteCode, loom.NewBigUIntFromInt(0)) if err != nil { return contractAddr, err diff --git a/plugin/vm_test.go b/plugin/vm_test.go index f089fab0e3..02711b2bb0 100644 --- a/plugin/vm_test.go +++ b/plugin/vm_test.go @@ -142,7 +142,7 @@ func TestPluginVMContractContextCaller(t *testing.T) { require.NoError(t, err) vm := NewPluginVM(loader, state, createRegistry(state), &fakeEventHandler{}, nil, nil, nil, nil) - evm := levm.NewLoomVm(state, nil, nil, nil, false) + evm := levm.NewLoomVm(state, nil, nil, false, nil) // Deploy contracts owner := loom.RootAddress("chain") From 7eef46aedbb1ef75cb2dc95279ef213182e751ac Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 15:38:24 +0000 Subject: [PATCH 10/39] unit tests --- rpc/mock_query_server.go | 8 ++++++++ rpc/query_server_test.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/rpc/mock_query_server.go b/rpc/mock_query_server.go index d3eed35700..f47260fbaf 100644 --- a/rpc/mock_query_server.go +++ b/rpc/mock_query_server.go @@ -9,6 +9,7 @@ import ( "github.com/loomnetwork/go-loom/plugin/types" "github.com/loomnetwork/loomchain/config" + "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/rpc/eth" "github.com/loomnetwork/loomchain/vm" ) @@ -368,3 +369,10 @@ func (m *MockQueryService) EvmUnSubscribe(id string) (bool, error) { m.MethodsCalled = append([]string{"EvmUnSubscribe"}, m.MethodsCalled...) return true, nil } + +func (m *MockQueryService) DebugTraceTransaction(hash eth.Data, config *debug.JsonTraceConfig) (interface{}, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.MethodsCalled = append([]string{"DebugTraceTransaction"}, m.MethodsCalled...) + return nil, nil +} diff --git a/rpc/query_server_test.go b/rpc/query_server_test.go index 2939f703cf..db74bb93ef 100644 --- a/rpc/query_server_test.go +++ b/rpc/query_server_test.go @@ -101,6 +101,10 @@ func (s *stateProvider) ReadOnlyState() loomchain.State { ) } +func (s *stateProvider) ReplayApplication(uint64, store.BlockStore) (*loomchain.Application, int64, error) { + return nil, 0, fmt.Errorf("Not implimented") +} + var testlog llog.TMLogger func TestQueryServer(t *testing.T) { From 117a17c330bceb111efc49d9911a62a8870c93d7 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 7 Nov 2019 16:23:39 +0000 Subject: [PATCH 11/39] try increase sleep --- e2e/tests/receipts/run_truffle_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/receipts/run_truffle_tests.sh b/e2e/tests/receipts/run_truffle_tests.sh index c176227a81..2591bdd9c1 100755 --- a/e2e/tests/receipts/run_truffle_tests.sh +++ b/e2e/tests/receipts/run_truffle_tests.sh @@ -17,7 +17,7 @@ if [[ "$OSTYPE" == "darwin"* ]] && [[ "$NODE_NAME" == "osx"* ]]; then # Jenkins OSX machine is slugish so give it more time to spin up the test cluster. sleep 5 else - sleep 1 + sleep 3 fi # Run Truffle tests using Truffle HDWallet provider & /eth endpoint From df9684613bd223e994fb2ca8011c36b411a29c20 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Fri, 8 Nov 2019 11:47:13 +0000 Subject: [PATCH 12/39] review issues --- rpc/query_server_test.go | 2 +- store/versioned_cachingstore.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/rpc/query_server_test.go b/rpc/query_server_test.go index db74bb93ef..d2862884e2 100644 --- a/rpc/query_server_test.go +++ b/rpc/query_server_test.go @@ -102,7 +102,7 @@ func (s *stateProvider) ReadOnlyState() loomchain.State { } func (s *stateProvider) ReplayApplication(uint64, store.BlockStore) (*loomchain.Application, int64, error) { - return nil, 0, fmt.Errorf("Not implimented") + return nil, 0, fmt.Errorf("Not implemented") } var testlog llog.TMLogger diff --git a/store/versioned_cachingstore.go b/store/versioned_cachingstore.go index e7ad896228..2436dbca8d 100644 --- a/store/versioned_cachingstore.go +++ b/store/versioned_cachingstore.go @@ -385,7 +385,10 @@ func (c *versionedCachingStore) GetSnapshotAt(version int64) (Snapshot, error) { c.cache, c.version-1, c.logger, ), nil } else { - return c.VersionedKVStore.GetSnapshotAt(version) + return newVersionedCachingStoreSnapshot( + c.VersionedKVStore.GetSnapshot(), + c.cache, version, c.logger, + ), nil } } From 8deefbd0b0314aa71d58bc8d1f8ec679e22e9a70 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 11 Nov 2019 15:24:09 +0000 Subject: [PATCH 13/39] reeview issues, --- .../sample_go_contract_test.go | 2 +- cmd/loom/db/evm.go | 4 +- cmd/loom/loom.go | 20 +++++-- config/config.go | 43 ++++++++++++--- config/config_test.go | 2 + config/loom.example.yaml | 9 ++++ evm/evm.go | 53 +++++++------------ evm/loomevm.go | 44 ++++++++------- integration_tests/ethcoin_evm_test.go | 6 +-- plugin/fake_context.go | 4 +- plugin/vm.go | 4 +- plugin/vm_test.go | 2 +- rpc/debug/trace.go | 6 +-- rpc/query_server.go | 8 +-- tx_handler/factory/handler_factory.go | 5 +- 15 files changed, 125 insertions(+), 87 deletions(-) diff --git a/builtin/plugins/sample_go_contract/sample_go_contract_test.go b/builtin/plugins/sample_go_contract/sample_go_contract_test.go index 5defad796f..4e3ea74821 100644 --- a/builtin/plugins/sample_go_contract/sample_go_contract_test.go +++ b/builtin/plugins/sample_go_contract/sample_go_contract_test.go @@ -59,7 +59,7 @@ func deployContractToEVM(ctx *plugin.FakeContextWithEVM, filename string, caller byteCode := common.FromHex(string(hexByteCode)) byteCode, err = hex.DecodeString(string(hexByteCode)) - vm := evm.NewLoomVm(ctx.State, nil, nil, false, nil) + vm := evm.NewLoomVm(ctx.State, nil, nil) _, contractAddr, err = vm.Create(caller, byteCode, loom.NewBigUIntFromInt(0)) if err != nil { return contractAddr, err diff --git a/cmd/loom/db/evm.go b/cmd/loom/db/evm.go index 2d1bb0969a..f6c4a95013 100644 --- a/cmd/loom/db/evm.go +++ b/cmd/loom/db/evm.go @@ -108,7 +108,7 @@ func newDumpEVMStateCommand() *cobra.Command { } } - vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false, nil) + vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, nil) if err != nil { return err } @@ -221,7 +221,7 @@ func newDumpEVMStateMultiWriterAppStoreCommand() *cobra.Command { } } - vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, false, nil) + vm, err := evm.NewLoomEvm(state, accountBalanceManager, nil, nil) if err != nil { return err } diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index df55c9db0a..15aa7df66c 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -16,9 +16,7 @@ import ( "syscall" "time" - "github.com/prometheus/client_golang/prometheus/push" - "github.com/tendermint/tendermint/libs/db" - + ethvm "github.com/ethereum/go-ethereum/core/vm" kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" @@ -29,6 +27,8 @@ import ( "github.com/loomnetwork/go-loom/crypto" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/util" + "github.com/prometheus/client_golang/prometheus/push" + "github.com/tendermint/tendermint/libs/db" "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/abci/backend" @@ -38,6 +38,7 @@ import ( plasmaOracle "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/oracle" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/receipts/leveldb" + "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/tx_handler/factory" "github.com/prometheus/client_golang/prometheus" @@ -834,7 +835,7 @@ func loadApp( return nil, err } } - return evm.NewLoomVm(state, receiptHandlerProvider.Writer(), createABM, cfg.EVMDebugEnabled, nil), nil + return evm.NewLoomVm(state, receiptHandlerProvider.Writer(), createABM), nil }) } evm.LogEthDbBatch = cfg.LogEthDbBatch @@ -975,8 +976,17 @@ func loadApp( } } + tracer := ethvm.Tracer(nil) + if cfg.EVMTracer.Enabled { + // todo should we force cfg.EVMTracer.Debug to be true here + tracer, err = debug.CreateTracer(cfg.EVMTracer.TraceConfig) + if err != nil { + return nil, errors.Wrapf(err, "creating tracer from %v", cfg.EVMTracer) + } + } + txHandlerFactory := factory.NewTxHandlerFactory(*cfg, vmManager, chainID, appStore, createRegistry) - chainTxHandler, err := txHandlerFactory.TxHandler(nil, true) + chainTxHandler, err := txHandlerFactory.TxHandler(tracer, true) if err != nil { return nil, err } diff --git a/config/config.go b/config/config.go index 73115af127..063ec68b58 100755 --- a/config/config.go +++ b/config/config.go @@ -1,3 +1,5 @@ +// +build evm + package config import ( @@ -10,6 +12,10 @@ import ( "path" "path/filepath" + etheth "github.com/ethereum/go-ethereum/eth" + "github.com/pkg/errors" + "github.com/spf13/viper" + "github.com/loomnetwork/loomchain/auth" plasmacfg "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/config" genesiscfg "github.com/loomnetwork/loomchain/config/genesis" @@ -22,8 +28,6 @@ import ( "github.com/loomnetwork/loomchain/store" blockindex "github.com/loomnetwork/loomchain/store/block_index" "github.com/loomnetwork/loomchain/throttle" - "github.com/pkg/errors" - "github.com/spf13/viper" "github.com/loomnetwork/loomchain/db" @@ -144,7 +148,7 @@ type Config struct { AllowNamedEvmContracts bool // Dragons - EVMDebugEnabled bool + EVMTracer *EVMTracer // Set to true to disable minimum required build number check on node startup SkipMinBuildCheck bool @@ -169,6 +173,17 @@ func DefaultFnConsensusConfig() *FnConsensusConfig { } } +type EVMTracer struct { + etheth.TraceConfig + Enabled bool // enable tracer +} + +func DefaultEvmTraceConfig() *EVMTracer { + return &EVMTracer{ + Enabled: false, + } +} + type DBBackendConfig struct { CacheSizeMegs int WriteBufferMegs int @@ -389,7 +404,6 @@ func DefaultConfig() *Config { EVMPersistentTxReceiptsMax: receipts.DefaultMaxReceipts, SessionDuration: 600, EVMAccountsEnabled: false, - EVMDebugEnabled: false, SampleGoContractEnabled: false, Oracle: "", @@ -424,7 +438,7 @@ func DefaultConfig() *Config { cfg.EventStore = events.DefaultEventStoreConfig() cfg.EvmStore = evm.DefaultEvmStoreConfig() cfg.Web3 = eth.DefaultWeb3Config() - + cfg.EVMTracer = DefaultEvmTraceConfig() cfg.FnConsensus = DefaultFnConsensusConfig() cfg.Auth = auth.DefaultConfig() @@ -790,7 +804,24 @@ PluginsDir: "{{ .PluginsDir }}" # # Here be dragons, don't change the defaults unless you know what you're doing # -EVMDebugEnabled: {{ .EVMDebugEnabled }} +{{- if .EVMTracer }} +EVMTracer: + Enabled: {{ .EVMTracer.Enabled }} + {{- if .EVMTracer.Tracer }} + Tracer: "{{ .EVMTracer.Tracer }}" //enable JavaScript-based transaction tracing LogConfig is ignored if not empty + {{- end}} + {{- if .EVMTracer.Timeout }} + Timeout: "{{ .EVMTracer.Timeout }}" //Not used + {{- end}} + {{- if .EVMTracer.LogConfig }} + LogConfig: + DisableMemory: {{ .EVMTracer.LogConfig.DisableMemory }} // disable memory capture + DisableStack: {{ .EVMTracer.LogConfig.DisableStack }} // disable stack capture + DisableStorage: {{ .EVMTracer.LogConfig.DisableStorage }} // disable storage capture + Debug: {{ .EVMTracer.LogConfig.Debug }} // print output during capture end. set to true to have an effect + Limit: {{ .EVMTracer.LogConfig.Debug }} // maximum length of output, but zero means unlimited + {{- end}} +{{- end}} AllowNamedEvmContracts: {{ .AllowNamedEvmContracts }} # Set to true to disable minimum required build number check on node startup SkipMinBuildCheck: {{ .SkipMinBuildCheck }} diff --git a/config/config_test.go b/config/config_test.go index 6aa34a77e0..a37e2bec5b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,3 +1,5 @@ +// +build evm + package config import ( diff --git a/config/loom.example.yaml b/config/loom.example.yaml index 2cec2c8de3..4c854dac45 100644 --- a/config/loom.example.yaml +++ b/config/loom.example.yaml @@ -83,6 +83,15 @@ DPOSv2OracleConfig: # ReadURI: "tcp://0.0.0.1" # PrivateKeyPath: "tcp://0.0.0.2" +EVMTracer: + Enabled: true + Timeout: "79" + LogConfig: + DisableMemory: true + DisableStack: false + DisableStorage: ture + Limit: 1000 + EventDispatcher: Dispatcher: log diff --git a/evm/evm.go b/evm/evm.go index 09e235f1e3..55985c308e 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -21,7 +21,6 @@ import ( "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/features" - "github.com/loomnetwork/loomchain/log" ) // EVMEnabled indicates whether or not Loom EVM integration is available @@ -138,7 +137,7 @@ func (m *evmAccountBalanceManager) Transfer(from, to common.Address, amount *big } // TODO: this shouldn't be exported, rename to wrappedEVM -type Evm struct { +type wrappedEVM struct { sdb vm.StateDB context vm.Context chainConfig params.ChainConfig @@ -148,9 +147,9 @@ type Evm struct { } func NewEvm( - sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManager, debug bool, tracer vm.Tracer, -) (*Evm, error) { - p := new(Evm) + sdb vm.StateDB, lstate loomchain.State, abm *evmAccountBalanceManager, tracer vm.Tracer, +) (*wrappedEVM, error) { + p := new(wrappedEVM) p.sdb = sdb p.gasLimit = lstate.Config().GetEvm().GetGasLimit() if p.gasLimit == 0 { @@ -160,7 +159,7 @@ func NewEvm( p.chainConfig = defaultChainConfig(lstate.FeatureEnabled(features.EvmConstantinopleFeature, false)) var err error - p.vmConfig, err = createVmConfig(debug, tracer) + p.vmConfig, err = createVmConfig(tracer) if err != nil { return nil, errors.Wrap(err, "creating vm.Config") } @@ -189,7 +188,7 @@ func NewEvm( return p, nil } -func (e Evm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]byte, loom.Address, error) { +func (e wrappedEVM) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]byte, loom.Address, error) { var err error var usedGas uint64 defer func(begin time.Time) { @@ -200,7 +199,7 @@ func (e Evm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]by }(time.Now()) origin := common.BytesToAddress(caller.Local) - vmenv := e.NewEnv(origin) + vmenv := e.newEnv(origin) var val *big.Int if value == nil { @@ -221,7 +220,7 @@ func (e Evm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]by return runCode, loomAddress, err } -func (e Evm) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { +func (e wrappedEVM) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { var err error var usedGas uint64 defer func(begin time.Time) { @@ -233,7 +232,7 @@ func (e Evm) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) }(time.Now()) origin := common.BytesToAddress(caller.Local) contract := common.BytesToAddress(addr.Local) - vmenv := e.NewEnv(origin) + vmenv := e.newEnv(origin) var val *big.Int if value == nil { @@ -253,25 +252,25 @@ func (e Evm) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) return ret, err } -func (e Evm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { +func (e wrappedEVM) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { origin := common.BytesToAddress(caller.Local) contract := common.BytesToAddress(addr.Local) - vmenv := e.NewEnv(origin) + vmenv := e.newEnv(origin) ret, _, err := vmenv.StaticCall(vm.AccountRef(origin), contract, input, e.gasLimit) return ret, err } -func (e Evm) GetCode(addr loom.Address) []byte { +func (e wrappedEVM) GetCode(addr loom.Address) []byte { return e.sdb.GetCode(common.BytesToAddress(addr.Local)) } -func (e Evm) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { +func (e wrappedEVM) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { result := e.sdb.GetState(common.BytesToAddress(addr.Local), common.BytesToHash(key)) return result.Bytes(), nil } // TODO: this doesn't need to be exported, rename to newEVM -func (e Evm) NewEnv(origin common.Address) *vm.EVM { +func (e wrappedEVM) newEnv(origin common.Address) *vm.EVM { e.context.Origin = origin return vm.NewEVM(e.context, e.sdb, &e.chainConfig, e.vmConfig) } @@ -305,29 +304,18 @@ func defaultChainConfig(enableConstantinople bool) params.ChainConfig { } } -func defaultVmConfig(evmDebuggingEnabled bool) vm.Config { +func defaultVmConfig() vm.Config { logCfg := vm.LogConfig{ DisableMemory: true, // disable memory capture DisableStack: true, // disable stack capture DisableStorage: true, // disable storage capture Limit: 0, // maximum length of output, but zero means unlimited } - debug := false - if evmDebuggingEnabled { - log.Error("WARNING!!!! EVM Debug mode enabled, do NOT run this on a production server!!!") - logCfg = vm.LogConfig{ - DisableMemory: true, // disable memory capture - DisableStack: true, // disable stack capture - DisableStorage: true, // disable storage capture - Limit: 0, // maximum length of output, but zero means unlimited - } - debug = true - } logger := vm.NewStructLogger(&logCfg) return vm.Config{ // Debug enabled debugging Interpreter options - Debug: debug, + Debug: false, // Tracer is the op code logger Tracer: logger, // NoRecursion disabled Interpreter call, callcode, @@ -342,11 +330,7 @@ func defaultVmConfig(evmDebuggingEnabled bool) vm.Config { } } -func createVmConfig(evmDebuggingEnabled bool, tracer vm.Tracer) (vm.Config, error) { - if evmDebuggingEnabled { - log.Error("WARNING!!!! EVM Debug mode enabled, do NOT run this on a production server!!!") - } - +func createVmConfig(tracer vm.Tracer) (vm.Config, error) { if tracer == nil { logCfg := vm.LogConfig{ DisableMemory: true, // disable memory capture @@ -356,9 +340,10 @@ func createVmConfig(evmDebuggingEnabled bool, tracer vm.Tracer) (vm.Config, erro } tracer = vm.NewStructLogger(&logCfg) } + return vm.Config{ // Debug enabled debugging Interpreter options - Debug: evmDebuggingEnabled || (tracer != nil), + Debug: tracer != nil, // Tracer is the op code logger Tracer: tracer, // NoRecursion disabled Interpreter call, callcode, diff --git a/evm/loomevm.go b/evm/loomevm.go index 79ed0ac416..619d6400dd 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -45,21 +45,21 @@ type ethdbLogContext struct { } // TODO: this doesn't need to be exported, rename to loomEvmWithState -type LoomEvm struct { - *Evm +type loomEvmWithState struct { + *wrappedEVM db ethdb.Database sdb StateDB } -// TODO: this doesn't need to be exported, rename to newLoomEvmWithState +// TODO: this doesn't need to be exported, rename to newLoomEvmWithState. +// NewLoomEvm is exported to db.newDumpEVMStateMultiWriterAppStoreCommand. func NewLoomEvm( loomState loomchain.State, accountBalanceManager AccountBalanceManager, logContext *ethdbLogContext, - debug bool, tracer ethvm.Tracer, -) (*LoomEvm, error) { - p := new(LoomEvm) +) (*loomEvmWithState, error) { + p := new(loomEvmWithState) p.db = NewLoomEthdb(loomState, logContext) oldRoot, err := p.db.Get(rootKey) if err != nil { @@ -77,14 +77,14 @@ func NewLoomEvm( return nil, err } - p.Evm, err = NewEvm(p.sdb, loomState, abm, debug, tracer) + p.wrappedEVM, err = NewEvm(p.sdb, loomState, abm, tracer) if err != nil { return nil, errors.Wrap(err, "creating tracer") } return p, nil } -func (levm LoomEvm) Commit() (common.Hash, error) { +func (levm loomEvmWithState) Commit() (common.Hash, error) { root, err := levm.sdb.Commit(true) if err != nil { return root, err @@ -98,7 +98,7 @@ func (levm LoomEvm) Commit() (common.Hash, error) { return root, err } -func (levm LoomEvm) RawDump() []byte { +func (levm loomEvmWithState) RawDump() []byte { d := levm.sdb.RawDump() output, err := json.MarshalIndent(d, "", " ") if err != nil { @@ -108,8 +108,6 @@ func (levm LoomEvm) RawDump() []byte { } var LoomVmFactory = func(state loomchain.State) (vm.VM, error) { - //TODO , debug bool, We should be able to pass in config - debug := false eventHandler := loomchain.NewDefaultEventHandler(events.NewLogEventDispatcher()) receiptHandlerProvider := receipts.NewReceiptHandlerProvider( eventHandler, @@ -117,7 +115,7 @@ var LoomVmFactory = func(state loomchain.State) (vm.VM, error) { nil, ) receiptHandler := receiptHandlerProvider.Writer() - return NewLoomVm(state, receiptHandler, nil, debug, nil), nil + return NewLoomVm(state, receiptHandler, nil), nil } // LoomVm implements the loomchain/vm.VM interface using the EVM. @@ -126,7 +124,6 @@ type LoomVm struct { state loomchain.State receiptHandler loomchain.WriteReceiptHandler createABM AccountBalanceManagerFactoryFunc - debug bool tracer ethvm.Tracer } @@ -134,18 +131,19 @@ func NewLoomVm( loomState loomchain.State, receiptHandler loomchain.WriteReceiptHandler, createABM AccountBalanceManagerFactoryFunc, - debug bool, - tracer ethvm.Tracer, -) vm.VM { +) *LoomVm { return &LoomVm{ state: loomState, receiptHandler: receiptHandler, createABM: createABM, - debug: debug, - tracer: tracer, } } +func (lvm LoomVm) WithTracer(tracer ethvm.Tracer) LoomVm { + lvm.tracer = tracer + return lvm +} + func (lvm LoomVm) accountBalanceManager(readOnly bool) AccountBalanceManager { if lvm.createABM == nil { return nil @@ -159,7 +157,7 @@ func (lvm LoomVm) Create(caller loom.Address, code []byte, value *loom.BigUInt) contractAddr: loom.Address{}, callerAddr: caller, } - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug, lvm.tracer) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.tracer) if err != nil { return nil, loom.Address{}, err } @@ -222,7 +220,7 @@ func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigU contractAddr: addr, callerAddr: caller, } - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.debug, lvm.tracer) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(false), logContext, lvm.tracer) if err != nil { return nil, err } @@ -270,7 +268,7 @@ func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigU } func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(true), nil, lvm.debug, lvm.tracer) + levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(true), nil, lvm.tracer) if err != nil { return nil, err } @@ -278,7 +276,7 @@ func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, e } func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug, lvm.tracer) + levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.tracer) if err != nil { return nil, err } @@ -286,7 +284,7 @@ func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { } func (lvm LoomVm) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { - levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.debug, lvm.tracer) + levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.tracer) if err != nil { return nil, err } diff --git a/integration_tests/ethcoin_evm_test.go b/integration_tests/ethcoin_evm_test.go index 07557fee96..7a657a4cc3 100644 --- a/integration_tests/ethcoin_evm_test.go +++ b/integration_tests/ethcoin_evm_test.go @@ -208,7 +208,7 @@ func (c *ethCoinIntegrationTestHelper) callEVM( if err != nil { return err } - vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager, false, nil) + vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager) _, err = vm.Call(ctx.Message().Sender, c.Address, input, loom.NewBigUIntFromInt(0)) if err != nil { return err @@ -223,7 +223,7 @@ func (c *ethCoinIntegrationTestHelper) staticCallEVM( if err != nil { return err } - vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager, false, nil) + vm := evm.NewLoomVm(ctx.State, nil, ctx.AccountBalanceManager) output, err := vm.StaticCall(ctx.Message().Sender, c.Address, input) if err != nil { return err @@ -239,7 +239,7 @@ func deployContractToEVM(ctx *plugin.FakeContextWithEVM, filename string, caller } byteCode := common.FromHex(string(hexByteCode)) - vm := evm.NewLoomVm(ctx.State, nil, nil, false, nil) + vm := evm.NewLoomVm(ctx.State, nil, nil) _, contractAddr, err = vm.Create(caller, byteCode, loom.NewBigUIntFromInt(0)) if err != nil { return contractAddr, err diff --git a/plugin/fake_context.go b/plugin/fake_context.go index 5acaec3315..d80e664991 100644 --- a/plugin/fake_context.go +++ b/plugin/fake_context.go @@ -103,7 +103,7 @@ func (c *FakeContextWithEVM) CallEVM(addr loom.Address, input []byte, value *loo if c.useAccountBalanceManager { createABM = c.AccountBalanceManager } - vm := levm.NewLoomVm(c.State, nil, createABM, false, nil) + vm := levm.NewLoomVm(c.State, nil, createABM) return vm.Call(c.ContractAddress(), addr, input, value) } @@ -112,7 +112,7 @@ func (c *FakeContextWithEVM) StaticCallEVM(addr loom.Address, input []byte) ([]b if c.useAccountBalanceManager { createABM = c.AccountBalanceManager } - vm := levm.NewLoomVm(c.State, nil, createABM, false, nil) + vm := levm.NewLoomVm(c.State, nil, createABM) return vm.StaticCall(c.ContractAddress(), addr, input) } diff --git a/plugin/vm.go b/plugin/vm.go index cca4c95715..4320a005b5 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -196,7 +196,7 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) return evm.Call(caller, addr, input, value) } @@ -209,7 +209,7 @@ func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]by return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) return evm.StaticCall(caller, addr, input) } diff --git a/plugin/vm_test.go b/plugin/vm_test.go index 02711b2bb0..0d50d21ca4 100644 --- a/plugin/vm_test.go +++ b/plugin/vm_test.go @@ -142,7 +142,7 @@ func TestPluginVMContractContextCaller(t *testing.T) { require.NoError(t, err) vm := NewPluginVM(loader, state, createRegistry(state), &fakeEventHandler{}, nil, nil, nil, nil) - evm := levm.NewLoomVm(state, nil, nil, false, nil) + evm := levm.NewLoomVm(state, nil, nil) // Deploy contracts owner := loom.RootAddress("chain") diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 569fc21b0f..224beac9e2 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -38,7 +38,7 @@ func TraceTransaction( return nil, errors.Wrapf(err, "getting block information at height %v", targetBlockNumber) } - tracer, err := createTracer(config) + tracer, err := CreateTracer(config) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func TraceTransaction( switch tracer := tracer.(type) { case *vm.StructLogger: return ðapi.ExecutionResult{ - Gas: 5, + Gas: 0, Failed: result.Code != abci.CodeTypeOK, ReturnValue: fmt.Sprintf("%x", result), StructLogs: ethapi.FormatLogs(tracer.StructLogs()), @@ -105,7 +105,7 @@ func requestEndBlock(height int64) abci.RequestEndBlock { } } -func createTracer(traceCfg eth.TraceConfig) (vm.Tracer, error) { +func CreateTracer(traceCfg eth.TraceConfig) (vm.Tracer, error) { if traceCfg.Tracer == nil || len(*traceCfg.Tracer) == 0 { return vm.NewStructLogger(traceCfg.LogConfig), nil } diff --git a/rpc/query_server.go b/rpc/query_server.go index 6d6eedc2a0..f5fa7d4ffc 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -270,7 +270,7 @@ func (s *QueryServer) queryEvm(state loomchain.State, caller, contract loom.Addr return nil, err } } - vm := levm.NewLoomVm(state, nil, createABM, false, nil) + vm := levm.NewLoomVm(state, nil, createABM) return vm.StaticCall(callerAddr, contract, query) } @@ -315,7 +315,7 @@ func (s *QueryServer) GetEvmCode(contract string) ([]byte, error) { snapshot := s.StateProvider.ReadOnlyState() defer snapshot.Release() - vm := levm.NewLoomVm(snapshot, nil, nil, false, nil) + vm := levm.NewLoomVm(snapshot, nil, nil) return vm.GetCode(contractAddr) } @@ -329,7 +329,7 @@ func (s *QueryServer) EthGetCode(address eth.Data, block eth.BlockHeight) (eth.D snapshot := s.StateProvider.ReadOnlyState() defer snapshot.Release() - evm := levm.NewLoomVm(snapshot, nil, nil, false, nil) + evm := levm.NewLoomVm(snapshot, nil, nil) code, err := evm.GetCode(addr) if err != nil { return "", errors.Wrapf(err, "getting evm code for %v", address) @@ -1104,7 +1104,7 @@ func (s *QueryServer) EthGetStorageAt(local eth.Data, position string, block eth return "", errors.Wrapf(err, "unable to get storage at height %v", block) } - evm := levm.NewLoomVm(snapshot, nil, nil, false, nil) + evm := levm.NewLoomVm(snapshot, nil, nil) storage, err := evm.GetStorageAt(address, ethcommon.HexToHash(position).Bytes()) if err != nil { return "", errors.Wrapf(err, "failed to get EVM storage at %v", address.Local.String()) diff --git a/tx_handler/factory/handler_factory.go b/tx_handler/factory/handler_factory.go index e629889435..72651454ac 100644 --- a/tx_handler/factory/handler_factory.go +++ b/tx_handler/factory/handler_factory.go @@ -1,3 +1,5 @@ +// +build evm + package factory import ( @@ -80,7 +82,8 @@ func createVmManager(vmManager *vm.Manager, tracer ethvm.Tracer) vm.Manager { managerWithTracer := vm.NewManager() managerWithTracer.Register(vm.VMType_EVM, func(_state loomchain.State) (vm.VM, error) { var createABM evm.AccountBalanceManagerFactoryFunc - return evm.NewLoomVm(_state, nil, createABM, false, tracer), nil + lvm := evm.NewLoomVm(_state, nil, createABM) + return lvm.WithTracer(tracer), nil }) return *managerWithTracer } From 9d4600eba0c4f7f1e90042ab9ddf9569443ffe1e Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 11 Nov 2019 16:09:14 +0000 Subject: [PATCH 14/39] config tracer --- cmd/loom/loom.go | 16 ++++++++++++++-- config/config.go | 30 +++++++++++------------------- config/loom.example.yaml | 11 +++++------ 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index 15aa7df66c..8c02fe2c0b 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -17,6 +17,7 @@ import ( "time" ethvm "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" @@ -978,8 +979,19 @@ func loadApp( tracer := ethvm.Tracer(nil) if cfg.EVMTracer.Enabled { - // todo should we force cfg.EVMTracer.Debug to be true here - tracer, err = debug.CreateTracer(cfg.EVMTracer.TraceConfig) + traceCfg := eth.TraceConfig{ + Tracer: &cfg.EVMTracer.Tracer, + } + if len(cfg.EVMTracer.Tracer) == 0 { + traceCfg.LogConfig = ðvm.LogConfig{ + DisableMemory: cfg.EVMTracer.DisableMemory, + DisableStack: cfg.EVMTracer.DisableStack, + DisableStorage: cfg.EVMTracer.DisableStorage, + Debug: true, + Limit: cfg.EVMTracer.Limit, + } + } + tracer, err = debug.CreateTracer(traceCfg) if err != nil { return nil, errors.Wrapf(err, "creating tracer from %v", cfg.EVMTracer) } diff --git a/config/config.go b/config/config.go index 063ec68b58..bfbfcf5ca2 100755 --- a/config/config.go +++ b/config/config.go @@ -1,5 +1,3 @@ -// +build evm - package config import ( @@ -12,7 +10,6 @@ import ( "path" "path/filepath" - etheth "github.com/ethereum/go-ethereum/eth" "github.com/pkg/errors" "github.com/spf13/viper" @@ -174,8 +171,12 @@ func DefaultFnConsensusConfig() *FnConsensusConfig { } type EVMTracer struct { - etheth.TraceConfig - Enabled bool // enable tracer + Enabled bool // enable tracer + Tracer string //enable JavaScript-based transaction tracing, + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + Limit int // maximum length of output, but zero means unlimited } func DefaultEvmTraceConfig() *EVMTracer { @@ -807,20 +808,11 @@ PluginsDir: "{{ .PluginsDir }}" {{- if .EVMTracer }} EVMTracer: Enabled: {{ .EVMTracer.Enabled }} - {{- if .EVMTracer.Tracer }} - Tracer: "{{ .EVMTracer.Tracer }}" //enable JavaScript-based transaction tracing LogConfig is ignored if not empty - {{- end}} - {{- if .EVMTracer.Timeout }} - Timeout: "{{ .EVMTracer.Timeout }}" //Not used - {{- end}} - {{- if .EVMTracer.LogConfig }} - LogConfig: - DisableMemory: {{ .EVMTracer.LogConfig.DisableMemory }} // disable memory capture - DisableStack: {{ .EVMTracer.LogConfig.DisableStack }} // disable stack capture - DisableStorage: {{ .EVMTracer.LogConfig.DisableStorage }} // disable storage capture - Debug: {{ .EVMTracer.LogConfig.Debug }} // print output during capture end. set to true to have an effect - Limit: {{ .EVMTracer.LogConfig.Debug }} // maximum length of output, but zero means unlimited - {{- end}} + Tracer: "{{ .EVMTracer.Tracer }}" + DisableMemory: {{ .EVMTracer.DisableMemory }} + DisableStack: {{ .EVMTracer.DisableStack }} + DisableStorage: {{ .EVMTracer.DisableStorage }} + Limit: {{ .EVMTracer.Limit }} {{- end}} AllowNamedEvmContracts: {{ .AllowNamedEvmContracts }} # Set to true to disable minimum required build number check on node startup diff --git a/config/loom.example.yaml b/config/loom.example.yaml index 4c854dac45..6f4a5b0b76 100644 --- a/config/loom.example.yaml +++ b/config/loom.example.yaml @@ -85,12 +85,11 @@ DPOSv2OracleConfig: EVMTracer: Enabled: true - Timeout: "79" - LogConfig: - DisableMemory: true - DisableStack: false - DisableStorage: ture - Limit: 1000 + Tracer: "" + DisableMemory: true + DisableStack: false + DisableStorage: true + Limit: 1000 EventDispatcher: Dispatcher: log From f49d7c6f0bb4408c024b0fd13ea22944a0f502af Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 11 Nov 2019 16:40:14 +0000 Subject: [PATCH 15/39] truffle test --- e2e/tests/truffle/test/DebugFunctions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js index b63b046a0c..3cb610bca0 100644 --- a/e2e/tests/truffle/test/DebugFunctions.js +++ b/e2e/tests/truffle/test/DebugFunctions.js @@ -46,7 +46,7 @@ contract('debug_traceTransaction', async (accounts) => { const txResult = await contract.methods.set(1111).send(); await web3js.currentProvider.send({ method: "debug_traceTransaction", - params: [txResult.transactionHash,{"disableStorage":true,"disableMemory":false,"disableStack":false,"fullStorage":false}], + params: [txResult.transactionHash,{"disableStorage":true,"disableMemory":false,"disableStack":false}], jsonrpc: "2.0", id: new Date().getTime() }, function (error, result) { From 769672f08acdbd67bdd346ad0131d0583c85ecfd Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 11 Nov 2019 19:02:30 +0000 Subject: [PATCH 16/39] debug trace --- app.go | 4 ++++ cmd/loom/loom.go | 1 + rpc/debug/trace.go | 30 ++++++++++++++++++++++++++++++ rpc/query_server.go | 14 +++++++++++++- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index c233f4d943..2091ff5b4a 100644 --- a/app.go +++ b/app.go @@ -373,6 +373,7 @@ type Application struct { config *cctypes.Config childTxRefs []evmaux.ChildTxRef // links Tendermint txs to EVM txs ReceiptsVersion int32 + EVMTracer vm.Tracer } var _ abci.Application = &Application{} @@ -707,6 +708,9 @@ func (a *Application) DeliverTx(txBytes []byte) abci.ResponseDeliverTx { } else { r = a.deliverTx(storeTx, txBytes) } + if a.EVMTracer != nil { + log.Debug("evm trace", "trace", a.EVMTracer) + } txFailed = r.Code != abci.CodeTypeOK // TODO: this isn't 100% reliable when txFailed == true diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index 8c02fe2c0b..62b7152e93 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -1018,6 +1018,7 @@ func loadApp( GetValidatorSet: getValidatorSet, EvmAuxStore: evmAuxStore, ReceiptsVersion: cfg.ReceiptsVersion, + EVMTracer: tracer, }, nil } diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 224beac9e2..3773692c84 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -3,6 +3,7 @@ package debug import ( + "bytes" "fmt" "github.com/ethereum/go-ethereum/core/vm" @@ -23,6 +24,7 @@ func TraceTransaction( blockstore store.BlockStore, startBlockNumber, targetBlockNumber, txIndex int64, config eth.TraceConfig, + txHash []byte, ) (trace interface{}, err error) { defer func() { if r := recover(); r != nil { @@ -46,7 +48,19 @@ func TraceTransaction( return nil, err } + txResult, err := blockstore.GetTxResult(txHash) + if err != nil { + return nil, err + } + + if err := app.SetTracer(tracer, false); err != nil { + return nil, err + } result := app.DeliverTx(block.Block.Data.Txs[txIndex]) + match, err := resultsMatch(txResult.TxResult, result) + if !match { + return nil, err + } switch tracer := tracer.(type) { case *vm.StructLogger: @@ -92,6 +106,22 @@ func runUpTo(app *loomchain.Application, blockstore store.BlockStore, startHeigh return errors.Errorf("cannot find transaction at height %d index %d", height, index) } +func resultsMatch(expected, actual abci.ResponseDeliverTx) (bool, error) { + if expected.Code != actual.Code { + return false, errors.Errorf("transaction result codes do not match, expected %v got $v", expected.Code, actual.Code) + } + if 0 == bytes.Compare(expected.Data, actual.Data) { + return false, errors.Errorf("transaction result data does not match, expected %v got $v", expected.Data, actual.Data) + } + if expected.Log != actual.Log { + return false, errors.Errorf("transaction logs do not match, expected %v got $v", expected.Log, actual.Log) + } + if expected.Info != actual.Info { + return false, errors.Errorf("transaction info does not match, expected %v got $v", expected.Info, actual.Info) + } + return true, nil +} + func requestBeginBlock(resultBlock ctypes.ResultBlock) abci.RequestBeginBlock { return abci.RequestBeginBlock{ Header: ttypes.TM2PB.Header(&resultBlock.BlockMeta.Header), diff --git a/rpc/query_server.go b/rpc/query_server.go index f5fa7d4ffc..6a18909d1e 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -1149,7 +1149,19 @@ func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTra if err != nil { return nil, err } - return debug.TraceTransaction(*replayApp, s.BlockStore, startBlockNumber, int64(blockNumber), int64(txIndex), cfg) + txHash, err := eth.DecDataToBytes(receipt.TxHash) + if err != nil { + return nil, err + } + return debug.TraceTransaction( + *replayApp, + s.BlockStore, + startBlockNumber, + int64(blockNumber), + int64(txIndex), + cfg, + txHash, + ) } func (s *QueryServer) getBlockHeightFromHash(hash []byte) (uint64, error) { From 895c456397bf20d8cb782aa85e87aa2d6c71e984 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 11 Nov 2019 19:11:22 +0000 Subject: [PATCH 17/39] truffle test --- e2e/tests/truffle/test/DebugFunctions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js index 3cb610bca0..69432d3174 100644 --- a/e2e/tests/truffle/test/DebugFunctions.js +++ b/e2e/tests/truffle/test/DebugFunctions.js @@ -46,7 +46,7 @@ contract('debug_traceTransaction', async (accounts) => { const txResult = await contract.methods.set(1111).send(); await web3js.currentProvider.send({ method: "debug_traceTransaction", - params: [txResult.transactionHash,{"disableStorage":true,"disableMemory":false,"disableStack":false}], + params: [txResult.transactionHash,{"disableStorage":false,"disableMemory":false,"disableStack":false}], jsonrpc: "2.0", id: new Date().getTime() }, function (error, result) { From c556b045c9c405d7553a9e09f015f19876dbc560 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Tue, 12 Nov 2019 13:14:24 +0000 Subject: [PATCH 18/39] fix vmmanager --- app.go | 17 +++++----- cmd/loom/loom.go | 45 ++++++++++++++------------- evm/evm.go | 2 -- evm/loomevm.go | 26 +++++++--------- rpc/debug/trace.go | 3 +- rpc/query_server.go | 5 --- tx_handler/factory/handler_factory.go | 22 +++++++------ 7 files changed, 58 insertions(+), 62 deletions(-) diff --git a/app.go b/app.go index 2091ff5b4a..fac1578455 100644 --- a/app.go +++ b/app.go @@ -307,7 +307,8 @@ type TxHandler interface { } type TxHandlerFactory interface { - TxHandler(tracer vm.Tracer, metrics bool) (TxHandler, error) + TxHandler(metrics bool) (TxHandler, error) + TxHandlerWithTracer(tracer vm.Tracer, metrics bool) (TxHandler, error) Copy(newStore store.VersionedKVStore) TxHandlerFactory } @@ -939,16 +940,16 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) factory := a.TxHandlerFactory.Copy(splitStore) - txHandle, err := factory.TxHandler(nil, false) - if err != nil { - return nil, 0, err - } + //txHandle, err := factory.TxHandler(nil, false) + //if err != nil { + // return nil, 0, err + //} newApp := &Application{ Store: splitStore, Init: func(state State) error { panic("init should not be called") }, - TxHandler: txHandle, + //TxHandler: txHandle, TxHandlerFactory: factory, BlockIndexStore: nil, EventHandler: nil, @@ -965,8 +966,10 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo return newApp, startVersion, nil } +// This modifies the tx handler to use a tracer. +// Danger, this looses the receipt handle and account balance manager information func (a *Application) SetTracer(tracer vm.Tracer, metrics bool) error { - newTxHandle, err := a.TxHandlerFactory.TxHandler(tracer, metrics) + newTxHandle, err := a.TxHandlerFactory.TxHandlerWithTracer(tracer, metrics) if err != nil { return errors.Wrap(err, "making transaction handle") } diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index 62b7152e93..bbcd194f5f 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -797,6 +797,26 @@ func loadApp( receiptHandlerProvider := receipts.NewReceiptHandlerProvider(eventHandler, cfg.EVMPersistentTxReceiptsMax, evmAuxStore) + tracer := ethvm.Tracer(nil) + if cfg.EVMTracer.Enabled { + traceCfg := eth.TraceConfig{ + Tracer: &cfg.EVMTracer.Tracer, + } + if len(cfg.EVMTracer.Tracer) == 0 { + traceCfg.LogConfig = ðvm.LogConfig{ + DisableMemory: cfg.EVMTracer.DisableMemory, + DisableStack: cfg.EVMTracer.DisableStack, + DisableStorage: cfg.EVMTracer.DisableStorage, + Debug: true, + Limit: cfg.EVMTracer.Limit, + } + } + tracer, err = debug.CreateTracer(traceCfg) + if err != nil { + return nil, errors.Wrapf(err, "creating tracer from %v", cfg.EVMTracer) + } + } + var newABMFactory plugin.NewAccountBalanceManagerFactoryFunc if evm.EVMEnabled && cfg.EVMAccountsEnabled { newABMFactory = plugin.NewAccountBalanceManagerFactory @@ -836,7 +856,8 @@ func loadApp( return nil, err } } - return evm.NewLoomVm(state, receiptHandlerProvider.Writer(), createABM), nil + lvm := evm.NewLoomVm(state, receiptHandlerProvider.Writer(), createABM) + return lvm.WithTracer(tracer), nil }) } evm.LogEthDbBatch = cfg.LogEthDbBatch @@ -977,28 +998,8 @@ func loadApp( } } - tracer := ethvm.Tracer(nil) - if cfg.EVMTracer.Enabled { - traceCfg := eth.TraceConfig{ - Tracer: &cfg.EVMTracer.Tracer, - } - if len(cfg.EVMTracer.Tracer) == 0 { - traceCfg.LogConfig = ðvm.LogConfig{ - DisableMemory: cfg.EVMTracer.DisableMemory, - DisableStack: cfg.EVMTracer.DisableStack, - DisableStorage: cfg.EVMTracer.DisableStorage, - Debug: true, - Limit: cfg.EVMTracer.Limit, - } - } - tracer, err = debug.CreateTracer(traceCfg) - if err != nil { - return nil, errors.Wrapf(err, "creating tracer from %v", cfg.EVMTracer) - } - } - txHandlerFactory := factory.NewTxHandlerFactory(*cfg, vmManager, chainID, appStore, createRegistry) - chainTxHandler, err := txHandlerFactory.TxHandler(tracer, true) + chainTxHandler, err := txHandlerFactory.TxHandler(true) if err != nil { return nil, err } diff --git a/evm/evm.go b/evm/evm.go index 55985c308e..752075063d 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -136,7 +136,6 @@ func (m *evmAccountBalanceManager) Transfer(from, to common.Address, amount *big m.abm.Transfer(fromAddr, toAddr, loom.NewBigUInt(amount)) } -// TODO: this shouldn't be exported, rename to wrappedEVM type wrappedEVM struct { sdb vm.StateDB context vm.Context @@ -269,7 +268,6 @@ func (e wrappedEVM) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) return result.Bytes(), nil } -// TODO: this doesn't need to be exported, rename to newEVM func (e wrappedEVM) newEnv(origin common.Address) *vm.EVM { e.context.Origin = origin return vm.NewEVM(e.context, e.sdb, &e.chainConfig, e.vmConfig) diff --git a/evm/loomevm.go b/evm/loomevm.go index 619d6400dd..d2a4e68913 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -44,15 +44,14 @@ type ethdbLogContext struct { callerAddr loom.Address } -// TODO: this doesn't need to be exported, rename to loomEvmWithState type loomEvmWithState struct { *wrappedEVM db ethdb.Database sdb StateDB } -// TODO: this doesn't need to be exported, rename to newLoomEvmWithState. // NewLoomEvm is exported to db.newDumpEVMStateMultiWriterAppStoreCommand. +// TODO: this doesn't need to be exported, rename to newLoomEvmWithState. func NewLoomEvm( loomState loomchain.State, accountBalanceManager AccountBalanceManager, @@ -118,9 +117,8 @@ var LoomVmFactory = func(state loomchain.State) (vm.VM, error) { return NewLoomVm(state, receiptHandler, nil), nil } -// LoomVm implements the loomchain/vm.VM interface using the EVM. -// TODO: rename to LoomEVM -type LoomVm struct { +// LoomEVM implements the loomchain/vm.VM interface using the EVM. +type LoomEvm struct { state loomchain.State receiptHandler loomchain.WriteReceiptHandler createABM AccountBalanceManagerFactoryFunc @@ -131,27 +129,27 @@ func NewLoomVm( loomState loomchain.State, receiptHandler loomchain.WriteReceiptHandler, createABM AccountBalanceManagerFactoryFunc, -) *LoomVm { - return &LoomVm{ +) *LoomEvm { + return &LoomEvm{ state: loomState, receiptHandler: receiptHandler, createABM: createABM, } } -func (lvm LoomVm) WithTracer(tracer ethvm.Tracer) LoomVm { +func (lvm LoomEvm) WithTracer(tracer ethvm.Tracer) LoomEvm { lvm.tracer = tracer return lvm } -func (lvm LoomVm) accountBalanceManager(readOnly bool) AccountBalanceManager { +func (lvm LoomEvm) accountBalanceManager(readOnly bool) AccountBalanceManager { if lvm.createABM == nil { return nil } return lvm.createABM(readOnly) } -func (lvm LoomVm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]byte, loom.Address, error) { +func (lvm LoomEvm) Create(caller loom.Address, code []byte, value *loom.BigUInt) ([]byte, loom.Address, error) { logContext := ðdbLogContext{ blockHeight: lvm.state.Block().Height, contractAddr: loom.Address{}, @@ -214,7 +212,7 @@ func (lvm LoomVm) Create(caller loom.Address, code []byte, value *loom.BigUInt) return response, addr, err } -func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { +func (lvm LoomEvm) Call(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { logContext := ðdbLogContext{ blockHeight: lvm.state.Block().Height, contractAddr: addr, @@ -267,7 +265,7 @@ func (lvm LoomVm) Call(caller, addr loom.Address, input []byte, value *loom.BigU return txHash, err } -func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { +func (lvm LoomEvm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, error) { levm, err := NewLoomEvm(lvm.state, lvm.accountBalanceManager(true), nil, lvm.tracer) if err != nil { return nil, err @@ -275,7 +273,7 @@ func (lvm LoomVm) StaticCall(caller, addr loom.Address, input []byte) ([]byte, e return levm.StaticCall(caller, addr, input) } -func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { +func (lvm LoomEvm) GetCode(addr loom.Address) ([]byte, error) { levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.tracer) if err != nil { return nil, err @@ -283,7 +281,7 @@ func (lvm LoomVm) GetCode(addr loom.Address) ([]byte, error) { return levm.GetCode(addr), nil } -func (lvm LoomVm) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { +func (lvm LoomEvm) GetStorageAt(addr loom.Address, key []byte) ([]byte, error) { levm, err := NewLoomEvm(lvm.state, nil, nil, lvm.tracer) if err != nil { return nil, err diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 3773692c84..94724e9089 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -24,7 +24,6 @@ func TraceTransaction( blockstore store.BlockStore, startBlockNumber, targetBlockNumber, txIndex int64, config eth.TraceConfig, - txHash []byte, ) (trace interface{}, err error) { defer func() { if r := recover(); r != nil { @@ -48,7 +47,7 @@ func TraceTransaction( return nil, err } - txResult, err := blockstore.GetTxResult(txHash) + txResult, err := blockstore.GetTxResult(block.Block.Data.Txs[txIndex].Hash()) if err != nil { return nil, err } diff --git a/rpc/query_server.go b/rpc/query_server.go index 6a18909d1e..68e19dac61 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -1149,10 +1149,6 @@ func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTra if err != nil { return nil, err } - txHash, err := eth.DecDataToBytes(receipt.TxHash) - if err != nil { - return nil, err - } return debug.TraceTransaction( *replayApp, s.BlockStore, @@ -1160,7 +1156,6 @@ func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTra int64(blockNumber), int64(txIndex), cfg, - txHash, ) } diff --git a/tx_handler/factory/handler_factory.go b/tx_handler/factory/handler_factory.go index 72651454ac..716d3b0567 100644 --- a/tx_handler/factory/handler_factory.go +++ b/tx_handler/factory/handler_factory.go @@ -55,37 +55,39 @@ func (f txHandleFactory) Copy(newStore store.VersionedKVStore) loomchain.TxHandl } } -func (f txHandleFactory) TxHandler(tracer ethvm.Tracer, metrics bool) (loomchain.TxHandler, error) { - vmManager := createVmManager(f.vmManager, tracer) +// Creates a handle with an entirely new vmManager with dummy account balance manager factory and receipt handler. +func (f txHandleFactory) TxHandlerWithTracer(tracer ethvm.Tracer, metrics bool) (loomchain.TxHandler, error) { + f.vmManager = createVmManager(tracer) + return f.TxHandler(metrics) +} + +func (f txHandleFactory) TxHandler(metrics bool) (loomchain.TxHandler, error) { nonceTxHandler := auth.NewNonceHandler() - txMiddleware, err := txMiddleWare(f.cfg, vmManager, nonceTxHandler, f.chainID, f.store, metrics) + txMiddleware, err := txMiddleWare(f.cfg, *f.vmManager, nonceTxHandler, f.chainID, f.store, metrics) if err != nil { return nil, err } - postCommitMiddlewares, err := postCommitMiddleWAre(f.cfg, vmManager, nonceTxHandler) + postCommitMiddlewares, err := postCommitMiddleWAre(f.cfg, *f.vmManager, nonceTxHandler) if err != nil { return nil, err } return loomchain.MiddlewareTxHandler( txMiddleware, - router(f.cfg, vmManager, f.createRegistry), + router(f.cfg, *f.vmManager, f.createRegistry), postCommitMiddlewares, ), nil } -func createVmManager(vmManager *vm.Manager, tracer ethvm.Tracer) vm.Manager { - if tracer == nil && vmManager != nil { - return *vmManager - } +func createVmManager(tracer ethvm.Tracer) *vm.Manager { managerWithTracer := vm.NewManager() managerWithTracer.Register(vm.VMType_EVM, func(_state loomchain.State) (vm.VM, error) { var createABM evm.AccountBalanceManagerFactoryFunc lvm := evm.NewLoomVm(_state, nil, createABM) return lvm.WithTracer(tracer), nil }) - return *managerWithTracer + return managerWithTracer } func txMiddleWare( From d9d554cc3e5caf959bf06ef16fba10dd3ffd3de4 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Tue, 12 Nov 2019 16:52:44 +0000 Subject: [PATCH 19/39] fix --- app.go | 10 +++++----- e2e/tests/truffle/test/DebugFunctions.js | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app.go b/app.go index fac1578455..14d93327dd 100644 --- a/app.go +++ b/app.go @@ -940,16 +940,16 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) factory := a.TxHandlerFactory.Copy(splitStore) - //txHandle, err := factory.TxHandler(nil, false) - //if err != nil { - // return nil, 0, err - //} + txHandle, err := factory.TxHandler(false) + if err != nil { + return nil, 0, err + } newApp := &Application{ Store: splitStore, Init: func(state State) error { panic("init should not be called") }, - //TxHandler: txHandle, + TxHandler: txHandle, TxHandlerFactory: factory, BlockIndexStore: nil, EventHandler: nil, diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js index 69432d3174..396b368a60 100644 --- a/e2e/tests/truffle/test/DebugFunctions.js +++ b/e2e/tests/truffle/test/DebugFunctions.js @@ -11,7 +11,6 @@ const TxHashTestContract = artifacts.require('TxHashTestContract'); contract('debug_traceTransaction', async (accounts) => { let contract, fromAddr, nodeAddr, txHashTestContract, web3, web3js; - const StructLogsLength = 87; beforeEach(async () => { nodeAddr = fs.readFileSync(path.join(process.env.CLUSTER_DIR, '0', 'node_rpc_addr'), 'utf-8').trim() const chainID = 'default'; @@ -50,7 +49,8 @@ contract('debug_traceTransaction', async (accounts) => { jsonrpc: "2.0", id: new Date().getTime() }, function (error, result) { - assert.equal(StructLogsLength, result.result.structLogs.length); + assert.equal(null, error, "debug_traceTransaction returned error"); + assert.equal(true, result.result.structLogs.length > 0, "trace did not return any data"); }); await waitForXBlocks(nodeAddr, 1) }) From fcc157be1850bf96f1030966336ebed96bc0a89d Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Tue, 12 Nov 2019 19:12:34 +0000 Subject: [PATCH 20/39] touch --- rpc/debug/trace.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 94724e9089..bb4229320c 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -55,6 +55,7 @@ func TraceTransaction( if err := app.SetTracer(tracer, false); err != nil { return nil, err } + result := app.DeliverTx(block.Block.Data.Txs[txIndex]) match, err := resultsMatch(txResult.TxResult, result) if !match { From a9702709ba1dad219d5a5a46d105510c9189e221 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 11:27:38 +0000 Subject: [PATCH 21/39] touch --- plugin/vm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/vm.go b/plugin/vm.go index 4320a005b5..1e6241609e 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -197,6 +197,7 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom } } evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + return evm.Call(caller, addr, input, value) } From b1be5c710b35381f370069090a5fb9baa9fc698e Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 12:56:01 +0000 Subject: [PATCH 22/39] test --- plugin/vm.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugin/vm.go b/plugin/vm.go index 1e6241609e..cca4c95715 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -196,8 +196,7 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) return evm.Call(caller, addr, input, value) } @@ -210,7 +209,7 @@ func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]by return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) return evm.StaticCall(caller, addr, input) } From 8eee6c45f075655e525d706bf588cc9aa4813338 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 13:16:32 +0000 Subject: [PATCH 23/39] test --- plugin/vm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/vm.go b/plugin/vm.go index cca4c95715..4320a005b5 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -196,7 +196,7 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) return evm.Call(caller, addr, input, value) } @@ -209,7 +209,7 @@ func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]by return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM, false, nil) + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) return evm.StaticCall(caller, addr, input) } From 548f1d7befb7b3b8d0efd0c15065cfdd83c87fe5 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 13:22:57 +0000 Subject: [PATCH 24/39] test --- plugin/vm.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugin/vm.go b/plugin/vm.go index 4320a005b5..a5fa2b6ac2 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -15,12 +15,12 @@ import ( "github.com/loomnetwork/go-loom" lp "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/util" + "github.com/pkg/errors" + "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/auth" - levm "github.com/loomnetwork/loomchain/evm" "github.com/loomnetwork/loomchain/registry" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" ) type ( @@ -188,7 +188,7 @@ func (vm *PluginVM) StaticCall(caller, addr loom.Address, input []byte) ([]byte, } func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { - var createABM levm.AccountBalanceManagerFactoryFunc + /*var createABM levm.AccountBalanceManagerFactoryFunc var err error if vm.newABMFactory != nil { createABM, err = vm.newABMFactory(vm) @@ -197,20 +197,22 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom } } evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - return evm.Call(caller, addr, input, value) + return evm.Call(caller, addr, input, value)*/ + return nil, nil } func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]byte, error) { - var createABM levm.AccountBalanceManagerFactoryFunc - var err error - if vm.newABMFactory != nil { - createABM, err = vm.newABMFactory(vm) - if err != nil { - return nil, err - } - } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - return evm.StaticCall(caller, addr, input) + //var createABM levm.AccountBalanceManagerFactoryFunc + //var err error + //if vm.newABMFactory != nil { + // createABM, err = vm.newABMFactory(vm) + // if err != nil { + // return nil, err + // } + //} + //evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + //return evm.StaticCall(caller, addr, input) + return nil, nil } func (vm *PluginVM) GetCode(addr loom.Address) ([]byte, error) { From 482625800b5f51ec5637c5a866eeeafb8acd2c1c Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 14:02:19 +0000 Subject: [PATCH 25/39] fix lint errors --- cmd/loom/gateway_reactors.go | 7 +++---- cmd/loom/loom.go | 20 -------------------- eth/polls/polls_test.go | 10 +++++----- eth/query/tx.go | 3 +++ evm/evm.go | 26 -------------------------- plugin/external.go | 2 +- plugin/loader.go | 2 +- receipts/leveldb/leveldb_test.go | 17 ++--------------- registry/v1/registry.go | 2 +- registry/v2/registry.go | 2 +- store/block_store_cache_test.go | 23 ++++++++++++++++++++--- store/pruning_iavlstore.go | 22 ++-------------------- 12 files changed, 39 insertions(+), 97 deletions(-) diff --git a/cmd/loom/gateway_reactors.go b/cmd/loom/gateway_reactors.go index a8b82f90af..19ab7b18b6 100644 --- a/cmd/loom/gateway_reactors.go +++ b/cmd/loom/gateway_reactors.go @@ -15,10 +15,9 @@ import ( ) const ( - GatewayName = "gateway" - LoomGatewayName = "loomcoin-gateway" - BinanceGatewayName = "binance-gateway" - TronGatewayName = "tron-gateway" + GatewayName = "gateway" + LoomGatewayName = "loomcoin-gateway" + TronGatewayName = "tron-gateway" ) func startGatewayReactors( diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index 170fac5eaa..ff667e1a8f 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -493,14 +493,6 @@ func contractInfoCommand() *cobra.Command { return cmd } -//nolint:deadcode -func recovery() { - if r := recover(); r != nil { - log.Error("caught RPC proxy exception, exiting", r) - os.Exit(1) - } -} - func startFeatureAutoEnabler( chainID string, cfg *config.ChainConfigConfig, nodeSigner glAuth.Signer, node backend.Backend, logger *loom.Logger, @@ -1069,8 +1061,6 @@ func deployContract( type contextFactory func(state loomchain.State) (contractpb.Context, error) -type staticContextFactory func(state loomchain.State) (contractpb.StaticContext, error) - func getContractCtx(pluginName string, vmManager *vm.Manager) contextFactory { return func(state loomchain.State) (contractpb.Context, error) { pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) @@ -1081,16 +1071,6 @@ func getContractCtx(pluginName string, vmManager *vm.Manager) contextFactory { } } -func getContractStaticCtx(pluginName string, vmManager *vm.Manager) staticContextFactory { - return func(state loomchain.State) (contractpb.StaticContext, error) { - pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) - if err != nil { - return nil, err - } - return plugin.NewInternalContractContext(pluginName, pvm.(*plugin.PluginVM), true) - } -} - func initBackend(cfg *config.Config, abciServerAddr string, fnRegistry fnConsensus.FnRegistry) backend.Backend { ovCfg := &backend.OverrideConfig{ LogLevel: cfg.BlockchainLogLevel, diff --git a/eth/polls/polls_test.go b/eth/polls/polls_test.go index a2168b922e..b0611c5df6 100644 --- a/eth/polls/polls_test.go +++ b/eth/polls/polls_test.go @@ -29,9 +29,8 @@ var ( ) const ( - deployId = uint32(1) - callId = uint32(2) - migrationTx = uint32(3) + deployId = uint32(1) + callId = uint32(2) ) func TestLogPoll(t *testing.T) { @@ -248,7 +247,7 @@ func testTimeout(t *testing.T, version handler.ReceiptHandlerVersion) { state40 := common.MockStateAt(state, uint64(40)) _ = sub.AddTxPoll(uint64(40)) - result, err = sub.LegacyPoll(state40, id, receiptHandler) + _, err = sub.LegacyPoll(state40, id, receiptHandler) require.Error(t, err, "poll did not timed out") require.NoError(t, receiptHandler.Close()) } @@ -360,6 +359,7 @@ func TestAddRemove(t *testing.T) { Topics: nil, } myFilter, err := eth.DecLogFilter(jsonFilter) + require.NoError(t, err) id, err := s.AddLogPoll(myFilter, 1) require.NoError(t, err) _, ok := s.polls[id] @@ -407,6 +407,6 @@ func mockSignedTx(t *testing.T, id uint32, to loom.Address, from loom.Address, d signedTx, err := proto.Marshal(&auth.SignedTx{ Inner: nonceTx, }) - + require.NoError(t, err) return signedTx } diff --git a/eth/query/tx.go b/eth/query/tx.go index 2937f18eec..a3623c3e2c 100644 --- a/eth/query/tx.go +++ b/eth/query/tx.go @@ -38,6 +38,9 @@ func GetTxByBlockAndIndex( iHeight := int64(height) blockResult, err := blockStore.GetBlockByHeight(&iHeight) + if err != nil { + return eth.GetEmptyTxObject(), errors.Errorf("error getting block for height %v", height) + } if blockResult == nil || blockResult.Block == nil { return eth.GetEmptyTxObject(), errors.Errorf("no block results found at height %v", height) } diff --git a/evm/evm.go b/evm/evm.go index 752075063d..b5a27d28eb 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -302,32 +302,6 @@ func defaultChainConfig(enableConstantinople bool) params.ChainConfig { } } -func defaultVmConfig() vm.Config { - logCfg := vm.LogConfig{ - DisableMemory: true, // disable memory capture - DisableStack: true, // disable stack capture - DisableStorage: true, // disable storage capture - Limit: 0, // maximum length of output, but zero means unlimited - } - - logger := vm.NewStructLogger(&logCfg) - return vm.Config{ - // Debug enabled debugging Interpreter options - Debug: false, - // Tracer is the op code logger - Tracer: logger, - // NoRecursion disabled Interpreter call, callcode, - // delegate call and create. - NoRecursion: false, - // Enable recording of SHA3/keccak preimages - EnablePreimageRecording: true, //TODO: make this optional, [MGC] I don't think we need to keep this - // JumpTable contains the EVM instruction table. This - // may be left uninitialised and will be set to the default - // table. - //JumpTable: [256]operation, - } -} - func createVmConfig(tracer vm.Tracer) (vm.Config, error) { if tracer == nil { logCfg := vm.LogConfig{ diff --git a/plugin/external.go b/plugin/external.go index 69e8c150b7..7d401fc57d 100644 --- a/plugin/external.go +++ b/plugin/external.go @@ -30,7 +30,7 @@ func (f *FileNameInfo) String() string { return f.Base + f.Ext + f.Version } -var fileInfoRE = regexp.MustCompile("(.+?)(\\.[a-zA-Z]+?)?\\.([0-9\\.]+)") +var fileInfoRE = regexp.MustCompile(`(.+?)(\.[a-zA-Z]+?)?\.([0-9\.]+)`) func parseFileName(name string) (*FileNameInfo, error) { groups := fileInfoRE.FindSubmatch([]byte(name)) diff --git a/plugin/loader.go b/plugin/loader.go index 4e2468925d..cbe960796c 100644 --- a/plugin/loader.go +++ b/plugin/loader.go @@ -121,7 +121,7 @@ func (m *StaticLoader) LoadContract(name string, blockHeight int64) (plugin.Cont } func (m *StaticLoader) findOverride(name string, blockHeight int64) plugin.Contract { - if overrides, _ := m.overrides[name]; overrides != nil { + if overrides := m.overrides[name]; overrides != nil { for i := len(overrides) - 1; i >= 0; i-- { if blockHeight >= overrides[i].BlockHeight { return overrides[i].Contract diff --git a/receipts/leveldb/leveldb_test.go b/receipts/leveldb/leveldb_test.go index e2fc61b10e..925691de3a 100644 --- a/receipts/leveldb/leveldb_test.go +++ b/receipts/leveldb/leveldb_test.go @@ -2,15 +2,15 @@ package leveldb import ( "bytes" - "fmt" "os" "testing" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom/plugin/types" + "github.com/stretchr/testify/require" + "github.com/loomnetwork/loomchain/receipts/common" evmaux "github.com/loomnetwork/loomchain/store/evm_aux" - "github.com/stretchr/testify/require" ) const ( @@ -173,19 +173,6 @@ func TestConfirmTransactionReceipts(t *testing.T) { require.Error(t, err) } -//nolint:deadcode -func dumpDbEntries(evmAuxStore *evmaux.EvmAuxStore) error { - fmt.Println("\nDumping leveldb") - db := evmAuxStore.DB() - iter := db.NewIterator(nil, nil) - defer iter.Release() - for iter.Next() { - fmt.Printf("key %s\t\tvalue %s", iter.Key(), iter.Value()) - } - fmt.Println() - return iter.Error() -} - func countDbEntries(evmAuxStore *evmaux.EvmAuxStore) (uint64, error) { count := uint64(0) db := evmAuxStore.DB() diff --git a/registry/v1/registry.go b/registry/v1/registry.go index 55313cd9c6..862aecea94 100644 --- a/registry/v1/registry.go +++ b/registry/v1/registry.go @@ -17,7 +17,7 @@ const ( ) var ( - validNameRE = regexp.MustCompile("^[a-zA-Z0-9\\.\\-]+$") + validNameRE = regexp.MustCompile(`^[a-zA-Z0-9\.\-]+$`) ) func recordKey(name string) []byte { diff --git a/registry/v2/registry.go b/registry/v2/registry.go index 152cb0ff5c..886e20ffdc 100644 --- a/registry/v2/registry.go +++ b/registry/v2/registry.go @@ -18,7 +18,7 @@ const ( ) var ( - validNameRE = regexp.MustCompile("^[a-zA-Z0-9\\.\\-]+$") + validNameRE = regexp.MustCompile(`^[a-zA-Z0-9\.\-]+$`) // Store Keys contractAddrKeyPrefix = []byte("reg_caddr") diff --git a/store/block_store_cache_test.go b/store/block_store_cache_test.go index 6edf12d98c..4faad52979 100644 --- a/store/block_store_cache_test.go +++ b/store/block_store_cache_test.go @@ -220,7 +220,13 @@ func TestBlockFetchAtHeight2Q(t *testing.T) { //request for a block for nil height, maximum height data is returned and cached blockstoreData, err = cachedblockStore.GetBlockByHeight(nil) require.NoError(t, err, "Gives maximum height block") - require.Equal(t, int64(50), blockstoreData.Block.Height, "Expecting blockstore height 50 as maximum height block is fetched,this is default functionality of corresponding tendermint blockstore API also") + require.Equal( + t, + int64(50), + blockstoreData.Block.Height, + "Expecting blockstore height 50 as maximum height block is fetched,this is default"+ + "functionality of corresponding tendermint blockstore API also", + ) //block at maximum height present in cache _, ok = cachedblockStore.TwoQueueCache.Get(height) @@ -249,7 +255,12 @@ func TestGetBlockRangeByHeight2Q(t *testing.T) { //Blocks returned by MockBlockStore are verified for data accuracy for i := minheight; i <= maxheight; i++ { - require.Equal(t, int64(i), int64(blockrangeData.BlockMetas[i-1].Header.Height), "Expecting height field in blockMetas equal height supplied to API for accuracy check,Expecting Data from API") + require.Equal( + t, + int64(i), + int64(blockrangeData.BlockMetas[i-1].Header.Height), + "Expecting height field in blockMetas equal height supplied to API for accuracy check,Expecting Data from API", + ) } //Block infos in above provided height range gets cached resulting in cache hit for each height in height range @@ -264,7 +275,13 @@ func TestGetBlockRangeByHeight2Q(t *testing.T) { //Blocks returned by Cached are verified for data accuracy for i := minheight; i <= maxheight; i++ { - require.Equal(t, int64(i), int64(blockrangeData.BlockMetas[i-1].Header.Height), "Expecting Data from Cache,Expecting height field in blockMetas equal height supplied to API for Cache accuracy check") + require.Equal( + t, + int64(i), + int64(blockrangeData.BlockMetas[i-1].Header.Height), + "Expecting Data from Cache,Expecting height field in blockMetas equal height"+ + " supplied to API for Cache accuracy check", + ) } //request for blockinfo for height range where max height is greater than maximum height of blockchain, error is returned by Cache, caching occurs till max height of blockchain diff --git a/store/pruning_iavlstore.go b/store/pruning_iavlstore.go index fe58887efc..3887d94b2d 100644 --- a/store/pruning_iavlstore.go +++ b/store/pruning_iavlstore.go @@ -2,7 +2,6 @@ package store import ( "fmt" - "runtime" "sync" "time" @@ -10,10 +9,11 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/plugin" - "github.com/loomnetwork/loomchain/log" "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/loomnetwork/loomchain/log" ) var ( @@ -234,24 +234,6 @@ func (s *PruningIAVLStore) deleteVersion(ver int64) error { return err } -// runWithRecovery should run in a goroutine, it will ensure the given function keeps on running in -// a goroutine as long as it doesn't panic due to a runtime error. -//[MGC] I believe this function shouldn't be used as we should just fail fast if this breaks -func (s *PruningIAVLStore) runWithRecovery(run func()) { - defer func() { - if r := recover(); r != nil { - s.logger.Error("Recovered from panic in PruningIAVLStore goroutine", "r", r) - // Unless it's a runtime error restart the goroutine - if _, ok := r.(runtime.Error); !ok { - time.Sleep(30 * time.Second) - s.logger.Info("Restarting PruningIAVLStore goroutine...\n") - go s.runWithRecovery(run) - } - } - }() - run() -} - // loopWithInterval will execute the step function in an endless loop, sleeping for the specified // interval at the end of each loop iteration. func (s *PruningIAVLStore) loopWithInterval(step func() error, interval time.Duration) { From 7bcc26e9cfae3173249d8fa7ef0e1b8754d6dc5a Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 14:44:24 +0000 Subject: [PATCH 26/39] untest --- plugin/vm.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/plugin/vm.go b/plugin/vm.go index a5fa2b6ac2..4320a005b5 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -15,12 +15,12 @@ import ( "github.com/loomnetwork/go-loom" lp "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/util" - "github.com/pkg/errors" - "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/auth" + levm "github.com/loomnetwork/loomchain/evm" "github.com/loomnetwork/loomchain/registry" "github.com/loomnetwork/loomchain/vm" + "github.com/pkg/errors" ) type ( @@ -188,7 +188,7 @@ func (vm *PluginVM) StaticCall(caller, addr loom.Address, input []byte) ([]byte, } func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { - /*var createABM levm.AccountBalanceManagerFactoryFunc + var createABM levm.AccountBalanceManagerFactoryFunc var err error if vm.newABMFactory != nil { createABM, err = vm.newABMFactory(vm) @@ -197,22 +197,20 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom } } evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - return evm.Call(caller, addr, input, value)*/ - return nil, nil + return evm.Call(caller, addr, input, value) } func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]byte, error) { - //var createABM levm.AccountBalanceManagerFactoryFunc - //var err error - //if vm.newABMFactory != nil { - // createABM, err = vm.newABMFactory(vm) - // if err != nil { - // return nil, err - // } - //} - //evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - //return evm.StaticCall(caller, addr, input) - return nil, nil + var createABM levm.AccountBalanceManagerFactoryFunc + var err error + if vm.newABMFactory != nil { + createABM, err = vm.newABMFactory(vm) + if err != nil { + return nil, err + } + } + evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + return evm.StaticCall(caller, addr, input) } func (vm *PluginVM) GetCode(addr loom.Address) ([]byte, error) { From 571c9de1d35875e03c143f707d9041b5bdcdb79c Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 15:26:20 +0000 Subject: [PATCH 27/39] test --- plugin/vm.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/plugin/vm.go b/plugin/vm.go index 4320a005b5..f0bf7fff26 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -15,12 +15,13 @@ import ( "github.com/loomnetwork/go-loom" lp "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/util" + "github.com/pkg/errors" + "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/auth" - levm "github.com/loomnetwork/loomchain/evm" + "github.com/loomnetwork/loomchain/evm" "github.com/loomnetwork/loomchain/registry" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" ) type ( @@ -188,7 +189,7 @@ func (vm *PluginVM) StaticCall(caller, addr loom.Address, input []byte) ([]byte, } func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom.BigUInt) ([]byte, error) { - var createABM levm.AccountBalanceManagerFactoryFunc + var createABM evm.AccountBalanceManagerFactoryFunc var err error if vm.newABMFactory != nil { createABM, err = vm.newABMFactory(vm) @@ -196,12 +197,12 @@ func (vm *PluginVM) CallEVM(caller, addr loom.Address, input []byte, value *loom return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - return evm.Call(caller, addr, input, value) + levm := evm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + return levm.Call(caller, addr, input, value) } func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]byte, error) { - var createABM levm.AccountBalanceManagerFactoryFunc + var createABM evm.AccountBalanceManagerFactoryFunc var err error if vm.newABMFactory != nil { createABM, err = vm.newABMFactory(vm) @@ -209,8 +210,8 @@ func (vm *PluginVM) StaticCallEVM(caller, addr loom.Address, input []byte) ([]by return nil, err } } - evm := levm.NewLoomVm(vm.State, vm.receiptWriter, createABM) - return evm.StaticCall(caller, addr, input) + levm := evm.NewLoomVm(vm.State, vm.receiptWriter, createABM) + return levm.StaticCall(caller, addr, input) } func (vm *PluginVM) GetCode(addr loom.Address) ([]byte, error) { From 1a5f57f1bcf0490ec3b781aa7db1d89c9eec35ee Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 13 Nov 2019 19:05:13 +0000 Subject: [PATCH 28/39] noevm --- evm/noevm.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/evm/noevm.go b/evm/noevm.go index ce88d22501..2b20130af1 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -18,8 +18,6 @@ func NewLoomVm( _ loomchain.State, _ loomchain.WriteReceiptHandler, _ AccountBalanceManagerFactoryFunc, - _ bool, - _ interface{}, ) lvm.VM { return nil } From 8d60e39234e697b7d534c2187b9916c88d82d0c9 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 14 Nov 2019 18:30:23 +0000 Subject: [PATCH 29/39] debug_storagerangeat --- evm/loomevm.go | 6 +-- rpc/debug/jsonrpc_conversion.go | 5 +++ rpc/debug/trace.go | 68 ++++++++++++++++++++++++++++----- rpc/instrumenting.go | 16 +++++++- rpc/mock_query_server.go | 12 +++++- rpc/query_server.go | 47 +++++++++++++++++++++++ rpc/query_service.go | 2 + 7 files changed, 141 insertions(+), 15 deletions(-) diff --git a/evm/loomevm.go b/evm/loomevm.go index d2a4e68913..c7a97163b7 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -28,7 +28,7 @@ import ( var ( vmPrefix = []byte("vm") - rootKey = []byte("vmroot") + RootKey = []byte("vmroot") ) type StateDB interface { @@ -60,7 +60,7 @@ func NewLoomEvm( ) (*loomEvmWithState, error) { p := new(loomEvmWithState) p.db = NewLoomEthdb(loomState, logContext) - oldRoot, err := p.db.Get(rootKey) + oldRoot, err := p.db.Get(RootKey) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func (levm loomEvmWithState) Commit() (common.Hash, error) { if err := levm.sdb.Database().TrieDB().Commit(root, false); err != nil { return root, err } - if err := levm.db.Put(rootKey, root[:]); err != nil { + if err := levm.db.Put(RootKey, root[:]); err != nil { return root, err } return root, err diff --git a/rpc/debug/jsonrpc_conversion.go b/rpc/debug/jsonrpc_conversion.go index 050fd896c2..5d3d565164 100644 --- a/rpc/debug/jsonrpc_conversion.go +++ b/rpc/debug/jsonrpc_conversion.go @@ -19,6 +19,11 @@ type JsonLogConfig struct { DisableStack bool `json:"disableStack,omitempty"` } +type JsonStorageRangeResult struct { + eth.StorageRangeResult + Complete bool `json:"complete"` +} + func DecTraceConfig(jcfg *JsonTraceConfig) eth.TraceConfig { var logConfig *vm.LogConfig if jcfg == nil { diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index bb4229320c..2baea22428 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -4,8 +4,11 @@ package debug import ( "bytes" + "context" "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethapi" @@ -16,6 +19,7 @@ import ( ttypes "github.com/tendermint/tendermint/types" "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/evm" "github.com/loomnetwork/loomchain/store" ) @@ -31,12 +35,9 @@ func TraceTransaction( } }() - if err := runUpTo(&app, blockstore, startBlockNumber, targetBlockNumber, txIndex); err != nil { - return nil, err - } - block, err := blockstore.GetBlockByHeight(&targetBlockNumber) + block, err := runTxsTo(&app, blockstore, startBlockNumber, targetBlockNumber, txIndex, false) if err != nil { - return nil, errors.Wrapf(err, "getting block information at height %v", targetBlockNumber) + return nil, err } tracer, err := CreateTracer(config) @@ -77,11 +78,57 @@ func TraceTransaction( } } -func runUpTo(app *loomchain.Application, blockstore store.BlockStore, startHeight, height, index int64) error { +func StorageRangeAt( + app loomchain.Application, + blockstore store.BlockStore, + address, begin []byte, + startBlockNumber, targetBlockNumber, txIndex int64, + maxResults int, +) (results JsonStorageRangeResult, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("loomchain panicked %v", r) + } + }() + + block, err := runTxsTo(&app, blockstore, startBlockNumber, targetBlockNumber, txIndex, true) + if err != nil { + return JsonStorageRangeResult{}, err + } + + storeState := loomchain.NewStoreState( + context.Background(), + app.Store, + ttypes.TM2PB.Header(&block.Block.Header), + block.BlockMeta.BlockID.Hash, + app.GetValidatorSet, + ) + ethDb := evm.NewLoomEthdb(storeState, nil) + root, err := ethDb.Get(evm.RootKey) + if err != nil { + return JsonStorageRangeResult{}, err + } + stateDb, err := state.New(common.BytesToHash(root), state.NewDatabase(ethDb)) + if err != nil { + return JsonStorageRangeResult{}, err + } + st := stateDb.StorageTrie(common.BytesToAddress(address)) + result, err := eth.StorageRangeAt(st, begin, maxResults) + if err != nil { + return JsonStorageRangeResult{}, err + } + + return JsonStorageRangeResult{ + StorageRangeResult: result, + Complete: result.NextKey == nil, + }, nil +} + +func runTxsTo(app *loomchain.Application, blockstore store.BlockStore, startHeight, height, index int64, includeEnd bool) (*ctypes.ResultBlock, error) { for h := startHeight; h <= height; h++ { resultBlock, err := blockstore.GetBlockByHeight(&h) if err != nil { - return errors.Wrapf(err, "getting block information at height %v", h) + return nil, errors.Wrapf(err, "getting block information at height %v", h) } _ = app.BeginBlock(requestBeginBlock(*resultBlock)) @@ -95,7 +142,10 @@ func runUpTo(app *loomchain.Application, blockstore store.BlockStore, startHeigh if i != int(index) { _ = app.DeliverTx(resultBlock.Block.Data.Txs[i]) } else { - return nil + if includeEnd { + _ = app.DeliverTx(resultBlock.Block.Data.Txs[i]) + } + return resultBlock, nil } } } @@ -103,7 +153,7 @@ func runUpTo(app *loomchain.Application, blockstore store.BlockStore, startHeigh _ = app.EndBlock(requestEndBlock(h)) _ = app.Commit() } - return errors.Errorf("cannot find transaction at height %d index %d", height, index) + return nil, errors.Errorf("cannot find transaction at height %d index %d", height, index) } func resultsMatch(expected, actual abci.ResponseDeliverTx) (bool, error) { diff --git a/rpc/instrumenting.go b/rpc/instrumenting.go index 587c9a8e3f..00b449e76f 100755 --- a/rpc/instrumenting.go +++ b/rpc/instrumenting.go @@ -7,11 +7,12 @@ import ( "github.com/go-kit/kit/metrics" "github.com/gorilla/websocket" "github.com/loomnetwork/go-loom/plugin/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" + "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/rpc/debug" "github.com/loomnetwork/loomchain/rpc/eth" "github.com/loomnetwork/loomchain/vm" - rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) // InstrumentingMiddleware implements QuerySerice interface @@ -597,3 +598,16 @@ func (m InstrumentingMiddleware) DebugTraceTransaction( resp, err = m.next.DebugTraceTransaction(hash, config) return } + +func (m InstrumentingMiddleware) DebugStorageRangeAt( + blockHashOrNumber string, txIndex int, address, begin string, maxResults int, +) (resp debug.JsonStorageRangeResult, err error) { + defer func(begin time.Time) { + lvs := []string{"method", "DebugTraceTransaction", "error", fmt.Sprint(err != nil)} + m.requestCount.With(lvs...).Add(1) + m.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) + }(time.Now()) + + resp, err = m.next.DebugStorageRangeAt(blockHashOrNumber, txIndex, address, begin, maxResults) + return +} diff --git a/rpc/mock_query_server.go b/rpc/mock_query_server.go index 03fa076dd7..b888aa86d5 100644 --- a/rpc/mock_query_server.go +++ b/rpc/mock_query_server.go @@ -4,9 +4,8 @@ import ( "sync" "github.com/gorilla/websocket" - rpctypes "github.com/tendermint/tendermint/rpc/lib/types" - "github.com/loomnetwork/go-loom/plugin/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/rpc/debug" @@ -381,3 +380,12 @@ func (m *MockQueryService) DebugTraceTransaction(hash eth.Data, config *debug.Js m.MethodsCalled = append([]string{"DebugTraceTransaction"}, m.MethodsCalled...) return nil, nil } + +func (m MockQueryService) DebugStorageRangeAt( + blockHashOrNumber string, txIndex int, address, begin string, maxResults int, +) (resp debug.JsonStorageRangeResult, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.MethodsCalled = append([]string{"DebugTraceTransaction"}, m.MethodsCalled...) + return debug.JsonStorageRangeResult{}, nil +} diff --git a/rpc/query_server.go b/rpc/query_server.go index 65c02bdc5d..a07513cdab 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/tendermint/tendermint/crypto/tmhash" "github.com/gogo/protobuf/proto" gtypes "github.com/loomnetwork/go-loom/types" @@ -1172,6 +1173,7 @@ func (s *QueryServer) EthAccounts() ([]eth.Data, error) { return []eth.Data{}, nil } +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#debug_tracetransaction func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTraceConfig) (interface{}, error) { receipt, err := s.EthGetTransactionReceipt(hash) if err != nil || receipt == nil { @@ -1200,6 +1202,51 @@ func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTra ) } +// https://github.com/ethereum/retesteth/wiki/RPC-Methods#debug_storagerangeat +func (s QueryServer) DebugStorageRangeAt( + blockHashOrNumber string, txIndex int, address, begin string, maxResults int, +) (resp debug.JsonStorageRangeResult, err error) { + if address[:2] == "0x" { + address = address[:2] + } + local, err := hex.DecodeString(address) + if err != nil { + return debug.JsonStorageRangeResult{}, err + } + + if blockHashOrNumber[:2] == "0x" { + blockHashOrNumber = blockHashOrNumber[:2] + } + var blockNumber uint64 + if len(blockHashOrNumber) >= tmhash.Size { + hash, err := hex.DecodeString(blockHashOrNumber) + if err != nil { + return debug.JsonStorageRangeResult{}, err + } + blockNumber, err = s.getBlockHeightFromHash(hash) + } else { + blockNumber, err = strconv.ParseUint(blockHashOrNumber, 0, 64) + } + if err != nil { + return debug.JsonStorageRangeResult{}, err + } + + replayApp, startBlockNumber, err := s.ReplayApplication(blockNumber, s.BlockStore) + if err != nil { + return debug.JsonStorageRangeResult{}, err + } + return debug.StorageRangeAt( + *replayApp, + s.BlockStore, + local, + nil, + startBlockNumber, + int64(blockNumber), + int64(txIndex), + maxResults, + ) +} + func (s *QueryServer) getBlockHeightFromHash(hash []byte) (uint64, error) { if nil != s.BlockIndexStore { return s.BlockIndexStore.GetBlockHeightByHash(hash) diff --git a/rpc/query_service.go b/rpc/query_service.go index 712b2068ed..714890280a 100755 --- a/rpc/query_service.go +++ b/rpc/query_service.go @@ -68,6 +68,7 @@ type QueryService interface { // debug transactions. DebugTraceTransaction(hash eth.Data, config *debug.JsonTraceConfig) (interface{}, error) + DebugStorageRangeAt(blockHashOrNumber string, txIndex int, address, begin string, maxResults int) (debug.JsonStorageRangeResult, error) // deprecated function EvmTxReceipt(txHash []byte) ([]byte, error) @@ -194,6 +195,7 @@ func createDefaultEthRoutes(svc QueryService, chainID string) map[string]eth.RPC routes["eth_getTransactionCount"] = eth.NewRPCFunc(svc.EthGetTransactionCount, "local,block") routes["eth_sendRawTransaction"] = NewSendRawTransactionRPCFunc(chainID, rpccore.BroadcastTxSync) routes["debug_traceTransaction"] = eth.NewRPCFunc(svc.DebugTraceTransaction, "hash,config") + routes["debug_storageRangeAt"] = eth.NewRPCFunc(svc.DebugStorageRangeAt, "blockHashOrNumber,txIndex,address,begin,maxResults") return routes } From 66cf30627e2c5e1df32e11ea77bf1c8f167fbbcd Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Fri, 15 Nov 2019 10:45:16 +0000 Subject: [PATCH 30/39] handle 0x --- rpc/query_server.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rpc/query_server.go b/rpc/query_server.go index a07513cdab..dde905f468 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -1206,19 +1206,19 @@ func (s *QueryServer) DebugTraceTransaction(hash eth.Data, config *debug.JsonTra func (s QueryServer) DebugStorageRangeAt( blockHashOrNumber string, txIndex int, address, begin string, maxResults int, ) (resp debug.JsonStorageRangeResult, err error) { - if address[:2] == "0x" { - address = address[:2] + if len(address) >= 2 && address[:2] != "0x" { + address = "0x" + address } - local, err := hex.DecodeString(address) + local, err := loom.LocalAddressFromHexString(address) if err != nil { return debug.JsonStorageRangeResult{}, err } - if blockHashOrNumber[:2] == "0x" { - blockHashOrNumber = blockHashOrNumber[:2] - } var blockNumber uint64 if len(blockHashOrNumber) >= tmhash.Size { + if address[:2] == "0x" { + address = address[2:] + } hash, err := hex.DecodeString(blockHashOrNumber) if err != nil { return debug.JsonStorageRangeResult{}, err From ffb7d755913355da1d2b6e48407bf857e651f625 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Fri, 15 Nov 2019 21:03:26 +0000 Subject: [PATCH 31/39] fix --- app.go | 7 ++++--- evm/loomethdb.go | 2 +- evm/loomevm.go | 2 +- rpc/debug/trace.go | 7 +++---- rpc/query_server.go | 4 ++-- store/splitstore.go | 2 +- tx_handler/factory/handler_factory.go | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app.go b/app.go index 14d93327dd..b815beea22 100644 --- a/app.go +++ b/app.go @@ -308,7 +308,7 @@ type TxHandler interface { type TxHandlerFactory interface { TxHandler(metrics bool) (TxHandler, error) - TxHandlerWithTracer(tracer vm.Tracer, metrics bool) (TxHandler, error) + TxHandlerWithTracerAndDefaultVmManager(tracer vm.Tracer, metrics bool) (TxHandler, error) Copy(newStore store.VersionedKVStore) TxHandlerFactory } @@ -940,7 +940,8 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) factory := a.TxHandlerFactory.Copy(splitStore) - txHandle, err := factory.TxHandler(false) + //txHandle, err := factory.TxHandler(false) + txHandle, err := factory.TxHandlerWithTracerAndDefaultVmManager(nil, false) if err != nil { return nil, 0, err } @@ -969,7 +970,7 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo // This modifies the tx handler to use a tracer. // Danger, this looses the receipt handle and account balance manager information func (a *Application) SetTracer(tracer vm.Tracer, metrics bool) error { - newTxHandle, err := a.TxHandlerFactory.TxHandlerWithTracer(tracer, metrics) + newTxHandle, err := a.TxHandlerFactory.TxHandlerWithTracerAndDefaultVmManager(tracer, metrics) if err != nil { return errors.Wrap(err, "making transaction handle") } diff --git a/evm/loomethdb.go b/evm/loomethdb.go index 9d5024f333..41981df589 100644 --- a/evm/loomethdb.go +++ b/evm/loomethdb.go @@ -30,7 +30,7 @@ type LoomEthdb struct { func NewLoomEthdb(_state loomchain.State, logContext *ethdbLogContext) *LoomEthdb { p := new(LoomEthdb) - p.state = store.PrefixKVStore(vmPrefix, _state) + p.state = store.PrefixKVStore(VmPrefix, _state) p.logContext = logContext return p } diff --git a/evm/loomevm.go b/evm/loomevm.go index c7a97163b7..74f35cfbc0 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -27,7 +27,7 @@ import ( ) var ( - vmPrefix = []byte("vm") + VmPrefix = []byte("vm") RootKey = []byte("vmroot") ) diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 2baea22428..8ffd39d89a 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethapi" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/loomnetwork/go-loom/util" "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -104,10 +105,8 @@ func StorageRangeAt( app.GetValidatorSet, ) ethDb := evm.NewLoomEthdb(storeState, nil) - root, err := ethDb.Get(evm.RootKey) - if err != nil { - return JsonStorageRangeResult{}, err - } + root := app.Store.Get(util.PrefixKey(evm.VmPrefix, evm.RootKey)) + stateDb, err := state.New(common.BytesToHash(root), state.NewDatabase(ethDb)) if err != nil { return JsonStorageRangeResult{}, err diff --git a/rpc/query_server.go b/rpc/query_server.go index dde905f468..1ce8d8966d 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -1216,8 +1216,8 @@ func (s QueryServer) DebugStorageRangeAt( var blockNumber uint64 if len(blockHashOrNumber) >= tmhash.Size { - if address[:2] == "0x" { - address = address[2:] + if blockHashOrNumber[:2] == "0x" { + blockHashOrNumber = blockHashOrNumber[2:] } hash, err := hex.DecodeString(blockHashOrNumber) if err != nil { diff --git a/store/splitstore.go b/store/splitstore.go index 493ed643cc..5e9bc9d527 100644 --- a/store/splitstore.go +++ b/store/splitstore.go @@ -22,7 +22,7 @@ func NewSplitStore(full KVReader, empty VersionedKVStore, version int64) Version func (ss *splitStore) Get(key []byte) []byte { if ss.VersionedKVStore.Has(key) { - return ss.KVReader.Get(key) + return ss.VersionedKVStore.Get(key) } if ss.deleted[string(key)] { return nil diff --git a/tx_handler/factory/handler_factory.go b/tx_handler/factory/handler_factory.go index 716d3b0567..485ef71365 100644 --- a/tx_handler/factory/handler_factory.go +++ b/tx_handler/factory/handler_factory.go @@ -56,7 +56,7 @@ func (f txHandleFactory) Copy(newStore store.VersionedKVStore) loomchain.TxHandl } // Creates a handle with an entirely new vmManager with dummy account balance manager factory and receipt handler. -func (f txHandleFactory) TxHandlerWithTracer(tracer ethvm.Tracer, metrics bool) (loomchain.TxHandler, error) { +func (f txHandleFactory) TxHandlerWithTracerAndDefaultVmManager(tracer ethvm.Tracer, metrics bool) (loomchain.TxHandler, error) { f.vmManager = createVmManager(tracer) return f.TxHandler(metrics) } From c5874bd75e5b80ea68b06b33d17f62b65745f194 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 18 Nov 2019 09:22:29 +0000 Subject: [PATCH 32/39] reciew issues --- config/config.go | 4 ++-- evm/evm.go | 24 ++++++++++++------------ evm/loomevm.go | 2 +- store/versioned_cachingstore.go | 6 +++++- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/config/config.go b/config/config.go index 64e458f86d..31d82ef49c 100755 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,7 @@ import ( "github.com/loomnetwork/loomchain/auth" plasmacfg "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/config" genesiscfg "github.com/loomnetwork/loomchain/config/genesis" + "github.com/loomnetwork/loomchain/db" "github.com/loomnetwork/loomchain/events" "github.com/loomnetwork/loomchain/evm" hsmpv "github.com/loomnetwork/loomchain/privval/hsm" @@ -26,7 +27,6 @@ import ( "github.com/loomnetwork/loomchain/store" blockindex "github.com/loomnetwork/loomchain/store/block_index" "github.com/loomnetwork/loomchain/throttle" - "github.com/loomnetwork/loomchain/db" "github.com/loomnetwork/loomchain/fnConsensus" ) @@ -174,7 +174,7 @@ func DefaultFnConsensusConfig() *FnConsensusConfig { type EVMTracer struct { Enabled bool // enable tracer - Tracer string //enable JavaScript-based transaction tracing, + Tracer string // enable JavaScript-based transaction tracing DisableMemory bool // disable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture diff --git a/evm/evm.go b/evm/evm.go index b5a27d28eb..a002c224f0 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -303,17 +303,7 @@ func defaultChainConfig(enableConstantinople bool) params.ChainConfig { } func createVmConfig(tracer vm.Tracer) (vm.Config, error) { - if tracer == nil { - logCfg := vm.LogConfig{ - DisableMemory: true, // disable memory capture - DisableStack: true, // disable stack capture - DisableStorage: true, // disable storage capture - Limit: 0, // maximum length of output, but zero means unlimited - } - tracer = vm.NewStructLogger(&logCfg) - } - - return vm.Config{ + vmConfig := vm.Config{ // Debug enabled debugging Interpreter options Debug: tracer != nil, // Tracer is the op code logger @@ -327,5 +317,15 @@ func createVmConfig(tracer vm.Tracer) (vm.Config, error) { // may be left uninitialised and will be set to the default // table. //JumpTable: [256]operation, - }, nil + } + if tracer == nil { + logCfg := vm.LogConfig{ + DisableMemory: true, // disable memory capture + DisableStack: true, // disable stack capture + DisableStorage: true, // disable storage capture + Limit: 0, // maximum length of output, but zero means unlimited + } + vmConfig.Tracer = vm.NewStructLogger(&logCfg) + } + return vmConfig, nil } diff --git a/evm/loomevm.go b/evm/loomevm.go index 74f35cfbc0..5bfb0d1ec7 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -78,7 +78,7 @@ func NewLoomEvm( p.wrappedEVM, err = NewEvm(p.sdb, loomState, abm, tracer) if err != nil { - return nil, errors.Wrap(err, "creating tracer") + return nil, errors.Wrap(err, "failed to create EVM") } return p, nil } diff --git a/store/versioned_cachingstore.go b/store/versioned_cachingstore.go index 2436dbca8d..0abe84af5b 100644 --- a/store/versioned_cachingstore.go +++ b/store/versioned_cachingstore.go @@ -385,8 +385,12 @@ func (c *versionedCachingStore) GetSnapshotAt(version int64) (Snapshot, error) { c.cache, c.version-1, c.logger, ), nil } else { + snapshot, err := c.VersionedKVStore.GetSnapshotAt(version) + if err != nil { + return nil, err + } return newVersionedCachingStoreSnapshot( - c.VersionedKVStore.GetSnapshot(), + snapshot, c.cache, version, c.logger, ), nil } From cb46125b046c886d8673d6d3aa8069a791efd188 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Mon, 18 Nov 2019 11:13:14 +0000 Subject: [PATCH 33/39] debug_storageRangeAt --- .../truffle/contracts/EventTestContract.sol | 1 + e2e/tests/truffle/test/DebugFunctions.js | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/e2e/tests/truffle/contracts/EventTestContract.sol b/e2e/tests/truffle/contracts/EventTestContract.sol index 89e3f8ea81..0aa294a863 100644 --- a/e2e/tests/truffle/contracts/EventTestContract.sol +++ b/e2e/tests/truffle/contracts/EventTestContract.sol @@ -1,3 +1,4 @@ +pragma solidity ^0.5.8; contract EventTestContract { uint256 public value; diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js index 396b368a60..32fcd0217d 100644 --- a/e2e/tests/truffle/test/DebugFunctions.js +++ b/e2e/tests/truffle/test/DebugFunctions.js @@ -42,7 +42,9 @@ contract('debug_traceTransaction', async (accounts) => { }); it('Test debug_traceTransaction', async () => { - const txResult = await contract.methods.set(1111).send(); + const setValue = 1111; + const txResult = await contract.methods.set(setValue).send(); + await waitForXBlocks(nodeAddr, 2) await web3js.currentProvider.send({ method: "debug_traceTransaction", params: [txResult.transactionHash,{"disableStorage":false,"disableMemory":false,"disableStack":false}], @@ -51,7 +53,17 @@ contract('debug_traceTransaction', async (accounts) => { }, function (error, result) { assert.equal(null, error, "debug_traceTransaction returned error"); assert.equal(true, result.result.structLogs.length > 0, "trace did not return any data"); + }); + await web3js.currentProvider.send({ + method: "debug_storageRangeAt", + params: [txResult.blockHash, txResult.transactionIndex, txHashTestContract.address, "", 1000], + jsonrpc: "2.0", + id: new Date().getTime() + }, function (error, result) { + assert.equal(1, Object.keys(result.result.storage).length, "count of items returned by debug_storageRangeAt"); + onlyEntry = result.result.storage[Object.keys(result.result.storage)[0]].value; + assert.equal(setValue, Number(onlyEntry), "memory value same as value set"); }); - await waitForXBlocks(nodeAddr, 1) + await waitForXBlocks(nodeAddr, 2) }) }); \ No newline at end of file From 05ddc25921ea77d69b3ca7b877f033f09a43fe45 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Tue, 19 Nov 2019 13:10:46 +0000 Subject: [PATCH 34/39] review issues --- app.go | 1 - store/versioned_cachingstore.go | 17 ++++++++--------- tx_handler/factory/handler_factory.go | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index b7a30efebb..332c369983 100644 --- a/app.go +++ b/app.go @@ -956,7 +956,6 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) factory := a.TxHandlerFactory.Copy(splitStore) - //txHandle, err := factory.TxHandler(false) txHandle, err := factory.TxHandlerWithTracerAndDefaultVmManager(nil, false) if err != nil { return nil, 0, err diff --git a/store/versioned_cachingstore.go b/store/versioned_cachingstore.go index 0abe84af5b..452964260f 100644 --- a/store/versioned_cachingstore.go +++ b/store/versioned_cachingstore.go @@ -384,16 +384,15 @@ func (c *versionedCachingStore) GetSnapshotAt(version int64) (Snapshot, error) { c.VersionedKVStore.GetSnapshot(), c.cache, c.version-1, c.logger, ), nil - } else { - snapshot, err := c.VersionedKVStore.GetSnapshotAt(version) - if err != nil { - return nil, err - } - return newVersionedCachingStoreSnapshot( - snapshot, - c.cache, version, c.logger, - ), nil } + snapshot, err := c.VersionedKVStore.GetSnapshotAt(version) + if err != nil { + return nil, err + } + return newVersionedCachingStoreSnapshot( + snapshot, + c.cache, version, c.logger, + ), nil } // CachingStoreSnapshot is a read-only CachingStore with specified version diff --git a/tx_handler/factory/handler_factory.go b/tx_handler/factory/handler_factory.go index 485ef71365..7241bcfe23 100644 --- a/tx_handler/factory/handler_factory.go +++ b/tx_handler/factory/handler_factory.go @@ -68,7 +68,7 @@ func (f txHandleFactory) TxHandler(metrics bool) (loomchain.TxHandler, error) { if err != nil { return nil, err } - postCommitMiddlewares, err := postCommitMiddleWAre(f.cfg, *f.vmManager, nonceTxHandler) + postCommitMiddlewares, err := postCommitMiddleware(f.cfg, *f.vmManager, nonceTxHandler) if err != nil { return nil, err } @@ -232,7 +232,7 @@ func router( return router } -func postCommitMiddleWAre( +func postCommitMiddleware( cfg config.Config, vmManager vm.Manager, nonceTxHandler *auth.NonceHandler, From 51a8d62a3a9f9624893fa2a6d863e9ceb845018a Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Tue, 19 Nov 2019 14:53:05 +0000 Subject: [PATCH 35/39] range --- store/splitstore.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/store/splitstore.go b/store/splitstore.go index 5e9bc9d527..120d23274f 100644 --- a/store/splitstore.go +++ b/store/splitstore.go @@ -33,12 +33,12 @@ func (ss *splitStore) Get(key []byte) []byte { func (ss *splitStore) Range(prefix []byte) plugin.RangeData { readerRange := ss.KVReader.Range(prefix) updateRange := ss.VersionedKVStore.Range(prefix) - for _, re := range updateRange { - if !ss.KVReader.Has(re.Key) && !ss.deleted[string(re.Key)] { - readerRange = append(readerRange, re) + for _, re := range readerRange { + if !ss.VersionedKVStore.Has(re.Key) && !ss.deleted[string(re.Key)] { + updateRange = append(updateRange, re) } } - return readerRange + return updateRange } func (ss *splitStore) Has(key []byte) bool { From a9777d002f74db636c62dcf31c741ef4a91c0db1 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 21 Nov 2019 13:20:16 +0000 Subject: [PATCH 36/39] review issues --- app.go | 5 +---- config/config.go | 8 ++++---- rpc/debug/trace.go | 3 +++ store/splitstore.go | 15 ++++++++++++--- store/store_test.go | 2 +- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index 332c369983..ab5685a2fa 100644 --- a/app.go +++ b/app.go @@ -715,9 +715,6 @@ func (a *Application) DeliverTx(txBytes []byte) abci.ResponseDeliverTx { } else { r = a.deliverTx(storeTx, txBytes) } - if a.EVMTracer != nil { - log.Debug("evm trace", "trace", a.EVMTracer) - } txFailed = r.Code != abci.CodeTypeOK // TODO: this isn't 100% reliable when txFailed == true @@ -954,7 +951,7 @@ func (a *Application) ReplayApplication(blockNumber uint64, blockstore store.Blo return nil, 0, errors.Errorf("no saved version for height %d", blockNumber) } - splitStore := store.NewSplitStore(snapshot, store.NewMemStore(), startVersion-1) + splitStore := store.NewSplitStore(snapshot, startVersion-1) factory := a.TxHandlerFactory.Copy(splitStore) txHandle, err := factory.TxHandlerWithTracerAndDefaultVmManager(nil, false) if err != nil { diff --git a/config/config.go b/config/config.go index 31d82ef49c..5c81759cb8 100755 --- a/config/config.go +++ b/config/config.go @@ -145,7 +145,7 @@ type Config struct { AllowNamedEvmContracts bool // Dragons - EVMTracer *EVMTracer + EVMTracer *EVMTracerConfig // Set to true to disable minimum required build number check on node startup SkipMinBuildCheck bool @@ -172,7 +172,7 @@ func DefaultFnConsensusConfig() *FnConsensusConfig { } } -type EVMTracer struct { +type EVMTracerConfig struct { Enabled bool // enable tracer Tracer string // enable JavaScript-based transaction tracing DisableMemory bool // disable memory capture @@ -181,8 +181,8 @@ type EVMTracer struct { Limit int // maximum length of output, but zero means unlimited } -func DefaultEvmTraceConfig() *EVMTracer { - return &EVMTracer{ +func DefaultEvmTraceConfig() *EVMTracerConfig { + return &EVMTracerConfig{ Enabled: false, } } diff --git a/rpc/debug/trace.go b/rpc/debug/trace.go index 8ffd39d89a..df57c2db4a 100644 --- a/rpc/debug/trace.go +++ b/rpc/debug/trace.go @@ -150,6 +150,9 @@ func runTxsTo(app *loomchain.Application, blockstore store.BlockStore, startHeig } _ = app.EndBlock(requestEndBlock(h)) + + // todo we should really be matching the app hash returned here with the one that was + // stored in the next block, otherwise we can't really know if the replay was successful or not. _ = app.Commit() } return nil, errors.Errorf("cannot find transaction at height %d index %d", height, index) diff --git a/store/splitstore.go b/store/splitstore.go index 120d23274f..9bfa4f0a27 100644 --- a/store/splitstore.go +++ b/store/splitstore.go @@ -1,6 +1,9 @@ package store import ( + "bytes" + "sort" + "github.com/loomnetwork/go-loom/plugin" ) @@ -11,10 +14,10 @@ type splitStore struct { version int64 } -func NewSplitStore(full KVReader, empty VersionedKVStore, version int64) VersionedKVStore { +func NewSplitStore(history KVReader, version int64) VersionedKVStore { return &splitStore{ - KVReader: full, - VersionedKVStore: empty, + KVReader: history, + VersionedKVStore: NewMemStore(), deleted: make(map[string]bool), version: version, } @@ -33,6 +36,12 @@ func (ss *splitStore) Get(key []byte) []byte { func (ss *splitStore) Range(prefix []byte) plugin.RangeData { readerRange := ss.KVReader.Range(prefix) updateRange := ss.VersionedKVStore.Range(prefix) + + // VersionedKVStore comes from a MemStore, hence updateRange is not deterministic + sort.Slice(updateRange, func(i, j int) bool { + return bytes.Compare(updateRange[i].Key, updateRange[j].Key) < 0 + }) + for _, re := range readerRange { if !ss.VersionedKVStore.Has(re.Key) && !ss.deleted[string(re.Key)] { updateRange = append(updateRange, re) diff --git a/store/store_test.go b/store/store_test.go index 1d51792ed5..879ed969d8 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -450,7 +450,7 @@ type SplitStoreTestSuite struct { // runs before each test in this suite func (ts *SplitStoreTestSuite) SetupTest() { - ts.store = NewSplitStore(NewMemStore(), NewMemStore(), 1) + ts.store = NewSplitStore(NewMemStore(), 1) } func (ts *SplitStoreTestSuite) SetupSuite() { From 4a27262c4544aca4aadab068de28658f8f5354cd Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Thu, 21 Nov 2019 13:31:43 +0000 Subject: [PATCH 37/39] restore --- app.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index ab5685a2fa..b8d0c5d55d 100644 --- a/app.go +++ b/app.go @@ -715,7 +715,9 @@ func (a *Application) DeliverTx(txBytes []byte) abci.ResponseDeliverTx { } else { r = a.deliverTx(storeTx, txBytes) } - + if a.EVMTracer != nil { + log.Debug("evm trace", "trace", a.EVMTracer) + } txFailed = r.Code != abci.CodeTypeOK // TODO: this isn't 100% reliable when txFailed == true isEvmTx = r.Info == utils.CallEVM || r.Info == utils.DeployEvm From d73f8e6d08ac1650e9adf14654f17ba28aaca09b Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 27 Nov 2019 09:28:10 +0000 Subject: [PATCH 38/39] sort range --- store/splitstore.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/store/splitstore.go b/store/splitstore.go index 9bfa4f0a27..b4a24fd561 100644 --- a/store/splitstore.go +++ b/store/splitstore.go @@ -36,17 +36,15 @@ func (ss *splitStore) Get(key []byte) []byte { func (ss *splitStore) Range(prefix []byte) plugin.RangeData { readerRange := ss.KVReader.Range(prefix) updateRange := ss.VersionedKVStore.Range(prefix) - - // VersionedKVStore comes from a MemStore, hence updateRange is not deterministic - sort.Slice(updateRange, func(i, j int) bool { - return bytes.Compare(updateRange[i].Key, updateRange[j].Key) < 0 - }) - for _, re := range readerRange { if !ss.VersionedKVStore.Has(re.Key) && !ss.deleted[string(re.Key)] { updateRange = append(updateRange, re) } } + // VersionedKVStore comes from a MemStore, hence updateRange is not deterministic + sort.Slice(updateRange, func(i, j int) bool { + return bytes.Compare(updateRange[i].Key, updateRange[j].Key) < 0 + }) return updateRange } From d98b4a5456632bf7601b38ee6009cc6707c80fc5 Mon Sep 17 00:00:00 2001 From: Piers Shepperson Date: Wed, 27 Nov 2019 09:56:39 +0000 Subject: [PATCH 39/39] touch --- store/splitstore.go | 1 + 1 file changed, 1 insertion(+) diff --git a/store/splitstore.go b/store/splitstore.go index b4a24fd561..b4c4d9cd7b 100644 --- a/store/splitstore.go +++ b/store/splitstore.go @@ -41,6 +41,7 @@ func (ss *splitStore) Range(prefix []byte) plugin.RangeData { updateRange = append(updateRange, re) } } + // VersionedKVStore comes from a MemStore, hence updateRange is not deterministic sort.Slice(updateRange, func(i, j int) bool { return bytes.Compare(updateRange[i].Key, updateRange[j].Key) < 0