-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update EIP-6110: Spec parse_deposit_data
#9460
base: master
Are you sure you want to change the base?
Update EIP-6110: Spec parse_deposit_data
#9460
Conversation
File
|
parse_deposit_data
parse_deposit_data
parse_deposit_data
parse_deposit_data
EIPS/eip-6110.md
Outdated
Parses deposit data from DepositContract.DepositEvent data. | ||
""" | ||
|
||
# TODO: verify byte length of deposit_event_data? Should be 576. Should this be part of the spec? (Yes?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the failure mode for this check? Maybe we should do it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added this also! 4f912ac 😄 👍
EIPS/eip-6110.md
Outdated
# which represents the dynamic size of the encoded bytes event data | ||
# The size itself is encoded in a 32-byte, so skip over this as well | ||
# to arrive at the first actual data offset to be read (pubkey) | ||
currentOffset = 32 * 5 + 32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have PUBKEY_OFFSET, WITHDRAWAL_CREDENTIALS_OFFSET, etc instead of these computations? and then:
pubkey = deposit_event_data[PUBKEY_OFFSET:PUBKEY_OFFSET + 48]
withdrawal_credentials = deposit_event_data[WITHDRAWAL_CREDENTIALS_OFFSET: WITHDRAWAL_CREDENTIALS_OFFSET + 32]
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have added! Please verify if my offsets and sizes are correct 😄 👍
EIPS/eip-6110.md
Outdated
pubkey = deposit_event_data[currentOffset:currentOffset + 48] | ||
# The ABI data is encoded in 32-byte chunks, so the 64 bytes are reserved for the data | ||
# The next 32 bytes are reserved for the size of the next item (withdrawals) so skip over this as well | ||
currentOffset += 48 + 16 + 32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using an ABI isn't particularly important; what is important is the data fields are length prefixed byte arrays. The fixed offsets are a quirk of the current contracts.
Properly parsing (using length prefix) means it would still work if a contract on a different chain output the same data but trimmed leading zeros; however using fixed offsets would break (if the argument is not to use ABI because "it might change"; changing would first break the offsets) (edited)
[3:06 PM]
The data is literally saying the lengths of the data elements; seem irresponsible to ignore it and lay a presumed offset format on it? If it was C that's how you'd get buffer over/under runs.
What if the deposit contract was written in viper or some other language that decided to encode packed? Is valid by the event log specification, would fail to be parsed.
It's also both literally encoding ABI and also ignoring it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good point regarding the ABI, I should not have added this as comment at all. Have removed :)
The goal this PR wants to solve is that parse_deposit_data
is currently not specced. This PR should spec it. We have two approaches (and we should decide async or via ACD-E):
- Hardcoded offsets/sizes (this PR)
- Use ABI decoding
Note that the current EIP spec has this in it:
#### Deposit request
The structure denoting the new deposit request consists of the following fields:
1. `pubkey: Bytes48`
2. `withdrawal_credentials: Bytes32`
3. `amount: uint64`
4. `signature: Bytes96`
5. `index: uint64`
This is how the EIP-7685 request_data
is being encoded. (This would also raise the question for me: why does the Deposit event not encode this directly in the event signature? But this is a side-question, we can't change this anyways, and neither can we change that on mainnet these bytes
are always at the same offset and same size for all event data of the deposit event)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the OP of this PR also regarding this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this EIP is encoding that anything decoding using the specified ABI should fail if junky data was passed but the offset would "pass" which seems sloppy? Since the data would be wrong for anyone parsing the event upstream (and would the data even be correct in that circumstance?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure if I got this comment right but I think this applies here as well: #9460 (comment)
85f70b7
to
4f912ac
Compare
@@ -97,7 +112,7 @@ def get_deposit_request_data(receipts) | |||
deposit_requests = [] | |||
for receipt in receipts: | |||
for log in receipt.logs: | |||
is_deposit_event = (len(log.topics) > 0 and log.topics[0] == DEPOSIT_EVENT_SIGNATURE_HASH) | |||
is_deposit_event = (len(log.topics) > 0 and log.topics[0] == DEPOSIT_EVENT_SIGNATURE_HASH and len(log.data) === 576) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when its the correct contract, correct signature but log.data
is not exactly 576 bytes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It skips the log. Note that the current spec tells us how to encode the request (no changes to it in this PR):
1. `pubkey: Bytes48`
2. `withdrawal_credentials: Bytes32`
3. `amount: uint64`
4. `signature: Bytes96`
5. `index: uint64`
Note that if these value lengths are ABI encoded you would still end up with the 576 length event, because:
- 5 offset
Bytes32
(160 bytes total) - 5 size
Bytes32
(160 bytes total) pubkey
: 48 bytes, but will get rounded up to 64 byteswithdrawal_credentials
: 32 bytesamount
: will get rounded up to 32 bytessignature
: 96 bytesindex
: will get rounded up to 32 bytes
So, in total (5+5) * 32 + 64 + 32 + 32 + 96 + 32 = 576 bytes. If we would take, for instance, the pubkey
and would now encode the actual bytes data as more than 64 bytes, then we would thus get extra 32 bytes extra in the log data, however, this cannot be encoded in the request as Bytes48
anymore (unless we want to ignore any data).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the current spec tells us how to encode the request
The spec does not specify lengths and offsets, it specifies that the input values are variable https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/deposit-contract.md#staking-deposit-contract
The deposit contract has a public deposit function to make deposits. It takes as arguments
bytes
calldata pubkey,bytes
calldata withdrawal_credentials,bytes
calldata signature,bytes32
deposit_data_root
and also that the logs are variable
Every deposit emits a
DepositEvent
log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain.
The L1 smart contract also specify that the types are variable https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa#code
Both in the input values
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) override external payable {
and in the log
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
Where is it specified that they are fixed sized buffers of Bytes48
, Bytes32
, uint64
, Bytes96
, uint64
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I should have been more clear, I was not referring to the solidity spec, but to the encoding spec of the actual request (so the request which gets in the block header) of this EIP. See: https://eips.ethereum.org/EIPS/eip-6110#execution-layer and then to the "Deposit request" part:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok but those are post parsing the log data? i.e. parse_deposit_data
and is about ho to convert to fixed sizes (for output)
Before that where is the len(log.data) === 576
guarantee in any prior spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There technically is none, but, the deposit event hash is now hardcoded in clients to filter out non-DepositEvent
logs (so the Sepolia bug)
So this is given:
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
Note: keccak256("DepositEvent(bytes,bytes,bytes,bytes,bytes)="0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"
By ABI encoding as mentioned here: #9460 (comment) if we keep the way how requests are encoded, then if more data would get added to one of these parameters, it would not fit in the requests data (and we thus need to edit how the request itself gets encoded in the header)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side-note, this extra check is taken from Geth's code: https://github.com/ethereum/go-ethereum/blob/cd78b65cdaa9e00866962e38685c4f93ac9e8444/core/types/deposit.go#L28
Note that on mainnet the deposit event length should be 576 bytes always (implied by the solidity implementation, did not check bytecode)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keccak256("DepositEvent(bytes,bytes,bytes,bytes,bytes)="0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"
Yes and bytes
are variable length values; not fixed length. So the ABI does not have fixed position offsets and lengths.
The L1 code does have require
statements specifying the length of the params; however we know from 3 other chains (Sepolia, Chaido and Gnosis) that the internal L1 contract implementation is not a guarenetted spec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. if the contract accepted 1ETH deposits 0xDE0B6B3A7640000
and didn't force it to a Uint256
size in the log output (as L1 contract does) or maybe output in wei
rather than gwei
; but output packed it would currently be valid and parsable.
However the log would also fail the len(log.data) === 576
check, which first appears in this PR and nothing else prior; and the offset for SIGNATURE_OFFSET
and INDEX_OFFSET
would be incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that on mainnet the deposit event length should be 576 bytes
What the mainnet contract does is no guarantee of what ABI compliant contracts that follow the spec do; or Sepolia wouldn't have broken.
What is in all prior specs is that these are all variable length fields; that the size is 576 is an implementation detail of the mainnet contract, however it is not guaranteed in any spec; which in fact specify they are variable length fields.
Mainnet output in gwei
for example is a particularly of mainnet asset and the beacon chain requirements, but only L1 uses mainnet CLs/beacon chain.
The
parse_deposit_data
is not specced. This PR adds this to the spec. There are two ways to decode and get the relevant data: either hardcoded offsets/sizes or use an ABI decoder. This PR uses the hardcoded approach and has no notion of "ABI" since this would enshrine the ABI in the protocol.Note that on mainnet the event always returns a fixed-length log and the relevant data is always at the same position and size.
Also note that the to-be-encoded data is not of dynamic
bytes
size but actually static:TODO:
0x7f02c3e3c98b133055b8b348b2ac625669ed295d
but has no source verified on etherscan). EDIT: It's here: https://github.com/protolambda/testnet-dep-contract/blob/master/deposit_contract.sol