Skip to content

Commit

Permalink
Update EIP-6404: Rename from signature fields
Browse files Browse the repository at this point in the history
Merged by EIP-Bot.
  • Loading branch information
etan-status authored Sep 17, 2024
1 parent f3deac8 commit 5a7d88e
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 105 deletions.
55 changes: 28 additions & 27 deletions EIPS/eip-6404.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,42 +59,43 @@ Definitions from existing specifications that are used throughout this document

### `ExecutionSignature` container

Signatures use their native, opaque representation, and are extended with an on-chain commitment to the `from` address.
Signatures use their native, opaque representation, and are extended with an on-chain commitment to the signing address.

| Name | Value | Description |
| - | - | - |
| `ECDSA_SIGNATURE_SIZE` | `32 + 32 + 1` (= 65) | Byte length of an ECDSA (secp256k1) signature |
| `SECP256K1_SIGNATURE_SIZE` | `32 + 32 + 1` (= 65) | Byte length of a secp256k1 ECDSA signature |
| `MAX_EXECUTION_SIGNATURE_FIELDS` | `uint64(2**4)` (= 16) | Maximum number of fields to which `ExecutionSignature` can ever grow in the future |

```python
class ExecutionSignature(StableContainer[MAX_EXECUTION_SIGNATURE_FIELDS]):
from_: Optional[ExecutionAddress]
ecdsa_signature: Optional[ByteVector[ECDSA_SIGNATURE_SIZE]]
address: Optional[ExecutionAddress]
secp256k1_signature: Optional[ByteVector[SECP256K1_SIGNATURE_SIZE]]

class EcdsaExecutionSignature(Profile[ExecutionSignature]):
from_: ExecutionAddress
ecdsa_signature: ByteVector[ECDSA_SIGNATURE_SIZE]
class Secp256k1ExecutionSignature(Profile[ExecutionSignature]):
address: ExecutionAddress
secp256k1_signature: ByteVector[SECP256K1_SIGNATURE_SIZE]

def ecdsa_pack_signature(y_parity: bool,
r: uint256,
s: uint256) -> ByteVector[ECDSA_SIGNATURE_SIZE]:
def secp256k1_pack_signature(y_parity: bool,
r: uint256,
s: uint256) -> ByteVector[SECP256K1_SIGNATURE_SIZE]:
return r.to_bytes(32, 'big') + s.to_bytes(32, 'big') + bytes([0x01 if y_parity else 0x00])

def ecdsa_unpack_signature(signature: ByteVector[ECDSA_SIGNATURE_SIZE]) -> tuple[bool, uint256, uint256]:
def secp256k1_unpack_signature(signature: ByteVector[SECP256K1_SIGNATURE_SIZE]
) -> tuple[bool, uint256, uint256]:
y_parity = signature[64] != 0
r = uint256.from_bytes(signature[0:32], 'big')
s = uint256.from_bytes(signature[32:64], 'big')
return (y_parity, r, s)

def ecdsa_validate_signature(signature: ByteVector[ECDSA_SIGNATURE_SIZE]):
def secp256k1_validate_signature(signature: ByteVector[SECP256K1_SIGNATURE_SIZE]):
SECP256K1N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
assert signature[64] in (0, 1)
_, r, s = ecdsa_unpack_signature(signature)
_, r, s = secp256k1_unpack_signature(signature)
assert 0 < r < SECP256K1N
assert 0 < s <= SECP256K1N // 2

def ecdsa_recover_from_address(signature: ByteVector[ECDSA_SIGNATURE_SIZE],
sig_hash: Hash32) -> ExecutionAddress:
def secp256k1_recover_from_address(signature: ByteVector[SECP256K1_SIGNATURE_SIZE],
sig_hash: Hash32) -> ExecutionAddress:
ecdsa = ECDSA()
recover_sig = ecdsa.ecdsa_recoverable_deserialize(signature[0:64], signature[64])
public_key = PublicKey(ecdsa.ecdsa_recover(sig_hash, recover_sig, raw=True))
Expand Down Expand Up @@ -156,7 +157,7 @@ class TransactionPayload(StableContainer[MAX_TRANSACTION_PAYLOAD_FIELDS]):

class Transaction(Container):
payload: TransactionPayload
signature: ExecutionSignature
from_: ExecutionSignature
```

![Transaction merkleization](../assets/eip-6404/transaction.png)
Expand Down Expand Up @@ -185,7 +186,7 @@ class RlpLegacyTransactionPayload(Profile[TransactionPayload]):

class RlpLegacyTransaction(Container):
payload: RlpLegacyTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature

class RlpAccessListTransactionPayload(Profile[TransactionPayload]):
type_: TransactionType
Expand All @@ -200,7 +201,7 @@ class RlpAccessListTransactionPayload(Profile[TransactionPayload]):

class RlpAccessListTransaction(Container):
payload: RlpAccessListTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature

class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]):
type_: TransactionType
Expand All @@ -216,7 +217,7 @@ class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]):

class RlpFeeMarketTransaction(Container):
payload: RlpFeeMarketTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature

class RlpBlobTransactionPayload(Profile[TransactionPayload]):
type_: TransactionType
Expand All @@ -233,7 +234,7 @@ class RlpBlobTransactionPayload(Profile[TransactionPayload]):

class RlpBlobTransaction(Container):
payload: RlpBlobTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature
```

A helper is provided to identify the the [EIP-7495](./eip-7495.md) `Profile` of a normalized `Transaction`. The type system ensures that fields required by a `Profile` are present and that excluded fields are absent.
Expand Down Expand Up @@ -262,13 +263,13 @@ def identify_transaction_profile(tx: Transaction) -> Type[Profile]:

### Transaction validation

As part of `Transaction` validation, the `from` address MUST be checked for consistency with the `ecdsa_signature`. See [EIP assets](../assets/eip-6404/tx_hashes.py) for a definition of `compute_sig_hash` that takes the various transaction types into account.
As part of `Transaction` validation, the `from` address MUST be checked for consistency with the `secp256k1_signature`. See [EIP assets](../assets/eip-6404/tx_hashes.py) for a definition of `compute_sig_hash` that takes the various transaction types into account.

```python
def validate_tx_from_address(tx):
ecdsa_validate_signature(tx.signature.ecdsa_signature)
assert tx.signature.from_ == ecdsa_recover_from_address(
tx.signature.ecdsa_signature,
secp256k1_validate_signature(tx.from_.secp256k1_signature)
assert tx.from_.address == secp256k1_recover_from_address(
tx.from_.secp256k1_signature,
compute_sig_hash(tx),
)
```
Expand All @@ -293,7 +294,7 @@ In the engine API, the structure of the `transactions` field in `ExecutionPayloa
`TransactionV1` is defined to map onto the SSZ `Transaction` type, as follows:

- `payload`: `TransactionPayloadV1` - An `OBJECT` containing the fields of a `TransactionPayloadV1` structure
- `signature`: `ExecutionSignatureV1` - An `OBJECT` containing the fields of a `ExecutionSignatureV1` structure
- `from`: `ExecutionSignatureV1` - An `OBJECT` containing the fields of a `ExecutionSignatureV1` structure

`TransactionPayloadV1` is defined to map onto the SSZ `TransactionPayload` `StableContainer`, as follows:

Expand Down Expand Up @@ -321,8 +322,8 @@ In the engine API, the structure of the `transactions` field in `ExecutionPayloa

`ExecutionSignatureV1` is defined to map onto the SSZ `ExecutionSignature` `StableContainer`, as follows:

- `from`: `DATA|null`, 20 Bytes or `null`
- `ecdsaSignature`: `DATA|null`, 65 Bytes or `null`
- `address`: `DATA|null`, 20 Bytes or `null`
- `secp256k1Signature`: `DATA|null`, 65 Bytes or `null`

### Consensus `ExecutionPayload` changes

Expand Down
4 changes: 2 additions & 2 deletions EIPS/eip-6493.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class BasicTransactionPayload(Profile[TransactionPayload]):

class BasicTransaction(Container):
payload: BasicTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature

class BlobTransactionPayload(Profile[TransactionPayload]):
chain_id: ChainId
Expand All @@ -94,7 +94,7 @@ class BlobTransactionPayload(Profile[TransactionPayload]):

class BlobTransaction(Container):
payload: BlobTransactionPayload
signature: EcdsaExecutionSignature
from_: Secp256k1ExecutionSignature
```

The `identify_transaction_profile` helper from [EIP-6404](./eip-6404.md) is updated to support native SSZ transactions.
Expand Down
44 changes: 24 additions & 20 deletions assets/eip-6404/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ def upgrade_rlp_transaction_to_ssz(pre_bytes: bytes):
if type_ == 0x03: # EIP-4844
pre = decode(pre_bytes[1:], BlobRlpTransaction)
assert pre.y_parity in (0, 1)
ecdsa_signature = ecdsa_pack_signature(
secp256k1_signature = secp256k1_pack_signature(
pre.y_parity != 0,
pre.r,
pre.s,
)
from_ = ecdsa_recover_from_address(ecdsa_signature, compute_blob_sig_hash(pre))
from_address = secp256k1_recover_from_address(
secp256k1_signature, compute_blob_sig_hash(pre))

return RlpBlobTransaction(
payload=RlpBlobTransactionPayload(
Expand All @@ -38,21 +39,22 @@ def upgrade_rlp_transaction_to_ssz(pre_bytes: bytes):
),
blob_versioned_hashes=pre.blob_versioned_hashes,
),
signature=EcdsaExecutionSignature(
from_=from_,
ecdsa_signature=ecdsa_signature,
from_=Secp256k1ExecutionSignature(
address=from_address,
secp256k1_signature=secp256k1_signature,
),
)

if type_ == 0x02: # EIP-1559
pre = decode(pre_bytes[1:], FeeMarketRlpTransaction)
assert pre.y_parity in (0, 1)
ecdsa_signature = ecdsa_pack_signature(
secp256k1_signature = secp256k1_pack_signature(
pre.y_parity != 0,
pre.r,
pre.s,
)
from_ = ecdsa_recover_from_address(ecdsa_signature, compute_fee_market_sig_hash(pre))
from_address = secp256k1_recover_from_address(
secp256k1_signature, compute_fee_market_sig_hash(pre))

return RlpFeeMarketTransaction(
payload=RlpFeeMarketTransactionPayload(
Expand All @@ -74,21 +76,22 @@ def upgrade_rlp_transaction_to_ssz(pre_bytes: bytes):
regular=pre.max_priority_fee_per_gas,
),
),
signature=EcdsaExecutionSignature(
from_=from_,
ecdsa_signature=ecdsa_signature,
from_=Secp256k1ExecutionSignature(
address=from_address,
secp256k1_signature=secp256k1_signature,
),
)

if type_ == 0x01: # EIP-2930
pre = decode(pre_bytes[1:], AccessListRlpTransaction)
assert pre.y_parity in (0, 1)
ecdsa_signature = ecdsa_pack_signature(
secp256k1_signature = secp256k1_pack_signature(
pre.y_parity != 0,
pre.r,
pre.s
)
from_ = ecdsa_recover_from_address(ecdsa_signature, compute_access_list_sig_hash(pre))
from_address = secp256k1_recover_from_address(
secp256k1_signature, compute_access_list_sig_hash(pre))

return RlpAccessListTransaction(
payload=RlpAccessListTransactionPayload(
Expand All @@ -107,20 +110,21 @@ def upgrade_rlp_transaction_to_ssz(pre_bytes: bytes):
storage_keys=access_tuple[1]
) for access_tuple in pre.access_list],
),
signature=EcdsaExecutionSignature(
from_=from_,
ecdsa_signature=ecdsa_signature,
from_=Secp256k1ExecutionSignature(
address=from_address,
secp256k1_signature=secp256k1_signature,
),
)

if 0xc0 <= type_ <= 0xfe: # Legacy
pre = decode(pre_bytes, LegacyRlpTransaction)
ecdsa_signature = ecdsa_pack_signature(
secp256k1_signature = secp256k1_pack_signature(
(pre.v & 0x1) == 0,
pre.r,
pre.s,
)
from_ = ecdsa_recover_from_address(ecdsa_signature, compute_legacy_sig_hash(pre))
from_address = secp256k1_recover_from_address(
secp256k1_signature, compute_legacy_sig_hash(pre))

if (pre.v not in (27, 28)): # EIP-155
chain_id = ((pre.v - 35) >> 1)
Expand All @@ -140,9 +144,9 @@ def upgrade_rlp_transaction_to_ssz(pre_bytes: bytes):
value=pre.value,
input_=pre.data,
),
signature=EcdsaExecutionSignature(
from_=from_,
ecdsa_signature=ecdsa_signature,
from_=Secp256k1ExecutionSignature(
address=from_address,
secp256k1_signature=secp256k1_signature,
),
)

Expand Down
Loading

0 comments on commit 5a7d88e

Please sign in to comment.