From 22a9a9b27dddd9322a95accd015c7e2dce44cedd Mon Sep 17 00:00:00 2001 From: Alexandr Borodulin <sashaborodulin@gmail.com> Date: Mon, 13 Dec 2021 01:24:21 +0300 Subject: [PATCH] Add error tests for starknet_sendRawTransaction method (#3102) * Add error tests for starknet_sendRawTransaction method * Noop tx pool * Rename cairo tx to starknet tx, refactor starknet send raw transaction test * Remove noop tx pool * Transaction struct Co-authored-by: Aleksandr Borodulin <a.borodulin@axioma.lv> --- .../commands/starknet_send_transaction.go | 5 +- .../starknet_send_transaction_test.go | 80 +++++++++++++++++++ cmd/starknet/services/raw_tx_generator.go | 2 +- core/types/access_list_tx.go | 15 ---- core/types/block_test.go | 10 +-- core/types/dynamic_fee_tx.go | 50 +++--------- core/types/legacy_tx.go | 40 ++++++---- core/types/{cairo_tx.go => starknet_tx.go} | 75 +++++++---------- core/types/transaction.go | 13 +-- core/types/transaction_signing.go | 2 +- core/types/transaction_test.go | 12 +-- eth/stagedsync/stage_bodies_test.go | 2 +- turbo/stages/blockchain_test.go | 10 +-- 13 files changed, 175 insertions(+), 141 deletions(-) create mode 100644 cmd/rpcdaemon/commands/starknet_send_transaction_test.go rename core/types/{cairo_tx.go => starknet_tx.go} (78%) diff --git a/cmd/rpcdaemon/commands/starknet_send_transaction.go b/cmd/rpcdaemon/commands/starknet_send_transaction.go index 8a8aa73edd..7bb90ea3bf 100644 --- a/cmd/rpcdaemon/commands/starknet_send_transaction.go +++ b/cmd/rpcdaemon/commands/starknet_send_transaction.go @@ -10,6 +10,7 @@ import ( "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/rlp" + "github.com/ledgerwatch/log/v3" ) var ( @@ -43,5 +44,7 @@ func (api *StarknetImpl) SendRawTransaction(ctx context.Context, encodedTx hexut return hash, fmt.Errorf("%s: %s", txPoolProto.ImportResult_name[int32(res.Imported[0])], res.Errors[0]) } - return common.Hash{}, err + log.Info("Submitted contract creation", "hash", txn.Hash().Hex(), "nonce", txn.GetNonce(), "value", txn.GetValue()) + + return txn.Hash(), nil } diff --git a/cmd/rpcdaemon/commands/starknet_send_transaction_test.go b/cmd/rpcdaemon/commands/starknet_send_transaction_test.go new file mode 100644 index 0000000000..251460a21e --- /dev/null +++ b/cmd/rpcdaemon/commands/starknet_send_transaction_test.go @@ -0,0 +1,80 @@ +package commands_test + +import ( + "bytes" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon-lib/gointerfaces/txpool" + "github.com/ledgerwatch/erigon-lib/kv/kvcache" + "github.com/ledgerwatch/erigon/cmd/rpcdaemon/commands" + "github.com/ledgerwatch/erigon/cmd/rpcdaemon/filters" + "github.com/ledgerwatch/erigon/cmd/rpcdaemon/rpcdaemontest" + "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/turbo/snapshotsync" + "github.com/ledgerwatch/erigon/turbo/stages" + "github.com/stretchr/testify/require" + "testing" +) + +func TestErrorStarknetSendRawTransaction(t *testing.T) { + var cases = []struct { + name string + tx string + error error + }{ + {name: "wrong tx type", tx: generateDynamicFeeTransaction(), error: commands.ErrOnlyStarknetTx}, + {name: "not contract creation", tx: generateStarknetTransaction(), error: commands.ErrOnlyContractDeploy}, + } + + m, require := stages.MockWithTxPool(t), require.New(t) + ctx, conn := rpcdaemontest.CreateTestGrpcConn(t, m) + txPool := txpool.NewTxpoolClient(conn) + ff := filters.New(ctx, nil, txPool, txpool.NewMiningClient(conn)) + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + + for _, tt := range cases { + api := commands.NewStarknetAPI(commands.NewBaseApi(ff, stateCache, snapshotsync.NewBlockReader(), false), m.DB, txPool) + + t.Run(tt.name, func(t *testing.T) { + hex, _ := hexutil.Decode(tt.tx) + + _, err := api.SendRawTransaction(ctx, hex) + + require.ErrorIs(err, tt.error) + }) + } +} + +func generateDynamicFeeTransaction() string { + buf := bytes.NewBuffer(nil) + types.DynamicFeeTransaction{ + CommonTx: types.CommonTx{ + ChainID: new(uint256.Int), + Nonce: 1, + Value: uint256.NewInt(1), + Gas: 1, + }, + Tip: new(uint256.Int), + FeeCap: new(uint256.Int), + }.MarshalBinary(buf) + + return hexutil.Encode(buf.Bytes()) +} + +func generateStarknetTransaction() string { + buf := bytes.NewBuffer(nil) + types.StarknetTransaction{ + CommonTx: types.CommonTx{ + ChainID: new(uint256.Int), + Nonce: 1, + Value: uint256.NewInt(1), + Gas: 1, + To: &common.Address{}, + }, + Tip: new(uint256.Int), + FeeCap: new(uint256.Int), + }.MarshalBinary(buf) + + return hexutil.Encode(buf.Bytes()) +} diff --git a/cmd/starknet/services/raw_tx_generator.go b/cmd/starknet/services/raw_tx_generator.go index 8c528c6567..26df5b54f7 100644 --- a/cmd/starknet/services/raw_tx_generator.go +++ b/cmd/starknet/services/raw_tx_generator.go @@ -41,7 +41,7 @@ func (g RawTxGenerator) CreateFromFS(fileSystem fs.FS, contractFileName string, enc := make([]byte, hex.EncodedLen(len(contract))) hex.Encode(enc, contract) - tx := types.CairoTransaction{ + tx := types.StarknetTransaction{ CommonTx: types.CommonTx{ Nonce: 1, Value: uint256.NewInt(1), diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index b60d891fe9..8e76375f90 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -617,18 +617,3 @@ func (tx *AccessListTx) Sender(signer Signer) (common.Address, error) { tx.from.Store(addr) return addr, nil } - -func (tx AccessListTx) GetSender() (common.Address, bool) { - if sc := tx.from.Load(); sc != nil { - return sc.(common.Address), true - } - return common.Address{}, false -} - -func (tx *AccessListTx) SetSender(addr common.Address) { - tx.from.Store(addr) -} - -func (tx AccessListTx) IsStarkNet() bool { - return false -} diff --git a/core/types/block_test.go b/core/types/block_test.go index 35857ee3e6..c72ace5fbb 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -107,12 +107,12 @@ func TestEIP1559BlockEncoding(t *testing.T) { to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") feeCap, _ := uint256.FromBig(block.BaseFee()) var tx2 Transaction = &DynamicFeeTransaction{ - ChainID: u256.Num1, CommonTx: CommonTx{ - Nonce: 0, - To: &to, - Gas: 123457, - Data: []byte{}, + ChainID: u256.Num1, + Nonce: 0, + To: &to, + Gas: 123457, + Data: []byte{}, }, FeeCap: feeCap, Tip: u256.Num0, diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index 42d0f6c62f..8669a7aa7d 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -31,7 +31,6 @@ import ( type DynamicFeeTransaction struct { CommonTx - ChainID *uint256.Int Tip *uint256.Int FeeCap *uint256.Int AccessList AccessList @@ -71,15 +70,15 @@ func (tx DynamicFeeTransaction) copy() *DynamicFeeTransaction { TransactionMisc: TransactionMisc{ time: tx.time, }, - Nonce: tx.Nonce, - To: tx.To, // TODO: copy pointed-to address - Data: common.CopyBytes(tx.Data), - Gas: tx.Gas, + ChainID: new(uint256.Int), + Nonce: tx.Nonce, + To: tx.To, // TODO: copy pointed-to address + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, // These are copied below. Value: new(uint256.Int), }, AccessList: make(AccessList, len(tx.AccessList)), - ChainID: new(uint256.Int), Tip: new(uint256.Int), FeeCap: new(uint256.Int), } @@ -115,10 +114,6 @@ func (tx *DynamicFeeTransaction) Size() common.StorageSize { return common.StorageSize(c) } -func (tx DynamicFeeTransaction) Protected() bool { - return true -} - func (tx DynamicFeeTransaction) EncodingSize() int { payloadSize, _, _, _ := tx.payloadSize() envelopeSize := payloadSize @@ -507,10 +502,6 @@ func (tx DynamicFeeTransaction) RawSignatureValues() (*uint256.Int, *uint256.Int return &tx.V, &tx.R, &tx.S } -func (tx DynamicFeeTransaction) GetChainID() *uint256.Int { - return tx.ChainID -} - func (tx *DynamicFeeTransaction) Sender(signer Signer) (common.Address, error) { if sc := tx.from.Load(); sc != nil { return sc.(common.Address), nil @@ -523,33 +514,18 @@ func (tx *DynamicFeeTransaction) Sender(signer Signer) (common.Address, error) { return addr, nil } -func (tx DynamicFeeTransaction) GetSender() (common.Address, bool) { - if sc := tx.from.Load(); sc != nil { - return sc.(common.Address), true - } - return common.Address{}, false -} - -func (tx *DynamicFeeTransaction) SetSender(addr common.Address) { - tx.from.Store(addr) -} - -func (tx DynamicFeeTransaction) IsStarkNet() bool { - return false -} - // NewTransaction creates an unsigned eip1559 transaction. func NewEIP1559Transaction(chainID uint256.Int, nonce uint64, to common.Address, amount *uint256.Int, gasLimit uint64, gasPrice *uint256.Int, gasTip *uint256.Int, gasFeeCap *uint256.Int, data []byte) *DynamicFeeTransaction { return &DynamicFeeTransaction{ CommonTx: CommonTx{ - Nonce: nonce, - To: &to, - Value: amount, - Gas: gasLimit, - Data: data, + ChainID: &chainID, + Nonce: nonce, + To: &to, + Value: amount, + Gas: gasLimit, + Data: data, }, - ChainID: &chainID, - Tip: gasTip, - FeeCap: gasFeeCap, + Tip: gasTip, + FeeCap: gasFeeCap, } } diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 07871d5a79..1cb0374658 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -31,6 +31,8 @@ import ( type CommonTx struct { TransactionMisc + + ChainID *uint256.Int Nonce uint64 // nonce of sender account Gas uint64 // gas limit To *common.Address `rlp:"nil"` // nil means contract creation @@ -39,6 +41,10 @@ type CommonTx struct { V, R, S uint256.Int // signature values } +func (ct CommonTx) GetChainID() *uint256.Int { + return ct.ChainID +} + func (ct CommonTx) GetNonce() uint64 { return ct.Nonce } @@ -59,10 +65,29 @@ func (ct CommonTx) GetData() []byte { return ct.Data } +func (ct CommonTx) GetSender() (common.Address, bool) { + if sc := ct.from.Load(); sc != nil { + return sc.(common.Address), true + } + return common.Address{}, false +} + +func (ct *CommonTx) SetSender(addr common.Address) { + ct.from.Store(addr) +} + +func (ct CommonTx) Protected() bool { + return true +} + func (ct CommonTx) IsContractDeploy() bool { return ct.GetTo() == nil } +func (ct CommonTx) IsStarkNet() bool { + return false +} + // LegacyTx is the transaction data of regular Ethereum transactions. type LegacyTx struct { CommonTx @@ -517,18 +542,3 @@ func (tx *LegacyTx) Sender(signer Signer) (common.Address, error) { tx.from.Store(addr) return addr, nil } - -func (tx LegacyTx) GetSender() (common.Address, bool) { - if sc := tx.from.Load(); sc != nil { - return sc.(common.Address), true - } - return common.Address{}, false -} - -func (tx *LegacyTx) SetSender(addr common.Address) { - tx.from.Store(addr) -} - -func (tx LegacyTx) IsStarkNet() bool { - return false -} diff --git a/core/types/cairo_tx.go b/core/types/starknet_tx.go similarity index 78% rename from core/types/cairo_tx.go rename to core/types/starknet_tx.go index bfa9cf7e6b..4c4c504223 100644 --- a/core/types/cairo_tx.go +++ b/core/types/starknet_tx.go @@ -11,24 +11,23 @@ import ( "math/bits" ) -type CairoTransaction struct { +type StarknetTransaction struct { CommonTx - ChainID *uint256.Int Tip *uint256.Int FeeCap *uint256.Int AccessList AccessList } -func (tx CairoTransaction) Type() byte { - return CairoType +func (tx StarknetTransaction) Type() byte { + return StarknetType } -func (tx CairoTransaction) IsStarkNet() bool { +func (tx StarknetTransaction) IsStarkNet() bool { return true } -func (tx *CairoTransaction) DecodeRLP(s *rlp.Stream) error { +func (tx *StarknetTransaction) DecodeRLP(s *rlp.Stream) error { _, err := s.List() if err != nil { return err @@ -111,35 +110,31 @@ func (tx *CairoTransaction) DecodeRLP(s *rlp.Stream) error { return s.ListEnd() } -func (tx CairoTransaction) GetChainID() *uint256.Int { +func (tx StarknetTransaction) GetPrice() *uint256.Int { panic("implement me") } -func (tx CairoTransaction) GetPrice() *uint256.Int { +func (tx StarknetTransaction) GetTip() *uint256.Int { panic("implement me") } -func (tx CairoTransaction) GetTip() *uint256.Int { +func (tx StarknetTransaction) GetEffectiveGasTip(baseFee *uint256.Int) *uint256.Int { panic("implement me") } -func (tx CairoTransaction) GetEffectiveGasTip(baseFee *uint256.Int) *uint256.Int { +func (tx StarknetTransaction) GetFeeCap() *uint256.Int { panic("implement me") } -func (tx CairoTransaction) GetFeeCap() *uint256.Int { +func (tx StarknetTransaction) Cost() *uint256.Int { panic("implement me") } -func (tx CairoTransaction) Cost() *uint256.Int { +func (tx StarknetTransaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { panic("implement me") } -func (tx CairoTransaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { - panic("implement me") -} - -func (tx *CairoTransaction) WithSignature(signer Signer, sig []byte) (Transaction, error) { +func (tx *StarknetTransaction) WithSignature(signer Signer, sig []byte) (Transaction, error) { cpy := tx.copy() r, s, v, err := signer.SignatureValues(tx, sig) if err != nil { @@ -152,11 +147,11 @@ func (tx *CairoTransaction) WithSignature(signer Signer, sig []byte) (Transactio return cpy, nil } -func (tx CairoTransaction) FakeSign(address common.Address) (Transaction, error) { +func (tx StarknetTransaction) FakeSign(address common.Address) (Transaction, error) { panic("implement me") } -func (tx CairoTransaction) Hash() common.Hash { +func (tx StarknetTransaction) Hash() common.Hash { if hash := tx.hash.Load(); hash != nil { return *hash.(*common.Hash) } @@ -176,30 +171,26 @@ func (tx CairoTransaction) Hash() common.Hash { return hash } -func (tx CairoTransaction) SigningHash(chainID *big.Int) common.Hash { +func (tx StarknetTransaction) SigningHash(chainID *big.Int) common.Hash { panic("implement me") } -func (tx CairoTransaction) Size() common.StorageSize { +func (tx StarknetTransaction) Size() common.StorageSize { panic("implement me") } -func (tx CairoTransaction) GetAccessList() AccessList { +func (tx StarknetTransaction) GetAccessList() AccessList { panic("implement me") } -func (tx CairoTransaction) Protected() bool { +func (tx StarknetTransaction) RawSignatureValues() (*uint256.Int, *uint256.Int, *uint256.Int) { panic("implement me") } -func (tx CairoTransaction) RawSignatureValues() (*uint256.Int, *uint256.Int, *uint256.Int) { - panic("implement me") -} - -func (tx CairoTransaction) MarshalBinary(w io.Writer) error { +func (tx StarknetTransaction) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() var b [33]byte - b[0] = CairoType + b[0] = StarknetType if _, err := w.Write(b[:1]); err != nil { return err } @@ -209,15 +200,11 @@ func (tx CairoTransaction) MarshalBinary(w io.Writer) error { return nil } -func (tx CairoTransaction) Sender(signer Signer) (common.Address, error) { - panic("implement me") -} - -func (tx CairoTransaction) SetSender(address common.Address) { +func (tx StarknetTransaction) Sender(signer Signer) (common.Address, error) { panic("implement me") } -func (tx CairoTransaction) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, gasLen, accessListLen int) error { +func (tx StarknetTransaction) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, gasLen, accessListLen int) error { // prefix if err := EncodeStructSizePrefix(payloadSize, w, b); err != nil { return err @@ -305,7 +292,7 @@ func (tx CairoTransaction) encodePayload(w io.Writer, b []byte, payloadSize, non return nil } -func (tx CairoTransaction) payloadSize() (payloadSize int, nonceLen, gasLen, accessListLen int) { +func (tx StarknetTransaction) payloadSize() (payloadSize int, nonceLen, gasLen, accessListLen int) { // size of ChainID payloadSize++ var chainIdLen int @@ -396,20 +383,20 @@ func (tx CairoTransaction) payloadSize() (payloadSize int, nonceLen, gasLen, acc return payloadSize, nonceLen, gasLen, accessListLen } -func (tx CairoTransaction) copy() *CairoTransaction { - cpy := &CairoTransaction{ +func (tx StarknetTransaction) copy() *StarknetTransaction { + cpy := &StarknetTransaction{ CommonTx: CommonTx{ TransactionMisc: TransactionMisc{ time: tx.time, }, - Nonce: tx.Nonce, - To: tx.To, - Data: common.CopyBytes(tx.Data), - Gas: tx.Gas, - Value: new(uint256.Int), + ChainID: new(uint256.Int), + Nonce: tx.Nonce, + To: tx.To, + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + Value: new(uint256.Int), }, AccessList: make(AccessList, len(tx.AccessList)), - ChainID: new(uint256.Int), Tip: new(uint256.Int), FeeCap: new(uint256.Int), } diff --git a/core/types/transaction.go b/core/types/transaction.go index 65269eb4b1..d82935e50a 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -44,7 +44,7 @@ const ( LegacyTxType = iota AccessListTxType DynamicFeeTxType - CairoType + StarknetType ) // Transaction is an Ethereum transaction. @@ -115,13 +115,6 @@ func (tm TransactionMisc) From() *atomic.Value { return &tm.from } -func (tm TransactionMisc) GetSender() (common.Address, bool) { - if sc := tm.from.Load(); sc != nil { - return sc.(common.Address), true - } - return common.Address{}, false -} - func DecodeTransaction(s *rlp.Stream) (Transaction, error) { kind, size, err := s.Kind() if err != nil { @@ -158,8 +151,8 @@ func DecodeTransaction(s *rlp.Stream) (Transaction, error) { return nil, err } tx = t - case CairoType: - t := &CairoTransaction{} + case StarknetType: + t := &StarknetTransaction{} if err = t.DecodeRLP(s); err != nil { return nil, err } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 09126ecb68..454bd0300d 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -271,7 +271,7 @@ func (sg Signer) SignatureValues(tx Transaction, sig []byte) (R, S, V *uint256.I return nil, nil, nil, ErrInvalidChainId } R, S, V = decodeSignature(sig) - case *CairoTransaction: + case *StarknetTransaction: // Check that chain ID of tx matches the signer. We also accept ID zero here, // because it indicates that the chain ID was not specified in the tx. if t.ChainID != nil && !t.ChainID.IsZero() && !t.ChainID.Eq(&sg.chainID) { diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index e34ebfb16a..18b3ec516c 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -78,13 +78,13 @@ var ( ) dynFeeTx = &DynamicFeeTransaction{ - ChainID: u256.Num1, CommonTx: CommonTx{ - Nonce: 3, - To: &testAddr, - Value: uint256.NewInt(10), - Gas: 25000, - Data: common.FromHex("5544"), + ChainID: u256.Num1, + Nonce: 3, + To: &testAddr, + Value: uint256.NewInt(10), + Gas: 25000, + Data: common.FromHex("5544"), }, Tip: uint256.NewInt(1), FeeCap: uint256.NewInt(1), diff --git a/eth/stagedsync/stage_bodies_test.go b/eth/stagedsync/stage_bodies_test.go index 8be8275a7b..5628718d80 100644 --- a/eth/stagedsync/stage_bodies_test.go +++ b/eth/stagedsync/stage_bodies_test.go @@ -18,7 +18,7 @@ import ( func TestBodiesUnwind(t *testing.T) { require, ctx := require.New(t), context.Background() _, tx := memdb.NewTestTx(t) - txn := &types.DynamicFeeTransaction{ChainID: u256.N1, Tip: u256.N1, FeeCap: u256.N1, CommonTx: types.CommonTx{Value: u256.N1, Gas: 1, Nonce: 1}} + txn := &types.DynamicFeeTransaction{Tip: u256.N1, FeeCap: u256.N1, CommonTx: types.CommonTx{ChainID: u256.N1, Value: u256.N1, Gas: 1, Nonce: 1}} buf := bytes.NewBuffer(nil) err := txn.MarshalBinary(buf) require.NoError(err) diff --git a/turbo/stages/blockchain_test.go b/turbo/stages/blockchain_test.go index de86ed1a92..1bc5b36a38 100644 --- a/turbo/stages/blockchain_test.go +++ b/turbo/stages/blockchain_test.go @@ -1943,12 +1943,12 @@ func TestEIP1559Transition(t *testing.T) { var chainID uint256.Int chainID.SetFromBig(gspec.Config.ChainID) var tx types.Transaction = &types.DynamicFeeTransaction{ - ChainID: &chainID, CommonTx: types.CommonTx{ - Nonce: 0, - To: &aa, - Gas: 30000, - Data: []byte{}, + ChainID: &chainID, + Nonce: 0, + To: &aa, + Gas: 30000, + Data: []byte{}, }, FeeCap: new(uint256.Int).Mul(new(uint256.Int).SetUint64(5), new(uint256.Int).SetUint64(params.GWei)), Tip: u256.Num2, -- GitLab