diff --git a/EIPS/eip-6404.md b/EIPS/eip-6404.md index fba1295285a3d..5abd98adeda00 100644 --- a/EIPS/eip-6404.md +++ b/EIPS/eip-6404.md @@ -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)) @@ -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) @@ -185,7 +186,7 @@ class RlpLegacyTransactionPayload(Profile[TransactionPayload]): class RlpLegacyTransaction(Container): payload: RlpLegacyTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpAccessListTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -200,7 +201,7 @@ class RlpAccessListTransactionPayload(Profile[TransactionPayload]): class RlpAccessListTransaction(Container): payload: RlpAccessListTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -216,7 +217,7 @@ class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]): class RlpFeeMarketTransaction(Container): payload: RlpFeeMarketTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpBlobTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -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. @@ -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), ) ``` @@ -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: @@ -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 diff --git a/EIPS/eip-6493.md b/EIPS/eip-6493.md index ff46446f66f12..e20a887143ac2 100644 --- a/EIPS/eip-6493.md +++ b/EIPS/eip-6493.md @@ -78,7 +78,7 @@ class BasicTransactionPayload(Profile[TransactionPayload]): class BasicTransaction(Container): payload: BasicTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class BlobTransactionPayload(Profile[TransactionPayload]): chain_id: ChainId @@ -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. diff --git a/assets/eip-6404/convert.py b/assets/eip-6404/convert.py index 556cd026a742f..cf0e679e92ab8 100644 --- a/assets/eip-6404/convert.py +++ b/assets/eip-6404/convert.py @@ -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( @@ -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( @@ -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( @@ -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) @@ -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, ), ) diff --git a/assets/eip-6404/ssz_types.py b/assets/eip-6404/ssz_types.py index db6abfe0a82dd..15fb8f021f752 100644 --- a/assets/eip-6404/ssz_types.py +++ b/assets/eip-6404/ssz_types.py @@ -15,38 +15,39 @@ class ExecutionAddress(ByteVector[20]): class VersionedHash(Bytes32): pass -ECDSA_SIGNATURE_SIZE = 32 + 32 + 1 +SECP256K1_SIGNATURE_SIZE = 32 + 32 + 1 MAX_EXECUTION_SIGNATURE_FIELDS = uint64(2**4) 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 len(signature) == 65 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)) @@ -113,37 +114,6 @@ class BlobFeesPerGas(Profile[FeesPerGas]): regular: FeePerGas blob: FeePerGas -class BasicTransactionPayload(Profile[TransactionPayload]): - chain_id: ChainId - nonce: uint64 - max_fees_per_gas: BasicFeesPerGas - gas: uint64 - to: Optional[ExecutionAddress] - value: uint256 - input_: ByteList[MAX_CALLDATA_SIZE] - access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] - max_priority_fees_per_gas: BasicFeesPerGas - -class BasicTransaction(Container): - payload: BasicTransactionPayload - signature: EcdsaExecutionSignature - -class BlobTransactionPayload(Profile[TransactionPayload]): - chain_id: ChainId - nonce: uint64 - max_fees_per_gas: BlobFeesPerGas - gas: uint64 - to: ExecutionAddress - value: uint256 - input_: ByteList[MAX_CALLDATA_SIZE] - access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] - max_priority_fees_per_gas: BlobFeesPerGas - blob_versioned_hashes: List[VersionedHash, MAX_BLOB_COMMITMENTS_PER_BLOCK] - -class BlobTransaction(Container): - payload: BlobTransactionPayload - signature: EcdsaExecutionSignature - class RlpLegacyTransactionPayload(Profile[TransactionPayload]): type_: TransactionType chain_id: Optional[ChainId] @@ -156,7 +126,7 @@ class RlpLegacyTransactionPayload(Profile[TransactionPayload]): class RlpLegacyTransaction(Container): payload: RlpLegacyTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpAccessListTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -171,7 +141,7 @@ class RlpAccessListTransactionPayload(Profile[TransactionPayload]): class RlpAccessListTransaction(Container): payload: RlpAccessListTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -187,7 +157,7 @@ class RlpFeeMarketTransactionPayload(Profile[TransactionPayload]): class RlpFeeMarketTransaction(Container): payload: RlpFeeMarketTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature class RlpBlobTransactionPayload(Profile[TransactionPayload]): type_: TransactionType @@ -204,7 +174,7 @@ class RlpBlobTransactionPayload(Profile[TransactionPayload]): class RlpBlobTransaction(Container): payload: RlpBlobTransactionPayload - signature: EcdsaExecutionSignature + from_: Secp256k1ExecutionSignature LEGACY_TX_TYPE = TransactionType(0x00) ACCESS_LIST_TX_TYPE = TransactionType(0x01) @@ -229,8 +199,8 @@ def identify_transaction_profile(tx: Transaction) -> Type[Profile]: from tx_hashes import compute_sig_hash 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), ) diff --git a/assets/eip-6404/transaction.png b/assets/eip-6404/transaction.png index f542427c92cb2..ba5b9c70dbb1d 100644 Binary files a/assets/eip-6404/transaction.png and b/assets/eip-6404/transaction.png differ diff --git a/assets/eip-6404/tx_hashes.py b/assets/eip-6404/tx_hashes.py index 4a567d1e83791..5c5301fe4d996 100644 --- a/assets/eip-6404/tx_hashes.py +++ b/assets/eip-6404/tx_hashes.py @@ -3,7 +3,7 @@ def recover_legacy_rlp_transaction( tx: RlpLegacyTransaction) -> LegacyRlpTransaction: - y_parity, r, s = ecdsa_unpack_signature(tx.signature.ecdsa_signature) + y_parity, r, s = secp256k1_unpack_signature(tx.from_.secp256k1_signature) if tx.payload.chain_id is not None: # EIP-155 v = uint64(1 if y_parity else 0) + 35 + tx.payload.chain_id * 2 else: @@ -23,7 +23,7 @@ def recover_legacy_rlp_transaction( def recover_access_list_rlp_transaction( tx: RlpAccessListTransaction) -> AccessListRlpTransaction: - y_parity, r, s = ecdsa_unpack_signature(tx.signature.ecdsa_signature) + y_parity, r, s = secp256k1_unpack_signature(tx.from_.secp256k1_signature) return AccessListRlpTransaction( chain_id=tx.payload.chain_id, @@ -44,7 +44,7 @@ def recover_access_list_rlp_transaction( def recover_fee_market_rlp_transaction( tx: RlpFeeMarketTransaction) -> FeeMarketRlpTransaction: - y_parity, r, s = ecdsa_unpack_signature(tx.signature.ecdsa_signature) + y_parity, r, s = secp256k1_unpack_signature(tx.from_.secp256k1_signature) return FeeMarketRlpTransaction( chain_id=tx.payload.chain_id, @@ -66,7 +66,7 @@ def recover_fee_market_rlp_transaction( def recover_blob_rlp_transaction(tx: RlpBlobTransaction) -> BlobRlpTransaction: assert tx.payload.max_priority_fees_per_gas.blob == FeePerGas(0) - y_parity, r, s = ecdsa_unpack_signature(tx.signature.ecdsa_signature) + y_parity, r, s = secp256k1_unpack_signature(tx.from_.secp256k1_signature) return BlobRlpTransaction( chain_id=tx.payload.chain_id,