Skip to content

Commit 7df9fc9

Browse files
Decode deposit log data without web3j (#8394)
* decode deposit log data without web3j Signed-off-by: Daniel Lehrner <[email protected]> * remove one unnecessary slice operation Signed-off-by: Daniel Lehrner <[email protected]> * added changelog entry Signed-off-by: Daniel Lehrner <[email protected]> --------- Signed-off-by: Daniel Lehrner <[email protected]> Co-authored-by: Stefan Pingel <[email protected]>
1 parent b41e282 commit 7df9fc9

File tree

4 files changed

+124
-76
lines changed

4 files changed

+124
-76
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ have support in besu-native can run mainnet ethereum configurations. Windows su
6464
- Upgrade to execution-spec-tests v4.1.0 including better EIP-2537 coverage for BLS [#8402](https://github.com/hyperledger/besu/pull/8402)
6565
- Add era1 format to blocks import subcommand [#7935](https://github.com/hyperledger/besu/issues/7935)
6666
- Add Hoodi as new named testnet [#8428](https://github.com/hyperledger/besu/issues/8428)
67+
- Decode deposit log data without Web3j [#8394](https://github.com/hyperledger/besu/issues/8394)
6768

6869
### Bug fixes
6970
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` so this method can be used with `--rpc-http-api-method-no-auth` [#8153](https://github.com/hyperledger/besu/issues/8153)

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/DepositContract.java

-61
This file was deleted.

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/DepositLogDecoder.java

+79-15
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,91 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.core.encoding;
1616

17-
import org.hyperledger.besu.ethereum.core.DepositContract;
1817
import org.hyperledger.besu.evm.log.Log;
1918

2019
import org.apache.tuweni.bytes.Bytes;
21-
import org.web3j.tx.Contract;
2220

21+
/**
22+
* Decodes a deposit log into its constituent parts.
23+
*
24+
* <p>Deposit logs are emitted by the Ethereum 2.0 deposit contract when a validator deposits funds
25+
* into the contract.
26+
*
27+
* <p>The data of the deposit log is organized in 32 bytes chunks. The data layout is:
28+
*
29+
* <ol>
30+
* <li>POSITION_PUB_KEY
31+
* <li>POSITION_WITHDRAWAL_CRED
32+
* <li>POSITION_AMOUNT
33+
* <li>POSITION_SIGNATURE
34+
* <li>POSITION_INDEX
35+
* <li>LENGTH_PUB_KEY
36+
* <li>PUB_KEY_DATA_1
37+
* <li>PUB_KEY_DATA_2
38+
* <li>LENGTH_WITHDRAWAL_CRED
39+
* <li>WITHDRAWAL_CRED_DATA
40+
* <li>LENGTH_AMOUNT
41+
* <li>AMOUNT_DATA
42+
* <li>LENGTH_SIGNATURE
43+
* <li>SIGNATURE_DATA_1
44+
* <li>SIGNATURE_DATA_2
45+
* <li>LENGTH_INDEX
46+
* <li>INDEX_DATA
47+
* </ol>
48+
*
49+
* We are only interested in the data fields and will skip over the position and length fields.
50+
*/
2351
public class DepositLogDecoder {
2452

53+
private static final int DEPOSIT_LOG_LENGTH = 576;
54+
private static final int LENGTH_FIELD_SIZE = 32;
55+
56+
private static final int PUB_KEY_LENGTH = 48;
57+
private static final int WITHDRAWAL_CRED_LENGTH = 32;
58+
private static final int AMOUNT_LENGTH = 8;
59+
private static final int SIGNATURE_LENGTH = 96;
60+
private static final int INDEX_LENGTH = 8;
61+
62+
// PublicKey is the first element.
63+
private static final int PUB_KEY_OFFSET = 0;
64+
// ABI encoding pads values to 32 bytes, so despite BLS public keys being length 48, the value
65+
// length here is 64. Then skip over the next length value.
66+
private static final int WITHDRAWAL_CRED_OFFSET =
67+
PUB_KEY_OFFSET + PUB_KEY_LENGTH + 16 + LENGTH_FIELD_SIZE;
68+
// WithdrawalCredentials is 32 bytes. Read that value then skip over next length.
69+
private static final int AMOUNT_OFFSET =
70+
WITHDRAWAL_CRED_OFFSET + WITHDRAWAL_CRED_LENGTH + LENGTH_FIELD_SIZE;
71+
// amount is only 8 bytes long but is stored in a 32 byte slot. The remaining 24 bytes need to be
72+
// added to the offset.
73+
private static final int SIGNATURE_OFFSET =
74+
AMOUNT_OFFSET + AMOUNT_LENGTH + 24 + LENGTH_FIELD_SIZE;
75+
// Signature is 96 bytes. Skip over it and the next length.
76+
private static final int INDEX_OFFSET = SIGNATURE_OFFSET + SIGNATURE_LENGTH + LENGTH_FIELD_SIZE;
77+
78+
// The ABI encodes the position of dynamic elements first. Since there are 5
79+
// elements, skip over the positional data. The first 32 bytes of dynamic
80+
// elements also encode their actual length. Skip over that value too.
81+
private static final int DATA_START_POSITION = 32 * 5 + LENGTH_FIELD_SIZE;
82+
2583
public static Bytes decodeFromLog(final Log log) {
26-
Contract.EventValuesWithLog eventValues = DepositContract.staticExtractDepositEventWithLog(log);
27-
final Bytes rawPublicKey =
28-
Bytes.wrap((byte[]) eventValues.getNonIndexedValues().get(0).getValue());
29-
final Bytes rawWithdrawalCredential =
30-
Bytes.wrap((byte[]) eventValues.getNonIndexedValues().get(1).getValue());
31-
final Bytes rawAmount =
32-
Bytes.wrap((byte[]) eventValues.getNonIndexedValues().get(2).getValue());
33-
final Bytes rawSignature =
34-
Bytes.wrap((byte[]) eventValues.getNonIndexedValues().get(3).getValue());
35-
final Bytes rawIndex = Bytes.wrap((byte[]) eventValues.getNonIndexedValues().get(4).getValue());
36-
37-
return Bytes.concatenate(
38-
rawPublicKey, rawWithdrawalCredential, rawAmount, rawSignature, rawIndex);
84+
final Bytes data = log.getData();
85+
86+
if (data.size() != DEPOSIT_LOG_LENGTH) {
87+
throw new IllegalArgumentException(
88+
"Invalid deposit log length. Must be "
89+
+ DEPOSIT_LOG_LENGTH
90+
+ " bytes, but is "
91+
+ data.size()
92+
+ " bytes");
93+
}
94+
95+
final Bytes pubKey = data.slice(DATA_START_POSITION + PUB_KEY_OFFSET, PUB_KEY_LENGTH);
96+
final Bytes withdrawalCred =
97+
data.slice(DATA_START_POSITION + WITHDRAWAL_CRED_OFFSET, WITHDRAWAL_CRED_LENGTH);
98+
final Bytes amount = data.slice(DATA_START_POSITION + AMOUNT_OFFSET, AMOUNT_LENGTH);
99+
final Bytes signature = data.slice(DATA_START_POSITION + SIGNATURE_OFFSET, SIGNATURE_LENGTH);
100+
final Bytes index = data.slice(DATA_START_POSITION + INDEX_OFFSET, INDEX_LENGTH);
101+
102+
return Bytes.concatenate(pubKey, withdrawalCred, amount, signature, index);
39103
}
40104
}

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/DepositLogDecoderTest.java

+44
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.hyperledger.besu.ethereum.core.encoding;
1616

1717
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1819

1920
import org.hyperledger.besu.datatypes.Address;
2021
import org.hyperledger.besu.evm.log.Log;
@@ -68,4 +69,47 @@ void shouldDecodeSepoliaDepositFromLog() {
6869

6970
assertThat(requestData).isEqualTo(expectedDepositRequestData);
7071
}
72+
73+
@Test
74+
void shouldDecodeDepositFromLogWithoutPositionAndLength() {
75+
final Address logger = Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");
76+
final List<LogTopic> topics =
77+
List.of(
78+
LogTopic.fromHexString(
79+
"0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"));
80+
final Bytes data =
81+
Bytes.fromHexString(
82+
"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000c3d5d53aa01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
83+
84+
final Log log = new Log(logger, data, topics);
85+
final Bytes requestData = DepositLogDecoder.decodeFromLog(log);
86+
87+
final Bytes expectedDepositRequestData =
88+
Bytes.fromHexString(
89+
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000c3d5d53aa010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000");
90+
91+
assertThat(requestData).isEqualTo(expectedDepositRequestData);
92+
}
93+
94+
@Test
95+
void shouldThrowExceptionIfDataSizeIsNotCorrectSize() {
96+
final Address logger = Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");
97+
final List<LogTopic> topics =
98+
List.of(
99+
LogTopic.fromHexString(
100+
"0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"));
101+
final Bytes data =
102+
Bytes.fromHexString(
103+
"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000c3d5d53aa01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
104+
105+
final Log logDataTooShort = new Log(logger, data.slice(1), topics);
106+
assertThatThrownBy(() -> DepositLogDecoder.decodeFromLog(logDataTooShort))
107+
.isInstanceOf(RuntimeException.class)
108+
.hasMessageContaining("Invalid deposit log length. Must be 576 bytes, but is 575 bytes");
109+
110+
final Log logDataTooLong = new Log(logger, Bytes.concatenate(data, data), topics);
111+
assertThatThrownBy(() -> DepositLogDecoder.decodeFromLog(logDataTooLong))
112+
.isInstanceOf(RuntimeException.class)
113+
.hasMessageContaining("Invalid deposit log length. Must be 576 bytes, but is 1152 bytes");
114+
}
71115
}

0 commit comments

Comments
 (0)