1- use alloy:: { consensus:: ReceiptEnvelope , eips:: eip6110:: DepositRequest } ;
2- use alloy_primitives:: Log ;
1+ use alloy:: consensus:: ReceiptEnvelope ;
2+ use alloy_primitives:: { Bytes , Log } ;
3+ use alloy_rlp:: BufMut ;
34use alloy_sol_types:: { sol, SolEvent } ;
45
56/// The address for the Ethereum 2.0 deposit contract on the mainnet.
@@ -22,49 +23,34 @@ sol! {
2223///
2324/// - The `DepositEvent` is the only event in the deposit contract.
2425/// - The deposit contract enforces the length of the fields.
25- pub fn parse_deposit_from_log ( log : & Log < DepositEvent > ) -> DepositRequest {
26- // SAFETY: These `expect` https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/solidity_deposit_contract/deposit_contract.sol#L107-L110
27- // are safe because the `DepositEvent` is the only event in the deposit contract and the length
28- // checks are done there.
29- DepositRequest {
30- pubkey : log
31- . pubkey
32- . as_ref ( )
33- . try_into ( )
34- . expect ( "pubkey length should be enforced in deposit contract" ) ,
35- withdrawal_credentials : log
36- . withdrawal_credentials
37- . as_ref ( )
38- . try_into ( )
39- . expect ( "withdrawal_credentials length should be enforced in deposit contract" ) ,
40- amount : u64:: from_le_bytes (
41- log. amount
42- . as_ref ( )
43- . try_into ( )
44- . expect ( "amount length should be enforced in deposit contract" ) ,
45- ) ,
46- signature : log
47- . signature
48- . as_ref ( )
49- . try_into ( )
50- . expect ( "signature length should be enforced in deposit contract" ) ,
51- index : u64:: from_le_bytes (
52- log. index
53- . as_ref ( )
54- . try_into ( )
55- . expect ( "deposit index length should be enforced in deposit contract" ) ,
56- ) ,
57- }
26+ pub fn parse_deposit_from_log ( log : & Log < DepositEvent > ) -> Bytes {
27+ [
28+ log. pubkey . as_ref ( ) ,
29+ log. withdrawal_credentials . as_ref ( ) ,
30+ log. amount . as_ref ( ) ,
31+ log. signature . as_ref ( ) ,
32+ log. index . as_ref ( ) ,
33+ ]
34+ . concat ( )
35+ . into ( )
36+ }
37+
38+ /// Accumulate a deposit request from a log.
39+ pub fn accumulate_deposit_from_log ( log : & Log < DepositEvent > , out : & mut dyn BufMut ) {
40+ out. put_slice ( log. pubkey . as_ref ( ) ) ;
41+ out. put_slice ( log. withdrawal_credentials . as_ref ( ) ) ;
42+ out. put_slice ( log. amount . as_ref ( ) ) ;
43+ out. put_slice ( log. signature . as_ref ( ) ) ;
44+ out. put_slice ( log. index . as_ref ( ) ) ;
5845}
5946
60- /// Check an iterator of logs for deposit events, parsing them into
61- /// [`DepositRequest`]s.
47+ /// Check an iterator of logs for deposit events.
6248///
6349/// When parsing logs, the following assumptions are made
6450///
6551/// - The `DepositEvent` is the only event in the deposit contract.
6652/// - The deposit contract enforces the length of the fields.
67- pub fn check_logs_for_deposits < ' a , I > ( logs : I ) -> impl Iterator < Item = DepositRequest > + ' a
53+ pub fn check_logs_for_deposits < ' a , I > ( logs : I ) -> impl Iterator < Item = Bytes > + ' a
6854where
6955 I : IntoIterator < Item = & ' a Log > ,
7056 I :: IntoIter : ' a ,
7763 } )
7864}
7965
66+ /// Accumulate deposits from an iterator of logs.
67+ pub fn accumulate_deposits_from_logs < ' a > (
68+ logs : impl IntoIterator < Item = & ' a Log > ,
69+ out : & mut dyn BufMut ,
70+ ) {
71+ logs. into_iter ( ) . filter ( |log| log. address == MAINNET_DEPOSIT_CONTRACT_ADDRESS ) . for_each (
72+ |log| {
73+ // We assume that the log is valid because it was emitted by the
74+ // deposit contract.
75+ let decoded_log = DepositEvent :: decode_log ( log, false ) . expect ( "invalid log" ) ;
76+ accumulate_deposit_from_log ( & decoded_log, out) ;
77+ } ,
78+ ) ;
79+ }
80+
8081/// Find deposit logs in a receipt. Iterates over the logs in the receipt and
81- /// returns a [`DepositRequest`] for each log that is emitted by the
82+ /// returns a deposit request bytestring for each log that is emitted by the
8283/// deposit contract.
8384///
8485/// When parsing logs, the following assumptions are made
@@ -87,10 +88,36 @@ where
8788/// - The deposit contract enforces the length of the fields.
8889pub fn check_receipt_for_deposits (
8990 receipt : & ReceiptEnvelope ,
90- ) -> impl Iterator < Item = DepositRequest > + ' _ {
91+ ) -> impl Iterator < Item = Bytes > + use < ' _ > {
9192 check_logs_for_deposits ( receipt. logs ( ) )
9293}
9394
95+ /// Accumulate deposits from a receipt. Iterates over the logs in the receipt
96+ /// and accumulates the deposit request bytestrings.
97+ pub fn accumulate_deposits_from_receipt ( receipt : & ReceiptEnvelope , out : & mut dyn BufMut ) {
98+ accumulate_deposits_from_logs ( receipt. logs ( ) , out) ;
99+ }
100+
101+ /// Accumulate deposits from a list of receipts. Iterates over the logs in the
102+ /// receipts and accumulates the deposit request bytestrings.
103+ pub fn accumulate_deposits_from_receipts < ' a , I > ( receipts : I , out : & mut dyn BufMut )
104+ where
105+ I : IntoIterator < Item = & ' a ReceiptEnvelope > ,
106+ {
107+ receipts. into_iter ( ) . for_each ( |receipt| accumulate_deposits_from_receipt ( receipt, out) ) ;
108+ }
109+
110+ /// Find deposit logs in a list of receipts, and return the concatenated
111+ /// deposit request bytestring.
112+ pub fn deposits_from_receipts < ' a , I > ( receipts : I ) -> Bytes
113+ where
114+ I : IntoIterator < Item = & ' a ReceiptEnvelope > ,
115+ {
116+ let mut out = Vec :: new ( ) ;
117+ accumulate_deposits_from_receipts ( receipts, & mut out) ;
118+ out. into ( )
119+ }
120+
94121// Some code above is reproduced from `reth`. It is reused here under the MIT
95122// license.
96123//
@@ -117,46 +144,44 @@ pub fn check_receipt_for_deposits(
117144// THE SOFTWARE.
118145
119146#[ cfg( test) ]
120- mod test {
121- use alloy:: {
122- consensus:: { Receipt , ReceiptEnvelope } ,
123- eips:: eip6110:: MAINNET_DEPOSIT_CONTRACT_ADDRESS ,
124- } ;
125- use alloy_primitives:: { Log , LogData } ;
126- use alloy_sol_types:: SolEvent ;
127-
128- use super :: DepositEvent ;
147+ mod tests {
148+ use super :: * ;
149+ use alloy:: consensus:: { Receipt , ReceiptEnvelope } ;
150+ use alloy_primitives:: bytes;
129151
130152 #[ test]
131- fn test_eip6110 ( ) {
132- let receipt = Receipt {
133- logs : vec ! [ Log {
134- address: MAINNET_DEPOSIT_CONTRACT_ADDRESS ,
135- data: LogData :: new_unchecked(
136- vec![ DepositEvent :: SIGNATURE_HASH ] ,
137- DepositEvent {
138- pubkey: [ 1 ; 48 ] . to_vec( ) . into( ) ,
139- withdrawal_credentials: [ 2 ; 32 ] . to_vec( ) . into( ) ,
140- amount: [ 3 ; 8 ] . to_vec( ) . into( ) ,
141- signature: [ 4 ; 96 ] . to_vec( ) . into( ) ,
142- index: [ 5 ; 8 ] . to_vec( ) . into( ) ,
143- }
144- . encode_data( )
145- . into( ) ,
146- ) ,
147- } ] ,
148- status : true . into ( ) ,
149- cumulative_gas_used : 0 ,
150- } ;
151-
152- let deposits: Vec < _ > =
153- super :: check_receipt_for_deposits ( & ReceiptEnvelope :: Eip1559 ( receipt. into ( ) ) ) . collect ( ) ;
153+ fn test_parse_deposit_from_log ( ) {
154+ let receipts = vec ! [
155+ // https://etherscan.io/tx/0xa5239d4c542063d29022545835815b78b09f571f2bf1c8427f4765d6f5abbce9
156+ #[ allow( clippy:: needless_update) ] // side-effect of optimism fields
157+ ReceiptEnvelope :: Legacy ( Receipt {
158+ // these don't matter
159+ status: true . into( ) ,
160+ cumulative_gas_used: 0 ,
161+ logs: serde_json:: from_str(
162+ r#"[{"address":"0x00000000219ab540356cbb839cbe05303d7705fa","topics":["0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"],"data":"0x00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030998c8086669bf65e24581cda47d8537966e9f5066fc6ffdcba910a1bfb91eae7a4873fcce166a1c4ea217e6b1afd396200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002001000000000000000000000001c340fb72ed14d4eaa71f7633ee9e33b88d4f3900000000000000000000000000000000000000000000000000000000000000080040597307000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006098ddbffd700c1aac324cfdf0492ff289223661eb26718ce3651ba2469b22f480d56efab432ed91af05a006bde0c1ea68134e0acd8cacca0c13ad1f716db874b44abfcc966368019753174753bca3af2ea84bc569c46f76592a91e97f311eddec0000000000000000000000000000000000000000000000000000000000000008e474160000000000000000000000000000000000000000000000000000000000","blockHash":"0x8d1289c5a7e0965b1d1bb75cdc4c3f73dda82d4ebb94ff5b98d1389cebd53b56","blockNumber":"0x12f0d8d","transactionHash":"0xa5239d4c542063d29022545835815b78b09f571f2bf1c8427f4765d6f5abbce9","transactionIndex":"0xc4","logIndex":"0x18f","removed":false}]"#
163+ ) . unwrap( ) ,
164+ ..Default :: default ( )
165+ } . with_bloom( ) ) ,
166+ // https://etherscan.io/tx/0xd9734d4e3953bcaa939fd1c1d80950ee54aeecc02eef6ae8179f47f5b7103338
167+ #[ allow( clippy:: needless_update) ] // side-effect of optimism fields
168+ ReceiptEnvelope :: Legacy ( Receipt {
169+ // these don't matter
170+ status: true . into( ) ,
171+ cumulative_gas_used: 0 ,
172+ logs: serde_json:: from_str(
173+ r#"[{"address":"0x00000000219ab540356cbb839cbe05303d7705fa","topics":["0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"],"data":"0x00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030a1a2ba870a90e889aa594a0cc1c6feffb94c2d8f65646c937f1f456a315ef649533e25a4614d8f4f66ebdb06481b90af0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000a0f04a231efbc29e1db7d086300ff550211c2f6000000000000000000000000000000000000000000000000000000000000000800405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060ad416d590e1a7f52baff770a12835b68904efad22cc9f8ba531e50cbbd26f32b9c7373cf6538a0577f501e4d3e3e63e208767bcccaae94e1e3720bfb734a286f9c017d17af46536545ccb7ca94d71f295e71f6d25bf978c09ada6f8d3f7ba0390000000000000000000000000000000000000000000000000000000000000008e374160000000000000000000000000000000000000000000000000000000000","blockHash":"0x8d1289c5a7e0965b1d1bb75cdc4c3f73dda82d4ebb94ff5b98d1389cebd53b56","blockNumber":"0x12f0d8d","transactionHash":"0xd9734d4e3953bcaa939fd1c1d80950ee54aeecc02eef6ae8179f47f5b7103338","transactionIndex":"0x7c","logIndex":"0xe2","removed":false}]"# ,
174+ ) . unwrap( ) ,
175+ ..Default :: default ( )
176+ } . with_bloom( ) ) ,
177+ ] ;
154178
155- assert_eq ! ( deposits. len( ) , 1 ) ;
156- assert_eq ! ( deposits[ 0 ] . pubkey, [ 1 ; 48 ] ) ;
157- assert_eq ! ( deposits[ 0 ] . withdrawal_credentials, [ 2 ; 32 ] ) ;
158- assert_eq ! ( deposits[ 0 ] . amount, 0x0303030303030303 ) ;
159- assert_eq ! ( deposits[ 0 ] . signature, [ 4 ; 96 ] ) ;
160- assert_eq ! ( deposits[ 0 ] . index, 0x0505050505050505 ) ;
179+ let request_data = deposits_from_receipts ( & receipts) ;
180+ assert_eq ! (
181+ request_data,
182+ bytes!(
183+ "998c8086669bf65e24581cda47d8537966e9f5066fc6ffdcba910a1bfb91eae7a4873fcce166a1c4ea217e6b1afd396201000000000000000000000001c340fb72ed14d4eaa71f7633ee9e33b88d4f39004059730700000098ddbffd700c1aac324cfdf0492ff289223661eb26718ce3651ba2469b22f480d56efab432ed91af05a006bde0c1ea68134e0acd8cacca0c13ad1f716db874b44abfcc966368019753174753bca3af2ea84bc569c46f76592a91e97f311eddece474160000000000a1a2ba870a90e889aa594a0cc1c6feffb94c2d8f65646c937f1f456a315ef649533e25a4614d8f4f66ebdb06481b90af0100000000000000000000000a0f04a231efbc29e1db7d086300ff550211c2f60040597307000000ad416d590e1a7f52baff770a12835b68904efad22cc9f8ba531e50cbbd26f32b9c7373cf6538a0577f501e4d3e3e63e208767bcccaae94e1e3720bfb734a286f9c017d17af46536545ccb7ca94d71f295e71f6d25bf978c09ada6f8d3f7ba039e374160000000000"
184+ )
185+ ) ;
161186 }
162187}
0 commit comments