diff --git a/index.html b/index.html index 4a162e6a778b..a3c458a294d0 100755 --- a/index.html +++ b/index.html @@ -351,7 +351,7 @@
-
+

scroll-reth

@@ -369,18 +369,18 @@
-
+7361
-
-474
+
+12722
+
-487
-
+172
-
-48
+
+174
+
-47
-
+

This is an overview of the changes in scroll-reth, a fork of reth.

@@ -390,7 +390,7 @@
-
-
diff --git reth/crates/scroll/chainspec/res/genesis/dev.json scroll-reth/crates/scroll/chainspec/res/genesis/dev.json +
diff --git reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs new file mode 100644 -index 0000000000000000000000000000000000000000..be72d7e96db2883e9a0ad9062182d88ae9d4135a +index 0000000000000000000000000000000000000000..7b3354865f684d7b479356bf7430d4de4885bf31 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/res/genesis/dev.json -@@ -0,0 +1 @@ -+{"nonce":"0x0","timestamp":"0x6490fdd2","extraData":"0x","gasLimit":"0x1c9c380","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","stateRoot":"0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494","alloc":{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":{"balance":"0xD3C21BCECCEDA1000000"},"0x70997970C51812dc3A010C7d01b50e0d17dc79C8":{"balance":"0xD3C21BCECCEDA1000000"},"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC":{"balance":"0xD3C21BCECCEDA1000000"},"0x90F79bf6EB2c4f870365E785982E1f101E93b906":{"balance":"0xD3C21BCECCEDA1000000"},"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65":{"balance":"0xD3C21BCECCEDA1000000"},"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc":{"balance":"0xD3C21BCECCEDA1000000"},"0x976EA74026E726554dB657fA54763abd0C3a0aa9":{"balance":"0xD3C21BCECCEDA1000000"},"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955":{"balance":"0xD3C21BCECCEDA1000000"},"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f":{"balance":"0xD3C21BCECCEDA1000000"},"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720":{"balance":"0xD3C21BCECCEDA1000000"},"0xBcd4042DE499D14e55001CcbB24a551F3b954096":{"balance":"0xD3C21BCECCEDA1000000"},"0x71bE63f3384f5fb98995898A86B02Fb2426c5788":{"balance":"0xD3C21BCECCEDA1000000"},"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a":{"balance":"0xD3C21BCECCEDA1000000"},"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec":{"balance":"0xD3C21BCECCEDA1000000"},"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097":{"balance":"0xD3C21BCECCEDA1000000"},"0xcd3B766CCDd6AE721141F452C550Ca635964ce71":{"balance":"0xD3C21BCECCEDA1000000"},"0x2546BcD3c84621e976D8185a91A922aE77ECEc30":{"balance":"0xD3C21BCECCEDA1000000"},"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E":{"balance":"0xD3C21BCECCEDA1000000"},"0xdD2FD4581271e230360230F9337D5c0430Bf44C0":{"balance":"0xD3C21BCECCEDA1000000"},"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199":{"balance":"0xD3C21BCECCEDA1000000"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+1
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/chainspec/res/genesis/scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json -new file mode 100644 -index 0000000000000000000000000000000000000000..ef6360ac9d90367c5029a8f6858c062a88866cef ---- /dev/null -+++ scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json -@@ -0,0 +1 @@ -+{"config":{"chainId":534352,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiTime":0,"bernoulliBlock":5220340,"curieBlock":7096836,"darwinTime":1724227200,"darwinV2Time":1725264000,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","l1Config":{"l1ChainId":"1","l1MessageQueueAddress":"0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B","scrollChainAddress":"0xa13BAF47339d63B743e7Da8741db5456DAc1E556","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x6524e860","extraData":"0x4c61206573746f6e7465636f206573746173206d616c6665726d6974612e0000d2ACF5d16a983DB0d909d9D761B8337Fabd6cBd10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"10000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0xF9062b8a30e0d7722960e305049FA50b86ba6253":{"balance":"2000000000000000000"},"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC":{"balance":"226156424291633194186662080095093570025917938800079226637565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61038d565b60405161013891906112fa565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611330565b61041f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461135a565b610439565b3480156101bc57600080fd5b5061010f6101cb366004611396565b61045d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b5061018261054e565b34801561020d57600080fd5b5061016161021c366004611330565b61055d565b34801561022d57600080fd5b5061018261023c3660046113af565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b506101826102723660046113af565b61057f565b34801561028357600080fd5b5061028c61059d565b60405161013897969594939291906113ca565b3480156102ab57600080fd5b5061012b610626565b3480156102c057600080fd5b506101616102cf366004611330565b610635565b3480156102e057600080fd5b506101616102ef366004611330565b6106b0565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611460565b6106be565b34801561032857600080fd5b506101826103373660046114d3565b610822565b336103478134610881565b806001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3460405161038291815260200190565b60405180910390a250565b60606003805461039c90611506565b80601f01602080910402602001604051908101604052809291908181526020018280546103c890611506565b80156104155780601f106103ea57610100808354040283529160200191610415565b820191906000526020600020905b8154815290600101906020018083116103f857829003601f168201915b5050505050905090565b60003361042d818585610940565b60019150505b92915050565b600033610447858285610a65565b610452858585610adf565b506001949350505050565b336104688183610c83565b6000816001600160a01b03168360405160006040518083038185875af1925050503d80600081146104b5576040519150601f19603f3d011682016040523d82523d6000602084013e6104ba565b606091505b50509050806105065760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b816001600160a01b03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658460405161054191815260200190565b60405180910390a2505050565b6000610558610db2565b905090565b60003361042d8185856105708383610822565b61057a919061153a565b610940565b6001600160a01b038116600090815260076020526040812054610433565b6000606080828080836105d17f577261707065642045746865720000000000000000000000000000000000000d6005610edd565b6105fc7f31000000000000000000000000000000000000000000000000000000000000016006610edd565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461039c90611506565b600033816106438286610822565b9050838110156106a35760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104fd565b6104528286868403610940565b60003361042d818585610adf565b8342111561070e5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104fd565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861073d8c610f81565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061079882610fa9565b905060006107a882878787610fd6565b9050896001600160a01b0316816001600160a01b03161461080b5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104fd565b6108168a8a8a610940565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006020835110156108695761086283610ffe565b9050610433565b8161087484826115bf565b5060ff9050610433565b90565b6001600160a01b0382166108d75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104fd565b80600260008282546108e9919061153a565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109a25760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104fd565b6001600160a01b038216610a035760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104fd565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a718484610822565b90506000198114610ad95781811015610acc5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104fd565b610ad98484848403610940565b50505050565b6001600160a01b038316610b435760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104fd565b6001600160a01b038216610ba55760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104fd565b6001600160a01b03831660009081526020819052604090205481811015610c1d5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104fd565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610ad9565b6001600160a01b038216610ce35760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104fd565b6001600160a01b03821660009081526020819052604090205481811015610d575760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104fd565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a58565b505050565b6000306001600160a01b037f000000000000000000000000530000000000000000000000000000000000000416148015610e0b57507f000000000000000000000000000000000000000000000000000000000008275046145b15610e3557507fe5b117a3cd7ae7ed3508e6e6c5a0794536b2a8dee12533c4d7524eae9c85438f90565b610558604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ef0576108628361103c565b818054610efc90611506565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2890611506565b8015610f755780601f10610f4a57610100808354040283529160200191610f75565b820191906000526020600020905b815481529060010190602001808311610f5857829003601f168201915b50505050509050610433565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610433610fb6610db2565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fe78787878761107b565b91509150610ff48161113f565b5095945050505050565b600080829050601f81511115611029578260405163305a27a960e01b81526004016104fd91906112fa565b80516110348261167f565b179392505050565b606060006110498361128c565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156110b25750600090506003611136565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611106573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661112f57600060019250925050611136565b9150600090505b94509492505050565b6000816004811115611153576111536116a3565b0361115b5750565b600181600481111561116f5761116f6116a3565b036111bc5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104fd565b60028160048111156111d0576111d06116a3565b0361121d5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104fd565b6003816004811115611231576112316116a3565b036112895760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104fd565b50565b600060ff8216601f81111561043357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112da576020818501810151868301820152016112be565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061130d60208301846112b4565b9392505050565b80356001600160a01b038116811461132b57600080fd5b919050565b6000806040838503121561134357600080fd5b61134c83611314565b946020939093013593505050565b60008060006060848603121561136f57600080fd5b61137884611314565b925061138660208501611314565b9150604084013590509250925092565b6000602082840312156113a857600080fd5b5035919050565b6000602082840312156113c157600080fd5b61130d82611314565b60ff60f81b881681526000602060e0818401526113ea60e084018a6112b4565b83810360408501526113fc818a6112b4565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561144e57835183529284019291840191600101611432565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561147b57600080fd5b61148488611314565b965061149260208901611314565b95506040880135945060608801359350608088013560ff811681146114b657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114e657600080fd5b6114ef83611314565b91506114fd60208401611314565b90509250929050565b600181811c9082168061151a57607f821691505b602082108103610fa357634e487b7160e01b600052602260045260246000fd5b8082018082111561043357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610dad57600081815260208120601f850160051c810160208610156115985750805b601f850160051c820191505b818110156115b7578281556001016115a4565b505050505050565b815167ffffffffffffffff8111156115d9576115d961155b565b6115ed816115e78454611506565b84611571565b602080601f831160018114611622576000841561160a5750858301515b600019600386901b1c1916600185901b1785556115b7565b600085815260208120601f198616915b8281101561165157888601518255948401946001909101908401611632565b508582101561166f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610fa35760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea26469706673582212207f39e33e122e8e2b0381aa6abea46046f56b05ced66c556a06bb1b80be7f55cf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100ab5760003560e01c806384411d651161006457806384411d65146101845780638da5cb5b1461019a5780639e7adc79146101ba578063f2fde38b146101da578063feec756c146101fa578063ff4f35461461021a57600080fd5b80632e1a7d4d146100b75780633cb747bf146100d95780633ccfd60b14610116578063457e1a491461012b57806366d003ac1461014f578063715018a61461016f57600080fd5b366100b257005b600080fd5b3480156100c357600080fd5b506100d76100d2366004610682565b61023a565b005b3480156100e557600080fd5b506002546100f9906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561012257600080fd5b506100d76103ff565b34801561013757600080fd5b5061014160015481565b60405190815260200161010d565b34801561015b57600080fd5b506003546100f9906001600160a01b031681565b34801561017b57600080fd5b506100d761040c565b34801561019057600080fd5b5061014160045481565b3480156101a657600080fd5b506000546100f9906001600160a01b031681565b3480156101c657600080fd5b506100d76101d536600461069b565b610442565b3480156101e657600080fd5b506100d76101f536600461069b565b6104be565b34801561020657600080fd5b506100d761021536600461069b565b610547565b34801561022657600080fd5b506100d7610235366004610682565b6105c3565b6001548110156102ca5760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b478082111561032e5760405162461bcd60e51b815260206004820152602a60248201527f4665655661756c743a20696e73756666696369656e742062616c616e636520746044820152696f20776974686472617760b01b60648201526084016102c1565b6004805483019055600354604080518481526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b9488946103c99491909216928592906004016106cb565b6000604051808303818588803b1580156103e257600080fd5b505af11580156103f6573d6000803e3d6000fd5b50505050505050565b476104098161023a565b50565b6000546001600160a01b031633146104365760405162461bcd60e51b81526004016102c190610737565b6104406000610632565b565b6000546001600160a01b0316331461046c5760405162461bcd60e51b81526004016102c190610737565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b031633146104e85760405162461bcd60e51b81526004016102c190610737565b6001600160a01b03811661053e5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102c1565b61040981610632565b6000546001600160a01b031633146105715760405162461bcd60e51b81526004016102c190610737565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105ed5760405162461bcd60e51b81526004016102c190610737565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561069457600080fd5b5035919050565b6000602082840312156106ad57600080fd5b81356001600160a01b03811681146106c457600080fd5b9392505050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b8181101561070d5786810183015185820160a0015282016106f1565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea2646970667358221220063c6c384f745ebcacfdd13320e5b9a50687aae43ff14566761f56273111b97e64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC","0x0000000000000000000000000000000000000000000000000000000000000003":"0x8FA3b4570B4C96f8036C13b64971BA65867eEB48"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
++++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs +@@ -0,0 +1,336 @@ ++//! Receipt envelope types for Scroll. ++ ++use crate::ScrollTxType; ++use alloy_consensus::{Eip658Value, Receipt, ReceiptWithBloom, TxReceipt}; ++use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}; ++use alloy_primitives::{logs_bloom, Bloom, Log}; ++use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable}; ++ ++/// Receipt envelope, as defined in [EIP-2718], modified for Scroll chains. ++/// ++/// This enum distinguishes between tagged and untagged legacy receipts, as the ++/// in-protocol merkle tree may commit to EITHER 0-prefixed or raw. Therefore ++/// we must ensure that encoding returns the precise byte-array that was ++/// decoded, preserving the presence or absence of the `TransactionType` flag. ++/// ++/// Transaction receipt payloads are specified in their respective EIPs. ++/// ++/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 ++#[derive(Debug, Clone, PartialEq, Eq)] ++#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr(feature = "serde", serde(tag = "type"))] ++#[non_exhaustive] ++pub enum ScrollReceiptEnvelope<T = Log> { ++ /// Receipt envelope with no type flag. ++ #[cfg_attr(feature = "serde", serde(rename = "0x0", alias = "0x00"))] ++ Legacy(ReceiptWithBloom<Receipt<T>>), ++ /// Receipt envelope with type flag 1, containing a [EIP-2930] receipt. ++ /// ++ /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 ++ #[cfg_attr(feature = "serde", serde(rename = "0x1", alias = "0x01"))] ++ Eip2930(ReceiptWithBloom<Receipt<T>>), ++ /// Receipt envelope with type flag 2, containing a [EIP-1559] receipt. ++ /// ++ /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 ++ #[cfg_attr(feature = "serde", serde(rename = "0x2", alias = "0x02"))] ++ Eip1559(ReceiptWithBloom<Receipt<T>>), ++ /// Receipt envelope with type flag 126, containing a [Scroll-L1-Message] receipt. ++ #[cfg_attr(feature = "serde", serde(rename = "0x7e", alias = "0x7E"))] ++ L1Message(ReceiptWithBloom<Receipt<T>>), ++} ++ ++impl ScrollReceiptEnvelope<Log> { ++ /// Creates a new [`ScrollReceiptEnvelope`] from the given parts. ++ pub fn from_parts<'a>( ++ status: bool, ++ cumulative_gas_used: u64, ++ logs: impl IntoIterator<Item = &'a Log>, ++ tx_type: ScrollTxType, ++ ) -> Self { ++ let logs = logs.into_iter().cloned().collect::<Vec<_>>(); ++ let logs_bloom = logs_bloom(&logs); ++ let inner_receipt = ++ Receipt { status: Eip658Value::Eip658(status), cumulative_gas_used, logs }; ++ match tx_type { ++ ScrollTxType::Legacy => { ++ Self::Legacy(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) ++ } ++ ScrollTxType::Eip2930 => { ++ Self::Eip2930(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) ++ } ++ ScrollTxType::Eip1559 => { ++ Self::Eip1559(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) ++ } ++ ScrollTxType::L1Message => { ++ Self::L1Message(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) ++ } ++ } ++ } ++} ++ ++impl<T> ScrollReceiptEnvelope<T> { ++ /// Return the [`ScrollTxType`] of the inner receipt. ++ pub const fn tx_type(&self) -> ScrollTxType { ++ match self { ++ Self::Legacy(_) => ScrollTxType::Legacy, ++ Self::Eip2930(_) => ScrollTxType::Eip2930, ++ Self::Eip1559(_) => ScrollTxType::Eip1559, ++ Self::L1Message(_) => ScrollTxType::L1Message, ++ } ++ } ++ ++ /// Return true if the transaction was successful. ++ pub fn is_success(&self) -> bool { ++ self.status() ++ } ++ ++ /// Returns the success status of the receipt's transaction. ++ pub fn status(&self) -> bool { ++ self.as_receipt().unwrap().status.coerce_status() ++ } ++ ++ /// Returns the cumulative gas used at this receipt. ++ pub fn cumulative_gas_used(&self) -> u64 { ++ self.as_receipt().unwrap().cumulative_gas_used ++ } ++ ++ /// Return the receipt logs. ++ pub fn logs(&self) -> &[T] { ++ &self.as_receipt().unwrap().logs ++ } ++ ++ /// Return the receipt's bloom. ++ pub const fn logs_bloom(&self) -> &Bloom { ++ match self { ++ Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::L1Message(t) => { ++ &t.logs_bloom ++ } ++ } ++ } ++ ++ /// Returns the L1 message receipt if it is a deposit receipt. ++ pub const fn as_l1_message_receipt_with_bloom(&self) -> Option<&ReceiptWithBloom<Receipt<T>>> { ++ match self { ++ Self::L1Message(t) => Some(t), ++ _ => None, ++ } ++ } ++ ++ /// Returns the L1 message receipt if it is a deposit receipt. ++ pub const fn as_l1_message_receipt(&self) -> Option<&Receipt<T>> { ++ match self { ++ Self::L1Message(t) => Some(&t.receipt), ++ _ => None, ++ } ++ } ++ ++ /// Return the inner receipt. Currently this is infallible, however, future ++ /// receipt types may be added. ++ pub const fn as_receipt(&self) -> Option<&Receipt<T>> { ++ match self { ++ Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::L1Message(t) => { ++ Some(&t.receipt) ++ } ++ } ++ } ++} ++ ++impl ScrollReceiptEnvelope { ++ /// Get the length of the inner receipt in the 2718 encoding. ++ pub fn inner_length(&self) -> usize { ++ match self { ++ Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::L1Message(t) => { ++ t.length() ++ } ++ } ++ } ++ ++ /// Calculate the length of the rlp payload of the network encoded receipt. ++ pub fn rlp_payload_length(&self) -> usize { ++ let length = self.inner_length(); ++ match self { ++ Self::Legacy(_) => length, ++ _ => length + 1, ++ } ++ } ++} ++ ++impl<T> TxReceipt for ScrollReceiptEnvelope<T> ++where ++ T: Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync, ++{ ++ type Log = T; ++ ++ fn status_or_post_state(&self) -> Eip658Value { ++ self.as_receipt().unwrap().status ++ } ++ ++ fn status(&self) -> bool { ++ self.as_receipt().unwrap().status.coerce_status() ++ } ++ ++ /// Return the receipt's bloom. ++ fn bloom(&self) -> Bloom { ++ *self.logs_bloom() ++ } ++ ++ fn bloom_cheap(&self) -> Option<Bloom> { ++ Some(self.bloom()) ++ } ++ ++ /// Returns the cumulative gas used at this receipt. ++ fn cumulative_gas_used(&self) -> u64 { ++ self.as_receipt().unwrap().cumulative_gas_used ++ } ++ ++ /// Return the receipt logs. ++ fn logs(&self) -> &[T] { ++ &self.as_receipt().unwrap().logs ++ } ++} ++ ++impl Encodable for ScrollReceiptEnvelope { ++ fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { ++ self.network_encode(out) ++ } ++ ++ fn length(&self) -> usize { ++ let mut payload_length = self.rlp_payload_length(); ++ if !self.is_legacy() { ++ payload_length += length_of_length(payload_length); ++ } ++ payload_length ++ } ++} ++ ++impl Decodable for ScrollReceiptEnvelope { ++ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ Self::network_decode(buf) ++ .map_or_else(|_| Err(alloy_rlp::Error::Custom("Unexpected type")), Ok) ++ } ++} ++ ++impl Encodable2718 for ScrollReceiptEnvelope { ++ fn type_flag(&self) -> Option<u8> { ++ match self { ++ Self::Legacy(_) => None, ++ Self::Eip2930(_) => Some(ScrollTxType::Eip2930 as u8), ++ Self::Eip1559(_) => Some(ScrollTxType::Eip1559 as u8), ++ Self::L1Message(_) => Some(ScrollTxType::L1Message as u8), ++ } ++ } ++ ++ fn encode_2718_len(&self) -> usize { ++ self.inner_length() + !self.is_legacy() as usize ++ } ++ ++ fn encode_2718(&self, out: &mut dyn BufMut) { ++ match self.type_flag() { ++ None => {} ++ Some(ty) => out.put_u8(ty), ++ } ++ match self { ++ Self::L1Message(t) | Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) => { ++ t.encode(out) ++ } ++ } ++ } ++} ++ ++impl Decodable2718 for ScrollReceiptEnvelope { ++ fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { ++ match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { ++ ScrollTxType::Legacy => { ++ Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported") ++ .into()) ++ } ++ ScrollTxType::Eip1559 => Ok(Self::Eip1559(Decodable::decode(buf)?)), ++ ScrollTxType::Eip2930 => Ok(Self::Eip2930(Decodable::decode(buf)?)), ++ ScrollTxType::L1Message => Ok(Self::L1Message(Decodable::decode(buf)?)), ++ } ++ } ++ ++ fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { ++ Ok(Self::Legacy(Decodable::decode(buf)?)) ++ } ++} ++ ++#[cfg(all(test, feature = "arbitrary"))] ++impl<'a, T> arbitrary::Arbitrary<'a> for ScrollReceiptEnvelope<T> ++where ++ T: arbitrary::Arbitrary<'a>, ++{ ++ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { ++ match u.int_in_range(0..=4)? { ++ 0 => Ok(Self::Legacy(ReceiptWithBloom::arbitrary(u)?)), ++ 1 => Ok(Self::Eip2930(ReceiptWithBloom::arbitrary(u)?)), ++ 2 => Ok(Self::Eip1559(ReceiptWithBloom::arbitrary(u)?)), ++ _ => Ok(Self::L1Message(ReceiptWithBloom::arbitrary(u)?)), ++ } ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use alloy_consensus::{Receipt, ReceiptWithBloom}; ++ use alloy_eips::eip2718::Encodable2718; ++ use alloy_primitives::{address, b256, bytes, hex, Log, LogData}; ++ use alloy_rlp::Encodable; ++ ++ #[cfg(not(feature = "std"))] ++ use alloc::vec; ++ ++ // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 ++ #[test] ++ fn encode_legacy_receipt() { ++ let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); ++ ++ let mut data = vec![]; ++ let receipt = ScrollReceiptEnvelope::Legacy(ReceiptWithBloom { ++ receipt: Receipt { ++ status: false.into(), ++ cumulative_gas_used: 0x1, ++ logs: vec![Log { ++ address: address!("0000000000000000000000000000000000000011"), ++ data: LogData::new_unchecked( ++ vec![ ++ b256!( ++ "000000000000000000000000000000000000000000000000000000000000dead" ++ ), ++ b256!( ++ "000000000000000000000000000000000000000000000000000000000000beef" ++ ), ++ ], ++ bytes!("0100ff"), ++ ), ++ }], ++ }, ++ logs_bloom: [0; 256].into(), ++ }); ++ ++ receipt.network_encode(&mut data); ++ ++ // check that the rlp length equals the length of the expected rlp ++ assert_eq!(receipt.length(), expected.len()); ++ assert_eq!(data, expected); ++ } ++ ++ #[test] ++ fn legacy_receipt_from_parts() { ++ let receipt = ScrollReceiptEnvelope::from_parts(true, 100, vec![], ScrollTxType::Legacy); ++ assert!(receipt.status()); ++ assert_eq!(receipt.cumulative_gas_used(), 100); ++ assert_eq!(receipt.logs().len(), 0); ++ assert_eq!(receipt.tx_type(), ScrollTxType::Legacy); ++ } ++ ++ #[test] ++ fn l1_message_receipt_from_parts() { ++ let receipt = ScrollReceiptEnvelope::from_parts(true, 100, vec![], ScrollTxType::L1Message); ++ assert!(receipt.status()); ++ assert_eq!(receipt.cumulative_gas_used(), 100); ++ assert_eq!(receipt.logs().len(), 0); ++ assert_eq!(receipt.tx_type(), ScrollTxType::L1Message); ++ } ++}
-
diff --git reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json +
diff --git reth/crates/scroll/alloy/consensus/src/receipt/mod.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/mod.rs new file mode 100644 -index 0000000000000000000000000000000000000000..64987e5c8c5eed66d6f1927b5f8f0b6d31b18de9 +index 0000000000000000000000000000000000000000..c06b4838314b7c82fa98a4e23231d6c0b00efc98 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json -@@ -0,0 +1 @@ -+{"config":{"chainId":534351,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiBlock":0,"bernoulliBlock":3747132,"curieBlock":4740239,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","enableEIP2718":false,"enableEIP1559":false,"l1Config":{"l1ChainId":"11155111","l1MessageQueueAddress":"0xF0B2293F5D834eAe920c6974D50957A1732de763","scrollChainAddress":"0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x64cfd015","extraData":"0x000000000000000000000000000000000000000000000000000000000000000048C3F81f3D998b6652900e1C3183736C238Fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"8000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x18960EEc21b1878C581937a14c5c3C43008F6b6B":{"balance":"10000000000000000000"},"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d":{"balance":"226156424291633194186662080095093570025917938800079226629565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61037d565b60405161013891906112cf565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611305565b61040f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461132f565b610429565b3480156101bc57600080fd5b5061010f6101cb36600461136b565b61044d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b50610182610523565b34801561020d57600080fd5b5061016161021c366004611305565b610532565b34801561022d57600080fd5b5061018261023c366004611384565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b50610182610272366004611384565b610554565b34801561028357600080fd5b5061028c610572565b604051610138979695949392919061139f565b3480156102ab57600080fd5b5061012b6105fb565b3480156102c057600080fd5b506101616102cf366004611305565b61060a565b3480156102e057600080fd5b506101616102ef366004611305565b610685565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611435565b610693565b34801561032857600080fd5b506101826103373660046114a8565b6107f7565b6103463334610856565b60405134815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2565b60606003805461038c906114db565b80601f01602080910402602001604051908101604052809291908181526020018280546103b8906114db565b80156104055780601f106103da57610100808354040283529160200191610405565b820191906000526020600020905b8154815290600101906020018083116103e857829003601f168201915b5050505050905090565b60003361041d818585610915565b60019150505b92915050565b600033610437858285610a3a565b610442858585610ab4565b506001949350505050565b6104573382610c58565b604051600090339083908381818185875af1925050503d8060008114610499576040519150601f19603f3d011682016040523d82523d6000602084013e61049e565b606091505b50509050806104ea5760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b60405182815233907f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b659060200160405180910390a25050565b600061052d610d87565b905090565b60003361041d81858561054583836107f7565b61054f919061150f565b610915565b6001600160a01b038116600090815260076020526040812054610423565b6000606080828080836105a67f577261707065642045746865720000000000000000000000000000000000000d6005610eb2565b6105d17f31000000000000000000000000000000000000000000000000000000000000016006610eb2565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461038c906114db565b6000338161061882866107f7565b9050838110156106785760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104e1565b6104428286868403610915565b60003361041d818585610ab4565b834211156106e35760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104e1565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886107128c610f56565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061076d82610f7e565b9050600061077d82878787610fab565b9050896001600160a01b0316816001600160a01b0316146107e05760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104e1565b6107eb8a8a8a610915565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600060208351101561083e5761083783610fd3565b9050610423565b816108498482611594565b5060ff9050610423565b90565b6001600160a01b0382166108ac5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104e1565b80600260008282546108be919061150f565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109775760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104e1565b6001600160a01b0382166109d85760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104e1565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a4684846107f7565b90506000198114610aae5781811015610aa15760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104e1565b610aae8484848403610915565b50505050565b6001600160a01b038316610b185760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104e1565b6001600160a01b038216610b7a5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104e1565b6001600160a01b03831660009081526020819052604090205481811015610bf25760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104e1565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610aae565b6001600160a01b038216610cb85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104e1565b6001600160a01b03821660009081526020819052604090205481811015610d2c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104e1565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a2d565b505050565b6000306001600160a01b037f0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa316148015610de057507f000000000000000000000000000000000000000000000000000000000008274f46145b15610e0a57507f624453decb4e78ca99c7630ff9f52222ea6f559f0a6c1bb60b935ef006fa159e90565b61052d604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ec55761083783611011565b818054610ed1906114db565b80601f0160208091040260200160405190810160405280929190818152602001828054610efd906114db565b8015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b50505050509050610423565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610423610f8b610d87565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fbc87878787611050565b91509150610fc981611114565b5095945050505050565b600080829050601f81511115610ffe578260405163305a27a960e01b81526004016104e191906112cf565b805161100982611654565b179392505050565b6060600061101e83611261565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611087575060009050600361110b565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156110db573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166111045760006001925092505061110b565b9150600090505b94509492505050565b600081600481111561112857611128611678565b036111305750565b600181600481111561114457611144611678565b036111915760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104e1565b60028160048111156111a5576111a5611678565b036111f25760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104e1565b600381600481111561120657611206611678565b0361125e5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104e1565b50565b600060ff8216601f81111561042357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112af57602081850181015186830182015201611293565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006112e26020830184611289565b9392505050565b80356001600160a01b038116811461130057600080fd5b919050565b6000806040838503121561131857600080fd5b611321836112e9565b946020939093013593505050565b60008060006060848603121561134457600080fd5b61134d846112e9565b925061135b602085016112e9565b9150604084013590509250925092565b60006020828403121561137d57600080fd5b5035919050565b60006020828403121561139657600080fd5b6112e2826112e9565b60ff60f81b881681526000602060e0818401526113bf60e084018a611289565b83810360408501526113d1818a611289565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561142357835183529284019291840191600101611407565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561145057600080fd5b611459886112e9565b9650611467602089016112e9565b95506040880135945060608801359350608088013560ff8116811461148b57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114bb57600080fd5b6114c4836112e9565b91506114d2602084016112e9565b90509250929050565b600181811c908216806114ef57607f821691505b602082108103610f7857634e487b7160e01b600052602260045260246000fd5b8082018082111561042357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610d8257600081815260208120601f850160051c8101602086101561156d5750805b601f850160051c820191505b8181101561158c57828155600101611579565b505050505050565b815167ffffffffffffffff8111156115ae576115ae611530565b6115c2816115bc84546114db565b84611546565b602080601f8311600181146115f757600084156115df5750858301515b600019600386901b1c1916600185901b17855561158c565b600085815260208120601f198616915b8281101561162657888601518255948401946001909101908401611607565b50858210156116445787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610f785760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122075458b204a41338df799effa8b73c6c1a17e612bc3b3311c0cec123c4da7709964736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100a05760003560e01c806384411d651161006457806384411d65146101595780638da5cb5b1461016f5780639e7adc791461018f578063f2fde38b146101af578063feec756c146101cf578063ff4f3546146101ef57600080fd5b80633cb747bf146100ac5780633ccfd60b146100e9578063457e1a491461010057806366d003ac14610124578063715018a61461014457600080fd5b366100a757005b600080fd5b3480156100b857600080fd5b506002546100cc906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100f557600080fd5b506100fe61020f565b005b34801561010c57600080fd5b5061011660015481565b6040519081526020016100e0565b34801561013057600080fd5b506003546100cc906001600160a01b031681565b34801561015057600080fd5b506100fe610371565b34801561016557600080fd5b5061011660045481565b34801561017b57600080fd5b506000546100cc906001600160a01b031681565b34801561019b57600080fd5b506100fe6101aa3660046105ea565b6103a7565b3480156101bb57600080fd5b506100fe6101ca3660046105ea565b610423565b3480156101db57600080fd5b506100fe6101ea3660046105ea565b6104af565b3480156101fb57600080fd5b506100fe61020a36600461061a565b61052b565b60015447908110156102a15760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b6004805482019055600354604080518381526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b94879461033c949190921692859290600401610633565b6000604051808303818588803b15801561035557600080fd5b505af1158015610369573d6000803e3d6000fd5b505050505050565b6000546001600160a01b0316331461039b5760405162461bcd60e51b81526004016102989061069f565b6103a5600061059a565b565b6000546001600160a01b031633146103d15760405162461bcd60e51b81526004016102989061069f565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b0316331461044d5760405162461bcd60e51b81526004016102989061069f565b6001600160a01b0381166104a35760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610298565b6104ac8161059a565b50565b6000546001600160a01b031633146104d95760405162461bcd60e51b81526004016102989061069f565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105555760405162461bcd60e51b81526004016102989061069f565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156105fc57600080fd5b81356001600160a01b038116811461061357600080fd5b9392505050565b60006020828403121561062c57600080fd5b5035919050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b818110156106755786810183015185820160a001528201610659565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea26469706673582212200c5bec0af207d4c7845829d5330f295a5f16702ab8bde670ae90be68974af0a764736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d","0x0000000000000000000000000000000000000000000000000000000000000003":"0x2351C7aD0c8cFEB25c81301EAC922ab1f1980bbe"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
++++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/mod.rs +@@ -0,0 +1,6 @@ ++mod envelope; ++pub use envelope::ScrollReceiptEnvelope; ++ ++#[allow(clippy::module_inception)] ++mod receipt; ++pub use receipt::{ScrollReceiptWithBloom, ScrollTransactionReceipt};
-
diff --git reth/crates/scroll/chainspec/src/constants.rs scroll-reth/crates/scroll/chainspec/src/constants.rs +
diff --git reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs new file mode 100644 -index 0000000000000000000000000000000000000000..c4d004e2af9c24ed9fad59adfa7dafdaa804d06d +index 0000000000000000000000000000000000000000..5cdd1b3e1134d5a0d3dc6f9e6888eb7d2a441814 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/constants.rs -@@ -0,0 +1,66 @@ -+use crate::genesis::L1Config; -+use alloy_primitives::{address, Address}; ++++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs +@@ -0,0 +1,256 @@ ++//! Transaction receipt types for Scroll. + -+/// The transaction fee recipient on the L2. -+pub const SCROLL_FEE_VAULT_ADDRESS: Address = address!("5300000000000000000000000000000000000005"); ++use alloy_consensus::{ ++ Eip658Value, Receipt, ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt, ++}; ++use alloy_primitives::{Bloom, Log, U256}; ++use alloy_rlp::{Buf, BufMut, Decodable, Encodable, Header}; ++ ++/// Receipt containing result of transaction execution. ++#[derive(Clone, Debug, PartialEq, Eq, Default)] ++#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] ++pub struct ScrollTransactionReceipt<T = Log> { ++ /// The inner receipt type. ++ #[cfg_attr(feature = "serde", serde(flatten))] ++ pub inner: Receipt<T>, ++ /// L1 fee for Scroll transactions. ++ pub l1_fee: U256, ++} + -+/// The L1 message queue address for Scroll mainnet. -+/// <https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B>. -+pub const SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS: Address = -+ address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"); ++impl<T> ScrollTransactionReceipt<T> { ++ /// Returns a new [`ScrollTransactionReceipt`] from the inner receipt and the l1 fee. ++ pub const fn new(inner: Receipt<T>, l1_fee: U256) -> Self { ++ Self { inner, l1_fee } ++ } ++} + -+/// The L1 proxy address for Scroll mainnet. -+/// <https://etherscan.io/address/0xa13BAF47339d63B743e7Da8741db5456DAc1E556>. -+pub const SCROLL_MAINNET_L1_PROXY_ADDRESS: Address = -+ address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"); ++impl ScrollTransactionReceipt { ++ /// Calculates [`Log`]'s bloom filter. This is slow operation and [`ScrollReceiptWithBloom`] ++ /// can be used to cache this value. ++ pub fn bloom_slow(&self) -> Bloom { ++ self.inner.logs.iter().collect() ++ } + -+/// The maximum allowed l1 messages per block for Scroll mainnet. -+pub const SCROLL_MAINNET_MAX_L1_MESSAGES: u64 = 10; ++ /// Calculates the bloom filter for the receipt and returns the [`ScrollReceiptWithBloom`] ++ /// container type. ++ pub fn with_bloom(self) -> ScrollReceiptWithBloom { ++ self.into() ++ } ++} + -+/// The L1 configuration for Scroll mainnet. -+pub const SCROLL_MAINNET_L1_CONFIG: L1Config = L1Config { -+ l1_chain_id: alloy_chains::NamedChain::Mainnet as u64, -+ l1_message_queue_address: SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, -+ scroll_chain_address: SCROLL_MAINNET_L1_PROXY_ADDRESS, -+ num_l1_messages_per_block: SCROLL_MAINNET_MAX_L1_MESSAGES, -+}; ++impl<T: Encodable> ScrollTransactionReceipt<T> { ++ /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. ++ /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. ++ /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> ++ pub fn rlp_encoded_fields_length_with_bloom(&self, bloom: &Bloom) -> usize { ++ self.inner.rlp_encoded_fields_length_with_bloom(bloom) ++ } + -+/// The L1 message queue address for Scroll sepolia. -+/// <https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763>. -+pub const SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS: Address = -+ address!("F0B2293F5D834eAe920c6974D50957A1732de763"); ++ /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. ++ /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. ++ /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> ++ pub fn rlp_encode_fields_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { ++ self.inner.rlp_encode_fields_with_bloom(bloom, out); ++ } + -+/// The L1 proxy address for Scroll sepolia. -+/// <https://sepolia.etherscan.io/address/0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0> -+pub const SCROLL_SEPOLIA_L1_PROXY_ADDRESS: Address = -+ address!("2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"); ++ /// Returns RLP header for this receipt encoding with the given [`Bloom`]. ++ /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. ++ /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> ++ pub fn rlp_header_with_bloom(&self, bloom: &Bloom) -> Header { ++ Header { list: true, payload_length: self.rlp_encoded_fields_length_with_bloom(bloom) } ++ } ++} + -+/// The maximum allowed l1 messages per block for Scroll sepolia. -+pub const SCROLL_SEPOLIA_MAX_L1_MESSAGES: u64 = 10; ++impl<T: Decodable> ScrollTransactionReceipt<T> { ++ /// RLP-decodes receipt's field with a [`Bloom`]. ++ /// ++ /// Does not expect an RLP header. ++ pub fn rlp_decode_fields_with_bloom( ++ buf: &mut &[u8], ++ ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { ++ let ReceiptWithBloom { receipt: inner, logs_bloom } = ++ Receipt::rlp_decode_fields_with_bloom(buf)?; + -+/// The L1 configuration for Scroll sepolia. -+pub const SCROLL_SEPOLIA_L1_CONFIG: L1Config = L1Config { -+ l1_chain_id: alloy_chains::NamedChain::Sepolia as u64, -+ l1_message_queue_address: SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, -+ scroll_chain_address: SCROLL_SEPOLIA_L1_PROXY_ADDRESS, -+ num_l1_messages_per_block: SCROLL_SEPOLIA_MAX_L1_MESSAGES, -+}; ++ Ok(ReceiptWithBloom { logs_bloom, receipt: Self { inner, l1_fee: Default::default() } }) ++ } ++} + -+/// The L1 message queue address for Scroll dev. -+pub const SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS: Address = -+ address!("0000000000000000000000000000000000000000"); ++impl<T> AsRef<Receipt<T>> for ScrollTransactionReceipt<T> { ++ fn as_ref(&self) -> &Receipt<T> { ++ &self.inner ++ } ++} + -+/// The L1 proxy address for Scroll dev. -+pub const SCROLL_DEV_L1_PROXY_ADDRESS: Address = -+ address!("0000000000000000000000000000000000000000"); ++impl<T> TxReceipt for ScrollTransactionReceipt<T> ++where ++ T: AsRef<Log> + Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync, ++{ ++ type Log = T; + -+/// The maximum allowed l1 messages per block for Scroll dev. -+pub const SCROLL_DEV_MAX_L1_MESSAGES: u64 = 10; ++ fn status_or_post_state(&self) -> Eip658Value { ++ self.inner.status_or_post_state() ++ } + -+/// The L1 configuration for Scroll dev. -+pub const SCROLL_DEV_L1_CONFIG: L1Config = L1Config { -+ l1_chain_id: alloy_chains::NamedChain::Goerli as u64, -+ l1_message_queue_address: SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, -+ scroll_chain_address: SCROLL_DEV_L1_PROXY_ADDRESS, -+ num_l1_messages_per_block: SCROLL_DEV_MAX_L1_MESSAGES, -+};
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+32
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/chainspec/src/dev.rs scroll-reth/crates/scroll/chainspec/src/dev.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..bba6bfdb0aac38d07ee974f572b1a520b462d760 ---- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/dev.rs -@@ -0,0 +1,32 @@ -+//! Chain specification in dev mode for custom chain. ++ fn status(&self) -> bool { ++ self.inner.status() ++ } + -+use alloc::sync::Arc; ++ fn bloom(&self) -> Bloom { ++ self.inner.bloom_slow() ++ } + -+use alloy_chains::Chain; -+use alloy_consensus::constants::DEV_GENESIS_HASH; -+use alloy_primitives::U256; -+use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; -+use reth_scroll_forks::DEV_HARDFORKS; ++ fn cumulative_gas_used(&self) -> u64 { ++ self.inner.cumulative_gas_used() ++ } + -+use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; ++ fn logs(&self) -> &[Self::Log] { ++ self.inner.logs() ++ } ++} + -+/// Scroll dev testnet specification ++impl<T: Encodable> RlpEncodableReceipt for ScrollTransactionReceipt<T> { ++ fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { ++ self.rlp_header_with_bloom(bloom).length_with_payload() ++ } ++ ++ fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { ++ self.rlp_header_with_bloom(bloom).encode(out); ++ self.rlp_encode_fields_with_bloom(bloom, out); ++ } ++} ++ ++impl<T: Decodable> RlpDecodableReceipt for ScrollTransactionReceipt<T> { ++ fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { ++ let header = Header::decode(buf)?; ++ if !header.list { ++ return Err(alloy_rlp::Error::UnexpectedString); ++ } ++ ++ if buf.len() < header.payload_length { ++ return Err(alloy_rlp::Error::InputTooShort); ++ } ++ ++ // Note: we pass a separate buffer to `rlp_decode_fields_with_bloom` to allow it decode ++ // optional fields based on the remaining length. ++ let mut fields_buf = &buf[..header.payload_length]; ++ let this = Self::rlp_decode_fields_with_bloom(&mut fields_buf)?; ++ ++ if !fields_buf.is_empty() { ++ return Err(alloy_rlp::Error::UnexpectedLength); ++ } ++ ++ buf.advance(header.payload_length); ++ ++ Ok(this) ++ } ++} ++ ++/// [`ScrollTransactionReceipt`] with calculated bloom filter, modified for Scroll. +/// -+/// Includes 20 prefunded accounts with `10_000` ETH each derived from mnemonic "test test test test -+/// test test test test test test test junk". -+pub static SCROLL_DEV: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { -+ ScrollChainSpec { -+ inner: ChainSpec { -+ chain: Chain::dev(), -+ genesis: serde_json::from_str(include_str!("../res/genesis/dev.json")) -+ .expect("Can't deserialize Dev testnet genesis json"), -+ genesis_hash: once_cell_set(DEV_GENESIS_HASH), -+ paris_block_and_final_difficulty: Some((0, U256::from(0))), -+ hardforks: DEV_HARDFORKS.clone(), -+ base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), -+ ..Default::default() -+ }, -+ config: ScrollChainConfig::dev(), ++/// This convenience type allows us to lazily calculate the bloom filter for a ++/// receipt, similar to [`Sealed`]. ++/// ++/// [`Sealed`]: alloy_consensus::Sealed ++pub type ScrollReceiptWithBloom<T = Log> = ReceiptWithBloom<ScrollTransactionReceipt<T>>; ++ ++#[cfg(any(test, feature = "arbitrary"))] ++impl<'a, T> arbitrary::Arbitrary<'a> for ScrollTransactionReceipt<T> ++where ++ T: arbitrary::Arbitrary<'a>, ++{ ++ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { ++ #[cfg(not(feature = "std"))] ++ use alloc::vec::Vec; ++ Ok(Self { ++ inner: Receipt { ++ status: Eip658Value::arbitrary(u)?, ++ cumulative_gas_used: u64::arbitrary(u)?, ++ logs: Vec::<T>::arbitrary(u)?, ++ }, ++ l1_fee: U256::arbitrary(u)?, ++ }) + } -+ .into() -+});
++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use alloy_consensus::Receipt; ++ use alloy_primitives::{address, b256, bytes, hex, Bytes, Log, LogData}; ++ use alloy_rlp::{Decodable, Encodable}; ++ ++ #[cfg(not(feature = "std"))] ++ use alloc::{vec, vec::Vec}; ++ ++ // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 ++ #[test] ++ fn decode_legacy_receipt() { ++ let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); ++ ++ // EIP658Receipt ++ let expected = ++ ScrollReceiptWithBloom { ++ receipt: ScrollTransactionReceipt { ++ inner: Receipt { ++ status: false.into(), ++ cumulative_gas_used: 0x1, ++ logs: vec![Log { ++ address: address!("0000000000000000000000000000000000000011"), ++ data: LogData::new_unchecked( ++ vec![ ++ b256!("000000000000000000000000000000000000000000000000000000000000dead"), ++ b256!("000000000000000000000000000000000000000000000000000000000000beef"), ++ ], ++ bytes!("0100ff"), ++ ), ++ }], ++ }, ++ l1_fee: U256::ZERO ++ }, ++ logs_bloom: [0; 256].into(), ++ }; ++ ++ let receipt = ScrollReceiptWithBloom::decode(&mut &data[..]).unwrap(); ++ assert_eq!(receipt, expected); ++ } ++ ++ #[test] ++ fn gigantic_receipt() { ++ let receipt = ScrollTransactionReceipt { ++ inner: Receipt { ++ cumulative_gas_used: 16747627, ++ status: true.into(), ++ logs: vec![ ++ Log { ++ address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"), ++ data: LogData::new_unchecked( ++ vec![b256!( ++ "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9" ++ )], ++ Bytes::from(vec![1; 0xffffff]), ++ ), ++ }, ++ Log { ++ address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"), ++ data: LogData::new_unchecked( ++ vec![b256!( ++ "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2" ++ )], ++ Bytes::from(vec![1; 0xffffff]), ++ ), ++ }, ++ ], ++ }, ++ l1_fee: U256::ZERO, ++ } ++ .with_bloom(); ++ ++ let mut data = vec![]; ++ ++ receipt.encode(&mut data); ++ let decoded = ScrollReceiptWithBloom::decode(&mut &data[..]).unwrap(); ++ ++ assert_eq!(decoded, receipt); ++ } ++}
@@ -1078,242 +1463,575 @@
-
+225
+
+558
-0
-
diff --git reth/crates/scroll/chainspec/src/genesis.rs scroll-reth/crates/scroll/chainspec/src/genesis.rs +
diff --git reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs new file mode 100644 -index 0000000000000000000000000000000000000000..9bd1a0c654a01f43fe2278a498d5e20d96ea160b +index 0000000000000000000000000000000000000000..9c5f88f779b3568a7294c5b89d2aaa61a82e36a3 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/genesis.rs -@@ -0,0 +1,225 @@ -+//! Scroll types for genesis data. -+ -+use crate::{ -+ constants::{SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, SCROLL_SEPOLIA_L1_CONFIG}, -+ SCROLL_DEV_L1_CONFIG, ++++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs +@@ -0,0 +1,558 @@ ++use alloy_consensus::{ ++ transaction::RlpEcdsaTx, Sealable, Sealed, Signed, Transaction, TxEip1559, TxEip2930, TxLegacy, ++ Typed2718, +}; -+use alloy_primitives::Address; -+use alloy_serde::OtherFields; -+use serde::de::Error; ++use alloy_eips::{ ++ eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, ++ eip2930::AccessList, ++ eip7702::SignedAuthorization, ++}; ++use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; ++use alloy_rlp::{Decodable, Encodable}; + -+/// Container type for all Scroll-specific fields in a genesis file. -+/// This struct represents the configuration details and metadata -+/// that are specific to the Scroll blockchain, used during the chain's initialization. -+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -+#[serde(rename_all = "camelCase")] -+pub struct ScrollChainInfo { -+ /// Information about hard forks specific to the Scroll chain. -+ /// This optional field contains metadata about various hard fork -+ /// configurations that are specific to the Scroll blockchain. -+ pub hard_fork_info: Option<ScrollHardforkInfo>, -+ /// Scroll chain-specific configuration details. -+ /// Encapsulates special parameters and settings -+ /// required for Scroll chain functionality, such as fee-related -+ /// addresses and Layer 1 configuration. -+ pub scroll_chain_config: ScrollChainConfig, -+} ++use crate::{ScrollTxType, TxL1Message}; + -+impl ScrollChainInfo { -+ /// Extracts the Scroll specific fields from a genesis file. These fields are expected to be -+ /// contained in the `genesis.config` under `extra_fields` property. -+ pub fn extract_from(others: &OtherFields) -> Option<Self> { -+ Self::try_from(others).ok() -+ } ++/// The Ethereum [EIP-2718] Transaction Envelope, modified for Scroll chains. ++/// ++/// # Note: ++/// ++/// This enum distinguishes between tagged and untagged legacy transactions, as ++/// the in-protocol merkle tree may commit to EITHER 0-prefixed or raw. ++/// Therefore we must ensure that encoding returns the precise byte-array that ++/// was decoded, preserving the presence or absence of the `TransactionType` ++/// flag. ++/// ++/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 ++#[derive(Debug, Clone, PartialEq, Eq)] ++#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr( ++ feature = "serde", ++ serde(into = "serde_from::TaggedTxEnvelope", from = "serde_from::MaybeTaggedTxEnvelope") ++)] ++#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))] ++#[non_exhaustive] ++pub enum ScrollTxEnvelope { ++ /// An untagged [`TxLegacy`]. ++ Legacy(Signed<TxLegacy>), ++ /// A [`TxEip2930`] tagged with type 1. ++ Eip2930(Signed<TxEip2930>), ++ /// A [`TxEip1559`] tagged with type 2. ++ Eip1559(Signed<TxEip1559>), ++ /// A [`TxL1Message`] tagged with type 0x7E. ++ L1Message(Sealed<TxL1Message>), +} + -+impl TryFrom<&OtherFields> for ScrollChainInfo { -+ type Error = serde_json::Error; -+ -+ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { -+ let hard_fork_info = ScrollHardforkInfo::try_from(others).ok(); -+ let scroll_chain_config = ScrollChainConfig::try_from(others)?; -+ -+ Ok(Self { hard_fork_info, scroll_chain_config }) ++impl From<Signed<TxLegacy>> for ScrollTxEnvelope { ++ fn from(v: Signed<TxLegacy>) -> Self { ++ Self::Legacy(v) + } +} + -+/// [`ScrollHardforkInfo`] specifies the block numbers and timestamps at which the Scroll hardforks -+/// were activated. -+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -+#[serde(rename_all = "camelCase")] -+pub struct ScrollHardforkInfo { -+ /// archimedes block number -+ pub archimedes_block: Option<u64>, -+ /// bernoulli block number -+ pub bernoulli_block: Option<u64>, -+ /// curie block number -+ pub curie_block: Option<u64>, -+ /// darwin hardfork timestamp -+ pub darwin_time: Option<u64>, -+ /// darwinV2 hardfork timestamp -+ pub darwin_v2_time: Option<u64>, ++impl From<Signed<TxEip2930>> for ScrollTxEnvelope { ++ fn from(v: Signed<TxEip2930>) -> Self { ++ Self::Eip2930(v) ++ } +} + -+impl ScrollHardforkInfo { -+ /// Extract the Scroll-specific genesis info from a genesis file. -+ pub fn extract_from(others: &OtherFields) -> Option<Self> { -+ Self::try_from(others).ok() ++impl From<Signed<TxEip1559>> for ScrollTxEnvelope { ++ fn from(v: Signed<TxEip1559>) -> Self { ++ Self::Eip1559(v) + } +} + -+impl TryFrom<&OtherFields> for ScrollHardforkInfo { -+ type Error = serde_json::Error; -+ -+ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { -+ others.deserialize_as() ++impl From<TxL1Message> for ScrollTxEnvelope { ++ fn from(v: TxL1Message) -> Self { ++ v.seal_slow().into() + } +} + -+/// The Scroll l1 config -+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -+#[serde(rename_all = "camelCase")] -+pub struct L1Config { -+ /// l1 chain id -+ pub l1_chain_id: u64, -+ /// The L1 contract address of the contract that handles the message queue targeting the Scroll -+ /// rollup. -+ pub l1_message_queue_address: Address, -+ /// The L1 contract address of the proxy contract which is responsible for Scroll rollup -+ /// settlement. -+ pub scroll_chain_address: Address, -+ /// The maximum number of L1 messages to be consumed per L2 rollup block. -+ pub num_l1_messages_per_block: u64, ++impl From<Sealed<TxL1Message>> for ScrollTxEnvelope { ++ fn from(v: Sealed<TxL1Message>) -> Self { ++ Self::L1Message(v) ++ } +} + -+/// The configuration for the Scroll sequencer chain. -+/// This struct holds the configuration details specific to the Scroll chain, -+/// including fee-related addresses and L1 chain-specific settings. -+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -+#[serde(rename_all = "camelCase")] -+pub struct ScrollChainConfig { -+ /// The address of the L2 transaction fee vault. -+ /// This is an optional field that, when set, specifies where L2 transaction fees -+ /// will be sent or stored. -+ pub fee_vault_address: Option<Address>, -+ /// The L1 configuration. -+ /// This field encapsulates specific settings and parameters required for L1 -+ pub l1_config: L1Config, ++impl Typed2718 for ScrollTxEnvelope { ++ fn ty(&self) -> u8 { ++ match self { ++ Self::Legacy(tx) => tx.tx().ty(), ++ Self::Eip2930(tx) => tx.tx().ty(), ++ Self::Eip1559(tx) => tx.tx().ty(), ++ Self::L1Message(tx) => tx.ty(), ++ } ++ } +} + -+impl ScrollChainConfig { -+ /// Extracts the scroll special info by looking for the `scroll` key. It is intended to be -+ /// parsed from a genesis file. -+ pub fn extract_from(others: &OtherFields) -> Option<Self> { -+ Self::try_from(others).ok() ++impl Transaction for ScrollTxEnvelope { ++ fn chain_id(&self) -> Option<u64> { ++ match self { ++ Self::Legacy(tx) => tx.tx().chain_id(), ++ Self::Eip2930(tx) => tx.tx().chain_id(), ++ Self::Eip1559(tx) => tx.tx().chain_id(), ++ Self::L1Message(tx) => tx.chain_id(), ++ } + } + -+ /// Returns the [`ScrollChainConfig`] for Scroll Mainnet. -+ pub const fn mainnet() -> Self { -+ Self { -+ fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), -+ l1_config: SCROLL_MAINNET_L1_CONFIG, ++ fn nonce(&self) -> u64 { ++ match self { ++ Self::Legacy(tx) => tx.tx().nonce(), ++ Self::Eip2930(tx) => tx.tx().nonce(), ++ Self::Eip1559(tx) => tx.tx().nonce(), ++ Self::L1Message(tx) => tx.nonce(), + } + } + -+ /// Returns the [`ScrollChainConfig`] for Scroll Sepolia. -+ pub const fn sepolia() -> Self { -+ Self { -+ fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), -+ l1_config: SCROLL_SEPOLIA_L1_CONFIG, ++ fn gas_limit(&self) -> u64 { ++ match self { ++ Self::Legacy(tx) => tx.tx().gas_limit(), ++ Self::Eip2930(tx) => tx.tx().gas_limit(), ++ Self::Eip1559(tx) => tx.tx().gas_limit(), ++ Self::L1Message(tx) => tx.gas_limit(), + } + } + -+ /// Returns the [`ScrollChainConfig`] for Scroll dev. -+ pub const fn dev() -> Self { -+ Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), l1_config: SCROLL_DEV_L1_CONFIG } ++ fn gas_price(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.tx().gas_price(), ++ Self::Eip2930(tx) => tx.tx().gas_price(), ++ Self::Eip1559(tx) => tx.tx().gas_price(), ++ Self::L1Message(tx) => tx.gas_price(), ++ } + } -+} + -+impl TryFrom<&OtherFields> for ScrollChainConfig { -+ type Error = serde_json::Error; ++ fn max_fee_per_gas(&self) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.tx().max_fee_per_gas(), ++ Self::Eip2930(tx) => tx.tx().max_fee_per_gas(), ++ Self::Eip1559(tx) => tx.tx().max_fee_per_gas(), ++ Self::L1Message(tx) => tx.max_fee_per_gas(), ++ } ++ } + -+ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { -+ if let Some(Ok(scroll_chain_config)) = others.get_deserialized::<Self>("scroll") { -+ Ok(scroll_chain_config) -+ } else { -+ Err(serde_json::Error::missing_field("scroll")) ++ fn max_priority_fee_per_gas(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(), ++ Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(), ++ Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(), ++ Self::L1Message(tx) => tx.max_priority_fee_per_gas(), + } + } -+} + -+#[cfg(test)] -+mod tests { -+ use super::*; -+ use alloy_primitives::address; ++ fn max_fee_per_blob_gas(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.tx().max_fee_per_blob_gas(), ++ Self::Eip2930(tx) => tx.tx().max_fee_per_blob_gas(), ++ Self::Eip1559(tx) => tx.tx().max_fee_per_blob_gas(), ++ Self::L1Message(tx) => tx.max_fee_per_blob_gas(), ++ } ++ } + -+ #[test] -+ fn test_extract_scroll_genesis_info() { -+ let genesis_info = r#" -+ { -+ "archimedesBlock": 0, -+ "bernoulliBlock": 10, -+ "curieBlock": 12, -+ "darwinTime": 0 ++ fn priority_fee_or_price(&self) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.tx().priority_fee_or_price(), ++ Self::Eip2930(tx) => tx.tx().priority_fee_or_price(), ++ Self::Eip1559(tx) => tx.tx().priority_fee_or_price(), ++ Self::L1Message(tx) => tx.priority_fee_or_price(), + } -+ "#; ++ } + -+ let others: OtherFields = serde_json::from_str(genesis_info).unwrap(); -+ let genesis_info = ScrollHardforkInfo::extract_from(&others).unwrap(); ++ fn to(&self) -> Option<Address> { ++ match self { ++ Self::Legacy(tx) => tx.tx().to(), ++ Self::Eip2930(tx) => tx.tx().to(), ++ Self::Eip1559(tx) => tx.tx().to(), ++ Self::L1Message(tx) => tx.to(), ++ } ++ } + -+ assert_eq!( -+ genesis_info, -+ ScrollHardforkInfo { -+ archimedes_block: Some(0), -+ bernoulli_block: Some(10), -+ curie_block: Some(12), -+ darwin_time: Some(0), -+ darwin_v2_time: None, ++ fn kind(&self) -> TxKind { ++ match self { ++ Self::Legacy(tx) => tx.tx().kind(), ++ Self::Eip2930(tx) => tx.tx().kind(), ++ Self::Eip1559(tx) => tx.tx().kind(), ++ Self::L1Message(tx) => tx.kind(), ++ } ++ } ++ ++ fn value(&self) -> U256 { ++ match self { ++ Self::Legacy(tx) => tx.tx().value(), ++ Self::Eip2930(tx) => tx.tx().value(), ++ Self::Eip1559(tx) => tx.tx().value(), ++ Self::L1Message(tx) => tx.value(), ++ } ++ } ++ ++ fn input(&self) -> &Bytes { ++ match self { ++ Self::Legacy(tx) => tx.tx().input(), ++ Self::Eip2930(tx) => tx.tx().input(), ++ Self::Eip1559(tx) => tx.tx().input(), ++ Self::L1Message(tx) => tx.input(), ++ } ++ } ++ ++ fn access_list(&self) -> Option<&AccessList> { ++ match self { ++ Self::Legacy(tx) => tx.tx().access_list(), ++ Self::Eip2930(tx) => tx.tx().access_list(), ++ Self::Eip1559(tx) => tx.tx().access_list(), ++ Self::L1Message(tx) => tx.access_list(), ++ } ++ } ++ ++ fn blob_versioned_hashes(&self) -> Option<&[B256]> { ++ match self { ++ Self::Legacy(tx) => tx.tx().blob_versioned_hashes(), ++ Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(), ++ Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(), ++ Self::L1Message(tx) => tx.blob_versioned_hashes(), ++ } ++ } ++ ++ fn authorization_list(&self) -> Option<&[SignedAuthorization]> { ++ match self { ++ Self::Legacy(tx) => tx.tx().authorization_list(), ++ Self::Eip2930(tx) => tx.tx().authorization_list(), ++ Self::Eip1559(tx) => tx.tx().authorization_list(), ++ Self::L1Message(tx) => tx.authorization_list(), ++ } ++ } ++ ++ fn is_dynamic_fee(&self) -> bool { ++ match self { ++ Self::Legacy(tx) => tx.tx().is_dynamic_fee(), ++ Self::Eip2930(tx) => tx.tx().is_dynamic_fee(), ++ Self::Eip1559(tx) => tx.tx().is_dynamic_fee(), ++ Self::L1Message(tx) => tx.is_dynamic_fee(), ++ } ++ } ++ ++ fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.tx().effective_gas_price(base_fee), ++ Self::Eip2930(tx) => tx.tx().effective_gas_price(base_fee), ++ Self::Eip1559(tx) => tx.tx().effective_gas_price(base_fee), ++ Self::L1Message(tx) => tx.effective_gas_price(base_fee), ++ } ++ } ++ ++ fn is_create(&self) -> bool { ++ match self { ++ Self::Legacy(tx) => tx.tx().is_create(), ++ Self::Eip2930(tx) => tx.tx().is_create(), ++ Self::Eip1559(tx) => tx.tx().is_create(), ++ Self::L1Message(tx) => tx.is_create(), ++ } ++ } ++} ++ ++impl ScrollTxEnvelope { ++ /// Returns true if the transaction is a legacy transaction. ++ #[inline] ++ pub const fn is_legacy(&self) -> bool { ++ matches!(self, Self::Legacy(_)) ++ } ++ ++ /// Returns true if the transaction is an EIP-2930 transaction. ++ #[inline] ++ pub const fn is_eip2930(&self) -> bool { ++ matches!(self, Self::Eip2930(_)) ++ } ++ ++ /// Returns true if the transaction is an EIP-1559 transaction. ++ #[inline] ++ pub const fn is_eip1559(&self) -> bool { ++ matches!(self, Self::Eip1559(_)) ++ } ++ ++ /// Returns true if the transaction is a deposit transaction. ++ #[inline] ++ pub const fn is_l1_message(&self) -> bool { ++ matches!(self, Self::L1Message(_)) ++ } ++ ++ /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. ++ pub const fn as_legacy(&self) -> Option<&Signed<TxLegacy>> { ++ match self { ++ Self::Legacy(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. ++ pub const fn as_eip2930(&self) -> Option<&Signed<TxEip2930>> { ++ match self { ++ Self::Eip2930(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. ++ pub const fn as_eip1559(&self) -> Option<&Signed<TxEip1559>> { ++ match self { ++ Self::Eip1559(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Returns the [`TxL1Message`] variant if the transaction is a deposit transaction. ++ pub const fn as_l1_message(&self) -> Option<&Sealed<TxL1Message>> { ++ match self { ++ Self::L1Message(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Return the [`ScrollTxType`] of the inner txn. ++ pub const fn tx_type(&self) -> ScrollTxType { ++ match self { ++ Self::Legacy(_) => ScrollTxType::Legacy, ++ Self::Eip2930(_) => ScrollTxType::Eip2930, ++ Self::Eip1559(_) => ScrollTxType::Eip1559, ++ Self::L1Message(_) => ScrollTxType::L1Message, ++ } ++ } ++ ++ /// Return the length of the inner txn, including type byte length ++ pub fn eip2718_encoded_length(&self) -> usize { ++ match self { ++ Self::Legacy(t) => t.eip2718_encoded_length(), ++ Self::Eip2930(t) => t.eip2718_encoded_length(), ++ Self::Eip1559(t) => t.eip2718_encoded_length(), ++ Self::L1Message(t) => t.eip2718_encoded_length(), ++ } ++ } ++} ++ ++impl Encodable for ScrollTxEnvelope { ++ fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { ++ self.network_encode(out) ++ } ++ ++ fn length(&self) -> usize { ++ self.network_len() ++ } ++} ++ ++impl Decodable for ScrollTxEnvelope { ++ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ Ok(Self::network_decode(buf)?) ++ } ++} ++ ++impl Decodable2718 for ScrollTxEnvelope { ++ fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { ++ match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { ++ ScrollTxType::Eip2930 => Ok(Self::Eip2930(TxEip2930::rlp_decode_signed(buf)?)), ++ ScrollTxType::Eip1559 => Ok(Self::Eip1559(TxEip1559::rlp_decode_signed(buf)?)), ++ ScrollTxType::L1Message => Ok(Self::L1Message(TxL1Message::decode(buf)?.seal_slow())), ++ ScrollTxType::Legacy => { ++ Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported") ++ .into()) + } -+ ); ++ } + } + -+ #[test] -+ fn test_extract_scroll_chain_info() { -+ let chain_info_str = r#" -+ { -+ "archimedesBlock": 0, -+ "bernoulliBlock": 10, -+ "curieBlock": 12, -+ "darwinTime": 0, -+ "scroll": { -+ "feeVaultAddress": "0x5300000000000000000000000000000000000005", -+ "l1Config": { -+ "l1ChainId": 1, -+ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", -+ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", -+ "numL1MessagesPerBlock": 10 ++ fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { ++ Ok(Self::Legacy(TxLegacy::rlp_decode_signed(buf)?)) ++ } ++} ++ ++impl Encodable2718 for ScrollTxEnvelope { ++ fn type_flag(&self) -> Option<u8> { ++ match self { ++ Self::Legacy(_) => None, ++ Self::Eip2930(_) => Some(ScrollTxType::Eip2930 as u8), ++ Self::Eip1559(_) => Some(ScrollTxType::Eip1559 as u8), ++ Self::L1Message(_) => Some(ScrollTxType::L1Message as u8), ++ } ++ } ++ ++ fn encode_2718_len(&self) -> usize { ++ self.eip2718_encoded_length() ++ } ++ ++ fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { ++ match self { ++ // Legacy transactions have no difference between network and 2718 ++ Self::Legacy(tx) => tx.eip2718_encode(out), ++ Self::Eip2930(tx) => { ++ tx.eip2718_encode(out); ++ } ++ Self::Eip1559(tx) => { ++ tx.eip2718_encode(out); ++ } ++ Self::L1Message(tx) => { ++ tx.eip2718_encode(out); + } -+ } + } -+ "#; ++ } + -+ let others: OtherFields = serde_json::from_str(chain_info_str).unwrap(); -+ let chain_info = ScrollChainInfo::extract_from(&others).unwrap(); ++ fn trie_hash(&self) -> B256 { ++ match self { ++ Self::Legacy(tx) => *tx.hash(), ++ Self::Eip1559(tx) => *tx.hash(), ++ Self::Eip2930(tx) => *tx.hash(), ++ Self::L1Message(tx) => tx.seal(), ++ } ++ } ++} + -+ let expected = ScrollChainInfo { -+ hard_fork_info: Some(ScrollHardforkInfo { -+ archimedes_block: Some(0), -+ bernoulli_block: Some(10), -+ curie_block: Some(12), -+ darwin_time: Some(0), -+ darwin_v2_time: None, -+ }), -+ scroll_chain_config: ScrollChainConfig { -+ fee_vault_address: Some(address!("5300000000000000000000000000000000000005")), -+ l1_config: L1Config { -+ l1_chain_id: 1, -+ l1_message_queue_address: address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"), -+ scroll_chain_address: address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"), -+ num_l1_messages_per_block: 10, -+ }, -+ }, ++#[cfg(feature = "serde")] ++mod serde_from { ++ //! NB: Why do we need this? ++ //! ++ //! Because the tag may be missing, we need an abstraction over tagged (with ++ //! type) and untagged (always legacy). This is [`MaybeTaggedTxEnvelope`]. ++ //! ++ //! The tagged variant is [`TaggedTxEnvelope`], which always has a type tag. ++ //! ++ //! We serialize via [`TaggedTxEnvelope`] and deserialize via ++ //! [`MaybeTaggedTxEnvelope`]. ++ use super::*; ++ ++ #[derive(Debug, serde::Deserialize)] ++ #[serde(untagged)] ++ pub(crate) enum MaybeTaggedTxEnvelope { ++ Tagged(TaggedTxEnvelope), ++ #[serde(with = "alloy_consensus::transaction::signed_legacy_serde")] ++ Untagged(Signed<TxLegacy>), ++ } ++ ++ #[derive(Debug, serde::Serialize, serde::Deserialize)] ++ #[serde(tag = "type")] ++ pub(crate) enum TaggedTxEnvelope { ++ #[serde( ++ rename = "0x0", ++ alias = "0x00", ++ with = "alloy_consensus::transaction::signed_legacy_serde" ++ )] ++ Legacy(Signed<TxLegacy>), ++ #[serde(rename = "0x1", alias = "0x01")] ++ Eip2930(Signed<TxEip2930>), ++ #[serde(rename = "0x2", alias = "0x02")] ++ Eip1559(Signed<TxEip1559>), ++ #[serde( ++ rename = "0x7e", ++ alias = "0x7E", ++ serialize_with = "crate::serde_l1_message_tx_rpc" ++ )] ++ L1Message(Sealed<TxL1Message>), ++ } ++ ++ impl From<MaybeTaggedTxEnvelope> for ScrollTxEnvelope { ++ fn from(value: MaybeTaggedTxEnvelope) -> Self { ++ match value { ++ MaybeTaggedTxEnvelope::Tagged(tagged) => tagged.into(), ++ MaybeTaggedTxEnvelope::Untagged(tx) => Self::Legacy(tx), ++ } ++ } ++ } ++ ++ impl From<TaggedTxEnvelope> for ScrollTxEnvelope { ++ fn from(value: TaggedTxEnvelope) -> Self { ++ match value { ++ TaggedTxEnvelope::Legacy(signed) => Self::Legacy(signed), ++ TaggedTxEnvelope::Eip2930(signed) => Self::Eip2930(signed), ++ TaggedTxEnvelope::Eip1559(signed) => Self::Eip1559(signed), ++ TaggedTxEnvelope::L1Message(tx) => Self::L1Message(tx), ++ } ++ } ++ } ++ ++ impl From<ScrollTxEnvelope> for TaggedTxEnvelope { ++ fn from(value: ScrollTxEnvelope) -> Self { ++ match value { ++ ScrollTxEnvelope::Legacy(signed) => Self::Legacy(signed), ++ ScrollTxEnvelope::Eip2930(signed) => Self::Eip2930(signed), ++ ScrollTxEnvelope::Eip1559(signed) => Self::Eip1559(signed), ++ ScrollTxEnvelope::L1Message(tx) => Self::L1Message(tx), ++ } ++ } ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ extern crate alloc; ++ use super::*; ++ use alloc::vec; ++ use alloy_primitives::{hex, Address, Bytes, U256}; ++ ++ #[test] ++ fn test_tx_gas_limit() { ++ let tx = TxL1Message { gas_limit: 1, ..Default::default() }; ++ let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); ++ assert_eq!(tx_envelope.gas_limit(), 1); ++ } ++ ++ #[test] ++ fn test_encode_decode_l1_message() { ++ let tx = TxL1Message { ++ queue_index: 1, ++ gas_limit: 2, ++ to: Address::left_padding_from(&[3]), ++ sender: Address::left_padding_from(&[4]), ++ value: U256::from(4_u64), ++ input: Bytes::from(vec![5]), + }; -+ assert_eq!(chain_info, expected); ++ let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); ++ let encoded = tx_envelope.encoded_2718(); ++ let decoded = ScrollTxEnvelope::decode_2718(&mut encoded.as_ref()).unwrap(); ++ assert_eq!(encoded.len(), tx_envelope.encode_2718_len()); ++ assert_eq!(decoded, tx_envelope); ++ } ++ ++ #[test] ++ #[cfg(feature = "serde")] ++ fn test_serde_roundtrip_l1_message() { ++ let tx = TxL1Message { ++ queue_index: 11, ++ gas_limit: u64::MAX, ++ sender: Address::random(), ++ to: Address::random(), ++ value: U256::MAX, ++ input: Bytes::new(), ++ }; ++ let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); ++ ++ let serialized = serde_json::to_string(&tx_envelope).unwrap(); ++ let deserialized: ScrollTxEnvelope = serde_json::from_str(&serialized).unwrap(); ++ ++ assert_eq!(tx_envelope, deserialized); ++ } ++ ++ #[test] ++ fn eip2718_l1_message_decode() { ++ // <https://scrollscan.com/tx/0xace7103cc22a372c81cda04e15442a721cd3d5d64eda2e1578ba310d91597d97> ++ let b = hex!("7ef9015a830e7991831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e799100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f8411b3f3d662006b9bf68884e71f1fc0f8ea04e4cb188354738202c3e34a473b93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478"); ++ ++ let tx = ScrollTxEnvelope::decode_2718(&mut b[..].as_ref()).unwrap(); ++ tx.as_l1_message().unwrap(); ++ } ++ ++ #[test] ++ fn eip1559_decode() { ++ use alloy_consensus::SignableTransaction; ++ use alloy_primitives::PrimitiveSignature; ++ let tx = TxEip1559 { ++ chain_id: 1u64, ++ nonce: 2, ++ max_fee_per_gas: 3, ++ max_priority_fee_per_gas: 4, ++ gas_limit: 5, ++ to: Address::left_padding_from(&[6]).into(), ++ value: U256::from(7_u64), ++ input: vec![8].into(), ++ access_list: Default::default(), ++ }; ++ let sig = PrimitiveSignature::test_signature(); ++ let tx_signed = tx.into_signed(sig); ++ let envelope: ScrollTxEnvelope = tx_signed.into(); ++ let encoded = envelope.encoded_2718(); ++ let mut slice = encoded.as_slice(); ++ let decoded = ScrollTxEnvelope::decode_2718(&mut slice).unwrap(); ++ assert!(matches!(decoded, ScrollTxEnvelope::Eip1559(_))); + } +}
@@ -1322,9 +2040,9 @@
@@ -1339,7 +2057,7 @@
@@ -1349,619 +2067,471 @@
-
+602
+
+454
-0
-
diff --git reth/crates/scroll/chainspec/src/lib.rs scroll-reth/crates/scroll/chainspec/src/lib.rs +
diff --git reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs new file mode 100644 -index 0000000000000000000000000000000000000000..b4f80e79091e31484a7ef2629f3b869fddd3923a +index 0000000000000000000000000000000000000000..c4d7b494f2bf8297bfb559914a67e326497f3382 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/lib.rs -@@ -0,0 +1,602 @@ -+//! Scroll-Reth chain specs. -+ -+#![doc( -+ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", -+ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", -+ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" -+)] -+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -+#![cfg_attr(not(feature = "std"), no_std)] ++++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs +@@ -0,0 +1,454 @@ ++//! Scroll L1 message transaction + -+use alloc::{boxed::Box, vec::Vec}; -+use alloy_chains::Chain; -+use alloy_consensus::Header; -+use alloy_genesis::Genesis; -+use alloy_primitives::{B256, U256}; -+use derive_more::{Constructor, Deref, Display, From, Into}; -+use reth_chainspec::{ -+ BaseFeeParams, ChainSpec, ChainSpecBuilder, DepositContract, EthChainSpec, EthereumHardforks, -+ ForkFilter, ForkId, Hardforks, Head, ++use crate::ScrollTxType; ++use alloy_consensus::{Sealable, Transaction, Typed2718}; ++use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}; ++use alloy_primitives::{ ++ keccak256, ++ private::alloy_rlp::{Encodable, Header}, ++ Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256, +}; -+use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; -+use reth_network_peers::NodeRecord; -+use reth_scroll_forks::{ScrollHardfork, ScrollHardforks}; -+ -+#[cfg(not(feature = "std"))] -+use once_cell::sync::Lazy as LazyLock; -+#[cfg(feature = "std")] -+use std::sync::LazyLock; ++use alloy_rlp::Decodable; ++use serde::{Deserialize, Serialize}; ++#[cfg(any(test, feature = "reth-codec"))] ++use {reth_codecs::Compact, reth_codecs_derive::add_arbitrary_tests}; + -+extern crate alloc; ++/// L1 message transaction type id, 0x7e in hex. ++pub const L1_MESSAGE_TRANSACTION_TYPE: u8 = 126; + -+mod constants; -+pub use constants::{ -+ SCROLL_DEV_L1_CONFIG, SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_DEV_L1_PROXY_ADDRESS, -+ SCROLL_DEV_MAX_L1_MESSAGES, SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, -+ SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_MAINNET_L1_PROXY_ADDRESS, -+ SCROLL_MAINNET_MAX_L1_MESSAGES, SCROLL_SEPOLIA_L1_CONFIG, -+ SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_SEPOLIA_L1_PROXY_ADDRESS, -+ SCROLL_SEPOLIA_MAX_L1_MESSAGES, -+}; -+ -+mod dev; -+pub use dev::SCROLL_DEV; -+ -+mod genesis; -+pub use genesis::{ScrollChainConfig, ScrollChainInfo}; -+ -+// convenience re-export of the chain spec provider. -+pub use reth_chainspec::ChainSpecProvider; -+ -+mod scroll; -+pub use scroll::SCROLL_MAINNET; ++/// A message transaction sent from the settlement layer to the L2 for execution. ++/// ++/// The signature of the L1 message is already verified on the L1 and as such doesn't contain ++/// a signature field. Gas for the transaction execution on Scroll is already paid for on the L1. ++/// ++/// # Bincode compatibility ++/// ++/// `bincode` crate doesn't work with optionally serializable serde fields and some of the execution ++/// types require optional serialization for RPC compatibility. Since `TxL1Message` doesn't ++/// contain optionally serializable fields, no `bincode` compatible bridge implementation is ++/// required. ++#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] ++#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))] ++#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] ++#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] ++#[cfg_attr(any(test, feature = "reth-codec"), add_arbitrary_tests(compact, rlp))] ++pub struct TxL1Message { ++ /// The queue index of the message in the L1 contract queue. ++ #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] ++ pub queue_index: u64, ++ /// The gas limit for the transaction. Gas is paid for when message is sent from the L1. ++ #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity", rename = "gas"))] ++ pub gas_limit: u64, ++ /// The destination for the transaction. `Address` is used in place of `TxKind` since contract ++ /// creations aren't allowed via L1 message transactions. ++ pub to: Address, ++ /// The value sent. ++ pub value: U256, ++ /// The L1 sender of the transaction. ++ pub sender: Address, ++ /// The input of the transaction. ++ pub input: Bytes, ++} + -+mod scroll_sepolia; -+pub use scroll_sepolia::SCROLL_SEPOLIA; ++impl TxL1Message { ++ /// Returns an empty signature for the [`TxL1Message`], which don't include a signature. ++ pub fn signature() -> Signature { ++ Signature::new(U256::ZERO, U256::ZERO, false) ++ } + -+/// Chain spec builder for a Scroll chain. -+#[derive(Debug, Default, From)] -+pub struct ScrollChainSpecBuilder { -+ /// [`ChainSpecBuilder`] -+ inner: ChainSpecBuilder, -+} ++ /// Decodes the inner [`TxL1Message`] fields from RLP bytes. ++ /// ++ /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following ++ /// RLP fields in the following order: ++ /// ++ /// - `queue_index` ++ /// - `gas_limit` ++ /// - `to` ++ /// - `value` ++ /// - `input` ++ /// - `sender` ++ pub fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ Ok(Self { ++ queue_index: Decodable::decode(buf)?, ++ gas_limit: Decodable::decode(buf)?, ++ to: Decodable::decode(buf)?, ++ value: Decodable::decode(buf)?, ++ input: Decodable::decode(buf)?, ++ sender: Decodable::decode(buf)?, ++ }) ++ } + -+impl ScrollChainSpecBuilder { -+ /// Construct a new builder from the scroll mainnet chain spec. -+ pub fn scroll_mainnet() -> Self { -+ Self { -+ inner: ChainSpecBuilder::default() -+ .chain(SCROLL_MAINNET.chain) -+ .genesis(SCROLL_MAINNET.genesis.clone()) -+ .with_forks(SCROLL_MAINNET.hardforks.clone()), ++ /// Decodes the transaction from RLP bytes. ++ pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ let header = Header::decode(buf)?; ++ if !header.list { ++ return Err(alloy_rlp::Error::UnexpectedString); + } -+ } ++ let remaining = buf.len(); + -+ /// Construct a new builder from the scroll sepolia chain spec. -+ pub fn scroll_sepolia() -> Self { -+ Self { -+ inner: ChainSpecBuilder::default() -+ .chain(SCROLL_SEPOLIA.chain) -+ .genesis(SCROLL_SEPOLIA.genesis.clone()) -+ .with_forks(SCROLL_SEPOLIA.hardforks.clone()), ++ let this = Self::rlp_decode_fields(buf)?; ++ ++ if buf.len() + header.payload_length != remaining { ++ return Err(alloy_rlp::Error::ListLengthMismatch { ++ expected: header.payload_length, ++ got: remaining - buf.len(), ++ }); + } -+ } -+} + -+impl ScrollChainSpecBuilder { -+ /// Set the chain ID -+ pub fn chain(mut self, chain: Chain) -> Self { -+ self.inner = self.inner.chain(chain); -+ self ++ Ok(this) + } + -+ /// Set the genesis block. -+ pub fn genesis(mut self, genesis: Genesis) -> Self { -+ self.inner = self.inner.genesis(genesis); -+ self ++ /// Outputs the length of the transaction's fields, without a RLP header. ++ fn rlp_encoded_fields_length(&self) -> usize { ++ self.queue_index.length() + ++ self.gas_limit.length() + ++ self.to.length() + ++ self.value.length() + ++ self.input.0.length() + ++ self.sender.length() + } + -+ /// Add the given fork with the given activation condition to the spec. -+ pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self { -+ self.inner = self.inner.with_fork(fork, condition); -+ self ++ /// Encode the fields of the transaction without a RLP header. ++ /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/l1_message_tx.go#L12-L19> ++ fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { ++ self.queue_index.encode(out); ++ self.gas_limit.encode(out); ++ self.to.encode(out); ++ self.value.encode(out); ++ self.input.encode(out); ++ self.sender.encode(out); + } + -+ /// Add the given forks with the given activation condition to the spec. -+ pub fn with_forks(mut self, forks: ChainHardforks) -> Self { -+ self.inner = self.inner.with_forks(forks); -+ self ++ pub(crate) const fn tx_type(&self) -> u8 { ++ L1_MESSAGE_TRANSACTION_TYPE + } + -+ /// Remove the given fork from the spec. -+ pub fn without_fork(mut self, fork: reth_scroll_forks::ScrollHardfork) -> Self { -+ self.inner = self.inner.without_fork(fork); -+ self ++ /// Create a RLP header for the transaction. ++ fn rlp_header(&self) -> Header { ++ Header { list: true, payload_length: self.rlp_encoded_fields_length() } + } + -+ /// Enable Archimedes at genesis -+ pub fn archimedes_activated(mut self) -> Self { -+ self.inner = self.inner.london_activated(); -+ self.inner = self -+ .inner -+ .with_fork(reth_scroll_forks::ScrollHardfork::Archimedes, ForkCondition::Block(0)); -+ self ++ /// RLP encodes the transaction. ++ pub fn rlp_encode(&self, out: &mut dyn alloy_rlp::BufMut) { ++ self.rlp_header().encode(out); ++ self.rlp_encode_fields(out); + } + -+ /// Enable Bernoulli at genesis -+ pub fn bernoulli_activated(mut self) -> Self { -+ self = self.archimedes_activated(); -+ self.inner = self.inner.with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0)); -+ self.inner = self -+ .inner -+ .with_fork(reth_scroll_forks::ScrollHardfork::Bernoulli, ForkCondition::Block(0)); -+ self ++ /// Get the length of the transaction when RLP encoded. ++ pub fn rlp_encoded_length(&self) -> usize { ++ self.rlp_header().length_with_payload() + } + -+ /// Enable Curie at genesis -+ pub fn curie_activated(mut self) -> Self { -+ self = self.bernoulli_activated(); -+ self.inner = -+ self.inner.with_fork(reth_scroll_forks::ScrollHardfork::Curie, ForkCondition::Block(0)); -+ self ++ /// Get the length of the transaction when EIP-2718 encoded. This is the ++ /// 1 byte type flag + the length of the RLP encoded transaction. ++ pub fn eip2718_encoded_length(&self) -> usize { ++ self.rlp_encoded_length() + 1 + } + -+ /// Enable Darwin at genesis -+ pub fn darwin_activated(mut self) -> Self { -+ self = self.curie_activated(); -+ self.inner = self -+ .inner -+ .with_fork(reth_scroll_forks::ScrollHardfork::Darwin, ForkCondition::Timestamp(0)); -+ self ++ /// EIP-2718 encode the transaction. ++ pub fn eip2718_encode(&self, out: &mut dyn alloy_rlp::BufMut) { ++ out.put_u8(L1_MESSAGE_TRANSACTION_TYPE); ++ self.rlp_encode(out) + } + -+ /// Enable `DarwinV2` at genesis -+ pub fn darwin_v2_activated(mut self) -> Self { -+ self = self.darwin_activated(); -+ self.inner = self -+ .inner -+ .with_fork(reth_scroll_forks::ScrollHardfork::DarwinV2, ForkCondition::Timestamp(0)); -+ self ++ /// Calculates the in-memory size of the [`TxL1Message`] transaction. ++ #[allow(clippy::missing_const_for_fn)] ++ #[inline] ++ pub fn size(&self) -> usize { ++ size_of::<u64>() + // queue_index ++ size_of::<u64>() + // gas_limit ++ size_of::<Address>() + // to ++ size_of::<U256>() + // value ++ self.input.len() + // input ++ size_of::<Address>() // sender + } + -+ /// Build the resulting [`ScrollChainSpec`]. -+ /// -+ /// # Panics -+ /// -+ /// This function panics if the chain ID and genesis is not set ([`Self::chain`] and -+ /// [`Self::genesis`]) -+ pub fn build(self, config: ScrollChainConfig) -> ScrollChainSpec { -+ ScrollChainSpec { inner: self.inner.build(), config } ++ /// Calculates the hash of the [`TxL1Message`] transaction. ++ pub fn tx_hash(&self) -> TxHash { ++ let mut buf = Vec::with_capacity(self.eip2718_encoded_length()); ++ self.eip2718_encode(&mut buf); ++ keccak256(&buf) + } +} + -+/// Scroll chain spec type. -+#[derive(Debug, Clone, Deref, Into, Constructor, PartialEq, Eq)] -+pub struct ScrollChainSpec { -+ /// [`ChainSpec`]. -+ #[deref] -+ pub inner: ChainSpec, -+ /// [`ScrollChainConfig`] -+ pub config: ScrollChainConfig, ++impl Typed2718 for TxL1Message { ++ fn ty(&self) -> u8 { ++ ScrollTxType::L1Message as u8 ++ } +} + -+impl EthChainSpec for ScrollChainSpec { -+ type Header = Header; -+ -+ fn chain(&self) -> alloy_chains::Chain { -+ self.inner.chain() ++impl Encodable2718 for TxL1Message { ++ fn type_flag(&self) -> Option<u8> { ++ Some(self.tx_type()) + } + -+ fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams { -+ // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 -+ self.inner.base_fee_params_at_block(block_number) ++ fn encode_2718_len(&self) -> usize { ++ self.eip2718_encoded_length() + } + -+ fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams { -+ // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 -+ self.inner.base_fee_params_at_timestamp(timestamp) ++ fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { ++ out.put_u8(self.tx_type()); ++ self.rlp_encode(out); + } ++} + -+ fn deposit_contract(&self) -> Option<&DepositContract> { -+ self.inner.deposit_contract() -+ } -+ -+ fn genesis_hash(&self) -> B256 { -+ *self.genesis_hash.get_or_init(|| self.genesis_header().hash_slow()) -+ } -+ -+ fn prune_delete_limit(&self) -> usize { -+ self.inner.prune_delete_limit() -+ } -+ -+ fn display_hardforks(&self) -> Box<dyn Display> { -+ Box::new(ChainSpec::display_hardforks(self)) ++impl Decodable2718 for TxL1Message { ++ fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { ++ if ty != L1_MESSAGE_TRANSACTION_TYPE { ++ return Err(Eip2718Error::UnexpectedType(ty)); ++ } ++ let tx = Self::rlp_decode(buf)?; ++ Ok(tx) + } + -+ fn genesis_header(&self) -> &Header { -+ self.inner.genesis_header.get_or_init(|| self.make_genesis_header()) ++ fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { ++ let tx = Self::decode(buf)?; ++ Ok(tx) + } ++} + -+ fn genesis(&self) -> &Genesis { -+ self.inner.genesis() ++impl Encodable for TxL1Message { ++ fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { ++ self.rlp_encode(out) + } + -+ fn bootnodes(&self) -> Option<Vec<NodeRecord>> { -+ self.inner.bootnodes() ++ fn length(&self) -> usize { ++ self.rlp_encoded_length() + } +} + -+impl ScrollChainSpec { -+ fn make_genesis_header(&self) -> Header { -+ Header { -+ gas_limit: self.genesis.gas_limit, -+ difficulty: self.genesis.difficulty, -+ nonce: self.genesis.nonce.into(), -+ extra_data: self.genesis.extra_data.clone(), -+ state_root: reth_trie_common::root::state_root_ref_unhashed(&self.genesis.alloc), -+ timestamp: self.genesis.timestamp, -+ mix_hash: self.genesis.mix_hash, -+ beneficiary: self.genesis.coinbase, -+ base_fee_per_gas: None, -+ withdrawals_root: None, -+ parent_beacon_block_root: None, -+ blob_gas_used: None, -+ excess_blob_gas: None, -+ requests_hash: None, -+ ..Default::default() -+ } ++impl Decodable for TxL1Message { ++ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ Self::rlp_decode(buf) + } +} + -+impl Hardforks for ScrollChainSpec { -+ fn fork<H: reth_chainspec::Hardfork>(&self, fork: H) -> reth_chainspec::ForkCondition { -+ self.inner.fork(fork) ++impl Transaction for TxL1Message { ++ fn chain_id(&self) -> Option<ChainId> { ++ None + } + -+ fn forks_iter( -+ &self, -+ ) -> impl Iterator<Item = (&dyn reth_chainspec::Hardfork, reth_chainspec::ForkCondition)> { -+ self.inner.forks_iter() ++ fn nonce(&self) -> u64 { ++ 0u64 + } + -+ fn fork_id(&self, head: &Head) -> ForkId { -+ self.inner.fork_id(head) ++ fn gas_limit(&self) -> u64 { ++ self.gas_limit + } + -+ fn latest_fork_id(&self) -> ForkId { -+ self.inner.latest_fork_id() ++ fn gas_price(&self) -> Option<u128> { ++ None + } + -+ fn fork_filter(&self, head: Head) -> ForkFilter { -+ self.inner.fork_filter(head) ++ fn max_fee_per_gas(&self) -> u128 { ++ 0 + } -+} + -+impl EthereumHardforks for ScrollChainSpec { -+ fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition { -+ self.fork(fork) ++ fn max_priority_fee_per_gas(&self) -> Option<u128> { ++ None + } + -+ fn get_final_paris_total_difficulty(&self) -> Option<U256> { -+ self.inner.get_final_paris_total_difficulty() ++ fn max_fee_per_blob_gas(&self) -> Option<u128> { ++ None + } + -+ fn final_paris_total_difficulty(&self, block_number: u64) -> Option<U256> { -+ self.inner.final_paris_total_difficulty(block_number) ++ fn priority_fee_or_price(&self) -> u128 { ++ 0 + } -+} + -+impl ScrollHardforks for ScrollChainSpec { -+ fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition { -+ self.fork(fork) ++ fn effective_gas_price(&self, _base_fee: Option<u64>) -> u128 { ++ 0 + } -+} + -+impl From<Genesis> for ScrollChainSpec { -+ fn from(genesis: Genesis) -> Self { -+ use reth_scroll_forks::ScrollHardfork; -+ let scroll_chain_info = ScrollConfigInfo::extract_from(&genesis); -+ let hard_fork_info = -+ scroll_chain_info.scroll_chain_info.hard_fork_info.expect("load scroll hard fork info"); ++ fn is_dynamic_fee(&self) -> bool { ++ false ++ } + -+ // Block-based hardforks -+ let hardfork_opts = [ -+ (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), -+ (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), -+ (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), -+ (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), -+ (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), -+ (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), -+ (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), -+ (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), -+ (EthereumHardfork::London.boxed(), genesis.config.london_block), -+ (ScrollHardfork::Archimedes.boxed(), hard_fork_info.archimedes_block), -+ (ScrollHardfork::Bernoulli.boxed(), hard_fork_info.bernoulli_block), -+ (ScrollHardfork::Curie.boxed(), hard_fork_info.curie_block), -+ ]; -+ let mut block_hardforks = hardfork_opts -+ .into_iter() -+ .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) -+ .collect::<Vec<_>>(); ++ fn kind(&self) -> TxKind { ++ TxKind::Call(self.to) ++ } + -+ // Time-based hardforks -+ let time_hardfork_opts = [ -+ (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), -+ (ScrollHardfork::Darwin.boxed(), hard_fork_info.darwin_time), -+ (ScrollHardfork::DarwinV2.boxed(), hard_fork_info.darwin_v2_time), -+ ]; ++ fn is_create(&self) -> bool { ++ false ++ } + -+ let mut time_hardforks = time_hardfork_opts -+ .into_iter() -+ .filter_map(|(hardfork, opt)| { -+ opt.map(|time| (hardfork, ForkCondition::Timestamp(time))) -+ }) -+ .collect::<Vec<_>>(); ++ fn value(&self) -> U256 { ++ self.value ++ } + -+ block_hardforks.append(&mut time_hardforks); ++ fn input(&self) -> &Bytes { ++ &self.input ++ } + -+ // Ordered Hardforks -+ let mainnet_hardforks = ScrollHardfork::scroll_mainnet(); -+ let mainnet_order = mainnet_hardforks.forks_iter(); ++ fn access_list(&self) -> Option<&alloy_eips::eip2930::AccessList> { ++ None ++ } + -+ let mut ordered_hardforks = Vec::with_capacity(block_hardforks.len()); -+ for (hardfork, _) in mainnet_order { -+ if let Some(pos) = block_hardforks.iter().position(|(e, _)| **e == *hardfork) { -+ ordered_hardforks.push(block_hardforks.remove(pos)); -+ } -+ } ++ fn blob_versioned_hashes(&self) -> Option<&[B256]> { ++ None ++ } + -+ // append the remaining unknown hardforks to ensure we don't filter any out -+ ordered_hardforks.append(&mut block_hardforks); ++ fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { ++ None ++ } ++} + -+ Self { -+ inner: ChainSpec { -+ chain: genesis.config.chain_id.into(), -+ genesis, -+ hardforks: ChainHardforks::new(ordered_hardforks), -+ ..Default::default() -+ }, -+ config: scroll_chain_info.scroll_chain_info.scroll_chain_config, -+ } ++impl Sealable for TxL1Message { ++ fn hash_slow(&self) -> B256 { ++ self.tx_hash() + } +} + -+#[derive(Default, Debug)] -+struct ScrollConfigInfo { -+ scroll_chain_info: ScrollChainInfo, ++/// Scroll specific transaction fields ++#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct ScrollL1MessageTransactionFields { ++ /// The index of the transaction in the message queue. ++ #[serde(with = "alloy_serde::quantity")] ++ pub queue_index: u64, ++ /// The sender of the transaction on the L1. ++ pub sender: Address, +} + -+impl ScrollConfigInfo { -+ fn extract_from(genesis: &Genesis) -> Self { -+ Self { -+ scroll_chain_info: ScrollChainInfo::extract_from(&genesis.config.extra_fields) -+ .expect("extract scroll extra fields failed"), -+ } ++/// L1 message transactions don't have a signature, however, we include an empty signature in the ++/// response for better compatibility. ++/// ++/// This function can be used as `serialize_with` serde attribute for the [`TxL1Message`] and will ++/// flatten [`TxL1Message::signature`] into response. ++/// ++/// <https://github.com/scroll-tech/go-ethereum/blob/develop/core/types/l1_message_tx.go#L51>. ++#[cfg(feature = "serde")] ++pub fn serde_l1_message_tx_rpc<T: serde::Serialize, S: serde::Serializer>( ++ value: &T, ++ serializer: S, ++) -> Result<S::Ok, S::Error> { ++ use serde::Serialize; ++ ++ #[derive(Serialize)] ++ struct SerdeHelper<'a, T> { ++ #[serde(flatten)] ++ value: &'a T, ++ #[serde(flatten)] ++ signature: Signature, + } ++ ++ SerdeHelper { value, signature: TxL1Message::signature() }.serialize(serializer) +} + +#[cfg(test)] +mod tests { -+ use crate::*; -+ use alloy_genesis::{ChainConfig, Genesis}; -+ use alloy_primitives::b256; -+ use reth_chainspec::test_fork_ids; -+ use reth_ethereum_forks::{EthereumHardfork, ForkHash}; -+ use reth_scroll_forks::ScrollHardfork; ++ use super::*; ++ use alloy_eips::eip2718::Encodable2718; ++ use alloy_primitives::{address, bytes, hex, Bytes, U256}; ++ use alloy_rlp::BytesMut; ++ use arbitrary::Arbitrary; ++ use rand::Rng; ++ use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; + + #[test] -+ fn scroll_mainnet_genesis_hash() { -+ let scroll_mainnet = -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); -+ assert_eq!( -+ b256!("908789cb20d00fc6070093f142aa8d02c21cfb0a9b9cfd4621d8cf0255234c0f"), -+ scroll_mainnet.genesis_hash() -+ ); ++ fn test_rlp_roundtrip() { ++ // <https://scrollscan.com/tx/0xace7103cc22a372c81cda04e15442a721cd3d5d64eda2e1578ba310d91597d97> ++ let bytes = Bytes::from_static(&hex!("7ef9015a830e7991831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e799100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f8411b3f3d662006b9bf68884e71f1fc0f8ea04e4cb188354738202c3e34a473b93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478")); ++ let tx_a = TxL1Message::decode(&mut bytes[1..].as_ref()).unwrap(); ++ let mut buf_a = BytesMut::default(); ++ tx_a.encode(&mut buf_a); ++ assert_eq!(&buf_a[..], &bytes[1..]); + } + + #[test] -+ fn scroll_sepolia_genesis_hash() { -+ let scroll_sepolia = -+ ScrollChainSpecBuilder::scroll_sepolia().build(ScrollChainConfig::sepolia()); -+ assert_eq!( -+ b256!("5e756a466b785b67e247b18c410d962866a53af97f09948016e9239b2054c94f"), -+ scroll_sepolia.genesis_hash() -+ ); ++ fn test_encode_decode_fields() { ++ let original = TxL1Message { ++ queue_index: 100, ++ gas_limit: 0, ++ to: Address::default(), ++ value: U256::default(), ++ sender: Address::default(), ++ input: Bytes::default(), ++ }; ++ let mut buffer = BytesMut::new(); ++ original.rlp_encode_fields(&mut buffer); ++ let decoded = TxL1Message::rlp_decode_fields(&mut &buffer[..]).expect("Failed to decode"); ++ ++ assert_eq!(original, decoded); + } + + #[test] -+ fn scroll_mainnet_forkids() { -+ test_fork_ids( -+ &SCROLL_MAINNET, -+ &[ -+ ( -+ Head { number: 0, ..Default::default() }, -+ ForkId { hash: ForkHash([0xea, 0x6b, 0x56, 0xca]), next: 5220340 }, -+ ), -+ ( -+ Head { number: 5220340, ..Default::default() }, -+ ForkId { hash: ForkHash([0xee, 0x46, 0xae, 0x2a]), next: 7096836 }, -+ ), -+ ( -+ Head { number: 7096836, ..Default::default() }, -+ ForkId { hash: ForkHash([0x18, 0xd3, 0xc8, 0xd9]), next: 1724227200 }, -+ ), -+ ( -+ Head { number: 7096836, timestamp: 1724227200, ..Default::default() }, -+ ForkId { hash: ForkHash([0xcc, 0xeb, 0x09, 0xb0]), next: 1725264000 }, -+ ), -+ ( -+ Head { number: 7096836, timestamp: 1725264000, ..Default::default() }, -+ ForkId { hash: ForkHash([0x21, 0xa2, 0x07, 0x54]), next: 0 }, -+ ), -+ ], -+ ); -+ } ++ fn test_encode_with_and_without_header() { ++ let tx_deposit = TxL1Message { ++ queue_index: 0, ++ gas_limit: 50000, ++ to: Address::default(), ++ value: U256::default(), ++ sender: Address::default(), ++ input: Bytes::default(), ++ }; + -+ #[test] -+ fn scroll_sepolia_forkids() { -+ test_fork_ids( -+ &SCROLL_SEPOLIA, -+ &[ -+ ( -+ Head { number: 0, ..Default::default() }, -+ ForkId { hash: ForkHash([0x25, 0xfa, 0xe4, 0x54]), next: 3747132 }, -+ ), -+ ( -+ Head { number: 3747132, ..Default::default() }, -+ ForkId { hash: ForkHash([0xda, 0x76, 0xc2, 0x2d]), next: 4740239 }, -+ ), -+ ( -+ Head { number: 4740239, ..Default::default() }, -+ ForkId { hash: ForkHash([0x9f, 0xb4, 0x75, 0xf1]), next: 1723622400 }, -+ ), -+ ( -+ Head { number: 4740239, timestamp: 1723622400, ..Default::default() }, -+ ForkId { hash: ForkHash([0xe9, 0x26, 0xd4, 0x9b]), next: 1724832000 }, -+ ), -+ ( -+ Head { number: 4740239, timestamp: 1724832000, ..Default::default() }, -+ ForkId { hash: ForkHash([0x69, 0xf3, 0x7e, 0xde]), next: 0 }, -+ ), -+ ], -+ ); ++ let mut buffer_with_header = BytesMut::new(); ++ tx_deposit.encode(&mut buffer_with_header); ++ ++ let mut buffer_without_header = BytesMut::new(); ++ tx_deposit.rlp_encode_fields(&mut buffer_without_header); ++ ++ assert!(buffer_with_header.len() > buffer_without_header.len()); + } + + #[test] -+ fn is_bernoulli_active() { -+ let scroll_mainnet = -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); -+ assert!(!scroll_mainnet.is_bernoulli_active_at_block(1)) ++ fn test_payload_length() { ++ let tx_deposit = TxL1Message { ++ queue_index: 0, ++ gas_limit: 50000, ++ to: Address::default(), ++ value: U256::default(), ++ sender: Address::default(), ++ input: Bytes::default(), ++ }; ++ ++ assert!(tx_deposit.size() > tx_deposit.rlp_encoded_fields_length()); + } + + #[test] -+ fn parse_scroll_hardforks() { -+ let geth_genesis = r#" -+ { -+ "config": { -+ "bernoulliBlock": 10, -+ "curieBlock": 20, -+ "darwinTime": 30, -+ "darwinV2Time": 31, -+ "scroll": { -+ "feeVaultAddress": "0x5300000000000000000000000000000000000005", -+ "l1Config": { -+ "l1ChainId": 1, -+ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", -+ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", -+ "numL1MessagesPerBlock": 10 -+ } -+ } -+ } ++ fn test_deserialize_hex_to_u64() { ++ let rpc_tx = r#"{"gas":"0x1e8480","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","value":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0"}"#; ++ // let obj: TxL1Message = serde_json::from_str(rpc_tx).unwrap(); ++ let obj = serde_json::from_str::<TxL1Message>(rpc_tx).unwrap(); ++ assert_eq!(obj.queue_index, 0xe7ba0); + } -+ "#; -+ let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); -+ -+ let actual_bernoulli_block = genesis.config.extra_fields.get("bernoulliBlock"); -+ assert_eq!(actual_bernoulli_block, Some(serde_json::Value::from(10)).as_ref()); -+ let actual_curie_block = genesis.config.extra_fields.get("curieBlock"); -+ assert_eq!(actual_curie_block, Some(serde_json::Value::from(20)).as_ref()); -+ let actual_darwin_timestamp = genesis.config.extra_fields.get("darwinTime"); -+ assert_eq!(actual_darwin_timestamp, Some(serde_json::Value::from(30)).as_ref()); -+ let actual_darwin_v2_timestamp = genesis.config.extra_fields.get("darwinV2Time"); -+ assert_eq!(actual_darwin_v2_timestamp, Some(serde_json::Value::from(31)).as_ref()); -+ let scroll_object = genesis.config.extra_fields.get("scroll").unwrap(); -+ assert_eq!( -+ scroll_object, -+ &serde_json::json!({ -+ "feeVaultAddress": "0x5300000000000000000000000000000000000005", -+ "l1Config": { -+ "l1ChainId": 1, -+ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", -+ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", -+ "numL1MessagesPerBlock": 10 -+ } -+ }) -+ ); -+ -+ let chain_spec: ScrollChainSpec = genesis.into(); + -+ assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 0)); -+ assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 0)); -+ assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 0)); -+ assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 0)); ++ #[test] ++ fn test_bincode_roundtrip() { ++ let mut bytes = [0u8; 1024]; ++ rand::thread_rng().fill(bytes.as_mut_slice()); ++ let tx = TxL1Message::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); + -+ assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 10)); -+ assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 20)); -+ assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 30)); -+ assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 31)); ++ let encoded = bincode::serialize(&tx).unwrap(); ++ let decoded: TxL1Message = bincode::deserialize(&encoded).unwrap(); ++ assert_eq!(decoded, tx); + } + + #[test] -+ fn test_fork_order_scroll_mainnet() { -+ let genesis = Genesis { -+ config: ChainConfig { -+ chain_id: 0, -+ homestead_block: Some(0), -+ dao_fork_block: Some(0), -+ dao_fork_support: false, -+ eip150_block: Some(0), -+ eip155_block: Some(0), -+ eip158_block: Some(0), -+ byzantium_block: Some(0), -+ constantinople_block: Some(0), -+ petersburg_block: Some(0), -+ istanbul_block: Some(0), -+ berlin_block: Some(0), -+ london_block: Some(0), -+ shanghai_time: Some(0), -+ extra_fields: [ -+ (String::from("archimedesBlock"), 0.into()), -+ (String::from("bernoulliBlock"), 0.into()), -+ (String::from("curieBlock"), 0.into()), -+ (String::from("darwinTime"), 0.into()), -+ (String::from("darwinV2Time"), 0.into()), -+ ( -+ String::from("scroll"), -+ serde_json::json!({ -+ "feeVaultAddress": "0x5300000000000000000000000000000000000005", -+ "l1Config": { -+ "l1ChainId": 1, -+ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", -+ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", -+ "numL1MessagesPerBlock": 10 -+ } -+ }), -+ ), -+ ] -+ .into_iter() -+ .collect(), -+ ..Default::default() -+ }, -+ ..Default::default() -+ }; -+ -+ let chain_spec: ScrollChainSpec = genesis.into(); ++ fn test_eip2718_encode() { ++ let tx = ++ TxL1Message { ++ queue_index: 947883, ++ gas_limit: 2000000, ++ to: address!("781e90f1c8fc4611c9b7497c3b47f99ef6969cbc"), ++ value: U256::ZERO, ++ sender: address!("7885bcbd5cecef1336b5300fb5186a12ddd8c478"), ++ input: bytes!("8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), ++ } ++ ; ++ let bytes = Bytes::from_static(&hex!("7ef9015a830e76ab831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478")); + -+ let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); -+ let expected_hardforks = vec![ -+ EthereumHardfork::Homestead.boxed(), -+ EthereumHardfork::Tangerine.boxed(), -+ EthereumHardfork::SpuriousDragon.boxed(), -+ EthereumHardfork::Byzantium.boxed(), -+ EthereumHardfork::Constantinople.boxed(), -+ EthereumHardfork::Petersburg.boxed(), -+ EthereumHardfork::Istanbul.boxed(), -+ EthereumHardfork::Berlin.boxed(), -+ EthereumHardfork::London.boxed(), -+ ScrollHardfork::Archimedes.boxed(), -+ EthereumHardfork::Shanghai.boxed(), -+ ScrollHardfork::Bernoulli.boxed(), -+ ScrollHardfork::Curie.boxed(), -+ ScrollHardfork::Darwin.boxed(), -+ ScrollHardfork::DarwinV2.boxed(), -+ ]; ++ let mut encoded = BytesMut::default(); ++ tx.encode_2718(&mut encoded); + -+ assert!(expected_hardforks -+ .iter() -+ .zip(hardforks.iter()) -+ .all(|(expected, actual)| &**expected == *actual)); ++ assert_eq!(encoded, bytes.as_ref()) ++ } + -+ assert_eq!(expected_hardforks.len(), hardforks.len()); ++ #[test] ++ fn test_compaction_backwards_compatibility() { ++ assert_eq!(TxL1Message::bitflag_encoded_bytes(), 2); ++ validate_bitflag_backwards_compat!(TxL1Message, UnusedBits::NotZero); + } +}
@@ -1970,9 +2540,9 @@
@@ -1987,7 +2557,7 @@
@@ -1997,58 +2567,44 @@
-
+30
+
+16
-0
-
diff --git reth/crates/scroll/chainspec/src/scroll.rs scroll-reth/crates/scroll/chainspec/src/scroll.rs +
diff --git reth/crates/scroll/alloy/consensus/src/transaction/mod.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/mod.rs new file mode 100644 -index 0000000000000000000000000000000000000000..ebd700386279ca870c67a032d4c83fa29c0f16bb +index 0000000000000000000000000000000000000000..40653e279e4202a9fa15ed476a60bb497dd9d322 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/scroll.rs -@@ -0,0 +1,30 @@ -+//! Chain specification for the Scroll Mainnet network. ++++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/mod.rs +@@ -0,0 +1,16 @@ ++//! Transaction types for Scroll. + -+use alloc::sync::Arc; ++mod tx_type; ++pub use tx_type::{ScrollTxType, L1_MESSAGE_TX_TYPE_ID}; + -+use alloy_chains::{Chain, NamedChain}; -+use alloy_primitives::b256; -+use reth_chainspec::{once_cell_set, ChainSpec}; -+use reth_scroll_forks::ScrollHardfork; ++mod envelope; ++pub use envelope::ScrollTxEnvelope; + -+use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; ++mod l1_message; ++pub use l1_message::{ScrollL1MessageTransactionFields, TxL1Message, L1_MESSAGE_TRANSACTION_TYPE}; + -+/// The Scroll Mainnet spec -+pub static SCROLL_MAINNET: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { -+ ScrollChainSpec { -+ inner: ChainSpec { -+ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ chain: Chain::from_named(NamedChain::Scroll), -+ genesis: serde_json::from_str(include_str!("../res/genesis/scroll.json")) -+ .expect("Can't deserialize Scroll Mainnet genesis json"), -+ // TODO(scroll): update this value once the bootnodes run the mpt. -+ genesis_hash: once_cell_set(b256!( -+ "bbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80" -+ )), -+ hardforks: ScrollHardfork::scroll_mainnet(), -+ ..Default::default() -+ }, -+ config: ScrollChainConfig::mainnet(), -+ } -+ .into() -+});
++mod typed; ++pub use typed::ScrollTypedTransaction; ++ ++#[cfg(feature = "serde")] ++pub use l1_message::serde_l1_message_tx_rpc;
@@ -2063,7 +2619,7 @@
@@ -2073,208 +2629,226 @@
-
+30
+
+198
-0
-
diff --git reth/crates/scroll/chainspec/src/scroll_sepolia.rs scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs +
diff --git reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs new file mode 100644 -index 0000000000000000000000000000000000000000..1a19597793a90586d2bc034249ad71f5bbc95cc1 +index 0000000000000000000000000000000000000000..e1892884f1b2bc0f309c603ce757ad2e3597fa56 --- /dev/null -+++ scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs -@@ -0,0 +1,30 @@ -+//! Chain specification for the Scroll Sepolia testnet network. ++++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs +@@ -0,0 +1,198 @@ ++//! Contains the transaction type identifier for Scroll. ++ ++use alloy_consensus::Typed2718; ++use alloy_eips::eip2718::Eip2718Error; ++use alloy_primitives::{U64, U8}; ++use alloy_rlp::{BufMut, Decodable, Encodable}; ++use derive_more::Display; ++use reth_codecs::{ ++ __private::bytes, ++ txtype::{ ++ COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930, ++ COMPACT_IDENTIFIER_LEGACY, ++ }, ++ Compact, ++}; + -+use alloc::sync::Arc; ++/// Identifier for an Scroll L1 message transaction ++pub const L1_MESSAGE_TX_TYPE_ID: u8 = 126; // 0x7E ++ ++/// Scroll `TransactionType` flags as specified in <https://docs.scroll.io/en/technology/chain/transactions/>. ++#[repr(u8)] ++#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Display)] ++pub enum ScrollTxType { ++ /// Legacy transaction type. ++ #[display("legacy")] ++ Legacy = 0, ++ /// EIP-2930 transaction type. ++ #[display("eip2930")] ++ Eip2930 = 1, ++ /// EIP-1559 transaction type. ++ #[display("eip1559")] ++ Eip1559 = 2, ++ /// L1 message transaction type. ++ #[display("l1_message")] ++ L1Message = L1_MESSAGE_TX_TYPE_ID, ++} + -+use alloy_chains::{Chain, NamedChain}; -+use alloy_primitives::b256; -+use reth_chainspec::{once_cell_set, ChainSpec}; -+use reth_scroll_forks::ScrollHardfork; ++impl ScrollTxType { ++ /// List of all variants. ++ pub const ALL: [Self; 4] = [Self::Legacy, Self::Eip1559, Self::Eip2930, Self::L1Message]; ++} + -+use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; ++#[cfg(any(test, feature = "arbitrary"))] ++impl arbitrary::Arbitrary<'_> for ScrollTxType { ++ fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { ++ let i = u.choose_index(Self::ALL.len())?; ++ Ok(Self::ALL[i]) ++ } ++} + -+/// The Scroll Sepolia spec -+pub static SCROLL_SEPOLIA: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { -+ ScrollChainSpec { -+ inner: ChainSpec { -+ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ chain: Chain::from_named(NamedChain::ScrollSepolia), -+ genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_scroll.json")) -+ .expect("Can't deserialize Scroll Sepolia genesis json"), -+ // TODO(scroll): update this value once the bootnodes run the mpt. -+ genesis_hash: once_cell_set(b256!( -+ "aa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c" -+ )), -+ hardforks: ScrollHardfork::scroll_sepolia(), -+ ..Default::default() -+ }, -+ config: ScrollChainConfig::sepolia(), ++impl From<ScrollTxType> for U8 { ++ fn from(tx_type: ScrollTxType) -> Self { ++ Self::from(u8::from(tx_type)) + } -+ .into() -+});
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+53
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/cli/Cargo.toml scroll-reth/crates/scroll/cli/Cargo.toml -new file mode 100644 -index 0000000000000000000000000000000000000000..cbfe12404e180b3cc57773f7691a99a6accb6aa7 ---- /dev/null -+++ scroll-reth/crates/scroll/cli/Cargo.toml -@@ -0,0 +1,54 @@ -+[package] -+name = "reth-scroll-cli" -+version.workspace = true -+edition.workspace = true -+rust-version.workspace = true -+license.workspace = true -+homepage.workspace = true -+repository.workspace = true -+exclude.workspace = true ++} + -+[lints] -+workspace = true ++impl From<ScrollTxType> for u8 { ++ fn from(v: ScrollTxType) -> Self { ++ v as Self ++ } ++} + -+[dependencies] -+# reth -+reth-db.workspace = true -+reth-cli.workspace = true -+reth-cli-commands.workspace = true -+reth-cli-runner.workspace = true -+reth-eth-wire.workspace = true -+reth-node-builder.workspace = true -+reth-node-core.workspace = true -+reth-node-metrics.workspace = true -+reth-primitives.workspace = true -+reth-tracing.workspace = true ++impl TryFrom<u8> for ScrollTxType { ++ type Error = Eip2718Error; + -+# alloy -+alloy-consensus.workspace = true ++ fn try_from(value: u8) -> Result<Self, Self::Error> { ++ Ok(match value { ++ x if x == Self::Legacy as u8 => Self::Legacy, ++ x if x == Self::Eip2930 as u8 => Self::Eip2930, ++ x if x == Self::Eip1559 as u8 => Self::Eip1559, ++ x if x == Self::L1Message as u8 => Self::L1Message, ++ _ => return Err(Eip2718Error::UnexpectedType(value)), ++ }) ++ } ++} + -+# scroll -+reth-scroll-chainspec.workspace = true -+reth-scroll-evm.workspace = true -+reth-scroll-node.workspace = true ++impl TryFrom<u64> for ScrollTxType { ++ type Error = &'static str; + -+# misc -+eyre.workspace = true -+clap.workspace = true -+tracing.workspace = true ++ fn try_from(value: u64) -> Result<Self, Self::Error> { ++ let err = || "invalid tx type"; ++ let value: u8 = value.try_into().map_err(|_| err())?; ++ Self::try_from(value).map_err(|_| err()) ++ } ++} + -+[features] -+optimism = [ -+ "reth-db/optimism", -+ "reth-node-core/optimism", -+ "reth-scroll-evm/optimism", -+ "reth-scroll-node/optimism", -+ "reth-primitives/optimism" -+] -+scroll = [ -+ "reth-db/scroll", -+ "reth-node-builder/scroll", -+ "reth-node-core/scroll", -+ "reth-scroll-evm/scroll", -+ "reth-scroll-node/scroll" -+] -\ No newline at end of file
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+3
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/cli/src/args.rs scroll-reth/crates/scroll/cli/src/args.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..d51f83146a06b3387dbd098f63ec1e3d8d46593b ---- /dev/null -+++ scroll-reth/crates/scroll/cli/src/args.rs -@@ -0,0 +1,3 @@ -+/// Rollup arguments for the Scroll node. -+#[derive(Debug, clap::Args)] -+pub struct ScrollRollupArgs;
++impl TryFrom<U64> for ScrollTxType { ++ type Error = &'static str; ++ ++ fn try_from(value: U64) -> Result<Self, Self::Error> { ++ value.to::<u64>().try_into() ++ } ++} ++ ++impl PartialEq<u8> for ScrollTxType { ++ fn eq(&self, other: &u8) -> bool { ++ (*self as u8) == *other ++ } ++} ++ ++impl PartialEq<ScrollTxType> for u8 { ++ fn eq(&self, other: &ScrollTxType) -> bool { ++ *self == *other as Self ++ } ++} ++ ++impl Encodable for ScrollTxType { ++ fn encode(&self, out: &mut dyn BufMut) { ++ (*self as u8).encode(out); ++ } ++ ++ fn length(&self) -> usize { ++ 1 ++ } ++} ++ ++impl Decodable for ScrollTxType { ++ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { ++ let ty = u8::decode(buf)?; ++ ++ Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type")) ++ } ++} ++ ++impl Compact for ScrollTxType { ++ fn to_compact<B>(&self, buf: &mut B) -> usize ++ where ++ B: BufMut + AsMut<[u8]>, ++ { ++ match self { ++ Self::Legacy => COMPACT_IDENTIFIER_LEGACY, ++ Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930, ++ Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559, ++ Self::L1Message => { ++ buf.put_u8(L1_MESSAGE_TX_TYPE_ID); ++ COMPACT_EXTENDED_IDENTIFIER_FLAG ++ } ++ } ++ } ++ ++ // For backwards compatibility purposes only 2 bits of the type are encoded in the identifier ++ // parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type ++ // is read from the buffer as a single byte. ++ fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { ++ use bytes::Buf; ++ ( ++ match identifier { ++ COMPACT_IDENTIFIER_LEGACY => Self::Legacy, ++ COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930, ++ COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559, ++ COMPACT_EXTENDED_IDENTIFIER_FLAG => { ++ let extended_identifier = buf.get_u8(); ++ match extended_identifier { ++ L1_MESSAGE_TX_TYPE_ID => Self::L1Message, ++ _ => panic!("Unsupported TxType identifier: {extended_identifier}"), ++ } ++ } ++ _ => panic!("Unknown identifier for TxType: {identifier}"), ++ }, ++ buf, ++ ) ++ } ++} ++ ++impl Typed2718 for ScrollTxType { ++ fn ty(&self) -> u8 { ++ (*self).into() ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ extern crate alloc; ++ use alloc::{vec, vec::Vec}; ++ ++ #[test] ++ fn test_all_tx_types() { ++ assert_eq!(ScrollTxType::ALL.len(), 4); ++ let all = vec![ ++ ScrollTxType::Legacy, ++ ScrollTxType::Eip1559, ++ ScrollTxType::Eip2930, ++ ScrollTxType::L1Message, ++ ]; ++ assert_eq!(ScrollTxType::ALL.to_vec(), all); ++ } ++ ++ #[test] ++ fn tx_type_roundtrip() { ++ for &tx_type in &ScrollTxType::ALL { ++ let mut buf = Vec::new(); ++ tx_type.encode(&mut buf); ++ let decoded = ScrollTxType::decode(&mut &buf[..]).unwrap(); ++ assert_eq!(tx_type, decoded); ++ } ++ } ++}
@@ -2289,7 +2863,7 @@
@@ -2299,76 +2873,436 @@
-
+48
+
+408
-0
-
diff --git reth/crates/scroll/cli/src/commands.rs scroll-reth/crates/scroll/cli/src/commands.rs +
diff --git reth/crates/scroll/alloy/consensus/src/transaction/typed.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/typed.rs new file mode 100644 -index 0000000000000000000000000000000000000000..a80a2aa8bc782020eed92c1c4eb1991c6f55c8b8 +index 0000000000000000000000000000000000000000..273fa351be1b6591ff0970eb2cb28730517258c7 --- /dev/null -+++ scroll-reth/crates/scroll/cli/src/commands.rs -@@ -0,0 +1,48 @@ -+use crate::ScrollChainSpecParser; -+use clap::Subcommand; -+use reth_cli::chainspec::ChainSpecParser; -+use reth_cli_commands::{ -+ config_cmd, db, dump_genesis, import, init_cmd, init_state, node, node::NoArgs, p2p, prune, -+ recover, stage, -+}; -+use std::fmt; ++++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/typed.rs +@@ -0,0 +1,408 @@ ++use crate::{ScrollTxEnvelope, ScrollTxType, TxL1Message}; ++use alloy_consensus::{Transaction, TxEip1559, TxEip2930, TxLegacy, Typed2718}; ++use alloy_eips::eip2930::AccessList; ++use alloy_primitives::{Address, Bytes, TxKind}; ++use reth_codecs::{Compact, __private::bytes}; ++use reth_codecs_derive::generate_tests; ++ ++/// The `TypedTransaction` enum represents all Ethereum transaction request types, modified for ++/// Scroll ++/// ++/// Its variants correspond to specific allowed transactions: ++/// 1. `Legacy` (pre-EIP2718) [`TxLegacy`] ++/// 2. `EIP2930` (state access lists) [`TxEip2930`] ++/// 3. `EIP1559` [`TxEip1559`] ++/// 4. `L1Message` [`TxL1Message`] ++#[derive(Debug, Clone, PartialEq, Eq, Hash)] ++#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] ++#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr( ++ feature = "serde", ++ serde( ++ from = "serde_from::MaybeTaggedTypedTransaction", ++ into = "serde_from::TaggedTypedTransaction" ++ ) ++)] ++pub enum ScrollTypedTransaction { ++ /// Legacy transaction ++ Legacy(TxLegacy), ++ /// EIP-2930 transaction ++ Eip2930(TxEip2930), ++ /// EIP-1559 transaction ++ Eip1559(TxEip1559), ++ /// Scroll L1 message transaction ++ L1Message(TxL1Message), ++} + -+/// Commands to be executed -+#[derive(Debug, Subcommand)] -+pub enum Commands< -+ Spec: ChainSpecParser = ScrollChainSpecParser, -+ Ext: clap::Args + fmt::Debug = NoArgs, -+> { -+ /// Start the node -+ #[command(name = "node")] -+ Node(Box<node::NodeCommand<Spec, Ext>>), -+ /// Initialize the database from a genesis file. -+ #[command(name = "init")] -+ Init(init_cmd::InitCommand<Spec>), -+ /// Initialize the database from a state dump file. -+ #[command(name = "init-state")] -+ InitState(init_state::InitStateCommand<Spec>), -+ /// This syncs RLP encoded blocks from a file. -+ #[command(name = "import")] -+ Import(import::ImportCommand<Spec>), -+ /// Dumps genesis block JSON configuration to stdout. -+ DumpGenesis(dump_genesis::DumpGenesisCommand<Spec>), -+ /// Database debugging utilities -+ #[command(name = "db")] -+ Db(db::Command<Spec>), -+ /// Manipulate individual stages. -+ #[command(name = "stage")] -+ Stage(Box<stage::Command<Spec>>), -+ /// P2P Debugging utilities -+ #[command(name = "p2p")] -+ P2P(p2p::Command<Spec>), -+ /// Write config to stdout -+ #[command(name = "config")] -+ Config(config_cmd::Command), -+ /// Scripts for node recovery -+ #[command(name = "recover")] -+ Recover(recover::Command<Spec>), -+ /// Prune according to the configuration without any limits -+ #[command(name = "prune")] -+ Prune(prune::PruneCommand<Spec>), -+}
-
++impl From<TxLegacy> for ScrollTypedTransaction { ++ fn from(tx: TxLegacy) -> Self { ++ Self::Legacy(tx) ++ } ++} ++ ++impl From<TxEip2930> for ScrollTypedTransaction { ++ fn from(tx: TxEip2930) -> Self { ++ Self::Eip2930(tx) ++ } ++} ++ ++impl From<TxEip1559> for ScrollTypedTransaction { ++ fn from(tx: TxEip1559) -> Self { ++ Self::Eip1559(tx) ++ } ++} ++ ++impl From<TxL1Message> for ScrollTypedTransaction { ++ fn from(tx: TxL1Message) -> Self { ++ Self::L1Message(tx) ++ } ++} ++ ++impl From<ScrollTxEnvelope> for ScrollTypedTransaction { ++ fn from(envelope: ScrollTxEnvelope) -> Self { ++ match envelope { ++ ScrollTxEnvelope::Legacy(tx) => Self::Legacy(tx.strip_signature()), ++ ScrollTxEnvelope::Eip2930(tx) => Self::Eip2930(tx.strip_signature()), ++ ScrollTxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()), ++ ScrollTxEnvelope::L1Message(tx) => Self::L1Message(tx.into_inner()), ++ } ++ } ++} ++ ++impl ScrollTypedTransaction { ++ /// Return the [`ScrollTxType`] of the inner txn. ++ pub const fn tx_type(&self) -> ScrollTxType { ++ match self { ++ Self::Legacy(_) => ScrollTxType::Legacy, ++ Self::Eip2930(_) => ScrollTxType::Eip2930, ++ Self::Eip1559(_) => ScrollTxType::Eip1559, ++ Self::L1Message(_) => ScrollTxType::L1Message, ++ } ++ } ++ ++ /// Return the inner legacy transaction if it exists. ++ pub const fn legacy(&self) -> Option<&TxLegacy> { ++ match self { ++ Self::Legacy(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Return the inner EIP-2930 transaction if it exists. ++ pub const fn eip2930(&self) -> Option<&TxEip2930> { ++ match self { ++ Self::Eip2930(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Return the inner EIP-1559 transaction if it exists. ++ pub const fn eip1559(&self) -> Option<&TxEip1559> { ++ match self { ++ Self::Eip1559(tx) => Some(tx), ++ _ => None, ++ } ++ } ++ ++ /// Return the inner l1 message if it exists. ++ pub const fn l1_message(&self) -> Option<&TxL1Message> { ++ match self { ++ Self::L1Message(tx) => Some(tx), ++ _ => None, ++ } ++ } ++} ++ ++impl Typed2718 for ScrollTypedTransaction { ++ fn ty(&self) -> u8 { ++ match self { ++ Self::Legacy(_) => ScrollTxType::Legacy as u8, ++ Self::Eip2930(_) => ScrollTxType::Eip2930 as u8, ++ Self::Eip1559(_) => ScrollTxType::Eip1559 as u8, ++ Self::L1Message(_) => ScrollTxType::L1Message as u8, ++ } ++ } ++} ++ ++impl Transaction for ScrollTypedTransaction { ++ fn chain_id(&self) -> Option<alloy_primitives::ChainId> { ++ match self { ++ Self::Legacy(tx) => tx.chain_id(), ++ Self::Eip2930(tx) => tx.chain_id(), ++ Self::Eip1559(tx) => tx.chain_id(), ++ Self::L1Message(tx) => tx.chain_id(), ++ } ++ } ++ ++ fn nonce(&self) -> u64 { ++ match self { ++ Self::Legacy(tx) => tx.nonce(), ++ Self::Eip2930(tx) => tx.nonce(), ++ Self::Eip1559(tx) => tx.nonce(), ++ Self::L1Message(tx) => tx.nonce(), ++ } ++ } ++ ++ fn gas_limit(&self) -> u64 { ++ match self { ++ Self::Legacy(tx) => tx.gas_limit(), ++ Self::Eip2930(tx) => tx.gas_limit(), ++ Self::Eip1559(tx) => tx.gas_limit(), ++ Self::L1Message(tx) => tx.gas_limit(), ++ } ++ } ++ ++ fn gas_price(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.gas_price(), ++ Self::Eip2930(tx) => tx.gas_price(), ++ Self::Eip1559(tx) => tx.gas_price(), ++ Self::L1Message(tx) => tx.gas_price(), ++ } ++ } ++ ++ fn max_fee_per_gas(&self) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.max_fee_per_gas(), ++ Self::Eip2930(tx) => tx.max_fee_per_gas(), ++ Self::Eip1559(tx) => tx.max_fee_per_gas(), ++ Self::L1Message(tx) => tx.max_fee_per_gas(), ++ } ++ } ++ ++ fn max_priority_fee_per_gas(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.max_priority_fee_per_gas(), ++ Self::Eip2930(tx) => tx.max_priority_fee_per_gas(), ++ Self::Eip1559(tx) => tx.max_priority_fee_per_gas(), ++ Self::L1Message(tx) => tx.max_priority_fee_per_gas(), ++ } ++ } ++ ++ fn max_fee_per_blob_gas(&self) -> Option<u128> { ++ match self { ++ Self::Legacy(tx) => tx.max_fee_per_blob_gas(), ++ Self::Eip2930(tx) => tx.max_fee_per_blob_gas(), ++ Self::Eip1559(tx) => tx.max_fee_per_blob_gas(), ++ Self::L1Message(tx) => tx.max_fee_per_blob_gas(), ++ } ++ } ++ ++ fn priority_fee_or_price(&self) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.priority_fee_or_price(), ++ Self::Eip2930(tx) => tx.priority_fee_or_price(), ++ Self::Eip1559(tx) => tx.priority_fee_or_price(), ++ Self::L1Message(tx) => tx.priority_fee_or_price(), ++ } ++ } ++ ++ fn to(&self) -> Option<Address> { ++ match self { ++ Self::Legacy(tx) => tx.to(), ++ Self::Eip2930(tx) => tx.to(), ++ Self::Eip1559(tx) => tx.to(), ++ Self::L1Message(tx) => tx.to(), ++ } ++ } ++ ++ fn kind(&self) -> TxKind { ++ match self { ++ Self::Legacy(tx) => tx.kind(), ++ Self::Eip2930(tx) => tx.kind(), ++ Self::Eip1559(tx) => tx.kind(), ++ Self::L1Message(tx) => tx.kind(), ++ } ++ } ++ ++ fn value(&self) -> alloy_primitives::U256 { ++ match self { ++ Self::Legacy(tx) => tx.value(), ++ Self::Eip2930(tx) => tx.value(), ++ Self::Eip1559(tx) => tx.value(), ++ Self::L1Message(tx) => tx.value(), ++ } ++ } ++ ++ fn input(&self) -> &Bytes { ++ match self { ++ Self::Legacy(tx) => tx.input(), ++ Self::Eip2930(tx) => tx.input(), ++ Self::Eip1559(tx) => tx.input(), ++ Self::L1Message(tx) => tx.input(), ++ } ++ } ++ ++ fn access_list(&self) -> Option<&AccessList> { ++ match self { ++ Self::Legacy(tx) => tx.access_list(), ++ Self::Eip2930(tx) => tx.access_list(), ++ Self::Eip1559(tx) => tx.access_list(), ++ Self::L1Message(tx) => tx.access_list(), ++ } ++ } ++ ++ fn blob_versioned_hashes(&self) -> Option<&[alloy_primitives::B256]> { ++ match self { ++ Self::Legacy(tx) => tx.blob_versioned_hashes(), ++ Self::Eip2930(tx) => tx.blob_versioned_hashes(), ++ Self::Eip1559(tx) => tx.blob_versioned_hashes(), ++ Self::L1Message(tx) => tx.blob_versioned_hashes(), ++ } ++ } ++ ++ fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { ++ match self { ++ Self::Legacy(tx) => tx.authorization_list(), ++ Self::Eip2930(tx) => tx.authorization_list(), ++ Self::Eip1559(tx) => tx.authorization_list(), ++ Self::L1Message(tx) => tx.authorization_list(), ++ } ++ } ++ ++ fn is_dynamic_fee(&self) -> bool { ++ match self { ++ Self::Legacy(tx) => tx.is_dynamic_fee(), ++ Self::Eip2930(tx) => tx.is_dynamic_fee(), ++ Self::Eip1559(tx) => tx.is_dynamic_fee(), ++ Self::L1Message(tx) => tx.is_dynamic_fee(), ++ } ++ } ++ ++ fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { ++ match self { ++ Self::Legacy(tx) => tx.effective_gas_price(base_fee), ++ Self::Eip2930(tx) => tx.effective_gas_price(base_fee), ++ Self::Eip1559(tx) => tx.effective_gas_price(base_fee), ++ Self::L1Message(tx) => tx.effective_gas_price(base_fee), ++ } ++ } ++ ++ fn is_create(&self) -> bool { ++ match self { ++ Self::Legacy(tx) => tx.is_create(), ++ Self::Eip2930(tx) => tx.is_create(), ++ Self::Eip1559(tx) => tx.is_create(), ++ Self::L1Message(tx) => tx.is_create(), ++ } ++ } ++} ++ ++impl Compact for ScrollTypedTransaction { ++ fn to_compact<B>(&self, out: &mut B) -> usize ++ where ++ B: bytes::BufMut + AsMut<[u8]>, ++ { ++ let identifier = self.tx_type().to_compact(out); ++ match self { ++ Self::Legacy(tx) => tx.to_compact(out), ++ Self::Eip2930(tx) => tx.to_compact(out), ++ Self::Eip1559(tx) => tx.to_compact(out), ++ Self::L1Message(tx) => tx.to_compact(out), ++ }; ++ identifier ++ } ++ ++ fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { ++ let (tx_type, buf) = ScrollTxType::from_compact(buf, identifier); ++ match tx_type { ++ ScrollTxType::Legacy => { ++ let (tx, buf) = Compact::from_compact(buf, buf.len()); ++ (Self::Legacy(tx), buf) ++ } ++ ScrollTxType::Eip2930 => { ++ let (tx, buf) = Compact::from_compact(buf, buf.len()); ++ (Self::Eip2930(tx), buf) ++ } ++ ScrollTxType::Eip1559 => { ++ let (tx, buf) = Compact::from_compact(buf, buf.len()); ++ (Self::Eip1559(tx), buf) ++ } ++ ScrollTxType::L1Message => { ++ let (tx, buf) = Compact::from_compact(buf, buf.len()); ++ (Self::L1Message(tx), buf) ++ } ++ } ++ } ++} ++ ++generate_tests!( ++ #[compact] ++ ScrollTypedTransaction, ++ ScrollTypedTransactionTests ++); ++ ++#[cfg(feature = "serde")] ++mod serde_from { ++ //! NB: Why do we need this? ++ //! ++ //! Because the tag may be missing, we need an abstraction over tagged (with ++ //! type) and untagged (always legacy). This is ++ //! [`MaybeTaggedTypedTransaction`]. ++ //! ++ //! The tagged variant is [`TaggedTypedTransaction`], which always has a ++ //! type tag. ++ //! ++ //! We serialize via [`TaggedTypedTransaction`] and deserialize via ++ //! [`MaybeTaggedTypedTransaction`]. ++ use super::*; ++ ++ #[derive(Debug, serde::Deserialize)] ++ #[serde(untagged)] ++ pub(crate) enum MaybeTaggedTypedTransaction { ++ Tagged(TaggedTypedTransaction), ++ Untagged(TxLegacy), ++ } ++ ++ #[derive(Debug, serde::Serialize, serde::Deserialize)] ++ #[serde(tag = "type")] ++ pub(crate) enum TaggedTypedTransaction { ++ /// `Legacy` transaction ++ #[serde(rename = "0x00", alias = "0x0")] ++ Legacy(TxLegacy), ++ /// `EIP-2930` transaction ++ #[serde(rename = "0x01", alias = "0x1")] ++ Eip2930(TxEip2930), ++ /// `EIP-1559` transaction ++ #[serde(rename = "0x02", alias = "0x2")] ++ Eip1559(TxEip1559), ++ /// `L1Message` transaction ++ #[serde( ++ rename = "0x7e", ++ alias = "0x7E", ++ serialize_with = "crate::serde_l1_message_tx_rpc" ++ )] ++ L1Message(TxL1Message), ++ } ++ ++ impl From<MaybeTaggedTypedTransaction> for ScrollTypedTransaction { ++ fn from(value: MaybeTaggedTypedTransaction) -> Self { ++ match value { ++ MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(), ++ MaybeTaggedTypedTransaction::Untagged(tx) => Self::Legacy(tx), ++ } ++ } ++ } ++ ++ impl From<TaggedTypedTransaction> for ScrollTypedTransaction { ++ fn from(value: TaggedTypedTransaction) -> Self { ++ match value { ++ TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed), ++ TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), ++ TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), ++ TaggedTypedTransaction::L1Message(tx) => Self::L1Message(tx), ++ } ++ } ++ } ++ ++ impl From<ScrollTypedTransaction> for TaggedTypedTransaction { ++ fn from(value: ScrollTypedTransaction) -> Self { ++ match value { ++ ScrollTypedTransaction::Legacy(signed) => Self::Legacy(signed), ++ ScrollTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), ++ ScrollTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), ++ ScrollTypedTransaction::L1Message(tx) => Self::L1Message(tx), ++ } ++ } ++ } ++} +
@@ -2383,7 +3317,7 @@
@@ -2393,186 +3327,105 @@
-
+158
+
+23
-0
-
diff --git reth/crates/scroll/cli/src/lib.rs scroll-reth/crates/scroll/cli/src/lib.rs +
diff --git reth/crates/scroll/alloy/network/Cargo.toml scroll-reth/crates/scroll/alloy/network/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..d70b94fc90c788984292d63472aeb3d011d6b0ae +index 0000000000000000000000000000000000000000..ae1ada97ade9cf5710d59b5b8519b2638e13c00a --- /dev/null -+++ scroll-reth/crates/scroll/cli/src/lib.rs -@@ -0,0 +1,158 @@ -+//! Scroll CLI implementation. -+#![cfg(all(feature = "scroll", not(feature = "optimism")))] -+ -+mod args; -+pub use args::ScrollRollupArgs; ++++ scroll-reth/crates/scroll/alloy/network/Cargo.toml +@@ -0,0 +1,23 @@ ++[package] ++name = "scroll-alloy-network" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+mod commands; -+pub use commands::Commands; ++[lints] ++workspace = true + -+mod spec; -+pub use spec::ScrollChainSpecParser; ++[dependencies] ++scroll-alloy-consensus.workspace = true ++scroll-alloy-rpc-types.workspace = true + -+use clap::{value_parser, Parser}; -+use reth_cli::chainspec::ChainSpecParser; -+use reth_cli_commands::{common::CliNodeTypes, node::NoArgs}; -+use reth_cli_runner::CliRunner; -+use reth_db::DatabaseEnv; -+use reth_eth_wire::EthNetworkPrimitives; -+use reth_node_builder::{NodeBuilder, WithLaunchContext}; -+use reth_node_core::{ -+ args::LogArgs, -+ version::{LONG_VERSION, SHORT_VERSION}, -+}; -+use reth_node_metrics::recorder::install_prometheus_recorder; -+use reth_primitives::EthPrimitives; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_scroll_evm::ScrollExecutorProvider; -+use reth_tracing::FileWorkerGuard; -+use std::{ffi::OsString, fmt, future::Future, sync::Arc}; -+use tracing::info; ++# alloy ++alloy-consensus.workspace = true ++alloy-network.workspace = true ++alloy-primitives.workspace = true ++alloy-rpc-types-eth.workspace = true ++alloy-signer.workspace = true
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+8
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/alloy/network/README.md scroll-reth/crates/scroll/alloy/network/README.md +new file mode 100644 +index 0000000000000000000000000000000000000000..da1f2fd66158a70e84a5a764eb413aea6be31f8f +--- /dev/null ++++ scroll-reth/crates/scroll/alloy/network/README.md +@@ -0,0 +1,8 @@ ++# scroll-alloy-network + -+/// The main scroll cli interface. -+/// -+/// This is the entrypoint to the executable. -+#[derive(Debug, Parser)] -+#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Scroll Reth", long_about = None -+)] -+pub struct Cli<Spec: ChainSpecParser = ScrollChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs> -+{ -+ /// The command to run -+ #[command(subcommand)] -+ command: Commands<Spec, Ext>, ++Scroll blockchain RPC behavior abstraction. + -+ /// The chain this node is running. -+ /// -+ /// Possible values are either a built-in chain or the path to a chain specification file. -+ #[arg( -+ long, -+ value_name = "CHAIN_OR_PATH", -+ long_help = Spec::help_message(), -+ default_value = Spec::SUPPORTED_CHAINS[0], -+ value_parser = Spec::parser(), -+ global = true, -+ )] -+ chain: Arc<Spec::ChainSpec>, -+ -+ /// Add a new instance of a node. -+ /// -+ /// Configures the ports of the node to avoid conflicts with the defaults. -+ /// This is useful for running multiple nodes on the same machine. -+ /// -+ /// Max number of instances is 200. It is chosen in a way so that it's not possible to have -+ /// port numbers that conflict with each other. -+ /// -+ /// Changes to the following port numbers: -+ /// - `DISCOVERY_PORT`: default + `instance` - 1 -+ /// - `AUTH_PORT`: default + `instance` * 100 - 100 -+ /// - `HTTP_RPC_PORT`: default - `instance` + 1 -+ /// - `WS_RPC_PORT`: default + `instance` * 2 - 2 -+ #[arg(long, value_name = "INSTANCE", global = true, default_value_t = 1, value_parser = value_parser!(u16).range(..=200) -+ )] -+ instance: u16, -+ -+ #[command(flatten)] -+ logs: LogArgs, -+} -+ -+impl Cli { -+ /// Parsers only the default CLI arguments -+ pub fn parse_args() -> Self { -+ Self::parse() -+ } -+ -+ /// Parsers only the default CLI arguments from the given iterator -+ pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error> -+ where -+ I: IntoIterator<Item = T>, -+ T: Into<OsString> + Clone, -+ { -+ Self::try_parse_from(itr) -+ } -+} -+ -+impl<C, Ext> Cli<C, Ext> -+where -+ C: ChainSpecParser<ChainSpec = ScrollChainSpec>, -+ Ext: clap::Args + fmt::Debug, -+{ -+ /// Execute the configured cli command. -+ /// -+ /// This accepts a closure that is used to launch the node via the -+ /// [`NodeCommand`](reth_cli_commands::node::NodeCommand). -+ pub fn run<L, Fut, Types>(mut self, launcher: L) -> eyre::Result<()> -+ where -+ L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut, -+ Fut: Future<Output = eyre::Result<()>>, -+ Types: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>, -+ { -+ // add network name to logs dir -+ self.logs.log_file_directory = -+ self.logs.log_file_directory.join(self.chain.chain().to_string()); -+ -+ let _guard = self.init_tracing()?; -+ info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory); -+ -+ // Install the prometheus recorder to be sure to record all metrics -+ let _ = install_prometheus_recorder(); -+ -+ let runner = CliRunner::default(); -+ match self.command { -+ Commands::Node(command) => { -+ runner.run_command_until_exit(|ctx| command.execute(ctx, launcher)) -+ } -+ Commands::Init(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), -+ Commands::InitState(command) => { -+ runner.run_blocking_until_ctrl_c(command.execute::<Types>()) -+ } -+ Commands::Import(command) => runner.run_blocking_until_ctrl_c( -+ command.execute::<Types, _, _>(ScrollExecutorProvider::scroll), -+ ), -+ Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()), -+ Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), -+ Commands::Stage(command) => runner.run_command_until_exit(|ctx| { -+ command.execute::<Types, _, _, EthNetworkPrimitives>( -+ ctx, -+ ScrollExecutorProvider::scroll, -+ ) -+ }), -+ Commands::P2P(command) => { -+ runner.run_until_ctrl_c(command.execute::<EthNetworkPrimitives>()) -+ } -+ Commands::Config(command) => runner.run_until_ctrl_c(command.execute()), -+ Commands::Recover(command) => { -+ runner.run_command_until_exit(|ctx| command.execute::<Types>(ctx)) -+ } -+ Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<Types>()), -+ } -+ } -+ -+ /// Initializes tracing with the configured options. -+ /// -+ /// If file logging is enabled, this function returns a guard that must be kept alive to ensure -+ /// that all logs are flushed to disk. -+ pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> { -+ let guard = self.logs.init_tracing()?; -+ Ok(guard) -+ } -+}
++This crate contains a simple abstraction of the RPC behavior of an ++Scroll blockchain. It is intended to be used by the Alloy client to ++provide a consistent interface to the rest of the library, regardless of ++changes the underlying blockchain makes to the RPC interface.
@@ -2587,7 +3440,7 @@
@@ -2597,37 +3450,244 @@
-
+21
+
+228
-0
-
diff --git reth/crates/scroll/cli/src/spec.rs scroll-reth/crates/scroll/cli/src/spec.rs +
diff --git reth/crates/scroll/alloy/network/src/lib.rs scroll-reth/crates/scroll/alloy/network/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..361029d9d1fc6101bd9f15b0d01b824a3aff04ec +index 0000000000000000000000000000000000000000..e0a171d82e04b43b29c476a7f15d0aea9a584cb2 --- /dev/null -+++ scroll-reth/crates/scroll/cli/src/spec.rs -@@ -0,0 +1,21 @@ -+use reth_cli::chainspec::{parse_genesis, ChainSpecParser}; -+use reth_scroll_chainspec::{ScrollChainSpec, SCROLL_DEV, SCROLL_MAINNET, SCROLL_SEPOLIA}; -+use std::sync::Arc; ++++ scroll-reth/crates/scroll/alloy/network/src/lib.rs +@@ -0,0 +1,228 @@ ++#![doc = include_str!("../README.md")] ++#![cfg_attr(not(test), warn(unused_crate_dependencies))] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + -+/// The parser for the Scroll chain specification. -+#[derive(Debug, Clone)] -+pub struct ScrollChainSpecParser; ++use alloy_consensus::{TxEnvelope, TxType, TypedTransaction}; ++pub use alloy_network::*; ++use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256}; ++use alloy_rpc_types_eth::AccessList; ++use scroll_alloy_consensus::{self, ScrollTxEnvelope, ScrollTxType, ScrollTypedTransaction}; ++use scroll_alloy_rpc_types::ScrollTransactionRequest; ++ ++/// Types for an Scroll-stack network. ++#[derive(Clone, Copy, Debug)] ++pub struct Scroll { ++ _private: (), ++} + -+impl ChainSpecParser for ScrollChainSpecParser { -+ type ChainSpec = ScrollChainSpec; -+ const SUPPORTED_CHAINS: &'static [&'static str] = &["dev", "scroll-mainnet", "scroll-sepolia"]; ++impl Network for Scroll { ++ type TxType = ScrollTxType; + -+ fn parse(s: &str) -> eyre::Result<Arc<Self::ChainSpec>> { -+ Ok(match s { -+ "dev" => SCROLL_DEV.clone(), -+ "scroll-mainnet" => SCROLL_MAINNET.clone(), -+ "scroll-sepolia" => SCROLL_SEPOLIA.clone(), -+ _ => Arc::new(parse_genesis(s)?.into()), ++ type TxEnvelope = scroll_alloy_consensus::ScrollTxEnvelope; ++ ++ type UnsignedTx = scroll_alloy_consensus::ScrollTypedTransaction; ++ ++ type ReceiptEnvelope = scroll_alloy_consensus::ScrollReceiptEnvelope; ++ ++ type Header = alloy_consensus::Header; ++ ++ type TransactionRequest = scroll_alloy_rpc_types::ScrollTransactionRequest; ++ ++ type TransactionResponse = scroll_alloy_rpc_types::Transaction; ++ ++ type ReceiptResponse = scroll_alloy_rpc_types::ScrollTransactionReceipt; ++ ++ type HeaderResponse = alloy_rpc_types_eth::Header; ++ ++ type BlockResponse = ++ alloy_rpc_types_eth::Block<Self::TransactionResponse, Self::HeaderResponse>; ++} ++ ++impl TransactionBuilder<Scroll> for ScrollTransactionRequest { ++ fn chain_id(&self) -> Option<ChainId> { ++ self.as_ref().chain_id() ++ } ++ ++ fn set_chain_id(&mut self, chain_id: ChainId) { ++ self.as_mut().set_chain_id(chain_id); ++ } ++ ++ fn nonce(&self) -> Option<u64> { ++ self.as_ref().nonce() ++ } ++ ++ fn set_nonce(&mut self, nonce: u64) { ++ self.as_mut().set_nonce(nonce); ++ } ++ ++ fn input(&self) -> Option<&Bytes> { ++ self.as_ref().input() ++ } ++ ++ fn set_input<T: Into<Bytes>>(&mut self, input: T) { ++ self.as_mut().set_input(input); ++ } ++ ++ fn from(&self) -> Option<Address> { ++ self.as_ref().from() ++ } ++ ++ fn set_from(&mut self, from: Address) { ++ self.as_mut().set_from(from); ++ } ++ ++ fn kind(&self) -> Option<TxKind> { ++ self.as_ref().kind() ++ } ++ ++ fn clear_kind(&mut self) { ++ self.as_mut().clear_kind(); ++ } ++ ++ fn set_kind(&mut self, kind: TxKind) { ++ self.as_mut().set_kind(kind); ++ } ++ ++ fn value(&self) -> Option<U256> { ++ self.as_ref().value() ++ } ++ ++ fn set_value(&mut self, value: U256) { ++ self.as_mut().set_value(value); ++ } ++ ++ fn gas_price(&self) -> Option<u128> { ++ self.as_ref().gas_price() ++ } ++ ++ fn set_gas_price(&mut self, gas_price: u128) { ++ self.as_mut().set_gas_price(gas_price); ++ } ++ ++ fn max_fee_per_gas(&self) -> Option<u128> { ++ self.as_ref().max_fee_per_gas() ++ } ++ ++ fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) { ++ self.as_mut().set_max_fee_per_gas(max_fee_per_gas); ++ } ++ ++ fn max_priority_fee_per_gas(&self) -> Option<u128> { ++ self.as_ref().max_priority_fee_per_gas() ++ } ++ ++ fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) { ++ self.as_mut().set_max_priority_fee_per_gas(max_priority_fee_per_gas); ++ } ++ ++ fn gas_limit(&self) -> Option<u64> { ++ self.as_ref().gas_limit() ++ } ++ ++ fn set_gas_limit(&mut self, gas_limit: u64) { ++ self.as_mut().set_gas_limit(gas_limit); ++ } ++ ++ fn access_list(&self) -> Option<&AccessList> { ++ self.as_ref().access_list() ++ } ++ ++ fn set_access_list(&mut self, access_list: AccessList) { ++ self.as_mut().set_access_list(access_list); ++ } ++ ++ fn complete_type(&self, ty: ScrollTxType) -> Result<(), Vec<&'static str>> { ++ match ty { ++ ScrollTxType::L1Message => Err(vec!["not implemented for L1 message tx"]), ++ _ => { ++ let ty = TxType::try_from(ty as u8).unwrap(); ++ self.as_ref().complete_type(ty) ++ } ++ } ++ } ++ ++ fn can_submit(&self) -> bool { ++ self.as_ref().can_submit() ++ } ++ ++ fn can_build(&self) -> bool { ++ self.as_ref().can_build() ++ } ++ ++ #[doc(alias = "output_transaction_type")] ++ fn output_tx_type(&self) -> ScrollTxType { ++ match self.as_ref().preferred_type() { ++ TxType::Eip1559 | TxType::Eip4844 => ScrollTxType::Eip1559, ++ TxType::Eip2930 => ScrollTxType::Eip2930, ++ TxType::Legacy => ScrollTxType::Legacy, ++ TxType::Eip7702 => unimplemented!(), ++ } ++ } ++ ++ #[doc(alias = "output_transaction_type_checked")] ++ fn output_tx_type_checked(&self) -> Option<ScrollTxType> { ++ self.as_ref().buildable_type().map(|tx_ty| match tx_ty { ++ TxType::Eip1559 | TxType::Eip4844 => ScrollTxType::Eip1559, ++ TxType::Eip2930 => ScrollTxType::Eip2930, ++ TxType::Legacy => ScrollTxType::Legacy, ++ TxType::Eip7702 => unimplemented!(), ++ }) ++ } ++ ++ fn prep_for_submission(&mut self) { ++ self.as_mut().prep_for_submission(); ++ } ++ ++ fn build_unsigned(self) -> BuildResult<ScrollTypedTransaction, Scroll> { ++ if let Err((tx_type, missing)) = self.as_ref().missing_keys() { ++ let tx_type = ScrollTxType::try_from(tx_type as u8).unwrap(); ++ return Err(TransactionBuilderError::InvalidTransactionRequest(tx_type, missing) ++ .into_unbuilt(self)); ++ } ++ Ok(self.build_typed_tx().expect("checked by missing_keys")) ++ } ++ ++ async fn build<W: NetworkWallet<Scroll>>( ++ self, ++ wallet: &W, ++ ) -> Result<<Scroll as Network>::TxEnvelope, TransactionBuilderError<Scroll>> { ++ Ok(wallet.sign_request(self).await?) ++ } ++} ++ ++impl NetworkWallet<Scroll> for EthereumWallet { ++ fn default_signer_address(&self) -> Address { ++ NetworkWallet::<Ethereum>::default_signer_address(self) ++ } ++ ++ fn has_signer_for(&self, address: &Address) -> bool { ++ NetworkWallet::<Ethereum>::has_signer_for(self, address) ++ } ++ ++ fn signer_addresses(&self) -> impl Iterator<Item = Address> { ++ NetworkWallet::<Ethereum>::signer_addresses(self) ++ } ++ ++ async fn sign_transaction_from( ++ &self, ++ sender: Address, ++ tx: ScrollTypedTransaction, ++ ) -> alloy_signer::Result<ScrollTxEnvelope> { ++ let tx = match tx { ++ ScrollTypedTransaction::Legacy(tx) => TypedTransaction::Legacy(tx), ++ ScrollTypedTransaction::Eip2930(tx) => TypedTransaction::Eip2930(tx), ++ ScrollTypedTransaction::Eip1559(tx) => TypedTransaction::Eip1559(tx), ++ ScrollTypedTransaction::L1Message(_) => { ++ return Err(alloy_signer::Error::other("not implemented for deposit tx")) ++ } ++ }; ++ let tx = NetworkWallet::<Ethereum>::sign_transaction_from(self, sender, tx).await?; ++ ++ Ok(match tx { ++ TxEnvelope::Eip1559(tx) => ScrollTxEnvelope::Eip1559(tx), ++ TxEnvelope::Eip2930(tx) => ScrollTxEnvelope::Eip2930(tx), ++ TxEnvelope::Legacy(tx) => ScrollTxEnvelope::Legacy(tx), ++ _ => unreachable!(), + }) + } +}
@@ -2637,9 +3697,9 @@
@@ -2664,21 +3724,21 @@
-
+22
+
+60
-0
-
diff --git reth/crates/scroll/consensus/Cargo.toml scroll-reth/crates/scroll/consensus/Cargo.toml +
diff --git reth/crates/scroll/alloy/rpc-types/Cargo.toml scroll-reth/crates/scroll/alloy/rpc-types/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..beec5c27120ddd162cd303f68f127d8b0946d452 +index 0000000000000000000000000000000000000000..e5f664d0e28b4d1d7f6ae4fc20e15c9c1d7231be --- /dev/null -+++ scroll-reth/crates/scroll/consensus/Cargo.toml -@@ -0,0 +1,22 @@ ++++ scroll-reth/crates/scroll/alloy/rpc-types/Cargo.toml +@@ -0,0 +1,60 @@ +[package] -+name = "reth-scroll-consensus" ++name = "scroll-alloy-rpc-types" +version.workspace = true +edition.workspace = true +rust-version.workspace = true @@ -2691,23 +3751,61 @@ +workspace = true + +[dependencies] -+# revm -+revm.workspace = true ++scroll-alloy-consensus = { workspace = true, features = ["serde"] } ++alloy-consensus = { version = "0.9.2", default-features = false } ++alloy-eips = { version = "0.9.2", features = ["serde"], default-features = false } ++alloy-network-primitives = { version = "0.9.2", default-features = false } ++alloy-primitives = { version = "0.8.12", features = ["map", "rlp", "serde"], default-features = false } ++alloy-rpc-types-eth = { version = "0.9.2", features = ["serde"], default-features = false } ++alloy-serde = { version = "0.9.2", default-features = false } ++arbitrary = { version = "1.4", features = ["derive", "derive"], optional = true } ++derive_more = { version = "1.0", default-features = false } ++serde = { version = "1.0", features = ["derive", "alloc", "derive"], default-features = false } ++serde_json = { version = "1.0", features = ["alloc"], default-features = false } + +[dev-dependencies] -+eyre.workspace = true ++similar-asserts = "1.6" ++alloy-consensus = { version = "0.9.2", features = ["arbitrary"], default-features = false } ++alloy-primitives = { version = "0.8.12", features = ["arbitrary"], default-features = false } ++alloy-rpc-types-eth = { version = "0.9.2", features = ["arbitrary"], default-features = false } ++arbitrary = { version = "1.4", features = ["derive", "derive"] } + +[features] -+scroll = ["revm/scroll"]
++arbitrary = [ ++ "std", ++ "dep:arbitrary", ++ "alloy-primitives/arbitrary", ++ "alloy-rpc-types-eth/arbitrary", ++ "scroll-alloy-consensus/arbitrary", ++ "alloy-consensus/arbitrary", ++ "alloy-eips/arbitrary", ++ "alloy-serde/arbitrary", ++] ++default = ["std"] ++k256 = [ ++ "alloy-rpc-types-eth/k256", ++ "scroll-alloy-consensus/k256" ++] ++std = [ ++ "alloy-network-primitives/std", ++ "alloy-eips/std", ++ "alloy-primitives/std", ++ "alloy-rpc-types-eth/std", ++ "alloy-consensus/std", ++ "alloy-serde/std", ++ "derive_more/std", ++ "serde/std", ++ "serde_json/std", ++]
@@ -2722,7 +3820,7 @@
@@ -2732,223 +3830,31 @@
-
+195
+
+3
-0
-
diff --git reth/crates/scroll/consensus/src/curie.rs scroll-reth/crates/scroll/consensus/src/curie.rs +
diff --git reth/crates/scroll/alloy/rpc-types/README.md scroll-reth/crates/scroll/alloy/rpc-types/README.md new file mode 100644 -index 0000000000000000000000000000000000000000..ecbe26f3bce68b768375139c0f1cc493ddc242a0 +index 0000000000000000000000000000000000000000..963a99c4b23daddceeb49feb55a579509ed91ac1 --- /dev/null -+++ scroll-reth/crates/scroll/consensus/src/curie.rs -@@ -0,0 +1,195 @@ -+//! Curie fork transition for Scroll. -+//! -+//! On block 7096836, Scroll performed a transition to the Curie fork state, which brought various -+//! changes to the protocol: -+//! 1. Fee reduction cost thanks to the use of compressed blobs on the L1. -+//! 2. Modified [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) pricing -+//! model along with support for EIP-1559 and EIP-2930 transactions. -+//! 3. Support for `MLOAD`, `TLOAD` and `MCOPY` ([EIP-1153](https://eips.ethereum.org/EIPS/eip-1153) -+//! and [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656)). -+//! 4. Dynamic block time. -+//! -+//! Compressed blobs allowed for more transactions to be stored in each blob, reducing the DA cost -+//! per transactions. Accordingly, the L1 gas oracle contract's bytecode was updated in order to -+//! reflect the update in the DA costs: -+//! - original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar`. -+//! - updated formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar`. -+//! -+//! More details on the Curie update: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade> -+ -+use revm::{ -+ db::states::StorageSlot, -+ primitives::{address, bytes, AccountInfo, Address, Bytecode, Bytes, U256}, -+ Database, State, -+}; -+ -+/// L1 gas price oracle address. -+/// <https://scrollscan.com/address/0x5300000000000000000000000000000000000002> -+pub const L1_GAS_PRICE_ORACLE_ADDRESS: Address = -+ address!("5300000000000000000000000000000000000002"); -+/// Bytecode of L1 gas price oracle at Curie transition. -+pub const CURIE_L1_GAS_PRICE_ORACLE_BYTECODE: Bytes = bytes!("608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033"); -+/// Storage update of L1 gas price oracle at Curie transition. -+pub const CURIE_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 4] = [ -+ (L1_BLOB_BASE_FEE_SLOT, INITIAL_L1_BLOB_BASE_FEE), -+ (COMMIT_SCALAR_SLOT, INITIAL_COMMIT_SCALAR), -+ (BLOB_SCALAR_SLOT, INITIAL_BLOB_SCALAR), -+ (IS_CURIE_SLOT, IS_CURIE), -+]; -+ -+/// L1 gas price oracle base fee slot. -+pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); -+/// L1 gas price oracle over head slot. -+pub const OVER_HEAD_SLOT: U256 = U256::from_limbs([2, 0, 0, 0]); -+/// L1 gas price oracle scalar slot. -+pub const SCALAR_SLOT: U256 = U256::from_limbs([3, 0, 0, 0]); -+ -+/// L1 gas price oracle blob base fee slot. Added in the Curie fork. -+pub const L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([5, 0, 0, 0]); -+/// L1 gas price oracle commit scalar slot. Added in the Curie fork. -+pub const COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6, 0, 0, 0]); -+/// L1 gas price oracle blob scalar slot. Added in the Curie fork. -+pub const BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7, 0, 0, 0]); -+/// L1 gas price oracle "is Curie" slot. Added in the Curie fork. -+pub const IS_CURIE_SLOT: U256 = U256::from_limbs([8, 0, 0, 0]); -+ -+/// The initial blob base fee used by the oracle contract. -+const INITIAL_L1_BLOB_BASE_FEE: U256 = U256::from_limbs([1, 0, 0, 0]); -+/// The initial commit scalar used by the oracle contract. -+const INITIAL_COMMIT_SCALAR: U256 = U256::from_limbs([230759955285, 0, 0, 0]); -+/// The initial blob scalar used by the oracle contract. -+const INITIAL_BLOB_SCALAR: U256 = U256::from_limbs([417565260, 0, 0, 0]); -+/// Curie slot is set to 1 (true) after the Curie block fork. -+const IS_CURIE: U256 = U256::from_limbs([1, 0, 0, 0]); -+ -+/// Applies the Scroll Curie hard fork to the state: -+/// - Updates the L1 oracle contract bytecode to reflect the DA cost reduction. -+/// - Sets the initial blob base fee, commit and blob scalar and sets the `isCurie` slot to 1 -+/// (true). -+pub fn apply_curie_hard_fork<DB: Database>(state: &mut State<DB>) -> Result<(), DB::Error> { -+ let oracle = state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; -+ -+ // compute the code hash -+ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); -+ let bytecode_len = bytecode.len(); -+ let code_hash = bytecode.hash_slow(); -+ -+ // get the old oracle account info -+ let old_oracle_info = oracle.account_info().unwrap_or_default(); -+ -+ // init new oracle account information -+ let new_oracle_info = -+ AccountInfo { code_size: bytecode_len, code_hash, code: Some(bytecode), ..old_oracle_info }; -+ -+ // init new storage -+ let new_storage = CURIE_L1_GAS_PRICE_ORACLE_STORAGE -+ .into_iter() -+ .map(|(slot, present_value)| { -+ ( -+ slot, -+ StorageSlot { -+ present_value, -+ previous_or_original_value: oracle.storage_slot(slot).unwrap_or_default(), -+ }, -+ ) -+ }) -+ .collect(); -+ -+ // create transition for oracle new account info and storage -+ let transition = oracle.change(new_oracle_info, new_storage); -+ -+ // add transition -+ if let Some(s) = state.transition_state.as_mut() { -+ s.add_transitions(vec![(L1_GAS_PRICE_ORACLE_ADDRESS, transition)]) -+ } -+ -+ Ok(()) -+} -+ -+#[cfg(test)] -+mod tests { -+ use crate::{ -+ apply_curie_hard_fork, -+ curie::{ -+ CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, CURIE_L1_GAS_PRICE_ORACLE_STORAGE, -+ L1_GAS_PRICE_ORACLE_ADDRESS, -+ }, -+ }; -+ use revm::{ -+ db::{ -+ states::{bundle_state::BundleRetention, plain_account::PlainStorage, StorageSlot}, -+ EmptyDB, -+ }, -+ primitives::{bytes, keccak256, AccountInfo, Bytecode, U256}, -+ Database, State, -+ }; -+ use std::str::FromStr; -+ -+ #[test] -+ fn test_apply_curie_fork() -> eyre::Result<()> { -+ // init state -+ let db = EmptyDB::new(); -+ let mut state = -+ State::builder().with_database(db).with_bundle_update().without_state_clear().build(); -+ -+ // oracle pre fork state -+ let bytecode_pre_fork = Bytecode::new_raw( bytes!("608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033")); -+ let oracle_pre_fork = AccountInfo { -+ code_size: bytecode_pre_fork.len(), -+ code_hash: bytecode_pre_fork.hash_slow(), -+ code: Some(bytecode_pre_fork), -+ ..Default::default() -+ }; -+ let oracle_storage_pre_fork = PlainStorage::from_iter([ -+ (U256::ZERO, U256::from_str("0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b")?), -+ (U256::from(1), U256::from(0x15f50e5e)), -+ (U256::from(2), U256::from(0x38)), -+ (U256::from(3), U256::from(0x3e95ba80)), -+ (U256::from(4), U256::from_str("0x5300000000000000000000000000000000000003")?), -+ ]); -+ state.insert_account_with_storage( -+ L1_GAS_PRICE_ORACLE_ADDRESS, -+ oracle_pre_fork.clone(), -+ oracle_storage_pre_fork.clone(), -+ ); -+ -+ // apply curie fork -+ apply_curie_hard_fork(&mut state)?; -+ -+ // merge transitions -+ state.merge_transitions(BundleRetention::Reverts); -+ let bundle = state.take_bundle(); -+ -+ // check oracle account info -+ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); -+ let code_hash = keccak256(&CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); -+ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); -+ let expected_oracle_info = AccountInfo { -+ code_size: CURIE_L1_GAS_PRICE_ORACLE_BYTECODE.len(), -+ code_hash, -+ code: Some(bytecode.clone()), -+ ..Default::default() -+ }; -+ -+ assert_eq!(oracle.original_info.unwrap(), oracle_pre_fork); -+ assert_eq!(oracle.info.unwrap(), expected_oracle_info); -+ -+ // check oracle storage changeset -+ let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); -+ storage.sort_by(|(a, _), (b, _)| a.cmp(b)); -+ for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { -+ assert_eq!(got.0, expected.0); -+ assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); -+ } -+ -+ // check oracle original storage -+ for (slot, value) in oracle_storage_pre_fork { -+ assert_eq!(state.storage(L1_GAS_PRICE_ORACLE_ADDRESS, slot)?, value) -+ } -+ -+ // check deployed contract -+ assert_eq!(bundle.contracts.get(&code_hash).unwrap().clone(), bytecode); ++++ scroll-reth/crates/scroll/alloy/rpc-types/README.md +@@ -0,0 +1,3 @@ ++# scroll-alloy-rpc-types + -+ Ok(()) -+ } -+}
++Scroll RPC-related types.
@@ -2963,7 +3869,7 @@
@@ -2973,39 +3879,42 @@
-
+11
+
+14
-0
-
diff --git reth/crates/scroll/consensus/src/lib.rs scroll-reth/crates/scroll/consensus/src/lib.rs +
diff --git reth/crates/scroll/alloy/rpc-types/src/lib.rs scroll-reth/crates/scroll/alloy/rpc-types/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..6d4e4562a5bdad13492ce646abb0db466428e409 +index 0000000000000000000000000000000000000000..3236795f79ae6fa5ee6b9ffba84a4806cd38af6d --- /dev/null -+++ scroll-reth/crates/scroll/consensus/src/lib.rs -@@ -0,0 +1,11 @@ -+//! Scroll consensus implementation. ++++ scroll-reth/crates/scroll/alloy/rpc-types/src/lib.rs +@@ -0,0 +1,14 @@ ++#![doc = include_str!("../README.md")] ++#![doc( ++ html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", ++ html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" ++)] ++#![cfg_attr(not(test), warn(unused_crate_dependencies))] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(not(any(test, feature = "std")), no_std)] + -+#![cfg(feature = "scroll")] ++mod receipt; ++pub use receipt::{ScrollTransactionReceipt, ScrollTransactionReceiptFields}; + -+mod curie; -+pub use curie::{ -+ apply_curie_hard_fork, BLOB_SCALAR_SLOT, COMMIT_SCALAR_SLOT, -+ CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, CURIE_L1_GAS_PRICE_ORACLE_STORAGE, IS_CURIE_SLOT, -+ L1_BASE_FEE_SLOT, L1_BLOB_BASE_FEE_SLOT, L1_GAS_PRICE_ORACLE_ADDRESS, OVER_HEAD_SLOT, -+ SCALAR_SLOT, -+};
++mod transaction; ++pub use transaction::{ScrollL1MessageTransactionFields, ScrollTransactionRequest, Transaction};
@@ -3020,7 +3929,7 @@
@@ -3030,69 +3939,208 @@
-
+39
+
+180
-0
-
diff --git reth/crates/scroll/engine/Cargo.toml scroll-reth/crates/scroll/engine/Cargo.toml +
diff --git reth/crates/scroll/alloy/rpc-types/src/receipt.rs scroll-reth/crates/scroll/alloy/rpc-types/src/receipt.rs new file mode 100644 -index 0000000000000000000000000000000000000000..ac1a29c93237be3b4ebaf05054443140faeee051 +index 0000000000000000000000000000000000000000..192f15c19a1a56df4274c64b1c2919683e0962c2 --- /dev/null -+++ scroll-reth/crates/scroll/engine/Cargo.toml -@@ -0,0 +1,40 @@ -+[package] -+name = "reth-scroll-engine" -+version.workspace = true -+edition.workspace = true -+rust-version.workspace = true -+license.workspace = true -+homepage.workspace = true -+repository.workspace = true -+exclude.workspace = true ++++ scroll-reth/crates/scroll/alloy/rpc-types/src/receipt.rs +@@ -0,0 +1,180 @@ ++//! Receipt types for RPC + -+[lints] -+workspace = true ++use alloy_consensus::{Receipt, ReceiptWithBloom}; ++use alloy_serde::OtherFields; ++use serde::{Deserialize, Serialize}; + -+[dependencies] -+# reth -+reth-primitives.workspace = true ++use scroll_alloy_consensus::ScrollReceiptEnvelope; + -+# scroll -+reth-scroll-chainspec.workspace = true -+reth-scroll-forks.workspace = true ++/// Scroll Transaction Receipt type ++#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] ++#[serde(rename_all = "camelCase")] ++#[doc(alias = "ScrollTxReceipt")] ++pub struct ScrollTransactionReceipt { ++ /// Regular eth transaction receipt including deposit receipts ++ #[serde(flatten)] ++ pub inner: ++ alloy_rpc_types_eth::TransactionReceipt<ScrollReceiptEnvelope<alloy_rpc_types_eth::Log>>, ++ /// L1 fee for the transaction. ++ #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] ++ pub l1_fee: Option<u128>, ++} + -+# alloy -+alloy-consensus.workspace = true -+alloy-eips.workspace = true -+alloy-rlp.workspace = true -+alloy-rpc-types-engine.workspace = true ++impl alloy_network_primitives::ReceiptResponse for ScrollTransactionReceipt { ++ fn contract_address(&self) -> Option<alloy_primitives::Address> { ++ self.inner.contract_address ++ } + -+[features] -+default = ["std"] -+std = [ -+ "reth-primitives/std", -+ "reth-scroll-chainspec/std", -+ "reth-scroll-forks/std", -+ "alloy-consensus/std", -+ "alloy-eips/std", -+ "alloy-rlp/std", -+ "alloy-rpc-types-engine/std" -+] -+scroll = ["reth-primitives/scroll"] -+optimism = ["reth-primitives/optimism"] -\ No newline at end of file
++ fn status(&self) -> bool { ++ self.inner.inner.status() ++ } ++ ++ fn block_hash(&self) -> Option<alloy_primitives::BlockHash> { ++ self.inner.block_hash ++ } ++ ++ fn block_number(&self) -> Option<u64> { ++ self.inner.block_number ++ } ++ ++ fn transaction_hash(&self) -> alloy_primitives::TxHash { ++ self.inner.transaction_hash ++ } ++ ++ fn transaction_index(&self) -> Option<u64> { ++ self.inner.transaction_index() ++ } ++ ++ fn gas_used(&self) -> u64 { ++ self.inner.gas_used() ++ } ++ ++ fn effective_gas_price(&self) -> u128 { ++ self.inner.effective_gas_price() ++ } ++ ++ fn blob_gas_used(&self) -> Option<u64> { ++ self.inner.blob_gas_used() ++ } ++ ++ fn blob_gas_price(&self) -> Option<u128> { ++ self.inner.blob_gas_price() ++ } ++ ++ fn from(&self) -> alloy_primitives::Address { ++ self.inner.from() ++ } ++ ++ fn to(&self) -> Option<alloy_primitives::Address> { ++ self.inner.to() ++ } ++ ++ fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { ++ self.inner.authorization_list() ++ } ++ ++ fn cumulative_gas_used(&self) -> u64 { ++ self.inner.cumulative_gas_used() ++ } ++ ++ fn state_root(&self) -> Option<alloy_primitives::B256> { ++ self.inner.state_root() ++ } ++} ++ ++/// Additional fields for Scroll transaction receipts: <https://github.com/scroll-tech/go-ethereum/blob/develop/core/types/receipt.go#L78> ++#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] ++#[serde(rename_all = "camelCase")] ++#[doc(alias = "ScrollTxReceiptFields")] ++pub struct ScrollTransactionReceiptFields { ++ /// L1 fee for the transaction. ++ #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] ++ pub l1_fee: Option<u128>, ++} ++ ++impl From<ScrollTransactionReceiptFields> for OtherFields { ++ fn from(value: ScrollTransactionReceiptFields) -> Self { ++ serde_json::to_value(value).unwrap().try_into().unwrap() ++ } ++} ++ ++impl From<ScrollTransactionReceipt> for ScrollReceiptEnvelope<alloy_primitives::Log> { ++ fn from(value: ScrollTransactionReceipt) -> Self { ++ let inner_envelope = value.inner.inner; ++ ++ /// Helper function to convert the inner logs within a [`ReceiptWithBloom`] from RPC to ++ /// consensus types. ++ #[inline(always)] ++ fn convert_standard_receipt( ++ receipt: ReceiptWithBloom<Receipt<alloy_rpc_types_eth::Log>>, ++ ) -> ReceiptWithBloom<Receipt<alloy_primitives::Log>> { ++ let ReceiptWithBloom { logs_bloom, receipt } = receipt; ++ ++ let consensus_logs = receipt.logs.into_iter().map(|log| log.inner).collect(); ++ ReceiptWithBloom { ++ receipt: Receipt { ++ status: receipt.status, ++ cumulative_gas_used: receipt.cumulative_gas_used, ++ logs: consensus_logs, ++ }, ++ logs_bloom, ++ } ++ } ++ ++ match inner_envelope { ++ ScrollReceiptEnvelope::Legacy(receipt) => { ++ Self::Legacy(convert_standard_receipt(receipt)) ++ } ++ ScrollReceiptEnvelope::Eip2930(receipt) => { ++ Self::Eip2930(convert_standard_receipt(receipt)) ++ } ++ ScrollReceiptEnvelope::Eip1559(receipt) => { ++ Self::Eip1559(convert_standard_receipt(receipt)) ++ } ++ ScrollReceiptEnvelope::L1Message(receipt) => { ++ Self::L1Message(convert_standard_receipt(receipt)) ++ } ++ _ => unreachable!("Unsupported ScrollReceiptEnvelope variant"), ++ } ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use serde_json::json; ++ ++ // <https://github.com/alloy-rs/op-alloy/issues/18> ++ #[test] ++ fn parse_rpc_receipt() { ++ let s = r#"{ ++ "blockHash": "0x9e6a0fb7e22159d943d760608cc36a0fb596d1ab3c997146f5b7c55c8c718c67", ++ "blockNumber": "0x6cfef89", ++ "contractAddress": null, ++ "cumulativeGasUsed": "0xfa0d", ++ "effectiveGasPrice": "0x0", ++ "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", ++ "gasUsed": "0xfa0d", ++ "logs": [], ++ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ++ "status": "0x1", ++ "to": "0x4200000000000000000000000000000000000015", ++ "transactionHash": "0xb7c74afdeb7c89fb9de2c312f49b38cb7a850ba36e064734c5223a477e83fdc9", ++ "transactionIndex": "0x0", ++ "type": "0x7e" ++ }"#; ++ ++ let receipt: ScrollTransactionReceipt = serde_json::from_str(s).unwrap(); ++ let value = serde_json::to_value(&receipt).unwrap(); ++ let expected_value = serde_json::from_str::<serde_json::Value>(s).unwrap(); ++ assert_eq!(value, expected_value); ++ } ++ ++ #[test] ++ fn serialize_empty_optimism_transaction_receipt_fields_struct() { ++ let scroll_fields = ScrollTransactionReceiptFields::default(); ++ ++ let json = serde_json::to_value(scroll_fields).unwrap(); ++ assert_eq!(json, json!({})); ++ } ++}
@@ -3107,7 +4155,7 @@
@@ -3117,90 +4165,308 @@
-
+15
+
+280
-0
-
diff --git reth/crates/scroll/engine/src/lib.rs scroll-reth/crates/scroll/engine/src/lib.rs +
diff --git reth/crates/scroll/alloy/rpc-types/src/transaction.rs scroll-reth/crates/scroll/alloy/rpc-types/src/transaction.rs new file mode 100644 -index 0000000000000000000000000000000000000000..2ebb577e9ffdc62c541c3a46da7a0dda71c76e2f +index 0000000000000000000000000000000000000000..3308fb18606cb94e05fddc2262fb81004338ca10 --- /dev/null -+++ scroll-reth/crates/scroll/engine/src/lib.rs -@@ -0,0 +1,15 @@ -+//! Scroll engine API crate. ++++ scroll-reth/crates/scroll/alloy/rpc-types/src/transaction.rs +@@ -0,0 +1,280 @@ ++//! Scroll specific types related to transactions. + -+#![doc( -+ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", -+ html_favicon_url = "https://avatars.githubusercontent.com/u/87750292?s=200&v=4", -+ issue_tracker_base_url = "https://github.com/scroll-tech/reth/issues/" ++use alloy_consensus::{Transaction as _, Typed2718}; ++use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization}; ++use alloy_primitives::{Address, BlockHash, Bytes, ChainId, TxKind, B256, U256}; ++use alloy_serde::OtherFields; ++use scroll_alloy_consensus::ScrollTxEnvelope; ++use serde::{Deserialize, Serialize}; ++ ++mod request; ++pub use request::ScrollTransactionRequest; ++ ++/// Scroll Transaction type ++#[derive( ++ Clone, Debug, PartialEq, Eq, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut, +)] -+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -+#![cfg_attr(not(feature = "std"), no_std)] -+#![cfg(all(feature = "scroll", not(feature = "optimism")))] ++#[serde(try_from = "tx_serde::TransactionSerdeHelper", into = "tx_serde::TransactionSerdeHelper")] ++#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))] ++pub struct Transaction { ++ /// Ethereum Transaction Types ++ #[deref] ++ #[deref_mut] ++ pub inner: alloy_rpc_types_eth::Transaction<ScrollTxEnvelope>, ++} + -+extern crate alloc; ++impl Typed2718 for Transaction { ++ fn ty(&self) -> u8 { ++ self.inner.ty() ++ } ++} + -+mod payload; -+pub use payload::utils::try_into_block;
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+1
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/engine/src/payload/mod.rs scroll-reth/crates/scroll/engine/src/payload/mod.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..80ce84551eacde12136174a65cfb84d49e1cfa48 ---- /dev/null -+++ scroll-reth/crates/scroll/engine/src/payload/mod.rs -@@ -0,0 +1 @@ -+pub(crate) mod utils;
++impl alloy_consensus::Transaction for Transaction { ++ fn chain_id(&self) -> Option<ChainId> { ++ self.inner.chain_id() ++ } ++ ++ fn nonce(&self) -> u64 { ++ self.inner.nonce() ++ } ++ ++ fn gas_limit(&self) -> u64 { ++ self.inner.gas_limit() ++ } ++ ++ fn gas_price(&self) -> Option<u128> { ++ self.inner.gas_price() ++ } ++ ++ fn max_fee_per_gas(&self) -> u128 { ++ self.inner.max_fee_per_gas() ++ } ++ ++ fn max_priority_fee_per_gas(&self) -> Option<u128> { ++ self.inner.max_priority_fee_per_gas() ++ } ++ ++ fn max_fee_per_blob_gas(&self) -> Option<u128> { ++ self.inner.max_fee_per_blob_gas() ++ } ++ ++ fn priority_fee_or_price(&self) -> u128 { ++ self.inner.priority_fee_or_price() ++ } ++ ++ fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { ++ self.inner.effective_gas_price(base_fee) ++ } ++ ++ fn is_dynamic_fee(&self) -> bool { ++ self.inner.is_dynamic_fee() ++ } ++ ++ fn kind(&self) -> TxKind { ++ self.inner.kind() ++ } ++ ++ fn is_create(&self) -> bool { ++ self.inner.is_create() ++ } ++ ++ fn to(&self) -> Option<Address> { ++ self.inner.to() ++ } ++ ++ fn value(&self) -> U256 { ++ self.inner.value() ++ } ++ ++ fn input(&self) -> &Bytes { ++ self.inner.input() ++ } ++ ++ fn access_list(&self) -> Option<&AccessList> { ++ self.inner.access_list() ++ } ++ ++ fn blob_versioned_hashes(&self) -> Option<&[B256]> { ++ self.inner.blob_versioned_hashes() ++ } ++ ++ fn authorization_list(&self) -> Option<&[SignedAuthorization]> { ++ self.inner.authorization_list() ++ } ++} ++ ++impl alloy_network_primitives::TransactionResponse for Transaction { ++ fn tx_hash(&self) -> alloy_primitives::TxHash { ++ self.inner.tx_hash() ++ } ++ ++ fn block_hash(&self) -> Option<BlockHash> { ++ self.inner.block_hash() ++ } ++ ++ fn block_number(&self) -> Option<u64> { ++ self.inner.block_number() ++ } ++ ++ fn transaction_index(&self) -> Option<u64> { ++ self.inner.transaction_index() ++ } ++ ++ fn from(&self) -> Address { ++ self.inner.from() ++ } ++} ++ ++/// Scroll specific transaction fields ++#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct ScrollL1MessageTransactionFields { ++ /// The index of the transaction in the message queue. ++ pub queue_index: u64, ++ /// The sender of the transaction on the L1. ++ pub sender: Address, ++} ++ ++impl From<ScrollL1MessageTransactionFields> for OtherFields { ++ fn from(value: ScrollL1MessageTransactionFields) -> Self { ++ serde_json::to_value(value).unwrap().try_into().unwrap() ++ } ++} ++ ++impl AsRef<ScrollTxEnvelope> for Transaction { ++ fn as_ref(&self) -> &ScrollTxEnvelope { ++ self.inner.as_ref() ++ } ++} ++ ++mod tx_serde { ++ //! Helper module for serializing and deserializing Scroll [`Transaction`]. ++ //! ++ //! This is needed because we might need to deserialize the `from` field into both ++ //! [`alloy_rpc_types_eth::Transaction::from`] and ++ //! [`scroll_alloy_consensus::TxL1Message`]. ++ //! ++ //! Additionally, we need similar logic for the `gasPrice` field ++ use super::*; ++ use serde::de::Error; ++ ++ /// Helper struct which will be flattened into the transaction and will only contain `from` ++ /// field if inner [`ScrollTxEnvelope`] did not consume it. ++ #[derive(Serialize, Deserialize)] ++ struct OptionalFields { ++ #[serde(default, skip_serializing_if = "Option::is_none")] ++ from: Option<Address>, ++ #[serde( ++ default, ++ rename = "gasPrice", ++ skip_serializing_if = "Option::is_none", ++ with = "alloy_serde::quantity::opt" ++ )] ++ effective_gas_price: Option<u128>, ++ } ++ ++ #[derive(Serialize, Deserialize)] ++ #[serde(rename_all = "camelCase")] ++ pub(crate) struct TransactionSerdeHelper { ++ #[serde(flatten)] ++ inner: ScrollTxEnvelope, ++ #[serde(default)] ++ block_hash: Option<BlockHash>, ++ #[serde(default, with = "alloy_serde::quantity::opt")] ++ block_number: Option<u64>, ++ #[serde(default, with = "alloy_serde::quantity::opt")] ++ transaction_index: Option<u64>, ++ #[serde(flatten)] ++ other: OptionalFields, ++ } ++ ++ impl From<Transaction> for TransactionSerdeHelper { ++ fn from(value: Transaction) -> Self { ++ let Transaction { ++ inner: ++ alloy_rpc_types_eth::Transaction { ++ inner, ++ block_hash, ++ block_number, ++ transaction_index, ++ effective_gas_price, ++ from, ++ }, ++ .. ++ } = value; ++ ++ // if inner transaction has its own `gasPrice` don't serialize it in this struct. ++ let effective_gas_price = effective_gas_price.filter(|_| inner.gas_price().is_none()); ++ ++ Self { ++ inner, ++ block_hash, ++ block_number, ++ transaction_index, ++ other: OptionalFields { from: Some(from), effective_gas_price }, ++ } ++ } ++ } ++ ++ impl TryFrom<TransactionSerdeHelper> for Transaction { ++ type Error = serde_json::Error; ++ ++ fn try_from(value: TransactionSerdeHelper) -> Result<Self, Self::Error> { ++ let TransactionSerdeHelper { ++ inner, ++ block_hash, ++ block_number, ++ transaction_index, ++ other, ++ } = value; ++ ++ // Try to get `from` field from inner envelope or from `MaybeFrom`, otherwise return ++ // error ++ let from = if let Some(from) = other.from { ++ from ++ } else if let ScrollTxEnvelope::L1Message(tx) = &inner { ++ tx.sender ++ } else { ++ return Err(serde_json::Error::custom("missing `from` field")); ++ }; ++ ++ let effective_gas_price = other.effective_gas_price.or_else(|| inner.gas_price()); ++ Ok(Self { ++ inner: alloy_rpc_types_eth::Transaction { ++ inner, ++ block_hash, ++ block_number, ++ transaction_index, ++ from, ++ effective_gas_price, ++ }, ++ }) ++ } ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use alloy_primitives::address; ++ ++ #[test] ++ fn can_deserialize_deposit() { ++ // cast rpc eth_getTransactionByHash ++ // 0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa --rpc-url https://rpc.scroll.io ++ let rpc_tx = r#"{"blockHash":"0x018ed80ea8340984a1f4841490284d6e51d71f9e9411feeca41e007a89fbfdff","blockNumber":"0xb81121","from":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","gas":"0x1e8480","gasPrice":"0x0","hash":"0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","transactionIndex":"0x0","value":"0x0","type":"0x7e","v":"0x0","r":"0x0","s":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0", "yParity":"0x0"}"#; ++ ++ let tx = serde_json::from_str::<Transaction>(rpc_tx).unwrap(); ++ ++ let ScrollTxEnvelope::L1Message(inner) = tx.as_ref() else { ++ panic!("Expected deposit transaction"); ++ }; ++ assert_eq!(inner.sender, address!("7885bcbd5cecef1336b5300fb5186a12ddd8c478")); ++ assert_eq!(inner.queue_index, 0xe7ba0); ++ assert_eq!(tx.inner.effective_gas_price, Some(0)); ++ ++ let deserialized = serde_json::to_value(&tx).unwrap(); ++ let expected = serde_json::from_str::<serde_json::Value>(rpc_tx).unwrap(); ++ similar_asserts::assert_eq!(deserialized, expected); ++ } ++}
@@ -3215,7 +4481,7 @@
@@ -3225,142 +4491,200 @@
-
+124
+
+182
-0
-
diff --git reth/crates/scroll/engine/src/payload/utils.rs scroll-reth/crates/scroll/engine/src/payload/utils.rs +
diff --git reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs scroll-reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs new file mode 100644 -index 0000000000000000000000000000000000000000..73ad2b50346a56a0bdf8b94d130d2da9d3fdd8aa +index 0000000000000000000000000000000000000000..b3185fd263e3342d19f539168f0791e5010c4b32 --- /dev/null -+++ scroll-reth/crates/scroll/engine/src/payload/utils.rs -@@ -0,0 +1,124 @@ -+use alloc::{sync::Arc, vec::Vec}; -+use alloy_consensus::EMPTY_OMMER_ROOT_HASH; -+use alloy_eips::eip2718::Decodable2718; -+use alloy_rlp::BufMut; -+use alloy_rpc_types_engine::{ -+ ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1, ExecutionPayloadV2, -+ ExecutionPayloadV3, PayloadError, ++++ scroll-reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs +@@ -0,0 +1,182 @@ ++use alloy_consensus::{ ++ Sealed, SignableTransaction, Signed, TxEip1559, TxEip4844, TypedTransaction, +}; -+use reth_primitives::{proofs, Block, BlockBody, Header}; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_scroll_forks::ScrollHardfork; ++use alloy_primitives::{Address, PrimitiveSignature as Signature, TxKind, U256}; ++use alloy_rpc_types_eth::{AccessList, TransactionInput, TransactionRequest}; ++use serde::{Deserialize, Serialize}; + -+/// Tries to create a new unsealed block from the given payload, sidecar and chain specification. -+pub fn try_into_block<T: Decodable2718>( -+ value: ExecutionPayload, -+ sidecar: &ExecutionPayloadSidecar, -+ chainspec: Arc<ScrollChainSpec>, -+) -> Result<Block<T>, PayloadError> { -+ let mut base_payload = match value { -+ ExecutionPayload::V1(payload) => try_payload_v1_to_block(payload, chainspec)?, -+ ExecutionPayload::V2(payload) => try_payload_v2_to_block(payload, chainspec)?, -+ ExecutionPayload::V3(payload) => try_payload_v3_to_block(payload, chainspec)?, -+ }; ++use scroll_alloy_consensus::{ScrollTxEnvelope, ScrollTypedTransaction, TxL1Message}; ++ ++/// `ScrollTransactionRequest` is a wrapper around the `TransactionRequest` struct. ++/// This struct derives several traits to facilitate easier use and manipulation ++/// in the codebase. ++#[derive( ++ Clone, ++ Debug, ++ Default, ++ PartialEq, ++ Eq, ++ Hash, ++ derive_more::From, ++ derive_more::AsRef, ++ derive_more::AsMut, ++ Serialize, ++ Deserialize, ++)] ++#[serde(transparent)] ++pub struct ScrollTransactionRequest(TransactionRequest); + -+ base_payload.header.parent_beacon_block_root = sidecar.parent_beacon_block_root(); -+ base_payload.header.requests_hash = sidecar.requests_hash(); ++impl ScrollTransactionRequest { ++ /// Sets the from field in the call to the provided address ++ #[inline] ++ pub const fn from(mut self, from: Address) -> Self { ++ self.0.from = Some(from); ++ self ++ } + -+ Ok(base_payload) -+} ++ /// Sets the transactions type for the transactions. ++ #[doc(alias = "tx_type")] ++ pub const fn transaction_type(mut self, transaction_type: u8) -> Self { ++ self.0.transaction_type = Some(transaction_type); ++ self ++ } + -+/// Tries to convert an [`ExecutionPayloadV1`] to [`Block`]. -+fn try_payload_v1_to_block<T: Decodable2718>( -+ payload: ExecutionPayloadV1, -+ chainspec: Arc<ScrollChainSpec>, -+) -> Result<Block<T>, PayloadError> { -+ // WARNING: It’s allowed for a base fee in EIP1559 to increase unbounded. We assume that -+ // it will fit in an u64. This is not always necessarily true, although it is extremely -+ // unlikely not to be the case, a u64 maximum would have 2^64 which equates to 18 ETH per -+ // gas. -+ let basefee = chainspec -+ .is_fork_active_at_block(ScrollHardfork::Curie, payload.block_number) -+ .then_some(payload.base_fee_per_gas) -+ .map(|b| b.try_into()) -+ .transpose() -+ .map_err(|_| PayloadError::BaseFee(payload.base_fee_per_gas))?; ++ /// Sets the gas limit for the transaction. ++ pub const fn gas_limit(mut self, gas_limit: u64) -> Self { ++ self.0.gas = Some(gas_limit); ++ self ++ } + -+ let transactions = payload -+ .transactions -+ .iter() -+ .map(|tx| { -+ let mut buf = tx.as_ref(); ++ /// Sets the nonce for the transaction. ++ pub const fn nonce(mut self, nonce: u64) -> Self { ++ self.0.nonce = Some(nonce); ++ self ++ } + -+ let tx = T::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; ++ /// Sets the maximum fee per gas for the transaction. ++ pub const fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self { ++ self.0.max_fee_per_gas = Some(max_fee_per_gas); ++ self ++ } + -+ if !buf.is_empty() { -+ return Err(alloy_rlp::Error::UnexpectedLength); -+ } ++ /// Sets the maximum priority fee per gas for the transaction. ++ pub const fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self { ++ self.0.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); ++ self ++ } + -+ Ok(tx) -+ }) -+ .collect::<Result<Vec<_>, _>>()?; ++ /// Sets the recipient address for the transaction. ++ #[inline] ++ pub const fn to(mut self, to: Address) -> Self { ++ self.0.to = Some(TxKind::Call(to)); ++ self ++ } + -+ // Reuse the encoded bytes for root calculation -+ let transactions_root = -+ proofs::ordered_trie_root_with_encoder(&payload.transactions, |item, buf| { -+ buf.put_slice(item) -+ }); ++ /// Sets the value (amount) for the transaction. ++ pub const fn value(mut self, value: U256) -> Self { ++ self.0.value = Some(value); ++ self ++ } + -+ let header = Header { -+ parent_hash: payload.parent_hash, -+ beneficiary: payload.fee_recipient, -+ state_root: payload.state_root, -+ transactions_root, -+ receipts_root: payload.receipts_root, -+ withdrawals_root: None, -+ logs_bloom: payload.logs_bloom, -+ number: payload.block_number, -+ gas_limit: payload.gas_limit, -+ gas_used: payload.gas_used, -+ timestamp: payload.timestamp, -+ mix_hash: payload.prev_randao, -+ base_fee_per_gas: basefee, -+ blob_gas_used: None, -+ excess_blob_gas: None, -+ parent_beacon_block_root: None, -+ requests_hash: None, -+ extra_data: payload.extra_data, -+ // Defaults -+ ommers_hash: EMPTY_OMMER_ROOT_HASH, -+ difficulty: Default::default(), -+ nonce: Default::default(), -+ }; ++ /// Sets the access list for the transaction. ++ pub fn access_list(mut self, access_list: AccessList) -> Self { ++ self.0.access_list = Some(access_list); ++ self ++ } + -+ Ok(Block { header, body: BlockBody { transactions, ..Default::default() } }) ++ /// Sets the input data for the transaction. ++ pub fn input(mut self, input: TransactionInput) -> Self { ++ self.0.input = input; ++ self ++ } ++ ++ /// Builds [`ScrollTypedTransaction`] from this builder. See ++ /// [`TransactionRequest::build_typed_tx`] for more info. ++ /// ++ /// Note that EIP-4844 transactions are not supported by Scroll and will be converted into ++ /// EIP-1559 transactions. ++ pub fn build_typed_tx(self) -> Result<ScrollTypedTransaction, Self> { ++ let tx = self.0.build_typed_tx().map_err(Self)?; ++ match tx { ++ TypedTransaction::Legacy(tx) => Ok(ScrollTypedTransaction::Legacy(tx)), ++ TypedTransaction::Eip1559(tx) => Ok(ScrollTypedTransaction::Eip1559(tx)), ++ TypedTransaction::Eip2930(tx) => Ok(ScrollTypedTransaction::Eip2930(tx)), ++ TypedTransaction::Eip4844(tx) => { ++ let tx: TxEip4844 = tx.into(); ++ Ok(ScrollTypedTransaction::Eip1559(TxEip1559 { ++ chain_id: tx.chain_id, ++ nonce: tx.nonce, ++ gas_limit: tx.gas_limit, ++ max_priority_fee_per_gas: tx.max_priority_fee_per_gas, ++ max_fee_per_gas: tx.max_fee_per_gas, ++ to: TxKind::Call(tx.to), ++ value: tx.value, ++ access_list: tx.access_list, ++ input: tx.input, ++ })) ++ } ++ TypedTransaction::Eip7702(_) => { ++ unimplemented!("EIP-7702 support is not implemented yet") ++ } ++ } ++ } +} + -+/// Tries to convert an [`ExecutionPayloadV2`] to [`Block`]. -+fn try_payload_v2_to_block<T: Decodable2718>( -+ payload: ExecutionPayloadV2, -+ chainspec: Arc<ScrollChainSpec>, -+) -> Result<Block<T>, PayloadError> { -+ // this performs the same conversion as the underlying V1 payload, but calculates the -+ // withdrawals root and adds withdrawals -+ let mut base_sealed_block = try_payload_v1_to_block(payload.payload_inner, chainspec)?; -+ let withdrawals_root = proofs::calculate_withdrawals_root(&payload.withdrawals); -+ base_sealed_block.body.withdrawals = Some(payload.withdrawals.into()); -+ base_sealed_block.header.withdrawals_root = Some(withdrawals_root); -+ Ok(base_sealed_block) ++impl From<TxL1Message> for ScrollTransactionRequest { ++ fn from(tx_l1_message: TxL1Message) -> Self { ++ let to = TxKind::from(tx_l1_message.to); ++ Self(TransactionRequest { ++ from: Some(tx_l1_message.sender), ++ to: Some(to), ++ value: Some(tx_l1_message.value), ++ gas: Some(tx_l1_message.gas_limit), ++ input: tx_l1_message.input.into(), ++ ..Default::default() ++ }) ++ } +} + -+/// Tries to convert an [`ExecutionPayloadV3`] to [`Block`]. -+fn try_payload_v3_to_block<T: Decodable2718>( -+ payload: ExecutionPayloadV3, -+ chainspec: Arc<ScrollChainSpec>, -+) -> Result<Block<T>, PayloadError> { -+ // this performs the same conversion as the underlying V2 payload, but inserts the blob gas -+ // used and excess blob gas -+ let mut base_block = try_payload_v2_to_block(payload.payload_inner, chainspec)?; ++impl From<Sealed<TxL1Message>> for ScrollTransactionRequest { ++ fn from(value: Sealed<TxL1Message>) -> Self { ++ value.into_inner().into() ++ } ++} + -+ base_block.header.blob_gas_used = Some(payload.blob_gas_used); -+ base_block.header.excess_blob_gas = Some(payload.excess_blob_gas); ++impl<T> From<Signed<T, Signature>> for ScrollTransactionRequest ++where ++ T: SignableTransaction<Signature> + Into<TransactionRequest>, ++{ ++ fn from(value: Signed<T, Signature>) -> Self { ++ #[cfg(feature = "k256")] ++ let from = value.recover_signer().ok(); ++ #[cfg(not(feature = "k256"))] ++ let from = None; + -+ Ok(base_block) ++ let mut inner: TransactionRequest = value.strip_signature().into(); ++ inner.from = from; ++ ++ Self(inner) ++ } ++} ++ ++impl From<ScrollTypedTransaction> for ScrollTransactionRequest { ++ fn from(tx: ScrollTypedTransaction) -> Self { ++ match tx { ++ ScrollTypedTransaction::Legacy(tx) => Self(tx.into()), ++ ScrollTypedTransaction::Eip2930(tx) => Self(tx.into()), ++ ScrollTypedTransaction::Eip1559(tx) => Self(tx.into()), ++ ScrollTypedTransaction::L1Message(tx) => tx.into(), ++ } ++ } ++} ++ ++impl From<ScrollTxEnvelope> for ScrollTransactionRequest { ++ fn from(value: ScrollTxEnvelope) -> Self { ++ match value { ++ ScrollTxEnvelope::Eip2930(tx) => tx.into(), ++ ScrollTxEnvelope::Eip1559(tx) => tx.into(), ++ ScrollTxEnvelope::L1Message(tx) => tx.into(), ++ _ => Default::default(), ++ } ++ } +}
@@ -3368,9 +4692,9 @@
@@ -3385,7 +4709,7 @@
@@ -3395,21 +4719,21 @@
-
+59
+
+46
-0
-
diff --git reth/crates/scroll/evm/Cargo.toml scroll-reth/crates/scroll/evm/Cargo.toml +
diff --git reth/crates/scroll/bin/scroll-reth/Cargo.toml scroll-reth/crates/scroll/bin/scroll-reth/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..af68eccf3fe03a272db1964df239dab140b1d7c3 +index 0000000000000000000000000000000000000000..975a0a2432b20265834ad655832ea6202b5c7408 --- /dev/null -+++ scroll-reth/crates/scroll/evm/Cargo.toml -@@ -0,0 +1,59 @@ ++++ scroll-reth/crates/scroll/bin/scroll-reth/Cargo.toml +@@ -0,0 +1,46 @@ +[package] -+name = "reth-scroll-evm" ++name = "scroll-reth" +version.workspace = true +edition.workspace = true +rust-version.workspace = true @@ -3423,59 +4747,46 @@ + +[dependencies] +# reth -+reth-chainspec.workspace = true -+reth-consensus.workspace = true -+reth-ethereum-consensus.workspace = true -+reth-evm.workspace = true -+reth-primitives.workspace = true -+reth-primitives-traits.workspace = true -+reth-revm.workspace = true -+ -+# revm -+revm = { workspace = true, features = ["optional_no_base_fee"] } -+ -+# scroll -+reth-scroll-chainspec.workspace = true -+reth-scroll-forks.workspace = true -+ -+# alloy -+alloy-consensus.workspace = true -+alloy-eips.workspace = true ++reth-cli-util.workspace = true ++reth-node-builder.workspace = true ++reth-provider.workspace = true + +# scroll -+reth-scroll-consensus.workspace = true ++reth-scroll-cli.workspace = true ++reth-scroll-node.workspace = true + +# misc -+auto_impl.workspace = true -+derive_more.workspace = true -+thiserror.workspace = true -+tracing.workspace = true -+ -+[dev-dependencies] -+eyre.workspace = true -+reth-scroll-primitives.workspace = true ++clap = { workspace = true, features = ["derive", "env"] } + +[features] ++scroll = [ ++ "reth-provider/scroll", ++ "reth-scroll-cli/scroll", ++] ++skip-state-root-validation = [ ++ "reth-node-builder/skip-state-root-validation", ++ "reth-scroll-node/skip-state-root-validation", ++ "reth-provider/skip-state-root-validation" ++] +optimism = [ -+ "reth-primitives/optimism", -+ "revm/optimism" ++ "reth-provider/optimism", ++ "reth-scroll-cli/optimism", ++ "reth-scroll-node/optimism" +] -+scroll = [ -+ "reth-evm/scroll", -+ "reth-primitives/scroll", -+ "reth-revm/scroll", -+ "reth-scroll-consensus/scroll", -+ "revm/scroll" -+]
++ ++[[bin]] ++name = "scroll-reth" ++path = "src/main.rs" ++required-features = ["skip-state-root-validation", "scroll"]
@@ -3490,7 +4801,7 @@
@@ -3500,355 +4811,191 @@
-
+327
+
+53
-0
-
diff --git reth/crates/scroll/evm/src/config.rs scroll-reth/crates/scroll/evm/src/config.rs +
diff --git reth/crates/scroll/bin/scroll-reth/src/main.rs scroll-reth/crates/scroll/bin/scroll-reth/src/main.rs new file mode 100644 -index 0000000000000000000000000000000000000000..84fcc924995d068977269101e4146b2958e50d64 +index 0000000000000000000000000000000000000000..14ecdbb2b5ac2e49461699f5df2aa18c5dbf8551 --- /dev/null -+++ scroll-reth/crates/scroll/evm/src/config.rs -@@ -0,0 +1,327 @@ -+use reth_chainspec::{ChainSpecProvider, Head}; -+use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; -+use reth_primitives::{transaction::FillTxEnv, TransactionSigned}; -+use reth_revm::{inspector_handle_register, Database, Evm, GetInspector}; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_scroll_forks::ScrollHardfork; -+use revm::{ -+ precompile::{Address, Bytes}, -+ primitives::{ -+ AnalysisKind, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, HandlerCfg, SpecId, TxEnv, U256, -+ }, -+ EvmBuilder, -+}; -+use std::{convert::Infallible, sync::Arc}; ++++ scroll-reth/crates/scroll/bin/scroll-reth/src/main.rs +@@ -0,0 +1,53 @@ ++//! Scroll binary + -+/// Scroll EVM configuration. -+#[derive(Clone, Debug)] -+pub struct ScrollEvmConfig { -+ /// The chain spec for Scroll. -+ chain_spec: Arc<ScrollChainSpec>, -+} ++#[global_allocator] ++static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); + -+impl ScrollEvmConfig { -+ /// Returns a new instance of [`ScrollEvmConfig`]. -+ pub const fn new(chain_spec: Arc<ScrollChainSpec>) -> Self { -+ Self { chain_spec } ++#[cfg(all(feature = "scroll", not(feature = "optimism")))] ++fn main() { ++ use clap::Parser; ++ use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; ++ use reth_provider::providers::BlockchainProvider; ++ use reth_scroll_cli::{Cli, ScrollChainSpecParser, ScrollRollupArgs}; ++ use reth_scroll_node::{ScrollAddOns, ScrollNode}; ++ reth_cli_util::sigsegv_handler::install(); ++ ++ // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. ++ if std::env::var_os("RUST_BACKTRACE").is_none() { ++ std::env::set_var("RUST_BACKTRACE", "1"); + } + -+ /// Returns the spec id at the given head. -+ pub fn spec_id_at_head(&self, head: &Head) -> SpecId { -+ let chain_spec = &self.chain_spec; -+ if chain_spec.fork(ScrollHardfork::Curie).active_at_head(head) { -+ SpecId::CURIE -+ } else if chain_spec.fork(ScrollHardfork::Bernoulli).active_at_head(head) { -+ SpecId::BERNOULLI -+ } else { -+ SpecId::PRE_BERNOULLI -+ } -+ } -+} -+ -+impl ConfigureEvm for ScrollEvmConfig { -+ type DefaultExternalContext<'a> = (); -+ -+ fn evm<DB: Database>(&self, db: DB) -> Evm<'_, Self::DefaultExternalContext<'_>, DB> { -+ EvmBuilder::default().with_db(db).scroll().build() -+ } ++ if let Err(err) = Cli::<ScrollChainSpecParser, ScrollRollupArgs>::parse() ++ .run::<_, _, ScrollNode>(|builder, _| async move { ++ let engine_tree_config = TreeConfig::default() ++ .with_persistence_threshold(builder.config().engine.persistence_threshold) ++ .with_memory_block_buffer_target( ++ builder.config().engine.memory_block_buffer_target, ++ ); ++ let handle = builder ++ .with_types_and_provider::<ScrollNode, BlockchainProvider<_>>() ++ .with_components(ScrollNode::components()) ++ .with_add_ons(ScrollAddOns::default()) ++ .launch_with_fn(|builder| { ++ let launcher = EngineNodeLauncher::new( ++ builder.task_executor().clone(), ++ builder.config().datadir(), ++ engine_tree_config, ++ ); ++ builder.launch_with(launcher) ++ }) ++ .await?; + -+ fn evm_with_inspector<DB, I>(&self, db: DB, inspector: I) -> Evm<'_, I, DB> -+ where -+ DB: Database, -+ I: GetInspector<DB>, ++ handle.node_exit_future.await ++ }) + { -+ EvmBuilder::default() -+ .with_db(db) -+ .with_external_context(inspector) -+ .scroll() -+ .append_handler_register(inspector_handle_register) -+ .build() ++ eprintln!("Error: {err:?}"); ++ std::process::exit(1); + } -+ -+ fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a> {} +} + -+impl ConfigureEvmEnv for ScrollEvmConfig { -+ type Transaction = TransactionSigned; -+ type Header = alloy_consensus::Header; -+ type Error = Infallible; -+ -+ fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &Self::Transaction, sender: Address) { -+ transaction.fill_tx_env(tx_env, sender); -+ } -+ -+ fn fill_tx_env_system_contract_call( -+ &self, -+ _env: &mut Env, -+ _caller: Address, -+ _contract: Address, -+ _data: Bytes, -+ ) { -+ /* noop */ -+ } -+ -+ fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { -+ let spec_id = self.spec_id_at_head(&Head { -+ number: header.number, -+ timestamp: header.timestamp, -+ difficulty: header.difficulty, -+ ..Default::default() -+ }); -+ -+ cfg_env.handler_cfg.spec_id = spec_id; -+ cfg_env.handler_cfg.is_scroll = true; -+ -+ cfg_env.chain_id = self.chain_spec.chain().id(); -+ cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; -+ } -+ -+ fn fill_block_env(&self, block_env: &mut BlockEnv, header: &Self::Header, spec_id: SpecId) { -+ block_env.number = U256::from(header.number); -+ -+ if let Some(vault_address) = self.chain_spec.config.fee_vault_address { -+ block_env.coinbase = vault_address; -+ } else { -+ block_env.coinbase = header.beneficiary; -+ } -+ -+ block_env.timestamp = U256::from(header.timestamp); -+ if spec_id >= SpecId::MERGE { -+ block_env.prevrandao = Some(header.mix_hash); -+ block_env.difficulty = U256::ZERO; -+ } else { -+ block_env.difficulty = header.difficulty; -+ block_env.prevrandao = None; -+ } -+ block_env.basefee = U256::from(header.base_fee_per_gas.unwrap_or_default()); -+ block_env.gas_limit = U256::from(header.gas_limit); -+ block_env.blob_excess_gas_and_price = None; -+ } -+ -+ fn next_cfg_and_block_env( -+ &self, -+ parent: &Self::Header, -+ attributes: NextBlockEnvAttributes, -+ ) -> Result<EvmEnv, Self::Error> { -+ // configure evm env based on parent block -+ let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id()); -+ -+ // fetch spec id from next head number and timestamp -+ let spec_id = self.spec_id_at_head(&Head { -+ number: parent.number + 1, -+ timestamp: attributes.timestamp, -+ ..Default::default() -+ }); -+ -+ let coinbase = if let Some(vault_address) = self.chain_spec.config.fee_vault_address { -+ vault_address -+ } else { -+ attributes.suggested_fee_recipient -+ }; -+ -+ let block_env = BlockEnv { -+ number: U256::from(parent.number + 1), -+ coinbase, -+ timestamp: U256::from(attributes.timestamp), -+ difficulty: U256::ZERO, -+ prevrandao: Some(attributes.prev_randao), -+ gas_limit: U256::from(parent.gas_limit), -+ // calculate basefee based on parent block's gas usage -+ // TODO(scroll): update with correct block fee calculation for block building. -+ basefee: U256::from(parent.base_fee_per_gas.unwrap_or_default()), -+ blob_excess_gas_and_price: None, -+ }; ++#[cfg(any(feature = "optimism", not(feature = "scroll")))] ++fn main() { ++ eprintln!("Scroll feature is not enabled"); ++ std::process::exit(1); ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+64
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/Cargo.toml scroll-reth/crates/scroll/chainspec/Cargo.toml +new file mode 100644 +index 0000000000000000000000000000000000000000..e422ad4f444996ae1984abb5b406012bd2f62fea +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/Cargo.toml +@@ -0,0 +1,64 @@ ++[package] ++name = "reth-scroll-chainspec" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++description = "EVM chain spec implementation for scroll." + -+ let cfg_with_handler_cfg = CfgEnvWithHandlerCfg { -+ cfg_env: cfg, -+ handler_cfg: HandlerCfg { spec_id, is_scroll: true }, -+ }; ++[lints] ++workspace = true + -+ Ok((cfg_with_handler_cfg, block_env).into()) -+ } -+} ++[dependencies] ++# reth ++reth-chainspec.workspace = true ++reth-ethereum-forks.workspace = true ++reth-network-peers.workspace = true ++reth-primitives-traits.workspace = true ++reth-trie-common.workspace = true + -+impl ChainSpecProvider for ScrollEvmConfig { -+ type ChainSpec = ScrollChainSpec; ++# scroll ++reth-scroll-forks.workspace = true + -+ fn chain_spec(&self) -> Arc<Self::ChainSpec> { -+ self.chain_spec.clone() -+ } -+} ++# ethereum ++alloy-chains.workspace = true ++alloy-genesis.workspace = true ++alloy-primitives.workspace = true ++alloy-consensus.workspace = true ++alloy-eips.workspace = true ++alloy-serde.workspace = true + -+#[cfg(test)] -+mod tests { -+ use super::*; -+ use alloy_consensus::Header; -+ use reth_chainspec::NamedChain::Scroll; -+ use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; -+ use revm::primitives::{SpecId, B256}; ++# io ++serde_json.workspace = true ++serde = { workspace = true, features = ["derive"] } + -+ #[test] -+ fn test_spec_at_head() { -+ let config = ScrollEvmConfig::new( -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), -+ ); ++# misc ++derive_more.workspace = true ++once_cell.workspace = true + -+ // prepare all fork heads -+ let curie_head = &Head { number: 7096836, ..Default::default() }; -+ let bernouilli_head = &Head { number: 5220340, ..Default::default() }; -+ let pre_bernouilli_head = &Head { number: 0, ..Default::default() }; ++[dev-dependencies] ++alloy-genesis.workspace = true ++reth-chainspec = { workspace = true, features = ["test-utils"] } + -+ // check correct spec id -+ assert_eq!(config.spec_id_at_head(curie_head), SpecId::CURIE); -+ assert_eq!(config.spec_id_at_head(bernouilli_head), SpecId::BERNOULLI); -+ assert_eq!(config.spec_id_at_head(pre_bernouilli_head), SpecId::PRE_BERNOULLI); -+ } -+ -+ #[test] -+ fn test_fill_cfg_env() { -+ let config = ScrollEvmConfig::new( -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), -+ ); -+ -+ // curie -+ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); -+ let curie_header = Header { number: 7096836, ..Default::default() }; -+ -+ // fill cfg env -+ config.fill_cfg_env(&mut cfg_env, &curie_header); -+ -+ // check correct cfg env -+ assert_eq!(cfg_env.chain_id, Scroll as u64); -+ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); -+ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::CURIE); -+ assert!(cfg_env.handler_cfg.is_scroll); -+ -+ // bernoulli -+ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); -+ let bernouilli_header = Header { number: 5220340, ..Default::default() }; -+ -+ // fill cfg env -+ config.fill_cfg_env(&mut cfg_env, &bernouilli_header); -+ -+ // check correct cfg env -+ assert_eq!(cfg_env.chain_id, Scroll as u64); -+ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); -+ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::BERNOULLI); -+ assert!(cfg_env.handler_cfg.is_scroll); -+ -+ // pre-bernoulli -+ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); -+ let pre_bernouilli_header = Header { number: 0, ..Default::default() }; -+ -+ // fill cfg env -+ config.fill_cfg_env(&mut cfg_env, &pre_bernouilli_header); -+ -+ // check correct cfg env -+ assert_eq!(cfg_env.chain_id, Scroll as u64); -+ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); -+ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::PRE_BERNOULLI); -+ assert!(cfg_env.handler_cfg.is_scroll); -+ } -+ -+ #[test] -+ fn test_fill_block_env() { -+ let config = ScrollEvmConfig::new( -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), -+ ); -+ let mut block_env = BlockEnv::default(); -+ -+ // curie header -+ let header = Header { -+ number: 7096836, -+ beneficiary: Address::random(), -+ timestamp: 1719994277, -+ mix_hash: B256::random(), -+ base_fee_per_gas: Some(155157341), -+ gas_limit: 10000000, -+ ..Default::default() -+ }; -+ -+ // fill block env -+ config.fill_block_env(&mut block_env, &header, SpecId::MERGE); -+ -+ // verify block env correctly updated -+ let expected = BlockEnv { -+ number: U256::from(header.number), -+ coinbase: config.chain_spec.config.fee_vault_address.unwrap(), -+ timestamp: U256::from(header.timestamp), -+ prevrandao: Some(header.mix_hash), -+ difficulty: U256::ZERO, -+ basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()), -+ gas_limit: U256::from(header.gas_limit), -+ blob_excess_gas_and_price: None, -+ }; -+ assert_eq!(block_env, expected) -+ } -+ -+ #[test] -+ fn test_next_cfg_and_block_env() -> eyre::Result<()> { -+ let config = ScrollEvmConfig::new( -+ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), -+ ); -+ -+ // pre curie header -+ let header = Header { -+ number: 7096835, -+ beneficiary: Address::random(), -+ timestamp: 1719994274, -+ mix_hash: B256::random(), -+ base_fee_per_gas: None, -+ gas_limit: 10000000, -+ ..Default::default() -+ }; -+ -+ // curie block attributes -+ let attributes = NextBlockEnvAttributes { -+ timestamp: 1719994277, -+ suggested_fee_recipient: Address::random(), -+ prev_randao: B256::random(), -+ gas_limit: 10000000, -+ }; -+ -+ // get next cfg env and block env -+ let (cfg_env, block_env) = config.next_cfg_and_block_env(&header, attributes)?.into(); -+ -+ // verify cfg env -+ assert_eq!(cfg_env.chain_id, Scroll as u64); -+ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::CURIE); -+ assert!(cfg_env.handler_cfg.is_scroll); -+ -+ // verify block env -+ let expected = BlockEnv { -+ number: U256::from(header.number + 1), -+ coinbase: config.chain_spec.config.fee_vault_address.unwrap(), -+ timestamp: U256::from(attributes.timestamp), -+ prevrandao: Some(attributes.prev_randao), -+ difficulty: U256::ZERO, -+ // TODO(scroll): this shouldn't be 0 at curie fork -+ basefee: U256::ZERO, -+ gas_limit: U256::from(header.gas_limit), -+ blob_excess_gas_and_price: None, -+ }; -+ assert_eq!(block_env, expected); -+ -+ Ok(()) -+ } -+}
++[features] ++default = ["std"] ++std = [ ++ "alloy-chains/std", ++ "alloy-genesis/std", ++ "alloy-primitives/std", ++ "alloy-eips/std", ++ "alloy-serde/std", ++ "reth-chainspec/std", ++ "reth-ethereum-forks/std", ++ "reth-primitives-traits/std", ++ "reth-scroll-forks/std", ++ "alloy-consensus/std", ++ "once_cell/std", ++ "serde/std", ++ "derive_more/std", ++ "reth-network-peers/std", ++ "serde_json/std", ++ "reth-trie-common/std" ++]
@@ -3863,7 +5010,7 @@
@@ -3873,57 +5020,29 @@
-
+29
+
+1
-0
-
diff --git reth/crates/scroll/evm/src/error.rs scroll-reth/crates/scroll/evm/src/error.rs +
diff --git reth/crates/scroll/chainspec/res/genesis/dev.json scroll-reth/crates/scroll/chainspec/res/genesis/dev.json new file mode 100644 -index 0000000000000000000000000000000000000000..3e2f469a4a6e6f9fad8b3d4b2d04800be47ab990 +index 0000000000000000000000000000000000000000..be72d7e96db2883e9a0ad9062182d88ae9d4135a --- /dev/null -+++ scroll-reth/crates/scroll/evm/src/error.rs -@@ -0,0 +1,29 @@ -+use derive_more::{Display, From}; -+use reth_evm::execute::BlockExecutionError; -+ -+/// Execution error for Scroll. -+#[derive(thiserror::Error, Display, From, Debug)] -+pub enum ScrollBlockExecutionError { -+ /// Error occurred at fork transition. -+ #[display("failed to apply fork: {_0}")] -+ Fork(ForkError), -+} -+ -+impl From<ScrollBlockExecutionError> for BlockExecutionError { -+ fn from(value: ScrollBlockExecutionError) -> Self { -+ Self::other(value) -+ } -+} -+ -+/// Scroll fork error. -+#[derive(Debug, Display)] -+pub enum ForkError { -+ /// Error occurred at Curie fork. -+ Curie(String), -+} -+ -+impl From<ForkError> for BlockExecutionError { -+ fn from(value: ForkError) -> Self { -+ ScrollBlockExecutionError::Fork(value).into() -+ } -+}
++++ scroll-reth/crates/scroll/chainspec/res/genesis/dev.json +@@ -0,0 +1 @@ ++{"nonce":"0x0","timestamp":"0x6490fdd2","extraData":"0x","gasLimit":"0x1c9c380","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","stateRoot":"0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494","alloc":{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":{"balance":"0xD3C21BCECCEDA1000000"},"0x70997970C51812dc3A010C7d01b50e0d17dc79C8":{"balance":"0xD3C21BCECCEDA1000000"},"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC":{"balance":"0xD3C21BCECCEDA1000000"},"0x90F79bf6EB2c4f870365E785982E1f101E93b906":{"balance":"0xD3C21BCECCEDA1000000"},"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65":{"balance":"0xD3C21BCECCEDA1000000"},"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc":{"balance":"0xD3C21BCECCEDA1000000"},"0x976EA74026E726554dB657fA54763abd0C3a0aa9":{"balance":"0xD3C21BCECCEDA1000000"},"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955":{"balance":"0xD3C21BCECCEDA1000000"},"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f":{"balance":"0xD3C21BCECCEDA1000000"},"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720":{"balance":"0xD3C21BCECCEDA1000000"},"0xBcd4042DE499D14e55001CcbB24a551F3b954096":{"balance":"0xD3C21BCECCEDA1000000"},"0x71bE63f3384f5fb98995898A86B02Fb2426c5788":{"balance":"0xD3C21BCECCEDA1000000"},"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a":{"balance":"0xD3C21BCECCEDA1000000"},"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec":{"balance":"0xD3C21BCECCEDA1000000"},"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097":{"balance":"0xD3C21BCECCEDA1000000"},"0xcd3B766CCDd6AE721141F452C550Ca635964ce71":{"balance":"0xD3C21BCECCEDA1000000"},"0x2546BcD3c84621e976D8185a91A922aE77ECEc30":{"balance":"0xD3C21BCECCEDA1000000"},"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E":{"balance":"0xD3C21BCECCEDA1000000"},"0xdD2FD4581271e230360230F9337D5c0430Bf44C0":{"balance":"0xD3C21BCECCEDA1000000"},"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199":{"balance":"0xD3C21BCECCEDA1000000"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}
@@ -3938,7 +5057,7 @@
@@ -3948,635 +5067,526 @@
-
+618
+
+1
-0
-
diff --git reth/crates/scroll/evm/src/execute.rs scroll-reth/crates/scroll/evm/src/execute.rs +
diff --git reth/crates/scroll/chainspec/res/genesis/scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json new file mode 100644 -index 0000000000000000000000000000000000000000..22e28ad58bd22fae03f43a8ff915c2cfdcbf5d70 +index 0000000000000000000000000000000000000000..ef6360ac9d90367c5029a8f6858c062a88866cef --- /dev/null -+++ scroll-reth/crates/scroll/evm/src/execute.rs -@@ -0,0 +1,618 @@ -+//! Implementation of the [`BlockExecutionStrategy`] for Scroll. -+ -+use crate::{ForkError, ScrollEvmConfig}; -+use alloy_consensus::{Header, Transaction}; -+use alloy_eips::eip7685::Requests; -+use reth_chainspec::EthereumHardforks; -+use reth_consensus::ConsensusError; -+use reth_evm::{ -+ execute::{ -+ BasicBlockExecutorProvider, BlockExecutionError, BlockExecutionStrategy, -+ BlockExecutionStrategyFactory, BlockValidationError, ExecuteOutput, ProviderError, -+ }, -+ ConfigureEvm, ConfigureEvmEnv, -+}; -+use reth_primitives::{ -+ gas_spent_by_transactions, BlockWithSenders, EthPrimitives, GotExpected, -+ InvalidTransactionError, Receipt, TransactionSigned, TxType, -+}; -+use reth_revm::primitives::{CfgEnvWithHandlerCfg, U256}; -+use reth_scroll_chainspec::{ChainSpecProvider, ScrollChainSpec}; -+use reth_scroll_consensus::{apply_curie_hard_fork, L1_GAS_PRICE_ORACLE_ADDRESS}; -+use reth_scroll_forks::{ScrollHardfork, ScrollHardforks}; -+use revm::{ -+ primitives::{BlockEnv, EnvWithHandlerCfg, ExecutionResult, ResultAndState}, -+ Database, DatabaseCommit, State, -+}; -+use std::{ -+ fmt::{Debug, Display}, -+ sync::Arc, -+}; -+ -+/// The Scroll block execution strategy. -+#[derive(Debug)] -+pub struct ScrollExecutionStrategy<DB, EvmConfig> { -+ /// Evm configuration. -+ evm_config: EvmConfig, -+ /// Current state for the execution. -+ state: State<DB>, -+} -+ -+impl<DB, EvmConfig> ScrollExecutionStrategy<DB, EvmConfig> { -+ /// Returns an instance of [`ScrollExecutionStrategy`]. -+ pub const fn new(evm_config: EvmConfig, state: State<DB>) -> Self { -+ Self { evm_config, state } -+ } -+} -+ -+impl<DB, EvmConfig> ScrollExecutionStrategy<DB, EvmConfig> -+where -+ EvmConfig: ConfigureEvmEnv<Header = Header>, -+{ -+ /// Configures a new evm configuration and block environment for the given block. -+ /// -+ /// # Caution -+ /// -+ /// This does not initialize the tx environment. -+ fn evm_env_for_block(&self, header: &Header) -> EnvWithHandlerCfg { -+ let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); -+ let mut block_env = BlockEnv::default(); -+ self.evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, header); -+ -+ EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) -+ } -+} -+ -+impl<DB, EvmConfig> BlockExecutionStrategy for ScrollExecutionStrategy<DB, EvmConfig> -+where -+ DB: Database<Error: Into<ProviderError> + Display>, -+ EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned> -+ + ChainSpecProvider<ChainSpec = ScrollChainSpec>, -+{ -+ type DB = DB; -+ type Primitives = EthPrimitives; -+ type Error = BlockExecutionError; -+ -+ fn apply_pre_execution_changes(&mut self, block: &BlockWithSenders) -> Result<(), Self::Error> { -+ // set state clear flag if the block is after the Spurious Dragon hardfork. -+ let state_clear_flag = -+ (*self.evm_config.chain_spec()).is_spurious_dragon_active_at_block(block.header.number); -+ self.state.set_state_clear_flag(state_clear_flag); ++++ scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json +@@ -0,0 +1 @@ ++{"config":{"chainId":534352,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiTime":0,"bernoulliBlock":5220340,"curieBlock":7096836,"darwinTime":1724227200,"darwinV2Time":1725264000,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","l1Config":{"l1ChainId":"1","l1MessageQueueAddress":"0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B","scrollChainAddress":"0xa13BAF47339d63B743e7Da8741db5456DAc1E556","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x6524e860","extraData":"0x4c61206573746f6e7465636f206573746173206d616c6665726d6974612e0000d2ACF5d16a983DB0d909d9D761B8337Fabd6cBd10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"10000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0xF9062b8a30e0d7722960e305049FA50b86ba6253":{"balance":"2000000000000000000"},"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC":{"balance":"226156424291633194186662080095093570025917938800079226637565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61038d565b60405161013891906112fa565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611330565b61041f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461135a565b610439565b3480156101bc57600080fd5b5061010f6101cb366004611396565b61045d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b5061018261054e565b34801561020d57600080fd5b5061016161021c366004611330565b61055d565b34801561022d57600080fd5b5061018261023c3660046113af565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b506101826102723660046113af565b61057f565b34801561028357600080fd5b5061028c61059d565b60405161013897969594939291906113ca565b3480156102ab57600080fd5b5061012b610626565b3480156102c057600080fd5b506101616102cf366004611330565b610635565b3480156102e057600080fd5b506101616102ef366004611330565b6106b0565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611460565b6106be565b34801561032857600080fd5b506101826103373660046114d3565b610822565b336103478134610881565b806001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3460405161038291815260200190565b60405180910390a250565b60606003805461039c90611506565b80601f01602080910402602001604051908101604052809291908181526020018280546103c890611506565b80156104155780601f106103ea57610100808354040283529160200191610415565b820191906000526020600020905b8154815290600101906020018083116103f857829003601f168201915b5050505050905090565b60003361042d818585610940565b60019150505b92915050565b600033610447858285610a65565b610452858585610adf565b506001949350505050565b336104688183610c83565b6000816001600160a01b03168360405160006040518083038185875af1925050503d80600081146104b5576040519150601f19603f3d011682016040523d82523d6000602084013e6104ba565b606091505b50509050806105065760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b816001600160a01b03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658460405161054191815260200190565b60405180910390a2505050565b6000610558610db2565b905090565b60003361042d8185856105708383610822565b61057a919061153a565b610940565b6001600160a01b038116600090815260076020526040812054610433565b6000606080828080836105d17f577261707065642045746865720000000000000000000000000000000000000d6005610edd565b6105fc7f31000000000000000000000000000000000000000000000000000000000000016006610edd565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461039c90611506565b600033816106438286610822565b9050838110156106a35760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104fd565b6104528286868403610940565b60003361042d818585610adf565b8342111561070e5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104fd565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861073d8c610f81565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061079882610fa9565b905060006107a882878787610fd6565b9050896001600160a01b0316816001600160a01b03161461080b5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104fd565b6108168a8a8a610940565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006020835110156108695761086283610ffe565b9050610433565b8161087484826115bf565b5060ff9050610433565b90565b6001600160a01b0382166108d75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104fd565b80600260008282546108e9919061153a565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109a25760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104fd565b6001600160a01b038216610a035760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104fd565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a718484610822565b90506000198114610ad95781811015610acc5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104fd565b610ad98484848403610940565b50505050565b6001600160a01b038316610b435760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104fd565b6001600160a01b038216610ba55760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104fd565b6001600160a01b03831660009081526020819052604090205481811015610c1d5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104fd565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610ad9565b6001600160a01b038216610ce35760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104fd565b6001600160a01b03821660009081526020819052604090205481811015610d575760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104fd565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a58565b505050565b6000306001600160a01b037f000000000000000000000000530000000000000000000000000000000000000416148015610e0b57507f000000000000000000000000000000000000000000000000000000000008275046145b15610e3557507fe5b117a3cd7ae7ed3508e6e6c5a0794536b2a8dee12533c4d7524eae9c85438f90565b610558604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ef0576108628361103c565b818054610efc90611506565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2890611506565b8015610f755780601f10610f4a57610100808354040283529160200191610f75565b820191906000526020600020905b815481529060010190602001808311610f5857829003601f168201915b50505050509050610433565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610433610fb6610db2565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fe78787878761107b565b91509150610ff48161113f565b5095945050505050565b600080829050601f81511115611029578260405163305a27a960e01b81526004016104fd91906112fa565b80516110348261167f565b179392505050565b606060006110498361128c565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156110b25750600090506003611136565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611106573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661112f57600060019250925050611136565b9150600090505b94509492505050565b6000816004811115611153576111536116a3565b0361115b5750565b600181600481111561116f5761116f6116a3565b036111bc5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104fd565b60028160048111156111d0576111d06116a3565b0361121d5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104fd565b6003816004811115611231576112316116a3565b036112895760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104fd565b50565b600060ff8216601f81111561043357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112da576020818501810151868301820152016112be565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061130d60208301846112b4565b9392505050565b80356001600160a01b038116811461132b57600080fd5b919050565b6000806040838503121561134357600080fd5b61134c83611314565b946020939093013593505050565b60008060006060848603121561136f57600080fd5b61137884611314565b925061138660208501611314565b9150604084013590509250925092565b6000602082840312156113a857600080fd5b5035919050565b6000602082840312156113c157600080fd5b61130d82611314565b60ff60f81b881681526000602060e0818401526113ea60e084018a6112b4565b83810360408501526113fc818a6112b4565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561144e57835183529284019291840191600101611432565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561147b57600080fd5b61148488611314565b965061149260208901611314565b95506040880135945060608801359350608088013560ff811681146114b657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114e657600080fd5b6114ef83611314565b91506114fd60208401611314565b90509250929050565b600181811c9082168061151a57607f821691505b602082108103610fa357634e487b7160e01b600052602260045260246000fd5b8082018082111561043357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610dad57600081815260208120601f850160051c810160208610156115985750805b601f850160051c820191505b818110156115b7578281556001016115a4565b505050505050565b815167ffffffffffffffff8111156115d9576115d961155b565b6115ed816115e78454611506565b84611571565b602080601f831160018114611622576000841561160a5750858301515b600019600386901b1c1916600185901b1785556115b7565b600085815260208120601f198616915b8281101561165157888601518255948401946001909101908401611632565b508582101561166f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610fa35760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea26469706673582212207f39e33e122e8e2b0381aa6abea46046f56b05ced66c556a06bb1b80be7f55cf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100ab5760003560e01c806384411d651161006457806384411d65146101845780638da5cb5b1461019a5780639e7adc79146101ba578063f2fde38b146101da578063feec756c146101fa578063ff4f35461461021a57600080fd5b80632e1a7d4d146100b75780633cb747bf146100d95780633ccfd60b14610116578063457e1a491461012b57806366d003ac1461014f578063715018a61461016f57600080fd5b366100b257005b600080fd5b3480156100c357600080fd5b506100d76100d2366004610682565b61023a565b005b3480156100e557600080fd5b506002546100f9906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561012257600080fd5b506100d76103ff565b34801561013757600080fd5b5061014160015481565b60405190815260200161010d565b34801561015b57600080fd5b506003546100f9906001600160a01b031681565b34801561017b57600080fd5b506100d761040c565b34801561019057600080fd5b5061014160045481565b3480156101a657600080fd5b506000546100f9906001600160a01b031681565b3480156101c657600080fd5b506100d76101d536600461069b565b610442565b3480156101e657600080fd5b506100d76101f536600461069b565b6104be565b34801561020657600080fd5b506100d761021536600461069b565b610547565b34801561022657600080fd5b506100d7610235366004610682565b6105c3565b6001548110156102ca5760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b478082111561032e5760405162461bcd60e51b815260206004820152602a60248201527f4665655661756c743a20696e73756666696369656e742062616c616e636520746044820152696f20776974686472617760b01b60648201526084016102c1565b6004805483019055600354604080518481526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b9488946103c99491909216928592906004016106cb565b6000604051808303818588803b1580156103e257600080fd5b505af11580156103f6573d6000803e3d6000fd5b50505050505050565b476104098161023a565b50565b6000546001600160a01b031633146104365760405162461bcd60e51b81526004016102c190610737565b6104406000610632565b565b6000546001600160a01b0316331461046c5760405162461bcd60e51b81526004016102c190610737565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b031633146104e85760405162461bcd60e51b81526004016102c190610737565b6001600160a01b03811661053e5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102c1565b61040981610632565b6000546001600160a01b031633146105715760405162461bcd60e51b81526004016102c190610737565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105ed5760405162461bcd60e51b81526004016102c190610737565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561069457600080fd5b5035919050565b6000602082840312156106ad57600080fd5b81356001600160a01b03811681146106c457600080fd5b9392505050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b8181101561070d5786810183015185820160a0015282016106f1565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea2646970667358221220063c6c384f745ebcacfdd13320e5b9a50687aae43ff14566761f56273111b97e64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC","0x0000000000000000000000000000000000000000000000000000000000000003":"0x8FA3b4570B4C96f8036C13b64971BA65867eEB48"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json +new file mode 100644 +index 0000000000000000000000000000000000000000..64987e5c8c5eed66d6f1927b5f8f0b6d31b18de9 +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json +@@ -0,0 +1 @@ ++{"config":{"chainId":534351,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiBlock":0,"bernoulliBlock":3747132,"curieBlock":4740239,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","enableEIP2718":false,"enableEIP1559":false,"l1Config":{"l1ChainId":"11155111","l1MessageQueueAddress":"0xF0B2293F5D834eAe920c6974D50957A1732de763","scrollChainAddress":"0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x64cfd015","extraData":"0x000000000000000000000000000000000000000000000000000000000000000048C3F81f3D998b6652900e1C3183736C238Fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"8000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x18960EEc21b1878C581937a14c5c3C43008F6b6B":{"balance":"10000000000000000000"},"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d":{"balance":"226156424291633194186662080095093570025917938800079226629565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61037d565b60405161013891906112cf565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611305565b61040f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461132f565b610429565b3480156101bc57600080fd5b5061010f6101cb36600461136b565b61044d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b50610182610523565b34801561020d57600080fd5b5061016161021c366004611305565b610532565b34801561022d57600080fd5b5061018261023c366004611384565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b50610182610272366004611384565b610554565b34801561028357600080fd5b5061028c610572565b604051610138979695949392919061139f565b3480156102ab57600080fd5b5061012b6105fb565b3480156102c057600080fd5b506101616102cf366004611305565b61060a565b3480156102e057600080fd5b506101616102ef366004611305565b610685565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611435565b610693565b34801561032857600080fd5b506101826103373660046114a8565b6107f7565b6103463334610856565b60405134815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2565b60606003805461038c906114db565b80601f01602080910402602001604051908101604052809291908181526020018280546103b8906114db565b80156104055780601f106103da57610100808354040283529160200191610405565b820191906000526020600020905b8154815290600101906020018083116103e857829003601f168201915b5050505050905090565b60003361041d818585610915565b60019150505b92915050565b600033610437858285610a3a565b610442858585610ab4565b506001949350505050565b6104573382610c58565b604051600090339083908381818185875af1925050503d8060008114610499576040519150601f19603f3d011682016040523d82523d6000602084013e61049e565b606091505b50509050806104ea5760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b60405182815233907f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b659060200160405180910390a25050565b600061052d610d87565b905090565b60003361041d81858561054583836107f7565b61054f919061150f565b610915565b6001600160a01b038116600090815260076020526040812054610423565b6000606080828080836105a67f577261707065642045746865720000000000000000000000000000000000000d6005610eb2565b6105d17f31000000000000000000000000000000000000000000000000000000000000016006610eb2565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461038c906114db565b6000338161061882866107f7565b9050838110156106785760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104e1565b6104428286868403610915565b60003361041d818585610ab4565b834211156106e35760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104e1565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886107128c610f56565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061076d82610f7e565b9050600061077d82878787610fab565b9050896001600160a01b0316816001600160a01b0316146107e05760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104e1565b6107eb8a8a8a610915565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600060208351101561083e5761083783610fd3565b9050610423565b816108498482611594565b5060ff9050610423565b90565b6001600160a01b0382166108ac5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104e1565b80600260008282546108be919061150f565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109775760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104e1565b6001600160a01b0382166109d85760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104e1565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a4684846107f7565b90506000198114610aae5781811015610aa15760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104e1565b610aae8484848403610915565b50505050565b6001600160a01b038316610b185760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104e1565b6001600160a01b038216610b7a5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104e1565b6001600160a01b03831660009081526020819052604090205481811015610bf25760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104e1565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610aae565b6001600160a01b038216610cb85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104e1565b6001600160a01b03821660009081526020819052604090205481811015610d2c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104e1565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a2d565b505050565b6000306001600160a01b037f0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa316148015610de057507f000000000000000000000000000000000000000000000000000000000008274f46145b15610e0a57507f624453decb4e78ca99c7630ff9f52222ea6f559f0a6c1bb60b935ef006fa159e90565b61052d604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ec55761083783611011565b818054610ed1906114db565b80601f0160208091040260200160405190810160405280929190818152602001828054610efd906114db565b8015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b50505050509050610423565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610423610f8b610d87565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fbc87878787611050565b91509150610fc981611114565b5095945050505050565b600080829050601f81511115610ffe578260405163305a27a960e01b81526004016104e191906112cf565b805161100982611654565b179392505050565b6060600061101e83611261565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611087575060009050600361110b565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156110db573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166111045760006001925092505061110b565b9150600090505b94509492505050565b600081600481111561112857611128611678565b036111305750565b600181600481111561114457611144611678565b036111915760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104e1565b60028160048111156111a5576111a5611678565b036111f25760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104e1565b600381600481111561120657611206611678565b0361125e5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104e1565b50565b600060ff8216601f81111561042357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112af57602081850181015186830182015201611293565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006112e26020830184611289565b9392505050565b80356001600160a01b038116811461130057600080fd5b919050565b6000806040838503121561131857600080fd5b611321836112e9565b946020939093013593505050565b60008060006060848603121561134457600080fd5b61134d846112e9565b925061135b602085016112e9565b9150604084013590509250925092565b60006020828403121561137d57600080fd5b5035919050565b60006020828403121561139657600080fd5b6112e2826112e9565b60ff60f81b881681526000602060e0818401526113bf60e084018a611289565b83810360408501526113d1818a611289565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561142357835183529284019291840191600101611407565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561145057600080fd5b611459886112e9565b9650611467602089016112e9565b95506040880135945060608801359350608088013560ff8116811461148b57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114bb57600080fd5b6114c4836112e9565b91506114d2602084016112e9565b90509250929050565b600181811c908216806114ef57607f821691505b602082108103610f7857634e487b7160e01b600052602260045260246000fd5b8082018082111561042357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610d8257600081815260208120601f850160051c8101602086101561156d5750805b601f850160051c820191505b8181101561158c57828155600101611579565b505050505050565b815167ffffffffffffffff8111156115ae576115ae611530565b6115c2816115bc84546114db565b84611546565b602080601f8311600181146115f757600084156115df5750858301515b600019600386901b1c1916600185901b17855561158c565b600085815260208120601f198616915b8281101561162657888601518255948401946001909101908401611607565b50858210156116445787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610f785760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122075458b204a41338df799effa8b73c6c1a17e612bc3b3311c0cec123c4da7709964736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100a05760003560e01c806384411d651161006457806384411d65146101595780638da5cb5b1461016f5780639e7adc791461018f578063f2fde38b146101af578063feec756c146101cf578063ff4f3546146101ef57600080fd5b80633cb747bf146100ac5780633ccfd60b146100e9578063457e1a491461010057806366d003ac14610124578063715018a61461014457600080fd5b366100a757005b600080fd5b3480156100b857600080fd5b506002546100cc906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100f557600080fd5b506100fe61020f565b005b34801561010c57600080fd5b5061011660015481565b6040519081526020016100e0565b34801561013057600080fd5b506003546100cc906001600160a01b031681565b34801561015057600080fd5b506100fe610371565b34801561016557600080fd5b5061011660045481565b34801561017b57600080fd5b506000546100cc906001600160a01b031681565b34801561019b57600080fd5b506100fe6101aa3660046105ea565b6103a7565b3480156101bb57600080fd5b506100fe6101ca3660046105ea565b610423565b3480156101db57600080fd5b506100fe6101ea3660046105ea565b6104af565b3480156101fb57600080fd5b506100fe61020a36600461061a565b61052b565b60015447908110156102a15760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b6004805482019055600354604080518381526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b94879461033c949190921692859290600401610633565b6000604051808303818588803b15801561035557600080fd5b505af1158015610369573d6000803e3d6000fd5b505050505050565b6000546001600160a01b0316331461039b5760405162461bcd60e51b81526004016102989061069f565b6103a5600061059a565b565b6000546001600160a01b031633146103d15760405162461bcd60e51b81526004016102989061069f565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b0316331461044d5760405162461bcd60e51b81526004016102989061069f565b6001600160a01b0381166104a35760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610298565b6104ac8161059a565b50565b6000546001600160a01b031633146104d95760405162461bcd60e51b81526004016102989061069f565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105555760405162461bcd60e51b81526004016102989061069f565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156105fc57600080fd5b81356001600160a01b038116811461061357600080fd5b9392505050565b60006020828403121561062c57600080fd5b5035919050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b818110156106755786810183015185820160a001528201610659565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea26469706673582212200c5bec0af207d4c7845829d5330f295a5f16702ab8bde670ae90be68974af0a764736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d","0x0000000000000000000000000000000000000000000000000000000000000003":"0x2351C7aD0c8cFEB25c81301EAC922ab1f1980bbe"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+66
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/src/constants.rs scroll-reth/crates/scroll/chainspec/src/constants.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..c4d004e2af9c24ed9fad59adfa7dafdaa804d06d +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/src/constants.rs +@@ -0,0 +1,66 @@ ++use crate::genesis::L1Config; ++use alloy_primitives::{address, Address}; + -+ // load the l1 gas oracle contract in cache -+ let _ = -+ self.state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS).map_err(|err| err.into())?; ++/// The transaction fee recipient on the L2. ++pub const SCROLL_FEE_VAULT_ADDRESS: Address = address!("5300000000000000000000000000000000000005"); + -+ if self -+ .evm_config -+ .chain_spec() -+ .fork(ScrollHardfork::Curie) -+ .transitions_at_block(block.number) -+ { -+ if let Err(err) = apply_curie_hard_fork(&mut self.state) { -+ tracing::debug!(%err, "failed to apply curie hardfork"); -+ return Err(ForkError::Curie(err.to_string()).into()); -+ }; -+ } ++/// The L1 message queue address for Scroll mainnet. ++/// <https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B>. ++pub const SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS: Address = ++ address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"); + -+ Ok(()) -+ } ++/// The L1 proxy address for Scroll mainnet. ++/// <https://etherscan.io/address/0xa13BAF47339d63B743e7Da8741db5456DAc1E556>. ++pub const SCROLL_MAINNET_L1_PROXY_ADDRESS: Address = ++ address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"); + -+ fn execute_transactions( -+ &mut self, -+ block: &BlockWithSenders, -+ ) -> Result<ExecuteOutput, Self::Error> { -+ let env = self.evm_env_for_block(&block.header); -+ let mut evm = self.evm_config.evm_with_env(&mut self.state, env); ++/// The maximum allowed l1 messages per block for Scroll mainnet. ++pub const SCROLL_MAINNET_MAX_L1_MESSAGES: u64 = 10; + -+ let mut cumulative_gas_used = 0; -+ let mut receipts = Vec::with_capacity(block.body.transactions.len()); -+ let chain_spec = self.evm_config.chain_spec(); ++/// The L1 configuration for Scroll mainnet. ++pub const SCROLL_MAINNET_L1_CONFIG: L1Config = L1Config { ++ l1_chain_id: alloy_chains::NamedChain::Mainnet as u64, ++ l1_message_queue_address: SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, ++ scroll_chain_address: SCROLL_MAINNET_L1_PROXY_ADDRESS, ++ num_l1_messages_per_block: SCROLL_MAINNET_MAX_L1_MESSAGES, ++}; + -+ for (sender, transaction) in block.transactions_with_sender() { -+ // The sum of the transaction’s gas limit and the gas utilized in this block prior, -+ // must be no greater than the block’s gasLimit. -+ let block_available_gas = block.header.gas_limit - cumulative_gas_used; -+ if transaction.gas_limit() > block_available_gas { -+ return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { -+ transaction_gas_limit: transaction.gas_limit(), -+ block_available_gas, -+ } -+ .into()) -+ } ++/// The L1 message queue address for Scroll sepolia. ++/// <https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763>. ++pub const SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS: Address = ++ address!("F0B2293F5D834eAe920c6974D50957A1732de763"); + -+ // verify the transaction type is accepted by the current fork. -+ match transaction.tx_type() { -+ TxType::Eip2930 if !chain_spec.is_curie_active_at_block(block.number) => { -+ return Err(ConsensusError::InvalidTransaction( -+ InvalidTransactionError::Eip2930Disabled, -+ ) -+ .into()) -+ } -+ TxType::Eip1559 if !chain_spec.is_curie_active_at_block(block.number) => { -+ return Err(ConsensusError::InvalidTransaction( -+ InvalidTransactionError::Eip1559Disabled, -+ ) -+ .into()) -+ } -+ TxType::Eip4844 => { -+ return Err(ConsensusError::InvalidTransaction( -+ InvalidTransactionError::Eip4844Disabled, -+ ) -+ .into()) -+ } -+ TxType::Eip7702 => { -+ return Err(ConsensusError::InvalidTransaction( -+ InvalidTransactionError::Eip7702Disabled, -+ ) -+ .into()) -+ } -+ _ => (), -+ }; ++/// The L1 proxy address for Scroll sepolia. ++/// <https://sepolia.etherscan.io/address/0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0> ++pub const SCROLL_SEPOLIA_L1_PROXY_ADDRESS: Address = ++ address!("2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"); + -+ self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); -+ if transaction.is_l1_message() { -+ evm.context.evm.env.cfg.disable_base_fee = true; // disable base fee for l1 msg -+ } ++/// The maximum allowed l1 messages per block for Scroll sepolia. ++pub const SCROLL_SEPOLIA_MAX_L1_MESSAGES: u64 = 10; + -+ // execute the transaction and commit the result to the database -+ let ResultAndState { result, state } = -+ evm.transact().map_err(|err| BlockValidationError::EVM { -+ hash: transaction.recalculate_hash(), -+ error: Box::new(err.map_db_err(|e| e.into())), -+ })?; -+ evm.db_mut().commit(state); ++/// The L1 configuration for Scroll sepolia. ++pub const SCROLL_SEPOLIA_L1_CONFIG: L1Config = L1Config { ++ l1_chain_id: alloy_chains::NamedChain::Sepolia as u64, ++ l1_message_queue_address: SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, ++ scroll_chain_address: SCROLL_SEPOLIA_L1_PROXY_ADDRESS, ++ num_l1_messages_per_block: SCROLL_SEPOLIA_MAX_L1_MESSAGES, ++}; + -+ let l1_fee = if transaction.is_l1_message() { -+ // l1 messages do not get any gas refunded -+ if let ExecutionResult::Success { gas_refunded, .. } = result { -+ cumulative_gas_used += gas_refunded -+ } ++/// The L1 message queue address for Scroll dev. ++pub const SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS: Address = ++ address!("0000000000000000000000000000000000000000"); + -+ U256::ZERO -+ } else { -+ // compute l1 fee for all non-l1 transaction -+ let l1_block_info = -+ evm.context.evm.inner.l1_block_info.as_ref().expect("l1_block_info loaded"); -+ let transaction_rlp_bytes = -+ evm.context.evm.env.tx.scroll.rlp_bytes.as_ref().expect("rlp_bytes loaded"); -+ l1_block_info.calculate_tx_l1_cost(transaction_rlp_bytes, evm.handler.cfg.spec_id) -+ }; ++/// The L1 proxy address for Scroll dev. ++pub const SCROLL_DEV_L1_PROXY_ADDRESS: Address = ++ address!("0000000000000000000000000000000000000000"); + -+ cumulative_gas_used += result.gas_used(); ++/// The maximum allowed l1 messages per block for Scroll dev. ++pub const SCROLL_DEV_MAX_L1_MESSAGES: u64 = 10; + -+ receipts.push(Receipt { -+ tx_type: transaction.tx_type(), -+ success: result.is_success(), -+ cumulative_gas_used, -+ logs: result.into_logs(), -+ l1_fee, -+ }) -+ } ++/// The L1 configuration for Scroll dev. ++pub const SCROLL_DEV_L1_CONFIG: L1Config = L1Config { ++ l1_chain_id: alloy_chains::NamedChain::Goerli as u64, ++ l1_message_queue_address: SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, ++ scroll_chain_address: SCROLL_DEV_L1_PROXY_ADDRESS, ++ num_l1_messages_per_block: SCROLL_DEV_MAX_L1_MESSAGES, ++};
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+32
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/src/dev.rs scroll-reth/crates/scroll/chainspec/src/dev.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..bba6bfdb0aac38d07ee974f572b1a520b462d760 +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/src/dev.rs +@@ -0,0 +1,32 @@ ++//! Chain specification in dev mode for custom chain. + -+ Ok(ExecuteOutput { receipts, gas_used: cumulative_gas_used }) -+ } ++use alloc::sync::Arc; + -+ fn apply_post_execution_changes( -+ &mut self, -+ _block: &BlockWithSenders, -+ _receipts: &[Receipt], -+ ) -> Result<Requests, Self::Error> { -+ Ok(Default::default()) -+ } ++use alloy_chains::Chain; ++use alloy_consensus::constants::DEV_GENESIS_HASH; ++use alloy_primitives::U256; ++use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; ++use reth_scroll_forks::DEV_HARDFORKS; + -+ fn state_ref(&self) -> &State<DB> { -+ &self.state -+ } ++use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; + -+ fn state_mut(&mut self) -> &mut State<DB> { -+ &mut self.state ++/// Scroll dev testnet specification ++/// ++/// Includes 20 prefunded accounts with `10_000` ETH each derived from mnemonic "test test test test ++/// test test test test test test test junk". ++pub static SCROLL_DEV: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { ++ ScrollChainSpec { ++ inner: ChainSpec { ++ chain: Chain::dev(), ++ genesis: serde_json::from_str(include_str!("../res/genesis/dev.json")) ++ .expect("Can't deserialize Dev testnet genesis json"), ++ genesis_hash: once_cell_set(DEV_GENESIS_HASH), ++ paris_block_and_final_difficulty: Some((0, U256::from(0))), ++ hardforks: DEV_HARDFORKS.clone(), ++ base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), ++ ..Default::default() ++ }, ++ config: ScrollChainConfig::dev(), + } ++ .into() ++});
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+225
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/src/genesis.rs scroll-reth/crates/scroll/chainspec/src/genesis.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..9bd1a0c654a01f43fe2278a498d5e20d96ea160b +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/src/genesis.rs +@@ -0,0 +1,225 @@ ++//! Scroll types for genesis data. + -+ fn validate_block_post_execution( -+ &self, -+ block: &BlockWithSenders, -+ receipts: &[Receipt], -+ _requests: &Requests, -+ ) -> Result<(), ConsensusError> { -+ // verify the block gas used -+ let cumulative_gas_used = receipts.last().map(|r| r.cumulative_gas_used).unwrap_or(0); -+ if block.gas_used != cumulative_gas_used { -+ return Err(ConsensusError::BlockGasUsed { -+ gas: GotExpected { got: cumulative_gas_used, expected: block.gas_used }, -+ gas_spent_by_tx: gas_spent_by_transactions(receipts), -+ }); -+ } ++use crate::{ ++ constants::{SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, SCROLL_SEPOLIA_L1_CONFIG}, ++ SCROLL_DEV_L1_CONFIG, ++}; ++use alloy_primitives::Address; ++use alloy_serde::OtherFields; ++use serde::de::Error; + -+ // verify the receipts logs bloom and root -+ if self.evm_config.chain_spec().is_byzantium_active_at_block(block.header.number) { -+ if let Err(error) = reth_ethereum_consensus::verify_receipts( -+ block.header.receipts_root, -+ block.header.logs_bloom, -+ receipts, -+ ) { -+ tracing::debug!( -+ %error, -+ ?receipts, -+ header_receipt_root = ?block.header.receipts_root, -+ header_bloom = ?block.header.logs_bloom, -+ "failed to verify receipts" -+ ); -+ return Err(error); -+ } -+ } ++/// Container type for all Scroll-specific fields in a genesis file. ++/// This struct represents the configuration details and metadata ++/// that are specific to the Scroll blockchain, used during the chain's initialization. ++#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct ScrollChainInfo { ++ /// Information about hard forks specific to the Scroll chain. ++ /// This optional field contains metadata about various hard fork ++ /// configurations that are specific to the Scroll blockchain. ++ pub hard_fork_info: Option<ScrollHardforkInfo>, ++ /// Scroll chain-specific configuration details. ++ /// Encapsulates special parameters and settings ++ /// required for Scroll chain functionality, such as fee-related ++ /// addresses and Layer 1 configuration. ++ pub scroll_chain_config: ScrollChainConfig, ++} + -+ Ok(()) ++impl ScrollChainInfo { ++ /// Extracts the Scroll specific fields from a genesis file. These fields are expected to be ++ /// contained in the `genesis.config` under `extra_fields` property. ++ pub fn extract_from(others: &OtherFields) -> Option<Self> { ++ Self::try_from(others).ok() + } +} + -+/// The factory for a [`ScrollExecutionStrategy`]. -+#[derive(Clone, Debug)] -+pub struct ScrollExecutionStrategyFactory<EvmConfig = ScrollEvmConfig> { -+ /// The Evm configuration for the [`ScrollExecutionStrategy`]. -+ evm_config: EvmConfig, -+} ++impl TryFrom<&OtherFields> for ScrollChainInfo { ++ type Error = serde_json::Error; + -+impl ScrollExecutionStrategyFactory { -+ /// Returns a new instance of the [`ScrollExecutionStrategyFactory`]. -+ pub const fn new(chain_spec: Arc<ScrollChainSpec>) -> Self { -+ let evm_config = ScrollEvmConfig::new(chain_spec); -+ Self { evm_config } -+ } ++ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { ++ let hard_fork_info = ScrollHardforkInfo::try_from(others).ok(); ++ let scroll_chain_config = ScrollChainConfig::try_from(others)?; + -+ /// Returns the EVM configuration for the strategy factory. -+ pub fn evm_config(&self) -> ScrollEvmConfig { -+ self.evm_config.clone() ++ Ok(Self { hard_fork_info, scroll_chain_config }) + } +} + -+impl<EvmConfig> BlockExecutionStrategyFactory for ScrollExecutionStrategyFactory<EvmConfig> -+where -+ EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned> -+ + ChainSpecProvider<ChainSpec = ScrollChainSpec>, -+{ -+ type Primitives = EthPrimitives; -+ type Strategy<DB: Database<Error: Into<ProviderError> + Display>> = -+ ScrollExecutionStrategy<DB, EvmConfig>; ++/// [`ScrollHardforkInfo`] specifies the block numbers and timestamps at which the Scroll hardforks ++/// were activated. ++#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct ScrollHardforkInfo { ++ /// archimedes block number ++ pub archimedes_block: Option<u64>, ++ /// bernoulli block number ++ pub bernoulli_block: Option<u64>, ++ /// curie block number ++ pub curie_block: Option<u64>, ++ /// darwin hardfork timestamp ++ pub darwin_time: Option<u64>, ++ /// darwinV2 hardfork timestamp ++ pub darwin_v2_time: Option<u64>, ++} + -+ /// Creates a strategy using the give database. -+ fn create_strategy<DB>(&self, db: DB) -> Self::Strategy<DB> -+ where -+ DB: Database<Error: Into<ProviderError> + Display>, -+ { -+ let state = -+ State::builder().with_database(db).without_state_clear().with_bundle_update().build(); -+ ScrollExecutionStrategy::new(self.evm_config.clone(), state) ++impl ScrollHardforkInfo { ++ /// Extract the Scroll-specific genesis info from a genesis file. ++ pub fn extract_from(others: &OtherFields) -> Option<Self> { ++ Self::try_from(others).ok() + } +} + -+/// Helper type with backwards compatible methods to obtain Scroll executor -+/// providers. -+#[derive(Debug)] -+pub struct ScrollExecutorProvider; ++impl TryFrom<&OtherFields> for ScrollHardforkInfo { ++ type Error = serde_json::Error; + -+impl ScrollExecutorProvider { -+ /// Creates a new default scroll executor provider. -+ pub const fn scroll( -+ chain_spec: Arc<ScrollChainSpec>, -+ ) -> BasicBlockExecutorProvider<ScrollExecutionStrategyFactory> { -+ BasicBlockExecutorProvider::new(ScrollExecutionStrategyFactory::new(chain_spec)) ++ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { ++ others.deserialize_as() + } +} + -+#[cfg(test)] -+mod tests { -+ use super::*; -+ use crate::{ScrollEvmConfig, ScrollExecutionStrategy, ScrollExecutionStrategyFactory}; -+ use reth_chainspec::MIN_TRANSACTION_GAS; -+ use reth_evm::execute::ExecuteOutput; -+ use reth_primitives::{Block, BlockBody, BlockWithSenders, Receipt, TransactionSigned, TxType}; -+ use reth_primitives_traits::transaction::signed::SignedTransaction; -+ use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; -+ use reth_scroll_consensus::{ -+ BLOB_SCALAR_SLOT, COMMIT_SCALAR_SLOT, CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, -+ CURIE_L1_GAS_PRICE_ORACLE_STORAGE, IS_CURIE_SLOT, L1_BASE_FEE_SLOT, L1_BLOB_BASE_FEE_SLOT, -+ L1_GAS_PRICE_ORACLE_ADDRESS, OVER_HEAD_SLOT, SCALAR_SLOT, -+ }; -+ use revm::{ -+ db::{ -+ states::{bundle_state::BundleRetention, StorageSlot}, -+ EmptyDBTyped, -+ }, -+ primitives::{AccountInfo, Address, Bytecode, TxKind, B256, U256}, -+ }; -+ -+ const BLOCK_GAS_LIMIT: u64 = 10_000_000; -+ const SCROLL_CHAIN_ID: u64 = 534352; -+ const NOT_CURIE_BLOCK_NUMBER: u64 = 7096835; -+ const CURIE_BLOCK_NUMBER: u64 = 7096837; ++/// The Scroll l1 config ++#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct L1Config { ++ /// l1 chain id ++ pub l1_chain_id: u64, ++ /// The L1 contract address of the contract that handles the message queue targeting the Scroll ++ /// rollup. ++ pub l1_message_queue_address: Address, ++ /// The L1 contract address of the proxy contract which is responsible for Scroll rollup ++ /// settlement. ++ pub scroll_chain_address: Address, ++ /// The maximum number of L1 messages to be consumed per L2 rollup block. ++ pub num_l1_messages_per_block: u64, ++} + -+ fn strategy() -> ScrollExecutionStrategy<EmptyDBTyped<ProviderError>, ScrollEvmConfig> { -+ let chain_spec = -+ Arc::new(ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet())); -+ let factory = ScrollExecutionStrategyFactory::new(chain_spec); -+ let db = EmptyDBTyped::<ProviderError>::new(); ++/// The configuration for the Scroll sequencer chain. ++/// This struct holds the configuration details specific to the Scroll chain, ++/// including fee-related addresses and L1 chain-specific settings. ++#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] ++#[serde(rename_all = "camelCase")] ++pub struct ScrollChainConfig { ++ /// The address of the L2 transaction fee vault. ++ /// This is an optional field that, when set, specifies where L2 transaction fees ++ /// will be sent or stored. ++ pub fee_vault_address: Option<Address>, ++ /// The L1 configuration. ++ /// This field encapsulates specific settings and parameters required for L1 ++ pub l1_config: L1Config, ++} + -+ factory.create_strategy(db) ++impl ScrollChainConfig { ++ /// Extracts the scroll special info by looking for the `scroll` key. It is intended to be ++ /// parsed from a genesis file. ++ pub fn extract_from(others: &OtherFields) -> Option<Self> { ++ Self::try_from(others).ok() + } + -+ fn block(number: u64, transactions: Vec<TransactionSigned>) -> BlockWithSenders { -+ let senders = transactions.iter().map(|t| t.recover_signer().unwrap()).collect(); -+ BlockWithSenders::new_unchecked( -+ Block { -+ header: Header { number, gas_limit: BLOCK_GAS_LIMIT, ..Default::default() }, -+ body: BlockBody { transactions, ..Default::default() }, -+ }, -+ senders, -+ ) ++ /// Returns the [`ScrollChainConfig`] for Scroll Mainnet. ++ pub const fn mainnet() -> Self { ++ Self { ++ fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), ++ l1_config: SCROLL_MAINNET_L1_CONFIG, ++ } + } + -+ fn transaction(typ: TxType, gas_limit: u64) -> TransactionSigned { -+ let mut transaction = match typ { -+ TxType::Legacy => reth_primitives::Transaction::Legacy(alloy_consensus::TxLegacy { -+ to: TxKind::Call(Address::ZERO), -+ ..Default::default() -+ }), -+ TxType::Eip2930 => reth_primitives::Transaction::Eip2930(alloy_consensus::TxEip2930 { -+ to: TxKind::Call(Address::ZERO), -+ ..Default::default() -+ }), -+ TxType::Eip1559 => reth_primitives::Transaction::Eip1559(alloy_consensus::TxEip1559 { -+ to: TxKind::Call(Address::ZERO), -+ ..Default::default() -+ }), -+ TxType::Eip4844 => reth_primitives::Transaction::Eip4844(alloy_consensus::TxEip4844 { -+ to: Address::ZERO, -+ ..Default::default() -+ }), -+ TxType::Eip7702 => reth_primitives::Transaction::Eip7702(alloy_consensus::TxEip7702 { -+ to: Address::ZERO, -+ ..Default::default() -+ }), -+ TxType::L1Message => { -+ reth_primitives::Transaction::L1Message(reth_scroll_primitives::TxL1Message { -+ sender: Address::random(), -+ to: Address::ZERO, -+ ..Default::default() -+ }) -+ } -+ }; -+ transaction.set_chain_id(SCROLL_CHAIN_ID); -+ transaction.set_gas_limit(gas_limit); -+ -+ let pk = B256::random(); -+ let signature = reth_primitives::sign_message(pk, transaction.signature_hash()).unwrap(); -+ TransactionSigned::new_unhashed(transaction, signature) ++ /// Returns the [`ScrollChainConfig`] for Scroll Sepolia. ++ pub const fn sepolia() -> Self { ++ Self { ++ fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), ++ l1_config: SCROLL_SEPOLIA_L1_CONFIG, ++ } + } + -+ fn execute_transactions( -+ tx_type: TxType, -+ block_number: u64, -+ expected_l1_fee: U256, -+ expected_error: Option<&str>, -+ ) -> eyre::Result<()> { -+ // init strategy -+ let mut strategy = strategy(); -+ -+ // prepare transaction -+ let transaction = transaction(tx_type, MIN_TRANSACTION_GAS); -+ let block = block(block_number, vec![transaction]); ++ /// Returns the [`ScrollChainConfig`] for Scroll dev. ++ pub const fn dev() -> Self { ++ Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), l1_config: SCROLL_DEV_L1_CONFIG } ++ } ++} + -+ // determine l1 gas oracle storage -+ let l1_gas_oracle_storage = -+ if strategy.evm_config.chain_spec().is_curie_active_at_block(block_number) { -+ vec![ -+ (L1_BASE_FEE_SLOT, U256::from(1000)), -+ (OVER_HEAD_SLOT, U256::from(1000)), -+ (SCALAR_SLOT, U256::from(1000)), -+ (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), -+ (COMMIT_SCALAR_SLOT, U256::from(1000)), -+ (BLOB_SCALAR_SLOT, U256::from(10000)), -+ (IS_CURIE_SLOT, U256::from(1)), -+ ] -+ } else { -+ vec![ -+ (L1_BASE_FEE_SLOT, U256::from(1000)), -+ (OVER_HEAD_SLOT, U256::from(1000)), -+ (SCALAR_SLOT, U256::from(1000)), -+ ] -+ } -+ .into_iter() -+ .collect(); ++impl TryFrom<&OtherFields> for ScrollChainConfig { ++ type Error = serde_json::Error; + -+ // load accounts in state -+ strategy.state.insert_account_with_storage( -+ L1_GAS_PRICE_ORACLE_ADDRESS, -+ Default::default(), -+ l1_gas_oracle_storage, -+ ); -+ for add in block.senders() { -+ strategy -+ .state -+ .insert_account(*add, AccountInfo { balance: U256::MAX, ..Default::default() }); ++ fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { ++ if let Some(Ok(scroll_chain_config)) = others.get_deserialized::<Self>("scroll") { ++ Ok(scroll_chain_config) ++ } else { ++ Err(serde_json::Error::missing_field("scroll")) + } ++ } ++} + -+ // execute and verify output -+ let res = strategy.execute_transactions(&block); -+ -+ // check for error or execution outcome -+ if let Some(error) = expected_error { -+ assert_eq!(res.unwrap_err().to_string(), error); -+ } else { -+ let ExecuteOutput { receipts, .. } = res?; -+ let expected = vec![Receipt { -+ tx_type, -+ cumulative_gas_used: MIN_TRANSACTION_GAS, -+ success: true, -+ l1_fee: expected_l1_fee, -+ ..Default::default() -+ }]; ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use alloy_primitives::address; + -+ assert_eq!(receipts, expected); ++ #[test] ++ fn test_extract_scroll_genesis_info() { ++ let genesis_info = r#" ++ { ++ "archimedesBlock": 0, ++ "bernoulliBlock": 10, ++ "curieBlock": 12, ++ "darwinTime": 0 + } ++ "#; + -+ Ok(()) ++ let others: OtherFields = serde_json::from_str(genesis_info).unwrap(); ++ let genesis_info = ScrollHardforkInfo::extract_from(&others).unwrap(); ++ ++ assert_eq!( ++ genesis_info, ++ ScrollHardforkInfo { ++ archimedes_block: Some(0), ++ bernoulli_block: Some(10), ++ curie_block: Some(12), ++ darwin_time: Some(0), ++ darwin_v2_time: None, ++ } ++ ); + } + + #[test] -+ fn test_apply_pre_execution_changes_curie_block() -> eyre::Result<()> { -+ // init strategy -+ let mut strategy = strategy(); ++ fn test_extract_scroll_chain_info() { ++ let chain_info_str = r#" ++ { ++ "archimedesBlock": 0, ++ "bernoulliBlock": 10, ++ "curieBlock": 12, ++ "darwinTime": 0, ++ "scroll": { ++ "feeVaultAddress": "0x5300000000000000000000000000000000000005", ++ "l1Config": { ++ "l1ChainId": 1, ++ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", ++ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", ++ "numL1MessagesPerBlock": 10 ++ } ++ } ++ } ++ "#; + -+ // init curie transition block -+ let curie_block = block(7096836, vec![]); -+ -+ // apply pre execution change -+ strategy.apply_pre_execution_changes(&curie_block)?; -+ -+ // take bundle -+ let mut state = strategy.state; -+ state.merge_transitions(BundleRetention::Reverts); -+ let bundle = state.take_bundle(); -+ -+ // assert oracle contract contains updated bytecode -+ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); -+ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); -+ assert_eq!(oracle.info.unwrap().code.unwrap(), bytecode); -+ -+ // check oracle contract contains storage changeset -+ let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); -+ storage.sort_by(|(a, _), (b, _)| a.cmp(b)); -+ for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { -+ assert_eq!(got.0, expected.0); -+ assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); -+ } -+ -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_apply_pre_execution_changes_not_curie_block() -> eyre::Result<()> { -+ // init strategy -+ let mut strategy = strategy(); -+ -+ // init block -+ let not_curie_block = block(7096837, vec![]); -+ -+ // apply pre execution change -+ strategy.apply_pre_execution_changes(&not_curie_block)?; -+ -+ // take bundle -+ let mut state = strategy.state; -+ state.merge_transitions(BundleRetention::Reverts); -+ let bundle = state.take_bundle(); -+ -+ // assert oracle contract is empty -+ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS); -+ assert!(oracle.is_none()); -+ -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_exceeds_block_gas_limit() -> eyre::Result<()> { -+ // init strategy -+ let mut strategy = strategy(); -+ -+ // prepare transaction exceeding block gas limit -+ let transaction = transaction(TxType::Legacy, BLOCK_GAS_LIMIT + 1); -+ let block = block(7096837, vec![transaction]); -+ -+ // execute and verify error -+ let res = strategy.execute_transactions(&block); -+ assert_eq!( -+ res.unwrap_err().to_string(), -+ "transaction gas limit 10000001 is more than blocks available gas 10000000" -+ ); -+ -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_eip4844() -> eyre::Result<()> { -+ // Execute eip4844 transaction -+ execute_transactions( -+ TxType::Eip4844, -+ CURIE_BLOCK_NUMBER, -+ U256::ZERO, -+ Some("EIP-4844 transactions are disabled"), -+ )?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_eip7702() -> eyre::Result<()> { -+ // Execute eip7702 transaction -+ execute_transactions( -+ TxType::Eip7702, -+ CURIE_BLOCK_NUMBER, -+ U256::ZERO, -+ Some("EIP-7702 transactions are disabled"), -+ )?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_l1_message() -> eyre::Result<()> { -+ // Execute l1 message on curie block -+ let expected_l1_fee = U256::ZERO; -+ execute_transactions(TxType::L1Message, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_legacy_curie_fork() -> eyre::Result<()> { -+ // Execute legacy transaction on curie block -+ let expected_l1_fee = U256::from(10); -+ execute_transactions(TxType::Legacy, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_legacy_not_curie_fork() -> eyre::Result<()> { -+ // Execute legacy before curie block -+ let expected_l1_fee = U256::from(2); -+ execute_transactions(TxType::Legacy, NOT_CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_eip2930_curie_fork() -> eyre::Result<()> { -+ // Execute eip2930 transaction on curie block -+ let expected_l1_fee = U256::from(10); -+ execute_transactions(TxType::Eip2930, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_eip2930_not_curie_fork() -> eyre::Result<()> { -+ // Execute eip2930 transaction before curie block -+ execute_transactions( -+ TxType::Eip2930, -+ NOT_CURIE_BLOCK_NUMBER, -+ U256::ZERO, -+ Some("EIP-2930 transactions are disabled"), -+ )?; -+ Ok(()) -+ } -+ -+ #[test] -+ fn test_execute_transactions_eip1559_curie_fork() -> eyre::Result<()> { -+ // Execute eip1559 transaction on curie block -+ let expected_l1_fee = U256::from(10); -+ execute_transactions(TxType::Eip1559, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; -+ Ok(()) -+ } ++ let others: OtherFields = serde_json::from_str(chain_info_str).unwrap(); ++ let chain_info = ScrollChainInfo::extract_from(&others).unwrap(); + -+ #[test] -+ fn test_execute_transactions_eip_not_curie_fork() -> eyre::Result<()> { -+ // Execute eip1559 transaction before curie block -+ execute_transactions( -+ TxType::Eip1559, -+ NOT_CURIE_BLOCK_NUMBER, -+ U256::ZERO, -+ Some("EIP-1559 transactions are disabled"), -+ )?; -+ Ok(()) ++ let expected = ScrollChainInfo { ++ hard_fork_info: Some(ScrollHardforkInfo { ++ archimedes_block: Some(0), ++ bernoulli_block: Some(10), ++ curie_block: Some(12), ++ darwin_time: Some(0), ++ darwin_v2_time: None, ++ }), ++ scroll_chain_config: ScrollChainConfig { ++ fee_vault_address: Some(address!("5300000000000000000000000000000000000005")), ++ l1_config: L1Config { ++ l1_chain_id: 1, ++ l1_message_queue_address: address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"), ++ scroll_chain_address: address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"), ++ num_l1_messages_per_block: 10, ++ }, ++ }, ++ }; ++ assert_eq!(chain_info, expected); + } +}
@@ -4585,9 +5595,9 @@
@@ -4602,7 +5612,7 @@
@@ -4612,519 +5622,706 @@
-
+13
+
+602
-0
-
diff --git reth/crates/scroll/evm/src/lib.rs scroll-reth/crates/scroll/evm/src/lib.rs +
diff --git reth/crates/scroll/chainspec/src/lib.rs scroll-reth/crates/scroll/chainspec/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..020de5994bfd346ff776df6c3ac6eb293f2f82ab +index 0000000000000000000000000000000000000000..b4f80e79091e31484a7ef2629f3b869fddd3923a --- /dev/null -+++ scroll-reth/crates/scroll/evm/src/lib.rs -@@ -0,0 +1,13 @@ -+//! Scroll evm execution implementation. -+#![cfg(all(feature = "scroll", not(feature = "optimism")))] ++++ scroll-reth/crates/scroll/chainspec/src/lib.rs +@@ -0,0 +1,602 @@ ++//! Scroll-Reth chain specs. + -+pub use config::ScrollEvmConfig; -+mod config; ++#![doc( ++ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", ++ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", ++ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" ++)] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(not(feature = "std"), no_std)] + -+pub use error::{ForkError, ScrollBlockExecutionError}; -+mod error; ++use alloc::{boxed::Box, vec::Vec}; ++use alloy_chains::Chain; ++use alloy_consensus::Header; ++use alloy_genesis::Genesis; ++use alloy_primitives::{B256, U256}; ++use derive_more::{Constructor, Deref, Display, From, Into}; ++use reth_chainspec::{ ++ BaseFeeParams, ChainSpec, ChainSpecBuilder, DepositContract, EthChainSpec, EthereumHardforks, ++ ForkFilter, ForkId, Hardforks, Head, ++}; ++use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; ++use reth_network_peers::NodeRecord; ++use reth_scroll_forks::{ScrollHardfork, ScrollHardforks}; + -+pub use execute::{ -+ ScrollExecutionStrategy, ScrollExecutionStrategyFactory, ScrollExecutorProvider, ++#[cfg(not(feature = "std"))] ++use once_cell::sync::Lazy as LazyLock; ++#[cfg(feature = "std")] ++use std::sync::LazyLock; ++ ++extern crate alloc; ++ ++mod constants; ++pub use constants::{ ++ SCROLL_DEV_L1_CONFIG, SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_DEV_L1_PROXY_ADDRESS, ++ SCROLL_DEV_MAX_L1_MESSAGES, SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, ++ SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_MAINNET_L1_PROXY_ADDRESS, ++ SCROLL_MAINNET_MAX_L1_MESSAGES, SCROLL_SEPOLIA_L1_CONFIG, ++ SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_SEPOLIA_L1_PROXY_ADDRESS, ++ SCROLL_SEPOLIA_MAX_L1_MESSAGES, +}; -+mod execute;
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+42
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/hardforks/Cargo.toml scroll-reth/crates/scroll/hardforks/Cargo.toml -new file mode 100644 -index 0000000000000000000000000000000000000000..09372f5ba5cb190f19cb30126f78e31c0ee811ee ---- /dev/null -+++ scroll-reth/crates/scroll/hardforks/Cargo.toml -@@ -0,0 +1,42 @@ -+[package] -+name = "reth-scroll-forks" -+version.workspace = true -+edition.workspace = true -+rust-version.workspace = true -+license.workspace = true -+homepage.workspace = true -+repository.workspace = true -+description = "Scroll hardforks used in reth" + -+[lints] -+workspace = true ++mod dev; ++pub use dev::SCROLL_DEV; + -+[dependencies] -+# reth -+reth-ethereum-forks.workspace = true ++mod genesis; ++pub use genesis::{ScrollChainConfig, ScrollChainInfo}; + -+# ethereum -+alloy-chains.workspace = true -+alloy-primitives.workspace = true ++// convenience re-export of the chain spec provider. ++pub use reth_chainspec::ChainSpecProvider; + -+# io -+serde = { workspace = true, optional = true } ++mod scroll; ++pub use scroll::SCROLL_MAINNET; + -+# misc -+once_cell.workspace = true ++mod scroll_sepolia; ++pub use scroll_sepolia::SCROLL_SEPOLIA; + -+[features] -+default = ["std"] -+std = [ -+ "alloy-primitives/std", -+ "once_cell/std", -+ "serde?/std", -+ "alloy-chains/std", -+ "reth-ethereum-forks/std" -+] -+serde = [ -+ "dep:serde", -+ "alloy-chains/serde", -+ "alloy-primitives/serde", -+ "reth-ethereum-forks/serde" -+]
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+342
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/hardforks/docs/hardforks.md scroll-reth/crates/scroll/hardforks/docs/hardforks.md -new file mode 100644 -index 0000000000000000000000000000000000000000..fb7d068fedbdeaca76ad435a112f1c60ec7dded2 ---- /dev/null -+++ scroll-reth/crates/scroll/hardforks/docs/hardforks.md -@@ -0,0 +1,343 @@ -+--- -+section: technology -+date: Last Modified -+title: "Scroll Upgrades" -+lang: "en" -+permalink: "technology/overview/scroll-upgrades" -+--- ++/// Chain spec builder for a Scroll chain. ++#[derive(Debug, Default, From)] ++pub struct ScrollChainSpecBuilder { ++ /// [`ChainSpecBuilder`] ++ inner: ChainSpecBuilder, ++} + -+As the team continues to progress on Scroll's roadmap, we will be upgrading the Scroll network to include new features and improvements. ++impl ScrollChainSpecBuilder { ++ /// Construct a new builder from the scroll mainnet chain spec. ++ pub fn scroll_mainnet() -> Self { ++ Self { ++ inner: ChainSpecBuilder::default() ++ .chain(SCROLL_MAINNET.chain) ++ .genesis(SCROLL_MAINNET.genesis.clone()) ++ .with_forks(SCROLL_MAINNET.hardforks.clone()), ++ } ++ } + -+The following contracts are used to initiate upgrades and execute upgrades after the two-week timelock period: ++ /// Construct a new builder from the scroll sepolia chain spec. ++ pub fn scroll_sepolia() -> Self { ++ Self { ++ inner: ChainSpecBuilder::default() ++ .chain(SCROLL_SEPOLIA.chain) ++ .genesis(SCROLL_SEPOLIA.genesis.clone()) ++ .with_forks(SCROLL_SEPOLIA.hardforks.clone()), ++ } ++ } ++} + -+| Contract | Network | Address | -+| ------------------------ | ------- | - | -+| L1 Scroll Multisig | Ethereum| [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://etherscan.io/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe)| -+| L1 Timelock | Ethereum| [`0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C`](https://etherscan.io/address/0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C)| -+| L2 Scroll Multisig | Scroll| [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://scrollscan.com/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe)| -+| L2 Timelock | Scroll | [`0xf6069DB81239E5194bb53f83aF564d282357bc99`](https://scrollscan.com/address/0xf6069DB81239E5194bb53f83aF564d282357bc99)| ++impl ScrollChainSpecBuilder { ++ /// Set the chain ID ++ pub fn chain(mut self, chain: Chain) -> Self { ++ self.inner = self.inner.chain(chain); ++ self ++ } + -+You can join our [Telegram channel for technical updates](https://t.me/scroll_tech_updates), which includes future upgrade announcements and on-chain operation events. ++ /// Set the genesis block. ++ pub fn genesis(mut self, genesis: Genesis) -> Self { ++ self.inner = self.inner.genesis(genesis); ++ self ++ } + -+## DarwinV2 Upgrade ++ /// Add the given fork with the given activation condition to the spec. ++ pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self { ++ self.inner = self.inner.with_fork(fork, condition); ++ self ++ } + -+### Overview ++ /// Add the given forks with the given activation condition to the spec. ++ pub fn with_forks(mut self, forks: ChainHardforks) -> Self { ++ self.inner = self.inner.with_forks(forks); ++ self ++ } + -+During internal testing, we identified that blocks may not always be compressible under certain conditions, which leads to unprovable chunks and batches. -+To fix this issue, a minor upgrade has been conducted so that uncompressed blobs will be enabled when this special case is detected. ++ /// Remove the given fork from the spec. ++ pub fn without_fork(mut self, fork: reth_scroll_forks::ScrollHardfork) -> Self { ++ self.inner = self.inner.without_fork(fork); ++ self ++ } + -+### Timeline ++ /// Enable Archimedes at genesis ++ pub fn archimedes_activated(mut self) -> Self { ++ self.inner = self.inner.london_activated(); ++ self.inner = self ++ .inner ++ .with_fork(reth_scroll_forks::ScrollHardfork::Archimedes, ForkCondition::Block(0)); ++ self ++ } + -+As this is a security related patch, we bypassed the 7-day timelock mechanism. ++ /// Enable Bernoulli at genesis ++ pub fn bernoulli_activated(mut self) -> Self { ++ self = self.archimedes_activated(); ++ self.inner = self.inner.with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0)); ++ self.inner = self ++ .inner ++ .with_fork(reth_scroll_forks::ScrollHardfork::Bernoulli, ForkCondition::Block(0)); ++ self ++ } + -+- **Scroll Sepolia**: August 28th, 2024 -+- **Scroll Mainnet**: September 2nd, 2024 ++ /// Enable Curie at genesis ++ pub fn curie_activated(mut self) -> Self { ++ self = self.bernoulli_activated(); ++ self.inner = ++ self.inner.with_fork(reth_scroll_forks::ScrollHardfork::Curie, ForkCondition::Block(0)); ++ self ++ } + -+### Compatibility ++ /// Enable Darwin at genesis ++ pub fn darwin_activated(mut self) -> Self { ++ self = self.curie_activated(); ++ self.inner = self ++ .inner ++ .with_fork(reth_scroll_forks::ScrollHardfork::Darwin, ForkCondition::Timestamp(0)); ++ self ++ } + -+#### Sequencer and Follower Nodes (l2geth) ++ /// Enable `DarwinV2` at genesis ++ pub fn darwin_v2_activated(mut self) -> Self { ++ self = self.darwin_activated(); ++ self.inner = self ++ .inner ++ .with_fork(reth_scroll_forks::ScrollHardfork::DarwinV2, ForkCondition::Timestamp(0)); ++ self ++ } + -+The new node version is `v5.7.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.7.0) for more information. ++ /// Build the resulting [`ScrollChainSpec`]. ++ /// ++ /// # Panics ++ /// ++ /// This function panics if the chain ID and genesis is not set ([`Self::chain`] and ++ /// [`Self::genesis`]) ++ pub fn build(self, config: ScrollChainConfig) -> ScrollChainSpec { ++ ScrollChainSpec { inner: self.inner.build(), config } ++ } ++} + -+This upgrade does not change Scroll's state transition function, so it is backward compatible. However, the format of the batch data committed to Ethereum changes. As a result, nodes that enabled rollup verification (`--rollup.verify`) must upgrade to be able to follow the chain. ++/// Scroll chain spec type. ++#[derive(Debug, Clone, Deref, Into, Constructor, PartialEq, Eq)] ++pub struct ScrollChainSpec { ++ /// [`ChainSpec`]. ++ #[deref] ++ pub inner: ChainSpec, ++ /// [`ScrollChainConfig`] ++ pub config: ScrollChainConfig, ++} + -+#### Dapps and Indexers ++impl EthChainSpec for ScrollChainSpec { ++ type Header = Header; + -+A change has been implemented to Scroll Mainnet to enhance sequencer throughput, which adjusted the maximum reorg depth to 17 blocks. Previously, the system performed thorough capacity checks within the signer thread to determine whether transactions exceed the circuit limit. While this ensures that all transactions within a block are compliant, it also requires additional CPU resources. -+We introduced a new circuit capacity checking scheme on Mainnet. The sequencer thread now will continue to perform capacity checks, but in a more approximate manner. In parallel, 16 worker threads will accurately verify the capacity of previous blocks. As a result, a reorg could occur with a maximum depth of 17 blocks, although the likelihood of this is low. ++ fn chain(&self) -> alloy_chains::Chain { ++ self.inner.chain() ++ } + -+For indexers, the `BatchHeader` version has been upgraded to 4. This is backward compatible (the only exception is for developers decoding the blob payload, which has changed slightly). ++ fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams { ++ // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 ++ self.inner.base_fee_params_at_block(block_number) ++ } + -+## Darwin Upgrade ++ fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams { ++ // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 ++ self.inner.base_fee_params_at_timestamp(timestamp) ++ } + -+### Overview ++ fn deposit_contract(&self) -> Option<&DepositContract> { ++ self.inner.deposit_contract() ++ } + -+This upgrade will reduce gas fees by 34% by using a single aggregated proof for multiple batches, eliminating the need to finalize each batch individually. ++ fn genesis_hash(&self) -> B256 { ++ *self.genesis_hash.get_or_init(|| self.genesis_header().hash_slow()) ++ } + -+- Darwin uses a new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). -+- In addition to the previous notions of `chunk` and `batch`, we have introduced a new concept called `bundle`. -+ - `Chunk`: A unit of zkEVM proving, consisting of a list of L2 blocks. -+ - `Batch`: A collection of chunks encoded into one EIP-4844 blob, serving as the unit of Data Availability. -+ - `Bundle`: A series of batches that functions as the unit of finalization. ++ fn prune_delete_limit(&self) -> usize { ++ self.inner.prune_delete_limit() ++ } + -+ The main difference compared to Curie is that Scroll will now finalize multiple batches using a single aggregated bundle proof. ++ fn display_hardforks(&self) -> Box<dyn Display> { ++ Box::new(ChainSpec::display_hardforks(self)) ++ } + -+- The on-chain bundle proof verifier uses a new public input layout. ++ fn genesis_header(&self) -> &Header { ++ self.inner.genesis_header.get_or_init(|| self.make_genesis_header()) ++ } + -+### Timeline ++ fn genesis(&self) -> &Genesis { ++ self.inner.genesis() ++ } + -+- **Scroll Sepolia** -+ - Network Upgrade: August 14th, 2024 -+- **Scroll Mainnet** -+ - Upgrade Initiation: August 5th, 2024 -+ - Timelock Completion & Upgrade: August 21st, 2024 ++ fn bootnodes(&self) -> Option<Vec<NodeRecord>> { ++ self.inner.bootnodes() ++ } ++} + -+### Technical Details ++impl ScrollChainSpec { ++ fn make_genesis_header(&self) -> Header { ++ Header { ++ gas_limit: self.genesis.gas_limit, ++ difficulty: self.genesis.difficulty, ++ nonce: self.genesis.nonce.into(), ++ extra_data: self.genesis.extra_data.clone(), ++ state_root: reth_trie_common::root::state_root_ref_unhashed(&self.genesis.alloc), ++ timestamp: self.genesis.timestamp, ++ mix_hash: self.genesis.mix_hash, ++ beneficiary: self.genesis.coinbase, ++ base_fee_per_gas: None, ++ withdrawals_root: None, ++ parent_beacon_block_root: None, ++ blob_gas_used: None, ++ excess_blob_gas: None, ++ requests_hash: None, ++ ..Default::default() ++ } ++ } ++} + -+#### Contract Changes ++impl Hardforks for ScrollChainSpec { ++ fn fork<H: reth_chainspec::Hardfork>(&self, fork: H) -> reth_chainspec::ForkCondition { ++ self.inner.fork(fork) ++ } + -+*Note: Since the previous Curie upgrade, we have migrated the Scroll contracts to a new repo at [scroll-contracts](https://github.com/scroll-tech/scroll-contracts).* ++ fn forks_iter( ++ &self, ++ ) -> impl Iterator<Item = (&dyn reth_chainspec::Hardfork, reth_chainspec::ForkCondition)> { ++ self.inner.forks_iter() ++ } + -+The code changes for this upgrade are implemented in [this PR](https://github.com/scroll-tech/scroll-contracts/pull/4). The key changes are as follows: ++ fn fork_id(&self, head: &Head) -> ForkId { ++ self.inner.fork_id(head) ++ } + -+- We have introduced a new `BatchHeaderV3Codec`. -+- We have changed how messages are processed in the `L1MessageQueue` contract. Prior to Darwin, we would process messages when a batch is finalized. After Darwin, most of this processing is moved to the commit step. -+- We have introduced a new public input format for bundle proofs. This is implemented in a new contract `IZkEvmVerifierV2`, which is in turn added to `MultipleVersionRollupVerifier`. -+- In the `ScrollChain` contract `version=3` batches will now be committed through a new function called `commitBatchWithBlobProof`. Bundles will be finalized using a new function called `finalizeBundleWithProof`. ++ fn latest_fork_id(&self) -> ForkId { ++ self.inner.latest_fork_id() ++ } + -+See the contract [release notes](https://github.com/scroll-tech/scroll-contracts/releases/tag/v1.0.0) for more information. ++ fn fork_filter(&self, head: Head) -> ForkFilter { ++ self.inner.fork_filter(head) ++ } ++} + -+#### Node Changes ++impl EthereumHardforks for ScrollChainSpec { ++ fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition { ++ self.fork(fork) ++ } + -+The new node version is `v5.6.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0) for more information. ++ fn get_final_paris_total_difficulty(&self) -> Option<U256> { ++ self.inner.get_final_paris_total_difficulty() ++ } + -+The main changes are: ++ fn final_paris_total_difficulty(&self, block_number: u64) -> Option<U256> { ++ self.inner.final_paris_total_difficulty(block_number) ++ } ++} + -+- Implementation of timestamp-based hard forks. -+- Processing V3 batch codec in rollup-verifier. ++impl ScrollHardforks for ScrollChainSpec { ++ fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition { ++ self.fork(fork) ++ } ++} + -+#### zkEVM circuit changes ++impl From<Genesis> for ScrollChainSpec { ++ fn from(genesis: Genesis) -> Self { ++ use reth_scroll_forks::ScrollHardfork; ++ let scroll_chain_info = ScrollConfigInfo::extract_from(&genesis); ++ let hard_fork_info = ++ scroll_chain_info.scroll_chain_info.hard_fork_info.expect("load scroll hard fork info"); + -+The new version of zkevm circuits is `v0.12.0`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.12.0) for the release log. ++ // Block-based hardforks ++ let hardfork_opts = [ ++ (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), ++ (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), ++ (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), ++ (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), ++ (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), ++ (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), ++ (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), ++ (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), ++ (EthereumHardfork::London.boxed(), genesis.config.london_block), ++ (ScrollHardfork::Archimedes.boxed(), hard_fork_info.archimedes_block), ++ (ScrollHardfork::Bernoulli.boxed(), hard_fork_info.bernoulli_block), ++ (ScrollHardfork::Curie.boxed(), hard_fork_info.curie_block), ++ ]; ++ let mut block_hardforks = hardfork_opts ++ .into_iter() ++ .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) ++ .collect::<Vec<_>>(); + -+We have introduced a `RecursionCircuit` that will bundle multiple sequential batches by recursively aggregating the SNARKs from the `BatchCircuit` (previously `AggregationCircuit`). The previously 5 layer proving system is now 7 layers as we introduce: ++ // Time-based hardforks ++ let time_hardfork_opts = [ ++ (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), ++ (ScrollHardfork::Darwin.boxed(), hard_fork_info.darwin_time), ++ (ScrollHardfork::DarwinV2.boxed(), hard_fork_info.darwin_v2_time), ++ ]; + -+- 6th Layer (layer5): `RecursionCircuit` that recursively aggregates `BatchCircuit` SNARKs. -+- 7th Layer (layer6): `CompressionCircuit` that compresses the `RecursionCircuit` SNARK and produce an EVM-verifiable validity proof. ++ let mut time_hardforks = time_hardfork_opts ++ .into_iter() ++ .filter_map(|(hardfork, opt)| { ++ opt.map(|time| (hardfork, ForkCondition::Timestamp(time))) ++ }) ++ .collect::<Vec<_>>(); + -+The public input to the `BatchCircuit` is now context-aware of the “previous” `batch`, which allows us to implement the recursion scheme we adopted (described [here](https://scrollzkp.notion.site/Upgrade-4-Darwin-Documentation-05a3ecb59e9d4f288254701f8c888173) in-depth). ++ block_hardforks.append(&mut time_hardforks); + -+#### Audits ++ // Ordered Hardforks ++ let mainnet_hardforks = ScrollHardfork::scroll_mainnet(); ++ let mainnet_order = mainnet_hardforks.forks_iter(); + -+- TrailofBits: coming soon! ++ let mut ordered_hardforks = Vec::with_capacity(block_hardforks.len()); ++ for (hardfork, _) in mainnet_order { ++ if let Some(pos) = block_hardforks.iter().position(|(e, _)| **e == *hardfork) { ++ ordered_hardforks.push(block_hardforks.remove(pos)); ++ } ++ } + -+### Compatibility ++ // append the remaining unknown hardforks to ensure we don't filter any out ++ ordered_hardforks.append(&mut block_hardforks); + -+#### Sequencer and Follower Nodes (l2geth) ++ Self { ++ inner: ChainSpec { ++ chain: genesis.config.chain_id.into(), ++ genesis, ++ hardforks: ChainHardforks::new(ordered_hardforks), ++ ..Default::default() ++ }, ++ config: scroll_chain_info.scroll_chain_info.scroll_chain_config, ++ } ++ } ++} + -+This upgrade does not alter the state transition function and is therefore backward-compatible. However, we strongly recommend node operators to upgrade to [v5.6.0](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0). ++#[derive(Default, Debug)] ++struct ScrollConfigInfo { ++ scroll_chain_info: ScrollChainInfo, ++} + -+#### Dapps and Indexers ++impl ScrollConfigInfo { ++ fn extract_from(genesis: &Genesis) -> Self { ++ Self { ++ scroll_chain_info: ScrollChainInfo::extract_from(&genesis.config.extra_fields) ++ .expect("extract scroll extra fields failed"), ++ } ++ } ++} + -+There are some major changes to how we commit and finalize batches after Darwin. ++#[cfg(test)] ++mod tests { ++ use crate::*; ++ use alloy_genesis::{ChainConfig, Genesis}; ++ use alloy_primitives::b256; ++ use reth_chainspec::test_fork_ids; ++ use reth_ethereum_forks::{EthereumHardfork, ForkHash}; ++ use reth_scroll_forks::ScrollHardfork; + -+- Batches will be encoded using the new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). This version adds two new fields: -+ 1. `lastBlockTimestamp` (the timestamp of the last block in this batch). -+ 2. `blobDataProof` (the KZG challenge point evaluation proof). ++ #[test] ++ fn scroll_mainnet_genesis_hash() { ++ let scroll_mainnet = ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); ++ assert_eq!( ++ b256!("908789cb20d00fc6070093f142aa8d02c21cfb0a9b9cfd4621d8cf0255234c0f"), ++ scroll_mainnet.genesis_hash() ++ ); ++ } + -+ This version removes `skippedL1MessageBitmap`. There will be no changes to how the blob data is encoded and compressed. -+- Batches will be committed using the `commitBatchWithBlobProof` function (instead of the previous `commitBatch`). ++ #[test] ++ fn scroll_sepolia_genesis_hash() { ++ let scroll_sepolia = ++ ScrollChainSpecBuilder::scroll_sepolia().build(ScrollChainConfig::sepolia()); ++ assert_eq!( ++ b256!("5e756a466b785b67e247b18c410d962866a53af97f09948016e9239b2054c94f"), ++ scroll_sepolia.genesis_hash() ++ ); ++ } + -+ New function signature: ++ #[test] ++ fn scroll_mainnet_forkids() { ++ test_fork_ids( ++ &SCROLL_MAINNET, ++ &[ ++ ( ++ Head { number: 0, ..Default::default() }, ++ ForkId { hash: ForkHash([0xea, 0x6b, 0x56, 0xca]), next: 5220340 }, ++ ), ++ ( ++ Head { number: 5220340, ..Default::default() }, ++ ForkId { hash: ForkHash([0xee, 0x46, 0xae, 0x2a]), next: 7096836 }, ++ ), ++ ( ++ Head { number: 7096836, ..Default::default() }, ++ ForkId { hash: ForkHash([0x18, 0xd3, 0xc8, 0xd9]), next: 1724227200 }, ++ ), ++ ( ++ Head { number: 7096836, timestamp: 1724227200, ..Default::default() }, ++ ForkId { hash: ForkHash([0xcc, 0xeb, 0x09, 0xb0]), next: 1725264000 }, ++ ), ++ ( ++ Head { number: 7096836, timestamp: 1725264000, ..Default::default() }, ++ ForkId { hash: ForkHash([0x21, 0xa2, 0x07, 0x54]), next: 0 }, ++ ), ++ ], ++ ); ++ } + -+ ```solidity -+ function commitBatchWithBlobProof(uint8 _version, bytes calldata _parentBatchHeader, bytes[] memory _chunks, bytes calldata _skippedL1MessageBitmap, bytes calldata _blobDataProof) -+ ``` -+ -+- Batches will be finalized using the `finalizeBundleWithProof` function (instead of the previous `finalizeBatchWithProof4844`). -+ -+ New function signature: -+ -+ ```solidity -+ function finalizeBundleWithProof(bytes calldata _batchHeader, bytes32 _postStateRoot, bytes32 _withdrawRoot, bytes calldata _aggrProof) -+ ``` -+ -+- The semantics of the `FinalizeBatch` event will change: It will now mean that all batches between the last finalized batch and the event’s `_batchIndex` have been finalized. The event’s stateRoot and withdrawRoot values belong to the last finalized batch in the bundle. Finalized roots for intermediate batches are no longer available. -+ -+ The semantics of the `CommitBatch` and `RevertBatch` events will not change. -+ -+Recommendations: -+ -+- Indexers that decode committed batch data should be adjusted to use the new codec and the new function signature. -+- Indexers that track batch finalization status should be adjusted to consider the new event semantics. -+ -+## Curie Upgrade -+ -+### Overview ++ #[test] ++ fn scroll_sepolia_forkids() { ++ test_fork_ids( ++ &SCROLL_SEPOLIA, ++ &[ ++ ( ++ Head { number: 0, ..Default::default() }, ++ ForkId { hash: ForkHash([0x25, 0xfa, 0xe4, 0x54]), next: 3747132 }, ++ ), ++ ( ++ Head { number: 3747132, ..Default::default() }, ++ ForkId { hash: ForkHash([0xda, 0x76, 0xc2, 0x2d]), next: 4740239 }, ++ ), ++ ( ++ Head { number: 4740239, ..Default::default() }, ++ ForkId { hash: ForkHash([0x9f, 0xb4, 0x75, 0xf1]), next: 1723622400 }, ++ ), ++ ( ++ Head { number: 4740239, timestamp: 1723622400, ..Default::default() }, ++ ForkId { hash: ForkHash([0xe9, 0x26, 0xd4, 0x9b]), next: 1724832000 }, ++ ), ++ ( ++ Head { number: 4740239, timestamp: 1724832000, ..Default::default() }, ++ ForkId { hash: ForkHash([0x69, 0xf3, 0x7e, 0xde]), next: 0 }, ++ ), ++ ], ++ ); ++ } + -+This significant upgrade will reduce gas fees on the Scroll chain by 1.5x. Highlights include: ++ #[test] ++ fn is_bernoulli_active() { ++ let scroll_mainnet = ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); ++ assert!(!scroll_mainnet.is_bernoulli_active_at_block(1)) ++ } + -+- Compresses the data stored in blobs using the [zstd](https://github.com/scroll-tech/da-codec/tree/main/libzstd) algorithm. This compression reduces the data size, allowing each blob to store more transactions, thereby reducing data availability cost per transaction. -+- Adopts a modified version of the EIP-1559 pricing model which is compatible with the EIP-1559 transaction interface, bringing beneftis such as more accurate transaction pricing and a more predictable and stable fee structure. -+- Support for new EVM opcodes `TLOAD`, `TSTORE`, and `MCOPY`. Users can safely use the latest Solidity compiler version `0.8.26` to build the contracts. -+- Introduces a dynamic block time. During periods of traffic congestion, a block will be packed when the number of transactions reaches the circuit limit instead of waiting for the 3-second interval. ++ #[test] ++ fn parse_scroll_hardforks() { ++ let geth_genesis = r#" ++ { ++ "config": { ++ "bernoulliBlock": 10, ++ "curieBlock": 20, ++ "darwinTime": 30, ++ "darwinV2Time": 31, ++ "scroll": { ++ "feeVaultAddress": "0x5300000000000000000000000000000000000005", ++ "l1Config": { ++ "l1ChainId": 1, ++ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", ++ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", ++ "numL1MessagesPerBlock": 10 ++ } ++ } ++ } ++ } ++ "#; ++ let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + -+### Timeline ++ let actual_bernoulli_block = genesis.config.extra_fields.get("bernoulliBlock"); ++ assert_eq!(actual_bernoulli_block, Some(serde_json::Value::from(10)).as_ref()); ++ let actual_curie_block = genesis.config.extra_fields.get("curieBlock"); ++ assert_eq!(actual_curie_block, Some(serde_json::Value::from(20)).as_ref()); ++ let actual_darwin_timestamp = genesis.config.extra_fields.get("darwinTime"); ++ assert_eq!(actual_darwin_timestamp, Some(serde_json::Value::from(30)).as_ref()); ++ let actual_darwin_v2_timestamp = genesis.config.extra_fields.get("darwinV2Time"); ++ assert_eq!(actual_darwin_v2_timestamp, Some(serde_json::Value::from(31)).as_ref()); ++ let scroll_object = genesis.config.extra_fields.get("scroll").unwrap(); ++ assert_eq!( ++ scroll_object, ++ &serde_json::json!({ ++ "feeVaultAddress": "0x5300000000000000000000000000000000000005", ++ "l1Config": { ++ "l1ChainId": 1, ++ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", ++ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", ++ "numL1MessagesPerBlock": 10 ++ } ++ }) ++ ); + -+- **Scroll Sepolia** -+ - Network Upgrade: June 17th, 2024 -+- **Scroll Mainnet** -+ - Upgrade Initiation: June 20th, 2024 -+ - Timelock Completion & Upgrade: July 3rd, 2024 ++ let chain_spec: ScrollChainSpec = genesis.into(); + -+### Technical Details ++ assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 0)); ++ assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 0)); ++ assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 0)); ++ assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 0)); + -+#### Contract Changes ++ assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 10)); ++ assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 20)); ++ assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 30)); ++ assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 31)); ++ } + -+The code changes for this upgrade are documented in the following PRs: ++ #[test] ++ fn test_fork_order_scroll_mainnet() { ++ let genesis = Genesis { ++ config: ChainConfig { ++ chain_id: 0, ++ homestead_block: Some(0), ++ dao_fork_block: Some(0), ++ dao_fork_support: false, ++ eip150_block: Some(0), ++ eip155_block: Some(0), ++ eip158_block: Some(0), ++ byzantium_block: Some(0), ++ constantinople_block: Some(0), ++ petersburg_block: Some(0), ++ istanbul_block: Some(0), ++ berlin_block: Some(0), ++ london_block: Some(0), ++ shanghai_time: Some(0), ++ extra_fields: [ ++ (String::from("archimedesBlock"), 0.into()), ++ (String::from("bernoulliBlock"), 0.into()), ++ (String::from("curieBlock"), 0.into()), ++ (String::from("darwinTime"), 0.into()), ++ (String::from("darwinV2Time"), 0.into()), ++ ( ++ String::from("scroll"), ++ serde_json::json!({ ++ "feeVaultAddress": "0x5300000000000000000000000000000000000005", ++ "l1Config": { ++ "l1ChainId": 1, ++ "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", ++ "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", ++ "numL1MessagesPerBlock": 10 ++ } ++ }), ++ ), ++ ] ++ .into_iter() ++ .collect(), ++ ..Default::default() ++ }, ++ ..Default::default() ++ }; + -+- [Accept compressed batches](https://github.com/scroll-tech/scroll/pull/1317) -+- [Update `L1GasPriceOracle`](https://github.com/scroll-tech/scroll/pull/1343) -+- [Change `MAX_COMMIT_SCALAR` and `MAX_BLOB_SCALAR` to 1e18](https://github.com/scroll-tech/scroll/pull/1354) -+- [Remove batch index check when updating a verifier](https://github.com/scroll-tech/scroll/pull/1372) ++ let chain_spec: ScrollChainSpec = genesis.into(); + -+The main changes are as follows: ++ let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); ++ let expected_hardforks = vec![ ++ EthereumHardfork::Homestead.boxed(), ++ EthereumHardfork::Tangerine.boxed(), ++ EthereumHardfork::SpuriousDragon.boxed(), ++ EthereumHardfork::Byzantium.boxed(), ++ EthereumHardfork::Constantinople.boxed(), ++ EthereumHardfork::Petersburg.boxed(), ++ EthereumHardfork::Istanbul.boxed(), ++ EthereumHardfork::Berlin.boxed(), ++ EthereumHardfork::London.boxed(), ++ ScrollHardfork::Archimedes.boxed(), ++ EthereumHardfork::Shanghai.boxed(), ++ ScrollHardfork::Bernoulli.boxed(), ++ ScrollHardfork::Curie.boxed(), ++ ScrollHardfork::Darwin.boxed(), ++ ScrollHardfork::DarwinV2.boxed(), ++ ]; + -+- The rollup contract (`ScrollChain`) will now accept batches with both versions 1 and 2. [Version 1](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv1) is used for uncompressed blobs (pre-Curie), while [version 2](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv2) is used for compressed blobs (post-Curie). -+- The `L1GasPriceOracle` contract will be updated to change the data fee formula to account for blob DA, providing a more accurate estimation of DA costs: -+ - Original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar` -+ - New formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar` ++ assert!(expected_hardforks ++ .iter() ++ .zip(hardforks.iter()) ++ .all(|(expected, actual)| &**expected == *actual)); + -+#### Node Changes ++ assert_eq!(expected_hardforks.len(), hardforks.len()); ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+30
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/chainspec/src/scroll.rs scroll-reth/crates/scroll/chainspec/src/scroll.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..ebd700386279ca870c67a032d4c83fa29c0f16bb +--- /dev/null ++++ scroll-reth/crates/scroll/chainspec/src/scroll.rs +@@ -0,0 +1,30 @@ ++//! Chain specification for the Scroll Mainnet network. + -+The new node version is `v5.5.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.5.0) for the list of changes. ++use alloc::sync::Arc; + -+#### zkEVM circuit changes ++use alloy_chains::{Chain, NamedChain}; ++use alloy_primitives::b256; ++use reth_chainspec::{once_cell_set, ChainSpec}; ++use reth_scroll_forks::ScrollHardfork; + -+The new version of zkevm circuits is `v0.11.4`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.11.4) for the release log. ++use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; + -+#### Audits -+ -+- TrailofBits: coming soon! -+- [Zellic](https://github.com/Zellic/publications/blob/master/Scroll%20zkEVM%20-%20Zellic%20Audit%20Report.pdf) -+ -+### Compatibility -+ -+#### Sequencer and Follower Nodes (l2geth) -+ -+This upgrade is a hard fork, introducing the `TLOAD`, `TSTORE`, and `MCOPY` opcodes. Operators running an `l2geth` node are required to upgrade before the hard fork block. For more information, see the [node release note](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.4.2). -+ -+#### Dapps and Indexers -+ -+For dApps, this upgrade is backward compatible. Developers should adjust the gas fee settings to incorporate the EIP-1559 pricing model. Note that dApps can no longer rely on the fixed 3-second block time in the application logic. -+ -+For indexers, the [data format](https://docs.scroll.io/en/technology/chain/rollup/#codec) remains the same. The will be however changes to the data content: -+ -+- The `version` field in `BatchHeader` will be changed to 2 since Curie block. -+- The data stored in blob will be compressed and can be decompressed by [zstd v1.5.6](https://github.com/facebook/zstd/releases/tag/v1.5.6). -+ -+## Bernoulli Upgrade -+ -+### Overview -+ -+This upgrade features a significant reduction in transaction costs by introducing support for EIP-4844 data blobs and supporting the SHA2-256 precompile. -+ -+### Timeline -+ -+- **Scroll Sepolia** -+ - Network Upgrade: April 15th, 2024 -+- **Scroll Mainnet** -+ - Upgrade Initiation: April 15th, 2024 -+ - Timelock Completion & Upgrade: April 29th, 2024 -+ -+### Technical Details -+ -+#### Contract changes -+ -+The contract changes for this upgrade are in [this PR](https://github.com/scroll-tech/scroll/pull/1179), along with the audit fixes [here](https://github.com/scroll-tech/scroll/pulls?q=is%3Apr+created%3A2024-04-10..2024-04-11+fix+in%3Atitle+label%3Abug). The main changes are as follows: -+ -+- `ScrollChain` now accepts batches with either calldata or blob encoding in `commitBatch`. -+- `ScrollChain` now supports finalizing blob-encoded batches through `finalizeBatchWithProof4844`. -+- `MultipleVersionRollupVerifier` can now manage different on-chain verifiers for each batch encoding version. -+ -+#### Node changes -+ -+The new node version is `v5.3.0`. See [here](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.3.0) for the release log. -+ -+#### zkEVM circuit changes -+ -+The new version of zkevm circuits is `v0.10.3`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.10.3) for the release log. -+ -+#### Audits -+ -+- [OpenZeppelin](https://blog.openzeppelin.com/scroll-eip-4844-support-audit) -+- [TrailofBits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-scroll-4844-blob-securityreview.pdf) -+ -+### Compatibility -+ -+#### Sequencer and follower nodes (l2geth) -+ -+This upgrade is a hard fork as it introduces the new blob data type and the SHA2-256 precompiled contract. Operators running an `l2geth` node are required to upgrade before the hard fork block. See the [node releases](https://github.com/scroll-tech/go-ethereum/releases) for more information. -+ -+#### Indexers and Bridges -+ -+This upgrade changes the format that Scroll uses to publish data to Ethereum. Projects that rely on this data should carefully review [the new data format](/en/technology/chain/rollup/#codec), and check whether their decoders need to be adjusted. A summary of the new format: -+ -+- The format of [`BlockContext`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L125) will not change. -+- `Chunks` will [no longer include](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L162) the L2 transaction data. This will instead be [stored in a blob](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L284) attached to the `commitBatch` transaction. -+- `BatchHeader` now contains one new field, [`BlobVersionedHash`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L405). -+ -+#### Provers -+ -+This upgrade involves a breaking change in [zkevm-circuits](https://github.com/scroll-tech/zkevm-circuits). Operators running a prover node are required to upgrade. -+ -+ -+## Bridge Upgrade -+ -+### Overview -+ -+To reduce bridging costs, we implemented several gas optimizations on our bridge and rollup contract suite. The optimization techniques used include the following: -+ -+- We will now use constants to store some companion contract addresses, instead of using storage variables. This is possible since these values should (almost) never change. With this change we can save on a few storage load operations. -+- We updated the intrinsic gas estimation in `L1MessageQueue` to use a simple upper bound instead of an exact calculation. The two results will be similar for most bridge transactions but the new implementation is significantly cheaper. -+- We merged two contracts `L1MessageQueue` and `L2GasPriceOracle` to save on call costs from one contract to the other. -+ -+### Timeline -+ -+- **Scroll Sepolia:** -+ - Network Upgrade: January 19, 2024 -+- **Scroll Mainnet:** -+ - Upgrade Initiation: February 7, 2024 -+ - Timelock Completion & Upgrade: February 21, 2024 -+ -+### Technical Details -+ -+#### Code Changes -+- [Bridge Cost Optimization](https://github.com/scroll-tech/scroll/pull/1011) -+- [Audit Fixes](https://github.com/scroll-tech/scroll/pulls?q=OZ+is%3Apr+created%3A2024-01-27..2024-02-10) -+- [Previously deployed version](https://github.com/scroll-tech/scroll/tree/ff380141a8cbcc214dc65f17ffa44faf4be646b6) (commit `ff380141a8cbcc214dc65f17ffa44faf4be646b6`) -+- [Version deployed](https://github.com/scroll-tech/scroll/tree/6030927680a92d0285c2c13e6bb27ed27d1f32d1) (commit `6030927680a92d0285c2c13e6bb27ed27d1f32d1`) -+ -+#### Audits -+ -+- [OpenZeppelin](https://blog.openzeppelin.com/scroll-bridge-gas-optimizations-audit) -+ -+#### List of Changes -+ -+**Changes to L1 contracts:** -+ -+- In `ScrollChain`, change `messageQueue` and `verifier` to `immutable`. -+- In `L1ScrollMessenger`, change `counterpart`, `rollup`, and `messageQueue` to `immutable`. -+- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. -+- Merge `L1MessageQueue` and `L2GasPriceOracle` into a single contract `L1MessageQueueWithGasPriceOracle` (deployed on the same address as the previous `L1MessageQueue`). In this contract, we also change `messenger` and `scrollChain` to `immutable`, and simplify `calculateIntrinsicGasFee`. -+ -+**Changes to L2 contracts:** -+ -+- In `L2ScrollMessenger`, change `counterpart` to `immutable`. -+- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. -+ -+**Contracts affected:** -+ -+- **L1:** `L1MessageQueue`, `L2GasPriceOracle`, `ScrollChain`, `L1WETHGateway`, `L1StandardERC20Gateway`, `L1GatewayRouter`, `L1ScrollMessenger`, `L1CustomERC20Gateway`, `L1ERC721Gateway`, `L1ERC1155Gateway`. -+- **L2:** `L2ScrollMessenger`, `L2WETHGateway`, `L2StandardERC20Gateway`, `L2GatewayRouter`, `L2CustomERC20Gateway`, `L2ERC721Gateway`, `L2ERC1155Gateway`. -+ -+#### Compatibility -+ -+##### Sequencer and follower nodes (l2geth) -+ -+Operators running an `l2geth` node do not need to upgrade. The changes in this upgrade will not affect `l2geth`. -+ -+##### Dapps and indexers -+ -+Dapps and indexers (and similar off-chain infrastructure) that query contracts or rely on contract interfaces would, in most cases, not need to be changed. The majority of the contract changes are internal and/or backward compatible. -+ -+If your application depends on [`L2GasPriceOracle`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) to monitor how Scroll keeps track of the L2 gas price on L1, from the upgrade block number you will need to start monitoring [`L1MessageQueueWithGasPriceOracle`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B). -+ -+The original gas price oracle contract will be deprecated: it will no longer be updated or used by the Scroll bridge. -+ -+- Ethereum: -+ - `L2GasPriceOracle`: [`0x987e300fDfb06093859358522a79098848C33852`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) -+ - `L1MessageQueueWithGasPriceOracle`: [`0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B) -+- Sepolia: -+ - `L2GasPriceOracle`: [`0x247969F4fad93a33d4826046bc3eAE0D36BdE548`](https://sepolia.etherscan.io/address/0x247969F4fad93a33d4826046bc3eAE0D36BdE548) -+ - `L1MessageQueueWithGasPriceOracle`: [`0xF0B2293F5D834eAe920c6974D50957A1732de763`](https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763) -\ No newline at end of file
++/// The Scroll Mainnet spec ++pub static SCROLL_MAINNET: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { ++ ScrollChainSpec { ++ inner: ChainSpec { ++ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ chain: Chain::from_named(NamedChain::Scroll), ++ genesis: serde_json::from_str(include_str!("../res/genesis/scroll.json")) ++ .expect("Can't deserialize Scroll Mainnet genesis json"), ++ // TODO(scroll): update this value once the bootnodes run the mpt. ++ genesis_hash: once_cell_set(b256!( ++ "bbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80" ++ )), ++ hardforks: ScrollHardfork::scroll_mainnet(), ++ ..Default::default() ++ }, ++ config: ScrollChainConfig::mainnet(), ++ } ++ .into() ++});
@@ -5139,7 +6336,7 @@
@@ -5149,45 +6346,48 @@
-
+27
+
+30
-0
-
diff --git reth/crates/scroll/hardforks/src/dev.rs scroll-reth/crates/scroll/hardforks/src/dev.rs +
diff --git reth/crates/scroll/chainspec/src/scroll_sepolia.rs scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs new file mode 100644 -index 0000000000000000000000000000000000000000..33fcd0d00776d70909085ffb29fb40ef9366324f +index 0000000000000000000000000000000000000000..1a19597793a90586d2bc034249ad71f5bbc95cc1 --- /dev/null -+++ scroll-reth/crates/scroll/hardforks/src/dev.rs -@@ -0,0 +1,27 @@ -+use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; ++++ scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs +@@ -0,0 +1,30 @@ ++//! Chain specification for the Scroll Sepolia testnet network. + -+#[cfg(not(feature = "std"))] -+use once_cell::sync::Lazy as LazyLock; -+#[cfg(feature = "std")] -+use std::sync::LazyLock; ++use alloc::sync::Arc; + -+/// Dev hardforks -+pub static DEV_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| { -+ ChainHardforks::new(vec![ -+ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), -+ (crate::ScrollHardfork::Archimedes.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)), -+ (crate::ScrollHardfork::Bernoulli.boxed(), ForkCondition::Block(0)), -+ (crate::ScrollHardfork::Curie.boxed(), ForkCondition::Block(0)), -+ (crate::ScrollHardfork::Darwin.boxed(), ForkCondition::Timestamp(0)), -+ (crate::ScrollHardfork::DarwinV2.boxed(), ForkCondition::Timestamp(0)), -+ ]) ++use alloy_chains::{Chain, NamedChain}; ++use alloy_primitives::b256; ++use reth_chainspec::{once_cell_set, ChainSpec}; ++use reth_scroll_forks::ScrollHardfork; ++ ++use crate::{LazyLock, ScrollChainConfig, ScrollChainSpec}; ++ ++/// The Scroll Sepolia spec ++pub static SCROLL_SEPOLIA: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { ++ ScrollChainSpec { ++ inner: ChainSpec { ++ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ chain: Chain::from_named(NamedChain::ScrollSepolia), ++ genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_scroll.json")) ++ .expect("Can't deserialize Scroll Sepolia genesis json"), ++ // TODO(scroll): update this value once the bootnodes run the mpt. ++ genesis_hash: once_cell_set(b256!( ++ "aa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c" ++ )), ++ hardforks: ScrollHardfork::scroll_sepolia(), ++ ..Default::default() ++ }, ++ config: ScrollChainConfig::sepolia(), ++ } ++ .into() +});
@@ -5195,9 +6395,9 @@
@@ -5212,7 +6412,7 @@
@@ -5222,282 +6422,216 @@
-
+264
+
+53
-0
-
diff --git reth/crates/scroll/hardforks/src/hardfork.rs scroll-reth/crates/scroll/hardforks/src/hardfork.rs +
diff --git reth/crates/scroll/cli/Cargo.toml scroll-reth/crates/scroll/cli/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..6a49e2fcffdf2da513b5ac5712a554ea6597c2d5 +index 0000000000000000000000000000000000000000..cbfe12404e180b3cc57773f7691a99a6accb6aa7 --- /dev/null -+++ scroll-reth/crates/scroll/hardforks/src/hardfork.rs -@@ -0,0 +1,264 @@ -+//! Hard forks of scroll protocol. ++++ scroll-reth/crates/scroll/cli/Cargo.toml +@@ -0,0 +1,54 @@ ++[package] ++name = "reth-scroll-cli" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+use alloc::{format, string::String, vec}; -+use core::{ -+ any::Any, -+ fmt::{self, Display, Formatter}, -+ str::FromStr, -+}; ++[lints] ++workspace = true + -+use alloy_chains::{Chain, NamedChain}; -+use reth_ethereum_forks::{hardfork, ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; -+#[cfg(feature = "serde")] -+use serde::{Deserialize, Serialize}; -+ -+hardfork!( -+ /// The name of the Scroll hardfork -+ ScrollHardfork { -+ /// Archimedes: scroll test hardfork. -+ Archimedes, -+ /// Bernoulli: <https://scroll.io/blog/blobs-are-here-scrolls-bernoulli-upgrade>. -+ Bernoulli, -+ /// Curie: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade>. -+ Curie, -+ /// Darwin: <https://scroll.io/blog/proof-recursion-scrolls-darwin-upgrade>. -+ Darwin, -+ /// DarwinV2 <https://x.com/Scroll_ZKP/status/1830565514755584269>. -+ DarwinV2, -+ } -+); -+ -+impl ScrollHardfork { -+ /// Retrieves the activation block for the specified hardfork on the given chain. -+ pub fn activation_block<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> { -+ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ if chain == Chain::from_named(NamedChain::Scroll) { -+ return Self::scroll_sepolia_activation_block(fork); -+ } -+ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ if chain == Chain::from_named(NamedChain::ScrollSepolia) { -+ return Self::scroll_mainnet_activation_block(fork); -+ } -+ -+ None -+ } ++[dependencies] ++# reth ++reth-db.workspace = true ++reth-cli.workspace = true ++reth-cli-commands.workspace = true ++reth-cli-runner.workspace = true ++reth-eth-wire.workspace = true ++reth-node-builder.workspace = true ++reth-node-core.workspace = true ++reth-node-metrics.workspace = true ++reth-primitives.workspace = true ++reth-tracing.workspace = true + -+ /// Retrieves the activation timestamp for the specified hardfork on the given chain. -+ pub fn activation_timestamp<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> { -+ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ if chain == Chain::from_named(NamedChain::ScrollSepolia) { -+ return Self::scroll_sepolia_activation_timestamp(fork); -+ } -+ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 -+ if chain == Chain::from_named(NamedChain::Scroll) { -+ return Self::scroll_mainnet_activation_timestamp(fork); -+ } ++# alloy ++alloy-consensus.workspace = true + -+ None -+ } ++# scroll ++reth-scroll-chainspec.workspace = true ++reth-scroll-evm.workspace = true ++reth-scroll-node.workspace = true + -+ /// Retrieves the activation block for the specified hardfork on the Scroll Sepolia testnet. -+ pub fn scroll_sepolia_activation_block<H: Hardfork>(fork: H) -> Option<u64> { -+ match_hardfork( -+ fork, -+ |fork| match fork { -+ EthereumHardfork::Homestead | -+ EthereumHardfork::Tangerine | -+ EthereumHardfork::SpuriousDragon | -+ EthereumHardfork::Byzantium | -+ EthereumHardfork::Constantinople | -+ EthereumHardfork::Petersburg | -+ EthereumHardfork::Istanbul | -+ EthereumHardfork::Berlin | -+ EthereumHardfork::London | -+ EthereumHardfork::Shanghai => Some(0), -+ _ => None, -+ }, -+ |fork| match fork { -+ Self::Archimedes => Some(0), -+ Self::Bernoulli => Some(3747132), -+ Self::Curie => Some(4740239), -+ Self::Darwin => Some(6075509), -+ Self::DarwinV2 => Some(6375501), -+ }, -+ ) -+ } ++# misc ++eyre.workspace = true ++clap.workspace = true ++tracing.workspace = true + -+ /// Retrieves the activation block for the specified hardfork on the Scroll mainnet. -+ pub fn scroll_mainnet_activation_block<H: Hardfork>(fork: H) -> Option<u64> { -+ match_hardfork( -+ fork, -+ |fork| match fork { -+ EthereumHardfork::Homestead | -+ EthereumHardfork::Tangerine | -+ EthereumHardfork::SpuriousDragon | -+ EthereumHardfork::Byzantium | -+ EthereumHardfork::Constantinople | -+ EthereumHardfork::Petersburg | -+ EthereumHardfork::Istanbul | -+ EthereumHardfork::Berlin | -+ EthereumHardfork::London | -+ EthereumHardfork::Shanghai => Some(0), -+ _ => None, -+ }, -+ |fork| match fork { -+ Self::Archimedes => Some(0), -+ Self::Bernoulli => Some(5220340), -+ Self::Curie => Some(7096836), -+ Self::Darwin => Some(8568134), -+ Self::DarwinV2 => Some(8923772), -+ }, -+ ) -+ } ++[features] ++optimism = [ ++ "reth-db/optimism", ++ "reth-node-core/optimism", ++ "reth-scroll-evm/optimism", ++ "reth-scroll-node/optimism", ++ "reth-primitives/optimism" ++] ++scroll = [ ++ "reth-db/scroll", ++ "reth-node-builder/scroll", ++ "reth-node-core/scroll", ++ "reth-scroll-evm/scroll", ++ "reth-scroll-node/scroll" ++] +\ No newline at end of file
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+3
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/cli/src/args.rs scroll-reth/crates/scroll/cli/src/args.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..d51f83146a06b3387dbd098f63ec1e3d8d46593b +--- /dev/null ++++ scroll-reth/crates/scroll/cli/src/args.rs +@@ -0,0 +1,3 @@ ++/// Rollup arguments for the Scroll node. ++#[derive(Debug, clap::Args)] ++pub struct ScrollRollupArgs;
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+48
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/cli/src/commands.rs scroll-reth/crates/scroll/cli/src/commands.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..a80a2aa8bc782020eed92c1c4eb1991c6f55c8b8 +--- /dev/null ++++ scroll-reth/crates/scroll/cli/src/commands.rs +@@ -0,0 +1,48 @@ ++use crate::ScrollChainSpecParser; ++use clap::Subcommand; ++use reth_cli::chainspec::ChainSpecParser; ++use reth_cli_commands::{ ++ config_cmd, db, dump_genesis, import, init_cmd, init_state, node, node::NoArgs, p2p, prune, ++ recover, stage, ++}; ++use std::fmt; + -+ /// Retrieves the activation timestamp for the specified hardfork on the Scroll Sepolia testnet. -+ pub fn scroll_sepolia_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> { -+ match_hardfork( -+ fork, -+ |fork| match fork { -+ EthereumHardfork::Homestead | -+ EthereumHardfork::Tangerine | -+ EthereumHardfork::SpuriousDragon | -+ EthereumHardfork::Byzantium | -+ EthereumHardfork::Constantinople | -+ EthereumHardfork::Petersburg | -+ EthereumHardfork::Istanbul | -+ EthereumHardfork::Berlin | -+ EthereumHardfork::London | -+ EthereumHardfork::Shanghai => Some(0), -+ _ => None, -+ }, -+ |fork| match fork { -+ Self::Archimedes => Some(0), -+ Self::Bernoulli => Some(1713175866), -+ Self::Curie => Some(1718616171), -+ Self::Darwin => Some(1723622400), -+ Self::DarwinV2 => Some(1724832000), -+ }, -+ ) -+ } -+ -+ /// Retrieves the activation timestamp for the specified hardfork on the Scroll mainnet. -+ pub fn scroll_mainnet_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> { -+ match_hardfork( -+ fork, -+ |fork| match fork { -+ EthereumHardfork::Homestead | -+ EthereumHardfork::Tangerine | -+ EthereumHardfork::SpuriousDragon | -+ EthereumHardfork::Byzantium | -+ EthereumHardfork::Constantinople | -+ EthereumHardfork::Petersburg | -+ EthereumHardfork::Istanbul | -+ EthereumHardfork::Berlin | -+ EthereumHardfork::London | -+ EthereumHardfork::Shanghai => Some(0), -+ _ => None, -+ }, -+ |fork| match fork { -+ Self::Archimedes => Some(0), -+ Self::Bernoulli => Some(1714358352), -+ Self::Curie => Some(1719994277), -+ Self::Darwin => Some(1724227200), -+ Self::DarwinV2 => Some(1725264000), -+ }, -+ ) -+ } -+ -+ /// Scroll mainnet list of hardforks. -+ pub fn scroll_mainnet() -> ChainHardforks { -+ ChainHardforks::new(vec![ -+ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Dao.boxed(), ForkCondition::Never), -+ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Never), -+ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::London.boxed(), ForkCondition::Never), -+ (EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Never), -+ (Self::Archimedes.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), -+ (Self::Bernoulli.boxed(), ForkCondition::Block(5220340)), -+ (Self::Curie.boxed(), ForkCondition::Block(7096836)), -+ (Self::Darwin.boxed(), ForkCondition::Timestamp(1724227200)), -+ (Self::DarwinV2.boxed(), ForkCondition::Timestamp(1725264000)), -+ ]) -+ } -+ -+ /// Scroll sepolia list of hardforks. -+ pub fn scroll_sepolia() -> ChainHardforks { -+ ChainHardforks::new(vec![ -+ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), -+ (Self::Archimedes.boxed(), ForkCondition::Block(0)), -+ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), -+ (Self::Bernoulli.boxed(), ForkCondition::Block(3747132)), -+ (Self::Curie.boxed(), ForkCondition::Block(4740239)), -+ (Self::Darwin.boxed(), ForkCondition::Timestamp(1723622400)), -+ (Self::DarwinV2.boxed(), ForkCondition::Timestamp(1724832000)), -+ ]) -+ } -+} -+ -+/// Match helper method since it's not possible to match on `dyn Hardfork` -+fn match_hardfork<H, HF, SHF>(fork: H, hardfork_fn: HF, scroll_hardfork_fn: SHF) -> Option<u64> -+where -+ H: Hardfork, -+ HF: Fn(&EthereumHardfork) -> Option<u64>, -+ SHF: Fn(&ScrollHardfork) -> Option<u64>, -+{ -+ let fork: &dyn Any = &fork; -+ if let Some(fork) = fork.downcast_ref::<EthereumHardfork>() { -+ return hardfork_fn(fork); -+ } -+ fork.downcast_ref::<ScrollHardfork>().and_then(scroll_hardfork_fn) -+} -+ -+#[cfg(test)] -+mod tests { -+ use super::*; -+ -+ #[test] -+ fn test_match_hardfork() { -+ assert_eq!( -+ ScrollHardfork::scroll_mainnet_activation_block(ScrollHardfork::Bernoulli), -+ Some(5220340) -+ ); -+ assert_eq!( -+ ScrollHardfork::scroll_mainnet_activation_block(ScrollHardfork::Curie), -+ Some(7096836) -+ ); -+ } -+ -+ #[test] -+ fn check_scroll_hardfork_from_str() { -+ let hardfork_str = ["BernOulLi", "CUrie", "DaRwIn", "DaRwInV2"]; -+ let expected_hardforks = [ -+ ScrollHardfork::Bernoulli, -+ ScrollHardfork::Curie, -+ ScrollHardfork::Darwin, -+ ScrollHardfork::DarwinV2, -+ ]; -+ -+ let hardforks: Vec<ScrollHardfork> = -+ hardfork_str.iter().map(|h| ScrollHardfork::from_str(h).unwrap()).collect(); -+ -+ assert_eq!(hardforks, expected_hardforks); -+ } -+ -+ #[test] -+ fn check_nonexistent_hardfork_from_str() { -+ assert!(ScrollHardfork::from_str("not a hardfork").is_err()); -+ } ++/// Commands to be executed ++#[derive(Debug, Subcommand)] ++pub enum Commands< ++ Spec: ChainSpecParser = ScrollChainSpecParser, ++ Ext: clap::Args + fmt::Debug = NoArgs, ++> { ++ /// Start the node ++ #[command(name = "node")] ++ Node(Box<node::NodeCommand<Spec, Ext>>), ++ /// Initialize the database from a genesis file. ++ #[command(name = "init")] ++ Init(init_cmd::InitCommand<Spec>), ++ /// Initialize the database from a state dump file. ++ #[command(name = "init-state")] ++ InitState(init_state::InitStateCommand<Spec>), ++ /// This syncs RLP encoded blocks from a file. ++ #[command(name = "import")] ++ Import(import::ImportCommand<Spec>), ++ /// Dumps genesis block JSON configuration to stdout. ++ DumpGenesis(dump_genesis::DumpGenesisCommand<Spec>), ++ /// Database debugging utilities ++ #[command(name = "db")] ++ Db(db::Command<Spec>), ++ /// Manipulate individual stages. ++ #[command(name = "stage")] ++ Stage(Box<stage::Command<Spec>>), ++ /// P2P Debugging utilities ++ #[command(name = "p2p")] ++ P2P(p2p::Command<Spec>), ++ /// Write config to stdout ++ #[command(name = "config")] ++ Config(config_cmd::Command), ++ /// Scripts for node recovery ++ #[command(name = "recover")] ++ Recover(recover::Command<Spec>), ++ /// Prune according to the configuration without any limits ++ #[command(name = "prune")] ++ Prune(prune::PruneCommand<Spec>), +}
@@ -5505,9 +6639,9 @@
@@ -5522,7 +6656,7 @@
@@ -5532,59 +6666,175 @@
-
+42
+
+158
-0
-
diff --git reth/crates/scroll/hardforks/src/lib.rs scroll-reth/crates/scroll/hardforks/src/lib.rs +
diff --git reth/crates/scroll/cli/src/lib.rs scroll-reth/crates/scroll/cli/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..79ddb3e726cfd055dde4ed096d6df914cd7d6b33 +index 0000000000000000000000000000000000000000..d70b94fc90c788984292d63472aeb3d011d6b0ae --- /dev/null -+++ scroll-reth/crates/scroll/hardforks/src/lib.rs -@@ -0,0 +1,42 @@ -+//! Scroll-Reth hard forks. -+ -+#![doc = include_str!("../docs/hardforks.md")] ++++ scroll-reth/crates/scroll/cli/src/lib.rs +@@ -0,0 +1,158 @@ ++//! Scroll CLI implementation. ++#![cfg(all(feature = "scroll", not(feature = "optimism")))] + -+extern crate alloc; ++mod args; ++pub use args::ScrollRollupArgs; + -+pub mod hardfork; ++mod commands; ++pub use commands::Commands; + -+mod dev; ++mod spec; ++pub use spec::ScrollChainSpecParser; + -+pub use dev::DEV_HARDFORKS; -+pub use hardfork::ScrollHardfork; ++use clap::{value_parser, Parser}; ++use reth_cli::chainspec::ChainSpecParser; ++use reth_cli_commands::{common::CliNodeTypes, node::NoArgs}; ++use reth_cli_runner::CliRunner; ++use reth_db::DatabaseEnv; ++use reth_eth_wire::EthNetworkPrimitives; ++use reth_node_builder::{NodeBuilder, WithLaunchContext}; ++use reth_node_core::{ ++ args::LogArgs, ++ version::{LONG_VERSION, SHORT_VERSION}, ++}; ++use reth_node_metrics::recorder::install_prometheus_recorder; ++use reth_primitives::EthPrimitives; ++use reth_scroll_chainspec::ScrollChainSpec; ++use reth_scroll_evm::ScrollExecutorProvider; ++use reth_tracing::FileWorkerGuard; ++use std::{ffi::OsString, fmt, future::Future, sync::Arc}; ++use tracing::info; + -+use reth_ethereum_forks::{EthereumHardforks, ForkCondition}; ++/// The main scroll cli interface. ++/// ++/// This is the entrypoint to the executable. ++#[derive(Debug, Parser)] ++#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Scroll Reth", long_about = None ++)] ++pub struct Cli<Spec: ChainSpecParser = ScrollChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs> ++{ ++ /// The command to run ++ #[command(subcommand)] ++ command: Commands<Spec, Ext>, + -+/// Extends [`EthereumHardforks`] with scroll helper methods. -+pub trait ScrollHardforks: EthereumHardforks { -+ /// Retrieves [`ForkCondition`] by an [`ScrollHardfork`]. If `fork` is not present, returns -+ /// [`ForkCondition::Never`]. -+ fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition; ++ /// The chain this node is running. ++ /// ++ /// Possible values are either a built-in chain or the path to a chain specification file. ++ #[arg( ++ long, ++ value_name = "CHAIN_OR_PATH", ++ long_help = Spec::help_message(), ++ default_value = Spec::SUPPORTED_CHAINS[0], ++ value_parser = Spec::parser(), ++ global = true, ++ )] ++ chain: Arc<Spec::ChainSpec>, + -+ /// Convenience method to check if [`Bernoulli`](ScrollHardfork::Bernoulli) is active at a given -+ /// block number. -+ fn is_bernoulli_active_at_block(&self, block_number: u64) -> bool { -+ self.scroll_fork_activation(ScrollHardfork::Bernoulli).active_at_block(block_number) -+ } ++ /// Add a new instance of a node. ++ /// ++ /// Configures the ports of the node to avoid conflicts with the defaults. ++ /// This is useful for running multiple nodes on the same machine. ++ /// ++ /// Max number of instances is 200. It is chosen in a way so that it's not possible to have ++ /// port numbers that conflict with each other. ++ /// ++ /// Changes to the following port numbers: ++ /// - `DISCOVERY_PORT`: default + `instance` - 1 ++ /// - `AUTH_PORT`: default + `instance` * 100 - 100 ++ /// - `HTTP_RPC_PORT`: default - `instance` + 1 ++ /// - `WS_RPC_PORT`: default + `instance` * 2 - 2 ++ #[arg(long, value_name = "INSTANCE", global = true, default_value_t = 1, value_parser = value_parser!(u16).range(..=200) ++ )] ++ instance: u16, + -+ /// Returns `true` if [`Curie`](ScrollHardfork::Curie) is active at given block block number. -+ fn is_curie_active_at_block(&self, block_number: u64) -> bool { -+ self.scroll_fork_activation(ScrollHardfork::Curie).active_at_block(block_number) ++ #[command(flatten)] ++ logs: LogArgs, ++} ++ ++impl Cli { ++ /// Parsers only the default CLI arguments ++ pub fn parse_args() -> Self { ++ Self::parse() + } + -+ /// Returns `true` if [`Darwin`](ScrollHardfork::Darwin) is active at given block timestamp. -+ fn is_darwin_active_at_timestamp(&self, timestamp: u64) -> bool { -+ self.scroll_fork_activation(ScrollHardfork::Darwin).active_at_timestamp(timestamp) ++ /// Parsers only the default CLI arguments from the given iterator ++ pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error> ++ where ++ I: IntoIterator<Item = T>, ++ T: Into<OsString> + Clone, ++ { ++ Self::try_parse_from(itr) + } ++} + -+ /// Returns `true` if [`DarwinV2`](ScrollHardfork::DarwinV2) is active at given block timestamp. -+ fn is_darwin_v2_active_at_timestamp(&self, timestamp: u64) -> bool { -+ self.scroll_fork_activation(ScrollHardfork::DarwinV2).active_at_timestamp(timestamp) ++impl<C, Ext> Cli<C, Ext> ++where ++ C: ChainSpecParser<ChainSpec = ScrollChainSpec>, ++ Ext: clap::Args + fmt::Debug, ++{ ++ /// Execute the configured cli command. ++ /// ++ /// This accepts a closure that is used to launch the node via the ++ /// [`NodeCommand`](reth_cli_commands::node::NodeCommand). ++ pub fn run<L, Fut, Types>(mut self, launcher: L) -> eyre::Result<()> ++ where ++ L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut, ++ Fut: Future<Output = eyre::Result<()>>, ++ Types: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>, ++ { ++ // add network name to logs dir ++ self.logs.log_file_directory = ++ self.logs.log_file_directory.join(self.chain.chain().to_string()); ++ ++ let _guard = self.init_tracing()?; ++ info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory); ++ ++ // Install the prometheus recorder to be sure to record all metrics ++ let _ = install_prometheus_recorder(); ++ ++ let runner = CliRunner::default(); ++ match self.command { ++ Commands::Node(command) => { ++ runner.run_command_until_exit(|ctx| command.execute(ctx, launcher)) ++ } ++ Commands::Init(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), ++ Commands::InitState(command) => { ++ runner.run_blocking_until_ctrl_c(command.execute::<Types>()) ++ } ++ Commands::Import(command) => runner.run_blocking_until_ctrl_c( ++ command.execute::<Types, _, _>(ScrollExecutorProvider::scroll), ++ ), ++ Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()), ++ Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), ++ Commands::Stage(command) => runner.run_command_until_exit(|ctx| { ++ command.execute::<Types, _, _, EthNetworkPrimitives>( ++ ctx, ++ ScrollExecutorProvider::scroll, ++ ) ++ }), ++ Commands::P2P(command) => { ++ runner.run_until_ctrl_c(command.execute::<EthNetworkPrimitives>()) ++ } ++ Commands::Config(command) => runner.run_until_ctrl_c(command.execute()), ++ Commands::Recover(command) => { ++ runner.run_command_until_exit(|ctx| command.execute::<Types>(ctx)) ++ } ++ Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<Types>()), ++ } ++ } ++ ++ /// Initializes tracing with the configured options. ++ /// ++ /// If file logging is enabled, this function returns a guard that must be kept alive to ensure ++ /// that all logs are flushed to disk. ++ pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> { ++ let guard = self.logs.init_tracing()?; ++ Ok(guard) + } +}
@@ -5593,9 +6843,9 @@
@@ -5610,7 +6860,7 @@
@@ -5620,94 +6870,49 @@
-
+64
+
+21
-0
-
diff --git reth/crates/scroll/node/Cargo.toml scroll-reth/crates/scroll/node/Cargo.toml +
diff --git reth/crates/scroll/cli/src/spec.rs scroll-reth/crates/scroll/cli/src/spec.rs new file mode 100644 -index 0000000000000000000000000000000000000000..0c6bee6a21834645506b08aec4ce3b4060f64d7c +index 0000000000000000000000000000000000000000..361029d9d1fc6101bd9f15b0d01b824a3aff04ec --- /dev/null -+++ scroll-reth/crates/scroll/node/Cargo.toml -@@ -0,0 +1,65 @@ -+[package] -+name = "reth-scroll-node" -+version.workspace = true -+edition.workspace = true -+rust-version.workspace = true -+license.workspace = true -+homepage.workspace = true -+repository.workspace = true -+exclude.workspace = true -+ -+[lints] -+workspace = true -+ -+[dependencies] -+# reth -+reth-db.workspace = true -+reth-consensus.workspace = true -+reth-ethereum-engine-primitives.workspace = true -+reth-ethereum-forks.workspace = true -+reth-evm.workspace = true -+reth-network.workspace = true -+reth-node-api.workspace = true -+reth-node-types.workspace = true -+reth-node-builder.workspace = true -+reth-payload-builder = { workspace = true, features = ["test-utils"] } -+reth-primitives.workspace = true -+reth-primitives-traits.workspace = true -+reth-provider.workspace = true -+reth-rpc.workspace = true -+reth-tracing.workspace = true -+reth-transaction-pool.workspace = true -+reth-trie-db.workspace = true -+ -+# alloy -+alloy-rpc-types-engine.workspace = true -+ -+# scroll -+reth-scroll-chainspec.workspace = true -+reth-scroll-engine.workspace = true -+reth-scroll-evm.workspace = true ++++ scroll-reth/crates/scroll/cli/src/spec.rs +@@ -0,0 +1,21 @@ ++use reth_cli::chainspec::{parse_genesis, ChainSpecParser}; ++use reth_scroll_chainspec::{ScrollChainSpec, SCROLL_DEV, SCROLL_MAINNET, SCROLL_SEPOLIA}; ++use std::sync::Arc; + -+# alloy -+alloy-primitives.workspace = true ++/// The parser for the Scroll chain specification. ++#[derive(Debug, Clone)] ++pub struct ScrollChainSpecParser; + -+# misc -+eyre.workspace = true ++impl ChainSpecParser for ScrollChainSpecParser { ++ type ChainSpec = ScrollChainSpec; ++ const SUPPORTED_CHAINS: &'static [&'static str] = &["dev", "scroll-mainnet", "scroll-sepolia"]; + -+[features] -+optimism = [ -+ "reth-db/optimism", -+ "reth-primitives/optimism", -+ "reth-provider/optimism", -+ "reth-scroll-evm/optimism", -+ "reth-scroll-engine/optimism" -+] -+scroll = [ -+ "reth-db/scroll", -+ "reth-node-builder/scroll", -+ "reth-primitives/scroll", -+ "reth-provider/scroll", -+ "reth-evm/scroll", -+ "reth-scroll-evm/scroll", -+ "reth-scroll-engine/scroll" -+] -+skip-state-root-validation = [] -\ No newline at end of file
++ fn parse(s: &str) -> eyre::Result<Arc<Self::ChainSpec>> { ++ Ok(match s { ++ "dev" => SCROLL_DEV.clone(), ++ "scroll-mainnet" => SCROLL_MAINNET.clone(), ++ "scroll-sepolia" => SCROLL_SEPOLIA.clone(), ++ _ => Arc::new(parse_genesis(s)?.into()), ++ }) ++ } ++}
@@ -5722,7 +6927,7 @@
@@ -5732,44 +6937,50 @@
-
+16
+
+22
-0
-
diff --git reth/crates/scroll/node/src/addons.rs scroll-reth/crates/scroll/node/src/addons.rs +
diff --git reth/crates/scroll/consensus/Cargo.toml scroll-reth/crates/scroll/consensus/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..588c901ae5dd8dcc16b5c9afc0a8fc561c390695 +index 0000000000000000000000000000000000000000..beec5c27120ddd162cd303f68f127d8b0946d452 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/addons.rs -@@ -0,0 +1,16 @@ -+use crate::ScrollEngineValidatorBuilder; -+use reth_network::NetworkHandle; -+use reth_node_builder::{rpc::RpcAddOns, FullNodeComponents, FullNodeTypes}; -+use reth_rpc::EthApi; ++++ scroll-reth/crates/scroll/consensus/Cargo.toml +@@ -0,0 +1,22 @@ ++[package] ++name = "reth-scroll-consensus" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+/// Add-ons for the Scroll follower node. -+pub type ScrollAddOns<N> = RpcAddOns< -+ N, -+ EthApi< -+ <N as FullNodeTypes>::Provider, -+ <N as FullNodeComponents>::Pool, -+ NetworkHandle, -+ <N as FullNodeComponents>::Evm, -+ >, -+ ScrollEngineValidatorBuilder, -+>;
++[lints] ++workspace = true ++ ++[dependencies] ++# revm ++revm.workspace = true ++ ++[dev-dependencies] ++eyre.workspace = true ++ ++[features] ++scroll = ["revm/scroll"]
@@ -5784,7 +6995,7 @@
@@ -5794,42 +7005,223 @@
-
+14
+
+195
-0
-
diff --git reth/crates/scroll/node/src/builder/consensus.rs scroll-reth/crates/scroll/node/src/builder/consensus.rs +
diff --git reth/crates/scroll/consensus/src/curie.rs scroll-reth/crates/scroll/consensus/src/curie.rs new file mode 100644 -index 0000000000000000000000000000000000000000..259e3109588e599b2b10c8e348c80e038475c806 +index 0000000000000000000000000000000000000000..ecbe26f3bce68b768375139c0f1cc493ddc242a0 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/consensus.rs -@@ -0,0 +1,14 @@ -+use reth_consensus::noop::NoopConsensus; -+use reth_node_builder::{components::ConsensusBuilder, BuilderContext, FullNodeTypes}; -+ -+/// The consensus builder for Scroll. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollConsensusBuilder; ++++ scroll-reth/crates/scroll/consensus/src/curie.rs +@@ -0,0 +1,195 @@ ++//! Curie fork transition for Scroll. ++//! ++//! On block 7096836, Scroll performed a transition to the Curie fork state, which brought various ++//! changes to the protocol: ++//! 1. Fee reduction cost thanks to the use of compressed blobs on the L1. ++//! 2. Modified [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) pricing ++//! model along with support for EIP-1559 and EIP-2930 transactions. ++//! 3. Support for `MLOAD`, `TLOAD` and `MCOPY` ([EIP-1153](https://eips.ethereum.org/EIPS/eip-1153) ++//! and [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656)). ++//! 4. Dynamic block time. ++//! ++//! Compressed blobs allowed for more transactions to be stored in each blob, reducing the DA cost ++//! per transactions. Accordingly, the L1 gas oracle contract's bytecode was updated in order to ++//! reflect the update in the DA costs: ++//! - original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar`. ++//! - updated formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar`. ++//! ++//! More details on the Curie update: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade> + -+impl<Node: FullNodeTypes> ConsensusBuilder<Node> for ScrollConsensusBuilder { -+ type Consensus = NoopConsensus; ++use revm::{ ++ db::states::StorageSlot, ++ primitives::{address, bytes, AccountInfo, Address, Bytecode, Bytes, U256}, ++ Database, State, ++}; + -+ async fn build_consensus(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> { -+ Ok(NoopConsensus::default()) -+ } -+}
-
- - -
++/// L1 gas price oracle address. ++/// <https://scrollscan.com/address/0x5300000000000000000000000000000000000002> ++pub const L1_GAS_PRICE_ORACLE_ADDRESS: Address = ++ address!("5300000000000000000000000000000000000002"); ++/// Bytecode of L1 gas price oracle at Curie transition. ++pub const CURIE_L1_GAS_PRICE_ORACLE_BYTECODE: Bytes = bytes!("608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033"); ++/// Storage update of L1 gas price oracle at Curie transition. ++pub const CURIE_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 4] = [ ++ (L1_BLOB_BASE_FEE_SLOT, INITIAL_L1_BLOB_BASE_FEE), ++ (COMMIT_SCALAR_SLOT, INITIAL_COMMIT_SCALAR), ++ (BLOB_SCALAR_SLOT, INITIAL_BLOB_SCALAR), ++ (IS_CURIE_SLOT, IS_CURIE), ++]; ++ ++/// L1 gas price oracle base fee slot. ++pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); ++/// L1 gas price oracle over head slot. ++pub const OVER_HEAD_SLOT: U256 = U256::from_limbs([2, 0, 0, 0]); ++/// L1 gas price oracle scalar slot. ++pub const SCALAR_SLOT: U256 = U256::from_limbs([3, 0, 0, 0]); ++ ++/// L1 gas price oracle blob base fee slot. Added in the Curie fork. ++pub const L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([5, 0, 0, 0]); ++/// L1 gas price oracle commit scalar slot. Added in the Curie fork. ++pub const COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6, 0, 0, 0]); ++/// L1 gas price oracle blob scalar slot. Added in the Curie fork. ++pub const BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7, 0, 0, 0]); ++/// L1 gas price oracle "is Curie" slot. Added in the Curie fork. ++pub const IS_CURIE_SLOT: U256 = U256::from_limbs([8, 0, 0, 0]); ++ ++/// The initial blob base fee used by the oracle contract. ++const INITIAL_L1_BLOB_BASE_FEE: U256 = U256::from_limbs([1, 0, 0, 0]); ++/// The initial commit scalar used by the oracle contract. ++const INITIAL_COMMIT_SCALAR: U256 = U256::from_limbs([230759955285, 0, 0, 0]); ++/// The initial blob scalar used by the oracle contract. ++const INITIAL_BLOB_SCALAR: U256 = U256::from_limbs([417565260, 0, 0, 0]); ++/// Curie slot is set to 1 (true) after the Curie block fork. ++const IS_CURIE: U256 = U256::from_limbs([1, 0, 0, 0]); ++ ++/// Applies the Scroll Curie hard fork to the state: ++/// - Updates the L1 oracle contract bytecode to reflect the DA cost reduction. ++/// - Sets the initial blob base fee, commit and blob scalar and sets the `isCurie` slot to 1 ++/// (true). ++pub fn apply_curie_hard_fork<DB: Database>(state: &mut State<DB>) -> Result<(), DB::Error> { ++ let oracle = state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; ++ ++ // compute the code hash ++ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); ++ let bytecode_len = bytecode.len(); ++ let code_hash = bytecode.hash_slow(); ++ ++ // get the old oracle account info ++ let old_oracle_info = oracle.account_info().unwrap_or_default(); ++ ++ // init new oracle account information ++ let new_oracle_info = ++ AccountInfo { code_size: bytecode_len, code_hash, code: Some(bytecode), ..old_oracle_info }; ++ ++ // init new storage ++ let new_storage = CURIE_L1_GAS_PRICE_ORACLE_STORAGE ++ .into_iter() ++ .map(|(slot, present_value)| { ++ ( ++ slot, ++ StorageSlot { ++ present_value, ++ previous_or_original_value: oracle.storage_slot(slot).unwrap_or_default(), ++ }, ++ ) ++ }) ++ .collect(); ++ ++ // create transition for oracle new account info and storage ++ let transition = oracle.change(new_oracle_info, new_storage); ++ ++ // add transition ++ if let Some(s) = state.transition_state.as_mut() { ++ s.add_transitions(vec![(L1_GAS_PRICE_ORACLE_ADDRESS, transition)]) ++ } ++ ++ Ok(()) ++} ++ ++#[cfg(test)] ++mod tests { ++ use crate::{ ++ apply_curie_hard_fork, ++ curie::{ ++ CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, CURIE_L1_GAS_PRICE_ORACLE_STORAGE, ++ L1_GAS_PRICE_ORACLE_ADDRESS, ++ }, ++ }; ++ use revm::{ ++ db::{ ++ states::{bundle_state::BundleRetention, plain_account::PlainStorage, StorageSlot}, ++ EmptyDB, ++ }, ++ primitives::{bytes, keccak256, AccountInfo, Bytecode, U256}, ++ Database, State, ++ }; ++ use std::str::FromStr; ++ ++ #[test] ++ fn test_apply_curie_fork() -> eyre::Result<()> { ++ // init state ++ let db = EmptyDB::new(); ++ let mut state = ++ State::builder().with_database(db).with_bundle_update().without_state_clear().build(); ++ ++ // oracle pre fork state ++ let bytecode_pre_fork = Bytecode::new_raw( bytes!("608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033")); ++ let oracle_pre_fork = AccountInfo { ++ code_size: bytecode_pre_fork.len(), ++ code_hash: bytecode_pre_fork.hash_slow(), ++ code: Some(bytecode_pre_fork), ++ ..Default::default() ++ }; ++ let oracle_storage_pre_fork = PlainStorage::from_iter([ ++ (U256::ZERO, U256::from_str("0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b")?), ++ (U256::from(1), U256::from(0x15f50e5e)), ++ (U256::from(2), U256::from(0x38)), ++ (U256::from(3), U256::from(0x3e95ba80)), ++ (U256::from(4), U256::from_str("0x5300000000000000000000000000000000000003")?), ++ ]); ++ state.insert_account_with_storage( ++ L1_GAS_PRICE_ORACLE_ADDRESS, ++ oracle_pre_fork.clone(), ++ oracle_storage_pre_fork.clone(), ++ ); ++ ++ // apply curie fork ++ apply_curie_hard_fork(&mut state)?; ++ ++ // merge transitions ++ state.merge_transitions(BundleRetention::Reverts); ++ let bundle = state.take_bundle(); ++ ++ // check oracle account info ++ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); ++ let code_hash = keccak256(&CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); ++ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); ++ let expected_oracle_info = AccountInfo { ++ code_size: CURIE_L1_GAS_PRICE_ORACLE_BYTECODE.len(), ++ code_hash, ++ code: Some(bytecode.clone()), ++ ..Default::default() ++ }; ++ ++ assert_eq!(oracle.original_info.unwrap(), oracle_pre_fork); ++ assert_eq!(oracle.info.unwrap(), expected_oracle_info); ++ ++ // check oracle storage changeset ++ let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); ++ storage.sort_by(|(a, _), (b, _)| a.cmp(b)); ++ for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { ++ assert_eq!(got.0, expected.0); ++ assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); ++ } ++ ++ // check oracle original storage ++ for (slot, value) in oracle_storage_pre_fork { ++ assert_eq!(state.storage(L1_GAS_PRICE_ORACLE_ADDRESS, slot)?, value) ++ } ++ ++ // check deployed contract ++ assert_eq!(bundle.contracts.get(&code_hash).unwrap().clone(), bytecode); ++ ++ Ok(()) ++ } ++}
+
+ + +
@@ -5844,7 +7236,7 @@
@@ -5854,131 +7246,39 @@
-
+103
+
+11
-0
-
diff --git reth/crates/scroll/node/src/builder/engine.rs scroll-reth/crates/scroll/node/src/builder/engine.rs +
diff --git reth/crates/scroll/consensus/src/lib.rs scroll-reth/crates/scroll/consensus/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..4d6985b4e3c11b5ffbfbff5d9f9e7b4a429c8d25 +index 0000000000000000000000000000000000000000..6d4e4562a5bdad13492ce646abb0db466428e409 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/engine.rs -@@ -0,0 +1,103 @@ -+use alloy_primitives::{Sealable, U256}; -+use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadSidecar, PayloadError}; -+use reth_ethereum_engine_primitives::{EthEngineTypes, EthPayloadAttributes}; -+use reth_node_api::PayloadValidator; -+use reth_node_builder::{ -+ rpc::EngineValidatorBuilder, AddOnsContext, EngineApiMessageVersion, -+ EngineObjectValidationError, EngineTypes, EngineValidator, FullNodeComponents, -+ PayloadOrAttributes, -+}; -+use reth_node_types::NodeTypesWithEngine; -+use reth_primitives::{Block, BlockExt, EthPrimitives, SealedBlockFor}; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_scroll_engine::try_into_block; -+use std::sync::Arc; -+ -+/// The block difficulty for in turn signing in the Clique consensus. -+const CLIQUE_IN_TURN_DIFFICULTY: U256 = U256::from_limbs([2, 0, 0, 0]); -+/// The block difficulty for out of turn signing in the Clique consensus. -+const CLIQUE_NO_TURN_DIFFICULTY: U256 = U256::from_limbs([1, 0, 0, 0]); -+ -+/// Builder for [`ScrollEngineValidator`]. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollEngineValidatorBuilder; -+ -+impl<Node, Types> EngineValidatorBuilder<Node> for ScrollEngineValidatorBuilder -+where -+ Types: NodeTypesWithEngine< -+ ChainSpec = ScrollChainSpec, -+ Primitives = EthPrimitives, -+ Engine = EthEngineTypes, -+ >, -+ Node: FullNodeComponents<Types = Types>, -+{ -+ type Validator = ScrollEngineValidator; -+ -+ async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> { -+ let chainspec = ctx.config.chain.clone(); -+ Ok(ScrollEngineValidator { chainspec }) -+ } -+} -+ -+/// Scroll engine validator. -+#[derive(Debug, Clone)] -+pub struct ScrollEngineValidator { -+ chainspec: Arc<ScrollChainSpec>, -+} -+ -+impl<Types> EngineValidator<Types> for ScrollEngineValidator -+where -+ Types: EngineTypes<PayloadAttributes = EthPayloadAttributes>, -+{ -+ fn validate_version_specific_fields( -+ &self, -+ _version: EngineApiMessageVersion, -+ _payload_or_attrs: PayloadOrAttributes<'_, EthPayloadAttributes>, -+ ) -> Result<(), EngineObjectValidationError> { -+ Ok(()) -+ } -+ -+ fn ensure_well_formed_attributes( -+ &self, -+ _version: EngineApiMessageVersion, -+ _attributes: &EthPayloadAttributes, -+ ) -> Result<(), EngineObjectValidationError> { -+ Ok(()) -+ } -+} ++++ scroll-reth/crates/scroll/consensus/src/lib.rs +@@ -0,0 +1,11 @@ ++//! Scroll consensus implementation. + -+impl PayloadValidator for ScrollEngineValidator { -+ type Block = Block; ++#![cfg(feature = "scroll")] + -+ fn ensure_well_formed_payload( -+ &self, -+ payload: ExecutionPayload, -+ sidecar: ExecutionPayloadSidecar, -+ ) -> Result<SealedBlockFor<Self::Block>, PayloadError> { -+ let expected_hash = payload.block_hash(); -+ -+ // First parse the block -+ let mut block = try_into_block(payload, &sidecar, self.chainspec.clone())?; -+ -+ // Seal the block with the in-turn difficulty and return if hashes match -+ block.header.difficulty = CLIQUE_IN_TURN_DIFFICULTY; -+ let sealed_block_in_turn = block.seal_ref_slow(); -+ if sealed_block_in_turn.hash() == expected_hash { -+ let hash = sealed_block_in_turn.hash(); -+ return Ok(block.seal(hash)) -+ } -+ -+ // Seal the block with the no-turn difficulty and return if hashes match -+ block.header.difficulty = CLIQUE_NO_TURN_DIFFICULTY; -+ let sealed_block_no_turn = block.seal_ref_slow(); -+ if sealed_block_no_turn.hash() == expected_hash { -+ let hash = sealed_block_no_turn.hash(); -+ return Ok(block.seal(hash)) -+ } -+ -+ Err(PayloadError::BlockHash { -+ execution: sealed_block_no_turn.hash(), -+ consensus: expected_hash, -+ }) -+ } -+}
++mod curie; ++pub use curie::{ ++ apply_curie_hard_fork, BLOB_SCALAR_SLOT, COMMIT_SCALAR_SLOT, ++ CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, CURIE_L1_GAS_PRICE_ORACLE_STORAGE, IS_CURIE_SLOT, ++ L1_BASE_FEE_SLOT, L1_BLOB_BASE_FEE_SLOT, L1_GAS_PRICE_ORACLE_ADDRESS, OVER_HEAD_SLOT, ++ SCALAR_SLOT, ++};
@@ -5993,7 +7293,7 @@
@@ -6003,60 +7303,69 @@
-
+32
+
+39
-0
-
diff --git reth/crates/scroll/node/src/builder/execution.rs scroll-reth/crates/scroll/node/src/builder/execution.rs +
diff --git reth/crates/scroll/engine/Cargo.toml scroll-reth/crates/scroll/engine/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..a1c823743e6bdff96e510b6f0b8b459a331b2110 +index 0000000000000000000000000000000000000000..ac1a29c93237be3b4ebaf05054443140faeee051 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/execution.rs -@@ -0,0 +1,32 @@ -+use reth_evm::execute::BasicBlockExecutorProvider; -+use reth_node_builder::{components::ExecutorBuilder, BuilderContext, FullNodeTypes}; -+use reth_node_types::NodeTypesWithEngine; -+use reth_primitives::EthPrimitives; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_scroll_evm::{ScrollEvmConfig, ScrollExecutionStrategyFactory}; ++++ scroll-reth/crates/scroll/engine/Cargo.toml +@@ -0,0 +1,40 @@ ++[package] ++name = "reth-scroll-engine" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+/// Executor builder for Scroll. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollExecutorBuilder; ++[lints] ++workspace = true + -+impl<Node> ExecutorBuilder<Node> for ScrollExecutorBuilder -+where -+ Node: FullNodeTypes, -+ Node::Types: NodeTypesWithEngine<ChainSpec = ScrollChainSpec, Primitives = EthPrimitives>, -+{ -+ type EVM = ScrollEvmConfig; -+ type Executor = BasicBlockExecutorProvider<ScrollExecutionStrategyFactory>; ++[dependencies] ++# reth ++reth-primitives.workspace = true + -+ async fn build_evm( -+ self, -+ ctx: &BuilderContext<Node>, -+ ) -> eyre::Result<(Self::EVM, Self::Executor)> { -+ let chain_spec = ctx.chain_spec(); -+ let strategy_factory = ScrollExecutionStrategyFactory::new(chain_spec); -+ let evm_config = strategy_factory.evm_config(); ++# scroll ++reth-scroll-chainspec.workspace = true ++reth-scroll-forks.workspace = true + -+ let executor = BasicBlockExecutorProvider::new(strategy_factory); ++# alloy ++alloy-consensus.workspace = true ++alloy-eips.workspace = true ++alloy-rlp.workspace = true ++alloy-rpc-types-engine.workspace = true + -+ Ok((evm_config, executor)) -+ } -+}
++[features] ++default = ["std"] ++std = [ ++ "reth-primitives/std", ++ "reth-scroll-chainspec/std", ++ "reth-scroll-forks/std", ++ "alloy-consensus/std", ++ "alloy-eips/std", ++ "alloy-rlp/std", ++ "alloy-rpc-types-engine/std" ++] ++scroll = ["reth-primitives/scroll"] ++optimism = ["reth-primitives/optimism"] +\ No newline at end of file
@@ -6071,7 +7380,7 @@
@@ -6081,34 +7390,43 @@
-
+6
+
+15
-0
-
diff --git reth/crates/scroll/node/src/builder/mod.rs scroll-reth/crates/scroll/node/src/builder/mod.rs +
diff --git reth/crates/scroll/engine/src/lib.rs scroll-reth/crates/scroll/engine/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..86ed6d848601ad99def48042f8331f48e688b84d +index 0000000000000000000000000000000000000000..2ebb577e9ffdc62c541c3a46da7a0dda71c76e2f --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/mod.rs -@@ -0,0 +1,6 @@ -+pub(crate) mod consensus; -+pub(crate) mod engine; -+pub(crate) mod execution; -+pub(crate) mod network; -+pub(crate) mod payload; -+pub(crate) mod pool;
++++ scroll-reth/crates/scroll/engine/src/lib.rs +@@ -0,0 +1,15 @@ ++//! Scroll engine API crate. ++ ++#![doc( ++ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", ++ html_favicon_url = "https://avatars.githubusercontent.com/u/87750292?s=200&v=4", ++ issue_tracker_base_url = "https://github.com/scroll-tech/reth/issues/" ++)] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(not(feature = "std"), no_std)] ++#![cfg(all(feature = "scroll", not(feature = "optimism")))] ++ ++extern crate alloc; ++ ++mod payload; ++pub use payload::utils::try_into_block;
@@ -6123,7 +7441,7 @@
@@ -6133,68 +7451,29 @@
-
+40
+
+1
-0
-
diff --git reth/crates/scroll/node/src/builder/network.rs scroll-reth/crates/scroll/node/src/builder/network.rs +
diff --git reth/crates/scroll/engine/src/payload/mod.rs scroll-reth/crates/scroll/engine/src/payload/mod.rs new file mode 100644 -index 0000000000000000000000000000000000000000..d161d19085b2c85f85631e95f59ef2a3d6d54a7a +index 0000000000000000000000000000000000000000..80ce84551eacde12136174a65cfb84d49e1cfa48 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/network.rs -@@ -0,0 +1,40 @@ -+use reth_network::{ -+ config::NetworkMode, EthNetworkPrimitives, NetworkConfig, NetworkManager, PeersInfo, -+}; -+use reth_node_api::TxTy; -+use reth_node_builder::{components::NetworkBuilder, BuilderContext, FullNodeTypes}; -+use reth_node_types::NodeTypes; -+use reth_primitives::{EthPrimitives, PooledTransaction}; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_tracing::tracing::info; -+use reth_transaction_pool::{PoolTransaction, TransactionPool}; -+ -+/// The network builder for Scroll. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollNetworkBuilder; -+ -+impl<Node, Pool> NetworkBuilder<Node, Pool> for ScrollNetworkBuilder -+where -+ Node: FullNodeTypes<Types: NodeTypes<ChainSpec = ScrollChainSpec, Primitives = EthPrimitives>>, -+ Pool: TransactionPool< -+ Transaction: PoolTransaction<Consensus = TxTy<Node::Types>, Pooled = PooledTransaction>, -+ > + Unpin -+ + 'static, -+{ -+ type Primitives = EthNetworkPrimitives; -+ -+ async fn build_network( -+ self, -+ ctx: &BuilderContext<Node>, -+ pool: Pool, -+ ) -> eyre::Result<reth_network::NetworkHandle> { -+ // set the network mode to work. -+ let config = ctx.network_config()?; -+ let config = NetworkConfig { network_mode: NetworkMode::Work, ..config }; -+ -+ let network = NetworkManager::builder(config).await?; -+ let handle = ctx.start_network(network, pool); -+ info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); -+ Ok(handle) -+ } -+}
++++ scroll-reth/crates/scroll/engine/src/payload/mod.rs +@@ -0,0 +1 @@ ++pub(crate) mod utils;
@@ -6209,7 +7488,7 @@
@@ -6219,131 +7498,142 @@
-
+47
+
+124
-0
-
diff --git reth/crates/scroll/node/src/builder/payload.rs scroll-reth/crates/scroll/node/src/builder/payload.rs +
diff --git reth/crates/scroll/engine/src/payload/utils.rs scroll-reth/crates/scroll/engine/src/payload/utils.rs new file mode 100644 -index 0000000000000000000000000000000000000000..945c12c40f5a03ea9c404c1b36d1a7486fec1db4 +index 0000000000000000000000000000000000000000..73ad2b50346a56a0bdf8b94d130d2da9d3fdd8aa --- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/payload.rs -@@ -0,0 +1,47 @@ -+use reth_ethereum_engine_primitives::{ -+ EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, -+}; -+use reth_node_builder::{ -+ components::PayloadServiceBuilder, BuilderContext, FullNodeTypes, PayloadTypes, -+}; -+use reth_node_types::NodeTypesWithEngine; -+use reth_payload_builder::{ -+ test_utils::TestPayloadJobGenerator, PayloadBuilderHandle, PayloadBuilderService, ++++ scroll-reth/crates/scroll/engine/src/payload/utils.rs +@@ -0,0 +1,124 @@ ++use alloc::{sync::Arc, vec::Vec}; ++use alloy_consensus::EMPTY_OMMER_ROOT_HASH; ++use alloy_eips::eip2718::Decodable2718; ++use alloy_rlp::BufMut; ++use alloy_rpc_types_engine::{ ++ ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1, ExecutionPayloadV2, ++ ExecutionPayloadV3, PayloadError, +}; -+use reth_primitives::EthPrimitives; -+use reth_provider::CanonStateSubscriptions; -+use reth_transaction_pool::TransactionPool; ++use reth_primitives::{proofs, Block, BlockBody, Header}; ++use reth_scroll_chainspec::ScrollChainSpec; ++use reth_scroll_forks::ScrollHardfork; + -+/// Payload builder for Scroll. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollPayloadBuilder; ++/// Tries to create a new unsealed block from the given payload, sidecar and chain specification. ++pub fn try_into_block<T: Decodable2718>( ++ value: ExecutionPayload, ++ sidecar: &ExecutionPayloadSidecar, ++ chainspec: Arc<ScrollChainSpec>, ++) -> Result<Block<T>, PayloadError> { ++ let mut base_payload = match value { ++ ExecutionPayload::V1(payload) => try_payload_v1_to_block(payload, chainspec)?, ++ ExecutionPayload::V2(payload) => try_payload_v2_to_block(payload, chainspec)?, ++ ExecutionPayload::V3(payload) => try_payload_v3_to_block(payload, chainspec)?, ++ }; + -+impl<Node, Pool> PayloadServiceBuilder<Node, Pool> for ScrollPayloadBuilder -+where -+ Node: FullNodeTypes, -+ Node::Types: NodeTypesWithEngine<Primitives = EthPrimitives>, -+ <Node::Types as NodeTypesWithEngine>::Engine: PayloadTypes< -+ BuiltPayload = EthBuiltPayload, -+ PayloadAttributes = EthPayloadAttributes, -+ PayloadBuilderAttributes = EthPayloadBuilderAttributes, -+ >, -+ Pool: TransactionPool, -+{ -+ async fn spawn_payload_service( -+ self, -+ ctx: &BuilderContext<Node>, -+ _pool: Pool, -+ ) -> eyre::Result< -+ PayloadBuilderHandle<<<Node as FullNodeTypes>::Types as NodeTypesWithEngine>::Engine>, -+ > { -+ let test_payload_generator = TestPayloadJobGenerator::default(); -+ let (payload_service, payload_builder) = PayloadBuilderService::new( -+ test_payload_generator, -+ ctx.provider().canonical_state_stream(), -+ ); ++ base_payload.header.parent_beacon_block_root = sidecar.parent_beacon_block_root(); ++ base_payload.header.requests_hash = sidecar.requests_hash(); + -+ ctx.task_executor().spawn_critical("payload builder service", Box::pin(payload_service)); ++ Ok(base_payload) ++} + -+ eyre::Ok(payload_builder) -+ } -+}
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+20
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/node/src/builder/pool.rs scroll-reth/crates/scroll/node/src/builder/pool.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..15ca82e98c8ec523d80deb61a4881a75e2500021 ---- /dev/null -+++ scroll-reth/crates/scroll/node/src/builder/pool.rs -@@ -0,0 +1,20 @@ -+use reth_node_builder::{components::PoolBuilder, BuilderContext, FullNodeTypes}; -+use reth_node_types::NodeTypes; -+use reth_primitives::EthPrimitives; -+use reth_scroll_chainspec::ScrollChainSpec; -+use reth_transaction_pool::noop::NoopTransactionPool; ++/// Tries to convert an [`ExecutionPayloadV1`] to [`Block`]. ++fn try_payload_v1_to_block<T: Decodable2718>( ++ payload: ExecutionPayloadV1, ++ chainspec: Arc<ScrollChainSpec>, ++) -> Result<Block<T>, PayloadError> { ++ // WARNING: It’s allowed for a base fee in EIP1559 to increase unbounded. We assume that ++ // it will fit in an u64. This is not always necessarily true, although it is extremely ++ // unlikely not to be the case, a u64 maximum would have 2^64 which equates to 18 ETH per ++ // gas. ++ let basefee = chainspec ++ .is_fork_active_at_block(ScrollHardfork::Curie, payload.block_number) ++ .then_some(payload.base_fee_per_gas) ++ .map(|b| b.try_into()) ++ .transpose() ++ .map_err(|_| PayloadError::BaseFee(payload.base_fee_per_gas))?; + -+/// Pool builder for Scroll. -+#[derive(Debug, Default, Clone, Copy)] -+pub struct ScrollPoolBuilder; ++ let transactions = payload ++ .transactions ++ .iter() ++ .map(|tx| { ++ let mut buf = tx.as_ref(); + -+impl<Node> PoolBuilder<Node> for ScrollPoolBuilder -+where -+ Node: FullNodeTypes<Types: NodeTypes<ChainSpec = ScrollChainSpec, Primitives = EthPrimitives>>, -+{ -+ type Pool = NoopTransactionPool; ++ let tx = T::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; + -+ async fn build_pool(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> { -+ Ok(NoopTransactionPool::default()) -+ } ++ if !buf.is_empty() { ++ return Err(alloy_rlp::Error::UnexpectedLength); ++ } ++ ++ Ok(tx) ++ }) ++ .collect::<Result<Vec<_>, _>>()?; ++ ++ // Reuse the encoded bytes for root calculation ++ let transactions_root = ++ proofs::ordered_trie_root_with_encoder(&payload.transactions, |item, buf| { ++ buf.put_slice(item) ++ }); ++ ++ let header = Header { ++ parent_hash: payload.parent_hash, ++ beneficiary: payload.fee_recipient, ++ state_root: payload.state_root, ++ transactions_root, ++ receipts_root: payload.receipts_root, ++ withdrawals_root: None, ++ logs_bloom: payload.logs_bloom, ++ number: payload.block_number, ++ gas_limit: payload.gas_limit, ++ gas_used: payload.gas_used, ++ timestamp: payload.timestamp, ++ mix_hash: payload.prev_randao, ++ base_fee_per_gas: basefee, ++ blob_gas_used: None, ++ excess_blob_gas: None, ++ parent_beacon_block_root: None, ++ requests_hash: None, ++ extra_data: payload.extra_data, ++ // Defaults ++ ommers_hash: EMPTY_OMMER_ROOT_HASH, ++ difficulty: Default::default(), ++ nonce: Default::default(), ++ }; ++ ++ Ok(Block { header, body: BlockBody { transactions, ..Default::default() } }) ++} ++ ++/// Tries to convert an [`ExecutionPayloadV2`] to [`Block`]. ++fn try_payload_v2_to_block<T: Decodable2718>( ++ payload: ExecutionPayloadV2, ++ chainspec: Arc<ScrollChainSpec>, ++) -> Result<Block<T>, PayloadError> { ++ // this performs the same conversion as the underlying V1 payload, but calculates the ++ // withdrawals root and adds withdrawals ++ let mut base_sealed_block = try_payload_v1_to_block(payload.payload_inner, chainspec)?; ++ let withdrawals_root = proofs::calculate_withdrawals_root(&payload.withdrawals); ++ base_sealed_block.body.withdrawals = Some(payload.withdrawals.into()); ++ base_sealed_block.header.withdrawals_root = Some(withdrawals_root); ++ Ok(base_sealed_block) ++} ++ ++/// Tries to convert an [`ExecutionPayloadV3`] to [`Block`]. ++fn try_payload_v3_to_block<T: Decodable2718>( ++ payload: ExecutionPayloadV3, ++ chainspec: Arc<ScrollChainSpec>, ++) -> Result<Block<T>, PayloadError> { ++ // this performs the same conversion as the underlying V2 payload, but inserts the blob gas ++ // used and excess blob gas ++ let mut base_block = try_payload_v2_to_block(payload.payload_inner, chainspec)?; ++ ++ base_block.header.blob_gas_used = Some(payload.blob_gas_used); ++ base_block.header.excess_blob_gas = Some(payload.excess_blob_gas); ++ ++ Ok(base_block) +}
@@ -6351,9 +7641,9 @@
@@ -6368,7 +7658,7 @@
@@ -6378,46 +7668,87 @@
-
+18
+
+59
-0
-
diff --git reth/crates/scroll/node/src/lib.rs scroll-reth/crates/scroll/node/src/lib.rs +
diff --git reth/crates/scroll/evm/Cargo.toml scroll-reth/crates/scroll/evm/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..0894d68b12ae736c03af6e0b8edd623e45fb4aea +index 0000000000000000000000000000000000000000..7ae825f842bdf4eb67d6fedf72110eb7d2391dff --- /dev/null -+++ scroll-reth/crates/scroll/node/src/lib.rs -@@ -0,0 +1,18 @@ -+//! Node specific implementations for Scroll. -+#![cfg(all(feature = "scroll", not(feature = "optimism")))] ++++ scroll-reth/crates/scroll/evm/Cargo.toml +@@ -0,0 +1,59 @@ ++[package] ++name = "reth-scroll-evm" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+mod builder; -+pub use builder::{ -+ consensus::ScrollConsensusBuilder, engine::ScrollEngineValidatorBuilder, -+ execution::ScrollExecutorBuilder, network::ScrollNetworkBuilder, payload::ScrollPayloadBuilder, -+ pool::ScrollPoolBuilder, -+}; ++[lints] ++workspace = true + -+mod addons; -+pub use addons::ScrollAddOns; ++[dependencies] ++# reth ++reth-chainspec.workspace = true ++reth-consensus.workspace = true ++reth-ethereum-consensus.workspace = true ++reth-evm.workspace = true ++reth-primitives.workspace = true ++reth-primitives-traits.workspace = true ++reth-revm.workspace = true + -+mod storage; -+pub use storage::ScrollStorage; ++# revm ++revm = { workspace = true, features = ["optional_no_base_fee"] } + -+mod node; -+pub use node::ScrollNode;
++# scroll ++reth-scroll-chainspec.workspace = true ++reth-scroll-forks.workspace = true ++ ++# alloy ++alloy-consensus.workspace = true ++alloy-eips.workspace = true ++ ++# scroll ++reth-scroll-consensus.workspace = true ++scroll-alloy-consensus.workspace = true ++ ++# misc ++auto_impl.workspace = true ++derive_more.workspace = true ++thiserror.workspace = true ++tracing.workspace = true ++ ++[dev-dependencies] ++eyre.workspace = true ++ ++[features] ++optimism = [ ++ "reth-primitives/optimism", ++ "revm/optimism" ++] ++scroll = [ ++ "reth-evm/scroll", ++ "reth-primitives/scroll", ++ "reth-revm/scroll", ++ "reth-scroll-consensus/scroll", ++ "revm/scroll" ++]
@@ -6432,7 +7763,7 @@
@@ -6442,225 +7773,344 @@
-
+90
+
+327
-0
-
diff --git reth/crates/scroll/node/src/node.rs scroll-reth/crates/scroll/node/src/node.rs +
diff --git reth/crates/scroll/evm/src/config.rs scroll-reth/crates/scroll/evm/src/config.rs new file mode 100644 -index 0000000000000000000000000000000000000000..95f9e57109016f2b4edc9845f6aa2992ee0baf49 +index 0000000000000000000000000000000000000000..84fcc924995d068977269101e4146b2958e50d64 --- /dev/null -+++ scroll-reth/crates/scroll/node/src/node.rs -@@ -0,0 +1,90 @@ -+//! Node specific implementations for Scroll. -+#![cfg(all(feature = "scroll", not(feature = "optimism")))] -+ -+use crate::{ -+ ScrollAddOns, ScrollConsensusBuilder, ScrollExecutorBuilder, ScrollNetworkBuilder, -+ ScrollPayloadBuilder, ScrollPoolBuilder, ScrollStorage, -+}; -+use reth_ethereum_engine_primitives::{ -+ EthBuiltPayload, EthEngineTypes, EthPayloadAttributes, EthPayloadBuilderAttributes, -+}; -+use reth_node_builder::{ -+ components::ComponentsBuilder, -+ node::{FullNodeTypes, NodeTypes, NodeTypesWithEngine}, -+ Node, NodeAdapter, NodeComponentsBuilder, PayloadTypes, -+}; -+use reth_primitives::EthPrimitives; ++++ scroll-reth/crates/scroll/evm/src/config.rs +@@ -0,0 +1,327 @@ ++use reth_chainspec::{ChainSpecProvider, Head}; ++use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; ++use reth_primitives::{transaction::FillTxEnv, TransactionSigned}; ++use reth_revm::{inspector_handle_register, Database, Evm, GetInspector}; +use reth_scroll_chainspec::ScrollChainSpec; -+use reth_trie_db::MerklePatriciaTrie; ++use reth_scroll_forks::ScrollHardfork; ++use revm::{ ++ precompile::{Address, Bytes}, ++ primitives::{ ++ AnalysisKind, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, HandlerCfg, SpecId, TxEnv, U256, ++ }, ++ EvmBuilder, ++}; ++use std::{convert::Infallible, sync::Arc}; + -+/// The Scroll node implementation. ++/// Scroll EVM configuration. +#[derive(Clone, Debug)] -+pub struct ScrollNode; ++pub struct ScrollEvmConfig { ++ /// The chain spec for Scroll. ++ chain_spec: Arc<ScrollChainSpec>, ++} + -+impl ScrollNode { -+ /// Returns a [`ComponentsBuilder`] configured for a regular Ethereum node. -+ pub fn components<Node>() -> ComponentsBuilder< -+ Node, -+ ScrollPoolBuilder, -+ ScrollPayloadBuilder, -+ ScrollNetworkBuilder, -+ ScrollExecutorBuilder, -+ ScrollConsensusBuilder, -+ > -+ where -+ Node: FullNodeTypes< -+ Types: NodeTypes<ChainSpec = ScrollChainSpec, Primitives = EthPrimitives>, -+ >, -+ <Node::Types as NodeTypesWithEngine>::Engine: PayloadTypes< -+ BuiltPayload = EthBuiltPayload, -+ PayloadAttributes = EthPayloadAttributes, -+ PayloadBuilderAttributes = EthPayloadBuilderAttributes, -+ >, -+ { -+ ComponentsBuilder::default() -+ .node_types::<Node>() -+ .pool(ScrollPoolBuilder) -+ .payload(ScrollPayloadBuilder) -+ .network(ScrollNetworkBuilder) -+ .executor(ScrollExecutorBuilder) -+ .consensus(ScrollConsensusBuilder) ++impl ScrollEvmConfig { ++ /// Returns a new instance of [`ScrollEvmConfig`]. ++ pub const fn new(chain_spec: Arc<ScrollChainSpec>) -> Self { ++ Self { chain_spec } + } -+} + -+impl<N> Node<N> for ScrollNode -+where -+ N: FullNodeTypes<Types = Self>, -+{ -+ type ComponentsBuilder = ComponentsBuilder< -+ N, -+ ScrollPoolBuilder, -+ ScrollPayloadBuilder, -+ ScrollNetworkBuilder, -+ ScrollExecutorBuilder, -+ ScrollConsensusBuilder, -+ >; ++ /// Returns the spec id at the given head. ++ pub fn spec_id_at_head(&self, head: &Head) -> SpecId { ++ let chain_spec = &self.chain_spec; ++ if chain_spec.fork(ScrollHardfork::Curie).active_at_head(head) { ++ SpecId::CURIE ++ } else if chain_spec.fork(ScrollHardfork::Bernoulli).active_at_head(head) { ++ SpecId::BERNOULLI ++ } else { ++ SpecId::PRE_BERNOULLI ++ } ++ } ++} + -+ type AddOns = ScrollAddOns< -+ NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>, -+ >; ++impl ConfigureEvm for ScrollEvmConfig { ++ type DefaultExternalContext<'a> = (); + -+ fn components_builder(&self) -> Self::ComponentsBuilder { -+ Self::components() ++ fn evm<DB: Database>(&self, db: DB) -> Evm<'_, Self::DefaultExternalContext<'_>, DB> { ++ EvmBuilder::default().with_db(db).scroll().build() + } + -+ fn add_ons(&self) -> Self::AddOns { -+ ScrollAddOns::default() ++ fn evm_with_inspector<DB, I>(&self, db: DB, inspector: I) -> Evm<'_, I, DB> ++ where ++ DB: Database, ++ I: GetInspector<DB>, ++ { ++ EvmBuilder::default() ++ .with_db(db) ++ .with_external_context(inspector) ++ .scroll() ++ .append_handler_register(inspector_handle_register) ++ .build() + } -+} + -+impl NodeTypesWithEngine for ScrollNode { -+ type Engine = EthEngineTypes; ++ fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a> {} +} + -+impl NodeTypes for ScrollNode { -+ // TODO(scroll): update to scroll primitives when we introduce the revm SDK pattern. -+ type Primitives = EthPrimitives; -+ type ChainSpec = ScrollChainSpec; -+ type StateCommitment = MerklePatriciaTrie; -+ type Storage = ScrollStorage; -+}
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+72
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/node/src/storage.rs scroll-reth/crates/scroll/node/src/storage.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..b1cf8916af6170ba0c95de56f7f068f3c9aefd13 ---- /dev/null -+++ scroll-reth/crates/scroll/node/src/storage.rs -@@ -0,0 +1,72 @@ -+use reth_db::transaction::{DbTx, DbTxMut}; -+use reth_ethereum_forks::EthereumHardforks; -+use reth_node_types::NodeTypes; -+use reth_primitives::{BlockBody, EthPrimitives}; -+use reth_provider::{ -+ providers::ChainStorage, BlockBodyReader, BlockBodyWriter, ChainSpecProvider, DBProvider, -+ EthStorage, ProviderResult, ReadBodyInput, StorageLocation, -+}; -+ -+/// Storage implementation for Scroll. -+#[derive(Debug, Default, Clone)] -+pub struct ScrollStorage(EthStorage); ++impl ConfigureEvmEnv for ScrollEvmConfig { ++ type Transaction = TransactionSigned; ++ type Header = alloy_consensus::Header; ++ type Error = Infallible; + -+impl<Provider> BlockBodyWriter<Provider, BlockBody> for ScrollStorage -+where -+ Provider: DBProvider<Tx: DbTxMut>, -+{ -+ fn write_block_bodies( -+ &self, -+ provider: &Provider, -+ bodies: Vec<(u64, Option<BlockBody>)>, -+ write_to: StorageLocation, -+ ) -> ProviderResult<()> { -+ self.0.write_block_bodies(provider, bodies, write_to) ++ fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &Self::Transaction, sender: Address) { ++ transaction.fill_tx_env(tx_env, sender); + } + -+ fn remove_block_bodies_above( ++ fn fill_tx_env_system_contract_call( + &self, -+ provider: &Provider, -+ block: alloy_primitives::BlockNumber, -+ remove_from: StorageLocation, -+ ) -> ProviderResult<()> { -+ self.0.remove_block_bodies_above(provider, block, remove_from) ++ _env: &mut Env, ++ _caller: Address, ++ _contract: Address, ++ _data: Bytes, ++ ) { ++ /* noop */ + } -+} + -+impl<Provider> BlockBodyReader<Provider> for ScrollStorage -+where -+ Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>, -+{ -+ type Block = reth_primitives::Block; ++ fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { ++ let spec_id = self.spec_id_at_head(&Head { ++ number: header.number, ++ timestamp: header.timestamp, ++ difficulty: header.difficulty, ++ ..Default::default() ++ }); + -+ fn read_block_bodies( -+ &self, -+ provider: &Provider, -+ inputs: Vec<ReadBodyInput<'_, Self::Block>>, -+ ) -> ProviderResult<Vec<BlockBody>> { -+ self.0.read_block_bodies(provider, inputs) ++ cfg_env.handler_cfg.spec_id = spec_id; ++ cfg_env.handler_cfg.is_scroll = true; ++ ++ cfg_env.chain_id = self.chain_spec.chain().id(); ++ cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; + } -+} + -+impl ChainStorage<EthPrimitives> for ScrollStorage { -+ fn reader<TX, Types>( -+ &self, -+ ) -> impl reth_provider::ChainStorageReader<reth_provider::DatabaseProvider<TX, Types>, EthPrimitives> -+ where -+ TX: DbTx + 'static, -+ Types: reth_provider::providers::NodeTypesForProvider<Primitives = EthPrimitives>, -+ { -+ self ++ fn fill_block_env(&self, block_env: &mut BlockEnv, header: &Self::Header, spec_id: SpecId) { ++ block_env.number = U256::from(header.number); ++ ++ if let Some(vault_address) = self.chain_spec.config.fee_vault_address { ++ block_env.coinbase = vault_address; ++ } else { ++ block_env.coinbase = header.beneficiary; ++ } ++ ++ block_env.timestamp = U256::from(header.timestamp); ++ if spec_id >= SpecId::MERGE { ++ block_env.prevrandao = Some(header.mix_hash); ++ block_env.difficulty = U256::ZERO; ++ } else { ++ block_env.difficulty = header.difficulty; ++ block_env.prevrandao = None; ++ } ++ block_env.basefee = U256::from(header.base_fee_per_gas.unwrap_or_default()); ++ block_env.gas_limit = U256::from(header.gas_limit); ++ block_env.blob_excess_gas_and_price = None; + } + -+ fn writer<TX, Types>( ++ fn next_cfg_and_block_env( + &self, -+ ) -> impl reth_provider::ChainStorageWriter<reth_provider::DatabaseProvider<TX, Types>, EthPrimitives> -+ where -+ TX: DbTxMut + DbTx + 'static, -+ Types: NodeTypes<Primitives = EthPrimitives>, -+ { -+ self ++ parent: &Self::Header, ++ attributes: NextBlockEnvAttributes, ++ ) -> Result<EvmEnv, Self::Error> { ++ // configure evm env based on parent block ++ let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id()); ++ ++ // fetch spec id from next head number and timestamp ++ let spec_id = self.spec_id_at_head(&Head { ++ number: parent.number + 1, ++ timestamp: attributes.timestamp, ++ ..Default::default() ++ }); ++ ++ let coinbase = if let Some(vault_address) = self.chain_spec.config.fee_vault_address { ++ vault_address ++ } else { ++ attributes.suggested_fee_recipient ++ }; ++ ++ let block_env = BlockEnv { ++ number: U256::from(parent.number + 1), ++ coinbase, ++ timestamp: U256::from(attributes.timestamp), ++ difficulty: U256::ZERO, ++ prevrandao: Some(attributes.prev_randao), ++ gas_limit: U256::from(parent.gas_limit), ++ // calculate basefee based on parent block's gas usage ++ // TODO(scroll): update with correct block fee calculation for block building. ++ basefee: U256::from(parent.base_fee_per_gas.unwrap_or_default()), ++ blob_excess_gas_and_price: None, ++ }; ++ ++ let cfg_with_handler_cfg = CfgEnvWithHandlerCfg { ++ cfg_env: cfg, ++ handler_cfg: HandlerCfg { spec_id, is_scroll: true }, ++ }; ++ ++ Ok((cfg_with_handler_cfg, block_env).into()) ++ } ++} ++ ++impl ChainSpecProvider for ScrollEvmConfig { ++ type ChainSpec = ScrollChainSpec; ++ ++ fn chain_spec(&self) -> Arc<Self::ChainSpec> { ++ self.chain_spec.clone() ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use alloy_consensus::Header; ++ use reth_chainspec::NamedChain::Scroll; ++ use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; ++ use revm::primitives::{SpecId, B256}; ++ ++ #[test] ++ fn test_spec_at_head() { ++ let config = ScrollEvmConfig::new( ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), ++ ); ++ ++ // prepare all fork heads ++ let curie_head = &Head { number: 7096836, ..Default::default() }; ++ let bernouilli_head = &Head { number: 5220340, ..Default::default() }; ++ let pre_bernouilli_head = &Head { number: 0, ..Default::default() }; ++ ++ // check correct spec id ++ assert_eq!(config.spec_id_at_head(curie_head), SpecId::CURIE); ++ assert_eq!(config.spec_id_at_head(bernouilli_head), SpecId::BERNOULLI); ++ assert_eq!(config.spec_id_at_head(pre_bernouilli_head), SpecId::PRE_BERNOULLI); ++ } ++ ++ #[test] ++ fn test_fill_cfg_env() { ++ let config = ScrollEvmConfig::new( ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), ++ ); ++ ++ // curie ++ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); ++ let curie_header = Header { number: 7096836, ..Default::default() }; ++ ++ // fill cfg env ++ config.fill_cfg_env(&mut cfg_env, &curie_header); ++ ++ // check correct cfg env ++ assert_eq!(cfg_env.chain_id, Scroll as u64); ++ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); ++ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::CURIE); ++ assert!(cfg_env.handler_cfg.is_scroll); ++ ++ // bernoulli ++ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); ++ let bernouilli_header = Header { number: 5220340, ..Default::default() }; ++ ++ // fill cfg env ++ config.fill_cfg_env(&mut cfg_env, &bernouilli_header); ++ ++ // check correct cfg env ++ assert_eq!(cfg_env.chain_id, Scroll as u64); ++ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); ++ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::BERNOULLI); ++ assert!(cfg_env.handler_cfg.is_scroll); ++ ++ // pre-bernoulli ++ let mut cfg_env = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); ++ let pre_bernouilli_header = Header { number: 0, ..Default::default() }; ++ ++ // fill cfg env ++ config.fill_cfg_env(&mut cfg_env, &pre_bernouilli_header); ++ ++ // check correct cfg env ++ assert_eq!(cfg_env.chain_id, Scroll as u64); ++ assert_eq!(cfg_env.perf_analyse_created_bytecodes, AnalysisKind::Analyse); ++ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::PRE_BERNOULLI); ++ assert!(cfg_env.handler_cfg.is_scroll); ++ } ++ ++ #[test] ++ fn test_fill_block_env() { ++ let config = ScrollEvmConfig::new( ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), ++ ); ++ let mut block_env = BlockEnv::default(); ++ ++ // curie header ++ let header = Header { ++ number: 7096836, ++ beneficiary: Address::random(), ++ timestamp: 1719994277, ++ mix_hash: B256::random(), ++ base_fee_per_gas: Some(155157341), ++ gas_limit: 10000000, ++ ..Default::default() ++ }; ++ ++ // fill block env ++ config.fill_block_env(&mut block_env, &header, SpecId::MERGE); ++ ++ // verify block env correctly updated ++ let expected = BlockEnv { ++ number: U256::from(header.number), ++ coinbase: config.chain_spec.config.fee_vault_address.unwrap(), ++ timestamp: U256::from(header.timestamp), ++ prevrandao: Some(header.mix_hash), ++ difficulty: U256::ZERO, ++ basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()), ++ gas_limit: U256::from(header.gas_limit), ++ blob_excess_gas_and_price: None, ++ }; ++ assert_eq!(block_env, expected) ++ } ++ ++ #[test] ++ fn test_next_cfg_and_block_env() -> eyre::Result<()> { ++ let config = ScrollEvmConfig::new( ++ ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), ++ ); ++ ++ // pre curie header ++ let header = Header { ++ number: 7096835, ++ beneficiary: Address::random(), ++ timestamp: 1719994274, ++ mix_hash: B256::random(), ++ base_fee_per_gas: None, ++ gas_limit: 10000000, ++ ..Default::default() ++ }; ++ ++ // curie block attributes ++ let attributes = NextBlockEnvAttributes { ++ timestamp: 1719994277, ++ suggested_fee_recipient: Address::random(), ++ prev_randao: B256::random(), ++ gas_limit: 10000000, ++ }; ++ ++ // get next cfg env and block env ++ let (cfg_env, block_env) = config.next_cfg_and_block_env(&header, attributes)?.into(); ++ ++ // verify cfg env ++ assert_eq!(cfg_env.chain_id, Scroll as u64); ++ assert_eq!(cfg_env.handler_cfg.spec_id, SpecId::CURIE); ++ assert!(cfg_env.handler_cfg.is_scroll); ++ ++ // verify block env ++ let expected = BlockEnv { ++ number: U256::from(header.number + 1), ++ coinbase: config.chain_spec.config.fee_vault_address.unwrap(), ++ timestamp: U256::from(attributes.timestamp), ++ prevrandao: Some(attributes.prev_randao), ++ difficulty: U256::ZERO, ++ // TODO(scroll): this shouldn't be 0 at curie fork ++ basefee: U256::ZERO, ++ gas_limit: U256::from(header.gas_limit), ++ blob_excess_gas_and_price: None, ++ }; ++ assert_eq!(block_env, expected); ++ ++ Ok(()) + } +}
@@ -6669,9 +8119,9 @@
@@ -6686,7 +8136,7 @@
@@ -6696,111 +8146,57 @@
-
+83
+
+29
-0
-
diff --git reth/crates/scroll/primitives/Cargo.toml scroll-reth/crates/scroll/primitives/Cargo.toml +
diff --git reth/crates/scroll/evm/src/error.rs scroll-reth/crates/scroll/evm/src/error.rs new file mode 100644 -index 0000000000000000000000000000000000000000..e9fbc837e787c8b68595d65575d6cdad15951ac2 +index 0000000000000000000000000000000000000000..3e2f469a4a6e6f9fad8b3d4b2d04800be47ab990 --- /dev/null -+++ scroll-reth/crates/scroll/primitives/Cargo.toml -@@ -0,0 +1,83 @@ -+[package] -+name = "reth-scroll-primitives" -+version.workspace = true -+edition.workspace = true -+rust-version.workspace = true -+license.workspace = true -+homepage.workspace = true -+repository.workspace = true -+exclude.workspace = true -+ -+[lints] -+workspace = true ++++ scroll-reth/crates/scroll/evm/src/error.rs +@@ -0,0 +1,29 @@ ++use derive_more::{Display, From}; ++use reth_evm::execute::BlockExecutionError; + -+[dependencies] -+# alloy -+alloy-consensus.workspace = true -+alloy-eips.workspace = true -+alloy-primitives.workspace = true -+alloy-rlp.workspace = true -+alloy-serde.workspace = true ++/// Execution error for Scroll. ++#[derive(thiserror::Error, Display, From, Debug)] ++pub enum ScrollBlockExecutionError { ++ /// Error occurred at fork transition. ++ #[display("failed to apply fork: {_0}")] ++ Fork(ForkError), ++} + -+# reth -+reth-codecs = { workspace = true, optional = true } -+reth-codecs-derive = { workspace = true, optional = true } ++impl From<ScrollBlockExecutionError> for BlockExecutionError { ++ fn from(value: ScrollBlockExecutionError) -> Self { ++ Self::other(value) ++ } ++} + -+# required by reth-codecs -+bytes.workspace = true -+modular-bitfield = { workspace = true, optional = true } -+serde.workspace = true -+ -+# misc -+arbitrary = { workspace = true, features = ["derive"], optional = true } -+ -+[dev-dependencies] -+alloy-primitives = { workspace = true, features = ["arbitrary"] } -+arbitrary = { workspace = true, features = ["derive"] } -+bincode.workspace = true -+rand.workspace = true -+reth-codecs = { workspace = true, features = ["test-utils"] } -+ -+proptest-arbitrary-interop.workspace = true -+proptest.workspace = true -+test-fuzz.workspace = true ++/// Scroll fork error. ++#[derive(Debug, Display)] ++pub enum ForkError { ++ /// Error occurred at Curie fork. ++ Curie(String), ++} + -+[features] -+default = [ -+ "reth-codec", -+ "std" -+] -+std = [ -+ "serde/std", -+ "alloy-primitives/std", -+ "reth-codecs/std", -+ "alloy-consensus/std", -+ "alloy-eips/std", -+ "alloy-rlp/std", -+ "alloy-serde/std", -+ "bytes/std", -+ "proptest/std", -+ "rand/std" -+] -+arbitrary = [ -+ "dep:arbitrary", -+ "alloy-primitives/arbitrary", -+ "alloy-consensus/arbitrary", -+ "alloy-eips/arbitrary", -+ "alloy-serde/arbitrary", -+ "reth-codecs/arbitrary" -+] -+reth-codec = [ -+ "dep:reth-codecs", -+ "dep:reth-codecs-derive", -+ "modular-bitfield", -+ "std" -+] -+serde = [ -+ "alloy-primitives/serde", -+ "alloy-consensus/serde", -+ "alloy-eips/serde", -+ "bytes/serde", -+ "rand/serde", -+ "reth-codecs/serde" -+]
++impl From<ForkError> for BlockExecutionError { ++ fn from(value: ForkError) -> Self { ++ ScrollBlockExecutionError::Fork(value).into() ++ } ++}
@@ -6815,7 +8211,7 @@
@@ -6825,374 +8221,648 @@
-
+344
+
+618
-0
-
diff --git reth/crates/scroll/primitives/src/l1_transaction.rs scroll-reth/crates/scroll/primitives/src/l1_transaction.rs +
diff --git reth/crates/scroll/evm/src/execute.rs scroll-reth/crates/scroll/evm/src/execute.rs new file mode 100644 -index 0000000000000000000000000000000000000000..aa380d50e0ef5ee040a99b7696fedef77937466d +index 0000000000000000000000000000000000000000..bba7aab238af589ef9001e57e2588f962a1b9ad5 --- /dev/null -+++ scroll-reth/crates/scroll/primitives/src/l1_transaction.rs -@@ -0,0 +1,344 @@ -+//! Scroll L1 message transaction ++++ scroll-reth/crates/scroll/evm/src/execute.rs +@@ -0,0 +1,618 @@ ++//! Implementation of the [`BlockExecutionStrategy`] for Scroll. + -+use alloy_consensus::{Transaction, Typed2718}; -+use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}; -+use alloy_primitives::{ -+ private::alloy_rlp::{Encodable, Header}, -+ Address, Bytes, ChainId, PrimitiveSignature as Signature, TxKind, B256, U256, ++use crate::{ForkError, ScrollEvmConfig}; ++use alloy_consensus::{Header, Transaction}; ++use alloy_eips::eip7685::Requests; ++use reth_chainspec::EthereumHardforks; ++use reth_consensus::ConsensusError; ++use reth_evm::{ ++ execute::{ ++ BasicBlockExecutorProvider, BlockExecutionError, BlockExecutionStrategy, ++ BlockExecutionStrategyFactory, BlockValidationError, ExecuteOutput, ProviderError, ++ }, ++ ConfigureEvm, ConfigureEvmEnv, ++}; ++use reth_primitives::{ ++ gas_spent_by_transactions, BlockWithSenders, EthPrimitives, GotExpected, ++ InvalidTransactionError, Receipt, TransactionSigned, TxType, ++}; ++use reth_revm::primitives::{CfgEnvWithHandlerCfg, U256}; ++use reth_scroll_chainspec::{ChainSpecProvider, ScrollChainSpec}; ++use reth_scroll_consensus::{apply_curie_hard_fork, L1_GAS_PRICE_ORACLE_ADDRESS}; ++use reth_scroll_forks::{ScrollHardfork, ScrollHardforks}; ++use revm::{ ++ primitives::{BlockEnv, EnvWithHandlerCfg, ExecutionResult, ResultAndState}, ++ Database, DatabaseCommit, State, ++}; ++use std::{ ++ fmt::{Debug, Display}, ++ sync::Arc, +}; -+use alloy_rlp::Decodable; -+use bytes::BufMut; -+use serde::{Deserialize, Serialize}; -+#[cfg(any(test, feature = "reth-codec"))] -+use {reth_codecs::Compact, reth_codecs_derive::add_arbitrary_tests}; -+ -+/// L1 message transaction type id, 0x7e in hex. -+pub const L1_MESSAGE_TRANSACTION_TYPE: u8 = 126; + -+/// A message transaction sent from the settlement layer to the L2 for execution. -+/// -+/// The signature of the L1 message is already verified on the L1 and as such doesn't contain -+/// a signature field. Gas for the transaction execution on Scroll is already paid for on the L1. -+/// -+/// # Bincode compatibility -+/// -+/// `bincode` crate doesn't work with optionally serializable serde fields and some of the execution -+/// types require optional serialization for RPC compatibility. Since `TxL1Message` doesn't -+/// contain optionally serializable fields, no `bincode` compatible bridge implementation is -+/// required. -+#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -+#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] -+#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))] -+#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -+#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] -+#[cfg_attr(any(test, feature = "reth-codec"), add_arbitrary_tests(compact, rlp))] -+pub struct TxL1Message { -+ /// The queue index of the message in the L1 contract queue. -+ #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] -+ pub queue_index: u64, -+ /// The gas limit for the transaction. Gas is paid for when message is sent from the L1. -+ #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity", rename = "gas"))] -+ pub gas_limit: u64, -+ /// The destination for the transaction. `Address` is used in place of `TxKind` since contract -+ /// creations aren't allowed via L1 message transactions. -+ pub to: Address, -+ /// The value sent. -+ pub value: U256, -+ /// The L1 sender of the transaction. -+ pub sender: Address, -+ /// The input of the transaction. -+ pub input: Bytes, ++/// The Scroll block execution strategy. ++#[derive(Debug)] ++pub struct ScrollExecutionStrategy<DB, EvmConfig> { ++ /// Evm configuration. ++ evm_config: EvmConfig, ++ /// Current state for the execution. ++ state: State<DB>, +} + -+impl TxL1Message { -+ /// Returns an empty signature for the [`TxL1Message`], which don't include a signature. -+ pub fn signature() -> Signature { -+ Signature::new(U256::ZERO, U256::ZERO, false) -+ } -+ -+ /// Returns the destination of the transaction as a [`TxKind`]. -+ pub const fn to(&self) -> TxKind { -+ TxKind::Call(self.to) ++impl<DB, EvmConfig> ScrollExecutionStrategy<DB, EvmConfig> { ++ /// Returns an instance of [`ScrollExecutionStrategy`]. ++ pub const fn new(evm_config: EvmConfig, state: State<DB>) -> Self { ++ Self { evm_config, state } + } ++} + -+ /// Decodes the inner [`TxL1Message`] fields from RLP bytes. ++impl<DB, EvmConfig> ScrollExecutionStrategy<DB, EvmConfig> ++where ++ EvmConfig: ConfigureEvmEnv<Header = Header>, ++{ ++ /// Configures a new evm configuration and block environment for the given block. + /// -+ /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following -+ /// RLP fields in the following order: ++ /// # Caution + /// -+ /// - `queue_index` -+ /// - `gas_limit` -+ /// - `to` -+ /// - `value` -+ /// - `input` -+ /// - `sender` -+ pub fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { -+ Ok(Self { -+ queue_index: Decodable::decode(buf)?, -+ gas_limit: Decodable::decode(buf)?, -+ to: Decodable::decode(buf)?, -+ value: Decodable::decode(buf)?, -+ input: Decodable::decode(buf)?, -+ sender: Decodable::decode(buf)?, -+ }) ++ /// This does not initialize the tx environment. ++ fn evm_env_for_block(&self, header: &Header) -> EnvWithHandlerCfg { ++ let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); ++ let mut block_env = BlockEnv::default(); ++ self.evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, header); ++ ++ EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) + } ++} + -+ /// Decodes the transaction from RLP bytes. -+ pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { -+ let header = Header::decode(buf)?; -+ if !header.list { -+ return Err(alloy_rlp::Error::UnexpectedString); -+ } -+ let remaining = buf.len(); ++impl<DB, EvmConfig> BlockExecutionStrategy for ScrollExecutionStrategy<DB, EvmConfig> ++where ++ DB: Database<Error: Into<ProviderError> + Display>, ++ EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned> ++ + ChainSpecProvider<ChainSpec = ScrollChainSpec>, ++{ ++ type DB = DB; ++ type Primitives = EthPrimitives; ++ type Error = BlockExecutionError; + -+ let this = Self::rlp_decode_fields(buf)?; ++ fn apply_pre_execution_changes(&mut self, block: &BlockWithSenders) -> Result<(), Self::Error> { ++ // set state clear flag if the block is after the Spurious Dragon hardfork. ++ let state_clear_flag = ++ (*self.evm_config.chain_spec()).is_spurious_dragon_active_at_block(block.header.number); ++ self.state.set_state_clear_flag(state_clear_flag); + -+ if buf.len() + header.payload_length != remaining { -+ return Err(alloy_rlp::Error::ListLengthMismatch { -+ expected: header.payload_length, -+ got: remaining - buf.len(), -+ }); ++ // load the l1 gas oracle contract in cache ++ let _ = ++ self.state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS).map_err(|err| err.into())?; ++ ++ if self ++ .evm_config ++ .chain_spec() ++ .fork(ScrollHardfork::Curie) ++ .transitions_at_block(block.number) ++ { ++ if let Err(err) = apply_curie_hard_fork(&mut self.state) { ++ tracing::debug!(%err, "failed to apply curie hardfork"); ++ return Err(ForkError::Curie(err.to_string()).into()); ++ }; + } + -+ Ok(this) ++ Ok(()) + } + -+ /// Outputs the length of the transaction's fields, without a RLP header. -+ fn rlp_encoded_fields_length(&self) -> usize { -+ self.queue_index.length() + -+ self.gas_limit.length() + -+ self.to.length() + -+ self.value.length() + -+ self.input.0.length() + -+ self.sender.length() -+ } ++ fn execute_transactions( ++ &mut self, ++ block: &BlockWithSenders, ++ ) -> Result<ExecuteOutput, Self::Error> { ++ let env = self.evm_env_for_block(&block.header); ++ let mut evm = self.evm_config.evm_with_env(&mut self.state, env); + -+ /// Encode the fields of the transaction without a RLP header. -+ /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/l1_message_tx.go#L12-L19> -+ fn rlp_encode_fields(&self, out: &mut dyn BufMut) { -+ self.queue_index.encode(out); -+ self.gas_limit.encode(out); -+ self.to.encode(out); -+ self.value.encode(out); -+ self.input.encode(out); -+ self.sender.encode(out); -+ } ++ let mut cumulative_gas_used = 0; ++ let mut receipts = Vec::with_capacity(block.body.transactions.len()); ++ let chain_spec = self.evm_config.chain_spec(); + -+ pub(crate) const fn tx_type(&self) -> u8 { -+ L1_MESSAGE_TRANSACTION_TYPE -+ } -+ -+ /// Create a RLP header for the transaction. -+ fn rlp_header(&self) -> Header { -+ Header { list: true, payload_length: self.rlp_encoded_fields_length() } -+ } -+ -+ /// RLP encodes the transaction. -+ pub fn rlp_encode(&self, out: &mut dyn BufMut) { -+ self.rlp_header().encode(out); -+ self.rlp_encode_fields(out); -+ } -+ -+ /// Get the length of the transaction when RLP encoded. -+ pub fn rlp_encoded_length(&self) -> usize { -+ self.rlp_header().length_with_payload() -+ } ++ for (sender, transaction) in block.transactions_with_sender() { ++ // The sum of the transaction’s gas limit and the gas utilized in this block prior, ++ // must be no greater than the block’s gasLimit. ++ let block_available_gas = block.header.gas_limit - cumulative_gas_used; ++ if transaction.gas_limit() > block_available_gas { ++ return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { ++ transaction_gas_limit: transaction.gas_limit(), ++ block_available_gas, ++ } ++ .into()) ++ } + -+ /// Get the length of the transaction when EIP-2718 encoded. This is the -+ /// 1 byte type flag + the length of the RLP encoded transaction. -+ pub fn eip2718_encoded_length(&self) -> usize { -+ self.rlp_encoded_length() + 1 -+ } ++ // verify the transaction type is accepted by the current fork. ++ match transaction.tx_type() { ++ TxType::Eip2930 if !chain_spec.is_curie_active_at_block(block.number) => { ++ return Err(ConsensusError::InvalidTransaction( ++ InvalidTransactionError::Eip2930Disabled, ++ ) ++ .into()) ++ } ++ TxType::Eip1559 if !chain_spec.is_curie_active_at_block(block.number) => { ++ return Err(ConsensusError::InvalidTransaction( ++ InvalidTransactionError::Eip1559Disabled, ++ ) ++ .into()) ++ } ++ TxType::Eip4844 => { ++ return Err(ConsensusError::InvalidTransaction( ++ InvalidTransactionError::Eip4844Disabled, ++ ) ++ .into()) ++ } ++ TxType::Eip7702 => { ++ return Err(ConsensusError::InvalidTransaction( ++ InvalidTransactionError::Eip7702Disabled, ++ ) ++ .into()) ++ } ++ _ => (), ++ }; + -+ /// Calculates the in-memory size of the [`TxL1Message`] transaction. -+ #[inline] -+ pub fn size(&self) -> usize { -+ size_of::<u64>() + // queue_index -+ size_of::<u64>() + // gas_limit -+ size_of::<Address>() + // to -+ size_of::<U256>() + // value -+ self.input.len() + // input -+ size_of::<Address>() // sender -+ } -+} ++ self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); ++ if transaction.is_l1_message() { ++ evm.context.evm.env.cfg.disable_base_fee = true; // disable base fee for l1 msg ++ } + -+impl Typed2718 for TxL1Message { -+ fn ty(&self) -> u8 { -+ self.tx_type() -+ } -+} ++ // execute the transaction and commit the result to the database ++ let ResultAndState { result, state } = ++ evm.transact().map_err(|err| BlockValidationError::EVM { ++ hash: transaction.recalculate_hash(), ++ error: Box::new(err.map_db_err(|e| e.into())), ++ })?; ++ evm.db_mut().commit(state); + -+impl Encodable2718 for TxL1Message { -+ fn type_flag(&self) -> Option<u8> { -+ Some(self.tx_type()) -+ } ++ let l1_fee = if transaction.is_l1_message() { ++ // l1 messages do not get any gas refunded ++ if let ExecutionResult::Success { gas_refunded, .. } = result { ++ cumulative_gas_used += gas_refunded ++ } + -+ fn encode_2718_len(&self) -> usize { -+ self.eip2718_encoded_length() -+ } ++ U256::ZERO ++ } else { ++ // compute l1 fee for all non-l1 transaction ++ let l1_block_info = ++ evm.context.evm.inner.l1_block_info.as_ref().expect("l1_block_info loaded"); ++ let transaction_rlp_bytes = ++ evm.context.evm.env.tx.scroll.rlp_bytes.as_ref().expect("rlp_bytes loaded"); ++ l1_block_info.calculate_tx_l1_cost(transaction_rlp_bytes, evm.handler.cfg.spec_id) ++ }; + -+ fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { -+ out.put_u8(self.tx_type()); -+ self.rlp_encode(out); -+ } -+} ++ cumulative_gas_used += result.gas_used(); + -+impl Decodable2718 for TxL1Message { -+ fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { -+ if ty != L1_MESSAGE_TRANSACTION_TYPE { -+ return Err(Eip2718Error::UnexpectedType(ty)); ++ receipts.push(Receipt { ++ tx_type: transaction.tx_type(), ++ success: result.is_success(), ++ cumulative_gas_used, ++ logs: result.into_logs(), ++ l1_fee, ++ }) + } -+ let tx = Self::rlp_decode(buf)?; -+ Ok(tx) -+ } + -+ fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { -+ let tx = Self::decode(buf)?; -+ Ok(tx) ++ Ok(ExecuteOutput { receipts, gas_used: cumulative_gas_used }) + } -+} + -+impl Encodable for TxL1Message { -+ fn encode(&self, out: &mut dyn BufMut) { -+ self.rlp_encode(out) ++ fn apply_post_execution_changes( ++ &mut self, ++ _block: &BlockWithSenders, ++ _receipts: &[Receipt], ++ ) -> Result<Requests, Self::Error> { ++ Ok(Default::default()) + } + -+ fn length(&self) -> usize { -+ self.rlp_encoded_length() ++ fn state_ref(&self) -> &State<DB> { ++ &self.state + } -+} + -+impl Decodable for TxL1Message { -+ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { -+ Self::rlp_decode(buf) ++ fn state_mut(&mut self) -> &mut State<DB> { ++ &mut self.state + } -+} + -+impl Transaction for TxL1Message { -+ fn chain_id(&self) -> Option<ChainId> { -+ None -+ } ++ fn validate_block_post_execution( ++ &self, ++ block: &BlockWithSenders, ++ receipts: &[Receipt], ++ _requests: &Requests, ++ ) -> Result<(), ConsensusError> { ++ // verify the block gas used ++ let cumulative_gas_used = receipts.last().map(|r| r.cumulative_gas_used).unwrap_or(0); ++ if block.gas_used != cumulative_gas_used { ++ return Err(ConsensusError::BlockGasUsed { ++ gas: GotExpected { got: cumulative_gas_used, expected: block.gas_used }, ++ gas_spent_by_tx: gas_spent_by_transactions(receipts), ++ }); ++ } + -+ fn nonce(&self) -> u64 { -+ 0u64 -+ } ++ // verify the receipts logs bloom and root ++ if self.evm_config.chain_spec().is_byzantium_active_at_block(block.header.number) { ++ if let Err(error) = reth_ethereum_consensus::verify_receipts( ++ block.header.receipts_root, ++ block.header.logs_bloom, ++ receipts, ++ ) { ++ tracing::debug!( ++ %error, ++ ?receipts, ++ header_receipt_root = ?block.header.receipts_root, ++ header_bloom = ?block.header.logs_bloom, ++ "failed to verify receipts" ++ ); ++ return Err(error); ++ } ++ } + -+ fn gas_limit(&self) -> u64 { -+ self.gas_limit ++ Ok(()) + } ++} + -+ fn gas_price(&self) -> Option<u128> { -+ None -+ } ++/// The factory for a [`ScrollExecutionStrategy`]. ++#[derive(Clone, Debug)] ++pub struct ScrollExecutionStrategyFactory<EvmConfig = ScrollEvmConfig> { ++ /// The Evm configuration for the [`ScrollExecutionStrategy`]. ++ evm_config: EvmConfig, ++} + -+ fn max_fee_per_gas(&self) -> u128 { -+ 0 ++impl ScrollExecutionStrategyFactory { ++ /// Returns a new instance of the [`ScrollExecutionStrategyFactory`]. ++ pub const fn new(chain_spec: Arc<ScrollChainSpec>) -> Self { ++ let evm_config = ScrollEvmConfig::new(chain_spec); ++ Self { evm_config } + } + -+ fn max_priority_fee_per_gas(&self) -> Option<u128> { -+ None ++ /// Returns the EVM configuration for the strategy factory. ++ pub fn evm_config(&self) -> ScrollEvmConfig { ++ self.evm_config.clone() + } ++} + -+ fn max_fee_per_blob_gas(&self) -> Option<u128> { -+ None -+ } ++impl<EvmConfig> BlockExecutionStrategyFactory for ScrollExecutionStrategyFactory<EvmConfig> ++where ++ EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned> ++ + ChainSpecProvider<ChainSpec = ScrollChainSpec>, ++{ ++ type Primitives = EthPrimitives; ++ type Strategy<DB: Database<Error: Into<ProviderError> + Display>> = ++ ScrollExecutionStrategy<DB, EvmConfig>; + -+ fn priority_fee_or_price(&self) -> u128 { -+ 0 ++ /// Creates a strategy using the give database. ++ fn create_strategy<DB>(&self, db: DB) -> Self::Strategy<DB> ++ where ++ DB: Database<Error: Into<ProviderError> + Display>, ++ { ++ let state = ++ State::builder().with_database(db).without_state_clear().with_bundle_update().build(); ++ ScrollExecutionStrategy::new(self.evm_config.clone(), state) + } ++} + -+ fn effective_gas_price(&self, _base_fee: Option<u64>) -> u128 { -+ 0 -+ } ++/// Helper type with backwards compatible methods to obtain Scroll executor ++/// providers. ++#[derive(Debug)] ++pub struct ScrollExecutorProvider; + -+ fn is_dynamic_fee(&self) -> bool { -+ false ++impl ScrollExecutorProvider { ++ /// Creates a new default scroll executor provider. ++ pub const fn scroll( ++ chain_spec: Arc<ScrollChainSpec>, ++ ) -> BasicBlockExecutorProvider<ScrollExecutionStrategyFactory> { ++ BasicBlockExecutorProvider::new(ScrollExecutionStrategyFactory::new(chain_spec)) + } ++} + -+ fn is_create(&self) -> bool { -+ false -+ } ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use crate::{ScrollEvmConfig, ScrollExecutionStrategy, ScrollExecutionStrategyFactory}; ++ use reth_chainspec::MIN_TRANSACTION_GAS; ++ use reth_evm::execute::ExecuteOutput; ++ use reth_primitives::{Block, BlockBody, BlockWithSenders, Receipt, TransactionSigned, TxType}; ++ use reth_primitives_traits::transaction::signed::SignedTransaction; ++ use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; ++ use reth_scroll_consensus::{ ++ BLOB_SCALAR_SLOT, COMMIT_SCALAR_SLOT, CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, ++ CURIE_L1_GAS_PRICE_ORACLE_STORAGE, IS_CURIE_SLOT, L1_BASE_FEE_SLOT, L1_BLOB_BASE_FEE_SLOT, ++ L1_GAS_PRICE_ORACLE_ADDRESS, OVER_HEAD_SLOT, SCALAR_SLOT, ++ }; ++ use revm::{ ++ db::{ ++ states::{bundle_state::BundleRetention, StorageSlot}, ++ EmptyDBTyped, ++ }, ++ primitives::{AccountInfo, Address, Bytecode, TxKind, B256, U256}, ++ }; + -+ fn kind(&self) -> TxKind { -+ self.to() -+ } ++ const BLOCK_GAS_LIMIT: u64 = 10_000_000; ++ const SCROLL_CHAIN_ID: u64 = 534352; ++ const NOT_CURIE_BLOCK_NUMBER: u64 = 7096835; ++ const CURIE_BLOCK_NUMBER: u64 = 7096837; + -+ fn value(&self) -> U256 { -+ self.value -+ } ++ fn strategy() -> ScrollExecutionStrategy<EmptyDBTyped<ProviderError>, ScrollEvmConfig> { ++ let chain_spec = ++ Arc::new(ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet())); ++ let factory = ScrollExecutionStrategyFactory::new(chain_spec); ++ let db = EmptyDBTyped::<ProviderError>::new(); + -+ fn input(&self) -> &Bytes { -+ &self.input ++ factory.create_strategy(db) + } + -+ fn access_list(&self) -> Option<&alloy_eips::eip2930::AccessList> { -+ None ++ fn block(number: u64, transactions: Vec<TransactionSigned>) -> BlockWithSenders { ++ let senders = transactions.iter().map(|t| t.recover_signer().unwrap()).collect(); ++ BlockWithSenders::new_unchecked( ++ Block { ++ header: Header { number, gas_limit: BLOCK_GAS_LIMIT, ..Default::default() }, ++ body: BlockBody { transactions, ..Default::default() }, ++ }, ++ senders, ++ ) + } + -+ fn blob_versioned_hashes(&self) -> Option<&[B256]> { -+ None -+ } ++ fn transaction(typ: TxType, gas_limit: u64) -> TransactionSigned { ++ let mut transaction = match typ { ++ TxType::Legacy => reth_primitives::Transaction::Legacy(alloy_consensus::TxLegacy { ++ to: TxKind::Call(Address::ZERO), ++ ..Default::default() ++ }), ++ TxType::Eip2930 => reth_primitives::Transaction::Eip2930(alloy_consensus::TxEip2930 { ++ to: TxKind::Call(Address::ZERO), ++ ..Default::default() ++ }), ++ TxType::Eip1559 => reth_primitives::Transaction::Eip1559(alloy_consensus::TxEip1559 { ++ to: TxKind::Call(Address::ZERO), ++ ..Default::default() ++ }), ++ TxType::Eip4844 => reth_primitives::Transaction::Eip4844(alloy_consensus::TxEip4844 { ++ to: Address::ZERO, ++ ..Default::default() ++ }), ++ TxType::Eip7702 => reth_primitives::Transaction::Eip7702(alloy_consensus::TxEip7702 { ++ to: Address::ZERO, ++ ..Default::default() ++ }), ++ TxType::L1Message => { ++ reth_primitives::Transaction::L1Message(scroll_alloy_consensus::TxL1Message { ++ sender: Address::random(), ++ to: Address::ZERO, ++ ..Default::default() ++ }) ++ } ++ }; ++ transaction.set_chain_id(SCROLL_CHAIN_ID); ++ transaction.set_gas_limit(gas_limit); + -+ fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { -+ None ++ let pk = B256::random(); ++ let signature = reth_primitives::sign_message(pk, transaction.signature_hash()).unwrap(); ++ TransactionSigned::new_unhashed(transaction, signature) + } -+} + -+/// Scroll specific transaction fields -+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -+#[serde(rename_all = "camelCase")] -+pub struct ScrollL1MessageTransactionFields { -+ /// The index of the transaction in the message queue. -+ #[serde(with = "alloy_serde::quantity")] -+ pub queue_index: u64, -+ /// The sender of the transaction on the L1. -+ pub sender: Address, -+} ++ fn execute_transactions( ++ tx_type: TxType, ++ block_number: u64, ++ expected_l1_fee: U256, ++ expected_error: Option<&str>, ++ ) -> eyre::Result<()> { ++ // init strategy ++ let mut strategy = strategy(); + -+#[cfg(test)] -+mod tests { -+ use super::TxL1Message; -+ use alloy_eips::eip2718::Encodable2718; -+ use alloy_primitives::{address, bytes, hex, Bytes, U256}; -+ use arbitrary::Arbitrary; -+ use bytes::BytesMut; -+ use rand::Rng; -+ use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; ++ // prepare transaction ++ let transaction = transaction(tx_type, MIN_TRANSACTION_GAS); ++ let block = block(block_number, vec![transaction]); + -+ #[test] -+ fn test_bincode_roundtrip() { -+ let mut bytes = [0u8; 1024]; -+ rand::thread_rng().fill(bytes.as_mut_slice()); -+ let tx = TxL1Message::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); ++ // determine l1 gas oracle storage ++ let l1_gas_oracle_storage = ++ if strategy.evm_config.chain_spec().is_curie_active_at_block(block_number) { ++ vec![ ++ (L1_BASE_FEE_SLOT, U256::from(1000)), ++ (OVER_HEAD_SLOT, U256::from(1000)), ++ (SCALAR_SLOT, U256::from(1000)), ++ (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), ++ (COMMIT_SCALAR_SLOT, U256::from(1000)), ++ (BLOB_SCALAR_SLOT, U256::from(10000)), ++ (IS_CURIE_SLOT, U256::from(1)), ++ ] ++ } else { ++ vec![ ++ (L1_BASE_FEE_SLOT, U256::from(1000)), ++ (OVER_HEAD_SLOT, U256::from(1000)), ++ (SCALAR_SLOT, U256::from(1000)), ++ ] ++ } ++ .into_iter() ++ .collect(); + -+ let encoded = bincode::serialize(&tx).unwrap(); -+ let decoded: TxL1Message = bincode::deserialize(&encoded).unwrap(); -+ assert_eq!(decoded, tx); ++ // load accounts in state ++ strategy.state.insert_account_with_storage( ++ L1_GAS_PRICE_ORACLE_ADDRESS, ++ Default::default(), ++ l1_gas_oracle_storage, ++ ); ++ for add in block.senders() { ++ strategy ++ .state ++ .insert_account(*add, AccountInfo { balance: U256::MAX, ..Default::default() }); ++ } ++ ++ // execute and verify output ++ let res = strategy.execute_transactions(&block); ++ ++ // check for error or execution outcome ++ if let Some(error) = expected_error { ++ assert_eq!(res.unwrap_err().to_string(), error); ++ } else { ++ let ExecuteOutput { receipts, .. } = res?; ++ let expected = vec![Receipt { ++ tx_type, ++ cumulative_gas_used: MIN_TRANSACTION_GAS, ++ success: true, ++ l1_fee: expected_l1_fee, ++ ..Default::default() ++ }]; ++ ++ assert_eq!(receipts, expected); ++ } ++ ++ Ok(()) + } + + #[test] -+ fn test_eip2718_encode() { -+ let tx = -+ TxL1Message { -+ queue_index: 947883, -+ gas_limit: 2000000, -+ to: address!("781e90f1c8fc4611c9b7497c3b47f99ef6969cbc"), -+ value: U256::ZERO, -+ sender: address!("7885bcbd5cecef1336b5300fb5186a12ddd8c478"), -+ input: bytes!("8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), -+ } -+ ; -+ let bytes = Bytes::from_static(&hex!("7ef9015a830e76ab831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478")); ++ fn test_apply_pre_execution_changes_curie_block() -> eyre::Result<()> { ++ // init strategy ++ let mut strategy = strategy(); + -+ let mut encoded = BytesMut::default(); -+ tx.encode_2718(&mut encoded); ++ // init curie transition block ++ let curie_block = block(7096836, vec![]); + -+ assert_eq!(encoded, bytes.as_ref()) ++ // apply pre execution change ++ strategy.apply_pre_execution_changes(&curie_block)?; ++ ++ // take bundle ++ let mut state = strategy.state; ++ state.merge_transitions(BundleRetention::Reverts); ++ let bundle = state.take_bundle(); ++ ++ // assert oracle contract contains updated bytecode ++ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); ++ let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); ++ assert_eq!(oracle.info.unwrap().code.unwrap(), bytecode); ++ ++ // check oracle contract contains storage changeset ++ let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); ++ storage.sort_by(|(a, _), (b, _)| a.cmp(b)); ++ for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { ++ assert_eq!(got.0, expected.0); ++ assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); ++ } ++ ++ Ok(()) + } + + #[test] -+ fn test_compaction_backwards_compatibility() { -+ assert_eq!(TxL1Message::bitflag_encoded_bytes(), 2); -+ validate_bitflag_backwards_compat!(TxL1Message, UnusedBits::NotZero); ++ fn test_apply_pre_execution_changes_not_curie_block() -> eyre::Result<()> { ++ // init strategy ++ let mut strategy = strategy(); ++ ++ // init block ++ let not_curie_block = block(7096837, vec![]); ++ ++ // apply pre execution change ++ strategy.apply_pre_execution_changes(&not_curie_block)?; ++ ++ // take bundle ++ let mut state = strategy.state; ++ state.merge_transitions(BundleRetention::Reverts); ++ let bundle = state.take_bundle(); ++ ++ // assert oracle contract is empty ++ let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS); ++ assert!(oracle.is_none()); ++ ++ Ok(()) + } -+}
-
- - -
-
- - - ++ ++ #[test] ++ fn test_execute_transactions_exceeds_block_gas_limit() -> eyre::Result<()> { ++ // init strategy ++ let mut strategy = strategy(); ++ ++ // prepare transaction exceeding block gas limit ++ let transaction = transaction(TxType::Legacy, BLOCK_GAS_LIMIT + 1); ++ let block = block(7096837, vec![transaction]); ++ ++ // execute and verify error ++ let res = strategy.execute_transactions(&block); ++ assert_eq!( ++ res.unwrap_err().to_string(), ++ "transaction gas limit 10000001 is more than blocks available gas 10000000" ++ ); ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip4844() -> eyre::Result<()> { ++ // Execute eip4844 transaction ++ execute_transactions( ++ TxType::Eip4844, ++ CURIE_BLOCK_NUMBER, ++ U256::ZERO, ++ Some("EIP-4844 transactions are disabled"), ++ )?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip7702() -> eyre::Result<()> { ++ // Execute eip7702 transaction ++ execute_transactions( ++ TxType::Eip7702, ++ CURIE_BLOCK_NUMBER, ++ U256::ZERO, ++ Some("EIP-7702 transactions are disabled"), ++ )?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_l1_message() -> eyre::Result<()> { ++ // Execute l1 message on curie block ++ let expected_l1_fee = U256::ZERO; ++ execute_transactions(TxType::L1Message, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_legacy_curie_fork() -> eyre::Result<()> { ++ // Execute legacy transaction on curie block ++ let expected_l1_fee = U256::from(10); ++ execute_transactions(TxType::Legacy, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_legacy_not_curie_fork() -> eyre::Result<()> { ++ // Execute legacy before curie block ++ let expected_l1_fee = U256::from(2); ++ execute_transactions(TxType::Legacy, NOT_CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip2930_curie_fork() -> eyre::Result<()> { ++ // Execute eip2930 transaction on curie block ++ let expected_l1_fee = U256::from(10); ++ execute_transactions(TxType::Eip2930, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip2930_not_curie_fork() -> eyre::Result<()> { ++ // Execute eip2930 transaction before curie block ++ execute_transactions( ++ TxType::Eip2930, ++ NOT_CURIE_BLOCK_NUMBER, ++ U256::ZERO, ++ Some("EIP-2930 transactions are disabled"), ++ )?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip1559_curie_fork() -> eyre::Result<()> { ++ // Execute eip1559 transaction on curie block ++ let expected_l1_fee = U256::from(10); ++ execute_transactions(TxType::Eip1559, CURIE_BLOCK_NUMBER, expected_l1_fee, None)?; ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_execute_transactions_eip_not_curie_fork() -> eyre::Result<()> { ++ // Execute eip1559 transaction before curie block ++ execute_transactions( ++ TxType::Eip1559, ++ NOT_CURIE_BLOCK_NUMBER, ++ U256::ZERO, ++ Some("EIP-1559 transactions are disabled"), ++ )?; ++ Ok(()) ++ } ++}
+
+ + +
+
@@ -7205,7 +8875,7 @@
@@ -7215,37 +8885,41 @@
-
+9
+
+13
-0
-
diff --git reth/crates/scroll/primitives/src/lib.rs scroll-reth/crates/scroll/primitives/src/lib.rs +
diff --git reth/crates/scroll/evm/src/lib.rs scroll-reth/crates/scroll/evm/src/lib.rs new file mode 100644 -index 0000000000000000000000000000000000000000..9d2439c91d5a7d572fe4fb65a2c0d5790229fc70 +index 0000000000000000000000000000000000000000..020de5994bfd346ff776df6c3ac6eb293f2f82ab --- /dev/null -+++ scroll-reth/crates/scroll/primitives/src/lib.rs -@@ -0,0 +1,9 @@ -+//! Primitive types for the Scroll extension of `Reth`. ++++ scroll-reth/crates/scroll/evm/src/lib.rs +@@ -0,0 +1,13 @@ ++//! Scroll evm execution implementation. ++#![cfg(all(feature = "scroll", not(feature = "optimism")))] + -+#![warn(unused_crate_dependencies)] -+#![cfg_attr(not(feature = "std"), no_std)] ++pub use config::ScrollEvmConfig; ++mod config; + -+pub use l1_transaction::{ -+ ScrollL1MessageTransactionFields, TxL1Message, L1_MESSAGE_TRANSACTION_TYPE, ++pub use error::{ForkError, ScrollBlockExecutionError}; ++mod error; ++ ++pub use execute::{ ++ ScrollExecutionStrategy, ScrollExecutionStrategyFactory, ScrollExecutorProvider, +}; -+pub mod l1_transaction;
++mod execute;
@@ -7260,7 +8934,7 @@
@@ -7270,59 +8944,70 @@
-
+29
+
+42
-0
-
diff --git reth/crates/scroll/trie/Cargo.toml scroll-reth/crates/scroll/trie/Cargo.toml +
diff --git reth/crates/scroll/hardforks/Cargo.toml scroll-reth/crates/scroll/hardforks/Cargo.toml new file mode 100644 -index 0000000000000000000000000000000000000000..e644b31108f58a2dd8f825a15a88384c4108a310 +index 0000000000000000000000000000000000000000..09372f5ba5cb190f19cb30126f78e31c0ee811ee --- /dev/null -+++ scroll-reth/crates/scroll/trie/Cargo.toml -@@ -0,0 +1,30 @@ ++++ scroll-reth/crates/scroll/hardforks/Cargo.toml +@@ -0,0 +1,42 @@ +[package] -+name = "reth-scroll-trie" ++name = "reth-scroll-forks" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true -+exclude.workspace = true ++description = "Scroll hardforks used in reth" + +[lints] +workspace = true + +[dependencies] +# reth -+reth-trie.workspace = true ++reth-ethereum-forks.workspace = true + -+# alloy ++# ethereum ++alloy-chains.workspace = true +alloy-primitives.workspace = true -+alloy-trie = { workspace = true, features = ["serde"] } + -+# misc -+poseidon-bn254 = { workspace = true, features = ["bn254"] } -+tracing.workspace = true ++# io ++serde = { workspace = true, optional = true } + -+[dev-dependencies] -+proptest-arbitrary-interop.workspace = true ++# misc ++once_cell.workspace = true + +[features] -+scroll = ["reth-trie/scroll"] -\ No newline at end of file
++default = ["std"] ++std = [ ++ "alloy-primitives/std", ++ "once_cell/std", ++ "serde?/std", ++ "alloy-chains/std", ++ "reth-ethereum-forks/std" ++] ++serde = [ ++ "dep:serde", ++ "alloy-chains/serde", ++ "alloy-primitives/serde", ++ "reth-ethereum-forks/serde" ++]
@@ -7337,7 +9022,7 @@
@@ -7347,383 +9032,372 @@
-
+4
+
+342
-0
-
diff --git reth/crates/scroll/trie/README.md scroll-reth/crates/scroll/trie/README.md +
diff --git reth/crates/scroll/hardforks/docs/hardforks.md scroll-reth/crates/scroll/hardforks/docs/hardforks.md new file mode 100644 -index 0000000000000000000000000000000000000000..8645b5efadd58f51d8d04146614741788a95febd +index 0000000000000000000000000000000000000000..fb7d068fedbdeaca76ad435a112f1c60ec7dded2 --- /dev/null -+++ scroll-reth/crates/scroll/trie/README.md -@@ -0,0 +1,5 @@ -+# scroll-trie ++++ scroll-reth/crates/scroll/hardforks/docs/hardforks.md +@@ -0,0 +1,343 @@ ++--- ++section: technology ++date: Last Modified ++title: "Scroll Upgrades" ++lang: "en" ++permalink: "technology/overview/scroll-upgrades" ++--- + -+Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for prefix-sorted bits. ++As the team continues to progress on Scroll's roadmap, we will be upgrading the Scroll network to include new features and improvements. + -+Please see the specification of zktrie [here](assets/zktrie.md). -\ No newline at end of file
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- - (binary file) - -
-
-
diff --git reth/crates/scroll/trie/assets/arch.png scroll-reth/crates/scroll/trie/assets/arch.png -new file mode 100644 -index 0000000000000000000000000000000000000000..aca3f8d0c5ce4eedb5d40b8dedb39d226e57efbe -Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/arch.png differ
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- - (binary file) - -
-
-
diff --git reth/crates/scroll/trie/assets/deletion.png scroll-reth/crates/scroll/trie/assets/deletion.png -new file mode 100644 -index 0000000000000000000000000000000000000000..6c699226e0996a30199a3ff4d2441700390cb8e2 -Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/deletion.png differ
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- - (binary file) - -
-
-
diff --git reth/crates/scroll/trie/assets/insertion.png scroll-reth/crates/scroll/trie/assets/insertion.png -new file mode 100644 -index 0000000000000000000000000000000000000000..942338a07f2ea46e33fc899fd222b6ae6671f5e4 -Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/insertion.png differ
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+186
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/trie/assets/zktrie.md scroll-reth/crates/scroll/trie/assets/zktrie.md -new file mode 100644 -index 0000000000000000000000000000000000000000..b86e1a016ab8bae8e13f7e1b9f7fe1d29bfc7711 ---- /dev/null -+++ scroll-reth/crates/scroll/trie/assets/zktrie.md -@@ -0,0 +1,186 @@ -+# zkTrie Spec ++The following contracts are used to initiate upgrades and execute upgrades after the two-week timelock period: + -+## 1. Tree Structure ++| Contract | Network | Address | ++| ------------------------ | ------- | - | ++| L1 Scroll Multisig | Ethereum| [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://etherscan.io/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe)| ++| L1 Timelock | Ethereum| [`0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C`](https://etherscan.io/address/0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C)| ++| L2 Scroll Multisig | Scroll| [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://scrollscan.com/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe)| ++| L2 Timelock | Scroll | [`0xf6069DB81239E5194bb53f83aF564d282357bc99`](https://scrollscan.com/address/0xf6069DB81239E5194bb53f83aF564d282357bc99)| + -+<figure> -+<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/arch.png" alt="zkTrie Structure" style="width:80%"> -+<figcaption align = "center"><b>Figure 1. zkTrie Structure</b></figcaption> -+</figure> ++You can join our [Telegram channel for technical updates](https://t.me/scroll_tech_updates), which includes future upgrade announcements and on-chain operation events. + -+In essence, zkTrie is a sparse binary Merkle Patricia Trie, depicted in the above figure. -+Before diving into the Sparse Binary Merkle Patricia Trie, let's briefly touch on Merkle Trees and Patricia Tries. -+* **Merkle Tree**: A Merkle Tree is a tree where each leaf node represents a hash of a data block, and each non-leaf node represents the hash of its child nodes. -+* **Patricia Trie**: A Patricia Trie is a type of radix tree or compressed trie used to store key-value pairs efficiently. It encodes the nodes with same prefix of the key to share the common path, where the path is determined by the value of the node key. ++## DarwinV2 Upgrade + -+As illustrated in the Figure 1, there are three types of nodes in the zkTrie. -+- Parent Node (type: 0): Given the zkTrie is a binary tree, a parent node has two children. -+- Leaf Node (type: 1): A leaf node holds the data of a key-value pair. -+- Empty Node (type: 2): An empty node is a special type of node, indicating the sub-trie that shares the same prefix is empty. ++### Overview + -+In zkTrie, we use Poseidon hash to compute the node hash because it's more friendly and efficient to prove it in the zk circuit. ++During internal testing, we identified that blocks may not always be compressible under certain conditions, which leads to unprovable chunks and batches. ++To fix this issue, a minor upgrade has been conducted so that uncompressed blobs will be enabled when this special case is detected. + -+## 2. Tree Construction ++### Timeline + -+Given a key-value pair, we first compute a *secure key* for the corresponding leaf node by hashing the original key (i.e., account address and storage key) using the Poseidon hash function. This can make the key uniformly distributed over the key space. The node key hashing method is described in the [Node Hashing](#3-node-hashing) section below. ++As this is a security related patch, we bypassed the 7-day timelock mechanism. + -+We then encode the path of a new leaf node by traversing the secure key from Least Significant Bit (LSB) to the Most Significant Bit (MSB). At each step, if the bit is 0, we will traverse to the left child; otherwise, traverse to the right child. ++- **Scroll Sepolia**: August 28th, 2024 ++- **Scroll Mainnet**: September 2nd, 2024 + -+We limit the maximum depth of zkTrie to 248, meaning that the tree will only traverse the lower 248 bits of the key. This is because the secure key space is a finite field used by Poseidon hash that doesn't occupy the full range of power of 2. This leads to an ambiguous bit representation of the key in a finite field and thus causes a soundness issue in the zk circuit. But if we truncate the key to lower 248 bits, the key space can fully occupy the range of $2^{248}$ and won't have the ambiguity in the bit representation. ++### Compatibility + -+We also apply an optimization to reduce the tree depth by contracting a subtree that has only one leaf node to a single leaf node. For example, in the Figure 1, the tree has three nodes in total, with keys `0100`, `0010`, and `1010`. Because there is only one node that has key with suffix `00`, the leaf node for key `0100` only traverses the suffix `00` and doesn't fully expand its key which would have resulted in depth of 4. ++#### Sequencer and Follower Nodes (l2geth) + -+## 3. Node Hashing ++The new node version is `v5.7.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.7.0) for more information. + -+In this section, we will describe how leaf secure key and node merkle hash are computed. We use Poseidon hash in both hashing computation, denoted as `h` in the doc below. ++This upgrade does not change Scroll's state transition function, so it is backward compatible. However, the format of the batch data committed to Ethereum changes. As a result, nodes that enabled rollup verification (`--rollup.verify`) must upgrade to be able to follow the chain. + -+<aside> -+💡 Note: We use `init_state = 0` in the Poseidon hash function for all use cases in the zkTrie. -+</aside> ++#### Dapps and Indexers + -+### 3.1 Empty Node ++A change has been implemented to Scroll Mainnet to enhance sequencer throughput, which adjusted the maximum reorg depth to 17 blocks. Previously, the system performed thorough capacity checks within the signer thread to determine whether transactions exceed the circuit limit. While this ensures that all transactions within a block are compliant, it also requires additional CPU resources. ++We introduced a new circuit capacity checking scheme on Mainnet. The sequencer thread now will continue to perform capacity checks, but in a more approximate manner. In parallel, 16 worker threads will accurately verify the capacity of previous blocks. As a result, a reorg could occur with a maximum depth of 17 blocks, although the likelihood of this is low. + -+The node hash of an empty node is 0. ++For indexers, the `BatchHeader` version has been upgraded to 4. This is backward compatible (the only exception is for developers decoding the blob payload, which has changed slightly). + -+### 3.2 Parent Node ++## Darwin Upgrade + -+The parent node hash is computed as follows ++### Overview + -+```go -+parentNodeHash = h(leftChildHash, rightChildHash) -+``` ++This upgrade will reduce gas fees by 34% by using a single aggregated proof for multiple batches, eliminating the need to finalize each batch individually. + -+### 3.3 Leaf Node ++- Darwin uses a new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). ++- In addition to the previous notions of `chunk` and `batch`, we have introduced a new concept called `bundle`. ++ - `Chunk`: A unit of zkEVM proving, consisting of a list of L2 blocks. ++ - `Batch`: A collection of chunks encoded into one EIP-4844 blob, serving as the unit of Data Availability. ++ - `Bundle`: A series of batches that functions as the unit of finalization. + -+The node hash of a leaf node is computed as follows ++ The main difference compared to Curie is that Scroll will now finalize multiple batches using a single aggregated bundle proof. + -+```go -+leafNodeHash = h(h(1, nodeKey), valueHash) -+``` ++- The on-chain bundle proof verifier uses a new public input layout. + -+The leaf node can hold two types of values: Ethereum accounts and storage key-value pairs. Next, we will describe how the node key and value hash are computed for each leaf node type. ++### Timeline + -+#### Ethereum Account Leaf Node -+For an Ethereum Account Leaf Node, it consists of an Ethereum address and a state account struct. The secure key is derived from the Ethereum address. -+``` -+address[0:20] (20 bytes in big-endian) -+valHi = address[0:16] -+valLo = address[16:20] * 2^96 (padding 12 bytes of 0 at the end) -+nodeKey = h(valHi, valLo) -+``` ++- **Scroll Sepolia** ++ - Network Upgrade: August 14th, 2024 ++- **Scroll Mainnet** ++ - Upgrade Initiation: August 5th, 2024 ++ - Timelock Completion & Upgrade: August 21st, 2024 + -+A state account struct in the Scroll consists of the following fields (`Fr` indicates the finite field used in Poseidon hash and is a 254-bit value) ++### Technical Details + -+- `Nonce`: u64 -+- `Balance`: u256, but treated as Fr -+- `StorageRoot`: Fr -+- `KeccakCodeHash`: u256 -+- `PoseidonCodeHash`: Fr -+- `CodeSize`: u64 ++#### Contract Changes + -+Before computing the value hash, the state account is first marshaled into a list of `u256` values. The marshaling scheme is ++*Note: Since the previous Curie upgrade, we have migrated the Scroll contracts to a new repo at [scroll-contracts](https://github.com/scroll-tech/scroll-contracts).* + -+``` -+(The following scheme assumes the big-endian encoding) -+[0:32] (bytes in big-endian) -+ [0:16] Reserved with all 0 -+ [16:24] CodeSize, uint64 in big-endian -+ [24:32] Nonce, uint64 in big-endian -+[32:64] Balance -+[64:96] StorageRoot -+[96:128] KeccakCodeHash -+[128:160] PoseidonCodehash -+(total 160 bytes) -+``` ++The code changes for this upgrade are implemented in [this PR](https://github.com/scroll-tech/scroll-contracts/pull/4). The key changes are as follows: + -+The marshal function also returns a `flag` value along with a vector of `u256` values. The `flag` is a bitmap that indicates whether a `u256` value CANNOT be treated as a field element (Fr). The `flag` value for state account is 8, shown below. ++- We have introduced a new `BatchHeaderV3Codec`. ++- We have changed how messages are processed in the `L1MessageQueue` contract. Prior to Darwin, we would process messages when a batch is finalized. After Darwin, most of this processing is moved to the commit step. ++- We have introduced a new public input format for bundle proofs. This is implemented in a new contract `IZkEvmVerifierV2`, which is in turn added to `MultipleVersionRollupVerifier`. ++- In the `ScrollChain` contract `version=3` batches will now be committed through a new function called `commitBatchWithBlobProof`. Bundles will be finalized using a new function called `finalizeBundleWithProof`. + -+``` -++--------------------+---------+------+----------+----------+ -+| 0 | 1 | 2 | 3 | 4 | (index) -++--------------------+---------+------+----------+----------+ -+| nonce||codesize||0 | balance | root | keccak | poseidon | (u256) -++--------------------+---------+------+----------+----------+ -+| 0 | 0 | 0 | 1 | 0 | (flag bits) -++--------------------+---------+------+----------+----------+ -+(LSB) (MSB) -+``` ++See the contract [release notes](https://github.com/scroll-tech/scroll-contracts/releases/tag/v1.0.0) for more information. + -+The value hash is computed in two steps: -+1. Convert the value that cannot be represented as a field element of the Poseidon hash to the field element. -+2. Combine field elements in a binary tree structure till the tree root is treated as the value hash. ++#### Node Changes + -+In the first step, when the bit in the `flag` is 1 indicating the `u256` value that cannot be treated as a field element, we split the value into a high-128bit value and a low-128bit value, and then pass them to a Poseidon hash to derive a field element value, `h(valueHi, valueLo)`. ++The new node version is `v5.6.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0) for more information. + -+Based on the definition, the value hash of the state account is computed as follows. ++The main changes are: + -+``` -+valueHash = -+h( -+ h( -+ h(nonce||codesize||0, balance), -+ h( -+ storageRoot, -+ h(keccakCodeHash[0:16], keccakCodeHash[16:32]), // convert Keccak codehash to a field element -+ ), -+ ), -+ poseidonCodeHash, -+) -+``` ++- Implementation of timestamp-based hard forks. ++- Processing V3 batch codec in rollup-verifier. + -+#### Storage Leaf Node ++#### zkEVM circuit changes + -+For a Storage Leaf Node, it is a key-value pair, which both are a `u256` value. The secure key of this leaf node is derived from the storage key. ++The new version of zkevm circuits is `v0.12.0`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.12.0) for the release log. + -+``` -+storageKey[0:32] (32 bytes in big-endian) -+valHi = storageKey[0:16] -+valLo = storageKey[16:32] -+nodeKey = h(valHi, valLo) -+``` ++We have introduced a `RecursionCircuit` that will bundle multiple sequential batches by recursively aggregating the SNARKs from the `BatchCircuit` (previously `AggregationCircuit`). The previously 5 layer proving system is now 7 layers as we introduce: + -+The storage value is a `u256` value. The `flag` for the storage value is 1, showed below. ++- 6th Layer (layer5): `RecursionCircuit` that recursively aggregates `BatchCircuit` SNARKs. ++- 7th Layer (layer6): `CompressionCircuit` that compresses the `RecursionCircuit` SNARK and produce an EVM-verifiable validity proof. + -+``` -++--------------+ -+| 0 | (index) -++--------------+ -+| storageValue | (u256) -++--------------+ -+| 1 | (flag bits) -++--------------+ -+``` ++The public input to the `BatchCircuit` is now context-aware of the “previous” `batch`, which allows us to implement the recursion scheme we adopted (described [here](https://scrollzkp.notion.site/Upgrade-4-Darwin-Documentation-05a3ecb59e9d4f288254701f8c888173) in-depth). + -+The value hash is computed as follows ++#### Audits + -+```go -+valueHash = h(storageValue[0:16], storageValue[16:32]) -+``` ++- TrailofBits: coming soon! + -+## 4. Tree Operations ++### Compatibility + -+### 4.1 Insertion ++#### Sequencer and Follower Nodes (l2geth) + -+<figure> -+<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/insertion.png" alt="zkTrie Structure" style="width:80%"> -+<figcaption align = "center"><b>Figure 2. Insert a new leaf node to zkTrie</b></figcaption> -+</figure> ++This upgrade does not alter the state transition function and is therefore backward-compatible. However, we strongly recommend node operators to upgrade to [v5.6.0](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0). + -+When we insert a new leaf node to the existing zkTrie, there could be two cases illustrated in the Figure 2. ++#### Dapps and Indexers + -+1. When traversing the path of the node key, it reaches an empty node (Figure 2(b)). In this case, we just need to replace this empty node by this leaf node and backtrace the path to update the merkle hash of parent nodes till the root. -+2. When traversing the path of the node key, it reaches another leaf node `b` (Figure 2(c)). In this case, we need to push down the existing leaf node `b` until the next bit in the node keys of two leaf nodes differs. At each push-down step, we need to insert an empty sibling node when necessary. When we reach the level where the bits differ, we then place two leaf nodes `b` and `c` as the left child and the right child depending on their bits. At last, we backtrace the path and update the merkle hash of all parent nodes. ++There are some major changes to how we commit and finalize batches after Darwin. + -+### 4.2 Deletion ++- Batches will be encoded using the new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). This version adds two new fields: ++ 1. `lastBlockTimestamp` (the timestamp of the last block in this batch). ++ 2. `blobDataProof` (the KZG challenge point evaluation proof). + -+<figure> -+<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/deletion.png" alt="zkTrie Structure" style="width:80%"> -+<figcaption align = "center"><b>Figure 3. Delete a leaf node from the zkTrie</b></figcaption> -+</figure> ++ This version removes `skippedL1MessageBitmap`. There will be no changes to how the blob data is encoded and compressed. ++- Batches will be committed using the `commitBatchWithBlobProof` function (instead of the previous `commitBatch`). + ++ New function signature: + -+The deletion of a leaf node is similar to the insertion. There are two cases illustrated in the Figure 3. ++ ```solidity ++ function commitBatchWithBlobProof(uint8 _version, bytes calldata _parentBatchHeader, bytes[] memory _chunks, bytes calldata _skippedL1MessageBitmap, bytes calldata _blobDataProof) ++ ``` + -+1. The sibling node of to-be-deleted leaf node is a parent node (Figure 3(b)). In this case, we can just replace the node `a` by an empty node and update the node hash of its ancestors till the root node. -+2. The node of to-be-deleted leaf node is a leaf node (Figure 3(c)). Similarly, we first replace the leaf node by an empty node and start to contract its sibling node upwards until its sibling node is not an empty node. For example, in Figure 3(c), we first replace the leaf node `b` by an empty node. During the contraction, since the sibling of node `c` now becomes an empty node, we move node `c` one level upward to replace its parent node. The new sibling of node `c`, node `e`, is still an empty node. So again we move node `c` upward. Now that the sibling of node `c` is node `a`, the deletion process is finished. ++- Batches will be finalized using the `finalizeBundleWithProof` function (instead of the previous `finalizeBatchWithProof4844`). + -+Note that the sibling of a leaf node in a valid zkTrie cannot be an empty node. Otherwise, we should always prune the subtree and move the leaf node upwards.
++ New function signature: ++ ++ ```solidity ++ function finalizeBundleWithProof(bytes calldata _batchHeader, bytes32 _postStateRoot, bytes32 _withdrawRoot, bytes calldata _aggrProof) ++ ``` ++ ++- The semantics of the `FinalizeBatch` event will change: It will now mean that all batches between the last finalized batch and the event’s `_batchIndex` have been finalized. The event’s stateRoot and withdrawRoot values belong to the last finalized batch in the bundle. Finalized roots for intermediate batches are no longer available. ++ ++ The semantics of the `CommitBatch` and `RevertBatch` events will not change. ++ ++Recommendations: ++ ++- Indexers that decode committed batch data should be adjusted to use the new codec and the new function signature. ++- Indexers that track batch finalization status should be adjusted to consider the new event semantics. ++ ++## Curie Upgrade ++ ++### Overview ++ ++This significant upgrade will reduce gas fees on the Scroll chain by 1.5x. Highlights include: ++ ++- Compresses the data stored in blobs using the [zstd](https://github.com/scroll-tech/da-codec/tree/main/libzstd) algorithm. This compression reduces the data size, allowing each blob to store more transactions, thereby reducing data availability cost per transaction. ++- Adopts a modified version of the EIP-1559 pricing model which is compatible with the EIP-1559 transaction interface, bringing beneftis such as more accurate transaction pricing and a more predictable and stable fee structure. ++- Support for new EVM opcodes `TLOAD`, `TSTORE`, and `MCOPY`. Users can safely use the latest Solidity compiler version `0.8.26` to build the contracts. ++- Introduces a dynamic block time. During periods of traffic congestion, a block will be packed when the number of transactions reaches the circuit limit instead of waiting for the 3-second interval. ++ ++### Timeline ++ ++- **Scroll Sepolia** ++ - Network Upgrade: June 17th, 2024 ++- **Scroll Mainnet** ++ - Upgrade Initiation: June 20th, 2024 ++ - Timelock Completion & Upgrade: July 3rd, 2024 ++ ++### Technical Details ++ ++#### Contract Changes ++ ++The code changes for this upgrade are documented in the following PRs: ++ ++- [Accept compressed batches](https://github.com/scroll-tech/scroll/pull/1317) ++- [Update `L1GasPriceOracle`](https://github.com/scroll-tech/scroll/pull/1343) ++- [Change `MAX_COMMIT_SCALAR` and `MAX_BLOB_SCALAR` to 1e18](https://github.com/scroll-tech/scroll/pull/1354) ++- [Remove batch index check when updating a verifier](https://github.com/scroll-tech/scroll/pull/1372) ++ ++The main changes are as follows: ++ ++- The rollup contract (`ScrollChain`) will now accept batches with both versions 1 and 2. [Version 1](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv1) is used for uncompressed blobs (pre-Curie), while [version 2](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv2) is used for compressed blobs (post-Curie). ++- The `L1GasPriceOracle` contract will be updated to change the data fee formula to account for blob DA, providing a more accurate estimation of DA costs: ++ - Original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar` ++ - New formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar` ++ ++#### Node Changes ++ ++The new node version is `v5.5.0`. See the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.5.0) for the list of changes. ++ ++#### zkEVM circuit changes ++ ++The new version of zkevm circuits is `v0.11.4`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.11.4) for the release log. ++ ++#### Audits ++ ++- TrailofBits: coming soon! ++- [Zellic](https://github.com/Zellic/publications/blob/master/Scroll%20zkEVM%20-%20Zellic%20Audit%20Report.pdf) ++ ++### Compatibility ++ ++#### Sequencer and Follower Nodes (l2geth) ++ ++This upgrade is a hard fork, introducing the `TLOAD`, `TSTORE`, and `MCOPY` opcodes. Operators running an `l2geth` node are required to upgrade before the hard fork block. For more information, see the [node release note](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.4.2). ++ ++#### Dapps and Indexers ++ ++For dApps, this upgrade is backward compatible. Developers should adjust the gas fee settings to incorporate the EIP-1559 pricing model. Note that dApps can no longer rely on the fixed 3-second block time in the application logic. ++ ++For indexers, the [data format](https://docs.scroll.io/en/technology/chain/rollup/#codec) remains the same. The will be however changes to the data content: ++ ++- The `version` field in `BatchHeader` will be changed to 2 since Curie block. ++- The data stored in blob will be compressed and can be decompressed by [zstd v1.5.6](https://github.com/facebook/zstd/releases/tag/v1.5.6). ++ ++## Bernoulli Upgrade ++ ++### Overview ++ ++This upgrade features a significant reduction in transaction costs by introducing support for EIP-4844 data blobs and supporting the SHA2-256 precompile. ++ ++### Timeline ++ ++- **Scroll Sepolia** ++ - Network Upgrade: April 15th, 2024 ++- **Scroll Mainnet** ++ - Upgrade Initiation: April 15th, 2024 ++ - Timelock Completion & Upgrade: April 29th, 2024 ++ ++### Technical Details ++ ++#### Contract changes ++ ++The contract changes for this upgrade are in [this PR](https://github.com/scroll-tech/scroll/pull/1179), along with the audit fixes [here](https://github.com/scroll-tech/scroll/pulls?q=is%3Apr+created%3A2024-04-10..2024-04-11+fix+in%3Atitle+label%3Abug). The main changes are as follows: ++ ++- `ScrollChain` now accepts batches with either calldata or blob encoding in `commitBatch`. ++- `ScrollChain` now supports finalizing blob-encoded batches through `finalizeBatchWithProof4844`. ++- `MultipleVersionRollupVerifier` can now manage different on-chain verifiers for each batch encoding version. ++ ++#### Node changes ++ ++The new node version is `v5.3.0`. See [here](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.3.0) for the release log. ++ ++#### zkEVM circuit changes ++ ++The new version of zkevm circuits is `v0.10.3`. See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.10.3) for the release log. ++ ++#### Audits ++ ++- [OpenZeppelin](https://blog.openzeppelin.com/scroll-eip-4844-support-audit) ++- [TrailofBits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-scroll-4844-blob-securityreview.pdf) ++ ++### Compatibility ++ ++#### Sequencer and follower nodes (l2geth) ++ ++This upgrade is a hard fork as it introduces the new blob data type and the SHA2-256 precompiled contract. Operators running an `l2geth` node are required to upgrade before the hard fork block. See the [node releases](https://github.com/scroll-tech/go-ethereum/releases) for more information. ++ ++#### Indexers and Bridges ++ ++This upgrade changes the format that Scroll uses to publish data to Ethereum. Projects that rely on this data should carefully review [the new data format](/en/technology/chain/rollup/#codec), and check whether their decoders need to be adjusted. A summary of the new format: ++ ++- The format of [`BlockContext`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L125) will not change. ++- `Chunks` will [no longer include](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L162) the L2 transaction data. This will instead be [stored in a blob](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L284) attached to the `commitBatch` transaction. ++- `BatchHeader` now contains one new field, [`BlobVersionedHash`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L405). ++ ++#### Provers ++ ++This upgrade involves a breaking change in [zkevm-circuits](https://github.com/scroll-tech/zkevm-circuits). Operators running a prover node are required to upgrade. ++ ++ ++## Bridge Upgrade ++ ++### Overview ++ ++To reduce bridging costs, we implemented several gas optimizations on our bridge and rollup contract suite. The optimization techniques used include the following: ++ ++- We will now use constants to store some companion contract addresses, instead of using storage variables. This is possible since these values should (almost) never change. With this change we can save on a few storage load operations. ++- We updated the intrinsic gas estimation in `L1MessageQueue` to use a simple upper bound instead of an exact calculation. The two results will be similar for most bridge transactions but the new implementation is significantly cheaper. ++- We merged two contracts `L1MessageQueue` and `L2GasPriceOracle` to save on call costs from one contract to the other. ++ ++### Timeline ++ ++- **Scroll Sepolia:** ++ - Network Upgrade: January 19, 2024 ++- **Scroll Mainnet:** ++ - Upgrade Initiation: February 7, 2024 ++ - Timelock Completion & Upgrade: February 21, 2024 ++ ++### Technical Details ++ ++#### Code Changes ++- [Bridge Cost Optimization](https://github.com/scroll-tech/scroll/pull/1011) ++- [Audit Fixes](https://github.com/scroll-tech/scroll/pulls?q=OZ+is%3Apr+created%3A2024-01-27..2024-02-10) ++- [Previously deployed version](https://github.com/scroll-tech/scroll/tree/ff380141a8cbcc214dc65f17ffa44faf4be646b6) (commit `ff380141a8cbcc214dc65f17ffa44faf4be646b6`) ++- [Version deployed](https://github.com/scroll-tech/scroll/tree/6030927680a92d0285c2c13e6bb27ed27d1f32d1) (commit `6030927680a92d0285c2c13e6bb27ed27d1f32d1`) ++ ++#### Audits ++ ++- [OpenZeppelin](https://blog.openzeppelin.com/scroll-bridge-gas-optimizations-audit) ++ ++#### List of Changes ++ ++**Changes to L1 contracts:** ++ ++- In `ScrollChain`, change `messageQueue` and `verifier` to `immutable`. ++- In `L1ScrollMessenger`, change `counterpart`, `rollup`, and `messageQueue` to `immutable`. ++- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. ++- Merge `L1MessageQueue` and `L2GasPriceOracle` into a single contract `L1MessageQueueWithGasPriceOracle` (deployed on the same address as the previous `L1MessageQueue`). In this contract, we also change `messenger` and `scrollChain` to `immutable`, and simplify `calculateIntrinsicGasFee`. ++ ++**Changes to L2 contracts:** ++ ++- In `L2ScrollMessenger`, change `counterpart` to `immutable`. ++- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. ++ ++**Contracts affected:** ++ ++- **L1:** `L1MessageQueue`, `L2GasPriceOracle`, `ScrollChain`, `L1WETHGateway`, `L1StandardERC20Gateway`, `L1GatewayRouter`, `L1ScrollMessenger`, `L1CustomERC20Gateway`, `L1ERC721Gateway`, `L1ERC1155Gateway`. ++- **L2:** `L2ScrollMessenger`, `L2WETHGateway`, `L2StandardERC20Gateway`, `L2GatewayRouter`, `L2CustomERC20Gateway`, `L2ERC721Gateway`, `L2ERC1155Gateway`. ++ ++#### Compatibility ++ ++##### Sequencer and follower nodes (l2geth) ++ ++Operators running an `l2geth` node do not need to upgrade. The changes in this upgrade will not affect `l2geth`. ++ ++##### Dapps and indexers ++ ++Dapps and indexers (and similar off-chain infrastructure) that query contracts or rely on contract interfaces would, in most cases, not need to be changed. The majority of the contract changes are internal and/or backward compatible. ++ ++If your application depends on [`L2GasPriceOracle`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) to monitor how Scroll keeps track of the L2 gas price on L1, from the upgrade block number you will need to start monitoring [`L1MessageQueueWithGasPriceOracle`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B). ++ ++The original gas price oracle contract will be deprecated: it will no longer be updated or used by the Scroll bridge. ++ ++- Ethereum: ++ - `L2GasPriceOracle`: [`0x987e300fDfb06093859358522a79098848C33852`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) ++ - `L1MessageQueueWithGasPriceOracle`: [`0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B) ++- Sepolia: ++ - `L2GasPriceOracle`: [`0x247969F4fad93a33d4826046bc3eAE0D36BdE548`](https://sepolia.etherscan.io/address/0x247969F4fad93a33d4826046bc3eAE0D36BdE548) ++ - `L1MessageQueueWithGasPriceOracle`: [`0xF0B2293F5D834eAe920c6974D50957A1732de763`](https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763) +\ No newline at end of file
@@ -7738,7 +9412,7 @@
@@ -7748,183 +9422,55 @@
-
+155
+
+27
-0
-
diff --git reth/crates/scroll/trie/src/branch.rs scroll-reth/crates/scroll/trie/src/branch.rs +
diff --git reth/crates/scroll/hardforks/src/dev.rs scroll-reth/crates/scroll/hardforks/src/dev.rs new file mode 100644 -index 0000000000000000000000000000000000000000..e6b433913d53e71f2db801ae4c89b3a6f975041c +index 0000000000000000000000000000000000000000..33fcd0d00776d70909085ffb29fb40ef9366324f --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/branch.rs -@@ -0,0 +1,155 @@ -+use super::{ -+ BRANCH_NODE_LBRB_DOMAIN, BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN, -+ BRANCH_NODE_LTRT_DOMAIN, -+}; -+use alloy_primitives::{hex, B256}; -+use alloy_trie::TrieMask; -+use core::{fmt, ops::Range, slice::Iter}; -+use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; -+ -+#[allow(unused_imports)] -+use alloc::vec::Vec; -+ -+/// The range of valid child indexes. -+pub(crate) const CHILD_INDEX_RANGE: Range<u8> = 0..2; -+ -+/// A trie mask to extract the two child indexes from a branch node. -+pub(crate) const CHILD_INDEX_MASK: TrieMask = TrieMask::new(0b11); -+ -+/// A reference to branch node and its state mask. -+/// NOTE: The stack may contain more items that specified in the state mask. -+#[derive(Clone)] -+pub(crate) struct BranchNodeRef<'a> { -+ /// Reference to the collection of hash nodes. -+ /// NOTE: The referenced stack might have more items than the number of children -+ /// for this node. We should only ever access items starting from -+ /// [`BranchNodeRef::first_child_index`]. -+ pub stack: &'a [B256], -+ /// Reference to bitmask indicating the presence of children at -+ /// the respective nibble positions. -+ pub state_mask: TrieMask, -+} -+ -+impl fmt::Debug for BranchNodeRef<'_> { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.debug_struct("BranchNodeRef") -+ .field("stack", &self.stack.iter().map(hex::encode).collect::<Vec<_>>()) -+ .field("state_mask", &self.state_mask) -+ .field("first_child_index", &self.first_child_index()) -+ .finish() -+ } -+} -+ -+impl<'a> BranchNodeRef<'a> { -+ /// Create a new branch node from the stack of nodes. -+ #[inline] -+ pub(crate) const fn new(stack: &'a [B256], state_mask: TrieMask) -> Self { -+ Self { stack, state_mask } -+ } -+ -+ /// Returns the stack index of the first child for this node. -+ /// -+ /// # Panics -+ /// -+ /// If the stack length is less than number of children specified in state mask. -+ /// Means that the node is in inconsistent state. -+ #[inline] -+ pub(crate) fn first_child_index(&self) -> usize { -+ self.stack -+ .len() -+ .checked_sub((self.state_mask & CHILD_INDEX_MASK).count_ones() as usize) -+ .expect("branch node stack is in inconsistent state") -+ } -+ -+ #[inline] -+ fn children(&self) -> impl Iterator<Item = (u8, Option<&B256>)> + '_ { -+ BranchChildrenIter::new(self) -+ } -+ -+ /// Given the hash mask of children, return an iterator over stack items -+ /// that match the mask. -+ #[inline] -+ pub(crate) fn child_hashes(&self, hash_mask: TrieMask) -> impl Iterator<Item = B256> + '_ { -+ self.children() -+ .filter_map(|(i, c)| c.map(|c| (i, c))) -+ .filter(move |(index, _)| hash_mask.is_bit_set(*index)) -+ .map(|(_, child)| B256::from_slice(&child[..])) -+ } -+ -+ pub(crate) fn hash(&self) -> B256 { -+ let mut children_iter = self.children(); -+ -+ let left_child = children_iter -+ .next() -+ .map(|(_, c)| *c.unwrap_or_default()) -+ .expect("branch node has two children"); -+ let left_child = -+ Fr::from_repr_vartime(left_child.0).expect("left child is a valid field element"); -+ let right_child = children_iter -+ .next() -+ .map(|(_, c)| *c.unwrap_or_default()) -+ .expect("branch node has two children"); -+ let right_child = -+ Fr::from_repr_vartime(right_child.0).expect("right child is a valid field element"); -+ -+ hash_with_domain(&[left_child, right_child], self.hashing_domain()).to_repr().into() -+ } -+ -+ fn hashing_domain(&self) -> Fr { -+ match *self.state_mask { -+ 0b1011 => BRANCH_NODE_LBRT_DOMAIN, -+ 0b1111 => BRANCH_NODE_LTRT_DOMAIN, -+ 0b0111 => BRANCH_NODE_LTRB_DOMAIN, -+ 0b0011 => BRANCH_NODE_LBRB_DOMAIN, -+ _ => unreachable!("invalid branch node state mask"), -+ } -+ } -+} -+ -+/// Iterator over branch node children. -+#[derive(Debug)] -+struct BranchChildrenIter<'a> { -+ range: Range<u8>, -+ state_mask: TrieMask, -+ stack_iter: Iter<'a, B256>, -+} -+ -+impl<'a> BranchChildrenIter<'a> { -+ /// Create new iterator over branch node children. -+ fn new(node: &BranchNodeRef<'a>) -> Self { -+ Self { -+ range: CHILD_INDEX_RANGE, -+ state_mask: node.state_mask, -+ stack_iter: node.stack[node.first_child_index()..].iter(), -+ } -+ } -+} -+ -+impl<'a> Iterator for BranchChildrenIter<'a> { -+ type Item = (u8, Option<&'a B256>); -+ -+ #[inline] -+ fn next(&mut self) -> Option<Self::Item> { -+ let i = self.range.next()?; -+ let value = self -+ .state_mask -+ .is_bit_set(i) -+ .then(|| unsafe { self.stack_iter.next().unwrap_unchecked() }); -+ Some((i, value)) -+ } -+ -+ #[inline] -+ fn size_hint(&self) -> (usize, Option<usize>) { -+ let len = self.len(); -+ (len, Some(len)) -+ } -+} ++++ scroll-reth/crates/scroll/hardforks/src/dev.rs +@@ -0,0 +1,27 @@ ++use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; + -+impl core::iter::FusedIterator for BranchChildrenIter<'_> {} ++#[cfg(not(feature = "std"))] ++use once_cell::sync::Lazy as LazyLock; ++#[cfg(feature = "std")] ++use std::sync::LazyLock; + -+impl ExactSizeIterator for BranchChildrenIter<'_> { -+ #[inline] -+ fn len(&self) -> usize { -+ self.range.len() -+ } -+}
++/// Dev hardforks ++pub static DEV_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| { ++ ChainHardforks::new(vec![ ++ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), ++ (crate::ScrollHardfork::Archimedes.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)), ++ (crate::ScrollHardfork::Bernoulli.boxed(), ForkCondition::Block(0)), ++ (crate::ScrollHardfork::Curie.boxed(), ForkCondition::Block(0)), ++ (crate::ScrollHardfork::Darwin.boxed(), ForkCondition::Timestamp(0)), ++ (crate::ScrollHardfork::DarwinV2.boxed(), ForkCondition::Timestamp(0)), ++ ]) ++});
@@ -7939,7 +9485,7 @@
@@ -7949,635 +9495,653 @@
-
+4
+
+264
-0
-
diff --git reth/crates/scroll/trie/src/constants.rs scroll-reth/crates/scroll/trie/src/constants.rs +
diff --git reth/crates/scroll/hardforks/src/hardfork.rs scroll-reth/crates/scroll/hardforks/src/hardfork.rs new file mode 100644 -index 0000000000000000000000000000000000000000..67bd414f835834ecca93f29497a396384c87f84e +index 0000000000000000000000000000000000000000..6a49e2fcffdf2da513b5ac5712a554ea6597c2d5 --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/constants.rs -@@ -0,0 +1,4 @@ -+use alloy_primitives::B256; ++++ scroll-reth/crates/scroll/hardforks/src/hardfork.rs +@@ -0,0 +1,264 @@ ++//! Hard forks of scroll protocol. + -+/// The root hash of an empty binary Merle Patricia trie. -+pub const EMPTY_ROOT_HASH: B256 = B256::ZERO;
-
- - -
- - -
-
-
- - (new) - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+568
-
-0
- -
- -
-
-
diff --git reth/crates/scroll/trie/src/hash_builder.rs scroll-reth/crates/scroll/trie/src/hash_builder.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..60bdf14087f523e9857e82d884fb034599ceb6d8 ---- /dev/null -+++ scroll-reth/crates/scroll/trie/src/hash_builder.rs -@@ -0,0 +1,568 @@ -+use crate::{ -+ branch::{BranchNodeRef, CHILD_INDEX_MASK}, -+ constants::EMPTY_ROOT_HASH, -+ leaf::HashLeaf, -+ sub_tree::SubTreeRef, ++use alloc::{format, string::String, vec}; ++use core::{ ++ any::Any, ++ fmt::{self, Display, Formatter}, ++ str::FromStr, +}; -+use alloy_primitives::{map::HashMap, B256}; -+use alloy_trie::{ -+ hash_builder::{HashBuilderValue, HashBuilderValueRef}, -+ nodes::LeafNodeRef, -+ proof::{ProofNodes, ProofRetainer}, -+ BranchNodeCompact, Nibbles, TrieMask, -+}; -+use core::cmp; -+use tracing::trace; -+ -+#[derive(Debug, Default)] -+#[allow(missing_docs)] -+pub struct HashBuilder { -+ pub key: Nibbles, -+ pub value: HashBuilderValue, -+ pub stack: Vec<B256>, -+ -+ // TODO(scroll): Introduce terminator / leaf masks -+ pub state_masks: Vec<TrieMask>, -+ pub tree_masks: Vec<TrieMask>, -+ pub hash_masks: Vec<TrieMask>, -+ -+ pub stored_in_database: bool, -+ -+ pub updated_branch_nodes: Option<HashMap<Nibbles, BranchNodeCompact>>, -+ pub proof_retainer: Option<ProofRetainer>, -+} + -+impl HashBuilder { -+ /// Enables the Hash Builder to store updated branch nodes. -+ /// -+ /// Call [`HashBuilder::split`] to get the updates to branch nodes. -+ pub fn with_updates(mut self, retain_updates: bool) -> Self { -+ self.set_updates(retain_updates); -+ self -+ } ++use alloy_chains::{Chain, NamedChain}; ++use reth_ethereum_forks::{hardfork, ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; ++#[cfg(feature = "serde")] ++use serde::{Deserialize, Serialize}; + -+ /// Enable specified proof retainer. -+ pub fn with_proof_retainer(mut self, retainer: ProofRetainer) -> Self { -+ self.proof_retainer = Some(retainer); -+ self ++hardfork!( ++ /// The name of the Scroll hardfork ++ ScrollHardfork { ++ /// Archimedes: scroll test hardfork. ++ Archimedes, ++ /// Bernoulli: <https://scroll.io/blog/blobs-are-here-scrolls-bernoulli-upgrade>. ++ Bernoulli, ++ /// Curie: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade>. ++ Curie, ++ /// Darwin: <https://scroll.io/blog/proof-recursion-scrolls-darwin-upgrade>. ++ Darwin, ++ /// DarwinV2 <https://x.com/Scroll_ZKP/status/1830565514755584269>. ++ DarwinV2, + } ++); + -+ /// Enables the Hash Builder to store updated branch nodes. -+ /// -+ /// Call [`HashBuilder::split`] to get the updates to branch nodes. -+ pub fn set_updates(&mut self, retain_updates: bool) { -+ if retain_updates { -+ self.updated_branch_nodes = Some(HashMap::default()); ++impl ScrollHardfork { ++ /// Retrieves the activation block for the specified hardfork on the given chain. ++ pub fn activation_block<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> { ++ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ if chain == Chain::from_named(NamedChain::Scroll) { ++ return Self::scroll_sepolia_activation_block(fork); ++ } ++ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ if chain == Chain::from_named(NamedChain::ScrollSepolia) { ++ return Self::scroll_mainnet_activation_block(fork); + } -+ } -+ -+ /// Splits the [`HashBuilder`] into a [`HashBuilder`] and hash builder updates. -+ pub fn split(mut self) -> (Self, HashMap<Nibbles, BranchNodeCompact>) { -+ let updates = self.updated_branch_nodes.take(); -+ (self, updates.unwrap_or_default()) -+ } -+ -+ /// Take and return retained proof nodes. -+ pub fn take_proof_nodes(&mut self) -> ProofNodes { -+ self.proof_retainer.take().map(ProofRetainer::into_proof_nodes).unwrap_or_default() -+ } + -+ /// The number of total updates accrued. -+ /// Returns `0` if [`Self::with_updates`] was not called. -+ pub fn updates_len(&self) -> usize { -+ self.updated_branch_nodes.as_ref().map(|u| u.len()).unwrap_or(0) ++ None + } + -+ /// Print the current stack of the Hash Builder. -+ pub fn print_stack(&self) { -+ println!("============ STACK ==============="); -+ for item in &self.stack { -+ println!("{}", alloy_primitives::hex::encode(item)); ++ /// Retrieves the activation timestamp for the specified hardfork on the given chain. ++ pub fn activation_timestamp<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> { ++ // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ if chain == Chain::from_named(NamedChain::ScrollSepolia) { ++ return Self::scroll_sepolia_activation_timestamp(fork); + } -+ println!("============ END STACK ==============="); -+ } -+ -+ /// Adds a new leaf element and its value to the trie hash builder. -+ pub fn add_leaf(&mut self, key: Nibbles, value: &[u8]) { -+ assert!(key > self.key, "add_leaf key {:?} self.key {:?}", key, self.key); -+ if !self.key.is_empty() { -+ self.update(&key); ++ // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 ++ if chain == Chain::from_named(NamedChain::Scroll) { ++ return Self::scroll_mainnet_activation_timestamp(fork); + } -+ self.set_key_value(key, HashBuilderValueRef::Bytes(value)); ++ ++ None + } + -+ /// Adds a new branch element and its hash to the trie hash builder. -+ pub fn add_branch(&mut self, key: Nibbles, value: B256, stored_in_database: bool) { -+ assert!( -+ key > self.key || (self.key.is_empty() && key.is_empty()), -+ "add_branch key {:?} self.key {:?}", -+ key, -+ self.key -+ ); -+ if !self.key.is_empty() { -+ self.update(&key); -+ } else if key.is_empty() { -+ self.stack.push(value); -+ } -+ self.set_key_value(key, HashBuilderValueRef::Hash(&value)); -+ self.stored_in_database = stored_in_database; ++ /// Retrieves the activation block for the specified hardfork on the Scroll Sepolia testnet. ++ pub fn scroll_sepolia_activation_block<H: Hardfork>(fork: H) -> Option<u64> { ++ match_hardfork( ++ fork, ++ |fork| match fork { ++ EthereumHardfork::Homestead | ++ EthereumHardfork::Tangerine | ++ EthereumHardfork::SpuriousDragon | ++ EthereumHardfork::Byzantium | ++ EthereumHardfork::Constantinople | ++ EthereumHardfork::Petersburg | ++ EthereumHardfork::Istanbul | ++ EthereumHardfork::Berlin | ++ EthereumHardfork::London | ++ EthereumHardfork::Shanghai => Some(0), ++ _ => None, ++ }, ++ |fork| match fork { ++ Self::Archimedes => Some(0), ++ Self::Bernoulli => Some(3747132), ++ Self::Curie => Some(4740239), ++ Self::Darwin => Some(6075509), ++ Self::DarwinV2 => Some(6375501), ++ }, ++ ) + } + -+ /// Returns the current root hash of the trie builder. -+ pub fn root(&mut self) -> B256 { -+ // Clears the internal state -+ if !self.key.is_empty() { -+ self.update(&Nibbles::default()); -+ self.key.clear(); -+ self.value.clear(); -+ } -+ let root = self.current_root(); -+ if root == EMPTY_ROOT_HASH { -+ if let Some(proof_retainer) = self.proof_retainer.as_mut() { -+ proof_retainer.retain(&Nibbles::default(), &[]) -+ } -+ } -+ root ++ /// Retrieves the activation block for the specified hardfork on the Scroll mainnet. ++ pub fn scroll_mainnet_activation_block<H: Hardfork>(fork: H) -> Option<u64> { ++ match_hardfork( ++ fork, ++ |fork| match fork { ++ EthereumHardfork::Homestead | ++ EthereumHardfork::Tangerine | ++ EthereumHardfork::SpuriousDragon | ++ EthereumHardfork::Byzantium | ++ EthereumHardfork::Constantinople | ++ EthereumHardfork::Petersburg | ++ EthereumHardfork::Istanbul | ++ EthereumHardfork::Berlin | ++ EthereumHardfork::London | ++ EthereumHardfork::Shanghai => Some(0), ++ _ => None, ++ }, ++ |fork| match fork { ++ Self::Archimedes => Some(0), ++ Self::Bernoulli => Some(5220340), ++ Self::Curie => Some(7096836), ++ Self::Darwin => Some(8568134), ++ Self::DarwinV2 => Some(8923772), ++ }, ++ ) + } + -+ #[inline] -+ fn set_key_value(&mut self, key: Nibbles, value: HashBuilderValueRef<'_>) { -+ self.log_key_value("old value"); -+ self.key = key; -+ self.value.set_from_ref(value); -+ self.log_key_value("new value"); ++ /// Retrieves the activation timestamp for the specified hardfork on the Scroll Sepolia testnet. ++ pub fn scroll_sepolia_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> { ++ match_hardfork( ++ fork, ++ |fork| match fork { ++ EthereumHardfork::Homestead | ++ EthereumHardfork::Tangerine | ++ EthereumHardfork::SpuriousDragon | ++ EthereumHardfork::Byzantium | ++ EthereumHardfork::Constantinople | ++ EthereumHardfork::Petersburg | ++ EthereumHardfork::Istanbul | ++ EthereumHardfork::Berlin | ++ EthereumHardfork::London | ++ EthereumHardfork::Shanghai => Some(0), ++ _ => None, ++ }, ++ |fork| match fork { ++ Self::Archimedes => Some(0), ++ Self::Bernoulli => Some(1713175866), ++ Self::Curie => Some(1718616171), ++ Self::Darwin => Some(1723622400), ++ Self::DarwinV2 => Some(1724832000), ++ }, ++ ) + } + -+ fn log_key_value(&self, msg: &str) { -+ trace!(target: "trie::hash_builder", -+ key = ?self.key, -+ value = ?self.value, -+ "{msg}", -+ ); ++ /// Retrieves the activation timestamp for the specified hardfork on the Scroll mainnet. ++ pub fn scroll_mainnet_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> { ++ match_hardfork( ++ fork, ++ |fork| match fork { ++ EthereumHardfork::Homestead | ++ EthereumHardfork::Tangerine | ++ EthereumHardfork::SpuriousDragon | ++ EthereumHardfork::Byzantium | ++ EthereumHardfork::Constantinople | ++ EthereumHardfork::Petersburg | ++ EthereumHardfork::Istanbul | ++ EthereumHardfork::Berlin | ++ EthereumHardfork::London | ++ EthereumHardfork::Shanghai => Some(0), ++ _ => None, ++ }, ++ |fork| match fork { ++ Self::Archimedes => Some(0), ++ Self::Bernoulli => Some(1714358352), ++ Self::Curie => Some(1719994277), ++ Self::Darwin => Some(1724227200), ++ Self::DarwinV2 => Some(1725264000), ++ }, ++ ) + } + -+ fn current_root(&self) -> B256 { -+ if let Some(node_ref) = self.stack.last() { -+ let mut root = *node_ref; -+ root.reverse(); -+ root -+ } else { -+ EMPTY_ROOT_HASH -+ } ++ /// Scroll mainnet list of hardforks. ++ pub fn scroll_mainnet() -> ChainHardforks { ++ ChainHardforks::new(vec![ ++ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Dao.boxed(), ForkCondition::Never), ++ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Never), ++ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::London.boxed(), ForkCondition::Never), ++ (EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Never), ++ (Self::Archimedes.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), ++ (Self::Bernoulli.boxed(), ForkCondition::Block(5220340)), ++ (Self::Curie.boxed(), ForkCondition::Block(7096836)), ++ (Self::Darwin.boxed(), ForkCondition::Timestamp(1724227200)), ++ (Self::DarwinV2.boxed(), ForkCondition::Timestamp(1725264000)), ++ ]) + } + -+ /// Given a new element, it appends it to the stack and proceeds to loop through the stack state -+ /// and convert the nodes it can into branch / extension nodes and hash them. This ensures -+ /// that the top of the stack always contains the merkle root corresponding to the trie -+ /// built so far. -+ fn update(&mut self, succeeding: &Nibbles) { -+ let mut build_extensions = false; -+ // current / self.key is always the latest added element in the trie -+ let mut current = self.key.clone(); -+ debug_assert!(!current.is_empty()); -+ -+ trace!(target: "trie::hash_builder", ?current, ?succeeding, "updating merkle tree"); ++ /// Scroll sepolia list of hardforks. ++ pub fn scroll_sepolia() -> ChainHardforks { ++ ChainHardforks::new(vec![ ++ (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), ++ (Self::Archimedes.boxed(), ForkCondition::Block(0)), ++ (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), ++ (Self::Bernoulli.boxed(), ForkCondition::Block(3747132)), ++ (Self::Curie.boxed(), ForkCondition::Block(4740239)), ++ (Self::Darwin.boxed(), ForkCondition::Timestamp(1723622400)), ++ (Self::DarwinV2.boxed(), ForkCondition::Timestamp(1724832000)), ++ ]) ++ } ++} + -+ let mut i = 0usize; -+ loop { -+ let _span = tracing::trace_span!(target: "trie::hash_builder", "loop", i, ?current, build_extensions).entered(); ++/// Match helper method since it's not possible to match on `dyn Hardfork` ++fn match_hardfork<H, HF, SHF>(fork: H, hardfork_fn: HF, scroll_hardfork_fn: SHF) -> Option<u64> ++where ++ H: Hardfork, ++ HF: Fn(&EthereumHardfork) -> Option<u64>, ++ SHF: Fn(&ScrollHardfork) -> Option<u64>, ++{ ++ let fork: &dyn Any = &fork; ++ if let Some(fork) = fork.downcast_ref::<EthereumHardfork>() { ++ return hardfork_fn(fork); ++ } ++ fork.downcast_ref::<ScrollHardfork>().and_then(scroll_hardfork_fn) ++} + -+ let preceding_exists = !self.state_masks.is_empty(); -+ let preceding_len = self.state_masks.len().saturating_sub(1); ++#[cfg(test)] ++mod tests { ++ use super::*; + -+ let common_prefix_len = succeeding.common_prefix_length(current.as_slice()); -+ let len = cmp::max(preceding_len, common_prefix_len); -+ assert!(len < current.len(), "len {} current.len {}", len, current.len()); ++ #[test] ++ fn test_match_hardfork() { ++ assert_eq!( ++ ScrollHardfork::scroll_mainnet_activation_block(ScrollHardfork::Bernoulli), ++ Some(5220340) ++ ); ++ assert_eq!( ++ ScrollHardfork::scroll_mainnet_activation_block(ScrollHardfork::Curie), ++ Some(7096836) ++ ); ++ } + -+ trace!( -+ target: "trie::hash_builder", -+ ?len, -+ ?common_prefix_len, -+ ?preceding_len, -+ preceding_exists, -+ "prefix lengths after comparing keys" -+ ); ++ #[test] ++ fn check_scroll_hardfork_from_str() { ++ let hardfork_str = ["BernOulLi", "CUrie", "DaRwIn", "DaRwInV2"]; ++ let expected_hardforks = [ ++ ScrollHardfork::Bernoulli, ++ ScrollHardfork::Curie, ++ ScrollHardfork::Darwin, ++ ScrollHardfork::DarwinV2, ++ ]; + -+ // Adjust the state masks for branch calculation -+ let extra_digit = current[len]; -+ if self.state_masks.len() <= len { -+ let new_len = len + 1; -+ trace!(target: "trie::hash_builder", new_len, old_len = self.state_masks.len(), "scaling state masks to fit"); -+ self.state_masks.resize(new_len, TrieMask::default()); -+ } -+ self.state_masks[len] |= TrieMask::from_nibble(extra_digit); -+ trace!( -+ target: "trie::hash_builder", -+ ?extra_digit, -+ groups = ?self.state_masks, -+ ); ++ let hardforks: Vec<ScrollHardfork> = ++ hardfork_str.iter().map(|h| ScrollHardfork::from_str(h).unwrap()).collect(); + -+ // Adjust the tree masks for exporting to the DB -+ if self.tree_masks.len() < current.len() { -+ self.resize_masks(current.len()); -+ } ++ assert_eq!(hardforks, expected_hardforks); ++ } + -+ let mut len_from = len; -+ if !succeeding.is_empty() || preceding_exists { -+ len_from += 1; -+ } -+ trace!(target: "trie::hash_builder", "skipping {len_from} nibbles"); ++ #[test] ++ fn check_nonexistent_hardfork_from_str() { ++ assert!(ScrollHardfork::from_str("not a hardfork").is_err()); ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+42
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/hardforks/src/lib.rs scroll-reth/crates/scroll/hardforks/src/lib.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..79ddb3e726cfd055dde4ed096d6df914cd7d6b33 +--- /dev/null ++++ scroll-reth/crates/scroll/hardforks/src/lib.rs +@@ -0,0 +1,42 @@ ++//! Scroll-Reth hard forks. + -+ // The key without the common prefix -+ let short_node_key = current.slice(len_from..); -+ trace!(target: "trie::hash_builder", ?short_node_key); ++#![doc = include_str!("../docs/hardforks.md")] + -+ // Concatenate the 2 nodes together -+ if !build_extensions { -+ match self.value.as_ref() { -+ HashBuilderValueRef::Bytes(leaf_value) => { -+ // TODO(scroll): Replace with terminator masks -+ // Set the terminator mask for the leaf node -+ self.state_masks[len] |= TrieMask::new(0b100 << extra_digit); -+ let leaf_node = LeafNodeRef::new(&current, leaf_value); -+ let leaf_hash = leaf_node.hash_leaf(); -+ trace!( -+ target: "trie::hash_builder", -+ ?leaf_node, -+ ?leaf_hash, -+ "pushing leaf node", -+ ); -+ self.stack.push(leaf_hash); -+ // self.retain_proof_from_stack(&current.slice(..len_from)); -+ } -+ HashBuilderValueRef::Hash(hash) => { -+ trace!(target: "trie::hash_builder", ?hash, "pushing branch node hash"); -+ self.stack.push(*hash); ++extern crate alloc; + -+ if self.stored_in_database { -+ self.tree_masks[current.len() - 1] |= TrieMask::from_nibble( -+ current -+ .last() -+ .expect("must have at least a single bit in the current key"), -+ ); -+ } -+ self.hash_masks[current.len() - 1] |= TrieMask::from_nibble( -+ current -+ .last() -+ .expect("must have at least a single bit in the current key"), -+ ); ++pub mod hardfork; + -+ build_extensions = true; -+ } -+ } -+ } ++mod dev; + -+ if build_extensions && !short_node_key.is_empty() { -+ self.update_masks(&current, len_from); -+ let stack_last = self.stack.pop().expect("there should be at least one stack item"); -+ let sub_tree = SubTreeRef::new(&short_node_key, &stack_last); -+ let sub_tree_root = sub_tree.root(); ++pub use dev::DEV_HARDFORKS; ++pub use hardfork::ScrollHardfork; + -+ trace!( -+ target: "trie::hash_builder", -+ ?short_node_key, -+ ?sub_tree_root, -+ "pushing subtree root", -+ ); -+ self.stack.push(sub_tree_root); -+ // self.retain_proof_from_stack(&current.slice(..len_from)); -+ self.resize_masks(len_from); -+ } ++use reth_ethereum_forks::{EthereumHardforks, ForkCondition}; + -+ if preceding_len <= common_prefix_len && !succeeding.is_empty() { -+ trace!(target: "trie::hash_builder", "no common prefix to create branch nodes from, returning"); -+ return; -+ } ++/// Extends [`EthereumHardforks`] with scroll helper methods. ++pub trait ScrollHardforks: EthereumHardforks { ++ /// Retrieves [`ForkCondition`] by an [`ScrollHardfork`]. If `fork` is not present, returns ++ /// [`ForkCondition::Never`]. ++ fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition; + -+ // Insert branch nodes in the stack -+ if !succeeding.is_empty() || preceding_exists { -+ // Pushes the corresponding branch node to the stack -+ let children = self.push_branch_node(&current, len); -+ // Need to store the branch node in an efficient format outside of the hash builder -+ self.store_branch_node(&current, len, children); -+ } ++ /// Convenience method to check if [`Bernoulli`](ScrollHardfork::Bernoulli) is active at a given ++ /// block number. ++ fn is_bernoulli_active_at_block(&self, block_number: u64) -> bool { ++ self.scroll_fork_activation(ScrollHardfork::Bernoulli).active_at_block(block_number) ++ } + -+ self.state_masks.resize(len, TrieMask::default()); -+ self.resize_masks(len); ++ /// Returns `true` if [`Curie`](ScrollHardfork::Curie) is active at given block block number. ++ fn is_curie_active_at_block(&self, block_number: u64) -> bool { ++ self.scroll_fork_activation(ScrollHardfork::Curie).active_at_block(block_number) ++ } + -+ if preceding_len == 0 { -+ trace!(target: "trie::hash_builder", "0 or 1 state masks means we have no more elements to process"); -+ return; -+ } ++ /// Returns `true` if [`Darwin`](ScrollHardfork::Darwin) is active at given block timestamp. ++ fn is_darwin_active_at_timestamp(&self, timestamp: u64) -> bool { ++ self.scroll_fork_activation(ScrollHardfork::Darwin).active_at_timestamp(timestamp) ++ } + -+ current.truncate(preceding_len); -+ trace!(target: "trie::hash_builder", ?current, "truncated nibbles to {} bytes", preceding_len); ++ /// Returns `true` if [`DarwinV2`](ScrollHardfork::DarwinV2) is active at given block timestamp. ++ fn is_darwin_v2_active_at_timestamp(&self, timestamp: u64) -> bool { ++ self.scroll_fork_activation(ScrollHardfork::DarwinV2).active_at_timestamp(timestamp) ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+77
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/node/Cargo.toml scroll-reth/crates/scroll/node/Cargo.toml +new file mode 100644 +index 0000000000000000000000000000000000000000..cc3c81b1b9c210a176a32009eaacbe60d7f77ec8 +--- /dev/null ++++ scroll-reth/crates/scroll/node/Cargo.toml +@@ -0,0 +1,78 @@ ++[package] ++name = "reth-scroll-node" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true + -+ trace!(target: "trie::hash_builder", groups = ?self.state_masks, "popping empty state masks"); -+ while self.state_masks.last() == Some(&TrieMask::default()) { -+ self.state_masks.pop(); -+ } ++[lints] ++workspace = true + -+ build_extensions = true; ++[dependencies] ++# reth ++reth-db.workspace = true ++reth-consensus.workspace = true ++reth-ethereum-engine-primitives.workspace = true ++reth-ethereum-forks.workspace = true ++reth-evm.workspace = true ++reth-network.workspace = true ++reth-node-api.workspace = true ++reth-node-types.workspace = true ++reth-node-builder.workspace = true ++reth-payload-builder = { workspace = true, features = ["test-utils"] } ++reth-primitives.workspace = true ++reth-primitives-traits.workspace = true ++reth-provider.workspace = true ++reth-rpc.workspace = true ++reth-tracing.workspace = true ++reth-transaction-pool.workspace = true ++reth-trie-db.workspace = true ++reth-rpc-server-types.workspace = true + -+ i += 1; -+ } -+ } ++# alloy ++alloy-rpc-types-engine.workspace = true + -+ /// Given the size of the longest common prefix, it proceeds to create a branch node -+ /// from the state mask and existing stack state, and store its RLP to the top of the stack, -+ /// after popping all the relevant elements from the stack. -+ /// -+ /// Returns the hashes of the children of the branch node, only if `updated_branch_nodes` is -+ /// enabled. -+ fn push_branch_node(&mut self, _current: &Nibbles, len: usize) -> Vec<B256> { -+ let state_mask = self.state_masks[len]; -+ let hash_mask = self.hash_masks[len]; -+ let branch_node = BranchNodeRef::new(&self.stack, state_mask); -+ // Avoid calculating this value if it's not needed. -+ let children = if self.updated_branch_nodes.is_some() { -+ branch_node.child_hashes(hash_mask).collect() -+ } else { -+ vec![] -+ }; ++# scroll ++reth-scroll-chainspec.workspace = true ++reth-scroll-engine.workspace = true ++reth-scroll-evm.workspace = true ++reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat"] } ++reth-scroll-rpc.workspace = true + -+ let branch_hash = branch_node.hash(); ++# alloy ++alloy-primitives.workspace = true + -+ // TODO: enable proof retention -+ // self.retain_proof_from_stack(&current.slice(..len)); ++# misc ++eyre.workspace = true + -+ // Clears the stack from the branch node elements -+ let first_child_idx = branch_node.first_child_index(); -+ trace!( -+ target: "trie::hash_builder", -+ new_len = first_child_idx, -+ old_len = self.stack.len(), -+ "resizing stack to prepare branch node" -+ ); -+ self.stack.resize_with(first_child_idx, Default::default); ++[features] ++default = ["reth-codec"] ++optimism = [ ++ "reth-db/optimism", ++ "reth-primitives/optimism", ++ "reth-provider/optimism", ++ "reth-scroll-evm/optimism", ++ "reth-scroll-engine/optimism", ++ "reth-scroll-rpc/optimism", ++] ++scroll = [ ++ "reth-db/scroll", ++ "reth-node-builder/scroll", ++ "reth-primitives/scroll", ++ "reth-provider/scroll", ++ "reth-evm/scroll", ++ "reth-scroll-evm/scroll", ++ "reth-scroll-engine/scroll", ++ "reth-scroll-rpc/scroll", ++ "reth-scroll-primitives/scroll", ++ "reth-primitives-traits/scroll" ++] ++reth-codec = [ ++ "reth-primitives/reth-codec", ++ "reth-primitives-traits/reth-codec", ++ "reth-scroll-primitives/reth-codec" ++] ++skip-state-root-validation = [] +\ No newline at end of file
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+113
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/node/src/addons.rs scroll-reth/crates/scroll/node/src/addons.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..711f86c869c765b6dfdb2bd823bfa3cbb2bd8612 +--- /dev/null ++++ scroll-reth/crates/scroll/node/src/addons.rs +@@ -0,0 +1,113 @@ ++use reth_ethereum_engine_primitives::EthEngineTypes; ++use reth_node_api::{AddOnsContext, NodeAddOns}; ++use reth_node_builder::{ ++ rpc::{EngineValidatorAddOn, EngineValidatorBuilder, RethRpcAddOns, RpcAddOns, RpcHandle}, ++ FullNodeComponents, ++}; ++use reth_node_types::{NodeTypes, NodeTypesWithEngine}; ++use reth_primitives::EthPrimitives; + -+ trace!(target: "trie::hash_builder", ?branch_hash, "pushing branch node with {state_mask:?} mask -+ from stack"); ++use reth_scroll_chainspec::ScrollChainSpec; ++use reth_scroll_rpc::ScrollEthApi; + -+ self.stack.push(branch_hash); -+ children -+ } ++use crate::{ScrollEngineValidator, ScrollEngineValidatorBuilder, ScrollStorage}; + -+ /// Given the current nibble prefix and the highest common prefix length, proceeds -+ /// to update the masks for the next level and store the branch node and the -+ /// masks in the database. We will use that when consuming the intermediate nodes -+ /// from the database to efficiently build the trie. -+ fn store_branch_node(&mut self, current: &Nibbles, len: usize, children: Vec<B256>) { -+ trace!(target: "trie::hash_builder", ?current, ?len, ?children, "store branch node"); -+ if len > 0 { -+ let parent_index = len - 1; -+ self.hash_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); -+ } ++/// Add-ons for the Scroll follower node. ++#[derive(Debug)] ++pub struct ScrollAddOns<N: FullNodeComponents> { ++ /// Rpc add-ons responsible for launching the RPC servers and instantiating the RPC handlers ++ /// and eth-api. ++ pub rpc_add_ons: RpcAddOns<N, ScrollEthApi<N>, ScrollEngineValidatorBuilder>, ++} + -+ let store_in_db_trie = !self.tree_masks[len].is_empty() || !self.hash_masks[len].is_empty(); -+ if store_in_db_trie { -+ if len > 0 { -+ let parent_index = len - 1; -+ self.tree_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); -+ } ++impl<N: FullNodeComponents<Types: NodeTypes<Primitives = EthPrimitives>>> Default ++ for ScrollAddOns<N> ++{ ++ fn default() -> Self { ++ Self::builder().build() ++ } ++} + -+ if self.updated_branch_nodes.is_some() { -+ let common_prefix = current.slice(..len); -+ let node = BranchNodeCompact::new( -+ self.state_masks[len] & CHILD_INDEX_MASK, -+ self.tree_masks[len], -+ self.hash_masks[len], -+ children, -+ (len == 0).then(|| self.current_root()), -+ ); -+ trace!(target: "trie::hash_builder", ?node, "storing updated intermediate node"); -+ self.updated_branch_nodes -+ .as_mut() -+ .expect("updates_branch_nodes is some") -+ .insert(common_prefix, node); -+ } -+ } ++impl<N: FullNodeComponents<Types: NodeTypes<Primitives = EthPrimitives>>> ScrollAddOns<N> { ++ /// Build a [`ScrollAddOns`] using [`ScrollAddOnsBuilder`]. ++ pub fn builder() -> ScrollAddOnsBuilder { ++ ScrollAddOnsBuilder::default() + } ++} + -+ // TODO(scroll): Enable proof retention -+ // fn retain_proof_from_stack(&mut self, prefix: &Nibbles) { -+ // if let Some(proof_retainer) = self.proof_retainer.as_mut() { -+ // proof_retainer.retain( -+ // prefix, -+ // self.stack.last().expect("there should be at least one stack item").as_ref(), -+ // ); -+ // } -+ // } -+ -+ fn update_masks(&mut self, current: &Nibbles, len_from: usize) { -+ if len_from > 0 { -+ let flag = TrieMask::from_nibble(current[len_from - 1]); -+ -+ self.hash_masks[len_from - 1] &= !flag; -+ -+ if !self.tree_masks[current.len() - 1].is_empty() { -+ self.tree_masks[len_from - 1] |= flag; -+ } -+ } -+ } ++impl<N> NodeAddOns<N> for ScrollAddOns<N> ++where ++ N: FullNodeComponents< ++ Types: NodeTypesWithEngine< ++ ChainSpec = ScrollChainSpec, ++ Primitives = EthPrimitives, ++ Storage = ScrollStorage, ++ Engine = EthEngineTypes, ++ >, ++ >, ++{ ++ type Handle = RpcHandle<N, ScrollEthApi<N>>; + -+ fn resize_masks(&mut self, new_len: usize) { -+ trace!( -+ target: "trie::hash_builder", -+ new_len, -+ old_tree_mask_len = self.tree_masks.len(), -+ old_hash_mask_len = self.hash_masks.len(), -+ "resizing tree/hash masks" -+ ); -+ self.tree_masks.resize(new_len, TrieMask::default()); -+ self.hash_masks.resize(new_len, TrieMask::default()); ++ async fn launch_add_ons( ++ self, ++ ctx: reth_node_api::AddOnsContext<'_, N>, ++ ) -> eyre::Result<Self::Handle> { ++ let Self { rpc_add_ons } = self; ++ rpc_add_ons.launch_add_ons_with(ctx, |_, _| Ok(())).await + } +} + -+// TODO(scroll): Introduce generic for the HashBuilder. -+impl From<reth_trie::HashBuilder> for HashBuilder { -+ fn from(hash_builder: reth_trie::HashBuilder) -> Self { -+ Self { -+ key: hash_builder.key, -+ value: hash_builder.value, -+ stack: hash_builder -+ .stack -+ .into_iter() -+ .map(|x| x.as_slice().try_into().expect("RlpNode contains 32 byte hashes")) -+ .collect(), -+ state_masks: hash_builder.groups, -+ tree_masks: hash_builder.tree_masks, -+ hash_masks: hash_builder.hash_masks, -+ stored_in_database: hash_builder.stored_in_database, -+ updated_branch_nodes: hash_builder.updated_branch_nodes, -+ proof_retainer: hash_builder.proof_retainer, -+ } -+ } -+} ++impl<N> RethRpcAddOns<N> for ScrollAddOns<N> ++where ++ N: FullNodeComponents< ++ Types: NodeTypesWithEngine< ++ ChainSpec = ScrollChainSpec, ++ Primitives = EthPrimitives, ++ Storage = ScrollStorage, ++ Engine = EthEngineTypes, ++ >, ++ >, ++{ ++ type EthApi = ScrollEthApi<N>; + -+impl From<HashBuilder> for reth_trie::HashBuilder { -+ fn from(value: HashBuilder) -> Self { -+ Self { -+ key: value.key, -+ value: value.value, -+ stack: value -+ .stack -+ .into_iter() -+ .map(|x| { -+ reth_trie::RlpNode::from_raw(&x.0).expect("32 byte hash can be cast to RlpNode") -+ }) -+ .collect(), -+ groups: value.state_masks, -+ tree_masks: value.tree_masks, -+ hash_masks: value.hash_masks, -+ stored_in_database: value.stored_in_database, -+ updated_branch_nodes: value.updated_branch_nodes, -+ proof_retainer: value.proof_retainer, -+ rlp_buf: Default::default(), -+ } ++ fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> { ++ self.rpc_add_ons.hooks_mut() + } +} + -+#[cfg(test)] -+mod test { -+ use super::*; -+ use crate::key::AsBytes; -+ use alloc::collections::BTreeMap; -+ use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; -+ -+ #[test] -+ fn test_basic_trie() { -+ // Test a basic trie consisting of three key value pairs: -+ // (0, 0, 0, 0, ... , 0) -+ // (0, 0, 0, 1, ... , 0) -+ // (0, 0, 1, 0, ... , 0) -+ // (1, 1, 1, 0, ... , 0) -+ // (1, 1, 1, 1, ... , 0) -+ // The branch associated with key 0xF will be collapsed into a single leaf. -+ -+ let leaf_1_key = Nibbles::from_nibbles_unchecked([ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, -+ ]); -+ let leaf_2_key = Nibbles::from_nibbles_unchecked([ -+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, -+ ]); -+ let leaf_3_key = Nibbles::from_nibbles_unchecked([ -+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, -+ ]); -+ let leaf_4_key = Nibbles::from_nibbles_unchecked([ -+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, -+ ]); -+ let leaf_5_key = Nibbles::from_nibbles_unchecked([ -+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, -+ ]); -+ let leaf_keys = [ -+ leaf_1_key.clone(), -+ leaf_2_key.clone(), -+ leaf_3_key.clone(), -+ leaf_4_key.clone(), -+ leaf_5_key.clone(), -+ ]; -+ -+ let leaf_values = leaf_keys -+ .into_iter() -+ .enumerate() -+ .map(|(i, key)| { -+ let mut leaf_value = [0u8; 32]; -+ leaf_value[0] = i as u8 + 1; -+ (key, leaf_value) -+ }) -+ .collect::<BTreeMap<_, _>>(); ++impl<N> EngineValidatorAddOn<N> for ScrollAddOns<N> ++where ++ N: FullNodeComponents< ++ Types: NodeTypesWithEngine< ++ ChainSpec = ScrollChainSpec, ++ Primitives = EthPrimitives, ++ Engine = EthEngineTypes, ++ >, ++ >, ++{ ++ type Validator = ScrollEngineValidator; + -+ let leaf_hashes: BTreeMap<_, _> = leaf_values -+ .iter() -+ .map(|(key, value)| { -+ let key_fr = -+ Fr::from_repr_vartime(key.as_bytes()).expect("key is valid field element"); -+ let value = Fr::from_repr_vartime(*value).expect("value is a valid field element"); -+ let hash = hash_with_domain(&[key_fr, value], crate::LEAF_NODE_DOMAIN); -+ (key.clone(), hash) -+ }) -+ .collect(); ++ async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> { ++ ScrollEngineValidatorBuilder.build(ctx).await ++ } ++} + -+ let mut hb = HashBuilder::default().with_updates(true); ++/// A regular scroll evm and executor builder. ++#[derive(Debug, Default, Clone)] ++#[non_exhaustive] ++pub struct ScrollAddOnsBuilder {} + -+ for (key, val) in &leaf_values { -+ hb.add_leaf(key.clone(), val); ++impl ScrollAddOnsBuilder { ++ /// Builds an instance of [`ScrollAddOns`]. ++ pub fn build<N>(self) -> ScrollAddOns<N> ++ where ++ N: FullNodeComponents<Types: NodeTypes<Primitives = EthPrimitives>>, ++ { ++ ScrollAddOns { ++ rpc_add_ons: RpcAddOns::new( ++ move |ctx| ScrollEthApi::<N>::builder().build(ctx), ++ Default::default(), ++ ), + } -+ -+ let root = hb.root(); -+ -+ // node_000 -> hash(leaf_1, leaf_2) LTRT -+ // node_00 -> hash(node_000, leaf_3) LBRT -+ // node_0 -> hash(node_00, EMPTY) LBRT -+ // node_111 -> hash(leaf_4, leaf_5) LTRT -+ // node_11 -> hash(EMPTY, node_111) LTRB -+ // node_1 -> hash(EMPTY, node_11) LTRB -+ // root -> hash(node_0, node_1) LBRB -+ -+ let expected: B256 = { -+ let node_000 = hash_with_domain( -+ &[*leaf_hashes.get(&leaf_1_key).unwrap(), *leaf_hashes.get(&leaf_2_key).unwrap()], -+ crate::BRANCH_NODE_LTRT_DOMAIN, -+ ); -+ let node_00 = hash_with_domain( -+ &[node_000, *leaf_hashes.get(&leaf_3_key).unwrap()], -+ crate::BRANCH_NODE_LBRT_DOMAIN, -+ ); -+ let node_0 = hash_with_domain(&[node_00, Fr::zero()], crate::BRANCH_NODE_LBRT_DOMAIN); -+ let node_111 = hash_with_domain( -+ &[*leaf_hashes.get(&leaf_4_key).unwrap(), *leaf_hashes.get(&leaf_5_key).unwrap()], -+ crate::BRANCH_NODE_LTRT_DOMAIN, -+ ); -+ let node_11 = hash_with_domain(&[Fr::zero(), node_111], crate::BRANCH_NODE_LTRB_DOMAIN); -+ let node_1 = hash_with_domain(&[Fr::zero(), node_11], crate::BRANCH_NODE_LTRB_DOMAIN); -+ -+ let mut root = -+ hash_with_domain(&[node_0, node_1], crate::BRANCH_NODE_LBRB_DOMAIN).to_repr(); -+ root.reverse(); -+ root.into() -+ }; -+ -+ assert_eq!(expected, root); + } +}
@@ -8586,9 +10150,9 @@
@@ -8603,7 +10167,7 @@
@@ -8613,38 +10177,31 @@
-
+21
+
+14
-0
-
diff --git reth/crates/scroll/trie/src/key.rs scroll-reth/crates/scroll/trie/src/key.rs +
diff --git reth/crates/scroll/node/src/builder/consensus.rs scroll-reth/crates/scroll/node/src/builder/consensus.rs new file mode 100644 -index 0000000000000000000000000000000000000000..0a58bd9f9ce06ceb31532ec32ace2c10471b079f +index 0000000000000000000000000000000000000000..259e3109588e599b2b10c8e348c80e038475c806 --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/key.rs -@@ -0,0 +1,21 @@ -+use reth_trie::Nibbles; ++++ scroll-reth/crates/scroll/node/src/builder/consensus.rs +@@ -0,0 +1,14 @@ ++use reth_consensus::noop::NoopConsensus; ++use reth_node_builder::{components::ConsensusBuilder, BuilderContext, FullNodeTypes}; + -+/// A type that can return its bytes representation encoded as a little-endian on 32 bytes. -+pub(crate) trait AsBytes { -+ /// Returns the type as its canonical little-endian representation on 32 bytes. -+ fn as_bytes(&self) -> [u8; 32]; -+} ++/// The consensus builder for Scroll. ++#[derive(Debug, Default, Clone, Copy)] ++pub struct ScrollConsensusBuilder; + -+impl AsBytes for Nibbles { -+ fn as_bytes(&self) -> [u8; 32] { -+ // This is strange we are now representing the leaf key using big endian?? -+ let mut result = [0u8; 32]; -+ for (byte_index, bytes) in self.as_slice().chunks(8).enumerate() { -+ for (bit_index, byte) in bytes.iter().enumerate() { -+ result[byte_index] |= byte << bit_index; -+ } -+ } ++impl<Node: FullNodeTypes> ConsensusBuilder<Node> for ScrollConsensusBuilder { ++ type Consensus = NoopConsensus; + -+ result ++ async fn build_consensus(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> { ++ Ok(NoopConsensus::default()) + } +}
@@ -8653,9 +10210,9 @@
@@ -8670,7 +10227,7 @@
@@ -8680,40 +10237,120 @@
-
+23
+
+103
-0
-
diff --git reth/crates/scroll/trie/src/leaf.rs scroll-reth/crates/scroll/trie/src/leaf.rs +
diff --git reth/crates/scroll/node/src/builder/engine.rs scroll-reth/crates/scroll/node/src/builder/engine.rs new file mode 100644 -index 0000000000000000000000000000000000000000..79216d8f562dfdf8e2bbe231e0454858e0459ae2 +index 0000000000000000000000000000000000000000..4d6985b4e3c11b5ffbfbff5d9f9e7b4a429c8d25 --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/leaf.rs -@@ -0,0 +1,23 @@ -+use super::LEAF_NODE_DOMAIN; -+use crate::key::AsBytes; -+use alloy_primitives::B256; -+use alloy_trie::nodes::LeafNodeRef; -+use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; ++++ scroll-reth/crates/scroll/node/src/builder/engine.rs +@@ -0,0 +1,103 @@ ++use alloy_primitives::{Sealable, U256}; ++use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadSidecar, PayloadError}; ++use reth_ethereum_engine_primitives::{EthEngineTypes, EthPayloadAttributes}; ++use reth_node_api::PayloadValidator; ++use reth_node_builder::{ ++ rpc::EngineValidatorBuilder, AddOnsContext, EngineApiMessageVersion, ++ EngineObjectValidationError, EngineTypes, EngineValidator, FullNodeComponents, ++ PayloadOrAttributes, ++}; ++use reth_node_types::NodeTypesWithEngine; ++use reth_primitives::{Block, BlockExt, EthPrimitives, SealedBlockFor}; ++use reth_scroll_chainspec::ScrollChainSpec; ++use reth_scroll_engine::try_into_block; ++use std::sync::Arc; + -+/// A trait used to hash the leaf node. -+pub(crate) trait HashLeaf { -+ /// Hash the leaf node. -+ fn hash_leaf(&self) -> B256; ++/// The block difficulty for in turn signing in the Clique consensus. ++const CLIQUE_IN_TURN_DIFFICULTY: U256 = U256::from_limbs([2, 0, 0, 0]); ++/// The block difficulty for out of turn signing in the Clique consensus. ++const CLIQUE_NO_TURN_DIFFICULTY: U256 = U256::from_limbs([1, 0, 0, 0]); ++ ++/// Builder for [`ScrollEngineValidator`]. ++#[derive(Debug, Default, Clone, Copy)] ++pub struct ScrollEngineValidatorBuilder; ++ ++impl<Node, Types> EngineValidatorBuilder<Node> for ScrollEngineValidatorBuilder ++where ++ Types: NodeTypesWithEngine< ++ ChainSpec = ScrollChainSpec, ++ Primitives = EthPrimitives, ++ Engine = EthEngineTypes, ++ >, ++ Node: FullNodeComponents<Types = Types>, ++{ ++ type Validator = ScrollEngineValidator; ++ ++ async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> { ++ let chainspec = ctx.config.chain.clone(); ++ Ok(ScrollEngineValidator { chainspec }) ++ } +} + -+impl HashLeaf for LeafNodeRef<'_> { -+ fn hash_leaf(&self) -> B256 { -+ let leaf_key = -+ Fr::from_repr_vartime(self.key.as_bytes()).expect("leaf key is a valid field element"); -+ let leaf_value = Fr::from_repr_vartime( -+ <[u8; 32]>::try_from(self.value).expect("leaf value is 32 bytes"), -+ ) -+ .expect("leaf value is a valid field element"); -+ hash_with_domain(&[leaf_key, leaf_value], LEAF_NODE_DOMAIN).to_repr().into() ++/// Scroll engine validator. ++#[derive(Debug, Clone)] ++pub struct ScrollEngineValidator { ++ chainspec: Arc<ScrollChainSpec>, ++} ++ ++impl<Types> EngineValidator<Types> for ScrollEngineValidator ++where ++ Types: EngineTypes<PayloadAttributes = EthPayloadAttributes>, ++{ ++ fn validate_version_specific_fields( ++ &self, ++ _version: EngineApiMessageVersion, ++ _payload_or_attrs: PayloadOrAttributes<'_, EthPayloadAttributes>, ++ ) -> Result<(), EngineObjectValidationError> { ++ Ok(()) ++ } ++ ++ fn ensure_well_formed_attributes( ++ &self, ++ _version: EngineApiMessageVersion, ++ _attributes: &EthPayloadAttributes, ++ ) -> Result<(), EngineObjectValidationError> { ++ Ok(()) ++ } ++} ++ ++impl PayloadValidator for ScrollEngineValidator { ++ type Block = Block; ++ ++ fn ensure_well_formed_payload( ++ &self, ++ payload: ExecutionPayload, ++ sidecar: ExecutionPayloadSidecar, ++ ) -> Result<SealedBlockFor<Self::Block>, PayloadError> { ++ let expected_hash = payload.block_hash(); ++ ++ // First parse the block ++ let mut block = try_into_block(payload, &sidecar, self.chainspec.clone())?; ++ ++ // Seal the block with the in-turn difficulty and return if hashes match ++ block.header.difficulty = CLIQUE_IN_TURN_DIFFICULTY; ++ let sealed_block_in_turn = block.seal_ref_slow(); ++ if sealed_block_in_turn.hash() == expected_hash { ++ let hash = sealed_block_in_turn.hash(); ++ return Ok(block.seal(hash)) ++ } ++ ++ // Seal the block with the no-turn difficulty and return if hashes match ++ block.header.difficulty = CLIQUE_NO_TURN_DIFFICULTY; ++ let sealed_block_no_turn = block.seal_ref_slow(); ++ if sealed_block_no_turn.hash() == expected_hash { ++ let hash = sealed_block_no_turn.hash(); ++ return Ok(block.seal(hash)) ++ } ++ ++ Err(PayloadError::BlockHash { ++ execution: sealed_block_no_turn.hash(), ++ consensus: expected_hash, ++ }) + } +}
@@ -8722,9 +10359,9 @@
@@ -8739,7 +10376,7 @@
@@ -8749,65 +10386,60 @@
-
+37
+
+32
-0
-
diff --git reth/crates/scroll/trie/src/lib.rs scroll-reth/crates/scroll/trie/src/lib.rs +
diff --git reth/crates/scroll/node/src/builder/execution.rs scroll-reth/crates/scroll/node/src/builder/execution.rs new file mode 100644 -index 0000000000000000000000000000000000000000..5b1a5fd5aa93d6ad8bc735ff4a6d4bb1fe17679e +index 0000000000000000000000000000000000000000..a1c823743e6bdff96e510b6f0b8b459a331b2110 --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/lib.rs -@@ -0,0 +1,37 @@ -+//! Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for -+//! prefix-sorted bits. -+ -+#![cfg_attr(not(doctest), doc = include_str!("../assets/zktrie.md"))] -+ -+#[macro_use] -+#[allow(unused_imports)] -+extern crate alloc; -+ -+mod branch; -+ -+mod constants; -+pub use constants::EMPTY_ROOT_HASH; -+ -+mod key; -+mod leaf; -+mod sub_tree; -+ -+mod hash_builder; -+pub use hash_builder::HashBuilder; -+ -+use poseidon_bn254::Fr; ++++ scroll-reth/crates/scroll/node/src/builder/execution.rs +@@ -0,0 +1,32 @@ ++use reth_evm::execute::BasicBlockExecutorProvider; ++use reth_node_builder::{components::ExecutorBuilder, BuilderContext, FullNodeTypes}; ++use reth_node_types::NodeTypesWithEngine; ++use reth_primitives::EthPrimitives; ++use reth_scroll_chainspec::ScrollChainSpec; ++use reth_scroll_evm::{ScrollEvmConfig, ScrollExecutionStrategyFactory}; + -+/// The hashing domain for leaf nodes. -+pub const LEAF_NODE_DOMAIN: Fr = Fr::from_raw([4, 0, 0, 0]); ++/// Executor builder for Scroll. ++#[derive(Debug, Default, Clone, Copy)] ++pub struct ScrollExecutorBuilder; + -+/// The hashing domain for a branch node with two terminal children. -+pub const BRANCH_NODE_LTRT_DOMAIN: Fr = Fr::from_raw([6, 0, 0, 0]); ++impl<Node> ExecutorBuilder<Node> for ScrollExecutorBuilder ++where ++ Node: FullNodeTypes, ++ Node::Types: NodeTypesWithEngine<ChainSpec = ScrollChainSpec, Primitives = EthPrimitives>, ++{ ++ type EVM = ScrollEvmConfig; ++ type Executor = BasicBlockExecutorProvider<ScrollExecutionStrategyFactory>; + -+/// The hashing domain for a branch node with a left terminal child and a right branch child. -+pub const BRANCH_NODE_LTRB_DOMAIN: Fr = Fr::from_raw([7, 0, 0, 0]); ++ async fn build_evm( ++ self, ++ ctx: &BuilderContext<Node>, ++ ) -> eyre::Result<(Self::EVM, Self::Executor)> { ++ let chain_spec = ctx.chain_spec(); ++ let strategy_factory = ScrollExecutionStrategyFactory::new(chain_spec); ++ let evm_config = strategy_factory.evm_config(); + -+/// The hashing domain for a branch node with a left branch child and a right terminal child. -+pub const BRANCH_NODE_LBRT_DOMAIN: Fr = Fr::from_raw([8, 0, 0, 0]); ++ let executor = BasicBlockExecutorProvider::new(strategy_factory); + -+/// The hashing domain for a branch node with two branch children. -+pub const BRANCH_NODE_LBRB_DOMAIN: Fr = Fr::from_raw([9, 0, 0, 0]);
++ Ok((evm_config, executor)) ++ } ++}
@@ -8822,7 +10454,7 @@
@@ -8832,108 +10464,35 @@
-
+44
+
+6
-0
-
diff --git reth/crates/scroll/trie/src/sub_tree.rs scroll-reth/crates/scroll/trie/src/sub_tree.rs +
diff --git reth/crates/scroll/node/src/builder/mod.rs scroll-reth/crates/scroll/node/src/builder/mod.rs new file mode 100644 -index 0000000000000000000000000000000000000000..31edad05c4166dcab958f28450206e81b61ab13f +index 0000000000000000000000000000000000000000..86ed6d848601ad99def48042f8331f48e688b84d --- /dev/null -+++ scroll-reth/crates/scroll/trie/src/sub_tree.rs -@@ -0,0 +1,44 @@ -+use super::{BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN}; -+use alloy_primitives::{hex, B256}; -+use alloy_trie::Nibbles; -+use core::fmt; -+use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; -+ -+/// [`SubTreeRef`] is a structure that allows for calculation of the root of a sparse binary Merkle -+/// tree consisting of a single leaf node. -+pub(crate) struct SubTreeRef<'a> { -+ /// The key to the child node. -+ pub key: &'a Nibbles, -+ /// A pointer to the child node. -+ pub child: &'a B256, -+} -+ -+impl<'a> SubTreeRef<'a> { -+ /// Creates a new subtree with the given key and a pointer to the child. -+ #[inline] -+ pub(crate) const fn new(key: &'a Nibbles, child: &'a B256) -> Self { -+ Self { key, child } -+ } -+ -+ pub(crate) fn root(&self) -> B256 { -+ let mut tree_root = -+ Fr::from_repr_vartime(self.child.0).expect("child is a valid field element"); -+ for bit in self.key.as_slice().iter().rev() { -+ tree_root = if *bit == 0 { -+ hash_with_domain(&[tree_root, Fr::zero()], BRANCH_NODE_LBRT_DOMAIN) -+ } else { -+ hash_with_domain(&[Fr::zero(), tree_root], BRANCH_NODE_LTRB_DOMAIN) -+ }; -+ } -+ tree_root.to_repr().into() -+ } -+} -+ -+impl fmt::Debug for SubTreeRef<'_> { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.debug_struct("SubTreeRef") -+ .field("key", &self.key) -+ .field("node", &hex::encode(self.child)) -+ .finish() -+ } -+}
-
- - - - -
- -
++++ scroll-reth/crates/scroll/node/src/builder/mod.rs +@@ -0,0 +1,6 @@ ++pub(crate) mod consensus; ++pub(crate) mod engine; ++pub(crate) mod execution; ++pub(crate) mod network; ++pub(crate) mod payload; ++pub(crate) mod pool; - -
- - - -
-
- -
- - - - -
-
-
-
@@ -9261,13 +10948,13 @@ @@ -9277,34 +10964,100 @@
-
+1
+
+72
-0
-
diff --git reth/crates/optimism/bin/Cargo.toml scroll-reth/crates/optimism/bin/Cargo.toml -index b182a4f278a301c8013d2481a271f6bac3f30d99..aa7a321b745bdba6c5622639cf802837cff15814 100644 ---- reth/crates/optimism/bin/Cargo.toml -+++ scroll-reth/crates/optimism/bin/Cargo.toml -@@ -47,6 +47,7 @@ "reth-optimism-rpc/optimism", - "reth-provider/optimism", - "reth-optimism-primitives/optimism", - ] -+scroll = [] -  - dev = [ - "reth-optimism-cli/dev",
+
diff --git reth/crates/scroll/node/src/storage.rs scroll-reth/crates/scroll/node/src/storage.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..b1cf8916af6170ba0c95de56f7f068f3c9aefd13 +--- /dev/null ++++ scroll-reth/crates/scroll/node/src/storage.rs +@@ -0,0 +1,72 @@ ++use reth_db::transaction::{DbTx, DbTxMut}; ++use reth_ethereum_forks::EthereumHardforks; ++use reth_node_types::NodeTypes; ++use reth_primitives::{BlockBody, EthPrimitives}; ++use reth_provider::{ ++ providers::ChainStorage, BlockBodyReader, BlockBodyWriter, ChainSpecProvider, DBProvider, ++ EthStorage, ProviderResult, ReadBodyInput, StorageLocation, ++}; ++ ++/// Storage implementation for Scroll. ++#[derive(Debug, Default, Clone)] ++pub struct ScrollStorage(EthStorage); ++ ++impl<Provider> BlockBodyWriter<Provider, BlockBody> for ScrollStorage ++where ++ Provider: DBProvider<Tx: DbTxMut>, ++{ ++ fn write_block_bodies( ++ &self, ++ provider: &Provider, ++ bodies: Vec<(u64, Option<BlockBody>)>, ++ write_to: StorageLocation, ++ ) -> ProviderResult<()> { ++ self.0.write_block_bodies(provider, bodies, write_to) ++ } ++ ++ fn remove_block_bodies_above( ++ &self, ++ provider: &Provider, ++ block: alloy_primitives::BlockNumber, ++ remove_from: StorageLocation, ++ ) -> ProviderResult<()> { ++ self.0.remove_block_bodies_above(provider, block, remove_from) ++ } ++} ++ ++impl<Provider> BlockBodyReader<Provider> for ScrollStorage ++where ++ Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>, ++{ ++ type Block = reth_primitives::Block; ++ ++ fn read_block_bodies( ++ &self, ++ provider: &Provider, ++ inputs: Vec<ReadBodyInput<'_, Self::Block>>, ++ ) -> ProviderResult<Vec<BlockBody>> { ++ self.0.read_block_bodies(provider, inputs) ++ } ++} ++ ++impl ChainStorage<EthPrimitives> for ScrollStorage { ++ fn reader<TX, Types>( ++ &self, ++ ) -> impl reth_provider::ChainStorageReader<reth_provider::DatabaseProvider<TX, Types>, EthPrimitives> ++ where ++ TX: DbTx + 'static, ++ Types: reth_provider::providers::NodeTypesForProvider<Primitives = EthPrimitives>, ++ { ++ self ++ } ++ ++ fn writer<TX, Types>( ++ &self, ++ ) -> impl reth_provider::ChainStorageWriter<reth_provider::DatabaseProvider<TX, Types>, EthPrimitives> ++ where ++ TX: DbTxMut + DbTx + 'static, ++ Types: NodeTypes<Primitives = EthPrimitives>, ++ { ++ self ++ } ++}
@@ -9313,13 +11066,13 @@ @@ -9329,120 +11082,150 @@
-
+2
-
-1
+
+122
+
-0
-
diff --git reth/crates/optimism/bin/src/lib.rs scroll-reth/crates/optimism/bin/src/lib.rs -index 21c28f7c5470f38893c52fa10198a0feed2323f7..b3e274e678f577e7f76827917b387f033c2011a7 100644 ---- reth/crates/optimism/bin/src/lib.rs -+++ scroll-reth/crates/optimism/bin/src/lib.rs -@@ -24,8 +24,9 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", - issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" - )] - #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] - // The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -+#![cfg(all(feature = "optimism", not(feature = "scroll")))] -  - /// Re-exported from `reth_optimism_cli`. - pub mod cli {
-
- - -
- - -
-
-
- - reth - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+15
-
-10
- -
- -
-
-
diff --git reth/crates/optimism/bin/src/main.rs scroll-reth/crates/optimism/bin/src/main.rs -index bba31e250117ecd6f32b15f4176984e9d947f8fd..da7f92e33192c346a3cedfa42b4a945226703977 100644 ---- reth/crates/optimism/bin/src/main.rs -+++ scroll-reth/crates/optimism/bin/src/main.rs -@@ -1,19 +1,18 @@ - #![allow(missing_docs, rustdoc::missing_crate_level_docs)] --// The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -- --use clap::Parser; --use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher, Node}; --use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; --use reth_optimism_node::{args::RollupArgs, OpNode}; --use reth_provider::providers::BlockchainProvider; -- --use tracing as _; -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] -  - #[global_allocator] - static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); -  -+// The `optimism` feature must be enabled to use this crate. -+#[cfg(all(feature = "optimism", not(feature = "scroll")))] - fn main() { -+ use clap::Parser; -+ use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher, Node}; -+ use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; -+ use reth_optimism_node::{args::RollupArgs, OpNode}; -+ use reth_provider::providers::BlockchainProvider; -+ use tracing as _; - reth_cli_util::sigsegv_handler::install(); -  - // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. -@@ -53,3 +52,9 @@ eprintln!("Error: {err:?}"); - std::process::exit(1); - } - } +
diff --git reth/crates/scroll/primitives/Cargo.toml scroll-reth/crates/scroll/primitives/Cargo.toml +new file mode 100644 +index 0000000000000000000000000000000000000000..9e60bdc3a9382e8aade5181d6a711fbdf99c641e +--- /dev/null ++++ scroll-reth/crates/scroll/primitives/Cargo.toml +@@ -0,0 +1,122 @@ ++[package] ++name = "reth-scroll-primitives" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true + -+#[cfg(not(all(feature = "optimism", not(feature = "scroll"))))] -+fn main() { -+ eprintln!("Optimism feature is not enabled"); -+ std::process::exit(1); -+}
++[lints] ++workspace = true ++ ++[dependencies] ++# reth ++reth-codecs = { workspace = true, optional = true } ++reth-primitives-traits = { workspace = true, features = ["scroll-in-memory-size"] } ++reth-zstd-compressors = { workspace = true, optional = true } ++ ++# revm ++revm-primitives = { workspace = true, optional = true } ++ ++# alloy ++alloy-consensus.workspace = true ++alloy-eips.workspace = true ++alloy-primitives.workspace = true ++alloy-rlp.workspace = true ++ ++# scroll ++scroll-alloy-consensus.workspace = true ++ ++# codec ++bytes = { workspace = true, optional = true } ++modular-bitfield = { workspace = true, optional = true } ++serde = { workspace = true, optional = true } ++ ++# misc ++derive_more.workspace = true ++once_cell.workspace = true ++rand = { workspace = true, optional = true } ++ ++# test ++arbitrary = { workspace = true, features = ["derive"], optional = true } ++proptest = { workspace = true, optional = true } ++secp256k1 = { workspace = true, optional = true } ++ ++[dev-dependencies] ++proptest-arbitrary-interop.workspace = true ++reth-codecs = { workspace = true, features = ["test-utils"] } ++rstest.workspace = true ++proptest.workspace = true ++secp256k1 = { workspace = true, features = ["rand"] } ++rand.workspace = true ++ ++[features] ++default = ["std"] ++std = [ ++ "serde?/std", ++ "scroll-alloy-consensus/std", ++ "alloy-consensus/std", ++ "alloy-eips/std", ++ "alloy-primitives/std", ++ "alloy-rlp/std", ++ "bytes?/std", ++ "reth-primitives-traits/std", ++ "reth-zstd-compressors?/std", ++ "reth-codecs?/std", ++ "derive_more/std", ++ "once_cell/std", ++ "proptest?/std", ++ "serde?/std", ++ "rand?/std", ++ "secp256k1?/std", ++ "revm-primitives?/std" ++] ++reth-codec = [ ++ "dep:reth-codecs", ++ "std", ++ "dep:proptest", ++ "dep:arbitrary", ++ "reth-primitives-traits/reth-codec", ++ "scroll-alloy-consensus/reth-codec", ++ "dep:bytes", ++ "dep:modular-bitfield", ++ "dep:reth-zstd-compressors" ++] ++serde = [ ++ "dep:serde", ++ "scroll-alloy-consensus/serde", ++ "secp256k1?/serde", ++ "alloy-consensus/serde", ++ "alloy-eips/serde", ++ "alloy-primitives/serde", ++ "bytes?/serde", ++ "rand?/serde", ++ "reth-codecs?/serde", ++ "reth-primitives-traits/serde" , ++ "revm-primitives?/serde", ++] ++serde-bincode-compat = [ ++ "alloy-consensus/serde-bincode-compat", ++ "alloy-eips/serde-bincode-compat", ++ "reth-primitives-traits/serde-bincode-compat", ++ "scroll-alloy-consensus/serde-bincode-compat" ++] ++arbitrary = [ ++ "dep:arbitrary", ++ "dep:secp256k1", ++ "secp256k1?/rand", ++ "rand", ++ "alloy-consensus/arbitrary", ++ "alloy-eips/arbitrary", ++ "alloy-primitives/arbitrary", ++ "reth-codecs?/arbitrary", ++ "reth-primitives-traits/arbitrary", ++ "revm-primitives?/arbitrary", ++ "scroll-alloy-consensus/arbitrary" ++] ++scroll = [ ++ "dep:revm-primitives", ++ "revm-primitives/scroll", ++ "reth-primitives-traits/scroll" ++]
@@ -9451,13 +11234,13 @@ @@ -9467,86 +11250,69 @@
-
+1
+
+41
-0
-
diff --git reth/crates/optimism/cli/Cargo.toml scroll-reth/crates/optimism/cli/Cargo.toml -index 3cd2edeecc5ab5a43962e892e79ac09d86061f9c..5573b1c97b870976a14d18eb990b20a9aad30d0a 100644 ---- reth/crates/optimism/cli/Cargo.toml -+++ scroll-reth/crates/optimism/cli/Cargo.toml -@@ -120,3 +120,4 @@ "reth-execution-types/serde", - "reth-provider/serde", - "reth-optimism-primitives/serde", - ] -+scroll = []
-
- - -
- - -
-
-
- - reth - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+2
-
-1
- -
- -
-
-
diff --git reth/crates/optimism/cli/src/lib.rs scroll-reth/crates/optimism/cli/src/lib.rs -index 5c3900a0e483829d7fb9f0decc01ef8e02a2d06f..791ca5ca8ff9aa9df2be80648ad01f77b25afcfc 100644 ---- reth/crates/optimism/cli/src/lib.rs -+++ scroll-reth/crates/optimism/cli/src/lib.rs -@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" - )] - #![cfg_attr(all(not(test), feature = "optimism"), warn(unused_crate_dependencies))] - #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] - // The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -+#![cfg(all(feature = "optimism", not(feature = "scroll")))] -  - /// Optimism chain specification parser. - pub mod chainspec;
+
diff --git reth/crates/scroll/primitives/src/lib.rs scroll-reth/crates/scroll/primitives/src/lib.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..31ce2e165348d824542121b6dd9ef1403a60451d +--- /dev/null ++++ scroll-reth/crates/scroll/primitives/src/lib.rs +@@ -0,0 +1,41 @@ ++//! Commonly used types in Scroll. ++ ++#![doc( ++ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", ++ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", ++ issue_tracker_base_url = "https://github.com/scroll-tech/reth/issues/" ++)] ++#![cfg_attr(not(test), warn(unused_crate_dependencies))] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(not(feature = "std"), no_std)] ++ ++use once_cell as _; ++ ++extern crate alloc; ++ ++pub mod transaction; ++pub use transaction::{signed::ScrollTransactionSigned, tx_type::ScrollTxType}; ++ ++use reth_primitives_traits::Block; ++ ++mod receipt; ++pub use receipt::ScrollReceipt; ++ ++/// Scroll-specific block type. ++pub type ScrollBlock = alloy_consensus::Block<ScrollTransactionSigned>; ++ ++/// Scroll-specific block body type. ++pub type ScrollBlockBody = <ScrollBlock as Block>::Body; ++ ++/// Primitive types for Scroll Node. ++#[derive(Debug, Default, Clone, PartialEq, Eq)] ++pub struct ScrollPrimitives; ++ ++#[cfg(feature = "scroll")] ++impl reth_primitives_traits::NodePrimitives for ScrollPrimitives { ++ type Block = ScrollBlock; ++ type BlockHeader = alloy_consensus::Header; ++ type BlockBody = ScrollBlockBody; ++ type SignedTx = ScrollTransactionSigned; ++ type Receipt = ScrollReceipt; ++}
@@ -9555,13 +11321,13 @@ @@ -9571,86 +11337,309 @@
-
+1
+
+281
-0
-
diff --git reth/crates/optimism/consensus/Cargo.toml scroll-reth/crates/optimism/consensus/Cargo.toml -index 024bb957f815390ebfe3a3671a9907e5f20dadc5..2e48677aa11767cdfc9713d1573708a93e192071 100644 ---- reth/crates/optimism/consensus/Cargo.toml -+++ scroll-reth/crates/optimism/consensus/Cargo.toml -@@ -54,3 +54,4 @@ "alloy-trie/std", - "op-alloy-consensus/std", - ] - optimism = ["reth-primitives/optimism", "reth-optimism-primitives/optimism"] -+scroll = []
- - - -
- - -
-
-
- - reth - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+2
-
-1
- -
- -
-
-
diff --git reth/crates/optimism/consensus/src/lib.rs scroll-reth/crates/optimism/consensus/src/lib.rs -index cedc8c462929f37e2a5555cd154ce3a756d40896..b77a8a056fbc048bfc17d56ed55e91ba9c75a259 100644 ---- reth/crates/optimism/consensus/src/lib.rs -+++ scroll-reth/crates/optimism/consensus/src/lib.rs -@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" - )] - #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - #![cfg_attr(not(feature = "std"), no_std)] -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] - // The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -+#![cfg(all(feature = "optimism", not(feature = "scroll")))] -  - extern crate alloc; -
+
diff --git reth/crates/scroll/primitives/src/receipt.rs scroll-reth/crates/scroll/primitives/src/receipt.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..fb4cea55235bc0bb6d246fcd29800659b0a7d665 +--- /dev/null ++++ scroll-reth/crates/scroll/primitives/src/receipt.rs +@@ -0,0 +1,281 @@ ++use alloy_consensus::{ ++ Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom, RlpDecodableReceipt, ++ RlpEncodableReceipt, TxReceipt, Typed2718, ++}; ++use alloy_primitives::{Bloom, Log, U256}; ++use alloy_rlp::{BufMut, Decodable, Header}; ++use reth_primitives_traits::InMemorySize; ++use scroll_alloy_consensus::{ScrollTransactionReceipt, ScrollTxType}; ++ ++/// Typed ethereum transaction receipt. ++/// Receipt containing result of transaction execution. ++#[derive(Clone, Debug, PartialEq, Eq)] ++#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] ++#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] ++pub enum ScrollReceipt { ++ /// Legacy receipt ++ Legacy(ScrollTransactionReceipt), ++ /// EIP-2930 receipt ++ Eip2930(ScrollTransactionReceipt), ++ /// EIP-1559 receipt ++ Eip1559(ScrollTransactionReceipt), ++ /// L1 message receipt ++ L1Message(Receipt), ++} ++ ++impl ScrollReceipt { ++ /// Returns [`ScrollTxType`] of the receipt. ++ pub const fn tx_type(&self) -> ScrollTxType { ++ match self { ++ Self::Legacy(_) => ScrollTxType::Legacy, ++ Self::Eip2930(_) => ScrollTxType::Eip2930, ++ Self::Eip1559(_) => ScrollTxType::Eip1559, ++ Self::L1Message(_) => ScrollTxType::L1Message, ++ } ++ } ++ ++ /// Returns inner [`Receipt`], ++ pub const fn as_receipt(&self) -> &Receipt { ++ match self { ++ Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) => { ++ &receipt.inner ++ } ++ Self::L1Message(receipt) => receipt, ++ } ++ } ++ ++ /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. ++ pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize { ++ match self { ++ Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) => { ++ receipt.rlp_encoded_fields_length_with_bloom(bloom) ++ } ++ Self::L1Message(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), ++ } ++ } ++ ++ /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. ++ pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) { ++ match self { ++ Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) => { ++ receipt.rlp_encode_fields_with_bloom(bloom, out) ++ } ++ Self::L1Message(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), ++ } ++ } ++ ++ /// Returns RLP header for inner encoding. ++ pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header { ++ Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) } ++ } ++ ++ /// RLP-decodes the receipt from the provided buffer. This does not expect a type byte or ++ /// network header. ++ pub fn rlp_decode_inner( ++ buf: &mut &[u8], ++ tx_type: ScrollTxType, ++ ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { ++ match tx_type { ++ ScrollTxType::Legacy => { ++ let ReceiptWithBloom { receipt, logs_bloom } = ++ RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; ++ Ok(ReceiptWithBloom { receipt: Self::Legacy(receipt), logs_bloom }) ++ } ++ ScrollTxType::Eip2930 => { ++ let ReceiptWithBloom { receipt, logs_bloom } = ++ RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; ++ Ok(ReceiptWithBloom { receipt: Self::Eip2930(receipt), logs_bloom }) ++ } ++ ScrollTxType::Eip1559 => { ++ let ReceiptWithBloom { receipt, logs_bloom } = ++ RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; ++ Ok(ReceiptWithBloom { receipt: Self::Eip1559(receipt), logs_bloom }) ++ } ++ ScrollTxType::L1Message => { ++ let ReceiptWithBloom { receipt, logs_bloom } = ++ RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; ++ Ok(ReceiptWithBloom { receipt: Self::L1Message(receipt), logs_bloom }) ++ } ++ } ++ } ++ ++ /// Returns the l1 fee for the transaction receipt. ++ pub const fn l1_fee(&self) -> U256 { ++ match self { ++ Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) => { ++ receipt.l1_fee ++ } ++ Self::L1Message(_) => U256::ZERO, ++ } ++ } ++} ++ ++impl Eip2718EncodableReceipt for ScrollReceipt { ++ fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { ++ !self.tx_type().is_legacy() as usize + self.rlp_header_inner(bloom).length_with_payload() ++ } ++ ++ fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { ++ if !self.tx_type().is_legacy() { ++ out.put_u8(self.tx_type() as u8); ++ } ++ self.rlp_header_inner(bloom).encode(out); ++ self.rlp_encode_fields(bloom, out); ++ } ++} ++ ++impl RlpEncodableReceipt for ScrollReceipt { ++ fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { ++ let mut len = self.eip2718_encoded_length_with_bloom(bloom); ++ if !self.tx_type().is_legacy() { ++ len += Header { ++ list: false, ++ payload_length: self.eip2718_encoded_length_with_bloom(bloom), ++ } ++ .length(); ++ } ++ ++ len ++ } ++ ++ fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { ++ if !self.tx_type().is_legacy() { ++ Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) } ++ .encode(out); ++ } ++ self.eip2718_encode_with_bloom(bloom, out); ++ } ++} ++ ++impl RlpDecodableReceipt for ScrollReceipt { ++ fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { ++ let header_buf = &mut &**buf; ++ let header = Header::decode(header_buf)?; ++ ++ // Legacy receipt, reuse initial buffer without advancing ++ if header.list { ++ return Self::rlp_decode_inner(buf, ScrollTxType::Legacy) ++ } ++ ++ // Otherwise, advance the buffer and try decoding type flag followed by receipt ++ *buf = *header_buf; ++ ++ let remaining = buf.len(); ++ let tx_type = ScrollTxType::decode(buf)?; ++ let this = Self::rlp_decode_inner(buf, tx_type)?; ++ ++ if buf.len() + header.payload_length != remaining { ++ return Err(alloy_rlp::Error::UnexpectedLength); ++ } ++ ++ Ok(this) ++ } ++} ++ ++impl TxReceipt for ScrollReceipt { ++ type Log = Log; ++ ++ fn status_or_post_state(&self) -> Eip658Value { ++ self.as_receipt().status_or_post_state() ++ } ++ ++ fn status(&self) -> bool { ++ self.as_receipt().status() ++ } ++ ++ fn bloom(&self) -> Bloom { ++ self.as_receipt().bloom() ++ } ++ ++ fn cumulative_gas_used(&self) -> u64 { ++ self.as_receipt().cumulative_gas_used() ++ } ++ ++ fn logs(&self) -> &[Log] { ++ self.as_receipt().logs() ++ } ++} ++ ++impl Typed2718 for ScrollReceipt { ++ fn ty(&self) -> u8 { ++ self.tx_type().into() ++ } ++} ++ ++impl InMemorySize for ScrollReceipt { ++ fn size(&self) -> usize { ++ self.as_receipt().size() ++ } ++} ++ ++impl reth_primitives_traits::Receipt for ScrollReceipt {} ++ ++#[cfg(feature = "reth-codec")] ++mod compact { ++ use super::*; ++ use alloc::borrow::Cow; ++ use alloy_primitives::U256; ++ use reth_codecs::Compact; ++ ++ #[derive(reth_codecs::CompactZstd)] ++ #[reth_zstd( ++ compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR, ++ decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR ++ )] ++ struct CompactScrollReceipt<'a> { ++ tx_type: ScrollTxType, ++ success: bool, ++ cumulative_gas_used: u64, ++ logs: Cow<'a, Vec<Log>>, ++ l1_fee: Option<U256>, ++ } ++ ++ impl<'a> From<&'a ScrollReceipt> for CompactScrollReceipt<'a> { ++ fn from(receipt: &'a ScrollReceipt) -> Self { ++ Self { ++ tx_type: receipt.tx_type(), ++ success: receipt.status(), ++ cumulative_gas_used: receipt.cumulative_gas_used(), ++ logs: Cow::Borrowed(&receipt.as_receipt().logs), ++ l1_fee: (receipt.l1_fee() != U256::ZERO).then_some(receipt.l1_fee()), ++ } ++ } ++ } ++ ++ impl From<CompactScrollReceipt<'_>> for ScrollReceipt { ++ fn from(receipt: CompactScrollReceipt<'_>) -> Self { ++ let CompactScrollReceipt { tx_type, success, cumulative_gas_used, logs, l1_fee } = ++ receipt; ++ ++ let inner = ++ Receipt { status: success.into(), cumulative_gas_used, logs: logs.into_owned() }; ++ ++ match tx_type { ++ ScrollTxType::Legacy => { ++ Self::Legacy(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) ++ } ++ ScrollTxType::Eip2930 => { ++ Self::Eip2930(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) ++ } ++ ScrollTxType::Eip1559 => { ++ Self::Eip1559(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) ++ } ++ ScrollTxType::L1Message => Self::L1Message(inner), ++ } ++ } ++ } ++ ++ impl Compact for ScrollReceipt { ++ fn to_compact<B>(&self, buf: &mut B) -> usize ++ where ++ B: bytes::BufMut + AsMut<[u8]>, ++ { ++ CompactScrollReceipt::from(self).to_compact(buf) ++ } ++ ++ fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { ++ let (receipt, buf) = CompactScrollReceipt::from_compact(buf, len); ++ (receipt.into(), buf) ++ } ++ } ++}
@@ -9659,13 +11648,13 @@ @@ -9675,86 +11664,32 @@
-
+1
+
+4
-0
-
diff --git reth/crates/optimism/evm/Cargo.toml scroll-reth/crates/optimism/evm/Cargo.toml -index 19b63d9fe03367c28260d6e44f429ca576be2770..37146033e23c731c72b8e3ed8371e3f7e2a5dc2a 100644 ---- reth/crates/optimism/evm/Cargo.toml -+++ scroll-reth/crates/optimism/evm/Cargo.toml -@@ -85,3 +85,4 @@ "revm/optimism", - "revm-primitives/optimism", - "reth-optimism-primitives/optimism", - ] -+scroll = []
- - - -
- - -
-
-
- - reth - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+2
-
-1
- -
- -
-
-
diff --git reth/crates/optimism/evm/src/lib.rs scroll-reth/crates/optimism/evm/src/lib.rs -index 37c3fd548be63182dc621eec398c3e33d0689026..3740a75d01dcfda50b8080c0d0c1d3d8a0cd6d60 100644 ---- reth/crates/optimism/evm/src/lib.rs -+++ scroll-reth/crates/optimism/evm/src/lib.rs -@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" - )] - #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - #![cfg_attr(not(feature = "std"), no_std)] -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] - // The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -+#![cfg(all(feature = "optimism", not(feature = "scroll")))] -  - extern crate alloc; -
+
diff --git reth/crates/scroll/primitives/src/transaction/mod.rs scroll-reth/crates/scroll/primitives/src/transaction/mod.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..0b23824d496eeb292fc66ba4ca1678f633e9c788 +--- /dev/null ++++ scroll-reth/crates/scroll/primitives/src/transaction/mod.rs +@@ -0,0 +1,4 @@ ++//! Scroll primitives transaction types. ++ ++pub mod signed; ++pub mod tx_type;
@@ -9763,13 +11698,13 @@ @@ -9779,85 +11714,4917 @@
-
+1
+
+596
-0
-
diff --git reth/crates/optimism/node/Cargo.toml scroll-reth/crates/optimism/node/Cargo.toml -index 25adb3d108932b505a9ec1c16a96c910b64995f0..d2e09c80615689f10853781f0dc891640f3772db 100644 ---- reth/crates/optimism/node/Cargo.toml -+++ scroll-reth/crates/optimism/node/Cargo.toml -@@ -138,3 +138,4 @@ reth-codec = [ - "reth-primitives/reth-codec", - "reth-optimism-primitives/reth-codec", - ] -+scroll = []
- - - -
- - -
-
-
- - reth - -
- -
- - scroll-reth - -
-
-
- -
- -
- -
+2
-
-1
- -
- -
-
-
diff --git reth/crates/optimism/node/src/lib.rs scroll-reth/crates/optimism/node/src/lib.rs -index 18c5715474ed024fc6698b846f738a8611028ba6..2f3d5e5c082d0c2b3f24257b4c4080860c790937 100644 ---- reth/crates/optimism/node/src/lib.rs -+++ scroll-reth/crates/optimism/node/src/lib.rs -@@ -9,8 +9,9 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", - issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" - )] - #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -+#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] - // The `optimism` feature must be enabled to use this crate. --#![cfg(feature = "optimism")] -+#![cfg(all(feature = "optimism", not(feature = "scroll")))] -  - /// CLI argument parsing for the optimism node. - pub mod args;
-
- - -
-
- -
+ +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+47
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/primitives/src/transaction/tx_type.rs scroll-reth/crates/scroll/primitives/src/transaction/tx_type.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..e549b81bd97a09f15f1b00dc99f1f2a34cb292b0 +--- /dev/null ++++ scroll-reth/crates/scroll/primitives/src/transaction/tx_type.rs +@@ -0,0 +1,47 @@ ++//! Scroll transaction type. ++ ++pub use scroll_alloy_consensus::ScrollTxType; ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use reth_codecs::{txtype::*, Compact}; ++ use rstest::rstest; ++ use scroll_alloy_consensus::L1_MESSAGE_TX_TYPE_ID; ++ ++ #[rstest] ++ #[case(ScrollTxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] ++ #[case(ScrollTxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] ++ #[case(ScrollTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] ++ #[case(ScrollTxType::L1Message, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![L1_MESSAGE_TX_TYPE_ID])] ++ fn test_txtype_to_compact( ++ #[case] tx_type: ScrollTxType, ++ #[case] expected_identifier: usize, ++ #[case] expected_buf: Vec<u8>, ++ ) { ++ let mut buf = vec![]; ++ let identifier = tx_type.to_compact(&mut buf); ++ ++ assert_eq!( ++ identifier, expected_identifier, ++ "Unexpected identifier for ScrollTxType {tx_type:?}", ++ ); ++ assert_eq!(buf, expected_buf, "Unexpected buffer for ScrollTxType {tx_type:?}",); ++ } ++ ++ #[rstest] ++ #[case(ScrollTxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] ++ #[case(ScrollTxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] ++ #[case(ScrollTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] ++ #[case(ScrollTxType::L1Message, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![L1_MESSAGE_TX_TYPE_ID])] ++ fn test_txtype_from_compact( ++ #[case] expected_type: ScrollTxType, ++ #[case] identifier: usize, ++ #[case] buf: Vec<u8>, ++ ) { ++ let (actual_type, remaining_buf) = ScrollTxType::from_compact(&buf, identifier); ++ ++ assert_eq!(actual_type, expected_type, "Unexpected TxType for identifier {identifier}"); ++ assert!(remaining_buf.is_empty(), "Buffer not fully consumed for identifier {identifier}"); ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+83
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/Cargo.toml scroll-reth/crates/scroll/rpc/Cargo.toml +new file mode 100644 +index 0000000000000000000000000000000000000000..5fd41c5ed2427ef861efb652561b630b07e85db1 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/Cargo.toml +@@ -0,0 +1,83 @@ ++[package] ++name = "reth-scroll-rpc" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++description = "Ethereum RPC implementation for scroll." ++ ++[lints] ++workspace = true ++ ++[dependencies] ++# reth ++reth-evm.workspace = true ++reth-primitives.workspace = true ++reth-provider.workspace = true ++reth-rpc-eth-api.workspace = true ++reth-rpc-eth-types.workspace = true ++reth-rpc-server-types.workspace = true ++reth-tasks = { workspace = true, features = ["rayon"] } ++reth-transaction-pool.workspace = true ++reth-rpc.workspace = true ++reth-rpc-api.workspace = true ++reth-node-api.workspace = true ++reth-network-api.workspace = true ++reth-node-builder.workspace = true ++reth-chainspec.workspace = true ++ ++#scroll ++reth-scroll-chainspec.workspace = true ++scroll-alloy-rpc-types.workspace = true ++scroll-alloy-network.workspace = true ++scroll-alloy-consensus.workspace = true ++reth-scroll-evm.workspace = true ++ ++# ethereum ++alloy-eips.workspace = true ++alloy-primitives.workspace = true ++alloy-rpc-types-eth.workspace = true ++alloy-rpc-types-debug.workspace = true ++alloy-consensus.workspace = true ++op-alloy-network.workspace = true ++op-alloy-rpc-types.workspace = true ++op-alloy-rpc-types-engine.workspace = true ++op-alloy-consensus.workspace = true ++revm.workspace = true ++ ++# async ++parking_lot.workspace = true ++tokio.workspace = true ++reqwest = { workspace = true, features = ["rustls-tls-native-roots"] } ++ ++# rpc ++jsonrpsee-core.workspace = true ++jsonrpsee-types.workspace = true ++serde_json.workspace = true ++ ++# misc ++thiserror.workspace = true ++tracing.workspace = true ++derive_more = { workspace = true, features = ["constructor", "deref"] } ++ ++[dev-dependencies] ++reth-optimism-chainspec.workspace = true ++ ++[features] ++scroll = [ ++ "reth-primitives/scroll", ++ "reth-provider/scroll", ++ "reth-scroll-evm/scroll", ++ "reth-evm/scroll", ++] ++optimism = [ ++ "reth-primitives/optimism", ++ "reth-provider/optimism", ++ "reth-scroll-evm/optimism", ++ "revm/optimism", ++] ++ ++ ++
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+35
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/error.rs scroll-reth/crates/scroll/rpc/src/error.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..e96fdbafd37bc5cf1e2e11deae78bce1dfbdf030 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/error.rs +@@ -0,0 +1,35 @@ ++//! RPC errors specific to Scroll. ++ ++use alloy_rpc_types_eth::BlockError; ++use reth_rpc_eth_api::AsEthApiError; ++use reth_rpc_eth_types::EthApiError; ++ ++/// Scroll specific errors, that extend [`EthApiError`]. ++#[derive(Debug, thiserror::Error)] ++pub enum ScrollEthApiError { ++ /// L1 ethereum error. ++ #[error(transparent)] ++ Eth(#[from] EthApiError), ++} ++ ++impl AsEthApiError for ScrollEthApiError { ++ fn as_err(&self) -> Option<&EthApiError> { ++ match self { ++ Self::Eth(err) => Some(err), ++ } ++ } ++} ++ ++impl From<ScrollEthApiError> for jsonrpsee_types::error::ErrorObject<'static> { ++ fn from(err: ScrollEthApiError) -> Self { ++ match err { ++ ScrollEthApiError::Eth(err) => err.into(), ++ } ++ } ++} ++ ++impl From<BlockError> for ScrollEthApiError { ++ fn from(error: BlockError) -> Self { ++ Self::Eth(error.into()) ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+75
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/block.rs scroll-reth/crates/scroll/rpc/src/eth/block.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..c05a01e1edbca650afc222a7601444d3c8fb0532 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/block.rs +@@ -0,0 +1,75 @@ ++//! Loads and formats Scroll block RPC response. ++ ++use alloy_consensus::BlockHeader; ++use alloy_rpc_types_eth::BlockId; ++use reth_chainspec::ChainSpecProvider; ++use reth_node_api::BlockBody; ++use reth_primitives::{Receipt, TransactionMeta, TransactionSigned}; ++use reth_provider::{BlockReader, HeaderProvider}; ++use reth_rpc_eth_api::{ ++ helpers::{EthBlocks, LoadBlock, LoadPendingBlock, LoadReceipt, SpawnBlocking}, ++ RpcReceipt, ++}; ++use scroll_alloy_network::Network; ++ ++use reth_scroll_chainspec::ScrollChainSpec; ++use scroll_alloy_rpc_types::ScrollTransactionReceipt; ++ ++use crate::{eth::ScrollNodeCore, ScrollEthApi, ScrollEthApiError, ScrollReceiptBuilder}; ++ ++impl<N> EthBlocks for ScrollEthApi<N> ++where ++ Self: LoadBlock< ++ Error = ScrollEthApiError, ++ NetworkTypes: Network<ReceiptResponse = ScrollTransactionReceipt>, ++ Provider: BlockReader<Receipt = Receipt, Transaction = TransactionSigned>, ++ >, ++ N: ScrollNodeCore<Provider: ChainSpecProvider<ChainSpec = ScrollChainSpec> + HeaderProvider>, ++{ ++ async fn block_receipts( ++ &self, ++ block_id: BlockId, ++ ) -> Result<Option<Vec<RpcReceipt<Self::NetworkTypes>>>, Self::Error> ++ where ++ Self: LoadReceipt, ++ { ++ if let Some((block, receipts)) = self.load_block_and_receipts(block_id).await? { ++ let block_number = block.number(); ++ let base_fee = block.base_fee_per_gas(); ++ let block_hash = block.hash(); ++ let excess_blob_gas = block.excess_blob_gas(); ++ let timestamp = block.timestamp(); ++ ++ return block ++ .body() ++ .transactions() ++ .iter() ++ .zip(receipts.iter()) ++ .enumerate() ++ .map(|(idx, (tx, receipt))| -> Result<_, _> { ++ let meta = TransactionMeta { ++ tx_hash: tx.hash(), ++ index: idx as u64, ++ block_hash, ++ block_number, ++ base_fee, ++ excess_blob_gas, ++ timestamp, ++ }; ++ ScrollReceiptBuilder::new(tx, meta, receipt, &receipts) ++ .map(|builder| builder.build()) ++ }) ++ .collect::<Result<Vec<_>, Self::Error>>() ++ .map(Some) ++ } ++ ++ Ok(None) ++ } ++} ++ ++impl<N> LoadBlock for ScrollEthApi<N> ++where ++ Self: LoadPendingBlock + SpawnBlocking, ++ N: ScrollNodeCore, ++{ ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+110
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/call.rs scroll-reth/crates/scroll/rpc/src/eth/call.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..ea5035171f4b8f6732ffac0da123af4fe843e894 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/call.rs +@@ -0,0 +1,110 @@ ++use alloy_primitives::{TxKind, U256}; ++use alloy_rpc_types_eth::transaction::TransactionRequest; ++use reth_evm::ConfigureEvm; ++use reth_provider::ProviderHeader; ++use reth_rpc_eth_api::{ ++ helpers::{estimate::EstimateCall, Call, EthCall, LoadBlock, LoadState, SpawnBlocking}, ++ FromEthApiError, FullEthApiTypes, IntoEthApiError, ++}; ++use reth_rpc_eth_types::{revm_utils::CallFees, RpcInvalidTransactionError}; ++use revm::primitives::{BlockEnv, TxEnv}; ++ ++use super::ScrollNodeCore; ++use crate::{ScrollEthApi, ScrollEthApiError}; ++ ++impl<N> EthCall for ScrollEthApi<N> ++where ++ Self: EstimateCall + LoadBlock + FullEthApiTypes, ++ N: ScrollNodeCore, ++{ ++} ++ ++impl<N> EstimateCall for ScrollEthApi<N> ++where ++ Self: Call, ++ Self::Error: From<ScrollEthApiError>, ++ N: ScrollNodeCore, ++{ ++} ++ ++impl<N> Call for ScrollEthApi<N> ++where ++ Self: LoadState<Evm: ConfigureEvm<Header = ProviderHeader<Self::Provider>>> + SpawnBlocking, ++ Self::Error: From<ScrollEthApiError>, ++ N: ScrollNodeCore, ++{ ++ #[inline] ++ fn call_gas_limit(&self) -> u64 { ++ self.inner.eth_api.gas_cap() ++ } ++ ++ #[inline] ++ fn max_simulate_blocks(&self) -> u64 { ++ self.inner.eth_api.max_simulate_blocks() ++ } ++ ++ fn create_txn_env( ++ &self, ++ block_env: &BlockEnv, ++ request: TransactionRequest, ++ ) -> Result<TxEnv, Self::Error> { ++ // Ensure that if versioned hashes are set, they're not empty ++ if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) { ++ return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err()) ++ } ++ ++ let TransactionRequest { ++ from, ++ to, ++ gas_price, ++ max_fee_per_gas, ++ max_priority_fee_per_gas, ++ gas, ++ value, ++ input, ++ nonce, ++ access_list, ++ chain_id, ++ blob_versioned_hashes, ++ max_fee_per_blob_gas, ++ authorization_list, ++ .. ++ } = request; ++ ++ let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } = ++ CallFees::ensure_fees( ++ gas_price.map(U256::from), ++ max_fee_per_gas.map(U256::from), ++ max_priority_fee_per_gas.map(U256::from), ++ block_env.basefee, ++ blob_versioned_hashes.as_deref(), ++ max_fee_per_blob_gas.map(U256::from), ++ block_env.get_blob_gasprice().map(U256::from), ++ )?; ++ ++ let gas_limit = gas.unwrap_or_else(|| block_env.gas_limit.min(U256::from(u64::MAX)).to()); ++ ++ let env = TxEnv { ++ gas_limit, ++ nonce, ++ caller: from.unwrap_or_default(), ++ gas_price, ++ gas_priority_fee: max_priority_fee_per_gas, ++ transact_to: to.unwrap_or(TxKind::Create), ++ value: value.unwrap_or_default(), ++ data: input ++ .try_into_unique_input() ++ .map_err(Self::Error::from_eth_err)? ++ .unwrap_or_default(), ++ chain_id, ++ access_list: access_list.unwrap_or_default().into(), ++ // EIP-4844 fields ++ blob_hashes: blob_versioned_hashes.unwrap_or_default(), ++ max_fee_per_blob_gas, ++ authorization_list: authorization_list.map(Into::into), ++ scroll: Default::default(), ++ }; ++ ++ Ok(env) ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+330
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/mod.rs scroll-reth/crates/scroll/rpc/src/eth/mod.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..55241c1a136b8dbcfce9fef62b8a4b0c5d9bb272 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/mod.rs +@@ -0,0 +1,330 @@ ++//! Scroll-Reth `eth_` endpoint implementation. ++ ++use std::{fmt, sync::Arc}; ++ ++use alloy_primitives::U256; ++use reth_chainspec::{EthChainSpec, EthereumHardforks}; ++use reth_evm::ConfigureEvm; ++use reth_network_api::NetworkInfo; ++use reth_node_api::NodePrimitives; ++use reth_node_builder::EthApiBuilderCtx; ++use reth_primitives::EthPrimitives; ++use reth_provider::{ ++ BlockNumReader, BlockReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ++ NodePrimitivesProvider, ProviderBlock, ProviderHeader, ProviderReceipt, ProviderTx, ++ StageCheckpointReader, StateProviderFactory, ++}; ++use reth_rpc::eth::{core::EthApiInner, DevSigner}; ++use reth_rpc_eth_api::{ ++ helpers::{ ++ AddDevSigners, EthApiSpec, EthFees, EthSigner, EthState, LoadBlock, LoadFee, LoadState, ++ SpawnBlocking, Trace, ++ }, ++ EthApiTypes, RpcNodeCore, RpcNodeCoreExt, ++}; ++use reth_rpc_eth_types::{EthStateCache, FeeHistoryCache, GasPriceOracle}; ++use reth_tasks::{ ++ pool::{BlockingTaskGuard, BlockingTaskPool}, ++ TaskSpawner, ++}; ++use reth_transaction_pool::TransactionPool; ++ ++pub use receipt::ScrollReceiptBuilder; ++use scroll_alloy_network::Scroll; ++ ++use crate::ScrollEthApiError; ++ ++mod block; ++mod call; ++mod pending_block; ++pub mod receipt; ++pub mod transaction; ++ ++/// Adapter for [`EthApiInner`], which holds all the data required to serve core `eth_` API. ++pub type EthApiNodeBackend<N> = EthApiInner< ++ <N as RpcNodeCore>::Provider, ++ <N as RpcNodeCore>::Pool, ++ <N as RpcNodeCore>::Network, ++ <N as RpcNodeCore>::Evm, ++>; ++ ++/// A helper trait with requirements for [`RpcNodeCore`] to be used in [`ScrollEthApi`]. ++pub trait ScrollNodeCore: RpcNodeCore<Provider: BlockReader> {} ++impl<T> ScrollNodeCore for T where T: RpcNodeCore<Provider: BlockReader> {} ++ ++/// Scroll-Reth `Eth` API implementation. ++/// ++/// This type provides the functionality for handling `eth_` related requests. ++/// ++/// This wraps a default `Eth` implementation, and provides additional functionality where the ++/// scroll spec deviates from the default (ethereum) spec, e.g. transaction forwarding to the ++/// receipts, additional RPC fields for transaction receipts. ++/// ++/// This type implements the [`FullEthApi`](reth_rpc_eth_api::helpers::FullEthApi) by implemented ++/// all the `Eth` helper traits and prerequisite traits. ++#[derive(Clone)] ++pub struct ScrollEthApi<N: ScrollNodeCore> { ++ /// Gateway to node's core components. ++ inner: Arc<ScrollEthApiInner<N>>, ++} ++ ++impl<N> ScrollEthApi<N> ++where ++ N: ScrollNodeCore< ++ Provider: BlockReaderIdExt ++ + ChainSpecProvider ++ + CanonStateSubscriptions<Primitives = EthPrimitives> ++ + Clone ++ + 'static, ++ >, ++{ ++ /// Returns a reference to the [`EthApiNodeBackend`]. ++ #[allow(clippy::missing_const_for_fn)] ++ pub fn eth_api(&self) -> &EthApiNodeBackend<N> { ++ self.inner.eth_api() ++ } ++ ++ /// Build a [`ScrollEthApi`] using [`ScrollEthApiBuildlmn9 ,ner`]. ++ pub const fn builder() -> ScrollEthApiBuilder { ++ ScrollEthApiBuilder::new() ++ } ++} ++ ++impl<N> EthApiTypes for ScrollEthApi<N> ++where ++ Self: Send + Sync, ++ N: ScrollNodeCore, ++{ ++ type Error = ScrollEthApiError; ++ type NetworkTypes = Scroll; ++ type TransactionCompat = Self; ++ ++ fn tx_resp_builder(&self) -> &Self::TransactionCompat { ++ self ++ } ++} ++ ++impl<N> RpcNodeCore for ScrollEthApi<N> ++where ++ N: ScrollNodeCore, ++{ ++ type Provider = N::Provider; ++ type Pool = N::Pool; ++ type Evm = <N as RpcNodeCore>::Evm; ++ type Network = <N as RpcNodeCore>::Network; ++ type PayloadBuilder = (); ++ ++ #[inline] ++ fn pool(&self) -> &Self::Pool { ++ self.inner.eth_api.pool() ++ } ++ ++ #[inline] ++ fn evm_config(&self) -> &Self::Evm { ++ self.inner.eth_api.evm_config() ++ } ++ ++ #[inline] ++ fn network(&self) -> &Self::Network { ++ self.inner.eth_api.network() ++ } ++ ++ #[inline] ++ fn payload_builder(&self) -> &Self::PayloadBuilder { ++ &() ++ } ++ ++ #[inline] ++ fn provider(&self) -> &Self::Provider { ++ self.inner.eth_api.provider() ++ } ++} ++ ++impl<N> RpcNodeCoreExt for ScrollEthApi<N> ++where ++ N: ScrollNodeCore, ++{ ++ #[inline] ++ fn cache(&self) -> &EthStateCache<ProviderBlock<N::Provider>, ProviderReceipt<N::Provider>> { ++ self.inner.eth_api.cache() ++ } ++} ++ ++impl<N> EthApiSpec for ScrollEthApi<N> ++where ++ N: ScrollNodeCore< ++ Provider: ChainSpecProvider<ChainSpec: EthereumHardforks> ++ + BlockNumReader ++ + StageCheckpointReader, ++ Network: NetworkInfo, ++ >, ++{ ++ type Transaction = ProviderTx<Self::Provider>; ++ ++ #[inline] ++ fn starting_block(&self) -> U256 { ++ self.inner.eth_api.starting_block() ++ } ++ ++ #[inline] ++ fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner<ProviderTx<Self::Provider>>>>> { ++ self.inner.eth_api.signers() ++ } ++} ++ ++impl<N> SpawnBlocking for ScrollEthApi<N> ++where ++ Self: Send + Sync + Clone + 'static, ++ N: ScrollNodeCore, ++{ ++ #[inline] ++ fn io_task_spawner(&self) -> impl TaskSpawner { ++ self.inner.eth_api.task_spawner() ++ } ++ ++ #[inline] ++ fn tracing_task_pool(&self) -> &BlockingTaskPool { ++ self.inner.eth_api.blocking_task_pool() ++ } ++ ++ #[inline] ++ fn tracing_task_guard(&self) -> &BlockingTaskGuard { ++ self.inner.eth_api.blocking_task_guard() ++ } ++} ++ ++impl<N> LoadFee for ScrollEthApi<N> ++where ++ Self: LoadBlock<Provider = N::Provider>, ++ N: ScrollNodeCore< ++ Provider: BlockReaderIdExt ++ + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks> ++ + StateProviderFactory, ++ >, ++{ ++ #[inline] ++ fn gas_oracle(&self) -> &GasPriceOracle<Self::Provider> { ++ self.inner.eth_api.gas_oracle() ++ } ++ ++ #[inline] ++ fn fee_history_cache(&self) -> &FeeHistoryCache { ++ self.inner.eth_api.fee_history_cache() ++ } ++} ++ ++impl<N> LoadState for ScrollEthApi<N> where ++ N: ScrollNodeCore< ++ Provider: StateProviderFactory + ChainSpecProvider<ChainSpec: EthereumHardforks>, ++ Pool: TransactionPool, ++ > ++{ ++} ++ ++impl<N> EthState for ScrollEthApi<N> ++where ++ Self: LoadState + SpawnBlocking, ++ N: ScrollNodeCore, ++{ ++ #[inline] ++ fn max_proof_window(&self) -> u64 { ++ self.inner.eth_api.eth_proof_window() ++ } ++} ++ ++impl<N> EthFees for ScrollEthApi<N> ++where ++ Self: LoadFee, ++ N: ScrollNodeCore, ++{ ++} ++ ++impl<N> Trace for ScrollEthApi<N> ++where ++ Self: RpcNodeCore<Provider: BlockReader> ++ + LoadState< ++ Evm: ConfigureEvm< ++ Header = ProviderHeader<Self::Provider>, ++ Transaction = ProviderTx<Self::Provider>, ++ >, ++ >, ++ N: ScrollNodeCore, ++{ ++} ++ ++impl<N> AddDevSigners for ScrollEthApi<N> ++where ++ N: ScrollNodeCore, ++{ ++ fn with_dev_accounts(&self) { ++ *self.inner.eth_api.signers().write() = DevSigner::random_signers(20) ++ } ++} ++ ++impl<N: ScrollNodeCore> fmt::Debug for ScrollEthApi<N> { ++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ++ f.debug_struct("ScrollEthApi").finish_non_exhaustive() ++ } ++} ++ ++/// Container type `ScrollEthApi` ++#[allow(missing_debug_implementations)] ++struct ScrollEthApiInner<N: ScrollNodeCore> { ++ /// Gateway to node's core components. ++ eth_api: EthApiNodeBackend<N>, ++} ++ ++impl<N: ScrollNodeCore> ScrollEthApiInner<N> { ++ /// Returns a reference to the [`EthApiNodeBackend`]. ++ const fn eth_api(&self) -> &EthApiNodeBackend<N> { ++ &self.eth_api ++ } ++} ++ ++/// A type that knows how to build a [`ScrollEthApi`]. ++#[derive(Debug, Default)] ++pub struct ScrollEthApiBuilder {} ++ ++impl ScrollEthApiBuilder { ++ /// Creates a [`ScrollEthApiBuilder`] instance from [`EthApiBuilderCtx`]. ++ pub const fn new() -> Self { ++ Self {} ++ } ++} ++ ++impl ScrollEthApiBuilder { ++ /// Builds an instance of [`ScrollEthApi`] ++ pub fn build<N>(self, ctx: &EthApiBuilderCtx<N>) -> ScrollEthApi<N> ++ where ++ N: ScrollNodeCore< ++ Provider: BlockReaderIdExt< ++ Block = <<N::Provider as NodePrimitivesProvider>::Primitives as NodePrimitives>::Block, ++ Receipt = <<N::Provider as NodePrimitivesProvider>::Primitives as NodePrimitives>::Receipt, ++ > + ChainSpecProvider ++ + CanonStateSubscriptions ++ + Clone ++ + 'static, ++ >, ++ { ++ let blocking_task_pool = ++ BlockingTaskPool::build().expect("failed to build blocking task pool"); ++ ++ let inner = EthApiInner::new( ++ ctx.provider.clone(), ++ ctx.pool.clone(), ++ ctx.network.clone(), ++ ctx.cache.clone(), ++ ctx.new_gas_price_oracle(), ++ ctx.config.rpc_gas_cap, ++ ctx.config.rpc_max_simulate_blocks, ++ ctx.config.eth_proof_window, ++ blocking_task_pool, ++ ctx.new_fee_history_cache(), ++ ctx.evm_config.clone(), ++ ctx.executor.clone(), ++ ctx.config.proof_permits, ++ ); ++ ++ ScrollEthApi { inner: Arc::new(ScrollEthApiInner { eth_api: inner }) } ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+125
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/pending_block.rs scroll-reth/crates/scroll/rpc/src/eth/pending_block.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..1445775b8fdaa0baf20d705776f4111169d31255 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/pending_block.rs +@@ -0,0 +1,125 @@ ++//! Loads Scroll pending block for an RPC response. ++ ++use alloy_consensus::{ ++ constants::EMPTY_WITHDRAWALS, proofs::calculate_transaction_root, Header, EMPTY_OMMER_ROOT_HASH, ++}; ++use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE}; ++use alloy_primitives::{logs_bloom, B256, U256}; ++use reth_chainspec::{EthChainSpec, EthereumHardforks}; ++use reth_evm::ConfigureEvm; ++use reth_primitives::{proofs::calculate_receipt_root_no_memo, BlockBody, Receipt}; ++use reth_provider::{ ++ BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderHeader, ProviderReceipt, ++ ProviderTx, StateProviderFactory, ++}; ++use reth_rpc_eth_api::{ ++ helpers::{LoadPendingBlock, SpawnBlocking}, ++ EthApiTypes, RpcNodeCore, ++}; ++use reth_rpc_eth_types::PendingBlock; ++use reth_transaction_pool::{PoolTransaction, TransactionPool}; ++use revm::primitives::{BlockEnv, ExecutionResult}; ++use scroll_alloy_network::Network; ++ ++use crate::ScrollEthApi; ++ ++impl<N> LoadPendingBlock for ScrollEthApi<N> ++where ++ Self: SpawnBlocking ++ + EthApiTypes< ++ NetworkTypes: Network< ++ HeaderResponse = alloy_rpc_types_eth::Header<ProviderHeader<Self::Provider>>, ++ >, ++ >, ++ N: RpcNodeCore< ++ Provider: BlockReaderIdExt< ++ Transaction = reth_primitives::TransactionSigned, ++ Block = reth_primitives::Block, ++ Receipt = reth_primitives::Receipt, ++ Header = reth_primitives::Header, ++ > + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks> ++ + StateProviderFactory, ++ Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<N::Provider>>>, ++ Evm: ConfigureEvm< ++ Header = ProviderHeader<Self::Provider>, ++ Transaction = ProviderTx<Self::Provider>, ++ >, ++ >, ++{ ++ #[inline] ++ fn pending_block( ++ &self, ++ ) -> &tokio::sync::Mutex< ++ Option<PendingBlock<ProviderBlock<Self::Provider>, ProviderReceipt<Self::Provider>>>, ++ > { ++ self.inner.eth_api.pending_block() ++ } ++ ++ fn assemble_block( ++ &self, ++ block_env: &BlockEnv, ++ parent_hash: B256, ++ state_root: B256, ++ transactions: Vec<ProviderTx<Self::Provider>>, ++ receipts: &[ProviderReceipt<Self::Provider>], ++ ) -> reth_provider::ProviderBlock<Self::Provider> { ++ let chain_spec = self.provider().chain_spec(); ++ let timestamp = block_env.timestamp.to::<u64>(); ++ ++ let transactions_root = calculate_transaction_root(&transactions); ++ let receipts_root = calculate_receipt_root_no_memo(&receipts.iter().collect::<Vec<_>>()); ++ ++ let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| &r.logs)); ++ let is_cancun = chain_spec.is_cancun_active_at_timestamp(timestamp); ++ let is_prague = chain_spec.is_prague_active_at_timestamp(timestamp); ++ let is_shanghai = chain_spec.is_shanghai_active_at_timestamp(timestamp); ++ ++ let header = Header { ++ parent_hash, ++ ommers_hash: EMPTY_OMMER_ROOT_HASH, ++ beneficiary: block_env.coinbase, ++ state_root, ++ transactions_root, ++ receipts_root, ++ withdrawals_root: (is_shanghai).then_some(EMPTY_WITHDRAWALS), ++ logs_bloom, ++ timestamp, ++ mix_hash: block_env.prevrandao.unwrap_or_default(), ++ nonce: BEACON_NONCE.into(), ++ base_fee_per_gas: Some(block_env.basefee.to::<u64>()), ++ number: block_env.number.to::<u64>(), ++ gas_limit: block_env.gas_limit.to::<u64>(), ++ difficulty: U256::ZERO, ++ gas_used: receipts.last().map(|r| r.cumulative_gas_used).unwrap_or_default(), ++ blob_gas_used: is_cancun.then(|| { ++ transactions.iter().map(|tx| tx.blob_gas_used().unwrap_or_default()).sum::<u64>() ++ }), ++ excess_blob_gas: block_env.get_blob_excess_gas(), ++ extra_data: Default::default(), ++ parent_beacon_block_root: is_cancun.then_some(B256::ZERO), ++ requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), ++ }; ++ ++ // seal the block ++ reth_primitives::Block { ++ header, ++ body: BlockBody { transactions, ommers: vec![], withdrawals: None }, ++ } ++ } ++ ++ fn assemble_receipt( ++ &self, ++ tx: &ProviderTx<Self::Provider>, ++ result: ExecutionResult, ++ cumulative_gas_used: u64, ++ ) -> reth_provider::ProviderReceipt<Self::Provider> { ++ #[allow(clippy::needless_update)] ++ Receipt { ++ tx_type: tx.tx_type(), ++ success: result.is_success(), ++ cumulative_gas_used, ++ logs: result.into_logs(), ++ ..Default::default() ++ } ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+89
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/receipt.rs scroll-reth/crates/scroll/rpc/src/eth/receipt.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..14e25d8b509643ccd451c256d78707cc76c52c0c +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/receipt.rs +@@ -0,0 +1,89 @@ ++//! Loads and formats Scroll receipt RPC response. ++ ++use crate::{ScrollEthApi, ScrollEthApiError}; ++use alloy_rpc_types_eth::{Log, TransactionReceipt}; ++use reth_node_api::{FullNodeComponents, NodeTypes}; ++use reth_primitives::{Receipt, TransactionMeta, TransactionSigned, TxType}; ++use reth_provider::{ReceiptProvider, TransactionsProvider}; ++use reth_rpc_eth_api::{helpers::LoadReceipt, FromEthApiError, RpcReceipt}; ++use reth_rpc_eth_types::{receipt::build_receipt, EthApiError}; ++ ++use reth_scroll_chainspec::ScrollChainSpec; ++use scroll_alloy_consensus::ScrollReceiptEnvelope; ++use scroll_alloy_rpc_types::{ScrollTransactionReceipt, ScrollTransactionReceiptFields}; ++ ++impl<N> LoadReceipt for ScrollEthApi<N> ++where ++ Self: Send + Sync, ++ N: FullNodeComponents<Types: NodeTypes<ChainSpec = ScrollChainSpec>>, ++ Self::Provider: ++ TransactionsProvider<Transaction = TransactionSigned> + ReceiptProvider<Receipt = Receipt>, ++{ ++ async fn build_transaction_receipt( ++ &self, ++ tx: TransactionSigned, ++ meta: TransactionMeta, ++ receipt: Receipt, ++ ) -> Result<RpcReceipt<Self::NetworkTypes>, Self::Error> { ++ let all_receipts = self ++ .inner ++ .eth_api ++ .cache() ++ .get_receipts(meta.block_hash) ++ .await ++ .map_err(Self::Error::from_eth_err)? ++ .ok_or(Self::Error::from_eth_err(EthApiError::HeaderNotFound( ++ meta.block_hash.into(), ++ )))?; ++ ++ Ok(ScrollReceiptBuilder::new(&tx, meta, &receipt, &all_receipts)?.build()) ++ } ++} ++ ++/// Builds an [`ScrollTransactionReceipt`]. ++#[derive(Debug)] ++pub struct ScrollReceiptBuilder { ++ /// Core receipt, has all the fields of an L1 receipt and is the basis for the Scroll receipt. ++ pub core_receipt: TransactionReceipt<ScrollReceiptEnvelope<Log>>, ++ /// Additional Scroll receipt fields. ++ pub scroll_receipt_fields: ScrollTransactionReceiptFields, ++} ++ ++impl ScrollReceiptBuilder { ++ /// Returns a new builder. ++ pub fn new( ++ transaction: &TransactionSigned, ++ meta: TransactionMeta, ++ receipt: &Receipt, ++ all_receipts: &[Receipt], ++ ) -> Result<Self, ScrollEthApiError> { ++ let core_receipt = ++ build_receipt(transaction, meta, receipt, all_receipts, |receipt_with_bloom| { ++ match receipt.tx_type { ++ TxType::Legacy => ScrollReceiptEnvelope::<Log>::Legacy(receipt_with_bloom), ++ TxType::Eip2930 => ScrollReceiptEnvelope::<Log>::Eip2930(receipt_with_bloom), ++ TxType::Eip1559 => ScrollReceiptEnvelope::<Log>::Eip1559(receipt_with_bloom), ++ TxType::Eip4844 => unimplemented!("eip4844 unsupported"), ++ TxType::Eip7702 => unimplemented!("eip7702 unsupported"), ++ TxType::L1Message => { ++ ScrollReceiptEnvelope::<Log>::L1Message(receipt_with_bloom) ++ } ++ } ++ })?; ++ ++ let scroll_receipt_fields = ++ ScrollTransactionReceiptFields { l1_fee: Some(receipt.l1_fee.saturating_to()) }; ++ ++ Ok(Self { core_receipt, scroll_receipt_fields }) ++ } ++ ++ /// Builds [`ScrollTransactionReceipt`] by combing core (l1) receipt fields and additional ++ /// Scroll receipt fields. ++ pub fn build(self) -> ScrollTransactionReceipt { ++ let Self { core_receipt: inner, scroll_receipt_fields } = self; ++ ++ let ScrollTransactionReceiptFields { l1_fee, .. } = scroll_receipt_fields; ++ ++ ScrollTransactionReceipt { inner, l1_fee } ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+154
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/eth/transaction.rs scroll-reth/crates/scroll/rpc/src/eth/transaction.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..d817ddc1da28dbf520c09003394a3991bf88e880 +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/eth/transaction.rs +@@ -0,0 +1,154 @@ ++//! Loads and formats Scroll transaction RPC response. ++ ++use alloy_consensus::{Signed, Transaction as _}; ++use alloy_primitives::{Bytes, PrimitiveSignature as Signature, Sealable, Sealed, B256}; ++use alloy_rpc_types_eth::TransactionInfo; ++use reth_node_api::FullNodeComponents; ++use reth_primitives::{RecoveredTx, TransactionSigned}; ++use reth_provider::{ ++ BlockReader, BlockReaderIdExt, ProviderTx, ReceiptProvider, TransactionsProvider, ++}; ++use reth_rpc_eth_api::{ ++ helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking}, ++ FromEthApiError, FullEthApiTypes, RpcNodeCore, RpcNodeCoreExt, TransactionCompat, ++}; ++use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError}; ++use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool}; ++ ++use scroll_alloy_consensus::ScrollTxEnvelope; ++use scroll_alloy_rpc_types::Transaction; ++ ++use crate::{eth::ScrollNodeCore, ScrollEthApi, ScrollEthApiError}; ++ ++impl<N> EthTransactions for ScrollEthApi<N> ++where ++ Self: LoadTransaction<Provider: BlockReaderIdExt>, ++ N: ScrollNodeCore<Provider: BlockReader<Transaction = ProviderTx<Self::Provider>>>, ++{ ++ fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner<ProviderTx<Self::Provider>>>>> { ++ self.inner.eth_api.signers() ++ } ++ ++ /// Decodes and recovers the transaction and submits it to the pool. ++ /// ++ /// Returns the hash of the transaction. ++ async fn send_raw_transaction(&self, tx: Bytes) -> Result<B256, Self::Error> { ++ let recovered = recover_raw_transaction(&tx)?; ++ let pool_transaction = <Self::Pool as TransactionPool>::Transaction::from_pooled(recovered); ++ ++ // submit the transaction to the pool with a `Local` origin ++ let hash = self ++ .pool() ++ .add_transaction(TransactionOrigin::Local, pool_transaction) ++ .await ++ .map_err(Self::Error::from_eth_err)?; ++ ++ Ok(hash) ++ } ++} ++ ++impl<N> LoadTransaction for ScrollEthApi<N> ++where ++ Self: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt, ++ N: ScrollNodeCore<Provider: TransactionsProvider, Pool: TransactionPool>, ++ Self::Pool: TransactionPool, ++{ ++} ++ ++impl<N> TransactionCompat for ScrollEthApi<N> ++where ++ N: FullNodeComponents<Provider: ReceiptProvider<Receipt = reth_primitives::Receipt>>, ++{ ++ type Transaction = Transaction; ++ type Error = ScrollEthApiError; ++ ++ fn fill( ++ &self, ++ tx: RecoveredTx<TransactionSigned>, ++ tx_info: TransactionInfo, ++ ) -> Result<Self::Transaction, Self::Error> { ++ let from = tx.signer(); ++ let hash = tx.hash(); ++ let TransactionSigned { transaction, signature, .. } = tx.into_tx(); ++ ++ let inner = match transaction { ++ reth_primitives::Transaction::Legacy(tx) => { ++ Signed::new_unchecked(tx, signature, hash).into() ++ } ++ reth_primitives::Transaction::Eip2930(tx) => { ++ Signed::new_unchecked(tx, signature, hash).into() ++ } ++ reth_primitives::Transaction::Eip1559(tx) => { ++ Signed::new_unchecked(tx, signature, hash).into() ++ } ++ reth_primitives::Transaction::Eip4844(_) | reth_primitives::Transaction::Eip7702(_) => { ++ unreachable!() ++ } ++ reth_primitives::Transaction::L1Message(tx) => { ++ ScrollTxEnvelope::L1Message(tx.seal_unchecked(hash)) ++ } ++ }; ++ ++ let TransactionInfo { ++ block_hash, block_number, index: transaction_index, base_fee, .. ++ } = tx_info; ++ ++ let effective_gas_price = if inner.is_l1_message() { ++ // For l1 message, we must always set the `gasPrice` field to 0 in rpc ++ // l1 message tx don't have a gas price field, but serde of `Transaction` will take care ++ // of it ++ 0 ++ } else { ++ base_fee ++ .map(|base_fee| { ++ inner.effective_tip_per_gas(base_fee as u64).unwrap_or_default() + base_fee ++ }) ++ .unwrap_or_else(|| inner.max_fee_per_gas()) ++ }; ++ ++ Ok(Transaction { ++ inner: alloy_rpc_types_eth::Transaction { ++ inner, ++ block_hash, ++ block_number, ++ transaction_index, ++ from, ++ effective_gas_price: Some(effective_gas_price), ++ }, ++ }) ++ } ++ ++ fn build_simulate_v1_transaction( ++ &self, ++ request: alloy_rpc_types_eth::TransactionRequest, ++ ) -> Result<TransactionSigned, Self::Error> { ++ let Ok(tx) = request.build_typed_tx() else { ++ return Err(ScrollEthApiError::Eth(EthApiError::TransactionConversionError)) ++ }; ++ ++ // Create an empty signature for the transaction. ++ let signature = Signature::new(Default::default(), Default::default(), false); ++ Ok(TransactionSigned::new_unhashed(tx.into(), signature)) ++ } ++ ++ fn otterscan_api_truncate_input(tx: &mut Self::Transaction) { ++ let input = match &mut tx.inner.inner { ++ ScrollTxEnvelope::Eip1559(tx) => &mut tx.tx_mut().input, ++ ScrollTxEnvelope::Eip2930(tx) => &mut tx.tx_mut().input, ++ ScrollTxEnvelope::Legacy(tx) => &mut tx.tx_mut().input, ++ ScrollTxEnvelope::L1Message(tx) => { ++ let (mut deposit, hash) = std::mem::replace( ++ tx, ++ Sealed::new_unchecked(Default::default(), Default::default()), ++ ) ++ .split(); ++ deposit.input = deposit.input.slice(..4); ++ let mut deposit = deposit.seal_unchecked(hash); ++ std::mem::swap(tx, &mut deposit); ++ return ++ } ++ _ => return, ++ }; ++ *input = input.slice(..4); ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+18
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/rpc/src/lib.rs scroll-reth/crates/scroll/rpc/src/lib.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..7464ea0192c814ebe9380872ba401a6fa1e6d3df +--- /dev/null ++++ scroll-reth/crates/scroll/rpc/src/lib.rs +@@ -0,0 +1,18 @@ ++//! Scroll-Reth RPC support. ++ ++#![doc( ++ html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", ++ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", ++ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" ++)] ++#![cfg_attr(all(not(test), feature = "scroll"), warn(unused_crate_dependencies))] ++#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] ++// The `optimism` feature must be enabled to use this crate. ++#![cfg(all(feature = "scroll", not(feature = "optimism")))] ++ ++pub mod error; ++pub mod eth; ++ ++pub use error::ScrollEthApiError; ++pub use eth::{ScrollEthApi, ScrollReceiptBuilder};
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+29
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/Cargo.toml scroll-reth/crates/scroll/trie/Cargo.toml +new file mode 100644 +index 0000000000000000000000000000000000000000..e644b31108f58a2dd8f825a15a88384c4108a310 +--- /dev/null ++++ scroll-reth/crates/scroll/trie/Cargo.toml +@@ -0,0 +1,30 @@ ++[package] ++name = "reth-scroll-trie" ++version.workspace = true ++edition.workspace = true ++rust-version.workspace = true ++license.workspace = true ++homepage.workspace = true ++repository.workspace = true ++exclude.workspace = true ++ ++[lints] ++workspace = true ++ ++[dependencies] ++# reth ++reth-trie.workspace = true ++ ++# alloy ++alloy-primitives.workspace = true ++alloy-trie = { workspace = true, features = ["serde"] } ++ ++# misc ++poseidon-bn254 = { workspace = true, features = ["bn254"] } ++tracing.workspace = true ++ ++[dev-dependencies] ++proptest-arbitrary-interop.workspace = true ++ ++[features] ++scroll = ["reth-trie/scroll"] +\ No newline at end of file
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+4
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/README.md scroll-reth/crates/scroll/trie/README.md +new file mode 100644 +index 0000000000000000000000000000000000000000..8645b5efadd58f51d8d04146614741788a95febd +--- /dev/null ++++ scroll-reth/crates/scroll/trie/README.md +@@ -0,0 +1,5 @@ ++# scroll-trie ++ ++Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for prefix-sorted bits. ++ ++Please see the specification of zktrie [here](assets/zktrie.md). +\ No newline at end of file
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ + (binary file) + +
+
+
diff --git reth/crates/scroll/trie/assets/arch.png scroll-reth/crates/scroll/trie/assets/arch.png +new file mode 100644 +index 0000000000000000000000000000000000000000..aca3f8d0c5ce4eedb5d40b8dedb39d226e57efbe +Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/arch.png differ
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ + (binary file) + +
+
+
diff --git reth/crates/scroll/trie/assets/deletion.png scroll-reth/crates/scroll/trie/assets/deletion.png +new file mode 100644 +index 0000000000000000000000000000000000000000..6c699226e0996a30199a3ff4d2441700390cb8e2 +Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/deletion.png differ
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ + (binary file) + +
+
+
diff --git reth/crates/scroll/trie/assets/insertion.png scroll-reth/crates/scroll/trie/assets/insertion.png +new file mode 100644 +index 0000000000000000000000000000000000000000..942338a07f2ea46e33fc899fd222b6ae6671f5e4 +Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/insertion.png differ
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+186
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/assets/zktrie.md scroll-reth/crates/scroll/trie/assets/zktrie.md +new file mode 100644 +index 0000000000000000000000000000000000000000..b86e1a016ab8bae8e13f7e1b9f7fe1d29bfc7711 +--- /dev/null ++++ scroll-reth/crates/scroll/trie/assets/zktrie.md +@@ -0,0 +1,186 @@ ++# zkTrie Spec ++ ++## 1. Tree Structure ++ ++<figure> ++<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/arch.png" alt="zkTrie Structure" style="width:80%"> ++<figcaption align = "center"><b>Figure 1. zkTrie Structure</b></figcaption> ++</figure> ++ ++In essence, zkTrie is a sparse binary Merkle Patricia Trie, depicted in the above figure. ++Before diving into the Sparse Binary Merkle Patricia Trie, let's briefly touch on Merkle Trees and Patricia Tries. ++* **Merkle Tree**: A Merkle Tree is a tree where each leaf node represents a hash of a data block, and each non-leaf node represents the hash of its child nodes. ++* **Patricia Trie**: A Patricia Trie is a type of radix tree or compressed trie used to store key-value pairs efficiently. It encodes the nodes with same prefix of the key to share the common path, where the path is determined by the value of the node key. ++ ++As illustrated in the Figure 1, there are three types of nodes in the zkTrie. ++- Parent Node (type: 0): Given the zkTrie is a binary tree, a parent node has two children. ++- Leaf Node (type: 1): A leaf node holds the data of a key-value pair. ++- Empty Node (type: 2): An empty node is a special type of node, indicating the sub-trie that shares the same prefix is empty. ++ ++In zkTrie, we use Poseidon hash to compute the node hash because it's more friendly and efficient to prove it in the zk circuit. ++ ++## 2. Tree Construction ++ ++Given a key-value pair, we first compute a *secure key* for the corresponding leaf node by hashing the original key (i.e., account address and storage key) using the Poseidon hash function. This can make the key uniformly distributed over the key space. The node key hashing method is described in the [Node Hashing](#3-node-hashing) section below. ++ ++We then encode the path of a new leaf node by traversing the secure key from Least Significant Bit (LSB) to the Most Significant Bit (MSB). At each step, if the bit is 0, we will traverse to the left child; otherwise, traverse to the right child. ++ ++We limit the maximum depth of zkTrie to 248, meaning that the tree will only traverse the lower 248 bits of the key. This is because the secure key space is a finite field used by Poseidon hash that doesn't occupy the full range of power of 2. This leads to an ambiguous bit representation of the key in a finite field and thus causes a soundness issue in the zk circuit. But if we truncate the key to lower 248 bits, the key space can fully occupy the range of $2^{248}$ and won't have the ambiguity in the bit representation. ++ ++We also apply an optimization to reduce the tree depth by contracting a subtree that has only one leaf node to a single leaf node. For example, in the Figure 1, the tree has three nodes in total, with keys `0100`, `0010`, and `1010`. Because there is only one node that has key with suffix `00`, the leaf node for key `0100` only traverses the suffix `00` and doesn't fully expand its key which would have resulted in depth of 4. ++ ++## 3. Node Hashing ++ ++In this section, we will describe how leaf secure key and node merkle hash are computed. We use Poseidon hash in both hashing computation, denoted as `h` in the doc below. ++ ++<aside> ++💡 Note: We use `init_state = 0` in the Poseidon hash function for all use cases in the zkTrie. ++</aside> ++ ++### 3.1 Empty Node ++ ++The node hash of an empty node is 0. ++ ++### 3.2 Parent Node ++ ++The parent node hash is computed as follows ++ ++```go ++parentNodeHash = h(leftChildHash, rightChildHash) ++``` ++ ++### 3.3 Leaf Node ++ ++The node hash of a leaf node is computed as follows ++ ++```go ++leafNodeHash = h(h(1, nodeKey), valueHash) ++``` ++ ++The leaf node can hold two types of values: Ethereum accounts and storage key-value pairs. Next, we will describe how the node key and value hash are computed for each leaf node type. ++ ++#### Ethereum Account Leaf Node ++For an Ethereum Account Leaf Node, it consists of an Ethereum address and a state account struct. The secure key is derived from the Ethereum address. ++``` ++address[0:20] (20 bytes in big-endian) ++valHi = address[0:16] ++valLo = address[16:20] * 2^96 (padding 12 bytes of 0 at the end) ++nodeKey = h(valHi, valLo) ++``` ++ ++A state account struct in the Scroll consists of the following fields (`Fr` indicates the finite field used in Poseidon hash and is a 254-bit value) ++ ++- `Nonce`: u64 ++- `Balance`: u256, but treated as Fr ++- `StorageRoot`: Fr ++- `KeccakCodeHash`: u256 ++- `PoseidonCodeHash`: Fr ++- `CodeSize`: u64 ++ ++Before computing the value hash, the state account is first marshaled into a list of `u256` values. The marshaling scheme is ++ ++``` ++(The following scheme assumes the big-endian encoding) ++[0:32] (bytes in big-endian) ++ [0:16] Reserved with all 0 ++ [16:24] CodeSize, uint64 in big-endian ++ [24:32] Nonce, uint64 in big-endian ++[32:64] Balance ++[64:96] StorageRoot ++[96:128] KeccakCodeHash ++[128:160] PoseidonCodehash ++(total 160 bytes) ++``` ++ ++The marshal function also returns a `flag` value along with a vector of `u256` values. The `flag` is a bitmap that indicates whether a `u256` value CANNOT be treated as a field element (Fr). The `flag` value for state account is 8, shown below. ++ ++``` +++--------------------+---------+------+----------+----------+ ++| 0 | 1 | 2 | 3 | 4 | (index) +++--------------------+---------+------+----------+----------+ ++| nonce||codesize||0 | balance | root | keccak | poseidon | (u256) +++--------------------+---------+------+----------+----------+ ++| 0 | 0 | 0 | 1 | 0 | (flag bits) +++--------------------+---------+------+----------+----------+ ++(LSB) (MSB) ++``` ++ ++The value hash is computed in two steps: ++1. Convert the value that cannot be represented as a field element of the Poseidon hash to the field element. ++2. Combine field elements in a binary tree structure till the tree root is treated as the value hash. ++ ++In the first step, when the bit in the `flag` is 1 indicating the `u256` value that cannot be treated as a field element, we split the value into a high-128bit value and a low-128bit value, and then pass them to a Poseidon hash to derive a field element value, `h(valueHi, valueLo)`. ++ ++Based on the definition, the value hash of the state account is computed as follows. ++ ++``` ++valueHash = ++h( ++ h( ++ h(nonce||codesize||0, balance), ++ h( ++ storageRoot, ++ h(keccakCodeHash[0:16], keccakCodeHash[16:32]), // convert Keccak codehash to a field element ++ ), ++ ), ++ poseidonCodeHash, ++) ++``` ++ ++#### Storage Leaf Node ++ ++For a Storage Leaf Node, it is a key-value pair, which both are a `u256` value. The secure key of this leaf node is derived from the storage key. ++ ++``` ++storageKey[0:32] (32 bytes in big-endian) ++valHi = storageKey[0:16] ++valLo = storageKey[16:32] ++nodeKey = h(valHi, valLo) ++``` ++ ++The storage value is a `u256` value. The `flag` for the storage value is 1, showed below. ++ ++``` +++--------------+ ++| 0 | (index) +++--------------+ ++| storageValue | (u256) +++--------------+ ++| 1 | (flag bits) +++--------------+ ++``` ++ ++The value hash is computed as follows ++ ++```go ++valueHash = h(storageValue[0:16], storageValue[16:32]) ++``` ++ ++## 4. Tree Operations ++ ++### 4.1 Insertion ++ ++<figure> ++<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/insertion.png" alt="zkTrie Structure" style="width:80%"> ++<figcaption align = "center"><b>Figure 2. Insert a new leaf node to zkTrie</b></figcaption> ++</figure> ++ ++When we insert a new leaf node to the existing zkTrie, there could be two cases illustrated in the Figure 2. ++ ++1. When traversing the path of the node key, it reaches an empty node (Figure 2(b)). In this case, we just need to replace this empty node by this leaf node and backtrace the path to update the merkle hash of parent nodes till the root. ++2. When traversing the path of the node key, it reaches another leaf node `b` (Figure 2(c)). In this case, we need to push down the existing leaf node `b` until the next bit in the node keys of two leaf nodes differs. At each push-down step, we need to insert an empty sibling node when necessary. When we reach the level where the bits differ, we then place two leaf nodes `b` and `c` as the left child and the right child depending on their bits. At last, we backtrace the path and update the merkle hash of all parent nodes. ++ ++### 4.2 Deletion ++ ++<figure> ++<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/deletion.png" alt="zkTrie Structure" style="width:80%"> ++<figcaption align = "center"><b>Figure 3. Delete a leaf node from the zkTrie</b></figcaption> ++</figure> ++ ++ ++The deletion of a leaf node is similar to the insertion. There are two cases illustrated in the Figure 3. ++ ++1. The sibling node of to-be-deleted leaf node is a parent node (Figure 3(b)). In this case, we can just replace the node `a` by an empty node and update the node hash of its ancestors till the root node. ++2. The node of to-be-deleted leaf node is a leaf node (Figure 3(c)). Similarly, we first replace the leaf node by an empty node and start to contract its sibling node upwards until its sibling node is not an empty node. For example, in Figure 3(c), we first replace the leaf node `b` by an empty node. During the contraction, since the sibling of node `c` now becomes an empty node, we move node `c` one level upward to replace its parent node. The new sibling of node `c`, node `e`, is still an empty node. So again we move node `c` upward. Now that the sibling of node `c` is node `a`, the deletion process is finished. ++ ++Note that the sibling of a leaf node in a valid zkTrie cannot be an empty node. Otherwise, we should always prune the subtree and move the leaf node upwards.
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+155
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/branch.rs scroll-reth/crates/scroll/trie/src/branch.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..e6b433913d53e71f2db801ae4c89b3a6f975041c +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/branch.rs +@@ -0,0 +1,155 @@ ++use super::{ ++ BRANCH_NODE_LBRB_DOMAIN, BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN, ++ BRANCH_NODE_LTRT_DOMAIN, ++}; ++use alloy_primitives::{hex, B256}; ++use alloy_trie::TrieMask; ++use core::{fmt, ops::Range, slice::Iter}; ++use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; ++ ++#[allow(unused_imports)] ++use alloc::vec::Vec; ++ ++/// The range of valid child indexes. ++pub(crate) const CHILD_INDEX_RANGE: Range<u8> = 0..2; ++ ++/// A trie mask to extract the two child indexes from a branch node. ++pub(crate) const CHILD_INDEX_MASK: TrieMask = TrieMask::new(0b11); ++ ++/// A reference to branch node and its state mask. ++/// NOTE: The stack may contain more items that specified in the state mask. ++#[derive(Clone)] ++pub(crate) struct BranchNodeRef<'a> { ++ /// Reference to the collection of hash nodes. ++ /// NOTE: The referenced stack might have more items than the number of children ++ /// for this node. We should only ever access items starting from ++ /// [`BranchNodeRef::first_child_index`]. ++ pub stack: &'a [B256], ++ /// Reference to bitmask indicating the presence of children at ++ /// the respective nibble positions. ++ pub state_mask: TrieMask, ++} ++ ++impl fmt::Debug for BranchNodeRef<'_> { ++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ++ f.debug_struct("BranchNodeRef") ++ .field("stack", &self.stack.iter().map(hex::encode).collect::<Vec<_>>()) ++ .field("state_mask", &self.state_mask) ++ .field("first_child_index", &self.first_child_index()) ++ .finish() ++ } ++} ++ ++impl<'a> BranchNodeRef<'a> { ++ /// Create a new branch node from the stack of nodes. ++ #[inline] ++ pub(crate) const fn new(stack: &'a [B256], state_mask: TrieMask) -> Self { ++ Self { stack, state_mask } ++ } ++ ++ /// Returns the stack index of the first child for this node. ++ /// ++ /// # Panics ++ /// ++ /// If the stack length is less than number of children specified in state mask. ++ /// Means that the node is in inconsistent state. ++ #[inline] ++ pub(crate) fn first_child_index(&self) -> usize { ++ self.stack ++ .len() ++ .checked_sub((self.state_mask & CHILD_INDEX_MASK).count_ones() as usize) ++ .expect("branch node stack is in inconsistent state") ++ } ++ ++ #[inline] ++ fn children(&self) -> impl Iterator<Item = (u8, Option<&B256>)> + '_ { ++ BranchChildrenIter::new(self) ++ } ++ ++ /// Given the hash mask of children, return an iterator over stack items ++ /// that match the mask. ++ #[inline] ++ pub(crate) fn child_hashes(&self, hash_mask: TrieMask) -> impl Iterator<Item = B256> + '_ { ++ self.children() ++ .filter_map(|(i, c)| c.map(|c| (i, c))) ++ .filter(move |(index, _)| hash_mask.is_bit_set(*index)) ++ .map(|(_, child)| B256::from_slice(&child[..])) ++ } ++ ++ pub(crate) fn hash(&self) -> B256 { ++ let mut children_iter = self.children(); ++ ++ let left_child = children_iter ++ .next() ++ .map(|(_, c)| *c.unwrap_or_default()) ++ .expect("branch node has two children"); ++ let left_child = ++ Fr::from_repr_vartime(left_child.0).expect("left child is a valid field element"); ++ let right_child = children_iter ++ .next() ++ .map(|(_, c)| *c.unwrap_or_default()) ++ .expect("branch node has two children"); ++ let right_child = ++ Fr::from_repr_vartime(right_child.0).expect("right child is a valid field element"); ++ ++ hash_with_domain(&[left_child, right_child], self.hashing_domain()).to_repr().into() ++ } ++ ++ fn hashing_domain(&self) -> Fr { ++ match *self.state_mask { ++ 0b1011 => BRANCH_NODE_LBRT_DOMAIN, ++ 0b1111 => BRANCH_NODE_LTRT_DOMAIN, ++ 0b0111 => BRANCH_NODE_LTRB_DOMAIN, ++ 0b0011 => BRANCH_NODE_LBRB_DOMAIN, ++ _ => unreachable!("invalid branch node state mask"), ++ } ++ } ++} ++ ++/// Iterator over branch node children. ++#[derive(Debug)] ++struct BranchChildrenIter<'a> { ++ range: Range<u8>, ++ state_mask: TrieMask, ++ stack_iter: Iter<'a, B256>, ++} ++ ++impl<'a> BranchChildrenIter<'a> { ++ /// Create new iterator over branch node children. ++ fn new(node: &BranchNodeRef<'a>) -> Self { ++ Self { ++ range: CHILD_INDEX_RANGE, ++ state_mask: node.state_mask, ++ stack_iter: node.stack[node.first_child_index()..].iter(), ++ } ++ } ++} ++ ++impl<'a> Iterator for BranchChildrenIter<'a> { ++ type Item = (u8, Option<&'a B256>); ++ ++ #[inline] ++ fn next(&mut self) -> Option<Self::Item> { ++ let i = self.range.next()?; ++ let value = self ++ .state_mask ++ .is_bit_set(i) ++ .then(|| unsafe { self.stack_iter.next().unwrap_unchecked() }); ++ Some((i, value)) ++ } ++ ++ #[inline] ++ fn size_hint(&self) -> (usize, Option<usize>) { ++ let len = self.len(); ++ (len, Some(len)) ++ } ++} ++ ++impl core::iter::FusedIterator for BranchChildrenIter<'_> {} ++ ++impl ExactSizeIterator for BranchChildrenIter<'_> { ++ #[inline] ++ fn len(&self) -> usize { ++ self.range.len() ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+4
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/constants.rs scroll-reth/crates/scroll/trie/src/constants.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..67bd414f835834ecca93f29497a396384c87f84e +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/constants.rs +@@ -0,0 +1,4 @@ ++use alloy_primitives::B256; ++ ++/// The root hash of an empty binary Merle Patricia trie. ++pub const EMPTY_ROOT_HASH: B256 = B256::ZERO;
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+568
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/hash_builder.rs scroll-reth/crates/scroll/trie/src/hash_builder.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..60bdf14087f523e9857e82d884fb034599ceb6d8 +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/hash_builder.rs +@@ -0,0 +1,568 @@ ++use crate::{ ++ branch::{BranchNodeRef, CHILD_INDEX_MASK}, ++ constants::EMPTY_ROOT_HASH, ++ leaf::HashLeaf, ++ sub_tree::SubTreeRef, ++}; ++use alloy_primitives::{map::HashMap, B256}; ++use alloy_trie::{ ++ hash_builder::{HashBuilderValue, HashBuilderValueRef}, ++ nodes::LeafNodeRef, ++ proof::{ProofNodes, ProofRetainer}, ++ BranchNodeCompact, Nibbles, TrieMask, ++}; ++use core::cmp; ++use tracing::trace; ++ ++#[derive(Debug, Default)] ++#[allow(missing_docs)] ++pub struct HashBuilder { ++ pub key: Nibbles, ++ pub value: HashBuilderValue, ++ pub stack: Vec<B256>, ++ ++ // TODO(scroll): Introduce terminator / leaf masks ++ pub state_masks: Vec<TrieMask>, ++ pub tree_masks: Vec<TrieMask>, ++ pub hash_masks: Vec<TrieMask>, ++ ++ pub stored_in_database: bool, ++ ++ pub updated_branch_nodes: Option<HashMap<Nibbles, BranchNodeCompact>>, ++ pub proof_retainer: Option<ProofRetainer>, ++} ++ ++impl HashBuilder { ++ /// Enables the Hash Builder to store updated branch nodes. ++ /// ++ /// Call [`HashBuilder::split`] to get the updates to branch nodes. ++ pub fn with_updates(mut self, retain_updates: bool) -> Self { ++ self.set_updates(retain_updates); ++ self ++ } ++ ++ /// Enable specified proof retainer. ++ pub fn with_proof_retainer(mut self, retainer: ProofRetainer) -> Self { ++ self.proof_retainer = Some(retainer); ++ self ++ } ++ ++ /// Enables the Hash Builder to store updated branch nodes. ++ /// ++ /// Call [`HashBuilder::split`] to get the updates to branch nodes. ++ pub fn set_updates(&mut self, retain_updates: bool) { ++ if retain_updates { ++ self.updated_branch_nodes = Some(HashMap::default()); ++ } ++ } ++ ++ /// Splits the [`HashBuilder`] into a [`HashBuilder`] and hash builder updates. ++ pub fn split(mut self) -> (Self, HashMap<Nibbles, BranchNodeCompact>) { ++ let updates = self.updated_branch_nodes.take(); ++ (self, updates.unwrap_or_default()) ++ } ++ ++ /// Take and return retained proof nodes. ++ pub fn take_proof_nodes(&mut self) -> ProofNodes { ++ self.proof_retainer.take().map(ProofRetainer::into_proof_nodes).unwrap_or_default() ++ } ++ ++ /// The number of total updates accrued. ++ /// Returns `0` if [`Self::with_updates`] was not called. ++ pub fn updates_len(&self) -> usize { ++ self.updated_branch_nodes.as_ref().map(|u| u.len()).unwrap_or(0) ++ } ++ ++ /// Print the current stack of the Hash Builder. ++ pub fn print_stack(&self) { ++ println!("============ STACK ==============="); ++ for item in &self.stack { ++ println!("{}", alloy_primitives::hex::encode(item)); ++ } ++ println!("============ END STACK ==============="); ++ } ++ ++ /// Adds a new leaf element and its value to the trie hash builder. ++ pub fn add_leaf(&mut self, key: Nibbles, value: &[u8]) { ++ assert!(key > self.key, "add_leaf key {:?} self.key {:?}", key, self.key); ++ if !self.key.is_empty() { ++ self.update(&key); ++ } ++ self.set_key_value(key, HashBuilderValueRef::Bytes(value)); ++ } ++ ++ /// Adds a new branch element and its hash to the trie hash builder. ++ pub fn add_branch(&mut self, key: Nibbles, value: B256, stored_in_database: bool) { ++ assert!( ++ key > self.key || (self.key.is_empty() && key.is_empty()), ++ "add_branch key {:?} self.key {:?}", ++ key, ++ self.key ++ ); ++ if !self.key.is_empty() { ++ self.update(&key); ++ } else if key.is_empty() { ++ self.stack.push(value); ++ } ++ self.set_key_value(key, HashBuilderValueRef::Hash(&value)); ++ self.stored_in_database = stored_in_database; ++ } ++ ++ /// Returns the current root hash of the trie builder. ++ pub fn root(&mut self) -> B256 { ++ // Clears the internal state ++ if !self.key.is_empty() { ++ self.update(&Nibbles::default()); ++ self.key.clear(); ++ self.value.clear(); ++ } ++ let root = self.current_root(); ++ if root == EMPTY_ROOT_HASH { ++ if let Some(proof_retainer) = self.proof_retainer.as_mut() { ++ proof_retainer.retain(&Nibbles::default(), &[]) ++ } ++ } ++ root ++ } ++ ++ #[inline] ++ fn set_key_value(&mut self, key: Nibbles, value: HashBuilderValueRef<'_>) { ++ self.log_key_value("old value"); ++ self.key = key; ++ self.value.set_from_ref(value); ++ self.log_key_value("new value"); ++ } ++ ++ fn log_key_value(&self, msg: &str) { ++ trace!(target: "trie::hash_builder", ++ key = ?self.key, ++ value = ?self.value, ++ "{msg}", ++ ); ++ } ++ ++ fn current_root(&self) -> B256 { ++ if let Some(node_ref) = self.stack.last() { ++ let mut root = *node_ref; ++ root.reverse(); ++ root ++ } else { ++ EMPTY_ROOT_HASH ++ } ++ } ++ ++ /// Given a new element, it appends it to the stack and proceeds to loop through the stack state ++ /// and convert the nodes it can into branch / extension nodes and hash them. This ensures ++ /// that the top of the stack always contains the merkle root corresponding to the trie ++ /// built so far. ++ fn update(&mut self, succeeding: &Nibbles) { ++ let mut build_extensions = false; ++ // current / self.key is always the latest added element in the trie ++ let mut current = self.key.clone(); ++ debug_assert!(!current.is_empty()); ++ ++ trace!(target: "trie::hash_builder", ?current, ?succeeding, "updating merkle tree"); ++ ++ let mut i = 0usize; ++ loop { ++ let _span = tracing::trace_span!(target: "trie::hash_builder", "loop", i, ?current, build_extensions).entered(); ++ ++ let preceding_exists = !self.state_masks.is_empty(); ++ let preceding_len = self.state_masks.len().saturating_sub(1); ++ ++ let common_prefix_len = succeeding.common_prefix_length(current.as_slice()); ++ let len = cmp::max(preceding_len, common_prefix_len); ++ assert!(len < current.len(), "len {} current.len {}", len, current.len()); ++ ++ trace!( ++ target: "trie::hash_builder", ++ ?len, ++ ?common_prefix_len, ++ ?preceding_len, ++ preceding_exists, ++ "prefix lengths after comparing keys" ++ ); ++ ++ // Adjust the state masks for branch calculation ++ let extra_digit = current[len]; ++ if self.state_masks.len() <= len { ++ let new_len = len + 1; ++ trace!(target: "trie::hash_builder", new_len, old_len = self.state_masks.len(), "scaling state masks to fit"); ++ self.state_masks.resize(new_len, TrieMask::default()); ++ } ++ self.state_masks[len] |= TrieMask::from_nibble(extra_digit); ++ trace!( ++ target: "trie::hash_builder", ++ ?extra_digit, ++ groups = ?self.state_masks, ++ ); ++ ++ // Adjust the tree masks for exporting to the DB ++ if self.tree_masks.len() < current.len() { ++ self.resize_masks(current.len()); ++ } ++ ++ let mut len_from = len; ++ if !succeeding.is_empty() || preceding_exists { ++ len_from += 1; ++ } ++ trace!(target: "trie::hash_builder", "skipping {len_from} nibbles"); ++ ++ // The key without the common prefix ++ let short_node_key = current.slice(len_from..); ++ trace!(target: "trie::hash_builder", ?short_node_key); ++ ++ // Concatenate the 2 nodes together ++ if !build_extensions { ++ match self.value.as_ref() { ++ HashBuilderValueRef::Bytes(leaf_value) => { ++ // TODO(scroll): Replace with terminator masks ++ // Set the terminator mask for the leaf node ++ self.state_masks[len] |= TrieMask::new(0b100 << extra_digit); ++ let leaf_node = LeafNodeRef::new(&current, leaf_value); ++ let leaf_hash = leaf_node.hash_leaf(); ++ trace!( ++ target: "trie::hash_builder", ++ ?leaf_node, ++ ?leaf_hash, ++ "pushing leaf node", ++ ); ++ self.stack.push(leaf_hash); ++ // self.retain_proof_from_stack(&current.slice(..len_from)); ++ } ++ HashBuilderValueRef::Hash(hash) => { ++ trace!(target: "trie::hash_builder", ?hash, "pushing branch node hash"); ++ self.stack.push(*hash); ++ ++ if self.stored_in_database { ++ self.tree_masks[current.len() - 1] |= TrieMask::from_nibble( ++ current ++ .last() ++ .expect("must have at least a single bit in the current key"), ++ ); ++ } ++ self.hash_masks[current.len() - 1] |= TrieMask::from_nibble( ++ current ++ .last() ++ .expect("must have at least a single bit in the current key"), ++ ); ++ ++ build_extensions = true; ++ } ++ } ++ } ++ ++ if build_extensions && !short_node_key.is_empty() { ++ self.update_masks(&current, len_from); ++ let stack_last = self.stack.pop().expect("there should be at least one stack item"); ++ let sub_tree = SubTreeRef::new(&short_node_key, &stack_last); ++ let sub_tree_root = sub_tree.root(); ++ ++ trace!( ++ target: "trie::hash_builder", ++ ?short_node_key, ++ ?sub_tree_root, ++ "pushing subtree root", ++ ); ++ self.stack.push(sub_tree_root); ++ // self.retain_proof_from_stack(&current.slice(..len_from)); ++ self.resize_masks(len_from); ++ } ++ ++ if preceding_len <= common_prefix_len && !succeeding.is_empty() { ++ trace!(target: "trie::hash_builder", "no common prefix to create branch nodes from, returning"); ++ return; ++ } ++ ++ // Insert branch nodes in the stack ++ if !succeeding.is_empty() || preceding_exists { ++ // Pushes the corresponding branch node to the stack ++ let children = self.push_branch_node(&current, len); ++ // Need to store the branch node in an efficient format outside of the hash builder ++ self.store_branch_node(&current, len, children); ++ } ++ ++ self.state_masks.resize(len, TrieMask::default()); ++ self.resize_masks(len); ++ ++ if preceding_len == 0 { ++ trace!(target: "trie::hash_builder", "0 or 1 state masks means we have no more elements to process"); ++ return; ++ } ++ ++ current.truncate(preceding_len); ++ trace!(target: "trie::hash_builder", ?current, "truncated nibbles to {} bytes", preceding_len); ++ ++ trace!(target: "trie::hash_builder", groups = ?self.state_masks, "popping empty state masks"); ++ while self.state_masks.last() == Some(&TrieMask::default()) { ++ self.state_masks.pop(); ++ } ++ ++ build_extensions = true; ++ ++ i += 1; ++ } ++ } ++ ++ /// Given the size of the longest common prefix, it proceeds to create a branch node ++ /// from the state mask and existing stack state, and store its RLP to the top of the stack, ++ /// after popping all the relevant elements from the stack. ++ /// ++ /// Returns the hashes of the children of the branch node, only if `updated_branch_nodes` is ++ /// enabled. ++ fn push_branch_node(&mut self, _current: &Nibbles, len: usize) -> Vec<B256> { ++ let state_mask = self.state_masks[len]; ++ let hash_mask = self.hash_masks[len]; ++ let branch_node = BranchNodeRef::new(&self.stack, state_mask); ++ // Avoid calculating this value if it's not needed. ++ let children = if self.updated_branch_nodes.is_some() { ++ branch_node.child_hashes(hash_mask).collect() ++ } else { ++ vec![] ++ }; ++ ++ let branch_hash = branch_node.hash(); ++ ++ // TODO: enable proof retention ++ // self.retain_proof_from_stack(&current.slice(..len)); ++ ++ // Clears the stack from the branch node elements ++ let first_child_idx = branch_node.first_child_index(); ++ trace!( ++ target: "trie::hash_builder", ++ new_len = first_child_idx, ++ old_len = self.stack.len(), ++ "resizing stack to prepare branch node" ++ ); ++ self.stack.resize_with(first_child_idx, Default::default); ++ ++ trace!(target: "trie::hash_builder", ?branch_hash, "pushing branch node with {state_mask:?} mask ++ from stack"); ++ ++ self.stack.push(branch_hash); ++ children ++ } ++ ++ /// Given the current nibble prefix and the highest common prefix length, proceeds ++ /// to update the masks for the next level and store the branch node and the ++ /// masks in the database. We will use that when consuming the intermediate nodes ++ /// from the database to efficiently build the trie. ++ fn store_branch_node(&mut self, current: &Nibbles, len: usize, children: Vec<B256>) { ++ trace!(target: "trie::hash_builder", ?current, ?len, ?children, "store branch node"); ++ if len > 0 { ++ let parent_index = len - 1; ++ self.hash_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); ++ } ++ ++ let store_in_db_trie = !self.tree_masks[len].is_empty() || !self.hash_masks[len].is_empty(); ++ if store_in_db_trie { ++ if len > 0 { ++ let parent_index = len - 1; ++ self.tree_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); ++ } ++ ++ if self.updated_branch_nodes.is_some() { ++ let common_prefix = current.slice(..len); ++ let node = BranchNodeCompact::new( ++ self.state_masks[len] & CHILD_INDEX_MASK, ++ self.tree_masks[len], ++ self.hash_masks[len], ++ children, ++ (len == 0).then(|| self.current_root()), ++ ); ++ trace!(target: "trie::hash_builder", ?node, "storing updated intermediate node"); ++ self.updated_branch_nodes ++ .as_mut() ++ .expect("updates_branch_nodes is some") ++ .insert(common_prefix, node); ++ } ++ } ++ } ++ ++ // TODO(scroll): Enable proof retention ++ // fn retain_proof_from_stack(&mut self, prefix: &Nibbles) { ++ // if let Some(proof_retainer) = self.proof_retainer.as_mut() { ++ // proof_retainer.retain( ++ // prefix, ++ // self.stack.last().expect("there should be at least one stack item").as_ref(), ++ // ); ++ // } ++ // } ++ ++ fn update_masks(&mut self, current: &Nibbles, len_from: usize) { ++ if len_from > 0 { ++ let flag = TrieMask::from_nibble(current[len_from - 1]); ++ ++ self.hash_masks[len_from - 1] &= !flag; ++ ++ if !self.tree_masks[current.len() - 1].is_empty() { ++ self.tree_masks[len_from - 1] |= flag; ++ } ++ } ++ } ++ ++ fn resize_masks(&mut self, new_len: usize) { ++ trace!( ++ target: "trie::hash_builder", ++ new_len, ++ old_tree_mask_len = self.tree_masks.len(), ++ old_hash_mask_len = self.hash_masks.len(), ++ "resizing tree/hash masks" ++ ); ++ self.tree_masks.resize(new_len, TrieMask::default()); ++ self.hash_masks.resize(new_len, TrieMask::default()); ++ } ++} ++ ++// TODO(scroll): Introduce generic for the HashBuilder. ++impl From<reth_trie::HashBuilder> for HashBuilder { ++ fn from(hash_builder: reth_trie::HashBuilder) -> Self { ++ Self { ++ key: hash_builder.key, ++ value: hash_builder.value, ++ stack: hash_builder ++ .stack ++ .into_iter() ++ .map(|x| x.as_slice().try_into().expect("RlpNode contains 32 byte hashes")) ++ .collect(), ++ state_masks: hash_builder.groups, ++ tree_masks: hash_builder.tree_masks, ++ hash_masks: hash_builder.hash_masks, ++ stored_in_database: hash_builder.stored_in_database, ++ updated_branch_nodes: hash_builder.updated_branch_nodes, ++ proof_retainer: hash_builder.proof_retainer, ++ } ++ } ++} ++ ++impl From<HashBuilder> for reth_trie::HashBuilder { ++ fn from(value: HashBuilder) -> Self { ++ Self { ++ key: value.key, ++ value: value.value, ++ stack: value ++ .stack ++ .into_iter() ++ .map(|x| { ++ reth_trie::RlpNode::from_raw(&x.0).expect("32 byte hash can be cast to RlpNode") ++ }) ++ .collect(), ++ groups: value.state_masks, ++ tree_masks: value.tree_masks, ++ hash_masks: value.hash_masks, ++ stored_in_database: value.stored_in_database, ++ updated_branch_nodes: value.updated_branch_nodes, ++ proof_retainer: value.proof_retainer, ++ rlp_buf: Default::default(), ++ } ++ } ++} ++ ++#[cfg(test)] ++mod test { ++ use super::*; ++ use crate::key::AsBytes; ++ use alloc::collections::BTreeMap; ++ use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; ++ ++ #[test] ++ fn test_basic_trie() { ++ // Test a basic trie consisting of three key value pairs: ++ // (0, 0, 0, 0, ... , 0) ++ // (0, 0, 0, 1, ... , 0) ++ // (0, 0, 1, 0, ... , 0) ++ // (1, 1, 1, 0, ... , 0) ++ // (1, 1, 1, 1, ... , 0) ++ // The branch associated with key 0xF will be collapsed into a single leaf. ++ ++ let leaf_1_key = Nibbles::from_nibbles_unchecked([ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, ++ ]); ++ let leaf_2_key = Nibbles::from_nibbles_unchecked([ ++ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, ++ ]); ++ let leaf_3_key = Nibbles::from_nibbles_unchecked([ ++ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, ++ ]); ++ let leaf_4_key = Nibbles::from_nibbles_unchecked([ ++ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, ++ ]); ++ let leaf_5_key = Nibbles::from_nibbles_unchecked([ ++ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, ++ ]); ++ let leaf_keys = [ ++ leaf_1_key.clone(), ++ leaf_2_key.clone(), ++ leaf_3_key.clone(), ++ leaf_4_key.clone(), ++ leaf_5_key.clone(), ++ ]; ++ ++ let leaf_values = leaf_keys ++ .into_iter() ++ .enumerate() ++ .map(|(i, key)| { ++ let mut leaf_value = [0u8; 32]; ++ leaf_value[0] = i as u8 + 1; ++ (key, leaf_value) ++ }) ++ .collect::<BTreeMap<_, _>>(); ++ ++ let leaf_hashes: BTreeMap<_, _> = leaf_values ++ .iter() ++ .map(|(key, value)| { ++ let key_fr = ++ Fr::from_repr_vartime(key.as_bytes()).expect("key is valid field element"); ++ let value = Fr::from_repr_vartime(*value).expect("value is a valid field element"); ++ let hash = hash_with_domain(&[key_fr, value], crate::LEAF_NODE_DOMAIN); ++ (key.clone(), hash) ++ }) ++ .collect(); ++ ++ let mut hb = HashBuilder::default().with_updates(true); ++ ++ for (key, val) in &leaf_values { ++ hb.add_leaf(key.clone(), val); ++ } ++ ++ let root = hb.root(); ++ ++ // node_000 -> hash(leaf_1, leaf_2) LTRT ++ // node_00 -> hash(node_000, leaf_3) LBRT ++ // node_0 -> hash(node_00, EMPTY) LBRT ++ // node_111 -> hash(leaf_4, leaf_5) LTRT ++ // node_11 -> hash(EMPTY, node_111) LTRB ++ // node_1 -> hash(EMPTY, node_11) LTRB ++ // root -> hash(node_0, node_1) LBRB ++ ++ let expected: B256 = { ++ let node_000 = hash_with_domain( ++ &[*leaf_hashes.get(&leaf_1_key).unwrap(), *leaf_hashes.get(&leaf_2_key).unwrap()], ++ crate::BRANCH_NODE_LTRT_DOMAIN, ++ ); ++ let node_00 = hash_with_domain( ++ &[node_000, *leaf_hashes.get(&leaf_3_key).unwrap()], ++ crate::BRANCH_NODE_LBRT_DOMAIN, ++ ); ++ let node_0 = hash_with_domain(&[node_00, Fr::zero()], crate::BRANCH_NODE_LBRT_DOMAIN); ++ let node_111 = hash_with_domain( ++ &[*leaf_hashes.get(&leaf_4_key).unwrap(), *leaf_hashes.get(&leaf_5_key).unwrap()], ++ crate::BRANCH_NODE_LTRT_DOMAIN, ++ ); ++ let node_11 = hash_with_domain(&[Fr::zero(), node_111], crate::BRANCH_NODE_LTRB_DOMAIN); ++ let node_1 = hash_with_domain(&[Fr::zero(), node_11], crate::BRANCH_NODE_LTRB_DOMAIN); ++ ++ let mut root = ++ hash_with_domain(&[node_0, node_1], crate::BRANCH_NODE_LBRB_DOMAIN).to_repr(); ++ root.reverse(); ++ root.into() ++ }; ++ ++ assert_eq!(expected, root); ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+21
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/key.rs scroll-reth/crates/scroll/trie/src/key.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..0a58bd9f9ce06ceb31532ec32ace2c10471b079f +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/key.rs +@@ -0,0 +1,21 @@ ++use reth_trie::Nibbles; ++ ++/// A type that can return its bytes representation encoded as a little-endian on 32 bytes. ++pub(crate) trait AsBytes { ++ /// Returns the type as its canonical little-endian representation on 32 bytes. ++ fn as_bytes(&self) -> [u8; 32]; ++} ++ ++impl AsBytes for Nibbles { ++ fn as_bytes(&self) -> [u8; 32] { ++ // This is strange we are now representing the leaf key using big endian?? ++ let mut result = [0u8; 32]; ++ for (byte_index, bytes) in self.as_slice().chunks(8).enumerate() { ++ for (bit_index, byte) in bytes.iter().enumerate() { ++ result[byte_index] |= byte << bit_index; ++ } ++ } ++ ++ result ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+23
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/leaf.rs scroll-reth/crates/scroll/trie/src/leaf.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..79216d8f562dfdf8e2bbe231e0454858e0459ae2 +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/leaf.rs +@@ -0,0 +1,23 @@ ++use super::LEAF_NODE_DOMAIN; ++use crate::key::AsBytes; ++use alloy_primitives::B256; ++use alloy_trie::nodes::LeafNodeRef; ++use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; ++ ++/// A trait used to hash the leaf node. ++pub(crate) trait HashLeaf { ++ /// Hash the leaf node. ++ fn hash_leaf(&self) -> B256; ++} ++ ++impl HashLeaf for LeafNodeRef<'_> { ++ fn hash_leaf(&self) -> B256 { ++ let leaf_key = ++ Fr::from_repr_vartime(self.key.as_bytes()).expect("leaf key is a valid field element"); ++ let leaf_value = Fr::from_repr_vartime( ++ <[u8; 32]>::try_from(self.value).expect("leaf value is 32 bytes"), ++ ) ++ .expect("leaf value is a valid field element"); ++ hash_with_domain(&[leaf_key, leaf_value], LEAF_NODE_DOMAIN).to_repr().into() ++ } ++}
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+37
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/lib.rs scroll-reth/crates/scroll/trie/src/lib.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..5b1a5fd5aa93d6ad8bc735ff4a6d4bb1fe17679e +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/lib.rs +@@ -0,0 +1,37 @@ ++//! Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for ++//! prefix-sorted bits. ++ ++#![cfg_attr(not(doctest), doc = include_str!("../assets/zktrie.md"))] ++ ++#[macro_use] ++#[allow(unused_imports)] ++extern crate alloc; ++ ++mod branch; ++ ++mod constants; ++pub use constants::EMPTY_ROOT_HASH; ++ ++mod key; ++mod leaf; ++mod sub_tree; ++ ++mod hash_builder; ++pub use hash_builder::HashBuilder; ++ ++use poseidon_bn254::Fr; ++ ++/// The hashing domain for leaf nodes. ++pub const LEAF_NODE_DOMAIN: Fr = Fr::from_raw([4, 0, 0, 0]); ++ ++/// The hashing domain for a branch node with two terminal children. ++pub const BRANCH_NODE_LTRT_DOMAIN: Fr = Fr::from_raw([6, 0, 0, 0]); ++ ++/// The hashing domain for a branch node with a left terminal child and a right branch child. ++pub const BRANCH_NODE_LTRB_DOMAIN: Fr = Fr::from_raw([7, 0, 0, 0]); ++ ++/// The hashing domain for a branch node with a left branch child and a right terminal child. ++pub const BRANCH_NODE_LBRT_DOMAIN: Fr = Fr::from_raw([8, 0, 0, 0]); ++ ++/// The hashing domain for a branch node with two branch children. ++pub const BRANCH_NODE_LBRB_DOMAIN: Fr = Fr::from_raw([9, 0, 0, 0]);
+
+ + +
+ + +
+
+
+ + (new) + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+44
+
-0
+ +
+ +
+
+
diff --git reth/crates/scroll/trie/src/sub_tree.rs scroll-reth/crates/scroll/trie/src/sub_tree.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..31edad05c4166dcab958f28450206e81b61ab13f +--- /dev/null ++++ scroll-reth/crates/scroll/trie/src/sub_tree.rs +@@ -0,0 +1,44 @@ ++use super::{BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN}; ++use alloy_primitives::{hex, B256}; ++use alloy_trie::Nibbles; ++use core::fmt; ++use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; ++ ++/// [`SubTreeRef`] is a structure that allows for calculation of the root of a sparse binary Merkle ++/// tree consisting of a single leaf node. ++pub(crate) struct SubTreeRef<'a> { ++ /// The key to the child node. ++ pub key: &'a Nibbles, ++ /// A pointer to the child node. ++ pub child: &'a B256, ++} ++ ++impl<'a> SubTreeRef<'a> { ++ /// Creates a new subtree with the given key and a pointer to the child. ++ #[inline] ++ pub(crate) const fn new(key: &'a Nibbles, child: &'a B256) -> Self { ++ Self { key, child } ++ } ++ ++ pub(crate) fn root(&self) -> B256 { ++ let mut tree_root = ++ Fr::from_repr_vartime(self.child.0).expect("child is a valid field element"); ++ for bit in self.key.as_slice().iter().rev() { ++ tree_root = if *bit == 0 { ++ hash_with_domain(&[tree_root, Fr::zero()], BRANCH_NODE_LBRT_DOMAIN) ++ } else { ++ hash_with_domain(&[Fr::zero(), tree_root], BRANCH_NODE_LTRB_DOMAIN) ++ }; ++ } ++ tree_root.to_repr().into() ++ } ++} ++ ++impl fmt::Debug for SubTreeRef<'_> { ++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ++ f.debug_struct("SubTreeRef") ++ .field("key", &self.key) ++ .field("node", &hex::encode(self.child)) ++ .finish() ++ } ++}
+
+ + + +
+
+ +
+
+ + + +
+ +
+
+
+ +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-1
+ +
+ +
+
+
diff --git reth/crates/ethereum/consensus/src/lib.rs scroll-reth/crates/ethereum/consensus/src/lib.rs +index b81ee1d5c448424b98cb21c20555121c9ca90a46..51739c88b85cd5333daa0e63fe2a77d3adaa1f94 100644 +--- reth/crates/ethereum/consensus/src/lib.rs ++++ scroll-reth/crates/ethereum/consensus/src/lib.rs +@@ -29,7 +29,7 @@ }; + use std::{fmt::Debug, sync::Arc, time::SystemTime}; +  + mod validation; +-pub use validation::validate_block_post_execution; ++pub use validation::{validate_block_post_execution, verify_receipts}; +  + /// Ethereum beacon consensus + ///
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-1
+ +
+ +
+
+
diff --git reth/crates/ethereum/consensus/src/validation.rs scroll-reth/crates/ethereum/consensus/src/validation.rs +index b9b38b6d51c2671fbc1ce4109b8aa0e33a6d8d5e..359b5315910b7b75e80ed0e6971458594772545a 100644 +--- reth/crates/ethereum/consensus/src/validation.rs ++++ scroll-reth/crates/ethereum/consensus/src/validation.rs +@@ -61,7 +61,7 @@ } +  + /// Calculate the receipts root, and compare it against against the expected receipts root and logs + /// bloom. +-fn verify_receipts( ++pub fn verify_receipts( + expected_receipts_root: B256, + expected_logs_bloom: Bloom, + receipts: &[Receipt],
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/ethereum/engine-primitives/src/lib.rs scroll-reth/crates/ethereum/engine-primitives/src/lib.rs +index 350780d0bdad3c1adcb0aa6c521bfc3c4ce27c87..010196e269099905d1fdfd16a3470eee012a174b 100644 +--- reth/crates/ethereum/engine-primitives/src/lib.rs ++++ scroll-reth/crates/ethereum/engine-primitives/src/lib.rs +@@ -87,6 +87,7 @@ } +  + /// Returns the chain spec used by the validator. + #[inline] ++ #[allow(clippy::missing_const_for_fn)] + fn chain_spec(&self) -> &ChainSpec { + self.inner.chain_spec() + }
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-0
+ +
+ +
+
+
diff --git reth/crates/ethereum/engine-primitives/src/payload.rs scroll-reth/crates/ethereum/engine-primitives/src/payload.rs +index 450302598ecdd34222d54999c597c22a78c5194c..97042fce3ea0e98dfc0553ea9cc5148c68c0132b 100644 +--- reth/crates/ethereum/engine-primitives/src/payload.rs ++++ scroll-reth/crates/ethereum/engine-primitives/src/payload.rs +@@ -59,6 +59,7 @@ self.id + } +  + /// Returns the built block(sealed) ++ #[allow(clippy::missing_const_for_fn)] + pub fn block(&self) -> &SealedBlock { + &self.block + } +@@ -69,6 +70,7 @@ self.fees + } +  + /// Returns the blob sidecars. ++ #[allow(clippy::missing_const_for_fn)] + pub fn sidecars(&self) -> &[BlobTransactionSidecar] { + &self.sidecars + }
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+4
+
-0
+ +
+ +
+
+
diff --git reth/crates/ethereum/evm/Cargo.toml scroll-reth/crates/ethereum/evm/Cargo.toml +index 7e6ed8052877b8b7fceb7f6d050a05bc21eeb425..31e63e941464eea59833a3de2d312cd42868fcac 100644 +--- reth/crates/ethereum/evm/Cargo.toml ++++ scroll-reth/crates/ethereum/evm/Cargo.toml +@@ -57,3 +57,7 @@ "serde_json/std", + "reth-primitives-traits/std", + "reth-chainspec/std" + ] ++scroll = [ ++ "reth-primitives/scroll", ++ "reth-testing-utils/scroll", ++]
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+3
+
-0
+ +
+ +
+
+
diff --git reth/crates/ethereum/payload/Cargo.toml scroll-reth/crates/ethereum/payload/Cargo.toml +index dd4a5f948cd070f0b175944c7614771553a2c524..76abd93022aa2247fd00ec84031406266c4b4bb4 100644 +--- reth/crates/ethereum/payload/Cargo.toml ++++ scroll-reth/crates/ethereum/payload/Cargo.toml +@@ -39,3 +39,6 @@ alloy-primitives.workspace = true +  + # misc + tracing.workspace = true ++ ++[features] ++scroll = []
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+9
+
-7
+ +
+ +
+
+
diff --git reth/crates/ethereum/payload/src/lib.rs scroll-reth/crates/ethereum/payload/src/lib.rs +index a4a02c3ef768f89ae9f039540e2154fbd1a53829..8de5e2bc3694e0be60c250caa1b50612c1d8f42d 100644 +--- reth/crates/ethereum/payload/src/lib.rs ++++ scroll-reth/crates/ethereum/payload/src/lib.rs +@@ -412,13 +412,15 @@ + // calculate the state root + let hashed_state = db.database.db.hashed_post_state(execution_outcome.state()); + let (state_root, trie_output) = { +- db.database.inner().state_root_with_updates(hashed_state.clone()).inspect_err(|err| { +- warn!(target: "payload_builder", +- parent_hash=%parent_header.hash(), +- %err, +- "failed to calculate state root for payload" +- ); +- })? ++ db.database.inner().state_root_from_state_with_updates(hashed_state.clone()).inspect_err( ++ |err| { ++ warn!(target: "payload_builder", ++ parent_hash=%parent_header.hash(), ++ %err, ++ "failed to calculate state root for payload" ++ ); ++ }, ++ )? + }; +  + // create the block header
+
+ + + +
+
+ +
+
+
+ + +
+ +
+
+
+ +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/bin/Cargo.toml scroll-reth/crates/optimism/bin/Cargo.toml +index b182a4f278a301c8013d2481a271f6bac3f30d99..aa7a321b745bdba6c5622639cf802837cff15814 100644 +--- reth/crates/optimism/bin/Cargo.toml ++++ scroll-reth/crates/optimism/bin/Cargo.toml +@@ -47,6 +47,7 @@ "reth-optimism-rpc/optimism", + "reth-provider/optimism", + "reth-optimism-primitives/optimism", + ] ++scroll = [] +  + dev = [ + "reth-optimism-cli/dev",
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-1
+ +
+ +
+
+
diff --git reth/crates/optimism/bin/src/lib.rs scroll-reth/crates/optimism/bin/src/lib.rs +index 21c28f7c5470f38893c52fa10198a0feed2323f7..b3e274e678f577e7f76827917b387f033c2011a7 100644 +--- reth/crates/optimism/bin/src/lib.rs ++++ scroll-reth/crates/optimism/bin/src/lib.rs +@@ -24,8 +24,9 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" + )] + #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] + // The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] ++#![cfg(all(feature = "optimism", not(feature = "scroll")))] +  + /// Re-exported from `reth_optimism_cli`. + pub mod cli {
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+15
+
-10
+ +
+ +
+
+
diff --git reth/crates/optimism/bin/src/main.rs scroll-reth/crates/optimism/bin/src/main.rs +index bba31e250117ecd6f32b15f4176984e9d947f8fd..da7f92e33192c346a3cedfa42b4a945226703977 100644 +--- reth/crates/optimism/bin/src/main.rs ++++ scroll-reth/crates/optimism/bin/src/main.rs +@@ -1,19 +1,18 @@ + #![allow(missing_docs, rustdoc::missing_crate_level_docs)] +-// The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] +- +-use clap::Parser; +-use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher, Node}; +-use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; +-use reth_optimism_node::{args::RollupArgs, OpNode}; +-use reth_provider::providers::BlockchainProvider; +- +-use tracing as _; ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] +  + #[global_allocator] + static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); +  ++// The `optimism` feature must be enabled to use this crate. ++#[cfg(all(feature = "optimism", not(feature = "scroll")))] + fn main() { ++ use clap::Parser; ++ use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher, Node}; ++ use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; ++ use reth_optimism_node::{args::RollupArgs, OpNode}; ++ use reth_provider::providers::BlockchainProvider; ++ use tracing as _; + reth_cli_util::sigsegv_handler::install(); +  + // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. +@@ -53,3 +52,9 @@ eprintln!("Error: {err:?}"); + std::process::exit(1); + } + } ++ ++#[cfg(not(all(feature = "optimism", not(feature = "scroll"))))] ++fn main() { ++ eprintln!("Optimism feature is not enabled"); ++ std::process::exit(1); ++}
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/cli/Cargo.toml scroll-reth/crates/optimism/cli/Cargo.toml +index 3cd2edeecc5ab5a43962e892e79ac09d86061f9c..5573b1c97b870976a14d18eb990b20a9aad30d0a 100644 +--- reth/crates/optimism/cli/Cargo.toml ++++ scroll-reth/crates/optimism/cli/Cargo.toml +@@ -120,3 +120,4 @@ "reth-execution-types/serde", + "reth-provider/serde", + "reth-optimism-primitives/serde", + ] ++scroll = []
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-1
+ +
+ +
+
+
diff --git reth/crates/optimism/cli/src/lib.rs scroll-reth/crates/optimism/cli/src/lib.rs +index 5c3900a0e483829d7fb9f0decc01ef8e02a2d06f..791ca5ca8ff9aa9df2be80648ad01f77b25afcfc 100644 +--- reth/crates/optimism/cli/src/lib.rs ++++ scroll-reth/crates/optimism/cli/src/lib.rs +@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" + )] + #![cfg_attr(all(not(test), feature = "optimism"), warn(unused_crate_dependencies))] + #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] + // The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] ++#![cfg(all(feature = "optimism", not(feature = "scroll")))] +  + /// Optimism chain specification parser. + pub mod chainspec;
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/consensus/Cargo.toml scroll-reth/crates/optimism/consensus/Cargo.toml +index 024bb957f815390ebfe3a3671a9907e5f20dadc5..2e48677aa11767cdfc9713d1573708a93e192071 100644 +--- reth/crates/optimism/consensus/Cargo.toml ++++ scroll-reth/crates/optimism/consensus/Cargo.toml +@@ -54,3 +54,4 @@ "alloy-trie/std", + "op-alloy-consensus/std", + ] + optimism = ["reth-primitives/optimism", "reth-optimism-primitives/optimism"] ++scroll = []
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-1
+ +
+ +
+
+
diff --git reth/crates/optimism/consensus/src/lib.rs scroll-reth/crates/optimism/consensus/src/lib.rs +index cedc8c462929f37e2a5555cd154ce3a756d40896..b77a8a056fbc048bfc17d56ed55e91ba9c75a259 100644 +--- reth/crates/optimism/consensus/src/lib.rs ++++ scroll-reth/crates/optimism/consensus/src/lib.rs +@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" + )] + #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + #![cfg_attr(not(feature = "std"), no_std)] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] + // The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] ++#![cfg(all(feature = "optimism", not(feature = "scroll")))] +  + extern crate alloc; +
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/evm/Cargo.toml scroll-reth/crates/optimism/evm/Cargo.toml +index 19b63d9fe03367c28260d6e44f429ca576be2770..37146033e23c731c72b8e3ed8371e3f7e2a5dc2a 100644 +--- reth/crates/optimism/evm/Cargo.toml ++++ scroll-reth/crates/optimism/evm/Cargo.toml +@@ -85,3 +85,4 @@ "revm/optimism", + "revm-primitives/optimism", + "reth-optimism-primitives/optimism", + ] ++scroll = []
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-1
+ +
+ +
+
+
diff --git reth/crates/optimism/evm/src/lib.rs scroll-reth/crates/optimism/evm/src/lib.rs +index 37c3fd548be63182dc621eec398c3e33d0689026..3740a75d01dcfda50b8080c0d0c1d3d8a0cd6d60 100644 +--- reth/crates/optimism/evm/src/lib.rs ++++ scroll-reth/crates/optimism/evm/src/lib.rs +@@ -7,8 +7,9 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" + )] + #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + #![cfg_attr(not(feature = "std"), no_std)] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] + // The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] ++#![cfg(all(feature = "optimism", not(feature = "scroll")))] +  + extern crate alloc; +
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/node/Cargo.toml scroll-reth/crates/optimism/node/Cargo.toml +index 25adb3d108932b505a9ec1c16a96c910b64995f0..d2e09c80615689f10853781f0dc891640f3772db 100644 +--- reth/crates/optimism/node/Cargo.toml ++++ scroll-reth/crates/optimism/node/Cargo.toml +@@ -138,3 +138,4 @@ reth-codec = [ + "reth-primitives/reth-codec", + "reth-optimism-primitives/reth-codec", + ] ++scroll = []
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/node/src/engine.rs scroll-reth/crates/optimism/node/src/engine.rs +index cb4c88e9d52a92cef3fca49108c2010ca525a60f..d655dbdeec3cbced10cf0bf90c983f3c9910df85 100644 +--- reth/crates/optimism/node/src/engine.rs ++++ scroll-reth/crates/optimism/node/src/engine.rs +@@ -84,6 +84,7 @@ } +  + /// Returns the chain spec used by the validator. + #[inline] ++ #[allow(clippy::missing_const_for_fn)] + fn chain_spec(&self) -> &OpChainSpec { + self.inner.chain_spec() + }
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-1
+ +
+ +
+
+
diff --git reth/crates/optimism/node/src/lib.rs scroll-reth/crates/optimism/node/src/lib.rs +index 18c5715474ed024fc6698b846f738a8611028ba6..2f3d5e5c082d0c2b3f24257b4c4080860c790937 100644 +--- reth/crates/optimism/node/src/lib.rs ++++ scroll-reth/crates/optimism/node/src/lib.rs +@@ -9,8 +9,9 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" + )] + #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] ++#![cfg_attr(feature = "scroll", allow(unused_crate_dependencies))] + // The `optimism` feature must be enabled to use this crate. +-#![cfg(feature = "optimism")] ++#![cfg(all(feature = "optimism", not(feature = "scroll")))] +  + /// CLI argument parsing for the optimism node. + pub mod args;
+
+ + +
+ @@ -9890,7 +16657,7 @@
-
diff --git reth/crates/optimism/node/tests/e2e/main.rs scroll-reth/crates/optimism/node/tests/e2e/main.rs +
diff --git reth/crates/optimism/node/tests/e2e/main.rs scroll-reth/crates/optimism/node/tests/e2e/main.rs index 7f4b22ba7e04eb795bc6286c44f1464f5c9f6c1c..d0c12c52cd61cc88872dc69bb9b017f300198ec7 100644 --- reth/crates/optimism/node/tests/e2e/main.rs +++ scroll-reth/crates/optimism/node/tests/e2e/main.rs @@ -9911,8 +16678,8 @@
@@ -9945,7 +16712,7 @@
-
diff --git reth/crates/optimism/node/tests/it/main.rs scroll-reth/crates/optimism/node/tests/it/main.rs +
diff --git reth/crates/optimism/node/tests/it/main.rs scroll-reth/crates/optimism/node/tests/it/main.rs index d0533fc4541ca1543d060df932dad643f9e3ab5c..6ac5cf6cf1c660ff7b1f0aee082b93b0ad0bc2df 100644 --- reth/crates/optimism/node/tests/it/main.rs +++ scroll-reth/crates/optimism/node/tests/it/main.rs @@ -9970,8 +16737,8 @@
@@ -10004,7 +16771,7 @@
-
diff --git reth/crates/optimism/payload/Cargo.toml scroll-reth/crates/optimism/payload/Cargo.toml +
diff --git reth/crates/optimism/payload/Cargo.toml scroll-reth/crates/optimism/payload/Cargo.toml index 49ae3d93e8459642e375902af40a1770c3a1e7da..e1aa93e52bcb79d4f5dd90ba4c8bf87326c5addb 100644 --- reth/crates/optimism/payload/Cargo.toml +++ scroll-reth/crates/optimism/payload/Cargo.toml @@ -10019,8 +16786,8 @@
@@ -10046,15 +16813,15 @@
-
+9
+
+10
-7
-
diff --git reth/crates/optimism/payload/src/builder.rs scroll-reth/crates/optimism/payload/src/builder.rs -index 7bf3f8015b71121d5e075e23989529c31008008b..3cf673a157aa19a0bb9e43ac5811b7c2c0bc7ceb 100644 +
diff --git reth/crates/optimism/payload/src/builder.rs scroll-reth/crates/optimism/payload/src/builder.rs +index 7bf3f8015b71121d5e075e23989529c31008008b..af035e91644763415ac3599097081eda2d837f9e 100644 --- reth/crates/optimism/payload/src/builder.rs +++ scroll-reth/crates/optimism/payload/src/builder.rs @@ -381,13 +381,15 @@ // // calculate the state root @@ -10079,15 +16846,23 @@ + )? };   - // create the block header
+ // create the block header +@@ -568,6 +570,7 @@ } +  + impl<EvmConfig> OpPayloadBuilderCtx<EvmConfig> { + /// Returns the parent block the payload will be build on. ++ #[allow(clippy::missing_const_for_fn)] + pub fn parent(&self) -> &SealedHeader { + &self.config.parent_header + }
@@ -10120,7 +16895,7 @@
-
diff --git reth/crates/optimism/payload/src/lib.rs scroll-reth/crates/optimism/payload/src/lib.rs +
diff --git reth/crates/optimism/payload/src/lib.rs scroll-reth/crates/optimism/payload/src/lib.rs index 53fad1118fd09c1ceb7328cd8885b19f909e624f..7ba359fc83e55c69d8e7701e0e69476b44b01aac 100644 --- reth/crates/optimism/payload/src/lib.rs +++ scroll-reth/crates/optimism/payload/src/lib.rs @@ -10141,8 +16916,60 @@
+ +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/payload/src/payload.rs scroll-reth/crates/optimism/payload/src/payload.rs +index 2b7c683df8556b37a630f5501f880fa91aef5c81..511af75d479c5e33b56f9580adaef7e090cd48a3 100644 +--- reth/crates/optimism/payload/src/payload.rs ++++ scroll-reth/crates/optimism/payload/src/payload.rs +@@ -170,6 +170,7 @@ self.id + } +  + /// Returns the built block(sealed) ++ #[allow(clippy::missing_const_for_fn)] + pub fn block(&self) -> &SealedBlockFor<OpBlock> { + &self.block + }
+
+ + +
+ @@ -10175,7 +17002,7 @@
-
diff --git reth/crates/optimism/rpc/Cargo.toml scroll-reth/crates/optimism/rpc/Cargo.toml +
diff --git reth/crates/optimism/rpc/Cargo.toml scroll-reth/crates/optimism/rpc/Cargo.toml index 1bc4071f16d0c6e499d4c4d0b67a241dce920637..23fc6deb224ea7c585e93e58e6c61cd7ea9f7919 100644 --- reth/crates/optimism/rpc/Cargo.toml +++ scroll-reth/crates/optimism/rpc/Cargo.toml @@ -10190,8 +17017,66 @@
+ +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/rpc/src/eth/mod.rs scroll-reth/crates/optimism/rpc/src/eth/mod.rs +index ebcbaed12d13d331306e51f409cc877d141a3ce3..9e6e5b6895b0e93ba57ed03f1a8b41b15381eb9c 100644 +--- reth/crates/optimism/rpc/src/eth/mod.rs ++++ scroll-reth/crates/optimism/rpc/src/eth/mod.rs +@@ -80,11 +80,13 @@ + 'static, + >, + { + /// Returns a reference to the [`EthApiNodeBackend`]. ++ #[allow(clippy::missing_const_for_fn)] + pub fn eth_api(&self) -> &EthApiNodeBackend<N> { + self.inner.eth_api() + } +  + /// Returns the configured sequencer client, if any. ++ #[allow(clippy::missing_const_for_fn)] + pub fn sequencer_client(&self) -> Option<&SequencerClient> { + self.inner.sequencer_client() + }
+
+ + +
+ @@ -10224,7 +17109,7 @@
-
diff --git reth/crates/optimism/rpc/src/lib.rs scroll-reth/crates/optimism/rpc/src/lib.rs +
diff --git reth/crates/optimism/rpc/src/lib.rs scroll-reth/crates/optimism/rpc/src/lib.rs index b76058ce531bd1eb270f61a433725412404a88fb..5a65f7ff55a8cdef85cdbfe300c9930bff36b0fc 100644 --- reth/crates/optimism/rpc/src/lib.rs +++ scroll-reth/crates/optimism/rpc/src/lib.rs @@ -10242,6 +17127,64 @@
+
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+2
+
-0
+ +
+ +
+
+
diff --git reth/crates/optimism/rpc/src/sequencer.rs scroll-reth/crates/optimism/rpc/src/sequencer.rs +index 43a23cb9e16349da0a8d019bef8fe2bbc79d85ca..26e3a636f932b1b75665d8a2fd9a2593eba04751 100644 +--- reth/crates/optimism/rpc/src/sequencer.rs ++++ scroll-reth/crates/optimism/rpc/src/sequencer.rs +@@ -36,11 +36,13 @@ Self { inner: Arc::new(inner) } + } +  + /// Returns the network of the client ++ #[allow(clippy::missing_const_for_fn)] + pub fn endpoint(&self) -> &str { + &self.inner.sequencer_endpoint + } +  + /// Returns the client ++ #[allow(clippy::missing_const_for_fn)] + pub fn http_client(&self) -> &Client { + &self.inner.http_client + }
+
+ +
@@ -10251,7 +17194,7 @@
-
@@ -13627,13 +21725,13 @@ @@ -13643,80 +21741,114 @@
-
+22
-
-8
+
+52
+
-7
-
diff --git reth/crates/storage/storage-api/src/noop.rs scroll-reth/crates/storage/storage-api/src/noop.rs -index 3e6ed7dbd52c114dd3143d93ffcb9eff589101d5..8ffb5b072510a3228a82cbcea24faacedf09c6a6 100644 ---- reth/crates/storage/storage-api/src/noop.rs -+++ scroll-reth/crates/storage/storage-api/src/noop.rs -@@ -3,10 +3,11 @@ - use crate::{ - AccountReader, BlockBodyIndicesProvider, BlockHashReader, BlockIdReader, BlockNumReader, - BlockReader, BlockReaderIdExt, BlockSource, ChangeSetReader, HashedPostStateProvider, -- HeaderProvider, NodePrimitivesProvider, OmmersProvider, PruneCheckpointReader, ReceiptProvider, -- ReceiptProviderIdExt, StageCheckpointReader, StateProofProvider, StateProvider, -- StateProviderBox, StateProviderFactory, StateRootProvider, StorageRootProvider, -- TransactionVariant, TransactionsProvider, WithdrawalsProvider, -+ HashedStorageProvider, HeaderProvider, KeyHasherProvider, NodePrimitivesProvider, -+ OmmersProvider, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, -+ StageCheckpointReader, StateProofProvider, StateProvider, StateProviderBox, -+ StateProviderFactory, StateRootProvider, StorageRootProvider, TransactionVariant, -+ TransactionsProvider, WithdrawalsProvider, - }; - use alloy_consensus::transaction::TransactionMeta; - use alloy_eips::{eip4895::Withdrawals, BlockHashOrNumber, BlockId, BlockNumberOrTag}; -@@ -22,9 +23,10 @@ use reth_prune_types::{PruneCheckpoint, PruneSegment}; - use reth_stages_types::{StageCheckpoint, StageId}; - use reth_storage_errors::provider::{ProviderError, ProviderResult}; +
diff --git reth/crates/trie/db/src/state.rs scroll-reth/crates/trie/db/src/state.rs +index 992335896cd3696d4e42d909402a0e6d927efbe5..e466e9ad7293bcaee6b353d186afe48caf216940 100644 +--- reth/crates/trie/db/src/state.rs ++++ scroll-reth/crates/trie/db/src/state.rs +@@ -12,9 +12,10 @@ }; + use reth_execution_errors::StateRootError; + use reth_storage_errors::db::DatabaseError; use reth_trie::{ -- updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, -- MultiProofTargets, TrieInput, -+ updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, KeyHasher, -+ MultiProof, MultiProofTargets, TrieInput, +- hashed_cursor::HashedPostStateCursorFactory, trie_cursor::InMemoryTrieCursorFactory, +- updates::TrieUpdates, HashedPostState, HashedStorage, KeccakKeyHasher, KeyHasher, StateRoot, +- StateRootProgress, TrieInput, ++ hashed_cursor::HashedPostStateCursorFactory, prefix_set::TriePrefixSets, ++ trie_cursor::InMemoryTrieCursorFactory, updates::TrieUpdates, HashedPostState, ++ HashedPostStateSorted, HashedStorage, IntermediateStateRootState, KeccakKeyHasher, KeyHasher, ++ StateRoot, StateRootProgress, TrieInput, }; -+use revm::db::BundleAccount; - use std::{ - marker::PhantomData, - ops::{RangeBounds, RangeInclusive}, -@@ -368,7 +370,7 @@ } - } + use std::{collections::HashMap, ops::RangeInclusive}; + use tracing::debug; +@@ -109,7 +110,7 @@ /// updates. See [`Self::overlay_root`] for more info. + fn overlay_root_with_updates( + tx: &'a TX, + post_state: HashedPostState, +- ) -> Result<(B256, TrieUpdates), StateRootError>; ++ ) -> Result<(B256, TrieUpdates, HashedPostStateSorted), StateRootError>;   - impl<C: Send + Sync, N: NodePrimitives> StateRootProvider for NoopProvider<C, N> { -- fn state_root(&self, _state: HashedPostState) -> ProviderResult<B256> { -+ fn state_root_from_state(&self, _state: HashedPostState) -> ProviderResult<B256> { - Ok(B256::default()) - } + /// Calculates the state root for provided [`HashedPostState`] using cached intermediate nodes. + fn overlay_root_from_nodes(tx: &'a TX, input: TrieInput) -> Result<B256, StateRootError>; +@@ -120,6 +121,27 @@ fn overlay_root_from_nodes_with_updates( + tx: &'a TX, + input: TrieInput, + ) -> Result<(B256, TrieUpdates), StateRootError>; ++ ++ /// Calculates the state root for the current state stored in the database. ++ fn root(tx: &'a TX) -> Result<B256, StateRootError>; ++ ++ /// Calculates the state root for the current state stored in the database and returns ++ /// trie updates. ++ fn root_with_updates(tx: &'a TX) -> Result<(B256, TrieUpdates), StateRootError>; ++ ++ /// Calculates the state root for the current state stored in the database updating the paths ++ /// associated with the provided prefix sets and returns the trie updates. ++ fn root_from_prefix_sets_with_updates( ++ tx: &'a TX, ++ prefix_sets: TriePrefixSets, ++ ) -> Result<(B256, TrieUpdates), StateRootError>; ++ ++ /// Calculates the state root for the current state stored in the database and returns the ++ /// intermediate progress of the computation. ++ fn root_with_progress( ++ tx: &'a TX, ++ state: Option<IntermediateStateRootState>, ++ ) -> Result<StateRootProgress, StateRootError>; + }   -@@ -376,7 +378,7 @@ fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> { - Ok(B256::default()) + /// Extends [`HashedPostState`] with operations specific for working with a database transaction. +@@ -182,15 +204,16 @@ + fn overlay_root_with_updates( + tx: &'a TX, + post_state: HashedPostState, +- ) -> Result<(B256, TrieUpdates), StateRootError> { ++ ) -> Result<(B256, TrieUpdates, HashedPostStateSorted), StateRootError> { + let prefix_sets = post_state.construct_prefix_sets().freeze(); + let state_sorted = post_state.into_sorted(); +- StateRoot::new( ++ let (root, updates) = StateRoot::new( + DatabaseTrieCursorFactory::new(tx), + HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), + ) + .with_prefix_sets(prefix_sets) +- .root_with_updates() ++ .root_with_updates()?; ++ Ok((root, updates, state_sorted)) }   -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - _state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { -@@ -449,6 +451,18 @@ - impl<C: Send + Sync, N: NodePrimitives> HashedPostStateProvider for NoopProvider<C, N> { - fn hashed_post_state(&self, _bundle_state: &revm::db::BundleState) -> HashedPostState { - HashedPostState::default() + fn overlay_root_from_nodes(tx: &'a TX, input: TrieInput) -> Result<B256, StateRootError> { +@@ -216,6 +239,28 @@ HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), + ) + .with_prefix_sets(input.prefix_sets.freeze()) + .root_with_updates() + } -+} + -+impl<C: Send + Sync, N: NodePrimitives> HashedStorageProvider for NoopProvider<C, N> { -+ fn hashed_storage(&self, _address: &BundleAccount) -> HashedStorage { -+ HashedStorage::default() ++ fn root(tx: &'a TX) -> Result<B256, StateRootError> { ++ Self::from_tx(tx).root() + } -+} + -+impl<C: Send + Sync, N: NodePrimitives> KeyHasherProvider for NoopProvider<C, N> { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ KeccakKeyHasher::hash_key(bytes) ++ fn root_with_updates(tx: &'a TX) -> Result<(B256, TrieUpdates), StateRootError> { ++ Self::from_tx(tx).root_with_updates() ++ } ++ ++ fn root_from_prefix_sets_with_updates( ++ tx: &'a TX, ++ prefix_sets: TriePrefixSets, ++ ) -> Result<(B256, TrieUpdates), StateRootError> { ++ Self::from_tx(tx).with_prefix_sets(prefix_sets).root_with_updates() ++ } ++ ++ fn root_with_progress( ++ tx: &'a TX, ++ state: Option<IntermediateStateRootState>, ++ ) -> Result<StateRootProgress, StateRootError> { ++ Self::from_tx(tx).with_intermediate_state(state).root_with_progress() } }
@@ -13726,9 +21858,9 @@
@@ -13737,13 +21869,13 @@ @@ -13753,58 +21885,122 @@
-
+11
-
-2
+
+13
+
-4
-
diff --git reth/crates/storage/storage-api/src/state.rs scroll-reth/crates/storage/storage-api/src/state.rs -index 23ba7ebb22e791d02b1539916726dc3cb9179f7e..8c15699e07ab936167d1d5267f71f214dac93e7c 100644 ---- reth/crates/storage/storage-api/src/state.rs -+++ scroll-reth/crates/storage/storage-api/src/state.rs -@@ -1,6 +1,6 @@ - use super::{ -- AccountReader, BlockHashReader, BlockIdReader, StateProofProvider, StateRootProvider, -- StorageRootProvider, -+ AccountReader, BlockHashReader, BlockIdReader, HashedStorageProvider, StateProofProvider, -+ StateRootProvider, StorageRootProvider, +
diff --git reth/crates/trie/db/src/storage.rs scroll-reth/crates/trie/db/src/storage.rs +index 5b143ac7eeea9f395c17e4212487670ffa079211..67551ec2a1dff39245bd0b3c3067e9644794eaa6 100644 +--- reth/crates/trie/db/src/storage.rs ++++ scroll-reth/crates/trie/db/src/storage.rs +@@ -4,7 +4,8 @@ use reth_db::{cursor::DbCursorRO, models::BlockNumberAddress, tables, DatabaseError}; + use reth_db_api::transaction::DbTx; + use reth_execution_errors::StorageRootError; + use reth_trie::{ +- hashed_cursor::HashedPostStateCursorFactory, HashedPostState, HashedStorage, StorageRoot, ++ hashed_cursor::HashedPostStateCursorFactory, HashedPostState, HashedStorage, KeyHasher, ++ StorageRoot, }; - use alloy_consensus::constants::KECCAK_EMPTY; - use alloy_eips::{BlockId, BlockNumberOrTag}; -@@ -24,6 +24,8 @@ + StateRootProvider - + StorageRootProvider - + StateProofProvider - + HashedPostStateProvider -+ + HashedStorageProvider -+ + KeyHasherProvider - + Send - + Sync - { -@@ -96,6 +98,13 @@ #[auto_impl(&, Arc, Box)] - pub trait HashedPostStateProvider: Send + Sync { - /// Returns the `HashedPostState` of the provided [`BundleState`]. - fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState; -+} -+ -+/// Trait that provides a method to hash bytes to produce a [`B256`] hash. -+#[auto_impl(&, Arc, Box)] -+pub trait KeyHasherProvider: Send + Sync { -+ /// Hashes the provided bytes into a 256-bit hash. -+ fn hash_key(&self, bytes: &[u8]) -> B256; +  + #[cfg(feature = "metrics")] +@@ -30,7 +31,11 @@ /// Extends [`HashedStorage`] with operations specific for working with a database transaction. + pub trait DatabaseHashedStorage<TX>: Sized { + /// Initializes [`HashedStorage`] from reverts. Iterates over storage reverts from the specified + /// block up to the current tip and aggregates them into hashed storage in reverse. +- fn from_reverts(tx: &TX, address: Address, from: BlockNumber) -> Result<Self, DatabaseError>; ++ fn from_reverts<KH: KeyHasher>( ++ tx: &TX, ++ address: Address, ++ from: BlockNumber, ++ ) -> Result<Self, DatabaseError>; }   - /// Trait implemented for database providers that can be converted into a historical state provider.
+ impl<'a, TX: DbTx> DatabaseStorageRoot<'a, TX> +@@ -79,13 +84,17 @@ } + } +  + impl<TX: DbTx> DatabaseHashedStorage<TX> for HashedStorage { +- fn from_reverts(tx: &TX, address: Address, from: BlockNumber) -> Result<Self, DatabaseError> { ++ fn from_reverts<KH: KeyHasher>( ++ tx: &TX, ++ address: Address, ++ from: BlockNumber, ++ ) -> Result<Self, DatabaseError> { + let mut storage = Self::new(false); + let mut storage_changesets_cursor = tx.cursor_read::<tables::StorageChangeSets>()?; + for entry in storage_changesets_cursor.walk_range(BlockNumberAddress((from, address))..)? { + let (BlockNumberAddress((_, storage_address)), storage_change) = entry?; + if storage_address == address { +- let hashed_slot = keccak256(storage_change.key); ++ let hashed_slot = KH::hash_key(storage_change.key); + if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) { + entry.insert(storage_change.value); + }
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-1
+ +
+ +
+
+
diff --git reth/crates/trie/sparse/src/trie.rs scroll-reth/crates/trie/sparse/src/trie.rs +index 21a64c0d6c210ad452ac5a1c1a2ab7be6b9fb04d..357673b751e8c9073fa7558c680047fbcd77bb3e 100644 +--- reth/crates/trie/sparse/src/trie.rs ++++ scroll-reth/crates/trie/sparse/src/trie.rs +@@ -2246,7 +2246,7 @@ /// We have two leaves that share the same prefix: 0x0001 and 0x0002, and a leaf with a + /// different prefix: 0x0100. Hash builder trie has only the first two leaves, and we have + /// proofs for them. + /// +- /// 1. Insert the leaf 0x0100 into the sparse trie, and check that the root extensino node was ++ /// 1. Insert the leaf 0x0100 into the sparse trie, and check that the root extension node was + /// turned into a branch node. + /// 2. Reveal the leaf 0x0001 in the sparse trie, and check that the root branch node wasn't + /// overwritten with the extension node from the proof.
-
diff --git reth/crates/storage/storage-api/src/storage.rs scroll-reth/crates/storage/storage-api/src/storage.rs -index fbdde137454fc07e51f7f3346b9a58cc8e936a90..a0f0b450b673de6c1b6b02546e944a374aa66006 100644 ---- reth/crates/storage/storage-api/src/storage.rs -+++ scroll-reth/crates/storage/storage-api/src/storage.rs -@@ -2,6 +2,8 @@ use alloy_primitives::{Address, BlockNumber, B256}; - use reth_db_api::models::BlockNumberAddress; - use reth_primitives_traits::StorageEntry; - use reth_storage_errors::provider::ProviderResult; -+use reth_trie::HashedStorage; -+use revm::db::BundleAccount; - use std::{ - collections::{BTreeMap, BTreeSet}, - ops::RangeInclusive, -@@ -64,3 +66,10 @@ pub const fn database(&self) -> bool { - matches!(self, Self::Database | Self::Both) - } - } -+ -+/// Provider of [`HashedStorage`] -+#[auto_impl::auto_impl(&, Arc, Box)] -+pub trait HashedStorageProvider: Send + Sync { -+ /// Construct [`HashedStorage`] from the provided [`BundleAccount`]. -+ fn hashed_storage(&self, account: &BundleAccount) -> HashedStorage; -+}
+
diff --git reth/crates/trie/trie/Cargo.toml scroll-reth/crates/trie/trie/Cargo.toml +index 4fa525a74bb38f43d0ba467627471a267658350e..bcf8f8529dd11a96c277473c946139f4f7c89e6d 100644 +--- reth/crates/trie/trie/Cargo.toml ++++ scroll-reth/crates/trie/trie/Cargo.toml +@@ -20,6 +20,8 @@ reth-storage-errors.workspace = true + reth-trie-sparse.workspace = true + reth-trie-common.workspace = true +  ++# revm re-export scroll ++reth-primitives-traits.workspace = true + revm.workspace = true +  + # alloy +@@ -67,15 +69,19 @@ "alloy-trie/serde", + "alloy-eips/serde", + "revm/serde", + "reth-trie-common/serde", ++ "reth-primitives-traits/serde", + ] + test-utils = [ + "triehash", + "revm/test-utils", + "reth-primitives/test-utils", + "reth-trie-common/test-utils", ++ "reth-trie-common/test-utils", + "reth-trie-sparse/test-utils", + "reth-stages-types/test-utils", ++ "reth-primitives-traits/test-utils" + ] ++scroll = ["revm/scroll"] +  + [[bench]] + name = "hash_post_state"
@@ -13877,13 +22082,13 @@ @@ -13893,91 +22098,125 @@
-
+41
-
-5
+
+26
+
-9
-
diff --git reth/crates/storage/storage-api/src/trie.rs scroll-reth/crates/storage/storage-api/src/trie.rs -index fefa7e4508d55b95abdc993da96db4a1bb1f5138..2223b551c26fe24978523cd3274ab3921bbebcfa 100644 ---- reth/crates/storage/storage-api/src/trie.rs -+++ scroll-reth/crates/storage/storage-api/src/trie.rs -@@ -1,9 +1,11 @@ --use alloy_primitives::{map::B256HashMap, Address, Bytes, B256}; -+use alloy_primitives::{map::B256HashMap, Address, BlockNumber, Bytes, B256}; - use reth_storage_errors::provider::ProviderResult; - use reth_trie::{ -+ prefix_set::TriePrefixSets, - updates::{StorageTrieUpdates, TrieUpdates}, -- AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof, -- StorageProof, TrieInput, -+ AccountProof, HashedPostState, HashedPostStateSorted, HashedStorage, -+ IntermediateStateRootState, MultiProof, MultiProofTargets, StateRootProgress, -+ StorageMultiProof, StorageProof, TrieInput, +
diff --git reth/crates/trie/trie/src/state.rs scroll-reth/crates/trie/trie/src/state.rs +index 510b914ceaf928245b66f44a85b6e8cf2f24828d..6704ce2e0b407f58cb18f385038f267c49272cda 100644 +--- reth/crates/trie/trie/src/state.rs ++++ scroll-reth/crates/trie/trie/src/state.rs +@@ -3,7 +3,6 @@ prefix_set::{PrefixSetMut, TriePrefixSetsMut}, + Nibbles, + }; + use alloy_primitives::{ +- keccak256, + map::{hash_map, B256HashMap, B256HashSet, HashMap, HashSet}, + Address, B256, U256, }; +@@ -34,8 +33,8 @@ let hashed = state + .into_par_iter() + .map(|(address, account)| { + let hashed_address = KH::hash_key(address); +- let hashed_account = account.info.as_ref().map(Into::into); +- let hashed_storage = HashedStorage::from_plain_storage( ++ let hashed_account = account.info.clone().map(Into::into); ++ let hashed_storage = HashedStorage::from_plain_storage::<KH>( + account.status, + account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), + ); +@@ -62,7 +61,7 @@ .into_par_iter() + .map(|(address, account)| { + let hashed_address = KH::hash_key(address); + let hashed_account = account.account.as_ref().map(|a| (&a.info).into()); +- let hashed_storage = HashedStorage::from_plain_storage( ++ let hashed_storage = HashedStorage::from_plain_storage::<KH>( + account.status, + account.account.as_ref().map(|a| a.storage.iter()).into_iter().flatten(), + ); +@@ -129,6 +128,7 @@ // Populate storage prefix sets. + let mut storage_prefix_sets = + HashMap::with_capacity_and_hasher(self.storages.len(), Default::default()); + for (hashed_address, hashed_storage) in &self.storages { ++ // TODO(scroll): replace this with abstraction. + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + storage_prefix_sets.insert(*hashed_address, hashed_storage.construct_prefix_set()); + } +@@ -224,14 +224,25 @@ pub fn from_iter(wiped: bool, iter: impl IntoIterator<Item = (B256, U256)>) -> Self { + Self { wiped, storage: HashMap::from_iter(iter) } + }   - /// A type that can compute the state root of a given post state. -@@ -16,7 +18,7 @@ /// - /// It is recommended to provide a different implementation from - /// `state_root_with_updates` since it affects the memory usage during state root - /// computation. -- fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256>; -+ fn state_root_from_state(&self, hashed_state: HashedPostState) -> ProviderResult<B256>; ++ /// Create a new hashed storage from the provided [`BundleAccount`] ++ /// ++ /// This function will use the present value of the storage slots in the account to create the ++ /// hashed storage. ++ pub fn from_bundle_account<KH: KeyHasher>(account: &BundleAccount) -> Self { ++ Self::from_plain_storage::<KH>( ++ account.status, ++ account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), ++ ) ++ } ++ + /// Create new hashed storage from account status and plain storage. +- pub fn from_plain_storage<'a>( ++ pub fn from_plain_storage<'a, KH: KeyHasher>( + status: AccountStatus, + storage: impl IntoIterator<Item = (&'a U256, &'a U256)>, + ) -> Self { + Self::from_iter( + status.was_destroyed(), +- storage.into_iter().map(|(key, value)| (keccak256(B256::from(*key)), *value)), ++ storage.into_iter().map(|(key, value)| (KH::hash_key(B256::from(*key)), *value)), + ) + }   - /// Returns the state root of the `HashedPostState` on top of the current state but re-uses the - /// intermediate nodes to speed up the computation. It's up to the caller to construct the -@@ -25,7 +27,7 @@ fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256>; +@@ -353,13 +364,14 @@ }   - /// Returns the state root of the `HashedPostState` on top of the current state with trie - /// updates to be committed to the database. -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - hashed_state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)>; -@@ -36,6 +38,40 @@ fn state_root_from_nodes_with_updates( - &self, - input: TrieInput, - ) -> ProviderResult<(B256, TrieUpdates)>; -+} -+ -+/// A trait that is used to compute the state root of the latest state stored in the database. -+pub trait StateRootProviderExt: Send + Sync { -+ /// Returns the state root of the current state. -+ fn state_root(&self) -> ProviderResult<B256>; -+ -+ /// Returns the state root of the current state and trie updates. -+ fn state_root_with_updates(&self) -> ProviderResult<(B256, TrieUpdates)>; -+ -+ /// Returns the state root with trie updates associated with the given block range. -+ fn incremental_state_root_with_updates( -+ &self, -+ range: std::ops::RangeInclusive<BlockNumber>, -+ ) -> ProviderResult<(B256, TrieUpdates)>; -+ -+ /// Returns the state root progress. -+ fn state_root_with_progress( -+ &self, -+ state: Option<IntermediateStateRootState>, -+ ) -> ProviderResult<StateRootProgress>; -+ -+ /// Returns the state root of the current state with the provided prefix sets updated. -+ fn state_root_from_prefix_sets_with_updates( -+ &self, -+ prefix_set: TriePrefixSets, -+ ) -> ProviderResult<(B256, TrieUpdates)>; -+ -+ /// Returns the state root of the [`HashedPostState`] on top of the current, the trie updates -+ /// and the sorted hashed post state ([`HashedPostStateSorted`]). -+ fn state_root_from_state_with_updates_and_sorted_state( -+ &self, -+ hashed_state: HashedPostState, -+ ) -> ProviderResult<(B256, TrieUpdates, HashedPostStateSorted)>; - } + #[cfg(test)] + mod tests { ++ #![allow(clippy::needless_update)] + use super::*; +- use alloy_primitives::Bytes; ++ use alloy_primitives::{keccak256, Address, Bytes}; + use reth_trie_common::KeccakKeyHasher; + use revm::{ + db::{ +- states::{plain_account::PlainStorage, StorageSlot}, +- PlainAccount, StorageWithOriginalValues, ++ states::{plain_account::PlainStorage, CacheAccount, StorageSlot}, ++ BundleAccount, PlainAccount, StorageWithOriginalValues, + }, + primitives::{AccountInfo, Bytecode}, + }; +@@ -449,6 +461,8 @@ balance: U256::from(123), + nonce: 42, + code_hash: B256::random(), + code: Some(Bytecode::LegacyRaw(Bytes::from(vec![1, 2]))), ++ #[cfg(feature = "scroll")] ++ code_size: 2, + };   - /// A type that can compute the storage root for a given account.
+ let mut storage = StorageWithOriginalValues::default(); +@@ -493,6 +507,7 @@ balance: U256::from(500), + nonce: 5, + code_hash: B256::random(), + code: None, ++ ..Default::default() + }; +  + let mut storage = PlainStorage::default(); +@@ -532,6 +547,8 @@ balance: U256::from(1000), + nonce: 1, + code_hash: B256::random(), + code: None, ++ #[cfg(feature = "scroll")] ++ code_size: 10, + }; +  + // Create hashed accounts with addresses.
@@ -13990,9 +22229,9 @@
-
+
+ - -
-
diff --git reth/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs scroll-reth/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs -index d4c0f3c6c408817fd65b29bdbd97b651b543fff9..29cc206c1f6dd29ed2b0eca0133e279e74e5c6b0 100644 ---- reth/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs -+++ scroll-reth/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs -@@ -24,13 +24,12 @@ use reth_primitives::{BlockExt, EthPrimitives}; - use reth_provider::{ - providers::ProviderNodeTypes, AccountExtReader, ChainSpecProvider, DatabaseProviderFactory, - HashedPostStateProvider, HashingWriter, LatestStateProviderRef, OriginalValuesKnown, -- ProviderFactory, StageCheckpointReader, StateWriter, StorageLocation, StorageReader, -+ ProviderFactory, StageCheckpointReader, StateRootProvider, StateRootProviderExt, StateWriter, -+ StorageLocation, StorageReader, - }; - use reth_revm::database::StateProviderDatabase; - use reth_stages::StageId; - use reth_tasks::TaskExecutor; --use reth_trie::StateRoot; --use reth_trie_db::DatabaseStateRoot; - use std::{path::PathBuf, sync::Arc}; - use tracing::*; -  -@@ -156,10 +155,10 @@ )?; - let execution_outcome = ExecutionOutcome::from((block_execution_output, block.number)); -  - // Unpacked `BundleState::state_root_slow` function -- let (in_memory_state_root, in_memory_updates) = StateRoot::overlay_root_with_updates( -- provider.tx_ref(), -- state_provider.hashed_post_state(execution_outcome.state()), -- )?; -+ let (in_memory_state_root, in_memory_updates) = state_provider -+ .state_root_from_state_with_updates( -+ state_provider.hashed_post_state(execution_outcome.state()), -+ )?; -  - if in_memory_state_root == block.state_root { - info!(target: "reth::cli", state_root = ?in_memory_state_root, "Computed in-memory state root matches"); -@@ -187,10 +186,8 @@ let account_lists = provider_rw.changed_accounts_with_range(block.number..=block.number)?; - let accounts = provider_rw.basic_accounts(account_lists)?; - provider_rw.insert_account_for_hashing(accounts)?; -  -- let (state_root, incremental_trie_updates) = StateRoot::incremental_root_with_updates( -- provider_rw.tx_ref(), -- block.number..=block.number, -- )?; -+ let (state_root, incremental_trie_updates) = -+ state_provider.incremental_state_root_with_updates(block.number..=block.number)?; - if state_root != block.state_root { - eyre::bail!( - "Computed incremental state root mismatch. Expected: {:?}. Got: {:?}",
+
diff --git reth/crates/engine/tree/benches/state_root_task.rs scroll-reth/crates/engine/tree/benches/state_root_task.rs +index 8c5b871385abb2e1fc8fd8f60cf3b22c8387a3be..85afe8c82b9cceb7ceb7069aaabc4dc2d11ef13f 100644 +--- reth/crates/engine/tree/benches/state_root_task.rs ++++ scroll-reth/crates/engine/tree/benches/state_root_task.rs +@@ -60,11 +60,13 @@ status: AccountStatus::SelfDestructed, + } + } else { + RevmAccount { ++ #[allow(clippy::needless_update)] + info: AccountInfo { + balance: U256::from(rng.gen::<u64>()), + nonce: rng.gen::<u64>(), + code_hash: KECCAK_EMPTY, + code: Some(Default::default()), ++ ..Default::default() + }, + storage: (0..rng.gen_range(0..=params.storage_slots_per_account)) + .map(|_| {
@@ -15622,13 +24485,13 @@ @@ -15638,73 +24501,83 @@
-
+20
-
-6
+
+23
+
-10
-
diff --git reth/crates/chain-state/src/in_memory.rs scroll-reth/crates/chain-state/src/in_memory.rs -index fac148d4a716dc8fdcb347449b3aff396baa33ab..9edc42d5e5053adf89b20023519edcc278cf110d 100644 ---- reth/crates/chain-state/src/in_memory.rs -+++ scroll-reth/crates/chain-state/src/in_memory.rs -@@ -949,13 +949,15 @@ use rand::Rng; - use reth_errors::ProviderResult; - use reth_primitives::{Account, Bytecode, EthPrimitives, Receipt}; - use reth_storage_api::{ -- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, -- StateRootProvider, StorageRootProvider, -+ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, -+ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, -+ StorageRootProvider, - }; - use reth_trie::{ -- AccountProof, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof, -- StorageProof, TrieInput, -+ AccountProof, HashedStorage, KeccakKeyHasher, KeyHasher, MultiProof, MultiProofTargets, -+ StorageMultiProof, StorageProof, TrieInput, - }; -+ use revm::db::BundleAccount; +
diff --git reth/crates/engine/tree/src/tree/cached_state.rs scroll-reth/crates/engine/tree/src/tree/cached_state.rs +index 84d2c8a0922523f17b5f966dc19531a3b04ea5ab..0057c4c7b438139ea994410867cd26db3c6c6f6e 100644 +--- reth/crates/engine/tree/src/tree/cached_state.rs ++++ scroll-reth/crates/engine/tree/src/tree/cached_state.rs +@@ -6,9 +6,10 @@ use reth_errors::ProviderResult; + use reth_metrics::Metrics; + use reth_primitives::{Account, Bytecode}; + use reth_provider::{ +- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, +- StateRootProvider, StorageRootProvider, ++ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, ++ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, + }; ++use reth_revm::db::BundleAccount; + use reth_trie::{ + updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, + MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, +@@ -139,26 +140,26 @@ } + }   - fn create_mock_state( - test_block_builder: &mut TestBlockBuilder<EthPrimitives>, -@@ -1025,7 +1027,7 @@ } + impl<S: StateRootProvider> StateRootProvider for CachedStateProvider<S> { +- fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { +- self.state_provider.state_root(hashed_state) ++ fn state_root_from_state(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { ++ self.state_provider.state_root_from_state(hashed_state) }   - impl StateRootProvider for MockStateProvider { -- fn state_root(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { -+ fn state_root_from_state(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { - Ok(B256::random()) - } + fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> { + self.state_provider.state_root_from_nodes(input) + }   -@@ -1033,7 +1035,7 @@ fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> { - Ok(B256::random()) - } +- fn state_root_from_nodes_with_updates( ++ fn state_root_from_state_with_updates( + &self, +- input: TrieInput, ++ hashed_state: HashedPostState, + ) -> ProviderResult<(B256, TrieUpdates)> { +- self.state_provider.state_root_from_nodes_with_updates(input) ++ self.state_provider.state_root_from_state_with_updates(hashed_state) + }   -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - _hashed_state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { -@@ -1051,6 +1053,18 @@ - impl HashedPostStateProvider for MockStateProvider { - fn hashed_post_state(&self, _bundle_state: &revm::db::BundleState) -> HashedPostState { - HashedPostState::default() -+ } +- fn state_root_with_updates( ++ fn state_root_from_nodes_with_updates( + &self, +- hashed_state: HashedPostState, ++ input: TrieInput, + ) -> ProviderResult<(B256, TrieUpdates)> { +- self.state_provider.state_root_with_updates(hashed_state) ++ self.state_provider.state_root_from_nodes_with_updates(input) + } + } +  +@@ -186,6 +187,18 @@ input: TrieInput, + target: HashedPostState, + ) -> ProviderResult<B256HashMap<alloy_primitives::Bytes>> { + self.state_provider.witness(input, target) + } ++} + -+ impl HashedStorageProvider for MockStateProvider { -+ fn hashed_storage(&self, _account: &BundleAccount) -> HashedStorage { -+ HashedStorage::default() -+ } ++impl<S: StateProvider> HashedStorageProvider for CachedStateProvider<S> { ++ fn hashed_storage(&self, account: &BundleAccount) -> HashedStorage { ++ self.state_provider.hashed_storage(account) + } ++} + -+ impl KeyHasherProvider for MockStateProvider { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ KeccakKeyHasher::hash_key(bytes) - } ++impl<S: StateProvider> KeyHasherProvider for CachedStateProvider<S> { ++ fn hash_key(&self, bytes: &[u8]) -> B256 { ++ self.state_provider.hash_key(bytes) } + }
@@ -15712,9 +24585,9 @@
@@ -15723,13 +24596,13 @@ @@ -15739,81 +24612,101 @@
-
+17
-
-5
+
+16
+
-10
-
diff --git reth/crates/chain-state/src/memory_overlay.rs scroll-reth/crates/chain-state/src/memory_overlay.rs -index 880c95ab3c37e1d5146a4d5741add33e92dfb703..e4673e27e2f56752f5906279dfbdf87bdad84c98 100644 ---- reth/crates/chain-state/src/memory_overlay.rs -+++ scroll-reth/crates/chain-state/src/memory_overlay.rs -@@ -6,14 +6,14 @@ }; - use reth_errors::ProviderResult; - use reth_primitives::{Account, Bytecode, NodePrimitives}; - use reth_storage_api::{ -- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, -- StateRootProvider, StorageRootProvider, -+ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, -+ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, - }; - use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, - MultiProofTargets, StorageMultiProof, TrieInput, +
diff --git reth/crates/engine/tree/src/tree/mod.rs scroll-reth/crates/engine/tree/src/tree/mod.rs +index ea92c0fb389807252e612f0e1ce0a2e38afd41a0..c02cbf155c92d70f07a65564ca21b611e14c6fef 100644 +--- reth/crates/engine/tree/src/tree/mod.rs ++++ scroll-reth/crates/engine/tree/src/tree/mod.rs +@@ -36,8 +36,7 @@ use reth_payload_builder::PayloadBuilderHandle; + use reth_payload_builder_primitives::PayloadBuilder; + use reth_payload_primitives::PayloadBuilderAttributes; + use reth_primitives::{ +- EthPrimitives, GotExpected, NodePrimitives, SealedBlockFor, SealedBlockWithSenders, +- SealedHeader, ++ EthPrimitives, NodePrimitives, SealedBlockFor, SealedBlockWithSenders, SealedHeader, }; --use revm::db::BundleState; -+use revm::db::{BundleAccount, BundleState}; - use std::sync::OnceLock; + use reth_primitives_traits::Block; + use reth_provider::{ +@@ -2379,8 +2378,8 @@ if task_state_root != block.header().state_root() { + debug!(target: "engine::tree", "Task state root does not match block state root"); + }   - /// A state provider that stores references to in-memory blocks along with their state as well as a -@@ -108,7 +108,7 @@ } - } +- let (regular_root, regular_updates) = +- state_provider.state_root_with_updates(hashed_state.clone())?; ++ let (regular_root, regular_updates) = state_provider ++ .state_root_from_state_with_updates(hashed_state.clone())?;   - impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_, N> { -- fn state_root(&self, state: HashedPostState) -> ProviderResult<B256> { -+ fn state_root_from_state(&self, state: HashedPostState) -> ProviderResult<B256> { - self.state_root_from_nodes(TrieInput::from_state(state)) - } + if regular_root == block.header().state_root() { + let provider = self.provider.database_provider_ro()?; +@@ -2400,8 +2399,8 @@ } + Err(error) => { + info!(target: "engine::tree", ?error, "Failed to wait for state root task result"); + // Fall back to sequential calculation +- let (root, updates) = +- state_provider.state_root_with_updates(hashed_state.clone())?; ++ let (root, updates) = state_provider ++ .state_root_from_state_with_updates(hashed_state.clone())?; + (root, updates, root_time.elapsed()) + } + } +@@ -2422,8 +2421,8 @@ Err(ParallelStateRootError::Provider(ProviderError::ConsistentView( + error, + ))) => { + debug!(target: "engine", %error, "Parallel state root computation failed consistency check, falling back"); +- let (root, updates) = +- state_provider.state_root_with_updates(hashed_state.clone())?; ++ let (root, updates) = state_provider ++ .state_root_from_state_with_updates(hashed_state.clone())?; + (root, updates, root_time.elapsed()) + } + Err(error) => return Err(InsertBlockErrorKind::Other(Box::new(error))), +@@ -2432,7 +2431,7 @@ } + } else { + debug!(target: "engine::tree", block=?sealed_block.num_hash(), ?persistence_not_in_progress, "Failed to compute state root in parallel"); + let (root, updates) = +- state_provider.state_root_with_updates(hashed_state.clone())?; ++ state_provider.state_root_from_state_with_updates(hashed_state.clone())?; + (root, updates, root_time.elapsed()) + };   -@@ -118,7 +118,7 @@ input.prepend_cached(nodes, state); - self.historical.state_root_from_nodes(input) - } +@@ -2447,6 +2446,9 @@ })?;   -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { -@@ -210,6 +210,18 @@ - impl<N: NodePrimitives> HashedPostStateProvider for MemoryOverlayStateProviderRef<'_, N> { - fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState { - self.historical.hashed_post_state(bundle_state) -+ } -+} -+ -+impl<N: NodePrimitives> HashedStorageProvider for MemoryOverlayStateProviderRef<'_, N> { -+ fn hashed_storage(&self, account: &BundleAccount) -> HashedStorage { -+ self.historical.hashed_storage(account) -+ } -+} -+ -+impl<N: NodePrimitives> KeyHasherProvider for MemoryOverlayStateProviderRef<'_, N> { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ self.historical.hash_key(bytes) - } - } -
+ let (state_root, trie_output, hashed_state, output, root_elapsed) = state_root_result; +  ++ #[cfg(feature = "skip-state-root-validation")] ++ let _ = state_root; ++ #[cfg(not(feature = "skip-state-root-validation"))] + if state_root != block.header().state_root() { + // call post-block hook + self.invalid_block_hook.on_invalid_block( +@@ -2456,7 +2458,11 @@ &output, + Some((&trie_output, state_root)), + ); + return Err(ConsensusError::BodyStateRootDiff( +- GotExpected { got: state_root, expected: block.header().state_root() }.into(), ++ reth_primitives::GotExpected { ++ got: state_root, ++ expected: block.header().state_root(), ++ } ++ .into(), + ) + .into()) + }
@@ -15822,13 +24715,13 @@ @@ -15838,46 +24731,42 @@
-
+3
-
-1
+
+2
+
-0
-
diff --git reth/crates/chainspec/src/spec.rs scroll-reth/crates/chainspec/src/spec.rs -index 3816175c46cb8b575047cd6f7447cbaecfed5d30..30adbf2c665c1a813064d17c0f28d7621f99821d 100644 ---- reth/crates/chainspec/src/spec.rs -+++ scroll-reth/crates/chainspec/src/spec.rs -@@ -26,7 +26,7 @@ ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS, - }; - use reth_network_peers::{ - base_nodes, base_testnet_nodes, holesky_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, -- sepolia_nodes, NodeRecord, -+ scroll_nodes, scroll_sepolia_nodes, sepolia_nodes, NodeRecord, - }; - use reth_primitives_traits::{ - sync::{LazyLock, OnceLock}, -@@ -633,8 +633,10 @@ C::Sepolia => Some(sepolia_nodes()), - C::Holesky => Some(holesky_nodes()), - C::Base => Some(base_nodes()), - C::Optimism => Some(op_nodes()), -+ C::Scroll => Some(scroll_nodes()), - C::BaseGoerli | C::BaseSepolia => Some(base_testnet_nodes()), - C::OptimismSepolia | C::OptimismGoerli | C::OptimismKovan => Some(op_testnet_nodes()), -+ C::ScrollSepolia => Some(scroll_sepolia_nodes()), - _ => None, - } - }
+
diff --git reth/crates/engine/tree/src/tree/root.rs scroll-reth/crates/engine/tree/src/tree/root.rs +index 93cac7b435ed87d96730c77bf922834fc5a6b008..a28841917991e9623d1fb3e0776794e53a993b91 100644 +--- reth/crates/engine/tree/src/tree/root.rs ++++ scroll-reth/crates/engine/tree/src/tree/root.rs +@@ -837,6 +837,7 @@ } +  + #[cfg(test)] + mod tests { ++ #![allow(clippy::needless_update)] + use super::*; + use reth_primitives::{Account as RethAccount, StorageEntry}; + use reth_provider::{ +@@ -895,6 +896,7 @@ balance: U256::from(rng.gen::<u64>()), + nonce: rng.gen::<u64>(), + code_hash: KECCAK_EMPTY, + code: Some(Default::default()), ++ ..Default::default() + }, + storage, + status: AccountStatus::Touched,
@@ -15886,13 +24775,13 @@ @@ -15902,44 +24791,88 @@
-
+2
-
-2
+
+1
+
-1
-
diff --git reth/crates/cli/commands/Cargo.toml scroll-reth/crates/cli/commands/Cargo.toml -index c5f6cef0d9085b4103c8bf112efbf1315a3ccc49..970d7e961ef13216b1cec61ced80a4430d9f2baf 100644 ---- reth/crates/cli/commands/Cargo.toml -+++ scroll-reth/crates/cli/commands/Cargo.toml -@@ -45,8 +45,7 @@ reth-stages.workspace = true - reth-stages-types = { workspace = true, optional = true } - reth-static-file-types = { workspace = true, features = ["clap"] } - reth-static-file.workspace = true --reth-trie = { workspace = true, features = ["metrics"] } --reth-trie-db = { workspace = true, features = ["metrics"] } -+reth-trie = { workspace = true, optional = true } - reth-trie-common = { workspace = true, optional = true } -  - # ethereum -@@ -113,6 +112,7 @@ "reth-trie-common/test-utils", - "reth-codecs/arbitrary", - "reth-prune-types?/arbitrary", - "reth-stages-types?/arbitrary", -+ "reth-trie", - "reth-trie-common?/arbitrary", - "alloy-consensus/arbitrary", - ]
+
diff --git reth/crates/engine/util/src/reorg.rs scroll-reth/crates/engine/util/src/reorg.rs +index 073f83545ab69f9bedde922c7f4fe902d33eb58f..97c23111a65c6758b226e8b9d947db35cfe54cdf 100644 +--- reth/crates/engine/util/src/reorg.rs ++++ scroll-reth/crates/engine/util/src/reorg.rs +@@ -422,7 +422,7 @@ logs_bloom: outcome.block_logs_bloom(reorg_target.header.number).unwrap(), + gas_used: cumulative_gas_used, + blob_gas_used, + excess_blob_gas, +- state_root: state_provider.state_root(hashed_state)?, ++ state_root: state_provider.state_root_from_state(hashed_state)?, + requests_hash: None, // TODO(prague) + }, + body: BlockBody {
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-1
+ +
+ +
+
+
diff --git reth/crates/ethereum-forks/src/display.rs scroll-reth/crates/ethereum-forks/src/display.rs +index 99e753cbb81265d1ac166d8c0ef763c9d55ac1cd..26ab5ced192ae9bf9d92cc7ffb2006858cb82425 100644 +--- reth/crates/ethereum-forks/src/display.rs ++++ scroll-reth/crates/ethereum-forks/src/display.rs +@@ -118,7 +118,7 @@ + format( + "Pre-merge hard forks (block based)", + &self.pre_merge, +- self.with_merge.is_empty(), ++ self.with_merge.is_empty() && self.post_merge.is_empty(), + f, + )?; +
@@ -15948,13 +24881,13 @@ @@ -15964,49 +24897,40 @@
-
+5
-
-4
+
+8
+
-1
-
diff --git reth/crates/cli/commands/src/recover/storage_tries.rs scroll-reth/crates/cli/commands/src/recover/storage_tries.rs -index 4ae32ff8f68bd016977ee9163c0603de45e1e5b0..43e72d162aee078b6a8c23006c15aa32e0bb24ab 100644 ---- reth/crates/cli/commands/src/recover/storage_tries.rs -+++ scroll-reth/crates/cli/commands/src/recover/storage_tries.rs -@@ -9,9 +9,10 @@ use reth_db_api::{ - cursor::{DbCursorRO, DbDupCursorRW}, - transaction::DbTx, - }; --use reth_provider::{BlockNumReader, HeaderProvider, ProviderError}; --use reth_trie::StateRoot; --use reth_trie_db::DatabaseStateRoot; -+use reth_provider::{ -+ BlockNumReader, HeaderProvider, LatestStateProviderRef, ProviderError, StateRootProviderExt, -+}; -+ - use tracing::*; -  - /// `reth recover storage-tries` command -@@ -51,7 +52,7 @@ - entry = storage_trie_cursor.next()?; - } -  -- let state_root = StateRoot::from_tx(tx_mut).root()?; -+ let state_root = LatestStateProviderRef::new(&provider.0).state_root()?; - if state_root != best_header.state_root() { - eyre::bail!( - "Recovery failed. Incorrect state root. Expected: {:?}. Received: {:?}",
+
diff --git reth/crates/evm/Cargo.toml scroll-reth/crates/evm/Cargo.toml +index 8f58e824127583edbdef4ef7e50977d7c2da7510..d773a234031fa819232e1847faa38df253b59cbd 100644 +--- reth/crates/evm/Cargo.toml ++++ scroll-reth/crates/evm/Cargo.toml +@@ -69,5 +69,12 @@ "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-revm/test-utils", + "revm/test-utils", +- "reth-prune-types/test-utils" ++ "reth-prune-types/test-utils", ++] ++scroll = [ ++ "revm/scroll", ++ "reth-primitives-traits/scroll", ++ "reth-primitives/scroll", ++ "reth-revm/scroll", ++ "reth-execution-types/scroll", + ]
@@ -16015,13 +24939,13 @@ @@ -16031,190 +24955,34 @@
-
+71
-
-39
+
+4
+
-0
-
diff --git reth/crates/consensus/debug-client/src/client.rs scroll-reth/crates/consensus/debug-client/src/client.rs -index 0e2a50370b85c9c3d3e2cc01d1b4778264b34a50..96ff3f5126381f66789919009a47935ef4c25ca9 100644 ---- reth/crates/consensus/debug-client/src/client.rs -+++ scroll-reth/crates/consensus/debug-client/src/client.rs -@@ -1,7 +1,10 @@ - use alloy_consensus::Transaction; - use alloy_eips::eip2718::Encodable2718; - use alloy_primitives::B256; --use alloy_rpc_types_engine::{ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}; -+use alloy_rpc_types_engine::{ -+ ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV2, -+ ExecutionPayloadV3, -+}; - use alloy_rpc_types_eth::{Block, BlockTransactions}; - use reth_node_api::EngineTypes; - use reth_rpc_builder::auth::AuthServerHandle; -@@ -85,7 +88,7 @@ rx - }; -  - while let Some(block) = block_stream.recv().await { -- let payload = block_to_execution_payload_v3(block); -+ let payload = block_to_execution_payload(block); -  - let block_hash = payload.block_hash(); - let block_number = payload.block_number(); -@@ -93,12 +96,25 @@ - previous_block_hashes.push(block_hash); -  - // Send new events to execution client -- let _ = reth_rpc_api::EngineApiClient::<T>::new_payload_v3( -- &execution_client, -- payload.execution_payload_v3, -- payload.versioned_hashes, -- payload.parent_beacon_block_root, -- ) -+ let fut = match payload.execution_payload { -+ ExecutionPayload::V1(v1) => { -+ reth_rpc_api::EngineApiClient::<T>::new_payload_v1(&execution_client, v1) -+ } -+ ExecutionPayload::V2(v2) => reth_rpc_api::EngineApiClient::<T>::new_payload_v2( -+ &execution_client, -+ ExecutionPayloadInputV2 { -+ execution_payload: v2.payload_inner, -+ withdrawals: Some(v2.withdrawals), -+ }, -+ ), -+ ExecutionPayload::V3(v3) => reth_rpc_api::EngineApiClient::<T>::new_payload_v3( -+ &execution_client, -+ v3, -+ payload.versioned_hashes, -+ payload.parent_beacon_block_root.unwrap(), -+ ), -+ }; -+ let _ = fut - .await - .inspect_err(|err| { - warn!(target: "consensus::debug-client", %err, %block_hash, %block_number, "failed to submit new payload to execution client"); -@@ -151,26 +167,34 @@ - /// Cancun "new payload" event. - #[derive(Debug)] - pub struct ExecutionNewPayload { -- pub execution_payload_v3: ExecutionPayloadV3, -+ pub execution_payload: ExecutionPayload, - pub versioned_hashes: Vec<B256>, -- pub parent_beacon_block_root: B256, -+ pub parent_beacon_block_root: Option<B256>, - } -  - impl ExecutionNewPayload { - /// Get block hash from block in the payload - pub const fn block_hash(&self) -> B256 { -- self.execution_payload_v3.payload_inner.payload_inner.block_hash -+ match self.execution_payload { -+ ExecutionPayload::V1(ref v1) => v1.block_hash, -+ ExecutionPayload::V2(ref v2) => v2.payload_inner.block_hash, -+ ExecutionPayload::V3(ref v3) => v3.payload_inner.payload_inner.block_hash, -+ } - } -  - /// Get block number from block in the payload - pub const fn block_number(&self) -> u64 { -- self.execution_payload_v3.payload_inner.payload_inner.block_number -+ match self.execution_payload { -+ ExecutionPayload::V1(ref v1) => v1.block_number, -+ ExecutionPayload::V2(ref v2) => v2.payload_inner.block_number, -+ ExecutionPayload::V3(ref v3) => v3.payload_inner.payload_inner.block_number, -+ } - } - } -  - /// Convert a block from RPC / Etherscan to params for an execution client's "new payload" - /// method. Assumes that the block contains full transactions. --pub fn block_to_execution_payload_v3(block: Block) -> ExecutionNewPayload { -+pub fn block_to_execution_payload(block: Block) -> ExecutionNewPayload { - let transactions = match &block.transactions { - BlockTransactions::Full(txs) => txs.clone(), - // Empty array gets deserialized as BlockTransactions::Hashes. -@@ -188,36 +212,44 @@ .flat_map(|tx| tx.blob_versioned_hashes().unwrap_or_default()) - .copied() - .collect(); -  -- let payload: ExecutionPayloadV3 = ExecutionPayloadV3 { -- payload_inner: ExecutionPayloadV2 { -- payload_inner: ExecutionPayloadV1 { -- parent_hash: block.header.parent_hash, -- fee_recipient: block.header.beneficiary, -- state_root: block.header.state_root, -- receipts_root: block.header.receipts_root, -- logs_bloom: block.header.logs_bloom, -- prev_randao: block.header.mix_hash, -- block_number: block.header.number, -- gas_limit: block.header.gas_limit, -- gas_used: block.header.gas_used, -- timestamp: block.header.timestamp, -- extra_data: block.header.extra_data.clone(), -- base_fee_per_gas: block.header.base_fee_per_gas.unwrap().try_into().unwrap(), -- block_hash: block.header.hash, -- transactions: transactions -- .into_iter() -- .map(|tx| tx.inner.encoded_2718().into()) -- .collect(), -+ let payload_v1 = ExecutionPayloadV1 { -+ parent_hash: block.header.parent_hash, -+ fee_recipient: block.header.beneficiary, -+ state_root: block.header.state_root, -+ receipts_root: block.header.receipts_root, -+ logs_bloom: block.header.logs_bloom, -+ prev_randao: block.header.mix_hash, -+ block_number: block.header.number, -+ gas_limit: block.header.gas_limit, -+ gas_used: block.header.gas_used, -+ timestamp: block.header.timestamp, -+ extra_data: block.header.extra_data.clone(), -+ base_fee_per_gas: block.header.base_fee_per_gas.unwrap().try_into().unwrap(), -+ block_hash: block.header.hash, -+ transactions: transactions.into_iter().map(|tx| tx.inner.encoded_2718().into()).collect(), -+ }; -+ -+ let payload = if block.withdrawals.is_none() { -+ ExecutionPayload::V1(payload_v1) -+ } else if block.header.blob_gas_used.is_none() { -+ ExecutionPayload::V2(ExecutionPayloadV2 { -+ payload_inner: payload_v1, -+ withdrawals: block.withdrawals.clone().unwrap_or_default().into_inner(), -+ }) -+ } else { -+ ExecutionPayload::V3(ExecutionPayloadV3 { -+ payload_inner: ExecutionPayloadV2 { -+ payload_inner: payload_v1, -+ withdrawals: block.withdrawals.clone().unwrap_or_default().into_inner(), - }, -- withdrawals: block.withdrawals.clone().unwrap_or_default().into_inner(), -- }, -- blob_gas_used: block.header.blob_gas_used.unwrap(), -- excess_blob_gas: block.header.excess_blob_gas.unwrap(), -+ blob_gas_used: block.header.blob_gas_used.unwrap(), -+ excess_blob_gas: block.header.excess_blob_gas.unwrap(), -+ }) - }; -  - ExecutionNewPayload { -- execution_payload_v3: payload, -+ execution_payload: payload, - versioned_hashes, -- parent_beacon_block_root: block.header.parent_beacon_block_root.unwrap(), -+ parent_beacon_block_root: block.header.parent_beacon_block_root, - } - }
+
diff --git reth/crates/evm/execution-types/Cargo.toml scroll-reth/crates/evm/execution-types/Cargo.toml +index 5a415f0b8892647af50f1d97281f1399477f4f2c..8555df0f169ab3ae619cec875759d7a67d451f9f 100644 +--- reth/crates/evm/execution-types/Cargo.toml ++++ scroll-reth/crates/evm/execution-types/Cargo.toml +@@ -69,3 +69,7 @@ "serde_with?/std", + "reth-trie-common?/std", + "reth-ethereum-primitives/std" + ] ++scroll = [ ++ "revm/scroll", ++ "reth-primitives/scroll" ++]
-
diff --git reth/crates/consensus/debug-client/src/lib.rs scroll-reth/crates/consensus/debug-client/src/lib.rs -index e4fa5f44a878a8fd24e5cbe13c2556a4ba29d034..2100af5d1ca8229d4875f8bba48160f084a4c57b 100644 ---- reth/crates/consensus/debug-client/src/lib.rs -+++ scroll-reth/crates/consensus/debug-client/src/lib.rs -@@ -15,5 +15,5 @@ - mod client; - mod providers; +
diff --git reth/crates/evm/execution-types/src/execution_outcome.rs scroll-reth/crates/evm/execution-types/src/execution_outcome.rs +index 1a0f8a5c39024ce7e002a650103f3ca5f73ed6a3..4683939ea6cc15adbd9d60e6ca89e68820b1d351 100644 +--- reth/crates/evm/execution-types/src/execution_outcome.rs ++++ scroll-reth/crates/evm/execution-types/src/execution_outcome.rs +@@ -770,10 +770,22 @@ let address2 = Address::random(); + let address3 = Address::random();   --pub use client::{block_to_execution_payload_v3, BlockProvider, DebugConsensusClient}; -+pub use client::{block_to_execution_payload, BlockProvider, DebugConsensusClient}; - pub use providers::{EtherscanBlockProvider, RpcBlockProvider};
+ // Set up account info with some changes +- let account_info1 = +- AccountInfo { nonce: 1, balance: U256::from(100), code_hash: B256::ZERO, code: None }; +- let account_info2 = +- AccountInfo { nonce: 2, balance: U256::from(200), code_hash: B256::ZERO, code: None }; ++ let account_info1 = AccountInfo { ++ nonce: 1, ++ balance: U256::from(100), ++ code_hash: B256::ZERO, ++ code: None, ++ #[cfg(feature = "scroll")] ++ code_size: 0, ++ }; ++ let account_info2 = AccountInfo { ++ nonce: 2, ++ balance: U256::from(200), ++ code_hash: B256::ZERO, ++ code: None, ++ #[cfg(feature = "scroll")] ++ code_size: 0, ++ }; +  + // Set up the bundle state with these accounts + let mut bundle_state = BundleState::default();
@@ -16274,13 +25062,13 @@ @@ -16290,39 +25078,70 @@
-
+9
-
-0
+
+5
+
-11
-
diff --git reth/crates/engine/invalid-block-hooks/Cargo.toml scroll-reth/crates/engine/invalid-block-hooks/Cargo.toml -index a7b0153d0d4b7d2968afc5b34e642bf2afdb65a2..a7e6d31c2614253ef3cb030bb5f31ddb0943b91f 100644 ---- reth/crates/engine/invalid-block-hooks/Cargo.toml -+++ scroll-reth/crates/engine/invalid-block-hooks/Cargo.toml -@@ -38,3 +38,12 @@ jsonrpsee.workspace = true - pretty_assertions = "1.4" - serde.workspace = true - serde_json.workspace = true -+ -+[features] -+scroll = [ -+ "reth-evm/scroll", -+ "reth-primitives/scroll", -+ "reth-revm/scroll", -+ "reth-provider/scroll", -+ "reth-trie/scroll", -+]
+
diff --git reth/crates/evm/src/execute.rs scroll-reth/crates/evm/src/execute.rs +index 8bf40d38caa0edb440057bba60cd4c54606a2a40..e77607d8639b58338319d18fdd3ba501bd808cc0 100644 +--- reth/crates/evm/src/execute.rs ++++ scroll-reth/crates/evm/src/execute.rs +@@ -519,7 +519,7 @@ use core::marker::PhantomData; + use reth_chainspec::{ChainSpec, MAINNET}; + use reth_primitives::EthPrimitives; + use revm::db::{CacheDB, EmptyDBTyped}; +- use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY}; ++ use revm_primitives::{address, bytes, AccountInfo, TxEnv}; + use std::sync::Arc; +  + #[derive(Clone, Default)] +@@ -777,12 +777,8 @@ ) -> State<CacheDB<EmptyDBTyped<BlockExecutionError>>> { + let db = CacheDB::<EmptyDBTyped<BlockExecutionError>>::default(); + let mut state = State::builder().with_database(db).with_bundle_update().build(); +  +- let account_info = AccountInfo { +- balance: U256::from(balance), +- nonce, +- code_hash: KECCAK_EMPTY, +- code: None, +- }; ++ let account_info = ++ AccountInfo { balance: U256::from(balance), nonce, ..Default::default() }; + state.insert_account(addr, account_info); + state + } +@@ -818,8 +814,7 @@ let addr2 = address!("2000000000000000000000000000000000000000"); +  + let mut state = setup_state_with_account(addr1, 100, 1); +  +- let account2 = +- AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; ++ let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; + state.insert_account(addr2, account2); +  + let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default(); +@@ -840,8 +835,7 @@ let addr2 = address!("2000000000000000000000000000000000000000"); +  + let mut state = setup_state_with_account(addr1, 100, 1); +  +- let account2 = +- AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; ++ let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; + state.insert_account(addr2, account2); +  + let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
@@ -16331,13 +25150,13 @@ @@ -16347,73 +25166,42 @@
-
+6
-
-7
+
+2
+
-0
-
diff --git reth/crates/engine/invalid-block-hooks/src/witness.rs scroll-reth/crates/engine/invalid-block-hooks/src/witness.rs -index a0c986e4384ab49ef460a621b38426d23c878c96..370a936e70e382a3a3c5d7947cdd8fd6a053c164 100644 ---- reth/crates/engine/invalid-block-hooks/src/witness.rs -+++ scroll-reth/crates/engine/invalid-block-hooks/src/witness.rs -@@ -1,5 +1,5 @@ - use alloy_consensus::BlockHeader; --use alloy_primitives::{keccak256, B256}; -+use alloy_primitives::B256; - use alloy_rpc_types_debug::ExecutionWitness; - use eyre::OptionExt; - use pretty_assertions::Comparison; -@@ -70,10 +70,9 @@ { - // TODO(alexey): unify with `DebugApi::debug_execution_witness` -  - // Setup database. -+ let provider = self.provider.state_by_block_hash(parent_header.hash())?; - let mut db = StateBuilder::new() -- .with_database(StateProviderDatabase::new( -- self.provider.state_by_block_hash(parent_header.hash())?, -- )) -+ .with_database(StateProviderDatabase::new(&provider)) - .with_bundle_update() - .build(); -  -@@ -136,7 +135,7 @@ // Note: We grab *all* accounts in the cache here, as the `BundleState` prunes - // referenced accounts + storage slots. - let mut hashed_state = db.database.hashed_post_state(&bundle_state); - for (address, account) in db.cache.accounts { -- let hashed_address = keccak256(address); -+ let hashed_address = provider.hash_key(address.as_ref()); - hashed_state - .accounts - .insert(hashed_address, account.account.as_ref().map(|a| a.info.clone().into())); -@@ -151,7 +150,7 @@ state_preimages.insert(hashed_address, alloy_rlp::encode(address).into()); -  - for (slot, value) in account.storage { - let slot = B256::from(slot); -- let hashed_slot = keccak256(slot); -+ let hashed_slot = provider.hash_key(slot.as_ref()); - storage.storage.insert(hashed_slot, value); +
diff --git reth/crates/evm/src/metrics.rs scroll-reth/crates/evm/src/metrics.rs +index 242ddfe5b79af714a532333d5bc5c2b1e8c6710e..33428ff7db2906d1e36115d60fbe1c41c9237de4 100644 +--- reth/crates/evm/src/metrics.rs ++++ scroll-reth/crates/evm/src/metrics.rs +@@ -142,6 +142,7 @@ }   - state_preimages.insert(hashed_slot, alloy_rlp::encode(slot).into()); -@@ -241,7 +240,7 @@ - // Calculate the state root and trie updates after re-execution. They should match - // the original ones. - let (re_executed_root, trie_output) = -- state_provider.state_root_with_updates(hashed_state)?; -+ state_provider.state_root_from_state_with_updates(hashed_state)?; - if let Some((original_updates, original_root)) = trie_updates { - if re_executed_root != original_root { - let filename = format!("{}_{}.state_root.diff", block.number(), block.hash());
+ #[cfg(test)] + mod tests { ++ #![allow(clippy::needless_update)] + use super::*; + use alloy_eips::eip7685::Requests; + use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter}; +@@ -247,6 +248,7 @@ balance: U256::from(100), + nonce: 10, + code_hash: B256::random(), + code: Default::default(), ++ ..Default::default() + }, + storage, + status: AccountStatus::Loaded,
@@ -16422,13 +25210,13 @@ @@ -16438,37 +25226,94 @@
-
+7
+
+11
-0
-
diff --git reth/crates/engine/primitives/Cargo.toml scroll-reth/crates/engine/primitives/Cargo.toml -index 2da1be9c928e9558ec046103d340722b89f267cb..97d2959b32949b97e5abe5bde438922eeca95199 100644 ---- reth/crates/engine/primitives/Cargo.toml -+++ scroll-reth/crates/engine/primitives/Cargo.toml -@@ -32,3 +32,10 @@ - # misc - serde.workspace = true - thiserror.workspace = true -+ -+[features] -+scroll = [ -+ "reth-execution-types/scroll", -+ "reth-primitives/scroll", -+ "reth-trie/scroll" -+]
+
diff --git reth/crates/exex/exex/Cargo.toml scroll-reth/crates/exex/exex/Cargo.toml +index 60ba7e25a6e75a35d6307f9d2ac9f12d5d9d62c3..25ae59cd0df56cfeb3dfa86d870b5d89d8bfcd88 100644 +--- reth/crates/exex/exex/Cargo.toml ++++ scroll-reth/crates/exex/exex/Cargo.toml +@@ -81,3 +81,14 @@ "rand/serde", + "secp256k1/serde", + "reth-primitives-traits/serde", + ] ++scroll = [ ++ "reth-node-core/scroll", ++ "reth-evm/scroll", ++ "reth-primitives/scroll", ++ "reth-provider/scroll", ++ "reth-evm-ethereum/scroll", ++ "reth-testing-utils/scroll", ++ "reth-revm/scroll", ++ "reth-primitives-traits/scroll", ++ "reth-node-api/scroll", ++]
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+1
+
-1
+ +
+ +
+
+
diff --git reth/crates/exex/exex/src/context.rs scroll-reth/crates/exex/exex/src/context.rs +index f536ed515f9eae48d701088220b3e3a8a40bb7ad..deb6fa2bd8d2449a4ccabdde596307a6d404f802 100644 +--- reth/crates/exex/exex/src/context.rs ++++ scroll-reth/crates/exex/exex/src/context.rs +@@ -21,7 +21,7 @@ /// + /// # Important + /// + /// The exex should emit a `FinishedHeight` whenever a processed block is safe to prune. +- /// Additionally, the exex can pre-emptively emit a `FinishedHeight` event to specify what ++ /// Additionally, the exex can preemptively emit a `FinishedHeight` event to specify what + /// blocks to receive notifications for. + pub events: UnboundedSender<ExExEvent>, + /// Channel to receive [`ExExNotification`](crate::ExExNotification)s.
@@ -16477,13 +25322,13 @@ @@ -16494,30 +25339,34 @@
+1
-
-0
+
-1
-
diff --git reth/crates/engine/tree/Cargo.toml scroll-reth/crates/engine/tree/Cargo.toml -index 76ce5a7ac5bbee7258797c98b37b5040a3e4ac15..fd994c1595bcb723ba11db43d9766d7d8bbdf65e 100644 ---- reth/crates/engine/tree/Cargo.toml -+++ scroll-reth/crates/engine/tree/Cargo.toml -@@ -129,3 +129,4 @@ "reth-trie-db/test-utils", - "reth-trie-parallel/test-utils", - "reth-network/test-utils" - ] -+skip-state-root-validation = ["reth-stages/skip-state-root-validation"]
+
diff --git reth/crates/exex/exex/src/dyn_context.rs scroll-reth/crates/exex/exex/src/dyn_context.rs +index 8bda75cac45db14a6a279b014b684bb48e0e146a..94f6ae81f8f7fd70006b78db376784f524a4a881 100644 +--- reth/crates/exex/exex/src/dyn_context.rs ++++ scroll-reth/crates/exex/exex/src/dyn_context.rs +@@ -26,7 +26,7 @@ /// + /// # Important + /// + /// The exex should emit a `FinishedHeight` whenever a processed block is safe to prune. +- /// Additionally, the exex can pre-emptively emit a `FinishedHeight` event to specify what ++ /// Additionally, the exex can preemptively emit a `FinishedHeight` event to specify what + /// blocks to receive notifications for. + pub events: mpsc::UnboundedSender<ExExEvent>, + /// Channel to receive [`ExExNotification`](crate::ExExNotification)s.
-
diff --git reth/crates/engine/tree/benches/channel_perf.rs scroll-reth/crates/engine/tree/benches/channel_perf.rs -index 5eb919da8b349a30797fa53280a245b920c812b9..5ac33f4286be0144e5df010dcb6a720109d0475c 100644 ---- reth/crates/engine/tree/benches/channel_perf.rs -+++ scroll-reth/crates/engine/tree/benches/channel_perf.rs -@@ -26,7 +26,7 @@ info: AccountInfo { - balance: U256::from(100), - nonce: 10, - code_hash: B256::from_slice(&rng.gen::<[u8; 32]>()), -- code: Default::default(), -+ ..Default::default() - }, - storage, - status: AccountStatus::Loaded,
+
diff --git reth/crates/net/eth-wire-types/src/capability.rs scroll-reth/crates/net/eth-wire-types/src/capability.rs +index 2002a03aea628986c7bf0fb81f859326e375644f..e3bc86976e9a50d871d7d2a82c979f96c30e76af 100644 +--- reth/crates/net/eth-wire-types/src/capability.rs ++++ scroll-reth/crates/net/eth-wire-types/src/capability.rs +@@ -109,6 +109,7 @@ + impl Capabilities { + /// Returns all capabilities. + #[inline] ++ #[allow(clippy::missing_const_for_fn)] + pub fn capabilities(&self) -> &[Capability] { + &self.inner + }
-
diff --git reth/crates/engine/tree/benches/state_root_task.rs scroll-reth/crates/engine/tree/benches/state_root_task.rs -index 8c5b871385abb2e1fc8fd8f60cf3b22c8387a3be..85afe8c82b9cceb7ceb7069aaabc4dc2d11ef13f 100644 ---- reth/crates/engine/tree/benches/state_root_task.rs -+++ scroll-reth/crates/engine/tree/benches/state_root_task.rs -@@ -60,11 +60,13 @@ status: AccountStatus::SelfDestructed, - } - } else { - RevmAccount { -+ #[allow(clippy::needless_update)] - info: AccountInfo { - balance: U256::from(rng.gen::<u64>()), - nonce: rng.gen::<u64>(), - code_hash: KECCAK_EMPTY, - code: Some(Default::default()), -+ ..Default::default() - }, - storage: (0..rng.gen_range(0..=params.storage_slots_per_account)) - .map(|_| {
+
diff --git reth/crates/net/network/src/network.rs scroll-reth/crates/net/network/src/network.rs +index a25ad0490818212333031991d890deb97cc7a59e..10866d1ca231ee3c55651fdd781ab44f77ab5098 100644 +--- reth/crates/net/network/src/network.rs ++++ scroll-reth/crates/net/network/src/network.rs +@@ -88,15 +88,18 @@ Self { inner: Arc::new(inner) } + } +  + /// Returns the [`PeerId`] used in the network. ++ #[allow(clippy::missing_const_for_fn)] + pub fn peer_id(&self) -> &PeerId { + &self.inner.local_peer_id + } +  ++ #[allow(clippy::missing_const_for_fn)] + fn manager(&self) -> &UnboundedSender<NetworkHandleMessage<N>> { + &self.inner.to_manager_tx + } +  + /// Returns the mode of the network, either pow, or pos ++ #[allow(clippy::missing_const_for_fn)] + pub fn mode(&self) -> &NetworkMode { + &self.inner.network_mode + } +@@ -182,11 +185,13 @@ self.send_message(NetworkHandleMessage::SetNetworkState(network_conn)); + } +  + /// Whether tx gossip is disabled ++ #[allow(clippy::missing_const_for_fn)] + pub fn tx_gossip_disabled(&self) -> bool { + self.inner.tx_gossip_disabled + } +  + /// Returns the secret key used for authenticating sessions. ++ #[allow(clippy::missing_const_for_fn)] + pub fn secret_key(&self) -> &SecretKey { + &self.inner.secret_key + }
-
diff --git reth/crates/engine/tree/src/tree/cached_state.rs scroll-reth/crates/engine/tree/src/tree/cached_state.rs -index 84d2c8a0922523f17b5f966dc19531a3b04ea5ab..0057c4c7b438139ea994410867cd26db3c6c6f6e 100644 ---- reth/crates/engine/tree/src/tree/cached_state.rs -+++ scroll-reth/crates/engine/tree/src/tree/cached_state.rs -@@ -6,9 +6,10 @@ use reth_errors::ProviderResult; - use reth_metrics::Metrics; - use reth_primitives::{Account, Bytecode}; - use reth_provider::{ -- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, -- StateRootProvider, StorageRootProvider, -+ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, -+ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, - }; -+use reth_revm::db::BundleAccount; - use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, - MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, -@@ -139,26 +140,26 @@ } - } -  - impl<S: StateRootProvider> StateRootProvider for CachedStateProvider<S> { -- fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { -- self.state_provider.state_root(hashed_state) -+ fn state_root_from_state(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { -+ self.state_provider.state_root_from_state(hashed_state) - } -  - fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> { - self.state_provider.state_root_from_nodes(input) +
diff --git reth/crates/net/network/src/test_utils/testnet.rs scroll-reth/crates/net/network/src/test_utils/testnet.rs +index 3477387c56ab0e1ba44bc91b8b5966f0118cda2e..1e4bb174508a49dbd384fb9923468a57a9c21335 100644 +--- reth/crates/net/network/src/test_utils/testnet.rs ++++ scroll-reth/crates/net/network/src/test_utils/testnet.rs +@@ -100,6 +100,7 @@ &mut self.peers }   -- fn state_root_from_nodes_with_updates( -+ fn state_root_from_state_with_updates( - &self, -- input: TrieInput, -+ hashed_state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { -- self.state_provider.state_root_from_nodes_with_updates(input) -+ self.state_provider.state_root_from_state_with_updates(hashed_state) + /// Return a slice of all peers. ++ #[allow(clippy::missing_const_for_fn)] + pub fn peers(&self) -> &[Peer<C, Pool>] { + &self.peers } -  -- fn state_root_with_updates( -+ fn state_root_from_nodes_with_updates( - &self, -- hashed_state: HashedPostState, -+ input: TrieInput, - ) -> ProviderResult<(B256, TrieUpdates)> { -- self.state_provider.state_root_with_updates(hashed_state) -+ self.state_provider.state_root_from_nodes_with_updates(input) +@@ -331,6 +332,7 @@ rx.await.unwrap() } - }   -@@ -186,6 +187,18 @@ input: TrieInput, - target: HashedPostState, - ) -> ProviderResult<B256HashMap<alloy_primitives::Bytes>> { - self.state_provider.witness(input, target) -+ } -+} -+ -+impl<S: StateProvider> HashedStorageProvider for CachedStateProvider<S> { -+ fn hashed_storage(&self, account: &BundleAccount) -> HashedStorage { -+ self.state_provider.hashed_storage(account) -+ } -+} -+ -+impl<S: StateProvider> KeyHasherProvider for CachedStateProvider<S> { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ self.state_provider.hash_key(bytes) - } - } -
+ /// Returns the [`PeerHandle`]s of this [`Testnet`]. ++ #[allow(clippy::missing_const_for_fn)] + pub fn peers(&self) -> &[PeerHandle<Pool>] { + &self.peers + }
@@ -16748,13 +25564,13 @@ @@ -16764,101 +25580,53 @@
-
+16
-
-10
+
+13
+
-0
-
diff --git reth/crates/engine/tree/src/tree/mod.rs scroll-reth/crates/engine/tree/src/tree/mod.rs -index ea92c0fb389807252e612f0e1ce0a2e38afd41a0..c02cbf155c92d70f07a65564ca21b611e14c6fef 100644 ---- reth/crates/engine/tree/src/tree/mod.rs -+++ scroll-reth/crates/engine/tree/src/tree/mod.rs -@@ -36,8 +36,7 @@ use reth_payload_builder::PayloadBuilderHandle; - use reth_payload_builder_primitives::PayloadBuilder; - use reth_payload_primitives::PayloadBuilderAttributes; - use reth_primitives::{ -- EthPrimitives, GotExpected, NodePrimitives, SealedBlockFor, SealedBlockWithSenders, -- SealedHeader, -+ EthPrimitives, NodePrimitives, SealedBlockFor, SealedBlockWithSenders, SealedHeader, - }; - use reth_primitives_traits::Block; - use reth_provider::{ -@@ -2379,8 +2378,8 @@ if task_state_root != block.header().state_root() { - debug!(target: "engine::tree", "Task state root does not match block state root"); - } -  -- let (regular_root, regular_updates) = -- state_provider.state_root_with_updates(hashed_state.clone())?; -+ let (regular_root, regular_updates) = state_provider -+ .state_root_from_state_with_updates(hashed_state.clone())?; -  - if regular_root == block.header().state_root() { - let provider = self.provider.database_provider_ro()?; -@@ -2400,8 +2399,8 @@ } - Err(error) => { - info!(target: "engine::tree", ?error, "Failed to wait for state root task result"); - // Fall back to sequential calculation -- let (root, updates) = -- state_provider.state_root_with_updates(hashed_state.clone())?; -+ let (root, updates) = state_provider -+ .state_root_from_state_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - } - } -@@ -2422,8 +2421,8 @@ Err(ParallelStateRootError::Provider(ProviderError::ConsistentView( - error, - ))) => { - debug!(target: "engine", %error, "Parallel state root computation failed consistency check, falling back"); -- let (root, updates) = -- state_provider.state_root_with_updates(hashed_state.clone())?; -+ let (root, updates) = state_provider -+ .state_root_from_state_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - } - Err(error) => return Err(InsertBlockErrorKind::Other(Box::new(error))), -@@ -2432,7 +2431,7 @@ } - } else { - debug!(target: "engine::tree", block=?sealed_block.num_hash(), ?persistence_not_in_progress, "Failed to compute state root in parallel"); - let (root, updates) = -- state_provider.state_root_with_updates(hashed_state.clone())?; -+ state_provider.state_root_from_state_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - }; -  -@@ -2447,6 +2446,9 @@ })?; -  - let (state_root, trie_output, hashed_state, output, root_elapsed) = state_root_result; +
diff --git reth/crates/net/peers/src/bootnodes/mod.rs scroll-reth/crates/net/peers/src/bootnodes/mod.rs +index b149c108a96922e90e8023d07a24e0f2b09f4464..811e8f1778dd58847c8bd622084440e19a030baf 100644 +--- reth/crates/net/peers/src/bootnodes/mod.rs ++++ scroll-reth/crates/net/peers/src/bootnodes/mod.rs +@@ -9,6 +9,9 @@ + mod optimism; + pub use optimism::*;   -+ #[cfg(feature = "skip-state-root-validation")] -+ let _ = state_root; -+ #[cfg(not(feature = "skip-state-root-validation"))] - if state_root != block.header().state_root() { - // call post-block hook - self.invalid_block_hook.on_invalid_block( -@@ -2456,7 +2458,11 @@ &output, - Some((&trie_output, state_root)), - ); - return Err(ConsensusError::BodyStateRootDiff( -- GotExpected { got: state_root, expected: block.header().state_root() }.into(), -+ reth_primitives::GotExpected { -+ got: state_root, -+ expected: block.header().state_root(), -+ } -+ .into(), - ) - .into()) - }
++mod scroll; ++pub use scroll::*; ++ + /// Returns parsed mainnet nodes + pub fn mainnet_nodes() -> Vec<NodeRecord> { + parse_nodes(&MAINNET_BOOTNODES[..]) +@@ -42,6 +45,16 @@ + /// Returns parsed op-stack base testnet nodes + pub fn base_testnet_nodes() -> Vec<NodeRecord> { + parse_nodes(OP_TESTNET_BOOTNODES) ++} ++ ++/// Returns parsed scroll mainnet nodes ++pub fn scroll_nodes() -> Vec<NodeRecord> { ++ parse_nodes(SCROLL_BOOTNODES) ++} ++ ++/// Returns parsed scroll seplo nodes ++pub fn scroll_sepolia_nodes() -> Vec<NodeRecord> { ++ parse_nodes(SCROLL_SEPOLIA_BOOTNODES) + } +  + /// Parses all the nodes
@@ -16867,13 +25635,13 @@ @@ -16883,42 +25651,101 @@
-
+2
+
+17
-0
-
diff --git reth/crates/engine/tree/src/tree/root.rs scroll-reth/crates/engine/tree/src/tree/root.rs -index 93cac7b435ed87d96730c77bf922834fc5a6b008..a28841917991e9623d1fb3e0776794e53a993b91 100644 ---- reth/crates/engine/tree/src/tree/root.rs -+++ scroll-reth/crates/engine/tree/src/tree/root.rs -@@ -837,6 +837,7 @@ } +
diff --git reth/crates/net/peers/src/bootnodes/scroll.rs scroll-reth/crates/net/peers/src/bootnodes/scroll.rs +new file mode 100644 +index 0000000000000000000000000000000000000000..7d85fcc8d077b6ea67ea3a1c5426620fb2185500 +--- /dev/null ++++ scroll-reth/crates/net/peers/src/bootnodes/scroll.rs +@@ -0,0 +1,17 @@ ++//! Scroll bootnodes come from <https://github.com/scroll-tech/go-ethereum/blob/develop/params/bootnodes.go> ++ ++/// Scroll mainnet boot nodes. ++pub static SCROLL_BOOTNODES: &[&str] = &[ ++ "enode://c6ac91f43df3d63916ac1ae411cdd5ba249d55d48a7bec7f8cd5bb351a31aba437e5a69e8a1de74d73fdfeba8af1cfe9caf9846ecd3abf60d1ffdf4925b55b23@54.186.123.248:30303", ++ "enode://fdcc807b5d1353f3a1e98b90208ce6ef1b7d446136e51eaa8ad657b55518a2f8b37655e42375d61622e6ea18f3faf9d070c9bbdf012cf5484bcbad33b7a15fb1@44.227.91.206:30303", ++ "enode://6beb5a3efbb39be73d17630b6da48e94c0ce7ec665172111463cb470197b20c12faa1fa6f835b81c28571277d1017e65c4e426cc92a46141cf69118ecf28ac03@44.237.194.52:30303", ++ "enode://7cf893d444eb8e129dca0f6485b3df579911606e7c728be4fa55fcc5f155a37c3ce07d217ccec5447798bde465ac2bdba2cb8763d107e9f3257e787579e9f27e@52.35.203.107:30303", ++ "enode://c7b2d94e95da343db6e667a01cef90376a592f2d277fbcbf6e9c9186734ed8003d01389571bd10cdbab7a6e5adfa6f0c7b55644d0db24e0b9deb4ec80f842075@54.70.236.187:30303", ++]; ++ ++/// Scroll sepolia boot nodes. ++pub static SCROLL_SEPOLIA_BOOTNODES: &[&str] = &[ ++ "enode://ceb1636bac5cbb262e5ad5b2cd22014bdb35ffe7f58b3506970d337a63099481814a338dbcd15f2d28757151e3ecd40ba38b41350b793cd0d910ff0436654f8c@35.85.84.250:30303", ++ "enode://29cee709c400533ae038a875b9ca975c8abef9eade956dcf3585e940acd5c0ae916968f514bd37d1278775aad1b7db30f7032a70202a87fd7365bd8de3c9f5fc@44.242.39.33:30303", ++ "enode://dd1ac5433c5c2b04ca3166f4cb726f8ff6d2da83dbc16d9b68b1ea83b7079b371eb16ef41c00441b6e85e32e33087f3b7753ea9e8b1e3f26d3e4df9208625e7f@54.148.111.168:30303", ++];
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+8
+
-0
+ +
+ +
+
+
diff --git reth/crates/node/api/Cargo.toml scroll-reth/crates/node/api/Cargo.toml +index fa323108dc666b033970b698c8a7e6c2e0b00dbb..c941adecc17acbb6df98ba203e6acb447304a012 100644 +--- reth/crates/node/api/Cargo.toml ++++ scroll-reth/crates/node/api/Cargo.toml +@@ -28,3 +28,11 @@ + alloy-rpc-types-engine.workspace = true   - #[cfg(test)] - mod tests { -+ #![allow(clippy::needless_update)] - use super::*; - use reth_primitives::{Account as RethAccount, StorageEntry}; - use reth_provider::{ -@@ -895,6 +896,7 @@ balance: U256::from(rng.gen::<u64>()), - nonce: rng.gen::<u64>(), - code_hash: KECCAK_EMPTY, - code: Some(Default::default()), -+ ..Default::default() - }, - storage, - status: AccountStatus::Touched,
+ eyre.workspace = true ++ ++[features] ++scroll = [ ++ "reth-provider/scroll", ++ "reth-evm/scroll", ++ "reth-node-core/scroll", ++ "reth-engine-primitives/scroll" ++]
@@ -16927,13 +25754,13 @@ @@ -16943,35 +25770,48 @@
-
+1
-
-1
+
+18
+
-0
-
diff --git reth/crates/engine/util/src/reorg.rs scroll-reth/crates/engine/util/src/reorg.rs -index 073f83545ab69f9bedde922c7f4fe902d33eb58f..97c23111a65c6758b226e8b9d947db35cfe54cdf 100644 ---- reth/crates/engine/util/src/reorg.rs -+++ scroll-reth/crates/engine/util/src/reorg.rs -@@ -422,7 +422,7 @@ logs_bloom: outcome.block_logs_bloom(reorg_target.header.number).unwrap(), - gas_used: cumulative_gas_used, - blob_gas_used, - excess_blob_gas, -- state_root: state_provider.state_root(hashed_state)?, -+ state_root: state_provider.state_root_from_state(hashed_state)?, - requests_hash: None, // TODO(prague) - }, - body: BlockBody {
+
diff --git reth/crates/node/builder/Cargo.toml scroll-reth/crates/node/builder/Cargo.toml +index 9f08507f9f21b9b96b0a8c9c3c912ca867af9233..54ab3ba21bd08c48bb3eea7198699f1590fdd706 100644 +--- reth/crates/node/builder/Cargo.toml ++++ scroll-reth/crates/node/builder/Cargo.toml +@@ -111,3 +111,21 @@ "reth-db-api/test-utils", + "reth-provider/test-utils", + "reth-transaction-pool/test-utils", + ] ++scroll = [ ++ "reth-primitives/scroll", ++ "reth-db-api/scroll", ++ "reth-evm/scroll", ++ "reth-provider/scroll", ++ "reth-db?/scroll", ++ "reth-rpc/scroll", ++ "reth-node-core/scroll", ++ "reth-node-api/scroll", ++ "reth-exex/scroll", ++ "reth-rpc-eth-types/scroll", ++ "reth-stages/scroll", ++ "reth-invalid-block-hooks/scroll" ++] ++skip-state-root-validation = [ ++ "reth-stages/skip-state-root-validation", ++ "reth-engine-tree/skip-state-root-validation" ++]
@@ -16980,13 +25820,13 @@ @@ -16996,35 +25836,38 @@
-
+1
-
-1
+
+5
+
-0
-
diff --git reth/crates/ethereum-forks/src/display.rs scroll-reth/crates/ethereum-forks/src/display.rs -index 99e753cbb81265d1ac166d8c0ef763c9d55ac1cd..26ab5ced192ae9bf9d92cc7ffb2006858cb82425 100644 ---- reth/crates/ethereum-forks/src/display.rs -+++ scroll-reth/crates/ethereum-forks/src/display.rs -@@ -118,7 +118,7 @@ - format( - "Pre-merge hard forks (block based)", - &self.pre_merge, -- self.with_merge.is_empty(), -+ self.with_merge.is_empty() && self.post_merge.is_empty(), - f, - )?; -
+
diff --git reth/crates/node/core/Cargo.toml scroll-reth/crates/node/core/Cargo.toml +index 7d4a417bed802fe6deefb9932953d65f5741f40b..c89d1174b44feb7c2c8ba03b3040c9770b728376 100644 +--- reth/crates/node/core/Cargo.toml ++++ scroll-reth/crates/node/core/Cargo.toml +@@ -80,6 +80,11 @@ optimism = ["reth-primitives/optimism", "reth-db/optimism"] + # Features for vergen to generate correct env vars + jemalloc = ["reth-cli-util/jemalloc"] + asm-keccak = ["reth-primitives/asm-keccak", "alloy-primitives/asm-keccak"] ++scroll = [ ++ "reth-primitives/scroll", ++ "reth-db/scroll", ++ "reth-rpc-eth-types/scroll", ++] +  + [build-dependencies] + vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl"] }
@@ -17033,13 +25876,13 @@ @@ -17049,30 +25892,27 @@
-
+8
+
+5
-1
-
diff --git reth/crates/evm/Cargo.toml scroll-reth/crates/evm/Cargo.toml -index 8f58e824127583edbdef4ef7e50977d7c2da7510..d773a234031fa819232e1847faa38df253b59cbd 100644 ---- reth/crates/evm/Cargo.toml -+++ scroll-reth/crates/evm/Cargo.toml -@@ -69,5 +69,12 @@ "reth-primitives/test-utils", - "reth-primitives-traits/test-utils", - "reth-revm/test-utils", - "revm/test-utils", -- "reth-prune-types/test-utils" -+ "reth-prune-types/test-utils", +
diff --git reth/crates/revm/Cargo.toml scroll-reth/crates/revm/Cargo.toml +index cc1c8edcb8db6efe67d63855e5421fb730a33e93..3e9c58015fe15310e73ae765e919b93d373a97db 100644 +--- reth/crates/revm/Cargo.toml ++++ scroll-reth/crates/revm/Cargo.toml +@@ -61,5 +61,9 @@ "alloy-primitives/serde", + "alloy-consensus/serde", + "reth-primitives-traits/serde", + "reth-trie?/serde", +- "reth-ethereum-forks/serde" ++ "reth-ethereum-forks/serde", +] +scroll = [ -+ "revm/scroll", + "reth-primitives-traits/scroll", -+ "reth-primitives/scroll", -+ "reth-revm/scroll", -+ "reth-execution-types/scroll", ++ "reth-trie?/scroll" ]
@@ -17080,9 +25920,9 @@
@@ -17091,13 +25931,13 @@ @@ -17107,34 +25947,34 @@
-
+4
+
+1
-0
-
diff --git reth/crates/evm/execution-types/Cargo.toml scroll-reth/crates/evm/execution-types/Cargo.toml -index 5a415f0b8892647af50f1d97281f1399477f4f2c..8555df0f169ab3ae619cec875759d7a67d451f9f 100644 ---- reth/crates/evm/execution-types/Cargo.toml -+++ scroll-reth/crates/evm/execution-types/Cargo.toml -@@ -69,3 +69,7 @@ "serde_with?/std", - "reth-trie-common?/std", - "reth-ethereum-primitives/std" - ] -+scroll = [ -+ "revm/scroll", -+ "reth-primitives/scroll" -+]
+
diff --git reth/crates/revm/src/batch.rs scroll-reth/crates/revm/src/batch.rs +index c980bdc987c3ee209a3145ddccaba608aaaace2d..5a7289d9959724a2e4cf225d9e2e15d5f7d7ad17 100644 +--- reth/crates/revm/src/batch.rs ++++ scroll-reth/crates/revm/src/batch.rs +@@ -101,6 +101,7 @@ core::mem::take(&mut self.receipts) + } +  + /// Returns the recorded requests. ++ #[allow(clippy::missing_const_for_fn)] + pub fn requests(&self) -> &[Requests] { + &self.requests + }
@@ -17143,13 +25983,13 @@ @@ -17159,53 +25999,79 @@
-
+16
-
-4
+
+17
+
-5
-
diff --git reth/crates/evm/execution-types/src/execution_outcome.rs scroll-reth/crates/evm/execution-types/src/execution_outcome.rs -index 1a0f8a5c39024ce7e002a650103f3ca5f73ed6a3..4683939ea6cc15adbd9d60e6ca89e68820b1d351 100644 ---- reth/crates/evm/execution-types/src/execution_outcome.rs -+++ scroll-reth/crates/evm/execution-types/src/execution_outcome.rs -@@ -770,10 +770,22 @@ let address2 = Address::random(); - let address3 = Address::random(); +
diff --git reth/crates/revm/src/test_utils.rs scroll-reth/crates/revm/src/test_utils.rs +index 6d2dbf2ca01d84e88228dbc8d691c927488863f3..f4e4f6d883acf1c16ffbb4fe8687b071cf453abc 100644 +--- reth/crates/revm/src/test_utils.rs ++++ scroll-reth/crates/revm/src/test_utils.rs +@@ -6,12 +6,12 @@ Address, BlockNumber, Bytes, StorageKey, B256, U256, + }; + use reth_primitives::{Account, Bytecode}; + use reth_storage_api::{ +- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, +- StateRootProvider, StorageRootProvider, ++ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, ++ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, + }; + use reth_storage_errors::provider::ProviderResult; + use reth_trie::{ +- updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, ++ updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, KeyHasher, + MultiProof, MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, + };   - // Set up account info with some changes -- let account_info1 = -- AccountInfo { nonce: 1, balance: U256::from(100), code_hash: B256::ZERO, code: None }; -- let account_info2 = -- AccountInfo { nonce: 2, balance: U256::from(200), code_hash: B256::ZERO, code: None }; -+ let account_info1 = AccountInfo { -+ nonce: 1, -+ balance: U256::from(100), -+ code_hash: B256::ZERO, -+ code: None, -+ #[cfg(feature = "scroll")] -+ code_size: 0, -+ }; -+ let account_info2 = AccountInfo { -+ nonce: 2, -+ balance: U256::from(200), -+ code_hash: B256::ZERO, -+ code: None, -+ #[cfg(feature = "scroll")] -+ code_size: 0, -+ }; +@@ -72,7 +72,7 @@ } + }   - // Set up the bundle state with these accounts - let mut bundle_state = BundleState::default();
+ impl StateRootProvider for StateProviderTest { +- fn state_root(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { ++ fn state_root_from_state(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { + unimplemented!("state root computation is not supported") + } +  +@@ -80,7 +80,7 @@ fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> { + unimplemented!("state root computation is not supported") + } +  +- fn state_root_with_updates( ++ fn state_root_from_state_with_updates( + &self, + _hashed_state: HashedPostState, + ) -> ProviderResult<(B256, TrieUpdates)> { +@@ -153,6 +153,18 @@ + impl HashedPostStateProvider for StateProviderTest { + fn hashed_post_state(&self, bundle_state: &revm::db::BundleState) -> HashedPostState { + HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state()) ++ } ++} ++ ++impl HashedStorageProvider for StateProviderTest { ++ fn hashed_storage(&self, account: &revm::db::BundleAccount) -> HashedStorage { ++ HashedStorage::from_bundle_account::<KeccakKeyHasher>(account) ++ } ++} ++ ++impl KeyHasherProvider for StateProviderTest { ++ fn hash_key(&self, bytes: &[u8]) -> B256 { ++ KeccakKeyHasher::hash_key(bytes) + } + } +
@@ -17214,13 +26080,13 @@ @@ -17230,70 +26096,37 @@
-
+5
-
-11
+
+7
+
-0
-
diff --git reth/crates/evm/src/execute.rs scroll-reth/crates/evm/src/execute.rs -index 8bf40d38caa0edb440057bba60cd4c54606a2a40..e77607d8639b58338319d18fdd3ba501bd808cc0 100644 ---- reth/crates/evm/src/execute.rs -+++ scroll-reth/crates/evm/src/execute.rs -@@ -519,7 +519,7 @@ use core::marker::PhantomData; - use reth_chainspec::{ChainSpec, MAINNET}; - use reth_primitives::EthPrimitives; - use revm::db::{CacheDB, EmptyDBTyped}; -- use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY}; -+ use revm_primitives::{address, bytes, AccountInfo, TxEnv}; - use std::sync::Arc; -  - #[derive(Clone, Default)] -@@ -777,12 +777,8 @@ ) -> State<CacheDB<EmptyDBTyped<BlockExecutionError>>> { - let db = CacheDB::<EmptyDBTyped<BlockExecutionError>>::default(); - let mut state = State::builder().with_database(db).with_bundle_update().build(); -  -- let account_info = AccountInfo { -- balance: U256::from(balance), -- nonce, -- code_hash: KECCAK_EMPTY, -- code: None, -- }; -+ let account_info = -+ AccountInfo { balance: U256::from(balance), nonce, ..Default::default() }; - state.insert_account(addr, account_info); - state - } -@@ -818,8 +814,7 @@ let addr2 = address!("2000000000000000000000000000000000000000"); -  - let mut state = setup_state_with_account(addr1, 100, 1); -  -- let account2 = -- AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; -+ let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; - state.insert_account(addr2, account2); -  - let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default(); -@@ -840,8 +835,7 @@ let addr2 = address!("2000000000000000000000000000000000000000"); -  - let mut state = setup_state_with_account(addr1, 100, 1); -  -- let account2 = -- AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; -+ let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; - state.insert_account(addr2, account2); -  - let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
+
diff --git reth/crates/rpc/rpc-eth-api/Cargo.toml scroll-reth/crates/rpc/rpc-eth-api/Cargo.toml +index 6f65b91d8f80d7faeae39c04b4905c9a6af91453..a4600e4cd5b878c12a19fe09c8916c78e0f44bab 100644 +--- reth/crates/rpc/rpc-eth-api/Cargo.toml ++++ scroll-reth/crates/rpc/rpc-eth-api/Cargo.toml +@@ -62,3 +62,10 @@ + [features] + js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] + client = ["jsonrpsee/client", "jsonrpsee/async-client"] ++scroll = [ ++ "reth-provider/scroll", ++ "reth-revm/scroll", ++ "reth-primitives/scroll", ++ "reth-evm/scroll", ++ "reth-rpc-eth-types/scroll", ++]
@@ -17302,13 +26135,13 @@ @@ -17319,41 +26152,35 @@
+2
-
-0
+
-1
-
diff --git reth/crates/evm/src/metrics.rs scroll-reth/crates/evm/src/metrics.rs -index 242ddfe5b79af714a532333d5bc5c2b1e8c6710e..33428ff7db2906d1e36115d60fbe1c41c9237de4 100644 ---- reth/crates/evm/src/metrics.rs -+++ scroll-reth/crates/evm/src/metrics.rs -@@ -142,6 +142,7 @@ } +
diff --git reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs scroll-reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +index 9b52b94a4db865ed348f04d620c6f276b0506aac..3ac5446df5b5e1fa61b312b9e1bbb8d3e58ca834 100644 +--- reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs ++++ scroll-reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +@@ -416,7 +416,8 @@ let bundle_state = db.take_bundle(); + let hashed_state = db.database.hashed_post_state(&bundle_state);   - #[cfg(test)] - mod tests { -+ #![allow(clippy::needless_update)] - use super::*; - use alloy_eips::eip7685::Requests; - use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter}; -@@ -247,6 +248,7 @@ balance: U256::from(100), - nonce: 10, - code_hash: B256::random(), - code: Default::default(), -+ ..Default::default() - }, - storage, - status: AccountStatus::Loaded,
+ // calculate the state root +- let state_root = db.database.state_root(hashed_state).map_err(Self::Error::from_eth_err)?; ++ let state_root = ++ db.database.state_root_from_state(hashed_state).map_err(Self::Error::from_eth_err)?; +  + let (block, receipts) = self.assemble_block_and_receipts( + &block_env,
@@ -17362,13 +26189,13 @@ @@ -17378,31 +26205,40 @@
-
+11
-
-0
+
+10
+
-1
-
diff --git reth/crates/exex/exex/Cargo.toml scroll-reth/crates/exex/exex/Cargo.toml -index 60ba7e25a6e75a35d6307f9d2ac9f12d5d9d62c3..25ae59cd0df56cfeb3dfa86d870b5d89d8bfcd88 100644 ---- reth/crates/exex/exex/Cargo.toml -+++ scroll-reth/crates/exex/exex/Cargo.toml -@@ -81,3 +81,14 @@ "rand/serde", - "secp256k1/serde", - "reth-primitives-traits/serde", - ] +
diff --git reth/crates/rpc/rpc-eth-types/Cargo.toml scroll-reth/crates/rpc/rpc-eth-types/Cargo.toml +index 72b153ab08457e8f8079cd61c830c84483723d7d..0d20719dd85dc9f3acb921397821397f3e362cc6 100644 +--- reth/crates/rpc/rpc-eth-types/Cargo.toml ++++ scroll-reth/crates/rpc/rpc-eth-types/Cargo.toml +@@ -33,9 +33,11 @@ alloy-primitives.workspace = true + alloy-consensus.workspace = true + alloy-sol-types.workspace = true + alloy-rpc-types-eth.workspace = true +-revm.workspace = true + revm-inspectors.workspace = true + revm-primitives = { workspace = true, features = ["dev"] } ++ ++# scroll ++revm.workspace = true +  + # rpc + jsonrpsee-core.workspace = true +@@ -64,3 +66,10 @@ +  + [features] + js-tracer = ["revm-inspectors/js-tracer"] +scroll = [ -+ "reth-node-core/scroll", -+ "reth-evm/scroll", -+ "reth-primitives/scroll", -+ "reth-provider/scroll", -+ "reth-evm-ethereum/scroll", -+ "reth-testing-utils/scroll", -+ "reth-revm/scroll", -+ "reth-primitives-traits/scroll", -+ "reth-node-api/scroll", ++ "revm/scroll", ++ "reth-execution-types/scroll", ++ "reth-revm/scroll", ++ "reth-primitives/scroll", ++ "reth-trie/scroll" +]
@@ -17410,9 +26246,9 @@
@@ -17421,13 +26257,13 @@ @@ -17437,53 +26273,84 @@
-
+13
-
-0
+
+19
+
-5
-
diff --git reth/crates/net/peers/src/bootnodes/mod.rs scroll-reth/crates/net/peers/src/bootnodes/mod.rs -index b149c108a96922e90e8023d07a24e0f2b09f4464..811e8f1778dd58847c8bd622084440e19a030baf 100644 ---- reth/crates/net/peers/src/bootnodes/mod.rs -+++ scroll-reth/crates/net/peers/src/bootnodes/mod.rs -@@ -9,6 +9,9 @@ - mod optimism; - pub use optimism::*; +
diff --git reth/crates/rpc/rpc-eth-types/src/cache/db.rs scroll-reth/crates/rpc/rpc-eth-types/src/cache/db.rs +index dd15f7602179a98d6695f8396c2280f4c35dfdb7..8d205f56a05e04931bc3707afe897912a50c740e 100644 +--- reth/crates/rpc/rpc-eth-types/src/cache/db.rs ++++ scroll-reth/crates/rpc/rpc-eth-types/src/cache/db.rs +@@ -5,7 +5,9 @@ + use alloy_primitives::{Address, B256, U256}; + use reth_errors::ProviderResult; + use reth_revm::{database::StateProviderDatabase, db::CacheDB, DatabaseRef}; +-use reth_storage_api::{HashedPostStateProvider, StateProvider}; ++use reth_storage_api::{ ++ HashedPostStateProvider, HashedStorageProvider, KeyHasherProvider, StateProvider, ++}; + use reth_trie::{HashedStorage, MultiProofTargets}; + use revm::Database; +  +@@ -18,11 +20,11 @@ #[allow(missing_debug_implementations)] + pub struct StateProviderTraitObjWrapper<'a>(pub &'a dyn StateProvider); +  + impl reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'_> { +- fn state_root( ++ fn state_root_from_state( + &self, + hashed_state: reth_trie::HashedPostState, + ) -> reth_errors::ProviderResult<B256> { +- self.0.state_root(hashed_state) ++ self.0.state_root_from_state(hashed_state) + }   -+mod scroll; -+pub use scroll::*; -+ - /// Returns parsed mainnet nodes - pub fn mainnet_nodes() -> Vec<NodeRecord> { - parse_nodes(&MAINNET_BOOTNODES[..]) -@@ -42,6 +45,16 @@ - /// Returns parsed op-stack base testnet nodes - pub fn base_testnet_nodes() -> Vec<NodeRecord> { - parse_nodes(OP_TESTNET_BOOTNODES) + fn state_root_from_nodes( +@@ -32,11 +34,11 @@ ) -> reth_errors::ProviderResult<B256> { + self.0.state_root_from_nodes(input) + } +  +- fn state_root_with_updates( ++ fn state_root_from_state_with_updates( + &self, + hashed_state: reth_trie::HashedPostState, + ) -> reth_errors::ProviderResult<(B256, reth_trie::updates::TrieUpdates)> { +- self.0.state_root_with_updates(hashed_state) ++ self.0.state_root_from_state_with_updates(hashed_state) + } +  + fn state_root_from_nodes_with_updates( +@@ -142,6 +144,18 @@ &self, + bundle_state: &revm::db::BundleState, + ) -> reth_trie::HashedPostState { + self.0.hashed_post_state(bundle_state) ++ } +} + -+/// Returns parsed scroll mainnet nodes -+pub fn scroll_nodes() -> Vec<NodeRecord> { -+ parse_nodes(SCROLL_BOOTNODES) ++impl HashedStorageProvider for StateProviderTraitObjWrapper<'_> { ++ fn hashed_storage(&self, account: &revm::db::BundleAccount) -> HashedStorage { ++ self.0.hashed_storage(account) ++ } +} + -+/// Returns parsed scroll seplo nodes -+pub fn scroll_sepolia_nodes() -> Vec<NodeRecord> { -+ parse_nodes(SCROLL_SEPOLIA_BOOTNODES) ++impl KeyHasherProvider for StateProviderTraitObjWrapper<'_> { ++ fn hash_key(&self, bytes: &[u8]) -> B256 { ++ self.0.hash_key(bytes) + } } -  - /// Parses all the nodes
+
@@ -17492,13 +26359,13 @@ @@ -17508,45 +26375,34 @@
-
+17
+
+1
-0
-
diff --git reth/crates/net/peers/src/bootnodes/scroll.rs scroll-reth/crates/net/peers/src/bootnodes/scroll.rs -new file mode 100644 -index 0000000000000000000000000000000000000000..7d85fcc8d077b6ea67ea3a1c5426620fb2185500 ---- /dev/null -+++ scroll-reth/crates/net/peers/src/bootnodes/scroll.rs -@@ -0,0 +1,17 @@ -+//! Scroll bootnodes come from <https://github.com/scroll-tech/go-ethereum/blob/develop/params/bootnodes.go> -+ -+/// Scroll mainnet boot nodes. -+pub static SCROLL_BOOTNODES: &[&str] = &[ -+ "enode://c6ac91f43df3d63916ac1ae411cdd5ba249d55d48a7bec7f8cd5bb351a31aba437e5a69e8a1de74d73fdfeba8af1cfe9caf9846ecd3abf60d1ffdf4925b55b23@54.186.123.248:30303", -+ "enode://fdcc807b5d1353f3a1e98b90208ce6ef1b7d446136e51eaa8ad657b55518a2f8b37655e42375d61622e6ea18f3faf9d070c9bbdf012cf5484bcbad33b7a15fb1@44.227.91.206:30303", -+ "enode://6beb5a3efbb39be73d17630b6da48e94c0ce7ec665172111463cb470197b20c12faa1fa6f835b81c28571277d1017e65c4e426cc92a46141cf69118ecf28ac03@44.237.194.52:30303", -+ "enode://7cf893d444eb8e129dca0f6485b3df579911606e7c728be4fa55fcc5f155a37c3ce07d217ccec5447798bde465ac2bdba2cb8763d107e9f3257e787579e9f27e@52.35.203.107:30303", -+ "enode://c7b2d94e95da343db6e667a01cef90376a592f2d277fbcbf6e9c9186734ed8003d01389571bd10cdbab7a6e5adfa6f0c7b55644d0db24e0b9deb4ec80f842075@54.70.236.187:30303", -+]; -+ -+/// Scroll sepolia boot nodes. -+pub static SCROLL_SEPOLIA_BOOTNODES: &[&str] = &[ -+ "enode://ceb1636bac5cbb262e5ad5b2cd22014bdb35ffe7f58b3506970d337a63099481814a338dbcd15f2d28757151e3ecd40ba38b41350b793cd0d910ff0436654f8c@35.85.84.250:30303", -+ "enode://29cee709c400533ae038a875b9ca975c8abef9eade956dcf3585e940acd5c0ae916968f514bd37d1278775aad1b7db30f7032a70202a87fd7365bd8de3c9f5fc@44.242.39.33:30303", -+ "enode://dd1ac5433c5c2b04ca3166f4cb726f8ff6d2da83dbc16d9b68b1ea83b7079b371eb16ef41c00441b6e85e32e33087f3b7753ea9e8b1e3f26d3e4df9208625e7f@54.148.111.168:30303", -+];
+
diff --git reth/crates/rpc/rpc-eth-types/src/fee_history.rs scroll-reth/crates/rpc/rpc-eth-types/src/fee_history.rs +index ae509dd2fdb44e8a7181e07c6f678d7e2d3b5b37..5de13dcaa4151d338f3879f4311bdf7866bbea8c 100644 +--- reth/crates/rpc/rpc-eth-types/src/fee_history.rs ++++ scroll-reth/crates/rpc/rpc-eth-types/src/fee_history.rs +@@ -48,6 +48,7 @@ } +  + /// How the cache is configured. + #[inline] ++ #[allow(clippy::missing_const_for_fn)] + pub fn config(&self) -> &FeeHistoryCacheConfig { + &self.inner.config + }
@@ -17555,13 +26411,13 @@ @@ -17571,28 +26427,32 @@
-
+8
+
+12
-0
-
diff --git reth/crates/node/api/Cargo.toml scroll-reth/crates/node/api/Cargo.toml -index fa323108dc666b033970b698c8a7e6c2e0b00dbb..c941adecc17acbb6df98ba203e6acb447304a012 100644 ---- reth/crates/node/api/Cargo.toml -+++ scroll-reth/crates/node/api/Cargo.toml -@@ -28,3 +28,11 @@ - alloy-rpc-types-engine.workspace = true +
diff --git reth/crates/rpc/rpc/Cargo.toml scroll-reth/crates/rpc/rpc/Cargo.toml +index 43fe54f121bc396e7919bd58e3a1fd6176051a19..cc3d1492a77d6b3868c6c36745c1be68a05dbb63 100644 +--- reth/crates/rpc/rpc/Cargo.toml ++++ scroll-reth/crates/rpc/rpc/Cargo.toml +@@ -101,3 +101,15 @@ jsonrpsee = { workspace = true, features = ["client"] }   - eyre.workspace = true -+ -+[features] + [features] + js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] +scroll = [ -+ "reth-provider/scroll", + "reth-evm/scroll", -+ "reth-node-core/scroll", -+ "reth-engine-primitives/scroll" ++ "reth-rpc-eth-types/scroll", ++ "reth-primitives/scroll", ++ "reth-revm/scroll", ++ "reth-provider/scroll", ++ "reth-testing-utils/scroll", ++ "reth-evm-ethereum/scroll", ++ "reth-rpc-eth-api/scroll", ++ "revm-primitives/scroll", ++ "revm/scroll", +]
@@ -17600,9 +26460,9 @@
@@ -17611,13 +26471,13 @@ @@ -17627,48 +26487,34 @@
-
+18
+
+1
-0
-
diff --git reth/crates/node/builder/Cargo.toml scroll-reth/crates/node/builder/Cargo.toml -index 9f08507f9f21b9b96b0a8c9c3c912ca867af9233..54ab3ba21bd08c48bb3eea7198699f1590fdd706 100644 ---- reth/crates/node/builder/Cargo.toml -+++ scroll-reth/crates/node/builder/Cargo.toml -@@ -111,3 +111,21 @@ "reth-db-api/test-utils", - "reth-provider/test-utils", - "reth-transaction-pool/test-utils", - ] -+scroll = [ -+ "reth-primitives/scroll", -+ "reth-db-api/scroll", -+ "reth-evm/scroll", -+ "reth-provider/scroll", -+ "reth-db?/scroll", -+ "reth-rpc/scroll", -+ "reth-node-core/scroll", -+ "reth-node-api/scroll", -+ "reth-exex/scroll", -+ "reth-rpc-eth-types/scroll", -+ "reth-stages/scroll", -+ "reth-invalid-block-hooks/scroll" -+] -+skip-state-root-validation = [ -+ "reth-stages/skip-state-root-validation", -+ "reth-engine-tree/skip-state-root-validation" -+]
+
diff --git reth/crates/rpc/rpc/src/debug.rs scroll-reth/crates/rpc/rpc/src/debug.rs +index b88bac816f8bfc2c32f4251e0bfb68f4c04fd59a..c009632a3ca7ab3844f9485375d4109f175d33ae 100644 +--- reth/crates/rpc/rpc/src/debug.rs ++++ scroll-reth/crates/rpc/rpc/src/debug.rs +@@ -66,6 +66,7 @@ Self { inner } + } +  + /// Access the underlying `Eth` API. ++ #[allow(clippy::missing_const_for_fn)] + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + }
@@ -17677,13 +26523,13 @@ @@ -17693,38 +26539,34 @@
-
+5
+
+1
-0
-
diff --git reth/crates/node/core/Cargo.toml scroll-reth/crates/node/core/Cargo.toml -index 7d4a417bed802fe6deefb9932953d65f5741f40b..c89d1174b44feb7c2c8ba03b3040c9770b728376 100644 ---- reth/crates/node/core/Cargo.toml -+++ scroll-reth/crates/node/core/Cargo.toml -@@ -80,6 +80,11 @@ optimism = ["reth-primitives/optimism", "reth-db/optimism"] - # Features for vergen to generate correct env vars - jemalloc = ["reth-cli-util/jemalloc"] - asm-keccak = ["reth-primitives/asm-keccak", "alloy-primitives/asm-keccak"] -+scroll = [ -+ "reth-primitives/scroll", -+ "reth-db/scroll", -+ "reth-rpc-eth-types/scroll", -+] +
diff --git reth/crates/rpc/rpc/src/eth/bundle.rs scroll-reth/crates/rpc/rpc/src/eth/bundle.rs +index 3cc1c1391638e8210833c741957fd8d2a61d6fb2..578ca78094507b03274a79bbf0a0829cf0448e01 100644 +--- reth/crates/rpc/rpc/src/eth/bundle.rs ++++ scroll-reth/crates/rpc/rpc/src/eth/bundle.rs +@@ -39,6 +39,7 @@ Self { inner: Arc::new(EthBundleInner { eth_api, blocking_task_guard }) } + }   - [build-dependencies] - vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl"] }
+ /// Access the underlying `Eth` API. ++ #[allow(clippy::missing_const_for_fn)] + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + }
@@ -17733,13 +26575,13 @@ @@ -17749,37 +26591,34 @@
-
+5
-
-1
+
+1
+
-0
-
diff --git reth/crates/revm/Cargo.toml scroll-reth/crates/revm/Cargo.toml -index cc1c8edcb8db6efe67d63855e5421fb730a33e93..3e9c58015fe15310e73ae765e919b93d373a97db 100644 ---- reth/crates/revm/Cargo.toml -+++ scroll-reth/crates/revm/Cargo.toml -@@ -61,5 +61,9 @@ "alloy-primitives/serde", - "alloy-consensus/serde", - "reth-primitives-traits/serde", - "reth-trie?/serde", -- "reth-ethereum-forks/serde" -+ "reth-ethereum-forks/serde", -+] -+scroll = [ -+ "reth-primitives-traits/scroll", -+ "reth-trie?/scroll" - ]
+
diff --git reth/crates/rpc/rpc/src/eth/filter.rs scroll-reth/crates/rpc/rpc/src/eth/filter.rs +index 337fbb91e06dde829f93059d242b6be6e3871585..4ca1796fc0eacb69731e4507a71061b1e575a481 100644 +--- reth/crates/rpc/rpc/src/eth/filter.rs ++++ scroll-reth/crates/rpc/rpc/src/eth/filter.rs +@@ -98,6 +98,7 @@ eth_filter + } +  + /// Returns all currently active filters ++ #[allow(clippy::missing_const_for_fn)] + pub fn active_filters(&self) -> &ActiveFilters<RpcTransaction<Eth::NetworkTypes>> { + &self.inner.active_filters + }
@@ -17788,95 +26627,50 @@ -
- -
- -
- -
+17
-
-5
- -
- -
-
-
diff --git reth/crates/revm/src/test_utils.rs scroll-reth/crates/revm/src/test_utils.rs -index 6d2dbf2ca01d84e88228dbc8d691c927488863f3..f4e4f6d883acf1c16ffbb4fe8687b071cf453abc 100644 ---- reth/crates/revm/src/test_utils.rs -+++ scroll-reth/crates/revm/src/test_utils.rs -@@ -6,12 +6,12 @@ Address, BlockNumber, Bytes, StorageKey, B256, U256, - }; - use reth_primitives::{Account, Bytecode}; - use reth_storage_api::{ -- AccountReader, BlockHashReader, HashedPostStateProvider, StateProofProvider, StateProvider, -- StateRootProvider, StorageRootProvider, -+ AccountReader, BlockHashReader, HashedPostStateProvider, HashedStorageProvider, -+ KeyHasherProvider, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, - }; - use reth_storage_errors::provider::ProviderResult; - use reth_trie::{ -- updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, -+ updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, KeyHasher, - MultiProof, MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, - }; -  -@@ -72,7 +72,7 @@ } - } -  - impl StateRootProvider for StateProviderTest { -- fn state_root(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { -+ fn state_root_from_state(&self, _hashed_state: HashedPostState) -> ProviderResult<B256> { - unimplemented!("state root computation is not supported") - } -  -@@ -80,7 +80,7 @@ fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> { - unimplemented!("state root computation is not supported") - } -  -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - _hashed_state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { -@@ -153,6 +153,18 @@ - impl HashedPostStateProvider for StateProviderTest { - fn hashed_post_state(&self, bundle_state: &revm::db::BundleState) -> HashedPostState { - HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state()) -+ } -+} -+ -+impl HashedStorageProvider for StateProviderTest { -+ fn hashed_storage(&self, account: &revm::db::BundleAccount) -> HashedStorage { -+ HashedStorage::from_bundle_account::<KeccakKeyHasher>(account) -+ } -+} -+ -+impl KeyHasherProvider for StateProviderTest { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ KeccakKeyHasher::hash_key(bytes) +
+ +
+ +
+ +
+1
+
-0
+ +
+ +
+ +
diff --git reth/crates/rpc/rpc/src/eth/sim_bundle.rs scroll-reth/crates/rpc/rpc/src/eth/sim_bundle.rs +index de2597b64a3fd11eab9622adfe74e26f7d7258fe..35d92fcbf8aee127e736c630afc0777cdd16bd42 100644 +--- reth/crates/rpc/rpc/src/eth/sim_bundle.rs ++++ scroll-reth/crates/rpc/rpc/src/eth/sim_bundle.rs +@@ -78,6 +78,7 @@ Self { inner: Arc::new(EthSimBundleInner { eth_api, blocking_task_guard }) } } - } -
+  + /// Access the underlying `Eth` API. ++ #[allow(clippy::missing_const_for_fn)] + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + }
@@ -17885,13 +26679,13 @@ @@ -17901,37 +26695,34 @@
-
+7
+
+1
-0
-
diff --git reth/crates/rpc/rpc-eth-api/Cargo.toml scroll-reth/crates/rpc/rpc-eth-api/Cargo.toml -index 6f65b91d8f80d7faeae39c04b4905c9a6af91453..a4600e4cd5b878c12a19fe09c8916c78e0f44bab 100644 ---- reth/crates/rpc/rpc-eth-api/Cargo.toml -+++ scroll-reth/crates/rpc/rpc-eth-api/Cargo.toml -@@ -62,3 +62,10 @@ - [features] - js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] - client = ["jsonrpsee/client", "jsonrpsee/async-client"] -+scroll = [ -+ "reth-provider/scroll", -+ "reth-revm/scroll", -+ "reth-primitives/scroll", -+ "reth-evm/scroll", -+ "reth-rpc-eth-types/scroll", -+]
+
diff --git reth/crates/rpc/rpc/src/reth.rs scroll-reth/crates/rpc/rpc/src/reth.rs +index dbd6398465f0d02ce42193d3961878dcb4a996f4..6f0008c8c0b2f88d402406ef9f169a4a98a88387 100644 +--- reth/crates/rpc/rpc/src/reth.rs ++++ scroll-reth/crates/rpc/rpc/src/reth.rs +@@ -22,6 +22,7 @@ // === impl RethApi === +  + impl<Provider> RethApi<Provider> { + /// The provider that can interact with the chain. ++ #[allow(clippy::missing_const_for_fn)] + pub fn provider(&self) -> &Provider { + &self.inner.provider + }
@@ -17940,13 +26731,13 @@ @@ -17956,36 +26747,34 @@
-
+2
-
-1
+
+1
+
-0
-
diff --git reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs scroll-reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs -index 9b52b94a4db865ed348f04d620c6f276b0506aac..3ac5446df5b5e1fa61b312b9e1bbb8d3e58ca834 100644 ---- reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs -+++ scroll-reth/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs -@@ -416,7 +416,8 @@ let bundle_state = db.take_bundle(); - let hashed_state = db.database.hashed_post_state(&bundle_state); -  - // calculate the state root -- let state_root = db.database.state_root(hashed_state).map_err(Self::Error::from_eth_err)?; -+ let state_root = -+ db.database.state_root_from_state(hashed_state).map_err(Self::Error::from_eth_err)?; +
diff --git reth/crates/rpc/rpc/src/trace.rs scroll-reth/crates/rpc/rpc/src/trace.rs +index d906419021b90076b5496e48cad3a050740fb48b..cfc1cafb1f85135bd1de63640df0742809ccbaa1 100644 +--- reth/crates/rpc/rpc/src/trace.rs ++++ scroll-reth/crates/rpc/rpc/src/trace.rs +@@ -60,6 +60,7 @@ self.inner.blocking_task_guard.clone().acquire_owned().await + }   - let (block, receipts) = self.assemble_block_and_receipts( - &block_env,
+ /// Access the underlying `Eth` API. ++ #[allow(clippy::missing_const_for_fn)] + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + }
@@ -17994,13 +26783,13 @@ @@ -18010,50 +26799,37 @@
-
+10
-
-1
+
+2
+
-2
-
diff --git reth/crates/rpc/rpc-eth-types/Cargo.toml scroll-reth/crates/rpc/rpc-eth-types/Cargo.toml -index 72b153ab08457e8f8079cd61c830c84483723d7d..0d20719dd85dc9f3acb921397821397f3e362cc6 100644 ---- reth/crates/rpc/rpc-eth-types/Cargo.toml -+++ scroll-reth/crates/rpc/rpc-eth-types/Cargo.toml -@@ -33,9 +33,11 @@ alloy-primitives.workspace = true - alloy-consensus.workspace = true - alloy-sol-types.workspace = true - alloy-rpc-types-eth.workspace = true --revm.workspace = true - revm-inspectors.workspace = true - revm-primitives = { workspace = true, features = ["dev"] } -+ -+# scroll -+revm.workspace = true +
diff --git reth/crates/rpc/rpc/src/validation.rs scroll-reth/crates/rpc/rpc/src/validation.rs +index d2faf0dd52e9dc886bb71643d5dc957042943f85..ae0d938aab977e2f1f1cc6ec62fdbb698e226a5f 100644 +--- reth/crates/rpc/rpc/src/validation.rs ++++ scroll-reth/crates/rpc/rpc/src/validation.rs +@@ -173,8 +173,8 @@ )?;   - # rpc - jsonrpsee-core.workspace = true -@@ -64,3 +66,10 @@ + self.ensure_payment(&block, &output, &message)?;   - [features] - js-tracer = ["revm-inspectors/js-tracer"] -+scroll = [ -+ "revm/scroll", -+ "reth-execution-types/scroll", -+ "reth-revm/scroll", -+ "reth-primitives/scroll", -+ "reth-trie/scroll" -+]
+- let state_root = +- state_provider.state_root(state_provider.hashed_post_state(&output.state))?; ++ let state_root = state_provider ++ .state_root_from_state(state_provider.hashed_post_state(&output.state))?; +  + if state_root != block.header().state_root() { + return Err(ConsensusError::BodyStateRootDiff(
@@ -18062,13 +26838,13 @@ @@ -18078,84 +26854,102 @@
-
+19
-
-5
+
+1
+
-0
-
diff --git reth/crates/rpc/rpc-eth-types/src/cache/db.rs scroll-reth/crates/rpc/rpc-eth-types/src/cache/db.rs -index dd15f7602179a98d6695f8396c2280f4c35dfdb7..8d205f56a05e04931bc3707afe897912a50c740e 100644 ---- reth/crates/rpc/rpc-eth-types/src/cache/db.rs -+++ scroll-reth/crates/rpc/rpc-eth-types/src/cache/db.rs -@@ -5,7 +5,9 @@ - use alloy_primitives::{Address, B256, U256}; - use reth_errors::ProviderResult; - use reth_revm::{database::StateProviderDatabase, db::CacheDB, DatabaseRef}; --use reth_storage_api::{HashedPostStateProvider, StateProvider}; -+use reth_storage_api::{ -+ HashedPostStateProvider, HashedStorageProvider, KeyHasherProvider, StateProvider, -+}; - use reth_trie::{HashedStorage, MultiProofTargets}; - use revm::Database; -  -@@ -18,11 +20,11 @@ #[allow(missing_debug_implementations)] - pub struct StateProviderTraitObjWrapper<'a>(pub &'a dyn StateProvider); -  - impl reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'_> { -- fn state_root( -+ fn state_root_from_state( - &self, - hashed_state: reth_trie::HashedPostState, - ) -> reth_errors::ProviderResult<B256> { -- self.0.state_root(hashed_state) -+ self.0.state_root_from_state(hashed_state) +
diff --git reth/crates/transaction-pool/src/lib.rs scroll-reth/crates/transaction-pool/src/lib.rs +index 56fe99421d5a4b3a538e1bc3e2b6b4df778a8509..12b42235a121662cbf6ca50e4eb7d006af6bce7a 100644 +--- reth/crates/transaction-pool/src/lib.rs ++++ scroll-reth/crates/transaction-pool/src/lib.rs +@@ -228,6 +228,7 @@ Self { pool: Arc::new(PoolInner::new(validator, ordering, blob_store, config)) } }   - fn state_root_from_nodes( -@@ -32,11 +34,11 @@ ) -> reth_errors::ProviderResult<B256> { - self.0.state_root_from_nodes(input) + /// Returns the wrapped pool. ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn inner(&self) -> &PoolInner<V, T, S> { + &self.pool + }
+
+ + +
+ + +
+
+
+ + reth + +
+ +
+ + scroll-reth + +
+
+
+ +
+ +
+ +
+3
+
-0
+ +
+ +
+
+
diff --git reth/crates/transaction-pool/src/pool/mod.rs scroll-reth/crates/transaction-pool/src/pool/mod.rs +index a7c16d05aa8e5433021ca3a03baaca94736f4ebe..0f6714b5095f8aeaa84e42a4e212c97143a7fc20 100644 +--- reth/crates/transaction-pool/src/pool/mod.rs ++++ scroll-reth/crates/transaction-pool/src/pool/mod.rs +@@ -1078,6 +1078,7 @@ PendingTransactionIter { kind, iter } }   -- fn state_root_with_updates( -+ fn state_root_from_state_with_updates( - &self, - hashed_state: reth_trie::HashedPostState, - ) -> reth_errors::ProviderResult<(B256, reth_trie::updates::TrieUpdates)> { -- self.0.state_root_with_updates(hashed_state) -+ self.0.state_root_from_state_with_updates(hashed_state) + /// Returns if the transaction should be propagated. ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn is_propagate_allowed(&self) -> bool { + self.transaction.propagate } -  - fn state_root_from_nodes_with_updates( -@@ -142,6 +144,18 @@ &self, - bundle_state: &revm::db::BundleState, - ) -> reth_trie::HashedPostState { - self.0.hashed_post_state(bundle_state) -+ } -+} -+ -+impl HashedStorageProvider for StateProviderTraitObjWrapper<'_> { -+ fn hashed_storage(&self, account: &revm::db::BundleAccount) -> HashedStorage { -+ self.0.hashed_storage(account) -+ } -+} -+ -+impl KeyHasherProvider for StateProviderTraitObjWrapper<'_> { -+ fn hash_key(&self, bytes: &[u8]) -> B256 { -+ self.0.hash_key(bytes) +@@ -1168,6 +1169,7 @@ } } - } -
+  + /// Returns the discarded transactions if there were any ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn discarded_transactions(&self) -> Option<&[Arc<ValidPoolTransaction<T>>]> { + match self { + Self::Pending(tx) => Some(&tx.discarded), +@@ -1211,6 +1213,7 @@ } +  + /// Returns the [`TransactionId`] of the added transaction + #[cfg(test)] ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn id(&self) -> &TransactionId { + match self { + Self::Pending(added) => added.transaction.id(),
@@ -18164,13 +26958,13 @@ @@ -18180,42 +26974,34 @@
-
+12
+
+1
-0
-
diff --git reth/crates/rpc/rpc/Cargo.toml scroll-reth/crates/rpc/rpc/Cargo.toml -index 43fe54f121bc396e7919bd58e3a1fd6176051a19..cc3d1492a77d6b3868c6c36745c1be68a05dbb63 100644 ---- reth/crates/rpc/rpc/Cargo.toml -+++ scroll-reth/crates/rpc/rpc/Cargo.toml -@@ -101,3 +101,15 @@ jsonrpsee = { workspace = true, features = ["client"] } -  - [features] - js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] -+scroll = [ -+ "reth-evm/scroll", -+ "reth-rpc-eth-types/scroll", -+ "reth-primitives/scroll", -+ "reth-revm/scroll", -+ "reth-provider/scroll", -+ "reth-testing-utils/scroll", -+ "reth-evm-ethereum/scroll", -+ "reth-rpc-eth-api/scroll", -+ "revm-primitives/scroll", -+ "revm/scroll", -+]
+
diff --git reth/crates/transaction-pool/src/pool/pending.rs scroll-reth/crates/transaction-pool/src/pool/pending.rs +index 27706bd17543064e44d4ad78e88c1bb3cca71360..6d81b929e74bdbe9e6e49a1a9529e82750b1e5e1 100644 +--- reth/crates/transaction-pool/src/pool/pending.rs ++++ scroll-reth/crates/transaction-pool/src/pool/pending.rs +@@ -560,6 +560,7 @@ } +  + impl<T: TransactionOrdering> PendingTransaction<T> { + /// The next transaction of the sender: `nonce + 1` ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn unlocks(&self) -> TransactionId { + self.transaction.transaction_id.descendant() + }
@@ -18224,13 +27010,13 @@ @@ -18240,36 +27026,47 @@
-
+2
-
-2
+
+3
+
-0
-
diff --git reth/crates/rpc/rpc/src/validation.rs scroll-reth/crates/rpc/rpc/src/validation.rs -index d2faf0dd52e9dc886bb71643d5dc957042943f85..ae0d938aab977e2f1f1cc6ec62fdbb698e226a5f 100644 ---- reth/crates/rpc/rpc/src/validation.rs -+++ scroll-reth/crates/rpc/rpc/src/validation.rs -@@ -173,8 +173,8 @@ )?; -  - self.ensure_payment(&block, &output, &message)?; +
diff --git reth/crates/transaction-pool/src/validate/eth.rs scroll-reth/crates/transaction-pool/src/validate/eth.rs +index f6733a5c1aa924a9aa9d347ccf76bbab9837e976..18f4fce8eb514d8cb2339d20fc330bf8dec11635 100644 +--- reth/crates/transaction-pool/src/validate/eth.rs ++++ scroll-reth/crates/transaction-pool/src/validate/eth.rs +@@ -45,11 +45,13 @@ } +  + impl<Client, Tx> EthTransactionValidator<Client, Tx> { + /// Returns the configured chain spec ++ #[allow(clippy::missing_const_for_fn)] + pub fn chain_spec(&self) -> &Arc<ChainSpec> { + &self.inner.chain_spec + }   -- let state_root = -- state_provider.state_root(state_provider.hashed_post_state(&output.state))?; -+ let state_root = state_provider -+ .state_root_from_state(state_provider.hashed_post_state(&output.state))?; + /// Returns the configured client ++ #[allow(clippy::missing_const_for_fn)] + pub fn client(&self) -> &Client { + &self.inner.client + } +@@ -165,6 +167,7 @@ // === impl EthTransactionValidatorInner ===   - if state_root != block.header().state_root() { - return Err(ConsensusError::BodyStateRootDiff(
+ impl<Client, Tx> EthTransactionValidatorInner<Client, Tx> { + /// Returns the configured chain id ++ #[allow(clippy::missing_const_for_fn)] + pub(crate) fn chain_id(&self) -> u64 { + self.chain_spec.chain().id() + }
- @@ -18285,7 +27082,7 @@
@@ -18302,7 +27099,7 @@
-
diff --git reth/deny.toml scroll-reth/deny.toml +
diff --git reth/deny.toml scroll-reth/deny.toml index 8d0807f9de5c63618c5ca26f05ae75a198d95715..71715c505f30dd18c2a3ebd1ac53632a9e6e144a 100644 --- reth/deny.toml +++ scroll-reth/deny.toml @@ -18338,8 +27135,8 @@
@@ -18372,7 +27169,7 @@
-
diff --git reth/examples/custom-beacon-withdrawals/Cargo.toml scroll-reth/examples/custom-beacon-withdrawals/Cargo.toml +
diff --git reth/examples/custom-beacon-withdrawals/Cargo.toml scroll-reth/examples/custom-beacon-withdrawals/Cargo.toml index c396ca11df8bd03b44535914a3dbe1643b4e67d3..6efebce956a59cb79cc38b4bb5fff30b3d512859 100644 --- reth/examples/custom-beacon-withdrawals/Cargo.toml +++ scroll-reth/examples/custom-beacon-withdrawals/Cargo.toml @@ -18403,8 +27200,8 @@
@@ -18437,7 +27234,7 @@
-
diff --git reth/examples/custom-beacon-withdrawals/src/main.rs scroll-reth/examples/custom-beacon-withdrawals/src/main.rs +
diff --git reth/examples/custom-beacon-withdrawals/src/main.rs scroll-reth/examples/custom-beacon-withdrawals/src/main.rs index f484b082be7a3fa8b6dc21603766979cc58c5665..f97a3e01c4cfb9fc3d9161e096df600a475ab050 100644 --- reth/examples/custom-beacon-withdrawals/src/main.rs +++ scroll-reth/examples/custom-beacon-withdrawals/src/main.rs @@ -18456,8 +27253,8 @@
@@ -18483,23 +27280,22 @@
-
+10
+
+7
-0
-
diff --git reth/testing/ef-tests/Cargo.toml scroll-reth/testing/ef-tests/Cargo.toml -index 2fc0c7512441691ca58f9bd67b6d2cc4e7f00bbd..72d1fb90893f42fe2d214d5c12cac2cd9113b1c8 100644 +
diff --git reth/testing/ef-tests/Cargo.toml scroll-reth/testing/ef-tests/Cargo.toml +index 2fc0c7512441691ca58f9bd67b6d2cc4e7f00bbd..37b8881f156012d20d6593f76de591d8d019dea3 100644 --- reth/testing/ef-tests/Cargo.toml +++ scroll-reth/testing/ef-tests/Cargo.toml -@@ -18,6 +18,14 @@ "reth-primitives/asm-keccak", +@@ -18,6 +18,13 @@ "reth-primitives/asm-keccak", "alloy-primitives/asm-keccak", "revm/asm-keccak", ] +scroll = [ -+ "reth-scroll-primitives", + "reth-revm/scroll", + "reth-primitives/scroll", + "reth-provider/scroll", @@ -18508,24 +27304,15 @@ +]   [dependencies] - reth-chainspec.workspace = true -@@ -34,6 +42,8 @@ reth-evm-ethereum.workspace = true - reth-revm = { workspace = true, features = ["std"] } -  - revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] } -+ -+reth-scroll-primitives = { workspace = true, optional = true } -  - alloy-rlp.workspace = true - alloy-primitives.workspace = true
+ reth-chainspec.workspace = true
@@ -18558,7 +27345,7 @@
-
diff --git reth/testing/testing-utils/Cargo.toml scroll-reth/testing/testing-utils/Cargo.toml +
diff --git reth/testing/testing-utils/Cargo.toml scroll-reth/testing/testing-utils/Cargo.toml index 8dc438170e8eb95e8e8b54057836d40ddbc79f02..57ac891a9ccf5f4366d182eefeaaeaadf8b8df91 100644 --- reth/testing/testing-utils/Cargo.toml +++ scroll-reth/testing/testing-utils/Cargo.toml @@ -18588,7 +27375,7 @@
-