diff --git a/Makefile b/Makefile index 1342d60436..1ec42c90c2 100644 --- a/Makefile +++ b/Makefile @@ -31,9 +31,9 @@ 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 = config # 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 diff --git a/app.go b/app.go index 3ebe298076..75de4cfc89 100644 --- a/app.go +++ b/app.go @@ -7,18 +7,22 @@ import ( "fmt" "time" - "github.com/loomnetwork/loomchain/eth/utils" - "github.com/loomnetwork/loomchain/features" - "github.com/loomnetwork/loomchain/registry" - + "github.com/ethereum/go-ethereum/core/vm" "github.com/go-kit/kit/metrics" kitprometheus "github.com/go-kit/kit/metrics/prometheus" cctypes "github.com/loomnetwork/go-loom/builtin/types/chainconfig" + "github.com/pkg/errors" + + // "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/common" ttypes "github.com/tendermint/tendermint/types" + "github.com/loomnetwork/loomchain/eth/utils" + "github.com/loomnetwork/loomchain/features" + "github.com/loomnetwork/loomchain/registry" + "github.com/loomnetwork/loomchain/txhandler" + "github.com/loomnetwork/loomchain/log" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" @@ -26,25 +30,6 @@ import ( evmaux "github.com/loomnetwork/loomchain/store/evm_aux" ) -type TxHandler interface { - ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) -} - -type TxHandlerFunc func(state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) - -type TxHandlerResult struct { - Data []byte - ValidatorUpdates []abci.Validator - Info string - // Tags to associate with the tx that produced this result. Tags can be used to filter txs - // via the ABCI query interface (see https://godoc.org/github.com/tendermint/tendermint/libs/pubsub/query) - Tags []common.KVPair -} - -func (f TxHandlerFunc) ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { - return f(state, txBytes, isCheckTx) -} - type QueryHandler interface { Handle(state appstate.ReadOnlyState, path string, data []byte) ([]byte, error) } @@ -73,10 +58,11 @@ type Application struct { curBlockHash []byte Store store.VersionedKVStore Init func(appstate.State) error - TxHandler + txhandler.TxHandler QueryHandler EventHandler ReceiptHandlerProvider + txhandler.TxHandlerFactory EvmAuxStore *evmaux.EvmAuxStore blockindex.BlockIndexStore CreateValidatorManager ValidatorsManagerFactoryFunc @@ -89,6 +75,7 @@ type Application struct { config *cctypes.Config childTxRefs []evmaux.ChildTxRef // links Tendermint txs to EVM txs ReceiptsVersion int32 + DebugTxHandler txhandler.TxHandler } var _ abci.Application = &Application{} @@ -304,13 +291,16 @@ 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 { + 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() @@ -438,7 +428,7 @@ func (a *Application) deliverTx(storeTx store.KVStoreTx, txBytes []byte) abci.Re return abci.ResponseDeliverTx{Code: abci.CodeTypeOK, Data: r.Data, Tags: r.Tags, Info: r.Info} } -func (a *Application) processTx(storeTx store.KVStoreTx, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { +func (a *Application) processTx(storeTx store.KVStoreTx, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { state := appstate.NewStoreState( context.Background(), storeTx, @@ -499,16 +489,17 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R a.curBlockHash, a.GetValidatorSet, ).WithOnChainConfig(a.config) - - receiptHandler := a.ReceiptHandlerProvider.Store() - defer receiptHandler.DiscardCurrentReceipt() - defer a.EventHandler.Rollback() + if a.ReceiptHandlerProvider != nil { + //receiptHandler := a.ReceiptHandlerProvider.Store() + defer a.ReceiptHandlerProvider.Store().DiscardCurrentReceipt() + 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 @@ -519,7 +510,7 @@ func (a *Application) deliverTx2(storeTx store.KVStoreTx, txBytes []byte) abci.R ChildTxHash: receiptTxHash, }) } - receiptHandler.CommitCurrentReceipt() + a.ReceiptHandlerProvider.Store().CommitCurrentReceipt() } if txErr != nil { @@ -530,13 +521,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 { @@ -563,23 +556,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 { @@ -611,6 +609,7 @@ func (a *Application) Query(req abci.RequestQuery) abci.ResponseQuery { func (a *Application) height() int64 { return a.Store.Version() + 1 } + func (a *Application) ReadOnlyState() appstate.State { // TODO: the store snapshot should be created atomically, otherwise the block header might // not match the state... need to figure out why this hasn't spectacularly failed already @@ -622,3 +621,53 @@ func (a *Application) ReadOnlyState() appstate.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 appstate.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/auth/auth.go b/auth/auth.go index 1006b2c334..3eda3aa50a 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -13,9 +13,11 @@ import ( loom "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/util" - "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth/keys" + "github.com/loomnetwork/loomchain/auth/sequence" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" ) var ( @@ -31,28 +33,13 @@ func init() { }, []string{}) } -type contextKey string - -func (c contextKey) String() string { - return "auth " + string(c) -} - -var ( - ContextKeyOrigin = contextKey("origin") - ContextKeyCheckTx = contextKey("CheckTx") -) - -func Origin(ctx context.Context) loom.Address { - return ctx.Value(ContextKeyOrigin).(loom.Address) -} - -var SignatureTxMiddleware = loomchain.TxMiddlewareFunc(func( +var SignatureTxMiddleware = txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult var tx SignedTx err := proto.Unmarshal(txBytes, &tx) @@ -65,7 +52,7 @@ var SignatureTxMiddleware = loomchain.TxMiddlewareFunc(func( return r, err } - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) return next(state.WithContext(ctx), tx.Inner, isCheckTx) }) @@ -93,7 +80,7 @@ func nonceKey(addr loom.Address) []byte { } func Nonce(state appstate.ReadOnlyState, addr loom.Address) uint64 { - return loomchain.NewSequence(nonceKey(addr)).Value(state) + return sequence.NewSequence(nonceKey(addr)).Value(state) } type NonceHandler struct { @@ -109,11 +96,11 @@ func (n *NonceHandler) Nonce( state appstate.State, kvStore store.KVStore, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult - origin := Origin(state.Context()) +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult + origin := keys.Origin(state.Context()) if origin.IsEmpty() { return r, errors.New("transaction has no origin [nonce]") } @@ -127,9 +114,9 @@ func (n *NonceHandler) Nonce( incrementNonceOnFailedTx := state.Config().GetNonceHandler().GetIncNonceOnFailedTx() if incrementNonceOnFailedTx && !isCheckTx { // Unconditionally increment the nonce in DeliverTx, regardless of whether the tx succeeds - seq = loomchain.NewSequence(nonceKey(origin)).Next(kvStore) + seq = sequence.NewSequence(nonceKey(origin)).Next(kvStore) } else { - seq = loomchain.NewSequence(nonceKey(origin)).Next(state) + seq = sequence.NewSequence(nonceKey(origin)).Next(state) } var tx NonceTx @@ -171,11 +158,11 @@ func (n *NonceHandler) Nonce( func (n *NonceHandler) IncNonce( state appstate.State, txBytes []byte, - result loomchain.TxHandlerResult, - postcommit loomchain.PostCommitHandler, + result txhandler.TxHandlerResult, + postcommit txhandler.PostCommitHandler, isCheckTx bool, ) error { - origin := Origin(state.Context()) + origin := keys.Origin(state.Context()) if origin.IsEmpty() { return errors.New("transaction has no origin [IncNonce]") } @@ -192,17 +179,17 @@ func (n *NonceHandler) IncNonce( return nil } -func (n *NonceHandler) TxMiddleware(kvStore store.KVStore) loomchain.TxMiddlewareFunc { - return loomchain.TxMiddlewareFunc(func( +func (n *NonceHandler) TxMiddleware(kvStore store.KVStore) txhandler.TxMiddlewareFunc { + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (res loomchain.TxHandlerResult, err error) { + ) (res txhandler.TxHandlerResult, err error) { return n.Nonce(state, kvStore, txBytes, next, isCheckTx) }) } -func (n *NonceHandler) PostCommitMiddleware() loomchain.PostCommitMiddlewareFunc { - return loomchain.PostCommitMiddlewareFunc(n.IncNonce) +func (n *NonceHandler) PostCommitMiddleware() txhandler.PostCommitMiddlewareFunc { + return txhandler.PostCommitMiddlewareFunc(n.IncNonce) } diff --git a/auth/auth_test.go b/auth/auth_test.go index b662bbe264..ef657ccb2a 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -14,9 +14,10 @@ import ( "github.com/loomnetwork/go-loom/auth" "github.com/loomnetwork/go-loom/config" - "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth/keys" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" ) func TestSignatureTxMiddleware(t *testing.T) { @@ -31,9 +32,9 @@ func TestSignatureTxMiddleware(t *testing.T) { require.NoError(t, err) state := appstate.NewStoreState(nil, store.NewMemStore(), abci.Header{}, nil, nil) SignatureTxMiddleware.ProcessTx(state, signedTxBytes, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { require.Equal(t, txBytes, origBytes) - return loomchain.TxHandlerResult{}, nil + return txhandler.TxHandlerResult{}, nil }, false, ) } @@ -67,76 +68,76 @@ func TestSignatureTxMiddlewareMultipleTxSameBlock(t *testing.T) { cfg := config.DefaultConfig() cfg.NonceHandler.IncNonceOnFailedTx = true - ctx := context.WithValue(context.Background(), ContextKeyOrigin, origin) - ctx = context.WithValue(ctx, ContextKeyCheckTx, true) + ctx := context.WithValue(context.Background(), keys.ContextKeyOrigin, origin) + ctx = context.WithValue(ctx, keys.ContextKeyCheckTx, true) kvStore := store.NewMemStore() state := appstate.NewStoreState(ctx, kvStore, abci.Header{Height: 27}, nil, nil).WithOnChainConfig(cfg) _, err = nonceTxHandler.Nonce(state, kvStore, nonceTxBytes, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, false, ) require.Nil(t, err) - nonceTxPostNonceMiddleware(state, nonceTxBytes, loomchain.TxHandlerResult{}, nil, false) + nonceTxPostNonceMiddleware(state, nonceTxBytes, txhandler.TxHandlerResult{}, nil, false) //State is reset on every run - ctx2 := context.WithValue(context.Background(), ContextKeyOrigin, origin) + ctx2 := context.WithValue(context.Background(), keys.ContextKeyOrigin, origin) kvStore2 := store.NewMemStore() state2 := appstate.NewStoreState(ctx2, kvStore2, abci.Header{Height: 27}, nil, nil).WithOnChainConfig(cfg) - _ = context.WithValue(ctx2, ContextKeyCheckTx, true) + _ = context.WithValue(ctx2, keys.ContextKeyCheckTx, true) //If we get the same sequence number in same block we should get an error _, err = nonceTxHandler.Nonce(state2, kvStore2, nonceTxBytes, - func(state2 appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state2 appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, true, ) require.Errorf(t, err, "sequence number does not match") // nonceTxPostNonceMiddleware shouldnt get called on an error //State is reset on every run - ctx3 := context.WithValue(context.Background(), ContextKeyOrigin, origin) + ctx3 := context.WithValue(context.Background(), keys.ContextKeyOrigin, origin) kvStore3 := store.NewMemStore() state3 := appstate.NewStoreState(ctx3, kvStore3, abci.Header{Height: 27}, nil, nil).WithOnChainConfig(cfg) - _ = context.WithValue(ctx3, ContextKeyCheckTx, true) + _ = context.WithValue(ctx3, keys.ContextKeyCheckTx, true) //If we get to tx with incrementing sequence numbers we should be fine in the same block _, err = nonceTxHandler.Nonce(state3, kvStore3, nonceTxBytes2, - func(state3 appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state3 appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, true, ) require.Nil(t, err) - nonceTxPostNonceMiddleware(state, nonceTxBytes2, loomchain.TxHandlerResult{}, nil, false) + nonceTxPostNonceMiddleware(state, nonceTxBytes2, txhandler.TxHandlerResult{}, nil, false) //Try a deliverTx at same height it should be fine - ctx3Dx := context.WithValue(context.Background(), ContextKeyOrigin, origin) + ctx3Dx := context.WithValue(context.Background(), keys.ContextKeyOrigin, origin) kvStore3Dx := store.NewMemStore() state3Dx := appstate.NewStoreState(ctx3Dx, kvStore3Dx, abci.Header{Height: 27}, nil, nil).WithOnChainConfig(cfg) - _ = context.WithValue(ctx3Dx, ContextKeyCheckTx, true) + _ = context.WithValue(ctx3Dx, keys.ContextKeyCheckTx, true) _, err = nonceTxHandler.Nonce(state3Dx, kvStore3Dx, nonceTxBytes, - func(state3 appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state3 appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, false, ) require.Nil(t, err) - nonceTxPostNonceMiddleware(state, nonceTxBytes, loomchain.TxHandlerResult{}, nil, false) + nonceTxPostNonceMiddleware(state, nonceTxBytes, txhandler.TxHandlerResult{}, nil, false) ///--------------increase block height should kill cache //State is reset on every run - ctx4 := context.WithValue(nil, ContextKeyOrigin, origin) + ctx4 := context.WithValue(nil, keys.ContextKeyOrigin, origin) kvStore4 := store.NewMemStore() state4 := appstate.NewStoreState(ctx4, kvStore4, abci.Header{Height: 28}, nil, nil).WithOnChainConfig(cfg) //If we get to tx with incrementing sequence numbers we should be fine in the same block _, err = nonceTxHandler.Nonce(state4, kvStore4, nonceTxBytes, - func(state4 appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state4 appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, true, ) require.Nil(t, err) - nonceTxPostNonceMiddleware(state, nonceTxBytes, loomchain.TxHandlerResult{}, nil, false) + nonceTxPostNonceMiddleware(state, nonceTxBytes, txhandler.TxHandlerResult{}, nil, false) } func TestRevertedTxNonceMiddleware(t *testing.T) { @@ -168,8 +169,8 @@ func TestRevertedTxNonceMiddleware(t *testing.T) { Local: loom.LocalAddressFromPublicKey(pubkey), } - ctx := context.WithValue(context.Background(), ContextKeyOrigin, origin) - ctx = context.WithValue(ctx, ContextKeyCheckTx, true) + ctx := context.WithValue(context.Background(), keys.ContextKeyOrigin, origin) + ctx = context.WithValue(ctx, keys.ContextKeyCheckTx, true) kvStore := store.NewMemStore() storeTx := store.WrapAtomic(kvStore).BeginTx() state := appstate.NewStoreState(ctx, storeTx, abci.Header{Height: 27}, nil, nil).WithOnChainConfig(cfg) @@ -180,19 +181,19 @@ func TestRevertedTxNonceMiddleware(t *testing.T) { // Send a successful tx _, err = nonceTxHandler.Nonce(state, kvStore, nonceTxBytes, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, nil + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, nil }, false, ) require.Nil(t, err) - nonceTxPostNonceMiddleware(state, nonceTxBytes, loomchain.TxHandlerResult{}, nil, false) + nonceTxPostNonceMiddleware(state, nonceTxBytes, txhandler.TxHandlerResult{}, nil, false) storeTx.Commit() storeTx.Rollback() // Send a failed tx, nonce should increase even though the transaction is reverted _, err = nonceTxHandler.Nonce(state, kvStore, nonceTxBytes2, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, errors.New("EVM transaction reverted") + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, errors.New("EVM transaction reverted") }, false, ) require.Error(t, err) @@ -212,8 +213,8 @@ func TestRevertedTxNonceMiddleware(t *testing.T) { // Send another failed tx, nonce should not increment because the transaction reverted _, err = nonceTxHandler.Nonce(state, kvStore, nonceTxBytes3, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { - return loomchain.TxHandlerResult{}, errors.New("EVM transaction reverted") + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + return txhandler.TxHandlerResult{}, errors.New("EVM transaction reverted") }, false, ) require.Error(t, err) diff --git a/auth/chainconfig_middleware.go b/auth/chainconfig_middleware.go index c2eae4e54f..2a117a7c63 100644 --- a/auth/chainconfig_middleware.go +++ b/auth/chainconfig_middleware.go @@ -5,24 +5,25 @@ import ( loom "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/plugin/contractpb" - "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" ) // NewChainConfigMiddleware returns middleware that verifies signed txs using either // SignedTxMiddleware or MultiChainSignatureTxMiddleware, it switches the underlying middleware // based on the on-chain and off-chain auth config settings. func NewChainConfigMiddleware( - authConfig *Config, + authConfig *keys.Config, createAddressMapperCtx func(state appstate.State) (contractpb.StaticContext, error), -) loomchain.TxMiddlewareFunc { - return loomchain.TxMiddlewareFunc(func( +) txhandler.TxMiddlewareFunc { + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (loomchain.TxHandlerResult, error) { + ) (txhandler.TxHandlerResult, error) { chains := getEnabledChains(authConfig.Chains, state) if len(chains) > 0 { mw := NewMultiChainSignatureTxMiddleware(chains, createAddressMapperCtx) @@ -34,8 +35,8 @@ func NewChainConfigMiddleware( } // Filters out any auth.ChainConfig(s) that haven't been enabled by the majority of validators. -func getEnabledChains(chains map[string]ChainConfig, state appstate.State) map[string]ChainConfig { - enabledChains := map[string]ChainConfig{} +func getEnabledChains(chains map[string]keys.ChainConfig, state appstate.State) map[string]keys.ChainConfig { + enabledChains := map[string]keys.ChainConfig{} for chainID, config := range chains { if state.FeatureEnabled(features.AuthSigTxFeaturePrefix+chainID, false) { enabledChains[chainID] = config @@ -46,9 +47,9 @@ func getEnabledChains(chains map[string]ChainConfig, state appstate.State) map[s if len(enabledChains) > 0 { curChainID := state.Block().ChainID if _, found := enabledChains[curChainID]; !found { - enabledChains[curChainID] = ChainConfig{ - TxType: LoomSignedTxType, - AccountType: NativeAccountType, + enabledChains[curChainID] = keys.ChainConfig{ + TxType: keys.LoomSignedTxType, + AccountType: keys.NativeAccountType, } } } @@ -58,7 +59,7 @@ func getEnabledChains(chains map[string]ChainConfig, state appstate.State) map[s // ResolveAccountAddress takes a local or foreign account address and returns the address used // to identify the account on this chain. func ResolveAccountAddress( - account loom.Address, state appstate.State, authCfg *Config, + account loom.Address, state appstate.State, authCfg *keys.Config, createAddressMapperCtx func(state appstate.State) (contractpb.StaticContext, error), ) (loom.Address, error) { chains := getEnabledChains(authCfg.Chains, state) @@ -69,10 +70,10 @@ func ResolveAccountAddress( } switch chain.AccountType { - case NativeAccountType: + case keys.NativeAccountType: return account, nil - case MappedAccountType: + case keys.MappedAccountType: addr, err := getMappedAccountAddress(state, account, createAddressMapperCtx) if err != nil { return loom.Address{}, err diff --git a/auth/chainconfig_middleware_test.go b/auth/chainconfig_middleware_test.go index 1450ca01d2..85d111435d 100644 --- a/auth/chainconfig_middleware_test.go +++ b/auth/chainconfig_middleware_test.go @@ -10,11 +10,14 @@ import ( "github.com/loomnetwork/go-loom/auth" goloomplugin "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/contractpb" - "github.com/loomnetwork/loomchain" + + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/address_mapper" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "golang.org/x/crypto/ed25519" @@ -34,15 +37,15 @@ func TestChainConfigMiddlewareSingleChain(t *testing.T) { fakeCtx := goloomplugin.CreateFakeContext(addr1, addr1) addresMapperAddr := fakeCtx.CreateContract(address_mapper.Contract) amCtx := contractpb.WrapPluginContext(fakeCtx.WithAddress(addresMapperAddr)) - authConfig := Config{ - Chains: map[string]ChainConfig{}, + authConfig := keys.Config{ + Chains: map[string]keys.ChainConfig{}, } chainConfigMiddleware := NewChainConfigMiddleware(&authConfig, func(state appstate.State) (contractpb.StaticContext, error) { return amCtx, nil }) _, err = chainConfigMiddleware.ProcessTx(state, signedTxBytes, - func(state appstate.State, txBytes []byte, isCheckTx bool) (loomchain.TxHandlerResult, error) { + func(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { require.Equal(t, txBytes, origBytes) - return loomchain.TxHandlerResult{}, nil + return txhandler.TxHandlerResult{}, nil }, false, ) require.NoError(t, err) @@ -58,27 +61,27 @@ func TestChainConfigMiddlewareMultipleChain(t *testing.T) { addresMapperAddr := fakeCtx.CreateContract(address_mapper.Contract) amCtx := contractpb.WrapPluginContext(fakeCtx.WithAddress(addresMapperAddr)) - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) - chains := map[string]ChainConfig{ + chains := map[string]keys.ChainConfig{ "default": { - TxType: LoomSignedTxType, - AccountType: NativeAccountType, + TxType: keys.LoomSignedTxType, + AccountType: keys.NativeAccountType, }, "eth": { - TxType: EthereumSignedTxType, - AccountType: MappedAccountType, + TxType: keys.EthereumSignedTxType, + AccountType: keys.MappedAccountType, }, "tron": { - TxType: TronSignedTxType, - AccountType: MappedAccountType, + TxType: keys.TronSignedTxType, + AccountType: keys.MappedAccountType, }, "binance": { - TxType: BinanceSignedTxType, - AccountType: MappedAccountType, + TxType: keys.BinanceSignedTxType, + AccountType: keys.MappedAccountType, }, } - authConfig := Config{ + authConfig := keys.Config{ Chains: chains, } diff --git a/auth/config.go b/auth/keys/config.go similarity index 98% rename from auth/config.go rename to auth/keys/config.go index 582b4a74d6..ab9e53bd81 100644 --- a/auth/config.go +++ b/auth/keys/config.go @@ -1,4 +1,4 @@ -package auth +package keys type Config struct { // Per-chain tx signing config, indexed by chain ID diff --git a/auth/keys/keys.go b/auth/keys/keys.go new file mode 100644 index 0000000000..e92c964f54 --- /dev/null +++ b/auth/keys/keys.go @@ -0,0 +1,43 @@ +package keys + +import ( + "context" + + "github.com/loomnetwork/go-loom" +) + +type SignedTxType string + +const ( + LoomSignedTxType SignedTxType = "loom" + EthereumSignedTxType SignedTxType = "eth" + TronSignedTxType SignedTxType = "tron" + BinanceSignedTxType SignedTxType = "binance" +) + +// AccountType is used to specify which address should be used on-chain to identify a tx sender. +type AccountType int + +const ( + // NativeAccountType indicates that the tx sender address should be passed through to contracts + // as is, with the original chain ID intact. + NativeAccountType AccountType = iota + // MappedAccountType indicates that the tx sender address should be mapped to a DAppChain + // address before being passed through to contracts. + MappedAccountType +) + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + ContextKeyOrigin = contextKey("origin") + ContextKeyCheckTx = contextKey("CheckTx") +) + +func Origin(ctx context.Context) loom.Address { + return ctx.Value(ContextKeyOrigin).(loom.Address) +} diff --git a/auth/multi_chain_sigtx_middleware.go b/auth/multi_chain_sigtx_middleware.go index 574ac67f6d..679e44d99a 100644 --- a/auth/multi_chain_sigtx_middleware.go +++ b/auth/multi_chain_sigtx_middleware.go @@ -12,21 +12,14 @@ import ( "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" "github.com/loomnetwork/go-loom/vm" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/builtin/plugins/address_mapper" - "github.com/loomnetwork/loomchain/features" - appstate "github.com/loomnetwork/loomchain/state" "github.com/pkg/errors" "golang.org/x/crypto/ed25519" -) - -type SignedTxType string -const ( - LoomSignedTxType SignedTxType = "loom" - EthereumSignedTxType SignedTxType = "eth" - TronSignedTxType SignedTxType = "tron" - BinanceSignedTxType SignedTxType = "binance" + "github.com/loomnetwork/loomchain/auth/keys" + "github.com/loomnetwork/loomchain/builtin/plugins/address_mapper" + "github.com/loomnetwork/loomchain/features" + appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" ) // AccountType is used to specify which address should be used on-chain to identify a tx sender. @@ -47,16 +40,16 @@ type originRecoveryFunc func(chainID string, tx SignedTx, allowedSigTypes []evmc // NewMultiChainSignatureTxMiddleware returns tx signing middleware that supports a set of chain // specific signing algos. func NewMultiChainSignatureTxMiddleware( - chains map[string]ChainConfig, + chains map[string]keys.ChainConfig, createAddressMapperCtx func(state appstate.State) (contractpb.StaticContext, error), -) loomchain.TxMiddlewareFunc { - return loomchain.TxMiddlewareFunc(func( +) txhandler.TxMiddlewareFunc { + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult + ) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult var signedTx SignedTx if err := proto.Unmarshal(txBytes, &signedTx); err != nil { @@ -110,11 +103,11 @@ func NewMultiChainSignatureTxMiddleware( } switch chain.AccountType { - case NativeAccountType: // pass through origin & message sender as is - ctx := context.WithValue(state.Context(), ContextKeyOrigin, msgSender) + case keys.NativeAccountType: // pass through origin & message sender as is + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, msgSender) return next(state.WithContext(ctx), signedTx.Inner, isCheckTx) - case MappedAccountType: // map origin & message sender to an address on this chain + case keys.MappedAccountType: // map origin & message sender to an address on this chain origin, err := getMappedAccountAddress(state, msgSender, createAddressMapperCtx) if err != nil { return r, err @@ -138,7 +131,7 @@ func NewMultiChainSignatureTxMiddleware( return r, errors.Wrap(err, "failed to marshal NonceTx") } - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) return next(state.WithContext(ctx), nonceTxBytes, isCheckTx) default: @@ -147,18 +140,18 @@ func NewMultiChainSignatureTxMiddleware( }) } -func getOriginRecoveryFunc(state appstate.State, txID types.TxID, txType SignedTxType) originRecoveryFunc { +func getOriginRecoveryFunc(state appstate.State, txID types.TxID, txType keys.SignedTxType) originRecoveryFunc { switch txType { - case LoomSignedTxType: + case keys.LoomSignedTxType: return verifyEd25519 - case EthereumSignedTxType: + case keys.EthereumSignedTxType: if (txID == types.TxID_ETHEREUM) && state.FeatureEnabled(features.EthTxFeature, false) { return VerifyWrappedEthTx } return verifySolidity66Byte - case TronSignedTxType: + case keys.TronSignedTxType: return verifyTron - case BinanceSignedTxType: + case keys.BinanceSignedTxType: return verifyBinance } return nil diff --git a/auth/multi_chain_sigtx_middleware_test.go b/auth/multi_chain_sigtx_middleware_test.go index 956adbe6a0..6dfdeb6ddc 100644 --- a/auth/multi_chain_sigtx_middleware_test.go +++ b/auth/multi_chain_sigtx_middleware_test.go @@ -12,28 +12,30 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/gogo/protobuf/proto" - loom "github.com/loomnetwork/go-loom" + "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/auth" amtypes "github.com/loomnetwork/go-loom/builtin/types/address_mapper" "github.com/loomnetwork/go-loom/common/evmcompat" goloomplugin "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" + gltypes "github.com/loomnetwork/go-loom/types" "github.com/loomnetwork/go-loom/vm" sha3 "github.com/miguelmota/go-solidity-sha3" "github.com/pkg/errors" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/address_mapper" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" ) const ( - sequence = uint64(4) + seq = uint64(4) // todo defaultLoomChainId = "default" ) @@ -51,11 +53,13 @@ var ( func TestSigning(t *testing.T) { pk, err := crypto.GenerateKey() + fmt.Printf("private key %x\n", pk) require.NoError(t, err) err = crypto.SaveECDSA("newpk", pk) require.NoError(t, err) privateKey, err := crypto.HexToECDSA(ethPrivateKey) + fmt.Printf("private key %x\n", privateKey) require.NoError(t, err) publicKey := crypto.FromECDSAPub(&privateKey.PublicKey) require.NoError(t, err) @@ -178,16 +182,16 @@ func TestEthAddressMappingVerification(t *testing.T) { addresMapperAddr := fakeCtx.CreateContract(address_mapper.Contract) amCtx := contractpb.WrapPluginContext(fakeCtx.WithAddress(addresMapperAddr)) - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) - chains := map[string]ChainConfig{ + chains := map[string]keys.ChainConfig{ "default": { - TxType: LoomSignedTxType, - AccountType: NativeAccountType, + TxType: keys.LoomSignedTxType, + AccountType: keys.NativeAccountType, }, "eth": { - TxType: EthereumSignedTxType, - AccountType: MappedAccountType, + TxType: keys.EthereumSignedTxType, + AccountType: keys.MappedAccountType, }, } tmx := NewMultiChainSignatureTxMiddleware( @@ -247,16 +251,16 @@ func TestBinanceAddressMappingVerification(t *testing.T) { addresMapperAddr := fakeCtx.CreateContract(address_mapper.Contract) amCtx := contractpb.WrapPluginContext(fakeCtx.WithAddress(addresMapperAddr)) - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) - chains := map[string]ChainConfig{ + chains := map[string]keys.ChainConfig{ "default": { - TxType: LoomSignedTxType, - AccountType: NativeAccountType, + TxType: keys.LoomSignedTxType, + AccountType: keys.NativeAccountType, }, "binance": { - TxType: BinanceSignedTxType, - AccountType: MappedAccountType, + TxType: keys.BinanceSignedTxType, + AccountType: keys.MappedAccountType, }, } tmx := NewMultiChainSignatureTxMiddleware( @@ -316,24 +320,24 @@ func TestChainIdVerification(t *testing.T) { addresMapperAddr := fakeCtx.CreateContract(address_mapper.Contract) amCtx := contractpb.WrapPluginContext(fakeCtx.WithAddress(addresMapperAddr)) - ctx := context.WithValue(state.Context(), ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) - chains := map[string]ChainConfig{ + chains := map[string]keys.ChainConfig{ "default": { - TxType: LoomSignedTxType, - AccountType: NativeAccountType, + TxType: keys.LoomSignedTxType, + AccountType: keys.NativeAccountType, }, "eth": { - TxType: EthereumSignedTxType, - AccountType: NativeAccountType, + TxType: keys.EthereumSignedTxType, + AccountType: keys.NativeAccountType, }, "tron": { - TxType: TronSignedTxType, - AccountType: NativeAccountType, + TxType: keys.TronSignedTxType, + AccountType: keys.NativeAccountType, }, "binance": { - TxType: BinanceSignedTxType, - AccountType: NativeAccountType, + TxType: keys.BinanceSignedTxType, + AccountType: keys.NativeAccountType, }, } tmx := NewMultiChainSignatureTxMiddleware( @@ -378,16 +382,16 @@ func TestChainIdVerification(t *testing.T) { require.NoError(t, err) } -func throttleMiddlewareHandler(ttm loomchain.TxMiddlewareFunc, state appstate.State, signedTx []byte, ctx context.Context) (loomchain.TxHandlerResult, error) { +func throttleMiddlewareHandler(ttm txhandler.TxMiddlewareFunc, state appstate.State, signedTx []byte, ctx context.Context) (txhandler.TxHandlerResult, error) { return ttm.ProcessTx(state.WithContext(ctx), signedTx, - func(state appstate.State, txBytes []byte, isCheckTx bool) (res loomchain.TxHandlerResult, err error) { + func(state appstate.State, txBytes []byte, isCheckTx bool) (res txhandler.TxHandlerResult, err error) { var nonceTx NonceTx if err := proto.Unmarshal(txBytes, &nonceTx); err != nil { return res, errors.Wrap(err, "throttle: unwrap nonce Tx") } - var tx loomchain.Transaction + var tx gltypes.Transaction if err := proto.Unmarshal(nonceTx.Inner, &tx); err != nil { return res, errors.New("throttle: unmarshal tx") } @@ -396,12 +400,12 @@ func throttleMiddlewareHandler(ttm loomchain.TxMiddlewareFunc, state appstate.St return res, errors.Wrapf(err, "unmarshal message tx %v", tx.Data) } - origin := Origin(state.Context()) + origin := keys.Origin(state.Context()) caller := loom.UnmarshalAddressPB(msg.From) if caller.Compare(origin) != 0 { return res, fmt.Errorf("Origin doesn't match caller: - %v != %v", origin, caller) } - return loomchain.TxHandlerResult{}, err + return txhandler.TxHandlerResult{}, err }, false, ) } @@ -415,7 +419,7 @@ func mockEd25519SignedTx(t *testing.T, key string) []byte { ChainID: defaultLoomChainId, Local: loom.LocalAddressFromPublicKey(signer.PublicKey()), } - nonceTx := mockNonceTx(t, addr, sequence) + nonceTx := mockNonceTx(t, addr, seq) signedTx := auth.SignTx(signer, nonceTx) marshalledSignedTx, err := proto.Marshal(signedTx) @@ -428,7 +432,7 @@ func mockSignedTx(t *testing.T, chainID string, signer auth.Signer) []byte { require.NoError(t, err) ethLocalAdr, err := loom.LocalAddressFromHexString(crypto.PubkeyToAddress(*pubKey).Hex()) require.NoError(t, err) - nonceTx := mockNonceTx(t, loom.Address{ChainID: chainID, Local: ethLocalAdr}, sequence) + nonceTx := mockNonceTx(t, loom.Address{ChainID: chainID, Local: ethLocalAdr}, seq) signedTx := auth.SignTx(signer, nonceTx) marshalledSignedTx, err := proto.Marshal(signedTx) @@ -439,7 +443,7 @@ func mockSignedTx(t *testing.T, chainID string, signer auth.Signer) []byte { func mockBinanceSignedTx(t *testing.T, chainID string, signer auth.Signer) []byte { foreignLocalAddr, err := loom.LocalAddressFromHexString(evmcompat.BitcoinAddress(signer.PublicKey()).Hex()) require.NoError(t, err) - nonceTx := mockNonceTx(t, loom.Address{ChainID: chainID, Local: foreignLocalAddr}, sequence) + nonceTx := mockNonceTx(t, loom.Address{ChainID: chainID, Local: foreignLocalAddr}, seq) signedTx := auth.SignTx(signer, nonceTx) marshalledSignedTx, err := proto.Marshal(signedTx) @@ -460,7 +464,7 @@ func mockNonceTx(t *testing.T, from loom.Address, sequence uint64) []byte { From: from.MarshalPB(), }) require.NoError(t, err) - tx, err := proto.Marshal(&loomchain.Transaction{ + tx, err := proto.Marshal(&gltypes.Transaction{ Id: uint32(types.TxID_CALL), Data: messageTx, }) diff --git a/sequence.go b/auth/sequence/sequence.go similarity index 97% rename from sequence.go rename to auth/sequence/sequence.go index 6986479ba2..5a084d3a63 100644 --- a/sequence.go +++ b/auth/sequence/sequence.go @@ -1,4 +1,4 @@ -package loomchain +package sequence import ( "bytes" 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/cmd/loom/db/evm.go b/cmd/loom/db/evm.go index 9c27b34f65..fa2aa57a75 100644 --- a/cmd/loom/db/evm.go +++ b/cmd/loom/db/evm.go @@ -111,7 +111,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 } @@ -224,7 +224,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/gateway_reactors.go b/cmd/loom/gateway_reactors.go index a8b82f90af..813240b170 100644 --- a/cmd/loom/gateway_reactors.go +++ b/cmd/loom/gateway_reactors.go @@ -8,17 +8,18 @@ import ( glAuth "github.com/loomnetwork/go-loom/auth" "github.com/loomnetwork/go-loom/client" + tgateway "github.com/loomnetwork/transfer-gateway/gateway" + tconfig "github.com/loomnetwork/transfer-gateway/gateway/config" + "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/fnConsensus" "github.com/loomnetwork/loomchain/log" - tgateway "github.com/loomnetwork/transfer-gateway/gateway" ) const ( - GatewayName = "gateway" - LoomGatewayName = "loomcoin-gateway" - BinanceGatewayName = "binance-gateway" - TronGatewayName = "tron-gateway" + GatewayName = "gateway" + LoomGatewayName = "loomcoin-gateway" + TronGatewayName = "tron-gateway" ) func startGatewayReactors( @@ -62,7 +63,7 @@ func checkQueryService(name string, chainID string, DAppChainReadURI string, DAp func startGatewayFn( chainID string, fnRegistry fnConsensus.FnRegistry, - cfg *tgateway.TransferGatewayConfig, + cfg *tconfig.TransferGatewayConfig, nodeSigner glAuth.Signer, ) error { if !cfg.BatchSignFnConfig.Enabled { @@ -85,7 +86,7 @@ func startGatewayFn( func startLoomCoinGatewayFn( chainID string, fnRegistry fnConsensus.FnRegistry, - cfg *tgateway.TransferGatewayConfig, + cfg *tconfig.TransferGatewayConfig, nodeSigner glAuth.Signer, ) error { if !cfg.BatchSignFnConfig.Enabled { @@ -107,7 +108,7 @@ func startLoomCoinGatewayFn( func startTronGatewayFn(chainID string, fnRegistry fnConsensus.FnRegistry, - cfg *tgateway.TransferGatewayConfig, + cfg *tconfig.TransferGatewayConfig, nodeSigner glAuth.Signer, ) error { if !cfg.BatchSignFnConfig.Enabled { diff --git a/cmd/loom/handler_test.go b/cmd/loom/handler_test.go index 03947e75a9..2db34359ab 100644 --- a/cmd/loom/handler_test.go +++ b/cmd/loom/handler_test.go @@ -12,11 +12,12 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "golang.org/x/crypto/ed25519" - "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/auth" registry "github.com/loomnetwork/loomchain/registry/factory" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" + "github.com/loomnetwork/loomchain/txhandler/middleware" "github.com/loomnetwork/loomchain/vm" ) @@ -32,19 +33,19 @@ func TestTxHandlerWithInvalidCaller(t *testing.T) { require.NoError(t, err) vmManager := vm.NewManager() - router := loomchain.NewTxRouter() - router.HandleDeliverTx(1, loomchain.GeneratePassthroughRouteHandler(&vm.DeployTxHandler{Manager: vmManager, CreateRegistry: createRegistry})) - router.HandleDeliverTx(2, loomchain.GeneratePassthroughRouteHandler(&vm.CallTxHandler{Manager: vmManager})) + router := middleware.NewTxRouter() + router.HandleDeliverTx(1, middleware.GeneratePassthroughRouteHandler(&vm.DeployTxHandler{Manager: vmManager, CreateRegistry: createRegistry})) + router.HandleDeliverTx(2, middleware.GeneratePassthroughRouteHandler(&vm.CallTxHandler{Manager: vmManager})) kvStore := store.NewMemStore() state := appstate.NewStoreState(nil, kvStore, abci.Header{ChainID: "default"}, nil, nil) - txMiddleWare := []loomchain.TxMiddleware{ + txMiddleWare := []txhandler.TxMiddleware{ auth.SignatureTxMiddleware, auth.NewNonceHandler().TxMiddleware(kvStore), } - rootHandler := loomchain.MiddlewareTxHandler(txMiddleWare, router, nil) + rootHandler := txhandler.MiddlewareTxHandler(txMiddleWare, router, nil) signer := lauth.NewEd25519Signer(alicePrivKey) caller := loom.Address{ ChainID: "default", diff --git a/cmd/loom/loom.go b/cmd/loom/loom.go index bb8ddae7e2..fcb55f7b3c 100644 --- a/cmd/loom/loom.go +++ b/cmd/loom/loom.go @@ -16,9 +16,6 @@ import ( "syscall" "time" - "github.com/prometheus/client_golang/prometheus/push" - "github.com/tendermint/tendermint/libs/db" - kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" @@ -29,16 +26,24 @@ 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" + "github.com/prometheus/client_golang/prometheus/push" + "github.com/tendermint/tendermint/libs/db" + + "github.com/pkg/errors" + stdprometheus "github.com/prometheus/client_golang/prometheus" + "github.com/spf13/cobra" + "golang.org/x/crypto/ed25519" + "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/prometheus/client_golang/prometheus" + "github.com/loomnetwork/loomchain/txhandler/factory" "github.com/loomnetwork/loomchain/chainconfig" chaincfgcmd "github.com/loomnetwork/loomchain/cmd/loom/chainconfig" @@ -57,10 +62,9 @@ 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" + rcommon "github.com/loomnetwork/loomchain/receipts/common" regcommon "github.com/loomnetwork/loomchain/registry" registry "github.com/loomnetwork/loomchain/registry/factory" "github.com/loomnetwork/loomchain/rpc" @@ -68,13 +72,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 ( @@ -492,14 +490,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, @@ -591,7 +581,7 @@ func destroyApp(cfg *config.Config) error { } func destroyReceiptsDB(cfg *config.Config) { - if cfg.ReceiptsVersion == handler.ReceiptHandlerLevelDb || cfg.ReceiptsVersion == 3 { + if cfg.ReceiptsVersion == rcommon.ReceiptHandlerLevelDb || cfg.ReceiptsVersion == 3 { receptHandler := leveldb.LevelDbReceipts{} receptHandler.ClearData() } @@ -671,7 +661,6 @@ func loadAppStore(cfg *config.Config, logger *loom.Logger, targetVersion int64) return nil, err } } - if cfg.CachingStoreConfig.CachingEnabled { appStore, err = store.NewVersionedCachingStore(appStore, cfg.CachingStoreConfig, appStore.Version()) if err != nil { @@ -835,36 +824,11 @@ 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, - }, - } - gen, err := config.ReadGenesis(cfg.GenesisPath()) if err != nil { return nil, err @@ -885,7 +849,7 @@ func loadApp( err := deployContract( state, contractCfg, - vmManager, + *vmManager, rootAddr, registry, logger, @@ -898,106 +862,7 @@ func loadApp( return nil } - router := loomchain.NewTxRouter() - - isEvmTx := func(txID uint32, state appstate.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) - } + createKarmaContractCtx := getContractCtx("karma", *vmManager) createContractUpkeepHandler := func(state appstate.State) (loomchain.KarmaHandler, error) { // TODO: This setting should be part of the config stored within the Karma contract itself, @@ -1019,7 +884,7 @@ func loadApp( getValidatorSet := func(state appstate.State) (loom.ValidatorSet, error) { if cfg.DPOSVersion == 3 || state.FeatureEnabled(features.DPOSVersion3Feature, false) { - createDPOSV3Ctx := getContractCtx("dposV3", vmManager) + createDPOSV3Ctx := getContractCtx("dposV3", *vmManager) dposV3Ctx, err := createDPOSV3Ctx(state) if err != nil { return nil, err @@ -1030,7 +895,7 @@ func loadApp( } return loom.NewValidatorSet(validators...), nil } else if cfg.DPOSVersion == 2 { - createDPOSV2Ctx := getContractCtx("dposV2", vmManager) + createDPOSV2Ctx := getContractCtx("dposV2", *vmManager) dposV2Ctx, err := createDPOSV2Ctx(state) if err != nil { return nil, err @@ -1046,19 +911,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 appstate.State) (loomchain.ValidatorsManager, error) { pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) if err != nil { @@ -1113,18 +965,17 @@ 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 { + 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, @@ -1141,7 +992,7 @@ func loadApp( func deployContract( state appstate.State, contractCfg config.ContractConfig, - vmManager *vm.Manager, + vmManager vm.Manager, rootAddr loom.Address, registry regcommon.Registry, logger log.TMLogger, @@ -1182,11 +1033,7 @@ func deployContract( return nil } -type contextFactory func(state appstate.State) (contractpb.Context, error) - -type staticContextFactory func(state appstate.State) (contractpb.StaticContext, error) - -func getContractCtx(pluginName string, vmManager *vm.Manager) contextFactory { +func getContractCtx(pluginName string, vmManager vm.Manager) func(state appstate.State) (contractpb.Context, error) { return func(state appstate.State) (contractpb.Context, error) { pvm, err := vmManager.InitVM(vm.VMType_PLUGIN, state) if err != nil { @@ -1196,16 +1043,6 @@ func getContractCtx(pluginName string, vmManager *vm.Manager) contextFactory { } } -func getContractStaticCtx(pluginName string, vmManager *vm.Manager) staticContextFactory { - return func(state appstate.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/config/config.go b/config/config.go index df94533427..0746460a82 100755 --- a/config/config.go +++ b/config/config.go @@ -10,20 +10,18 @@ import ( "path" "path/filepath" - "github.com/loomnetwork/loomchain/auth" + "github.com/pkg/errors" + "github.com/spf13/viper" + + "github.com/loomnetwork/loomchain/auth/keys" plasmacfg "github.com/loomnetwork/loomchain/builtin/plugins/plasma_cash/config" genesiscfg "github.com/loomnetwork/loomchain/config/genesis" "github.com/loomnetwork/loomchain/events" - "github.com/loomnetwork/loomchain/evm" hsmpv "github.com/loomnetwork/loomchain/privval/hsm" - receipts "github.com/loomnetwork/loomchain/receipts/handler" + "github.com/loomnetwork/loomchain/receipts/common" registry "github.com/loomnetwork/loomchain/registry/factory" - "github.com/loomnetwork/loomchain/rpc/eth" "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" @@ -68,9 +66,9 @@ type Config struct { CallEnabled bool SessionDuration int64 Karma *KarmaConfig - GoContractDeployerWhitelist *throttle.GoContractDeployerWhitelistConfig - TxLimiter *throttle.TxLimiterConfig - ContractTxLimiter *throttle.ContractTxLimiterConfig + GoContractDeployerWhitelist *GoContractDeployerWhitelistConfig + TxLimiter *TxLimiterConfig + ContractTxLimiter *ContractTxLimiterConfig // Logging LogDestination string ContractLogLevel string @@ -134,9 +132,9 @@ type Config struct { FnConsensus *FnConsensusConfig - Auth *auth.Config + Auth *keys.Config - EvmStore *evm.EvmStoreConfig + EvmStore *EvmStoreConfig // Allow deployment of named EVM contracts (should only be used in tests!) AllowNamedEvmContracts bool @@ -145,7 +143,7 @@ type Config struct { // Set to true to disable minimum required build number check on node startup SkipMinBuildCheck bool - Web3 *eth.Web3Config + Web3 *Web3Config } type Metrics struct { @@ -213,6 +211,17 @@ type UserDeployerWhitelistConfig struct { ContractEnabled bool } +type Web3Config struct { + // GetLogsMaxBlockRange specifies the maximum number of blocks eth_getLogs will query per request + GetLogsMaxBlockRange uint64 +} + +func DefaultWeb3Config() *Web3Config { + return &Web3Config{ + GetLogsMaxBlockRange: 20, + } +} + func DefaultDBBackendConfig() *DBBackendConfig { return &DBBackendConfig{ CacheSizeMegs: 1042, //1 Gigabyte @@ -381,8 +390,8 @@ func DefaultConfig() *Config { LogStateDB: false, LogEthDbBatch: false, RegistryVersion: int32(registry.RegistryV2), - ReceiptsVersion: int32(receipts.ReceiptHandlerLevelDb), - EVMPersistentTxReceiptsMax: receipts.DefaultMaxReceipts, + ReceiptsVersion: int32(common.ReceiptHandlerLevelDb), + EVMPersistentTxReceiptsMax: common.DefaultMaxReceipts, SessionDuration: 600, EVMAccountsEnabled: false, EVMDebugEnabled: false, @@ -402,9 +411,9 @@ func DefaultConfig() *Config { cfg.PlasmaCash = plasmacfg.DefaultConfig() cfg.AppStore = store.DefaultConfig() cfg.HsmConfig = hsmpv.DefaultConfig() - cfg.TxLimiter = throttle.DefaultTxLimiterConfig() - cfg.ContractTxLimiter = throttle.DefaultContractTxLimiterConfig() - cfg.GoContractDeployerWhitelist = throttle.DefaultGoContractDeployerWhitelistConfig() + cfg.TxLimiter = DefaultTxLimiterConfig() + cfg.ContractTxLimiter = DefaultContractTxLimiterConfig() + cfg.GoContractDeployerWhitelist = DefaultGoContractDeployerWhitelistConfig() cfg.DPOSv2OracleConfig = DefaultDPOS2OracleConfig() cfg.CachingStoreConfig = store.DefaultCachingStoreConfig() cfg.BlockStore = store.DefaultBlockStoreConfig() @@ -418,12 +427,12 @@ func DefaultConfig() *Config { cfg.PrometheusPushGateway = DefaultPrometheusPushGatewayConfig() cfg.EventDispatcher = events.DefaultEventDispatcherConfig() cfg.EventStore = events.DefaultEventStoreConfig() - cfg.EvmStore = evm.DefaultEvmStoreConfig() - cfg.Web3 = eth.DefaultWeb3Config() + cfg.EvmStore = DefaultEvmStoreConfig() + cfg.Web3 = DefaultWeb3Config() cfg.FnConsensus = DefaultFnConsensusConfig() - cfg.Auth = auth.DefaultConfig() + cfg.Auth = keys.DefaultConfig() return cfg } diff --git a/evm/config.go b/config/evm_config.go similarity index 98% rename from evm/config.go rename to config/evm_config.go index 4239a74b11..47977012c1 100644 --- a/evm/config.go +++ b/config/evm_config.go @@ -1,4 +1,4 @@ -package evm +package config type EvmStoreConfig struct { // DBName defines database file name diff --git a/config/gateway_config.go b/config/gateway_config.go index 2a6649d7c4..d7f1035498 100644 --- a/config/gateway_config.go +++ b/config/gateway_config.go @@ -3,23 +3,23 @@ package config import ( - "github.com/loomnetwork/transfer-gateway/gateway" + "github.com/loomnetwork/transfer-gateway/gateway/config" dpos2cfg "github.com/loomnetwork/transfer-gateway/oracles/dpos2/config" ) -type TransferGatewayConfig = gateway.TransferGatewayConfig +type TransferGatewayConfig = config.TransferGatewayConfig type OracleSerializableConfig = dpos2cfg.OracleSerializableConfig func DefaultTGConfig(rpcProxyPort int32) *TransferGatewayConfig { - return gateway.DefaultConfig(rpcProxyPort) + return config.DefaultConfig(rpcProxyPort) } func DefaultLoomCoinTGConfig(rpcProxyPort int32) *TransferGatewayConfig { - return gateway.DefaultLoomCoinTGConfig(rpcProxyPort) + return config.DefaultLoomCoinTGConfig(rpcProxyPort) } func DefaultTronTGConfig(rpcProxyPort int32) *TransferGatewayConfig { - return gateway.DefaultTronConfig(rpcProxyPort) + return config.DefaultTronConfig(rpcProxyPort) } func DefaultBinanceTGConfig() *TransferGatewayConfig { diff --git a/config/throttle_config.go b/config/throttle_config.go new file mode 100644 index 0000000000..fe5664b399 --- /dev/null +++ b/config/throttle_config.go @@ -0,0 +1,82 @@ +package config + +import ( + "github.com/loomnetwork/go-loom" + "github.com/pkg/errors" +) + +type GoContractDeployerWhitelistConfig struct { + Enabled bool + DeployerAddressList []string +} + +func DefaultGoContractDeployerWhitelistConfig() *GoContractDeployerWhitelistConfig { + return &GoContractDeployerWhitelistConfig{ + Enabled: false, + } +} + +func (c *GoContractDeployerWhitelistConfig) DeployerAddresses(chainId string) ([]loom.Address, error) { + deployerAddressList := make([]loom.Address, 0, len(c.DeployerAddressList)) + for _, addrStr := range c.DeployerAddressList { + addr, err := loom.ParseAddress(addrStr) + if err != nil { + addr, err = loom.ParseAddress(chainId + ":" + addrStr) + if err != nil { + return nil, errors.Wrapf(err, "parsing deploy address %s", addrStr) + } + } + deployerAddressList = append(deployerAddressList, addr) + } + return deployerAddressList, nil +} + +type TxLimiterConfig struct { + // Enables the tx limiter middleware + Enabled bool + // Number of seconds each session lasts + SessionDuration int64 + // Maximum number of txs that should be allowed per session + MaxTxsPerSession int64 +} + +func DefaultTxLimiterConfig() *TxLimiterConfig { + return &TxLimiterConfig{ + SessionDuration: 60, + MaxTxsPerSession: 60, + } +} + +// Clone returns a deep clone of the config. +func (c *TxLimiterConfig) Clone() *TxLimiterConfig { + if c == nil { + return nil + } + clone := *c + return &clone +} + +type ContractTxLimiterConfig struct { + // Enables the middleware + Enabled bool + // Number of seconds each refresh lasts + ContractDataRefreshInterval int64 + TierDataRefreshInterval int64 +} + +func DefaultContractTxLimiterConfig() *ContractTxLimiterConfig { + return &ContractTxLimiterConfig{ + Enabled: false, + ContractDataRefreshInterval: 15 * 60, + TierDataRefreshInterval: 15 * 60, + } +} + +// Clone returns a deep clone of the config. +func (c *ContractTxLimiterConfig) Clone() *ContractTxLimiterConfig { + if c == nil { + return nil + } + clone := *c + return &clone +} diff --git a/e2e/tests/truffle/test/DebugFunctions.js b/e2e/tests/truffle/test/DebugFunctions.js new file mode 100644 index 0000000000..05d78a9d44 --- /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) + }) +}); diff --git a/eth/polls/polls_test.go b/eth/polls/polls_test.go index 4c0f2b7bcf..14f37b8c16 100644 --- a/eth/polls/polls_test.go +++ b/eth/polls/polls_test.go @@ -9,18 +9,18 @@ import ( "github.com/loomnetwork/loomchain/auth" "github.com/loomnetwork/loomchain/rpc/eth" - "github.com/loomnetwork/loomchain/vm" appstate "github.com/loomnetwork/loomchain/state" - - "github.com/loomnetwork/loomchain/events" - "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/vm" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/plugin/types" + ltypes "github.com/loomnetwork/go-loom/types" "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/events" "github.com/loomnetwork/loomchain/receipts/common" "github.com/loomnetwork/loomchain/receipts/handler" + "github.com/loomnetwork/loomchain/store" "github.com/stretchr/testify/require" ) @@ -30,18 +30,21 @@ var ( ) const ( - deployId = uint32(1) - callId = uint32(2) - migrationTx = uint32(3) + deployId = uint32(1) + callId = uint32(2) ) func TestLogPoll(t *testing.T) { + testLogPoll(t, common.ReceiptHandlerLevelDb) +} + +func testLogPoll(t *testing.T, version handler.ReceiptHandlerVersion) { evmAuxStore, err := common.NewMockEvmAuxStore() require.NoError(t, err) blockStore := store.NewMockBlockStore() eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) sub := NewEthSubscriptions(evmAuxStore, blockStore) allFilter := eth.JsonFilter{ FromBlock: "earliest", @@ -102,7 +105,7 @@ func testLegacyTxPoll(t *testing.T) { blockStore := store.NewMockBlockStore() eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) sub := NewEthSubscriptions(evmAuxStore, blockStore) state := makeMockState(t, receiptHandler, blockStore) @@ -141,7 +144,7 @@ func testTxPoll(t *testing.T) { blockStore := store.NewMockBlockStore() eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) sub := NewEthSubscriptions(evmAuxStore, blockStore) state := makeMockState(t, receiptHandler, blockStore) @@ -207,7 +210,7 @@ func testTxPoll(t *testing.T) { } func TestTimeout(t *testing.T) { - testTimeout(t, handler.ReceiptHandlerLevelDb) + testTimeout(t, common.ReceiptHandlerLevelDb) } func testTimeout(t *testing.T, version handler.ReceiptHandlerVersion) { @@ -216,7 +219,7 @@ func testTimeout(t *testing.T, version handler.ReceiptHandlerVersion) { blockStore := store.NewMockBlockStore() eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) BlockTimeout = 10 sub := NewEthSubscriptions(evmAuxStore, blockStore) @@ -393,7 +396,7 @@ func mockSignedTx(t *testing.T, id uint32, to loom.Address, from loom.Address, d }) require.NoError(t, err) - txTx, err := proto.Marshal(&loomchain.Transaction{ + txTx, err := proto.Marshal(<ypes.Transaction{ Data: messageTx, Id: id, }) diff --git a/eth/query/block.go b/eth/query/block.go index aae2167eeb..dfa62c1e34 100644 --- a/eth/query/block.go +++ b/eth/query/block.go @@ -146,7 +146,7 @@ func GetTxObjectFromBlockResult( } txObj.Nonce = eth.EncInt(int64(nonceTx.Sequence)) - var txTx loomchain.Transaction + var txTx ltypes.Transaction if err := proto.Unmarshal(nonceTx.Inner, &txTx); err != nil { return eth.GetEmptyTxObject(), nil, err } diff --git a/eth/query/noevm.go b/eth/query/noevm.go index 6d252a7c9c..f7a7b37208 100644 --- a/eth/query/noevm.go +++ b/eth/query/noevm.go @@ -13,7 +13,7 @@ import ( ) func DeprecatedQueryChain( - _ string, _ store.BlockStore, _ appstate.ReadOnlyState, + _ string, _ store.BlockStore, _ state.ReadOnlyState, _ loomchain.ReadReceiptHandler, _ *evmaux.EvmAuxStore, _ uint64, ) ([]byte, error) { return nil, nil @@ -68,7 +68,7 @@ func GetTxByBlockAndIndex(_ store.BlockStore, _, _ uint64, _ *evmaux.EvmAuxStore } func QueryChain( - _ store.BlockStore, _ appstate.ReadOnlyState, _ eth.EthFilter, + _ store.BlockStore, _ state.ReadOnlyState, _ eth.EthFilter, _ loomchain.ReadReceiptHandler, _ *evmaux.EvmAuxStore, _ uint64, ) ([]*types.EthFilterLog, error) { return nil, nil diff --git a/eth/query/query_test.go b/eth/query/query_test.go index f7884b5388..9497da1787 100644 --- a/eth/query/query_test.go +++ b/eth/query/query_test.go @@ -36,11 +36,15 @@ func getFilter(fromBlock, toBlock string) string { return "{\"fromBlock\":\"" + fromBlock + "\",\"toBlock\":\"" + toBlock + "\",\"address\":\"\",\"topics\":[]}" } func TestQueryChain(t *testing.T) { + testQueryChain(t, common.ReceiptHandlerLevelDb) +} + +func testQueryChain(t *testing.T, v handler.ReceiptHandlerVersion) { evmAuxStore, err := common.NewMockEvmAuxStore() require.NoError(t, err) eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) var writer loomchain.WriteReceiptHandler = receiptHandler blockStore := store.NewMockBlockStore() @@ -155,7 +159,7 @@ func TestMatchFilters(t *testing.T) { ethFilter5 := eth.EthBlockFilter{ Topics: [][]string{{"Topic1"}, {"Topic6"}}, } - bloomFilter := bloom.GenBloomFilter(common.ConvertEventData(testEvents)) + bloomFilter := bloom.GenBloomFilter(ConvertEventData(testEvents)) require.True(t, MatchBloomFilter(ethFilter1, bloomFilter)) require.False(t, MatchBloomFilter(ethFilter2, bloomFilter)) // address does not match @@ -177,12 +181,16 @@ func TestMatchFilters(t *testing.T) { } func TestGetLogs(t *testing.T) { + testGetLogs(t, common.ReceiptHandlerLevelDb) +} + +func testGetLogs(t *testing.T, v handler.ReceiptHandlerVersion) { evmAuxStore, err := common.NewMockEvmAuxStore() require.NoError(t, err) eventDispatcher := events.NewLogEventDispatcher() eventHandler := loomchain.NewDefaultEventHandler(eventDispatcher) - receiptHandler := handler.NewReceiptHandler(eventHandler, handler.DefaultMaxReceipts, evmAuxStore) + receiptHandler := handler.NewReceiptHandler(eventHandler, common.DefaultMaxReceipts, evmAuxStore) var writer loomchain.WriteReceiptHandler = receiptHandler require.NoError(t, err) @@ -240,6 +248,15 @@ func TestGetLogs(t *testing.T) { require.NoError(t, receiptHandler.Close()) } +func ConvertEventData(events []*loomchain.EventData) []*types.EventData { + typesEvents := make([]*types.EventData, 0, len(events)) + for _, event := range events { + typeEvent := types.EventData(*event) + typesEvents = append(typesEvents, &typeEvent) + } + return typesEvents +} + func TestDupEvmTxHash(t *testing.T) { blockTxHash := getRandomTxHash() txHash1 := getRandomTxHash() // DeployEVMTx that has dup EVM Tx Hash @@ -343,7 +360,7 @@ func mockSignedTx(t *testing.T, id ltypes.TxID, to loom.Address, from loom.Addre }) require.NoError(t, err) - txTx, err := proto.Marshal(&loomchain.Transaction{ + txTx, err := proto.Marshal(<ypes.Transaction{ Data: messageTx, Id: uint32(id), }) diff --git a/events/db_indexer.go b/events/db_indexer.go index 945376ca99..5c8561a4a0 100644 --- a/events/db_indexer.go +++ b/events/db_indexer.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/loomnetwork/go-loom/plugin/types" - "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/store" ) @@ -16,7 +16,7 @@ type DBIndexerEventDispatcher struct { sync.Mutex } -var _ loomchain.EventDispatcher = &DBIndexerEventDispatcher{} +//var _ loomchain.EventDispatcher = &DBIndexerEventDispatcher{} func NewDBIndexerEventDispatcher(es store.EventStore) *DBIndexerEventDispatcher { return &DBIndexerEventDispatcher{EventStore: es} diff --git a/events/log.go b/events/log.go index 1959176aab..b93e218845 100644 --- a/events/log.go +++ b/events/log.go @@ -1,15 +1,14 @@ package events import ( - "github.com/loomnetwork/loomchain" - log "github.com/loomnetwork/loomchain/log" + "github.com/loomnetwork/loomchain/log" ) // LogEventDispatcher just logs events type LogEventDispatcher struct { } -var _ loomchain.EventDispatcher = &LogEventDispatcher{} +//var _ loomchain.EventDispatcher = &LogEventDispatcher{} // NewLogEventDispatcher create a new redis dispatcher func NewLogEventDispatcher() *LogEventDispatcher { diff --git a/events/redis.go b/events/redis.go index 66400e5dad..d253d97b0f 100644 --- a/events/redis.go +++ b/events/redis.go @@ -1,8 +1,7 @@ package events import ( - "github.com/loomnetwork/loomchain" - log "github.com/loomnetwork/loomchain/log" + "github.com/loomnetwork/loomchain/log" "github.com/gomodule/redigo/redis" ) @@ -17,7 +16,7 @@ type RedisEventDispatcher struct { queue string } -var _ loomchain.EventDispatcher = &RedisEventDispatcher{} +//var _ loomchain.EventDispatcher = &RedisEventDispatcher{} // NewRedisEventDispatcher create a new redis dispatcher func NewRedisEventDispatcher(host string) (*RedisEventDispatcher, error) { diff --git a/evm/evm.go b/evm/evm.go index 8a914d768d..002d6de560 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -29,6 +29,7 @@ import ( const ( EVMEnabled = true defaultGasLimit = math.MaxUint64 + //defaultTraceTimeout = 5 * time.Second ) //Metrics @@ -148,17 +149,21 @@ type Evm struct { gasLimit uint64 } -func NewEvm(sdb vm.StateDB, lstate state.State, abm *evmAccountBalanceManager, debug bool) *Evm { +func NewEvm( + sdb vm.StateDB, lstate state.State, abm *evmAccountBalanceManager, debug bool, tracer vm.Tracer, +) (*Evm, error) { p := new(Evm) p.sdb = sdb p.gasLimit = lstate.Config().GetEvm().GetGasLimit() if p.gasLimit == 0 { p.gasLimit = defaultGasLimit } - 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, @@ -181,7 +186,7 @@ func NewEvm(sdb vm.StateDB, lstate state.State, abm *evmAccountBalanceManager, d 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) { @@ -300,31 +305,24 @@ func defaultChainConfig(enableConstantinople bool) params.ChainConfig { } } -func defaultVmConfig(evmDebuggingEnabled bool) 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 { +func createVmConfig(evmDebuggingEnabled bool, tracer vm.Tracer) (vm.Config, error) { + if evmDebuggingEnabled || tracer != nil { log.Error("WARNING!!!! EVM Debug mode enabled, do NOT run this on a production server!!!") - logCfg = vm.LogConfig{ + } + 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 } - debug = true + tracer = vm.NewStructLogger(&logCfg) } - logger := vm.NewStructLogger(&logCfg) return vm.Config{ // Debug enabled debugging Interpreter options - Debug: debug, + Debug: evmDebuggingEnabled || (tracer != nil), // Tracer is the op code logger - Tracer: logger, + Tracer: tracer, // NoRecursion disabled Interpreter call, callcode, // delegate call and create. NoRecursion: false, @@ -334,5 +332,5 @@ func defaultVmConfig(evmDebuggingEnabled bool) vm.Config { // 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 a984f1ee0a..cebed43433 100644 --- a/evm/loomevm.go +++ b/evm/loomevm.go @@ -23,7 +23,7 @@ import ( "github.com/loomnetwork/loomchain/events" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/receipts" - "github.com/loomnetwork/loomchain/receipts/handler" + rcommon "github.com/loomnetwork/loomchain/receipts/common" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/vm" ) @@ -56,7 +56,7 @@ type LoomEvm struct { // TODO: this doesn't need to be exported, rename to newLoomEvmWithState func NewLoomEvm( loomState appstate.State, accountBalanceManager AccountBalanceManager, - logContext *ethdbLogContext, debug bool, + logContext *ethdbLogContext, debug bool, tracer ethvm.Tracer, ) (*LoomEvm, error) { p := new(LoomEvm) p.db = NewLoomEthdb(loomState, logContext) @@ -76,7 +76,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 } @@ -109,11 +112,11 @@ var LoomVmFactory = func(state appstate.State) (vm.VM, error) { eventHandler := loomchain.NewDefaultEventHandler(events.NewLogEventDispatcher()) receiptHandlerProvider := receipts.NewReceiptHandlerProvider( eventHandler, - handler.DefaultMaxReceipts, + rcommon.DefaultMaxReceipts, 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. @@ -123,20 +126,22 @@ type LoomVm struct { receiptHandler loomchain.WriteReceiptHandler createABM AccountBalanceManagerFactoryFunc debug bool + tracer ethvm.Tracer } func NewLoomVm( loomState appstate.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, } } @@ -153,7 +158,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 } @@ -216,7 +221,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 } @@ -264,7 +269,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 } @@ -272,7 +277,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 } @@ -280,7 +285,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 911f0fb93d..d0ad707f4b 100644 --- a/evm/noevm.go +++ b/evm/noevm.go @@ -3,6 +3,8 @@ package evm import ( + "github.com/ethereum/go-ethereum/eth" + "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/state" lvm "github.com/loomnetwork/loomchain/vm" @@ -16,11 +18,11 @@ var ( const EVMEnabled = false func NewLoomVm( - loomState state.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/fork.go b/fork.go index 43ac29489d..cbaaebb70d 100644 --- a/fork.go +++ b/fork.go @@ -4,10 +4,11 @@ import ( "sort" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" ) type forkRoute struct { - TxHandler + txhandler.TxHandler Height int64 } @@ -35,7 +36,7 @@ func NewForkRouter() *ForkRouter { } } -func (r *ForkRouter) Handle(chainID string, height int64, handler TxHandler) { +func (r *ForkRouter) Handle(chainID string, height int64, handler txhandler.TxHandler) { routes := r.routes[chainID] found := sort.Search(len(routes), func(i int) bool { return routes[i].Height >= height @@ -52,11 +53,11 @@ func (r *ForkRouter) Handle(chainID string, height int64, handler TxHandler) { r.routes[chainID] = routes } -func (r *ForkRouter) ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { +func (r *ForkRouter) ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { block := state.Block() routes := r.routes[block.ChainID] - var found TxHandler + var found txhandler.TxHandler for _, route := range routes { if route.Height > block.Height { break 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/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/fake_context.go b/plugin/fake_context.go index 4aa075ba0c..c7b497dfb7 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/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/plugin/vm.go b/plugin/vm.go index 893e48bd71..a059a01ad0 100644 --- a/plugin/vm.go +++ b/plugin/vm.go @@ -197,7 +197,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) } @@ -210,7 +210,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/plugin/vm_test.go b/plugin/vm_test.go index 4911bfb29c..947dc1c1af 100644 --- a/plugin/vm_test.go +++ b/plugin/vm_test.go @@ -144,7 +144,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") @@ -210,7 +210,7 @@ func TestGetEvmTxReceipt(t *testing.T) { require.NoError(t, err) receiptHandler := handler.NewReceiptHandler( loomchain.NewDefaultEventHandler(events.NewLogEventDispatcher()), - handler.DefaultMaxReceipts, + rcommon.DefaultMaxReceipts, evmAuxStore, ) require.NoError(t, err) @@ -240,7 +240,7 @@ func TestGetEvmTxReceiptNoCommit(t *testing.T) { require.NoError(t, err) receiptHandler := handler.NewReceiptHandler( loomchain.NewDefaultEventHandler(events.NewLogEventDispatcher()), - handler.DefaultMaxReceipts, + rcommon.DefaultMaxReceipts, evmAuxStore, ) diff --git a/receipts/common/common.go b/receipts/common/common.go index 7383c4c94e..48ce128218 100644 --- a/receipts/common/common.go +++ b/receipts/common/common.go @@ -3,14 +3,14 @@ package common import ( "encoding/binary" - "github.com/loomnetwork/go-loom/plugin/types" - "github.com/loomnetwork/loomchain" "github.com/pkg/errors" ) const ( - StatusTxSuccess = int32(1) - StatusTxFail = int32(0) + StatusTxSuccess = int32(1) + StatusTxFail = int32(0) + ReceiptHandlerLevelDb = 2 //ctypes.ReceiptStorage_LEVELDB + DefaultMaxReceipts = uint64(2000) ) var ( @@ -23,12 +23,3 @@ func BlockHeightToBytes(height uint64) []byte { binary.LittleEndian.PutUint64(heightB, height) return heightB } - -func ConvertEventData(events []*loomchain.EventData) []*types.EventData { - typesEvents := make([]*types.EventData, 0, len(events)) - for _, event := range events { - typeEvent := types.EventData(*event) - typesEvents = append(typesEvents, &typeEvent) - } - return typesEvents -} diff --git a/receipts/handler/handler.go b/receipts/handler/handler.go index 501468abc1..bdda9a1293 100644 --- a/receipts/handler/handler.go +++ b/receipts/handler/handler.go @@ -18,11 +18,6 @@ import ( type ReceiptHandlerVersion int32 -const ( - ReceiptHandlerLevelDb = 2 //ctypes.ReceiptStorage_LEVELDB - DefaultMaxReceipts = uint64(2000) -) - // ReceiptHandler implements loomchain.ReadReceiptHandler, loomchain.WriteReceiptHandler, and // loomchain.ReceiptHandlerStore interfaces. type ReceiptHandler struct { diff --git a/receipts/handler/handler_test.go b/receipts/handler/handler_test.go index fc0b1d3f27..4e8dbe5892 100644 --- a/receipts/handler/handler_test.go +++ b/receipts/handler/handler_test.go @@ -9,6 +9,7 @@ import ( "github.com/loomnetwork/go-loom/plugin/types" "github.com/loomnetwork/go-loom/util" "github.com/loomnetwork/loomchain" + "github.com/loomnetwork/loomchain/auth/sequence" "github.com/loomnetwork/loomchain/eth/utils" "github.com/loomnetwork/loomchain/receipts/common" "github.com/stretchr/testify/require" @@ -27,7 +28,7 @@ func TestReceiptsHandlerChain(t *testing.T) { evmAuxStore, err := common.NewMockEvmAuxStore() require.NoError(t, err) - handler := NewReceiptHandler(&loomchain.DefaultEventHandler{}, DefaultMaxReceipts, evmAuxStore) + handler := NewReceiptHandler(&loomchain.DefaultEventHandler{}, common.DefaultMaxReceipts, evmAuxStore) var writer loomchain.WriteReceiptHandler = handler var reader loomchain.ReadReceiptHandler = handler @@ -38,7 +39,7 @@ func TestReceiptsHandlerChain(t *testing.T) { for nonce := 1; nonce < 21; nonce++ { var txError error var resp abci.ResponseDeliverTx - loomchain.NewSequence(util.PrefixKey([]byte("nonce"), addr1.Bytes())).Next(state) + sequence.NewSequence(util.PrefixKey([]byte("nonce"), addr1.Bytes())).Next(state) var txHash []byte if nonce%2 == 1 { // mock EVM transaction 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 b65d2feb39..cd7dfcd568 100644 --- a/registry/v1/registry.go +++ b/registry/v1/registry.go @@ -18,7 +18,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 70770aa13e..50ca02fed0 100644 --- a/registry/v2/registry.go +++ b/registry/v2/registry.go @@ -19,7 +19,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/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..d0d5b1baaa --- /dev/null +++ b/rpc/debug/trace.go @@ -0,0 +1,115 @@ +// +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 err + } + _ = app.BeginBlock(requestBeginBlock(*resultBlock)) + + block, err := blockstore.GetBlockByHeight(&h) + if err != nil { + return errors.Wrapf(err, "getting block information at height %v", h) + } + for i := 0; i < len(block.Block.Data.Txs); i++ { + if h != height || i != int(index) { + _ = app.DeliverTx(block.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/eth/config.go b/rpc/eth/config.go deleted file mode 100644 index b55573c7e1..0000000000 --- a/rpc/eth/config.go +++ /dev/null @@ -1,14 +0,0 @@ -package eth - -// Web3Config contains settings that control the operation of the Web3 JSON-RPC method exposed -// via the /eth endpoint. -type Web3Config struct { - // GetLogsMaxBlockRange specifies the maximum number of blocks eth_getLogs will query per request - GetLogsMaxBlockRange uint64 -} - -func DefaultWeb3Config() *Web3Config { - return &Web3Config{ - GetLogsMaxBlockRange: 20, - } -} diff --git a/rpc/instrumenting.go b/rpc/instrumenting.go index 3a3df82e46..1d6c320347 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/json_handler_test.go b/rpc/json_handler_test.go index 6944abc88a..42ef72c595 100644 --- a/rpc/json_handler_test.go +++ b/rpc/json_handler_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/events" "github.com/loomnetwork/loomchain/log" "github.com/loomnetwork/loomchain/registry/factory" @@ -116,7 +116,7 @@ func testEthSubscribeEthUnSubscribe(t *testing.T) { Loader: loader, CreateRegistry: createRegistry, BlockStore: store.NewMockBlockStore(), - AuthCfg: auth.DefaultConfig(), + AuthCfg: keys.DefaultConfig(), EthSubscriptions: eventHandler.EthSubscriptionSet(), } handler := MakeEthQueryServiceHandler(testlog, hub, createDefaultEthRoutes(qs, "default")) diff --git a/rpc/mock_query_server.go b/rpc/mock_query_server.go index b6506afbbe..796eb5fc15 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.go b/rpc/query_server.go index 49f267dbca..9774fcfb9e 100755 --- a/rpc/query_server.go +++ b/rpc/query_server.go @@ -7,9 +7,8 @@ import ( "strconv" "strings" - "github.com/gorilla/websocket" - "github.com/gogo/protobuf/proto" + "github.com/gorilla/websocket" sha3 "github.com/miguelmota/go-solidity-sha3" "github.com/phonkee/go-pubsub" "github.com/pkg/errors" @@ -26,6 +25,7 @@ import ( "github.com/loomnetwork/loomchain" "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/ethcoin" "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/eth/polls" @@ -39,6 +39,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" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" @@ -61,6 +62,7 @@ const ( // StateProvider interface is used by QueryServer to access the read-only application state type StateProvider interface { ReadOnlyState() appstate.State + ReplayApplication(uint64, store.BlockStore) (*loomchain.Application, int64, error) } // QueryServer provides the ability to query the current state of the DAppChain via RPC. @@ -131,8 +133,8 @@ type QueryServer struct { *evmaux.EvmAuxStore blockindex.BlockIndexStore EventStore store.EventStore - AuthCfg *auth.Config - Web3Cfg *eth.Web3Config + Web3Cfg *config.Web3Config + AuthCfg *keys.Config } var _ QueryService = &QueryServer{} @@ -270,7 +272,7 @@ func (s *QueryServer) queryEvm(state appstate.State, caller, contract loom.Addre 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) } @@ -315,7 +317,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) } @@ -329,7 +331,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) @@ -1091,7 +1093,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()) @@ -1118,6 +1120,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_server_test.go b/rpc/query_server_test.go index 4ff1ab8128..049ad74089 100644 --- a/rpc/query_server_test.go +++ b/rpc/query_server_test.go @@ -13,19 +13,20 @@ import ( proto "github.com/gogo/protobuf/proto" lp "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/types" + stdprometheus "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + rpcclient "github.com/tendermint/tendermint/rpc/lib/client" + "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/eth/subs" llog "github.com/loomnetwork/loomchain/log" "github.com/loomnetwork/loomchain/plugin" registry "github.com/loomnetwork/loomchain/registry/factory" "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" - rpcclient "github.com/tendermint/tendermint/rpc/lib/client" ) type queryableContract struct { @@ -102,6 +103,10 @@ func (s *stateProvider) ReadOnlyState() state.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) { @@ -124,7 +129,7 @@ func testQueryServerContractQuery(t *testing.T) { Loader: loader, CreateRegistry: createRegistry, BlockStore: store.NewMockBlockStore(), - AuthCfg: auth.DefaultConfig(), + AuthCfg: keys.DefaultConfig(), } bus := &QueryEventBus{ Subs: *loomchain.NewSubscriptionSet(), @@ -179,7 +184,7 @@ func testQueryServerNonce(t *testing.T) { ChainID: "default", }, BlockStore: store.NewMockBlockStore(), - AuthCfg: auth.DefaultConfig(), + AuthCfg: keys.DefaultConfig(), } bus := &QueryEventBus{ Subs: *loomchain.NewSubscriptionSet(), @@ -248,7 +253,7 @@ func testQueryMetric(t *testing.T) { Loader: loader, CreateRegistry: createRegistry, BlockStore: store.NewMockBlockStore(), - AuthCfg: auth.DefaultConfig(), + AuthCfg: keys.DefaultConfig(), } qs = InstrumentingMiddleware{requestCount, requestLatency, qs} bus := &QueryEventBus{ diff --git a/rpc/query_service.go b/rpc/query_service.go index 2c31a49474..c3b6541b54 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/state/state.go b/state/state.go index 8a682e7815..8ed95cd30a 100644 --- a/state/state.go +++ b/state/state.go @@ -70,6 +70,21 @@ func NewStoreState( } } +func NewStoreState2( + ctx context.Context, + store store.KVStore, + block types.BlockHeader, + getValidatorSet GetValidatorSet, +) *StoreState { + return &StoreState{ + ctx: ctx, + store: store, + block: block, + validators: loom.NewValidatorSet(), + getValidatorSet: getValidatorSet, + } +} + func (s *StoreState) WithOnChainConfig(config *cctypes.Config) *StoreState { s.config = config return s 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..df15af1e77 100755 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -8,11 +8,12 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/util" - "github.com/loomnetwork/loomchain/log" "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" "github.com/tendermint/iavl" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/loomnetwork/loomchain/log" ) var ( @@ -216,6 +217,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..9fce729f43 100644 --- a/store/logstore.go +++ b/store/logstore.go @@ -127,5 +127,13 @@ func (s *LogStore) Prune() error { } func (s *LogStore) GetSnapshot() Snapshot { - return s.store.GetSnapshot() + snapshot, err := s.GetSnapshotAt(0) + if err != nil { + panic(err) + } + return snapshot +} + +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 50fa059163..e28acefad9 100644 --- a/store/multi_writer_app_store.go +++ b/store/multi_writer_app_store.go @@ -11,12 +11,13 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/util" - "github.com/loomnetwork/loomchain/db" - "github.com/loomnetwork/loomchain/features" - "github.com/loomnetwork/loomchain/log" "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" "github.com/tendermint/iavl" + + "github.com/loomnetwork/loomchain/db" + "github.com/loomnetwork/loomchain/features" + "github.com/loomnetwork/loomchain/log" ) var ( @@ -119,7 +120,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 } @@ -257,12 +260,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 { @@ -297,7 +318,6 @@ func (s *multiWriterStoreSnapshot) Get(key []byte) []byte { return val } -// Range iterates in-order over the keys in the store prefixed by the given prefix. func (s *multiWriterStoreSnapshot) Range(prefix []byte) plugin.RangeData { if len(prefix) == 0 { panic(errors.New("Range over nil prefix not implemented")) diff --git a/store/pruning_iavlstore.go b/store/pruning_iavlstore.go index 4e73cbad31..cff86bfd89 100644 --- a/store/pruning_iavlstore.go +++ b/store/pruning_iavlstore.go @@ -178,10 +178,15 @@ 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, + snapshot, err := s.GetSnapshotAt(0) + if err != nil { + panic(err) } + return snapshot +} + +func (s *PruningIAVLStore) GetSnapshotAt(version int64) (Snapshot, error) { + return s.store.GetSnapshotAt(version) } func (s *PruningIAVLStore) prune() error { @@ -261,11 +266,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/throttle/evm.go b/throttle/evm.go index 7f2e73142e..444967ee7d 100644 --- a/throttle/evm.go +++ b/throttle/evm.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" ) -func isEthDeploy(txBytes []byte) (bool, error) { +func IsEthDeploy(txBytes []byte) (bool, error) { var tx types.Transaction if err := rlp.DecodeBytes(txBytes, &tx); err != nil { return false, errors.Wrap(err, "decoding ethereum transaction") diff --git a/throttle/legacy_deployer_middleware.go b/throttle/legacy_deployer_middleware.go index 3826e3aa65..0855539349 100644 --- a/throttle/legacy_deployer_middleware.go +++ b/throttle/legacy_deployer_middleware.go @@ -6,24 +6,25 @@ import ( "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/pkg/errors" + + "github.com/loomnetwork/loomchain/auth/keys" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" ) // GetGoDeployTxMiddleWare creates middlware that only allows Go contract deployment tx to go through // if they originate from one of the allowed deployer accounts. This middleware has been superseded // by the DeployerWhitelist contract & middleware, though it's still in use on some clusters. -func GetGoDeployTxMiddleWare(allowedDeployers []loom.Address) loomchain.TxMiddlewareFunc { - return loomchain.TxMiddlewareFunc(func( +func GetGoDeployTxMiddleWare(allowedDeployers []loom.Address) txhandler.TxMiddlewareFunc { + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (res loomchain.TxHandlerResult, err error) { - var tx loomchain.Transaction + ) (res txhandler.TxHandlerResult, err error) { + var tx types.Transaction if err := proto.Unmarshal(txBytes, &tx); err != nil { return res, errors.Wrapf(err, "unmarshal tx %v", txBytes) } @@ -43,7 +44,7 @@ func GetGoDeployTxMiddleWare(allowedDeployers []loom.Address) loomchain.TxMiddle } if deployTx.VmType == vm.VMType_PLUGIN { - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) for _, allowed := range allowedDeployers { if 0 == origin.Compare(allowed) { return next(state, txBytes, isCheckTx) @@ -54,29 +55,3 @@ func GetGoDeployTxMiddleWare(allowedDeployers []loom.Address) loomchain.TxMiddle return next(state, txBytes, isCheckTx) }) } - -type GoContractDeployerWhitelistConfig struct { - Enabled bool - DeployerAddressList []string -} - -func DefaultGoContractDeployerWhitelistConfig() *GoContractDeployerWhitelistConfig { - return &GoContractDeployerWhitelistConfig{ - Enabled: false, - } -} - -func (c *GoContractDeployerWhitelistConfig) DeployerAddresses(chainId string) ([]loom.Address, error) { - deployerAddressList := make([]loom.Address, 0, len(c.DeployerAddressList)) - for _, addrStr := range c.DeployerAddressList { - addr, err := loom.ParseAddress(addrStr) - if err != nil { - addr, err = loom.ParseAddress(chainId + ":" + addrStr) - if err != nil { - return nil, errors.Wrapf(err, "parsing deploy address %s", addrStr) - } - } - deployerAddressList = append(deployerAddressList, addr) - } - return deployerAddressList, nil -} diff --git a/throttle/noevm.go b/throttle/noevm.go index 1260d8894c..3ffd294bef 100644 --- a/throttle/noevm.go +++ b/throttle/noevm.go @@ -6,6 +6,6 @@ import ( "github.com/pkg/errors" ) -func isEthDeploy(_ []byte) (bool, error) { +func IsEthDeploy(_ []byte) (bool, error) { return false, errors.New("ethereum transactions not supported in non evm build") } diff --git a/throttle/throttle.go b/throttle/throttle.go index 1fdcfb93c7..33f136069c 100644 --- a/throttle/throttle.go +++ b/throttle/throttle.go @@ -15,19 +15,13 @@ import ( "github.com/loomnetwork/go-loom/common" "github.com/loomnetwork/go-loom/plugin/contractpb" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/karma" appstate "github.com/loomnetwork/loomchain/state" ) -const ( - deployId = uint32(1) - callId = uint32(2) - migrationId = uint32(3) -) - type Throttle struct { - maxCallCount int64 + MaxCallCount int64 sessionDuration int64 callLimiterPool map[string]*limiter.Limiter deployLimiterPool map[string]*limiter.Limiter @@ -44,7 +38,7 @@ func NewThrottle( maxCallCount int64, ) *Throttle { return &Throttle{ - maxCallCount: maxCallCount, + MaxCallCount: maxCallCount, sessionDuration: sessionDuration, callLimiterPool: make(map[string]*limiter.Limiter), deployLimiterPool: make(map[string]*limiter.Limiter), @@ -52,7 +46,7 @@ func NewThrottle( } } -func (t *Throttle) getNewLimiter(ctx context.Context, limit int64) *limiter.Limiter { +func (t *Throttle) GetNewLimiter(ctx context.Context, limit int64) *limiter.Limiter { rate := limiter.Rate{ Period: time.Duration(t.sessionDuration) * time.Second, Limit: limit, @@ -62,14 +56,14 @@ func (t *Throttle) getNewLimiter(ctx context.Context, limit int64) *limiter.Limi } func (t *Throttle) getLimiterFromPool(ctx context.Context, limit int64) *limiter.Limiter { - address := auth.Origin(ctx).String() + address := keys.Origin(ctx).String() _, ok := t.callLimiterPool[address] if !ok { - t.callLimiterPool[address] = t.getNewLimiter(ctx, limit) + t.callLimiterPool[address] = t.GetNewLimiter(ctx, limit) } if t.callLimiterPool[address].Rate.Limit != limit { delete(t.callLimiterPool, address) - t.callLimiterPool[address] = t.getNewLimiter(ctx, limit) + t.callLimiterPool[address] = t.GetNewLimiter(ctx, limit) } return t.callLimiterPool[address] @@ -78,7 +72,7 @@ func (t *Throttle) getLimiterFromPool(ctx context.Context, limit int64) *limiter func (t *Throttle) getLimiterContext( ctx context.Context, nonce uint64, limit int64, txId uint32, key string, ) (limiter.Context, error) { - address := auth.Origin(ctx).String() + address := keys.Origin(ctx).String() if address == t.lastAddress && nonce == t.lastNonce && t.lastId == txId { return t.lastLimiterContext, nil } else { @@ -91,7 +85,7 @@ func (t *Throttle) getLimiterContext( } } -func (t *Throttle) runThrottle( +func (t *Throttle) RunThrottle( state appstate.State, nonce uint64, origin loom.Address, limit int64, txId uint32, key string, ) error { limitCtx, err := t.getLimiterContext(state.Context(), nonce, limit, txId, key) @@ -112,7 +106,7 @@ func (t *Throttle) runThrottle( return nil } -func (t *Throttle) getKarmaForTransaction( +func (t *Throttle) GetKarmaForTransaction( karmaContractCtx contractpb.Context, origin loom.Address, isDeployTx bool, ) (*common.BigUInt, error) { // TODO: maybe should only count karma from active sources diff --git a/throttle/tx_limiter_middleware.go b/throttle/tx_limiter_middleware.go deleted file mode 100644 index 70992cf006..0000000000 --- a/throttle/tx_limiter_middleware.go +++ /dev/null @@ -1,94 +0,0 @@ -package throttle - -import ( - "context" - "time" - - "github.com/loomnetwork/go-loom" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" - appstate "github.com/loomnetwork/loomchain/state" - "github.com/pkg/errors" - "github.com/ulule/limiter" - "github.com/ulule/limiter/drivers/store/memory" -) - -type TxLimiterConfig struct { - // Enables the tx limiter middleware - Enabled bool - // Number of seconds each session lasts - SessionDuration int64 - // Maximum number of txs that should be allowed per session - MaxTxsPerSession int64 -} - -func DefaultTxLimiterConfig() *TxLimiterConfig { - return &TxLimiterConfig{ - SessionDuration: 60, - MaxTxsPerSession: 60, - } -} - -// Clone returns a deep clone of the config. -func (c *TxLimiterConfig) Clone() *TxLimiterConfig { - if c == nil { - return nil - } - clone := *c - return &clone -} - -type txLimiter struct { - *limiter.Limiter -} - -func newTxLimiter(cfg *TxLimiterConfig) *txLimiter { - return &txLimiter{ - Limiter: limiter.New( - memory.NewStore(), - limiter.Rate{ - Period: time.Duration(cfg.SessionDuration) * time.Second, - Limit: cfg.MaxTxsPerSession, - }, - ), - } -} - -func (txl *txLimiter) isAccountLimitReached(account loom.Address) bool { - lmtCtx, err := txl.Limiter.Get(context.TODO(), account.String()) - // Doesn't look like the current implementation of the limit with the in-memory store will ever - // return an error anyway. - if err != nil { - panic(err) - } - return lmtCtx.Reached -} - -// NewTxLimiterMiddleware creates middleware that throttles txs (all types) in CheckTx, the rate -// can be configured in loom.yml. Since this middleware only runs in CheckTx the rate limit can -// differ between nodes on the same cluster, and private nodes don't really need to run the rate -// limiter at all. -func NewTxLimiterMiddleware(cfg *TxLimiterConfig) loomchain.TxMiddlewareFunc { - txl := newTxLimiter(cfg) - return loomchain.TxMiddlewareFunc(func( - state appstate.State, - txBytes []byte, - next loomchain.TxHandlerFunc, - isCheckTx bool, - ) (loomchain.TxHandlerResult, error) { - if !isCheckTx { - return next(state, txBytes, isCheckTx) - } - - origin := auth.Origin(state.Context()) - if origin.IsEmpty() { - return loomchain.TxHandlerResult{}, errors.New("throttle: transaction has no origin [get-karma]") - } - - if txl.isAccountLimitReached(origin) { - return loomchain.TxHandlerResult{}, errors.New("tx limit reached, try again later") - } - - return next(state, txBytes, isCheckTx) - }) -} diff --git a/tx_handler/eth_tx_handler.go b/txhandler/ethtx/eth_tx_handler.go similarity index 92% rename from tx_handler/eth_tx_handler.go rename to txhandler/ethtx/eth_tx_handler.go index 8630374594..df0f47c7c2 100644 --- a/tx_handler/eth_tx_handler.go +++ b/txhandler/ethtx/eth_tx_handler.go @@ -1,6 +1,6 @@ // +build evm -package tx_handler +package ethtx import ( "fmt" @@ -12,12 +12,12 @@ import ( "github.com/loomnetwork/go-loom/types" "github.com/pkg/errors" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/eth/utils" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/registry/factory" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" ) @@ -31,8 +31,8 @@ func (h *EthTxHandler) ProcessTx( state appstate.State, txBytes []byte, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult if !state.FeatureEnabled(features.EthTxFeature, false) { return r, errors.New("ethereum transactions feature not enabled") @@ -43,7 +43,7 @@ func (h *EthTxHandler) ProcessTx( return r, err } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) caller := loom.UnmarshalAddressPB(msg.From) if caller.Compare(origin) != 0 { diff --git a/tx_handler/eth_tx_handler_noevm.go b/txhandler/ethtx/eth_tx_handler_noevm.go similarity index 96% rename from tx_handler/eth_tx_handler_noevm.go rename to txhandler/ethtx/eth_tx_handler_noevm.go index 07404f5ac4..5012d4d984 100644 --- a/tx_handler/eth_tx_handler_noevm.go +++ b/txhandler/ethtx/eth_tx_handler_noevm.go @@ -1,6 +1,6 @@ // +build !evm -package tx_handler +package ethtx import ( "github.com/loomnetwork/loomchain" diff --git a/txhandler/factory/handler_factory.go b/txhandler/factory/handler_factory.go new file mode 100644 index 0000000000..12809b5a0f --- /dev/null +++ b/txhandler/factory/handler_factory.go @@ -0,0 +1,269 @@ +package factory + +import ( + "github.com/loomnetwork/go-loom/plugin/contractpb" + "github.com/pkg/errors" + + ethvm "github.com/ethereum/go-ethereum/core/vm" + "github.com/gogo/protobuf/proto" + + "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/state" + "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/throttle" + "github.com/loomnetwork/loomchain/txhandler" + "github.com/loomnetwork/loomchain/txhandler/middleware" + "github.com/loomnetwork/loomchain/txhandler/migration" + "github.com/loomnetwork/loomchain/vm" +) + +func NewTxHandlerFactory( + cfg config.Config, + vmManager *vm.Manager, + chainID string, + store store.VersionedKVStore, + createRegistry factory.RegistryFactoryFunc, +) txhandler.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) txhandler.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) (txhandler.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 txhandler.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 state.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, +) ([]txhandler.TxMiddleware, error) { + txMiddleWare := []txhandler.TxMiddleware{ + txhandler.LogTxMiddleware, + txhandler.RecoveryTxMiddleware, + } + + txMiddleWare = append(txMiddleWare, auth.NewChainConfigMiddleware( + cfg.Auth, + getContractStaticCtx("addressmapper", vmManager), + )) + + if cfg.Karma.Enabled { + txMiddleWare = append(txMiddleWare, middleware.GetKarmaMiddleWare( + cfg.Karma.Enabled, + cfg.Karma.MaxCallCount, + cfg.Karma.SessionDuration, + getContractCtx("karma", vmManager), + )) + } + + if cfg.TxLimiter.Enabled { + txMiddleWare = append(txMiddleWare, middleware.NewTxLimiterMiddleware(cfg.TxLimiter)) + } + + if cfg.ContractTxLimiter.Enabled { + udwCtxFactory := getContractCtx("user-deployer-whitelist", vmManager) + txMiddleWare = append( + txMiddleWare, middleware.NewContractTxLimiterMiddleware(cfg.ContractTxLimiter, udwCtxFactory), + ) + } + + if cfg.DeployerWhitelist.ContractEnabled { + dwMiddleware, err := middleware.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, txhandler.NewInstrumentingTxMiddleware()) + } + + return txMiddleWare, nil +} + +func router( + cfg config.Config, + vmManager vm.Manager, + createRegistry factory.RegistryFactoryFunc, +) txhandler.TxHandler { + router := middleware.NewTxRouter() + isEvmTx := func(txID uint32, _ state.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, + } + + migrationTxHandler := &migration.MigrationTxHandler{ + Manager: &vmManager, + CreateRegistry: createRegistry, + Migrations: map[int32]migration.MigrationFunc{ + 1: migrations.DPOSv3Migration, + 2: migrations.GatewayMigration, + 3: migrations.GatewayMigration, + }, + } + + router.HandleDeliverTx(1, middleware.GeneratePassthroughRouteHandler(deployTxHandler)) + router.HandleDeliverTx(2, middleware.GeneratePassthroughRouteHandler(callTxHandler)) + router.HandleDeliverTx(3, middleware.GeneratePassthroughRouteHandler(migrationTxHandler)) + + // TODO: Write this in more elegant way + router.HandleCheckTx(1, middleware.GenerateConditionalRouteHandler(isEvmTx, txhandler.NoopTxHandler, deployTxHandler)) + router.HandleCheckTx(2, middleware.GenerateConditionalRouteHandler(isEvmTx, txhandler.NoopTxHandler, callTxHandler)) + router.HandleCheckTx(3, middleware.GenerateConditionalRouteHandler(isEvmTx, txhandler.NoopTxHandler, migrationTxHandler)) + + return router +} + +func postCommitMiddleWAre( + cfg config.Config, + vmManager vm.Manager, + nonceTxHandler *auth.NonceHandler, +) ([]txhandler.PostCommitMiddleware, error) { + postCommitMiddlewares := []txhandler.PostCommitMiddleware{ + txhandler.LogPostCommitMiddleware, + } + + if cfg.UserDeployerWhitelist.ContractEnabled { + udwContractFactory := getContractCtx("user-deployer-whitelist", vmManager) + evmDeployRecorderMiddleware, err := middleware.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(_ state.State) (contractpb.Context, error) { + return func(_state state.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(_ state.State) (contractpb.StaticContext, error) { + return func(_state state.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) + } +} diff --git a/middleware.go b/txhandler/middleware.go similarity index 99% rename from middleware.go rename to txhandler/middleware.go index d5325450de..ec0d60cd34 100644 --- a/middleware.go +++ b/txhandler/middleware.go @@ -1,4 +1,4 @@ -package loomchain +package txhandler import ( "encoding/base64" diff --git a/throttle/contract_tx_limiter_middleware_test.go b/txhandler/middleware/contract_tx_limiter_middleware_test.go similarity index 94% rename from throttle/contract_tx_limiter_middleware_test.go rename to txhandler/middleware/contract_tx_limiter_middleware_test.go index 75612ecffe..451e314337 100644 --- a/throttle/contract_tx_limiter_middleware_test.go +++ b/txhandler/middleware/contract_tx_limiter_middleware_test.go @@ -1,6 +1,6 @@ // +build evm -package throttle +package middleware import ( "testing" @@ -11,16 +11,18 @@ import ( goloomplugin "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/loomnetwork/loomchain/builtin/plugins/coin" "github.com/loomnetwork/loomchain/builtin/plugins/deployer_whitelist" udw "github.com/loomnetwork/loomchain/builtin/plugins/user_deployer_whitelist" + "github.com/loomnetwork/loomchain/config" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" ) var ( @@ -103,7 +105,7 @@ func TestContractTxLimiterMiddleware(t *testing.T) { state := appstate.NewStoreState(nil, store.NewMemStore(), abci.Header{Height: 5}, nil, nil) //EVMTxn txSignedEVM1 := mockSignedTx(t, uint64(1), types.TxID_CALL, vm.VMType_EVM, contractAddr) - cfg := DefaultContractTxLimiterConfig() + cfg := config.DefaultContractTxLimiterConfig() contractTxLimiterMiddleware := NewContractTxLimiterMiddleware(cfg, func(_ appstate.State) (contractpb.Context, error) { return contractpb.WrapPluginContext(udwContext), nil @@ -115,9 +117,9 @@ func TestContractTxLimiterMiddleware(t *testing.T) { contractTxLimiterMiddleware.ProcessTx( state, txbytes, - func(_ appstate.State, txBytes []byte, isCheckTx bool) (res loomchain.TxHandlerResult, err error) { + func(_ appstate.State, txBytes []byte, isCheckTx bool) (res txhandler.TxHandlerResult, err error) { allowed = true - return loomchain.TxHandlerResult{}, nil + return txhandler.TxHandlerResult{}, nil }, true, ) diff --git a/throttle/deployer-middleware.go b/txhandler/middleware/deployer-middleware.go similarity index 86% rename from throttle/deployer-middleware.go rename to txhandler/middleware/deployer-middleware.go index 694079eeab..3099f4a9f1 100644 --- a/throttle/deployer-middleware.go +++ b/txhandler/middleware/deployer-middleware.go @@ -1,19 +1,26 @@ -package throttle +package middleware import ( "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" + "github.com/loomnetwork/go-loom/auth" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/pkg/errors" + + "github.com/loomnetwork/loomchain/auth/keys" dw "github.com/loomnetwork/loomchain/builtin/plugins/deployer_whitelist" udw "github.com/loomnetwork/loomchain/builtin/plugins/user_deployer_whitelist" "github.com/loomnetwork/loomchain/eth/utils" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/throttle" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" +) + +const ( + callId = uint32(2) ) var ( @@ -28,12 +35,12 @@ var ( // Records deploymentAddress and vmType func NewEVMDeployRecorderPostCommitMiddleware( createDeployerWhitelistCtx func(state appstate.State) (contractpb.Context, error), -) (loomchain.PostCommitMiddleware, error) { - return loomchain.PostCommitMiddlewareFunc(func( +) (txhandler.PostCommitMiddleware, error) { + return txhandler.PostCommitMiddlewareFunc(func( state appstate.State, txBytes []byte, - res loomchain.TxHandlerResult, - next loomchain.PostCommitHandler, + res txhandler.TxHandlerResult, + next txhandler.PostCommitHandler, isCheckTx bool, ) error { if !state.FeatureEnabled(features.UserDeployerWhitelistFeature, false) { @@ -55,7 +62,7 @@ func NewEVMDeployRecorderPostCommitMiddleware( return errors.Wrapf(err, "unmarshal deploy response %v", res.Data) } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) ctx, err := createDeployerWhitelistCtx(state) if err != nil { return err @@ -71,13 +78,13 @@ func NewEVMDeployRecorderPostCommitMiddleware( func NewDeployerWhitelistMiddleware( createDeployerWhitelistCtx func(state appstate.State) (contractpb.Context, error), -) (loomchain.TxMiddlewareFunc, error) { - return loomchain.TxMiddlewareFunc(func( +) (txhandler.TxMiddlewareFunc, error) { + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (res loomchain.TxHandlerResult, err error) { + ) (res txhandler.TxHandlerResult, err error) { if !state.FeatureEnabled(features.DeployerWhitelistFeature, false) { return next(state, txBytes, isCheckTx) @@ -106,7 +113,7 @@ func NewDeployerWhitelistMiddleware( } if deployTx.VmType == vm.VMType_PLUGIN { - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) ctx, err := createDeployerWhitelistCtx(state) if err != nil { return res, err @@ -115,7 +122,7 @@ func NewDeployerWhitelistMiddleware( return res, err } } else if deployTx.VmType == vm.VMType_EVM { - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) ctx, err := createDeployerWhitelistCtx(state) if err != nil { return res, err @@ -126,7 +133,7 @@ func NewDeployerWhitelistMiddleware( } case types.TxID_MIGRATION: - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) ctx, err := createDeployerWhitelistCtx(state) if err != nil { return res, err @@ -144,14 +151,14 @@ func NewDeployerWhitelistMiddleware( if err := proto.Unmarshal(tx.Data, &msg); err != nil { return res, errors.Wrap(err, "failed to unmarshal MessageTx") } - isDeploy, err := isEthDeploy(msg.Data) + isDeploy, err := throttle.IsEthDeploy(msg.Data) if err != nil { return res, err } if !isDeploy { break } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) ctx, err := createDeployerWhitelistCtx(state) if err != nil { return res, err diff --git a/throttle/deployer-middleware_test.go b/txhandler/middleware/deployer-middleware_test.go similarity index 77% rename from throttle/deployer-middleware_test.go rename to txhandler/middleware/deployer-middleware_test.go index 8efbc66d27..b1108f6c90 100644 --- a/throttle/deployer-middleware_test.go +++ b/txhandler/middleware/deployer-middleware_test.go @@ -1,6 +1,6 @@ // +build evm -package throttle +package middleware import ( "context" @@ -14,7 +14,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/loomnetwork/go-loom/types" - loomAuth "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" dw "github.com/loomnetwork/loomchain/builtin/plugins/deployer_whitelist" "github.com/loomnetwork/loomchain/features" appstate "github.com/loomnetwork/loomchain/state" @@ -46,8 +46,8 @@ func TestDeployerWhitelistMiddleware(t *testing.T) { Owner: owner.MarshalPB(), })) - guestCtx := context.WithValue(state.Context(), loomAuth.ContextKeyOrigin, guest) - ownerCtx := context.WithValue(state.Context(), loomAuth.ContextKeyOrigin, owner) + guestCtx := context.WithValue(state.Context(), keys.ContextKeyOrigin, guest) + ownerCtx := context.WithValue(state.Context(), keys.ContextKeyOrigin, owner) dwMiddleware, err := NewDeployerWhitelistMiddleware( func(_ appstate.State) (contractpb.Context, error) { @@ -57,24 +57,24 @@ func TestDeployerWhitelistMiddleware(t *testing.T) { require.NoError(t, err) // unauthorized deployer (DeployTx Plugin) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedPlugin, guestCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedPlugin, guestCtx) require.Equal(t, ErrNotAuthorized, err) // unauthorized deployer (DeployTx EVM) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedEVM, guestCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedEVM, guestCtx) require.Equal(t, ErrNotAuthorized, err) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedEth, guestCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedEth, guestCtx) require.Equal(t, ErrNotAuthorized, err) // unauthorized deployer (MigrationTx) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedMigration, guestCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedMigration, guestCtx) require.Equal(t, ErrNotAuthorized, err) // authorized deployer - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedPlugin, ownerCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedPlugin, ownerCtx) require.NoError(t, err) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedEVM, ownerCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedEVM, ownerCtx) require.NoError(t, err) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedEth, ownerCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedEth, ownerCtx) require.NoError(t, err) - _, err = throttleMiddlewareHandler(dwMiddleware, state, txSignedMigration, ownerCtx) + _, err = ThrottleMiddlewareHandler(dwMiddleware, state, txSignedMigration, ownerCtx) require.NoError(t, err) } diff --git a/throttle/karma-middleware.go b/txhandler/middleware/karma-middleware.go similarity index 87% rename from throttle/karma-middleware.go rename to txhandler/middleware/karma-middleware.go index 18118bf142..ad7880a9b5 100644 --- a/throttle/karma-middleware.go +++ b/txhandler/middleware/karma-middleware.go @@ -1,4 +1,4 @@ -package throttle +package middleware import ( "fmt" @@ -10,13 +10,15 @@ import ( "github.com/loomnetwork/go-loom/common" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/pkg/errors" + + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/karma" "github.com/loomnetwork/loomchain/eth/utils" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/throttle" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" ) const karmaMiddlewareThrottleKey = "ThrottleTxMiddleWare" @@ -26,19 +28,19 @@ func GetKarmaMiddleWare( maxCallCount int64, sessionDuration int64, createKarmaContractCtx func(state appstate.State) (contractpb.Context, error), -) loomchain.TxMiddlewareFunc { - th := NewThrottle(sessionDuration, maxCallCount) - return loomchain.TxMiddlewareFunc(func( +) txhandler.TxMiddlewareFunc { + th := throttle.NewThrottle(sessionDuration, maxCallCount) + return txhandler.TxMiddlewareFunc(func( state appstate.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (res loomchain.TxHandlerResult, err error) { + ) (res txhandler.TxHandlerResult, err error) { if !karmaEnabled { return next(state, txBytes, isCheckTx) } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) if origin.IsEmpty() { return res, errors.New("throttle: transaction has no origin [get-karma]") } @@ -48,7 +50,7 @@ func GetKarmaMiddleWare( return res, errors.Wrap(err, "throttle: unwrap nonce Tx") } - var tx loomchain.Transaction + var tx types.Transaction if err := proto.Unmarshal(nonceTx.Inner, &tx); err != nil { return res, errors.New("throttle: unmarshal tx") } @@ -85,7 +87,7 @@ func GetKarmaMiddleWare( isDeployTx = true case types.TxID_ETHEREUM: - isDeployTx, err = isEthDeploy(msg.Data) + isDeployTx, err = throttle.IsEthDeploy(msg.Data) if err != nil { return res, err } @@ -125,7 +127,7 @@ func GetKarmaMiddleWare( return r, nil } - originKarma, err := th.getKarmaForTransaction(ctx, origin, isDeployTx) + originKarma, err := th.GetKarmaForTransaction(ctx, origin, isDeployTx) if err != nil { return res, errors.Wrap(err, "getting total karma") } @@ -156,11 +158,11 @@ func GetKarmaMiddleWare( if maxCallCount <= 0 { return res, errors.Errorf("max call count %d non positive", maxCallCount) } - callCount := th.maxCallCount + originKarmaTotal - if originKarmaTotal > math.MaxInt64-th.maxCallCount { + callCount := th.MaxCallCount + originKarmaTotal + if originKarmaTotal > math.MaxInt64-th.MaxCallCount { callCount = math.MaxInt64 } - err := th.runThrottle(state, nonceTx.Sequence, origin, callCount, tx.Id, karmaMiddlewareThrottleKey) + err := th.RunThrottle(state, nonceTx.Sequence, origin, callCount, tx.Id, karmaMiddlewareThrottleKey) if err != nil { return res, errors.Wrap(err, "call karma throttle") } diff --git a/throttle/karma-middleware_test.go b/txhandler/middleware/karma-middleware_test.go similarity index 85% rename from throttle/karma-middleware_test.go rename to txhandler/middleware/karma-middleware_test.go index e850cf1d19..6d5371297d 100644 --- a/throttle/karma-middleware_test.go +++ b/txhandler/middleware/karma-middleware_test.go @@ -1,6 +1,6 @@ // +build evm -package throttle +package middleware import ( "context" @@ -11,14 +11,15 @@ import ( goloomplugin "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain/auth" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/karma" "github.com/loomnetwork/loomchain/log" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" "github.com/loomnetwork/loomchain/vm" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" ) var ( @@ -56,7 +57,7 @@ func TestKarmaMiddleWare(t *testing.T) { // This can also be done on init, but more concise this way require.NoError(t, karma.AddKarma(contractContext, origin, sourceStatesDeploy)) - ctx := context.WithValue(state.Context(), auth.ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) tmx := GetKarmaMiddleWare( true, @@ -69,23 +70,23 @@ func TestKarmaMiddleWare(t *testing.T) { // call fails as contract is not deployed txSigned := mockSignedTx(t, uint64(1), types.TxID_CALL, vm.VMType_EVM, contract) - _, err := throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err := ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.Error(t, err) txSigned = mockSignedTx(t, uint64(1), types.TxID_ETHEREUM, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.Error(t, err) // deploy contract txSigned = mockSignedTx(t, uint64(2), types.TxID_DEPLOY, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.NoError(t, err) // call now works txSigned = mockSignedTx(t, uint64(3), types.TxID_CALL, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.NoError(t, err) txSigned = mockSignedTx(t, uint64(1), types.TxID_ETHEREUM, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.NoError(t, err) // deactivate contract @@ -95,10 +96,10 @@ func TestKarmaMiddleWare(t *testing.T) { // call now fails txSigned = mockSignedTx(t, uint64(4), types.TxID_CALL, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.Error(t, err) txSigned = mockSignedTx(t, uint64(1), types.TxID_ETHEREUM, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.Error(t, err) } @@ -124,7 +125,7 @@ func TestMinKarmaToDeploy(t *testing.T) { require.NoError(t, karma.AddKarma(contractContext, origin, sourceStatesDeploy)) - ctx := context.WithValue(state.Context(), auth.ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) tmx := GetKarmaMiddleWare( true, @@ -137,7 +138,7 @@ func TestMinKarmaToDeploy(t *testing.T) { // deploy contract txSigned := mockSignedTx(t, uint64(2), types.TxID_DEPLOY, vm.VMType_EVM, contract) - _, err := throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err := ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.NoError(t, err) require.NoError(t, karma.SetConfig(contractContext, &ktypes.KarmaConfig{ @@ -146,6 +147,6 @@ func TestMinKarmaToDeploy(t *testing.T) { // deploy contract txSigned = mockSignedTx(t, uint64(2), types.TxID_DEPLOY, vm.VMType_EVM, contract) - _, err = throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err = ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) require.Error(t, err) } diff --git a/throttle/middleware_test_helper.go b/txhandler/middleware/middleware_test_helper.go similarity index 83% rename from throttle/middleware_test_helper.go rename to txhandler/middleware/middleware_test_helper.go index 0c83dc9b2f..239d792935 100644 --- a/throttle/middleware_test_helper.go +++ b/txhandler/middleware/middleware_test_helper.go @@ -1,4 +1,4 @@ -package throttle +package middleware import ( "context" @@ -7,29 +7,31 @@ import ( "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/auth" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" + "github.com/pkg/errors" + loomAuth "github.com/loomnetwork/loomchain/auth" "github.com/loomnetwork/loomchain/eth/utils" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/throttle" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" - "github.com/pkg/errors" ) var ( contract = loom.MustParseAddress("chain:0x9a1aC42a17AAD6Dbc6d21c162989d0f701074044") ) -func throttleMiddlewareHandler(ttm loomchain.TxMiddlewareFunc, state appstate.State, tx auth.SignedTx, ctx context.Context) (loomchain.TxHandlerResult, error) { +func ThrottleMiddlewareHandler(ttm txhandler.TxMiddlewareFunc, state appstate.State, tx auth.SignedTx, ctx context.Context) (txhandler.TxHandlerResult, error) { return ttm.ProcessTx( state.WithContext(ctx), tx.Inner, - func(state appstate.State, txBytes []byte, isCheckTx bool) (res loomchain.TxHandlerResult, err error) { + func(state appstate.State, txBytes []byte, isCheckTx bool) (res txhandler.TxHandlerResult, err error) { var nonceTx loomAuth.NonceTx if err := proto.Unmarshal(txBytes, &nonceTx); err != nil { return res, errors.Wrap(err, "throttle: unwrap nonce Tx") } - var tx loomchain.Transaction + var tx types.Transaction if err := proto.Unmarshal(nonceTx.Inner, &tx); err != nil { return res, errors.New("throttle: unmarshal tx") } @@ -69,7 +71,7 @@ func throttleMiddlewareHandler(ttm loomchain.TxMiddlewareFunc, state appstate.St }) case types.TxID_ETHEREUM: - isDeploy, err := isEthDeploy(msg.Data) + isDeploy, err := throttle.IsEthDeploy(msg.Data) if err != nil { return res, err } @@ -84,7 +86,7 @@ func throttleMiddlewareHandler(ttm loomchain.TxMiddlewareFunc, state appstate.St info = utils.CallEVM } } - return loomchain.TxHandlerResult{Data: data, Info: info}, err + return txhandler.TxHandlerResult{Data: data, Info: info}, err }, false, ) diff --git a/router.go b/txhandler/middleware/router.go similarity index 74% rename from router.go rename to txhandler/middleware/router.go index d8dd531b0c..3b758b0f41 100644 --- a/router.go +++ b/txhandler/middleware/router.go @@ -1,4 +1,4 @@ -package loomchain +package middleware import ( "github.com/gogo/protobuf/proto" @@ -6,27 +6,26 @@ import ( "github.com/loomnetwork/go-loom/types" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" ) -type Transaction = types.Transaction - type TxRouter struct { deliverTxRoutes map[uint32]RouteHandler checkTxRoutes map[uint32]RouteHandler } -type RouteHandler func(txID uint32, state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) +type RouteHandler func(txID uint32, _ appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) type RouteConditionFunc func(txID uint32, state appstate.State, txBytes []byte, isCheckTx bool) bool -var GeneratePassthroughRouteHandler = func(txHandler TxHandler) RouteHandler { - return func(txID uint32, state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { +var GeneratePassthroughRouteHandler = func(txHandler txhandler.TxHandler) RouteHandler { + return func(txID uint32, state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { return txHandler.ProcessTx(state, txBytes, isCheckTx) } } -func GenerateConditionalRouteHandler(conditionFn RouteConditionFunc, onTrue TxHandler, onFalse TxHandler) RouteHandler { - return RouteHandler(func(txId uint32, state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { +func GenerateConditionalRouteHandler(conditionFn RouteConditionFunc, onTrue txhandler.TxHandler, onFalse txhandler.TxHandler) RouteHandler { + return RouteHandler(func(txId uint32, state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { if conditionFn(txId, state, txBytes, isCheckTx) { return onTrue.ProcessTx(state, txBytes, isCheckTx) } @@ -57,10 +56,10 @@ func (r *TxRouter) HandleCheckTx(txID uint32, handler RouteHandler) { r.checkTxRoutes[txID] = handler } -func (r *TxRouter) ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { - var res TxHandlerResult +func (r *TxRouter) ProcessTx(state appstate.State, txBytes []byte, isCheckTx bool) (txhandler.TxHandlerResult, error) { + var res txhandler.TxHandlerResult - var tx Transaction + var tx types.Transaction err := proto.Unmarshal(txBytes, &tx) if err != nil { return res, err diff --git a/throttle/contract_tx_limiter_middleware.go b/txhandler/middleware/throttle.go similarity index 64% rename from throttle/contract_tx_limiter_middleware.go rename to txhandler/middleware/throttle.go index b90c1f1484..9f9b8bed51 100644 --- a/throttle/contract_tx_limiter_middleware.go +++ b/txhandler/middleware/throttle.go @@ -1,6 +1,7 @@ -package throttle +package middleware import ( + "context" "fmt" "time" @@ -8,29 +9,87 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/gogo/protobuf/proto" "github.com/loomnetwork/go-loom" - udwtypes "github.com/loomnetwork/go-loom/builtin/types/user_deployer_whitelist" "github.com/loomnetwork/go-loom/plugin/contractpb" - ltypes "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" - udw "github.com/loomnetwork/loomchain/builtin/plugins/user_deployer_whitelist" - appstate "github.com/loomnetwork/loomchain/state" - "github.com/loomnetwork/loomchain/vm" + "github.com/loomnetwork/go-loom/types" "github.com/pkg/errors" stdprometheus "github.com/prometheus/client_golang/prometheus" -) + "github.com/ulule/limiter" + "github.com/ulule/limiter/drivers/store/memory" -var ( - ErrTxLimitReached = errors.New("tx limit reached, try again later") - ErrContractNotWhitelisted = errors.New("contract not whitelisted") - ErrInactiveDeployer = errors.New("can't call contract belonging to inactive deployer") + udwtypes "github.com/loomnetwork/go-loom/builtin/types/user_deployer_whitelist" + + "github.com/loomnetwork/go-loom/auth" + + "github.com/loomnetwork/loomchain/auth/keys" + udw "github.com/loomnetwork/loomchain/builtin/plugins/user_deployer_whitelist" + "github.com/loomnetwork/loomchain/config" + "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" + "github.com/loomnetwork/loomchain/vm" ) var ( tierMapLoadLatency metrics.Histogram contractTierMapLoadLatency metrics.Histogram + ErrInactiveDeployer = errors.New("can't call contract belonging to inactive deployer") + ErrTxLimitReached = errors.New("tx limit reached, try again later") ) +type txLimiter struct { + *limiter.Limiter +} + +func newTxLimiter(cfg *config.TxLimiterConfig) *txLimiter { + return &txLimiter{ + Limiter: limiter.New( + memory.NewStore(), + limiter.Rate{ + Period: time.Duration(cfg.SessionDuration) * time.Second, + Limit: cfg.MaxTxsPerSession, + }, + ), + } +} + +func (txl *txLimiter) isAccountLimitReached(account loom.Address) bool { + lmtCtx, err := txl.Limiter.Get(context.TODO(), account.String()) + // Doesn't look like the current implementation of the limit with the in-memory store will ever + // return an error anyway. + if err != nil { + panic(err) + } + return lmtCtx.Reached +} + +// NewTxLimiterMiddleware creates middleware that throttles txs (all types) in CheckTx, the rate +// can be configured in loom.yml. Since this middleware only runs in CheckTx the rate limit can +// differ between nodes on the same cluster, and private nodes don't really need to run the rate +// limiter at all. +func NewTxLimiterMiddleware(cfg *config.TxLimiterConfig) txhandler.TxMiddlewareFunc { + txl := newTxLimiter(cfg) + return txhandler.TxMiddlewareFunc(func( + _state state.State, + txBytes []byte, + next txhandler.TxHandlerFunc, + isCheckTx bool, + ) (txhandler.TxHandlerResult, error) { + if !isCheckTx { + return next(_state, txBytes, isCheckTx) + } + + origin := keys.Origin(_state.Context()) + if origin.IsEmpty() { + return txhandler.TxHandlerResult{}, errors.New("throttle: transaction has no origin [get-karma]") + } + + if txl.isAccountLimitReached(origin) { + return txhandler.TxHandlerResult{}, errors.New("tx limit reached, try again later") + } + + return next(_state, txBytes, isCheckTx) + }) +} + func init() { fieldKeys := []string{"method", "error"} tierMapLoadLatency = kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ @@ -49,31 +108,6 @@ func init() { }, fieldKeys) } -type ContractTxLimiterConfig struct { - // Enables the middleware - Enabled bool - // Number of seconds each refresh lasts - ContractDataRefreshInterval int64 - TierDataRefreshInterval int64 -} - -func DefaultContractTxLimiterConfig() *ContractTxLimiterConfig { - return &ContractTxLimiterConfig{ - Enabled: false, - ContractDataRefreshInterval: 15 * 60, - TierDataRefreshInterval: 15 * 60, - } -} - -// Clone returns a deep clone of the config. -func (c *ContractTxLimiterConfig) Clone() *ContractTxLimiterConfig { - if c == nil { - return nil - } - clone := *c - return &clone -} - type contractTxLimiter struct { // contract_address to limiting parametres structure contractToTierMap map[string]udw.TierID @@ -143,64 +177,47 @@ func loadTierMap(ctx contractpb.StaticContext) (map[udwtypes.TierID]udwtypes.Tie // NewContractTxLimiterMiddleware creates a middleware function that limits how many call txs can be // sent to an EVM contract within a pre-configured block range. -func NewContractTxLimiterMiddleware(cfg *ContractTxLimiterConfig, - createUserDeployerWhitelistCtx func(state appstate.State) (contractpb.Context, error), -) loomchain.TxMiddlewareFunc { +func NewContractTxLimiterMiddleware(cfg *config.ContractTxLimiterConfig, + createUserDeployerWhitelistCtx func(_state state.State) (contractpb.Context, error), +) txhandler.TxMiddlewareFunc { txl := &contractTxLimiter{ contractStatsMap: make(map[string]*contractStats), } - return loomchain.TxMiddlewareFunc(func( - state appstate.State, + return txhandler.TxMiddlewareFunc(func( + _state state.State, txBytes []byte, - next loomchain.TxHandlerFunc, + next txhandler.TxHandlerFunc, isCheckTx bool, - ) (res loomchain.TxHandlerResult, err error) { + ) (res txhandler.TxHandlerResult, err error) { if !isCheckTx { - return next(state, txBytes, isCheckTx) + return next(_state, txBytes, isCheckTx) } var nonceTx auth.NonceTx if err := proto.Unmarshal(txBytes, &nonceTx); err != nil { return res, errors.Wrap(err, "throttle: unwrap nonce Tx") } - var tx loomchain.Transaction + var tx types.Transaction if err := proto.Unmarshal(nonceTx.Inner, &tx); err != nil { return res, errors.New("throttle: unmarshal tx") } - + if tx.Id != callId { + return next(_state, txBytes, isCheckTx) + } var msg vm.MessageTx - switch ltypes.TxID(tx.Id) { - case ltypes.TxID_CALL: - if err := proto.Unmarshal(tx.Data, &msg); err != nil { - return res, errors.Wrapf(err, "unmarshal message tx %v", tx.Data) - } - var callTx vm.CallTx - if err := proto.Unmarshal(msg.Data, &callTx); err != nil { - return res, errors.Wrapf(err, "unmarshal call tx %v", msg.Data) - } - if callTx.VmType != vm.VMType_EVM { - return next(state, txBytes, isCheckTx) - } - - case ltypes.TxID_ETHEREUM: - if err := proto.Unmarshal(tx.Data, &msg); err != nil { - return res, errors.Wrapf(err, "unmarshal message tx %v", tx.Data) - } - isDeploy, err := isEthDeploy(msg.Data) - if err != nil { - return res, err - } - if isDeploy { - return next(state, txBytes, isCheckTx) - } - - default: - return next(state, txBytes, isCheckTx) + if err := proto.Unmarshal(tx.Data, &msg); err != nil { + return res, errors.Wrapf(err, "unmarshal message tx %v", tx.Data) + } + var msgTx vm.CallTx + if err := proto.Unmarshal(msg.Data, &msgTx); err != nil { + return res, errors.Wrapf(err, "unmarshal call tx %v", msg.Data) + } + if msgTx.VmType != vm.VMType_EVM { + return next(_state, txBytes, isCheckTx) } - if txl.inactiveDeployerContracts == nil || txl.contractToTierMap == nil || (txl.contractDataLastUpdated+cfg.ContractDataRefreshInterval) < time.Now().Unix() { - ctx, err := createUserDeployerWhitelistCtx(state) + ctx, err := createUserDeployerWhitelistCtx(_state) if err != nil { return res, errors.Wrap(err, "throttle: context creation") } @@ -222,11 +239,11 @@ func NewContractTxLimiterMiddleware(cfg *ContractTxLimiterConfig, // contracts the limiter doesn't know about shouldn't be throttled contractTierID, ok := txl.contractToTierMap[contractAddr.String()] if !ok { - return next(state, txBytes, isCheckTx) + return next(_state, txBytes, isCheckTx) } if txl.tierMap == nil || (txl.tierDataLastUpdated+cfg.TierDataRefreshInterval) < time.Now().Unix() { - ctx, er := createUserDeployerWhitelistCtx(state) + ctx, er := createUserDeployerWhitelistCtx(_state) if er != nil { return res, errors.Wrap(err, "throttle: context creation") } @@ -239,7 +256,7 @@ func NewContractTxLimiterMiddleware(cfg *ContractTxLimiterConfig, // ensure that tier corresponding to contract available in tierMap _, ok = txl.tierMap[contractTierID] if !ok { - ctx, er := createUserDeployerWhitelistCtx(state) + ctx, er := createUserDeployerWhitelistCtx(_state) if er != nil { return res, errors.Wrap(err, "throttle: context creation") } @@ -249,12 +266,11 @@ func NewContractTxLimiterMiddleware(cfg *ContractTxLimiterConfig, } txl.tierMap[contractTierID] = tierInfo } - - if txl.isAccountLimitReached(contractAddr, state.Block().Height) { - return loomchain.TxHandlerResult{}, ErrTxLimitReached + if txl.isAccountLimitReached(contractAddr, _state.Block().Height) { + return txhandler.TxHandlerResult{}, ErrTxLimitReached } - txl.updateState(contractAddr, state.Block().Height) + txl.updateState(contractAddr, _state.Block().Height) - return next(state, txBytes, isCheckTx) + return next(_state, txBytes, isCheckTx) }) } diff --git a/throttle/throttle_test.go b/txhandler/middleware/throttle_test.go similarity index 93% rename from throttle/throttle_test.go rename to txhandler/middleware/throttle_test.go index 3330621fb8..8f6cb8c9de 100644 --- a/throttle/throttle_test.go +++ b/txhandler/middleware/throttle_test.go @@ -1,6 +1,6 @@ // +build evm -package throttle +package middleware import ( "context" @@ -20,16 +20,16 @@ import ( goloomplugin "github.com/loomnetwork/go-loom/plugin" "github.com/loomnetwork/go-loom/plugin/contractpb" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - loomAuth "github.com/loomnetwork/loomchain/auth" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "golang.org/x/crypto/ed25519" + + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/builtin/plugins/karma" "github.com/loomnetwork/loomchain/log" appstate "github.com/loomnetwork/loomchain/state" "github.com/loomnetwork/loomchain/store" "github.com/loomnetwork/loomchain/vm" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "golang.org/x/crypto/ed25519" ) const ( @@ -82,7 +82,7 @@ func TestDeployThrottleTxMiddleware(t *testing.T) { // This can also be done on init, but more concise this way require.NoError(t, karma.AddKarma(contractContext, origin, sourceStates)) - ctx := context.WithValue(state.Context(), loomAuth.ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) tmx := GetKarmaMiddleWare( true, @@ -97,7 +97,7 @@ func TestDeployThrottleTxMiddleware(t *testing.T) { for i := int64(1); i <= deployKarma.Value.Int64()+1; i++ { txSigned := mockSignedTx(t, uint64(i), types.TxID_DEPLOY, vm.VMType_PLUGIN, contract) - _, err := throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err := ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) if i <= deployKarma.Value.Int64() { require.NoError(t, err) @@ -106,7 +106,7 @@ func TestDeployThrottleTxMiddleware(t *testing.T) { for i := int64(1); i <= deployKarma.Value.Int64()+1; i++ { txSigned := mockSignedTx(t, uint64(i), types.TxID_ETHEREUM, vm.VMType_EVM, loom.Address{}) - _, err := throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err := ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) if i <= deployKarma.Value.Int64() { require.NoError(t, err) @@ -133,7 +133,7 @@ func TestCallThrottleTxMiddleware(t *testing.T) { // This can also be done on init, but more concise this way require.NoError(t, karma.AddKarma(contractContext, origin, sourceStates)) - ctx := context.WithValue(state.Context(), loomAuth.ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) tmx := GetKarmaMiddleWare( true, @@ -148,7 +148,7 @@ func TestCallThrottleTxMiddleware(t *testing.T) { for i := int64(1); i <= maxCallCount*2+callKarma.Value.Int64(); i++ { txSigned := mockSignedTx(t, uint64(i), types.TxID_CALL, vm.VMType_PLUGIN, contract) - _, err := throttleMiddlewareHandler(tmx, state, txSigned, ctx) + _, err := ThrottleMiddlewareHandler(tmx, state, txSigned, ctx) if i <= maxCallCount+callKarma.Value.Int64() { require.NoError(t, err) @@ -218,7 +218,7 @@ func mockSignedTx(t *testing.T, sequence uint64, id types.TxID, vmType vm.VMType require.FailNow(t, "invalid tx ID") } - tx, err := proto.Marshal(&loomchain.Transaction{ + tx, err := proto.Marshal(&types.Transaction{ Id: uint32(id), Data: messageTx, }) diff --git a/middleware_test.go b/txhandler/middleware_test.go similarity index 99% rename from middleware_test.go rename to txhandler/middleware_test.go index 8e98d35b3c..a574660964 100644 --- a/middleware_test.go +++ b/txhandler/middleware_test.go @@ -1,4 +1,4 @@ -package loomchain +package txhandler import ( "testing" diff --git a/tx_handler/migration_tx_handler.go b/txhandler/migration/migration_tx_handler.go similarity index 90% rename from tx_handler/migration_tx_handler.go rename to txhandler/migration/migration_tx_handler.go index 759d3ad3a1..cf5b3bb298 100644 --- a/tx_handler/migration_tx_handler.go +++ b/txhandler/migration/migration_tx_handler.go @@ -1,22 +1,23 @@ -package tx_handler +package migration import ( "bytes" "encoding/binary" "fmt" - proto "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" "github.com/pkg/errors" - loom "github.com/loomnetwork/go-loom" + "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/util" goloomvm "github.com/loomnetwork/go-loom/vm" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/migrations" registry "github.com/loomnetwork/loomchain/registry/factory" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" "github.com/loomnetwork/loomchain/vm" ) @@ -48,8 +49,8 @@ func (h *MigrationTxHandler) ProcessTx( state appstate.State, txBytes []byte, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult if !state.FeatureEnabled(features.MigrationTxFeature, false) { return r, fmt.Errorf("MigrationTx feature hasn't been enabled") @@ -61,7 +62,7 @@ func (h *MigrationTxHandler) ProcessTx( return r, err } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) caller := loom.UnmarshalAddressPB(msg.From) if caller.Compare(origin) != 0 { diff --git a/tx_handler/migration_tx_handler_test.go b/txhandler/migration/migration_tx_handler_test.go similarity index 96% rename from tx_handler/migration_tx_handler_test.go rename to txhandler/migration/migration_tx_handler_test.go index ba54ec03a3..84896d2a03 100644 --- a/tx_handler/migration_tx_handler_test.go +++ b/txhandler/migration/migration_tx_handler_test.go @@ -1,4 +1,4 @@ -package tx_handler +package migration import ( "context" @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/features" "github.com/loomnetwork/loomchain/migrations" appstate "github.com/loomnetwork/loomchain/state" @@ -24,7 +24,7 @@ func TestMigrationTxHandler(t *testing.T) { state := appstate.NewStoreState(nil, store.NewMemStore(), abci.Header{}, nil, nil) state.SetFeature(features.MigrationTxFeature, true) - ctx := context.WithValue(state.Context(), auth.ContextKeyOrigin, origin) + ctx := context.WithValue(state.Context(), keys.ContextKeyOrigin, origin) s := state.WithContext(ctx) migrationTx1 := mockMessageTx(t, uint32(1), origin, origin, []byte{}) diff --git a/txhandler/txhandler.go b/txhandler/txhandler.go new file mode 100644 index 0000000000..ec2033e7d9 --- /dev/null +++ b/txhandler/txhandler.go @@ -0,0 +1,34 @@ +package txhandler + +import ( + "github.com/ethereum/go-ethereum/core/vm" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/common" + + "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/store" +) + +type TxHandler interface { + ProcessTx(state.State, []byte, bool) (TxHandlerResult, error) +} + +type TxHandlerFunc func(state.State, []byte, bool) (TxHandlerResult, error) + +type TxHandlerResult struct { + Data []byte + ValidatorUpdates []abci.Validator + Info string + // Tags to associate with the tx that produced this result. Tags can be used to filter txs + // via the ABCI query interface (see https://godoc.org/github.com/tendermint/tendermint/libs/pubsub/query) + Tags []common.KVPair +} + +func (f TxHandlerFunc) ProcessTx(s state.State, txBytes []byte, isCheckTx bool) (TxHandlerResult, error) { + return f(s, txBytes, isCheckTx) +} + +type TxHandlerFactory interface { + TxHandler(tracer vm.Tracer, metrics bool) (TxHandler, error) + Copy(newStore store.VersionedKVStore) TxHandlerFactory +} diff --git a/vm/handler.go b/vm/handler.go index b9ec579307..c2af845c62 100644 --- a/vm/handler.go +++ b/vm/handler.go @@ -8,12 +8,12 @@ import ( "github.com/loomnetwork/go-loom" "github.com/loomnetwork/go-loom/types" - "github.com/loomnetwork/loomchain" - "github.com/loomnetwork/loomchain/auth" + "github.com/loomnetwork/loomchain/auth/keys" "github.com/loomnetwork/loomchain/eth/utils" "github.com/loomnetwork/loomchain/features" registry "github.com/loomnetwork/loomchain/registry/factory" appstate "github.com/loomnetwork/loomchain/state" + "github.com/loomnetwork/loomchain/txhandler" ) type DeployTxHandler struct { @@ -26,8 +26,8 @@ func (h *DeployTxHandler) ProcessTx( state appstate.State, txBytes []byte, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult var msg MessageTx err := proto.Unmarshal(txBytes, &msg) @@ -35,7 +35,7 @@ func (h *DeployTxHandler) ProcessTx( return r, err } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) caller := loom.UnmarshalAddressPB(msg.From) if caller.Compare(origin) != 0 { @@ -108,8 +108,8 @@ func (h *CallTxHandler) ProcessTx( txBytes []byte, isCheckTx bool, -) (loomchain.TxHandlerResult, error) { - var r loomchain.TxHandlerResult +) (txhandler.TxHandlerResult, error) { + var r txhandler.TxHandlerResult var msg MessageTx err := proto.Unmarshal(txBytes, &msg) @@ -117,7 +117,7 @@ func (h *CallTxHandler) ProcessTx( return r, err } - origin := auth.Origin(state.Context()) + origin := keys.Origin(state.Context()) caller := loom.UnmarshalAddressPB(msg.From) addr := loom.UnmarshalAddressPB(msg.To)