From 91c55131647d865484c606ceb50bc8325aea1e32 Mon Sep 17 00:00:00 2001 From: Valentin Staykov <79150443+V-Staykov@users.noreply.github.com> Date: Fri, 9 Feb 2024 19:01:11 +0200 Subject: [PATCH] Etrog sync fix (#116) * remove hardcoded value * minor fixes * fixed sync almost to tip for Cardana --- core/blockchain_zkevm.go | 26 ++++- core/state/intra_block_state_zkevm.go | 4 +- eth/stagedsync/stage_execute_zkevm.go | 2 +- smt/pkg/blockinfo/block_info.go | 10 +- zk/stages/stage_batches.go | 8 +- zk/stages/stage_sequence_execute.go | 4 +- zk/tx/tx.go | 51 +++++----- zk/tx/tx_test.go | 141 ++++++++++++++++++++++---- 8 files changed, 183 insertions(+), 63 deletions(-) diff --git a/core/blockchain_zkevm.go b/core/blockchain_zkevm.go index 0c1fa1dbb00..a274de38556 100644 --- a/core/blockchain_zkevm.go +++ b/core/blockchain_zkevm.go @@ -35,6 +35,7 @@ import ( "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" "github.com/ledgerwatch/erigon/smt/pkg/blockinfo" + txTypes "github.com/ledgerwatch/erigon/zk/tx" ) // ExecuteBlockEphemerally runs a block from provided stateReader and @@ -58,7 +59,7 @@ func ExecuteBlockEphemerallyZk( block.Uncles() ibs := state.New(stateReader) header := block.Header() - blockTransaction := block.Transactions() + blockTransactions := block.Transactions() blockGasLimit := block.GasLimit() gp := new(GasPool) gp.AddGas(blockGasLimit) @@ -79,7 +80,7 @@ func ExecuteBlockEphemerallyZk( } if !vmConfig.ReadOnly { - if err := InitializeBlockExecution(engine, chainReader, header, blockTransaction, block.Uncles(), chainConfig, ibs, excessDataGas); err != nil { + if err := InitializeBlockExecution(engine, chainReader, header, blockTransactions, block.Uncles(), chainConfig, ibs, excessDataGas); err != nil { return nil, err } } @@ -115,7 +116,6 @@ func ExecuteBlockEphemerallyZk( } blockTime := block.Time() ibs.SyncerPreExecuteStateSet(chainConfig, blockNum, blockTime, prevBlockHash, &blockGer, &l1BlockHash, &gersInBetween) - blockInfoTree := blockinfo.NewBlockInfoTree() if chainConfig.IsForkID7Etrog(blockNum) { coinbase := block.Coinbase() @@ -137,7 +137,7 @@ func ExecuteBlockEphemerallyZk( logIndex := int64(0) usedGas := new(uint64) - for txIndex, tx := range blockTransaction { + for txIndex, tx := range blockTransactions { ibs.Prepare(tx.Hash(), block.Hash(), txIndex) writeTrace := false if vmConfig.Debug && vmConfig.Tracer == nil { @@ -180,9 +180,25 @@ func ExecuteBlockEphemerallyZk( ibs.ScalableSetSmtRootHash(roHermezDb) } + txSender, _ := tx.GetSender() if chainConfig.IsForkID7Etrog(blockNum) { + l2TxHash, err := txTypes.ComputeL2TxHash( + chainConfig.ChainID, + tx.GetValue(), + tx.GetPrice(), + tx.GetNonce(), + tx.GetGas(), + tx.GetTo(), + &txSender, + tx.GetData(), + ) + if err != nil { + return nil, err + } + //block info tree _, err = blockInfoTree.SetBlockTx( + &l2TxHash, txIndex, receipt, logIndex, @@ -231,7 +247,7 @@ func ExecuteBlockEphemerallyZk( //} } if !vmConfig.ReadOnly { - txs := blockTransaction + txs := blockTransactions if _, _, _, err := FinalizeBlockExecution(engine, stateReader, block.Header(), txs, block.Uncles(), stateWriter, chainConfig, ibs, receipts, block.Withdrawals(), chainReader, false, excessDataGas); err != nil { return nil, err } diff --git a/core/state/intra_block_state_zkevm.go b/core/state/intra_block_state_zkevm.go index a6a52af098e..bf541dea151 100644 --- a/core/state/intra_block_state_zkevm.go +++ b/core/state/intra_block_state_zkevm.go @@ -65,7 +65,7 @@ func (sdb *IntraBlockState) PreExecuteStateSet(chainConfig *chain.Config, blockN } } -func (sdb *IntraBlockState) SyncerPreExecuteStateSet(chainConfig *chain.Config, blockNumber uint64, blockTimestamp uint64, stateRoot, blockGer, l1BlockHash *libcommon.Hash, gerUpdates *[]*dstypes.GerUpdate) { +func (sdb *IntraBlockState) SyncerPreExecuteStateSet(chainConfig *chain.Config, blockNumber uint64, blockTimestamp uint64, prevBlockHash, blockGer, l1BlockHash *libcommon.Hash, gerUpdates *[]*dstypes.GerUpdate) { if !sdb.Exist(ADDRESS_SCALABLE_L2) { // create account if not exists sdb.CreateAccount(ADDRESS_SCALABLE_L2, true) @@ -83,7 +83,7 @@ func (sdb *IntraBlockState) SyncerPreExecuteStateSet(chainConfig *chain.Config, } //save prev block hash - sdb.scalableSetBlockHash(blockNumber-1, stateRoot) + sdb.scalableSetBlockHash(blockNumber-1, prevBlockHash) //save ger with l1blockhash if blockGer != nil && *blockGer != emptyHash { diff --git a/eth/stagedsync/stage_execute_zkevm.go b/eth/stagedsync/stage_execute_zkevm.go index 32870a64470..fda5aa95d37 100644 --- a/eth/stagedsync/stage_execute_zkevm.go +++ b/eth/stagedsync/stage_execute_zkevm.go @@ -243,7 +243,7 @@ Loop: later. */ headerHash := header.Hash() - prevBlockHash = headerHash + prevBlockHash = header.Root rawdb.WriteHeader(tx, header) err = rawdb.WriteCanonicalHash(tx, headerHash, blockNum) diff --git a/smt/pkg/blockinfo/block_info.go b/smt/pkg/blockinfo/block_info.go index 9581ed96369..0d20ab94dc6 100644 --- a/smt/pkg/blockinfo/block_info.go +++ b/smt/pkg/blockinfo/block_info.go @@ -64,6 +64,7 @@ func (b *BlockInfoTree) InitBlockHeader(oldBlockHash *libcommon.Hash, coinbase * } func (b *BlockInfoTree) SetBlockTx( + l2TxHash *libcommon.Hash, txIndex int, receipt *ethTypes.Receipt, logIndex int64, @@ -71,11 +72,13 @@ func (b *BlockInfoTree) SetBlockTx( effectivePercentage uint8, ) (*big.Int, error) { txIndexBig := big.NewInt(int64(txIndex)) - _, err := setL2TxHash(b.smt, txIndexBig, receipt.TxHash.Big()) + _, err := setL2TxHash(b.smt, txIndexBig, l2TxHash.Big()) if err != nil { return nil, err } - _, err = setTxStatus(b.smt, txIndexBig, big.NewInt(int64(receipt.Status))) + + bigStatus := big.NewInt(0).SetUint64(receipt.Status) + _, err = setTxStatus(b.smt, txIndexBig, bigStatus) if err != nil { return nil, err } @@ -110,7 +113,8 @@ func (b *BlockInfoTree) SetBlockTx( logIndex += 1 } - root, err := setTxEffectivePercentage(b.smt, txIndexBig, big.NewInt(int64(effectivePercentage))) + bigEffectivePercentage := big.NewInt(0).SetUint64(uint64(effectivePercentage)) + root, err := setTxEffectivePercentage(b.smt, txIndexBig, bigEffectivePercentage) if err != nil { return nil, err } diff --git a/zk/stages/stage_batches.go b/zk/stages/stage_batches.go index 88647f2d612..f9d04fc61eb 100644 --- a/zk/stages/stage_batches.go +++ b/zk/stages/stage_batches.go @@ -407,13 +407,7 @@ func writeL2Block(eriDb ErigonDb, hermezDb HermezDb, l2Block *types.FullL2Block) return fmt.Errorf("write effective gas price percentage error: %v", err) } - //TODO: temp datastream fix, remove later - //works only for Cardona - stateRoot := transaction.StateRoot - if bn.Uint64() == 59056 { - stateRoot = common.HexToHash("0x0c21a98253d5ef5cf77faa5fbbc44e627fd14ec6d410c6e87d40e639e54b41d7") - } - if err := hermezDb.WriteStateRoot(l2Block.L2BlockNumber, stateRoot); err != nil { + if err := hermezDb.WriteStateRoot(l2Block.L2BlockNumber, transaction.StateRoot); err != nil { return fmt.Errorf("write rpc root error: %v", err) } } diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 673e2e0a23a..767b73b505d 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -389,7 +389,9 @@ func postBlockStateHandling( for i := 0; i < len(transactions); i++ { receipt := receipts[i] // todo: how to set the effective gas percentage as a the sequencer - _, err := infoTree.SetBlockTx(i, receipt, logIndex, receipt.CumulativeGasUsed, 0) + // TODO: calculate l2 tx hash + l2TxHash := common.Hash{} + _, err := infoTree.SetBlockTx(&l2TxHash, i, receipt, logIndex, receipt.CumulativeGasUsed, 0) if err != nil { return err } diff --git a/zk/tx/tx.go b/zk/tx/tx.go index d9aed92184d..5b58a99b69e 100644 --- a/zk/tx/tx.go +++ b/zk/tx/tx.go @@ -8,6 +8,9 @@ import ( "bytes" + "regexp" + "strings" + "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon/core/types" @@ -15,8 +18,6 @@ import ( "github.com/ledgerwatch/erigon/smt/pkg/utils" "github.com/ledgerwatch/erigon/zkevm/hex" "github.com/ledgerwatch/log/v3" - "regexp" - "strings" ) const ( @@ -235,21 +236,26 @@ func rlpFieldsToLegacyTx(fields [][]byte, v, r, s []byte) (tx *types.LegacyTx, e }, nil } -const txGasLimit = 30000000 +func ComputeL2TxHash( + chainId *big.Int, + value, gasPrice *uint256.Int, + nonce, txGasLimit uint64, + to, from *common.Address, + data []byte, +) (common.Hash, error) { -func ComputeL2TxHash(tx types.LegacyTx) (common.Hash, error) { txType := "01" - if tx.GetChainID().Eq(uint256.NewInt(0)) { + if chainId != nil && chainId.Cmp(big.NewInt(0)) == 0 { txType = "00" } // add txType, nonce, gasPrice and gasLimit - noncePart, err := formatL2TxHashParam(tx.GetNonce(), 8) + noncePart, err := formatL2TxHashParam(nonce, 8) if err != nil { return common.Hash{}, err } - gasPricePart, err := formatL2TxHashParam(tx.GetPrice(), 32) + gasPricePart, err := formatL2TxHashParam(gasPrice, 32) if err != nil { return common.Hash{}, err } @@ -260,26 +266,30 @@ func ComputeL2TxHash(tx types.LegacyTx) (common.Hash, error) { hash := fmt.Sprintf("%s%s%s%s", txType, noncePart, gasPricePart, gasLimitPart) // check is deploy - if tx.GetTo() == nil || tx.GetTo().Hex() == "0x" { + if to == nil || to.Hex() == "0x0000000000000000000000000000000000000000" { hash += "01" } else { - toPart, err := formatL2TxHashParam(tx.GetTo().Hex(), 20) + toPart, err := formatL2TxHashParam(to.Hex(), 20) if err != nil { return common.Hash{}, err } hash += fmt.Sprintf("00%s", toPart) } - // add value - valuePart, err := formatL2TxHashParam(tx.GetValue(), 32) + valuePart, err := formatL2TxHashParam(value, 32) if err != nil { return common.Hash{}, err } hash += valuePart // compute data length - data := tx.GetData() - dataLength := len(data) + dataStr := hex.EncodeToHex(data) + if len(dataStr) > 1 && dataStr[:2] == "0x" { + dataStr = dataStr[2:] + } + + //round to ceil + dataLength := (len(dataStr) + 1) / 2 dataLengthPart, err := formatL2TxHashParam(dataLength, 3) if err != nil { return common.Hash{}, err @@ -287,7 +297,7 @@ func ComputeL2TxHash(tx types.LegacyTx) (common.Hash, error) { hash += dataLengthPart if dataLength > 0 { - dataPart, err := formatL2TxHashParam(data, dataLength) + dataPart, err := formatL2TxHashParam(dataStr, dataLength) if err != nil { return common.Hash{}, err } @@ -295,9 +305,8 @@ func ComputeL2TxHash(tx types.LegacyTx) (common.Hash, error) { } // add chainID - cid := tx.ChainID - if cid != nil { - chainIDPart, err := formatL2TxHashParam(cid, 8) + if chainId != nil { + chainIDPart, err := formatL2TxHashParam(chainId, 8) if err != nil { return common.Hash{}, err } @@ -305,11 +314,7 @@ func ComputeL2TxHash(tx types.LegacyTx) (common.Hash, error) { } // add from - sender, found := tx.GetSender() - if !found { - return common.Hash{}, fmt.Errorf("sender not found") - } - fromPart, err := formatL2TxHashParam(sender, 20) + fromPart, err := formatL2TxHashParam(from.Hex(), 20) if err != nil { return common.Hash{}, err } @@ -364,7 +369,7 @@ func formatL2TxHashParam(param interface{}, paramLength int) (string, error) { paramStr = v.Hex() } case []uint8: - paramStr = hex.EncodeToString(v) + paramStr = hex.EncodeToHex(v) case common.Address: paramStr = v.Hex() case string: diff --git a/zk/tx/tx_test.go b/zk/tx/tx_test.go index 61e30617b85..8aee1d0686b 100644 --- a/zk/tx/tx_test.go +++ b/zk/tx/tx_test.go @@ -6,7 +6,10 @@ import ( "math/big" "testing" + zkhex "github.com/ledgerwatch/erigon/zkevm/hex" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon-lib/common" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/core/types" @@ -160,32 +163,128 @@ func createTx(nonce, gasPrice, gasLimit, from, to, value, data string, chainID u return tx } -var testScenarios = map[string]func(t *testing.T){ - "ZeroNonce": func(t *testing.T) { - tx := createTx( - "0x00", - "0x3b9aca00", - "", // fixed using a constant at 30000000 - "0x4d5Cf5032B2a844602278b01199ED191A86c93ff", - "0x1275fbb540c8efc58b812ba83b0d0b8b9917ae98", - "0x00", - "0x188ec356", - 1000) - expectedHash := "0xf3de9c9f50d72933104d5bb109915d93e4958117de78c9a7d1a58b5c6e4cbb77" - actualHash, err := ComputeL2TxHash(tx) +func TestComputeL2TxHashScenarios(t *testing.T) { + tests := []struct { + chainId *big.Int + nonce uint64 + gasPrice *uint256.Int + gasLimit uint64 + value *uint256.Int + data string + to libcommon.Address + from libcommon.Address + expectedTxHash string + }{ + { + chainId: big.NewInt(1000), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 30000000, + value: uint256.NewInt(0), + data: "0x188ec356", + to: libcommon.HexToAddress("0x1275fbb540c8efc58b812ba83b0d0b8b9917ae98"), + from: libcommon.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0xf3de9c9f50d72933104d5bb109915d93e4958117de78c9a7d1a58b5c6e4cbb77", + }, + { + chainId: big.NewInt(1700), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 100000, + value: uint256.NewInt(0), + data: "0x56d5be740000000000000000000000001275fbb540c8efc58b812ba83b0d0b8b9917ae98", + to: libcommon.HexToAddress("0x005Cf5032B2a844602278b01199ED191A86c93ff"), + from: libcommon.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0x42e14eabd58bb4f26e928cada9a74081343e9ca0aad0d4f3f4e6254cb3a805ca", + }, + { + chainId: big.NewInt(1700), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 100000, + value: uint256.NewInt(0), + data: "0x56d5be740000000000000000000000001275fbb540c8efc58b812ba83b0d0b8b9917ae98", + to: common.HexToAddress("0x0"), + from: common.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0x8f9cfb43c0f6bc7ce9f9e43e8761776a2ef9657ccf87318e2487c313d119b8cf", + }, { + chainId: big.NewInt(4096), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 100000, + value: uint256.NewInt(0), + data: "0x56d5be740000000000000000000000001275fbb540c8efc58b812ba83b0d0b8b9917ae98", + to: common.HexToAddress(""), + from: common.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0xe93d9aadf9ec7453204b7f26380472820729cb401e371b473132cc3ea27d2eef", + }, { + chainId: big.NewInt(1700), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 100000, + value: uint256.NewInt(0), + data: "0x", + to: common.HexToAddress(""), + from: common.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0xe8cd2bb2321ae825c970cb1b8ffd3ba6fb28488ca2a8003f9622d07d0cb2b63c", + }, { + chainId: big.NewInt(1700), + nonce: 0, + gasPrice: uint256.NewInt(1000000000), + gasLimit: 100000, + value: uint256.NewInt(0), + data: "0x", + to: common.HexToAddress(""), + from: common.HexToAddress("0x4d5Cf5032B2a844602278b01199ED191A86c93ff"), + expectedTxHash: "0xe8cd2bb2321ae825c970cb1b8ffd3ba6fb28488ca2a8003f9622d07d0cb2b63c", + }, { + chainId: big.NewInt(2442), + nonce: 50534, + gasPrice: uint256.NewInt(105300000), + gasLimit: 30000000, + value: uint256.NewInt(10000000000000), + data: "", + to: common.HexToAddress("0x417a7BA2d8d0060ae6c54fd098590DB854B9C1d5"), + from: common.HexToAddress("0x9AF3049dD15616Fd627A35563B5282bEA5C32E20"), + expectedTxHash: "0x26460f7fa46b88e6a383a496e567ba76cb307ccaa82b64fc739bfeebbef8d747", + }, { + chainId: big.NewInt(2442), + nonce: 50534, + gasPrice: uint256.NewInt(105300000), + gasLimit: 21000, + value: uint256.NewInt(10000000000000), + data: "", + to: common.HexToAddress("0x417a7BA2d8d0060ae6c54fd098590DB854B9C1d5"), + from: common.HexToAddress("0x9af3049dd15616fd627a35563b5282bea5c32e20"), + expectedTxHash: "0x0a3b9eafc5562a432f25398a849fd2296c717e0d9e90189d1c41e7b6ddcaa3dd", + }, + } + + for i, test := range tests { + dataBytes, err := zkhex.DecodeHex(test.data) if err != nil { - t.Fatalf("ComputeL2TxHash returned an error: %v", err) + t.Fatalf("Test %d: unexpected error: %v", i+1, err) } - if actualHash.Hex() != expectedHash { - t.Errorf("Expected hash %s, got %s", expectedHash, actualHash.Hex()) + result, err := ComputeL2TxHash( + test.chainId, + test.value, + test.gasPrice, + test.nonce, + test.gasLimit, + &test.to, + &test.from, + dataBytes, + ) + if err != nil { + t.Fatalf("Test %d: unexpected error: %v", i+1, err) } - }, -} -func TestComputeL2TxHashScenarios(t *testing.T) { - for name, scenario := range testScenarios { - t.Run(name, scenario) + resultString := result.Hex() + if resultString != test.expectedTxHash { + t.Fatalf("Test %d: expected tx hash %s, got %s", i+1, test.expectedTxHash, resultString) + } } + } type testCase struct {