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 ;
3
4
use alloy_sol_types:: { sol, SolEvent } ;
4
5
5
6
/// The address for the Ethereum 2.0 deposit contract on the mainnet.
@@ -22,49 +23,34 @@ sol! {
22
23
///
23
24
/// - The `DepositEvent` is the only event in the deposit contract.
24
25
/// - 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 ( ) ) ;
58
45
}
59
46
60
- /// Check an iterator of logs for deposit events, parsing them into
61
- /// [`DepositRequest`]s.
47
+ /// Check an iterator of logs for deposit events.
62
48
///
63
49
/// When parsing logs, the following assumptions are made
64
50
///
65
51
/// - The `DepositEvent` is the only event in the deposit contract.
66
52
/// - 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
68
54
where
69
55
I : IntoIterator < Item = & ' a Log > ,
70
56
I :: IntoIter : ' a ,
77
63
} )
78
64
}
79
65
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
+
80
81
/// 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
82
83
/// deposit contract.
83
84
///
84
85
/// When parsing logs, the following assumptions are made
@@ -87,10 +88,36 @@ where
87
88
/// - The deposit contract enforces the length of the fields.
88
89
pub fn check_receipt_for_deposits (
89
90
receipt : & ReceiptEnvelope ,
90
- ) -> impl Iterator < Item = DepositRequest > + ' _ {
91
+ ) -> impl Iterator < Item = Bytes > + use < ' _ > {
91
92
check_logs_for_deposits ( receipt. logs ( ) )
92
93
}
93
94
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
+
94
121
// Some code above is reproduced from `reth`. It is reused here under the MIT
95
122
// license.
96
123
//
@@ -117,46 +144,44 @@ pub fn check_receipt_for_deposits(
117
144
// THE SOFTWARE.
118
145
119
146
#[ 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;
129
151
130
152
#[ 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
+ ] ;
154
178
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
+ ) ;
161
186
}
162
187
}
0 commit comments