418 lines
17 KiB
Python
418 lines
17 KiB
Python
|
from remerkleable.settings import zero_hashes
|
||
|
from secp256k1 import ECDSA, PublicKey
|
||
|
from create_proofs import *
|
||
|
|
||
|
def verify_transaction_proof(
|
||
|
proof: TransactionProof,
|
||
|
cfg: ExecutionConfig,
|
||
|
transactions_root: Root,
|
||
|
expected_tx_hash: Root,
|
||
|
):
|
||
|
assert proof.tx_root == expected_tx_hash
|
||
|
|
||
|
tx_gindex = GeneralizedIndex(MAX_TRANSACTIONS_PER_PAYLOAD * 2 + uint64(proof.tx_index))
|
||
|
assert calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_root.hash_tree_root(),
|
||
|
proof.tx_selector.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_branch,
|
||
|
[
|
||
|
tx_gindex * 2 + 0,
|
||
|
tx_gindex * 2 + 1,
|
||
|
],
|
||
|
get_helper_indices([tx_gindex]),
|
||
|
) == transactions_root
|
||
|
|
||
|
def verify_amount_proof(
|
||
|
proof: AmountProof,
|
||
|
cfg: ExecutionConfig,
|
||
|
transactions_root: Root,
|
||
|
expected_tx_to: ExecutionAddress,
|
||
|
expected_tx_value_min: uint256,
|
||
|
):
|
||
|
match proof.tx_proof.selector():
|
||
|
case 3:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP4844_AMOUNT_PROOF_INDICES,
|
||
|
EIP4844_AMOUNT_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
case 2:
|
||
|
assert proof.tx_proof.value().destination.selector() == 1
|
||
|
assert proof.tx_proof.value().destination.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().amount >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().gas_limit.hash_tree_root(),
|
||
|
proof.tx_proof.value().destination.hash_tree_root(),
|
||
|
proof.tx_proof.value().amount.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP1559_AMOUNT_PROOF_INDICES,
|
||
|
EIP1559_AMOUNT_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
case 1:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP2930_AMOUNT_PROOF_INDICES,
|
||
|
EIP2930_AMOUNT_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
case 0:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().startgas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
zero_hashes[1],
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
LEGACY_AMOUNT_PROOF_INDICES,
|
||
|
LEGACY_AMOUNT_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
|
||
|
tx_gindex = GeneralizedIndex(MAX_TRANSACTIONS_PER_PAYLOAD * 2 + uint64(proof.tx_index))
|
||
|
assert calculate_multi_merkle_root(
|
||
|
[
|
||
|
sig_root,
|
||
|
proof.tx_proof.value().signature_root.hash_tree_root(),
|
||
|
uint8(proof.tx_proof.selector()).hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_branch,
|
||
|
[
|
||
|
tx_gindex * 4 + 0,
|
||
|
tx_gindex * 4 + 1,
|
||
|
tx_gindex * 2 + 1,
|
||
|
],
|
||
|
get_helper_indices([tx_gindex]),
|
||
|
) == transactions_root
|
||
|
|
||
|
def verify_sender_proof(
|
||
|
proof: SenderProof,
|
||
|
cfg: ExecutionConfig,
|
||
|
transactions_root: Root,
|
||
|
expected_tx_to: ExecutionAddress,
|
||
|
expected_tx_value_min: uint256,
|
||
|
) -> ExecutionAddress:
|
||
|
match proof.tx_proof.selector():
|
||
|
case 3:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP4844_SENDER_PROOF_INDICES,
|
||
|
EIP4844_SENDER_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
case 2:
|
||
|
assert proof.tx_proof.value().destination.selector() == 1
|
||
|
assert proof.tx_proof.value().destination.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().amount >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().gas_limit.hash_tree_root(),
|
||
|
proof.tx_proof.value().destination.hash_tree_root(),
|
||
|
proof.tx_proof.value().amount.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP1559_SENDER_PROOF_INDICES,
|
||
|
EIP1559_SENDER_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
case 1:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP2930_SENDER_PROOF_INDICES,
|
||
|
EIP2930_SENDER_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
case 0:
|
||
|
assert proof.tx_proof.value().to.selector() == 1
|
||
|
assert proof.tx_proof.value().to.value() == expected_tx_to
|
||
|
assert proof.tx_proof.value().value >= expected_tx_value_min
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().startgas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
zero_hashes[1],
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
LEGACY_SENDER_PROOF_INDICES,
|
||
|
LEGACY_SENDER_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
signature = ECDSASignature(
|
||
|
y_parity=(proof.tx_proof.value().signature.v & 0x1) == 0,
|
||
|
r = proof.tx_proof.value().signature.r,
|
||
|
s = proof.tx_proof.value().signature.s,
|
||
|
)
|
||
|
|
||
|
ecdsa = ECDSA()
|
||
|
recover_sig = ecdsa.ecdsa_recoverable_deserialize(
|
||
|
signature.r.to_bytes(32, 'big') + signature.s.to_bytes(32, 'big'),
|
||
|
0x01 if signature.y_parity else 0,
|
||
|
)
|
||
|
public_key = PublicKey(ecdsa.ecdsa_recover(sig_root, recover_sig, raw=True))
|
||
|
uncompressed = public_key.serialize(compressed=False)
|
||
|
tx_from = ExecutionAddress(keccak(uncompressed)[12:32])
|
||
|
|
||
|
tx_gindex = GeneralizedIndex(MAX_TRANSACTIONS_PER_PAYLOAD * 2 + uint64(proof.tx_index))
|
||
|
assert calculate_multi_merkle_root(
|
||
|
[
|
||
|
sig_root,
|
||
|
proof.tx_proof.value().signature.hash_tree_root(),
|
||
|
uint8(proof.tx_proof.selector()).hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_branch,
|
||
|
[
|
||
|
tx_gindex * 4 + 0,
|
||
|
tx_gindex * 4 + 1,
|
||
|
tx_gindex * 2 + 1,
|
||
|
],
|
||
|
get_helper_indices([tx_gindex]),
|
||
|
) == transactions_root
|
||
|
|
||
|
return tx_from
|
||
|
|
||
|
def verify_info_proof(
|
||
|
proof: InfoProof,
|
||
|
cfg: ExecutionConfig,
|
||
|
transactions_root: Root,
|
||
|
) -> TransactionInfo:
|
||
|
match proof.tx_proof.selector():
|
||
|
case 3:
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().nonce.hash_tree_root(),
|
||
|
proof.tx_proof.value().max_priority_fee_per_gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().max_fee_per_gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
cfg.chain_id.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP4844_INFO_PROOF_INDICES,
|
||
|
EIP4844_INFO_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
print(sig_root.hex())
|
||
|
to = proof.tx_proof.value().to
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
info = TransactionInfo(
|
||
|
tx_index=proof.tx_index,
|
||
|
nonce=proof.tx_proof.value().nonce,
|
||
|
tx_value=proof.tx_proof.value().value,
|
||
|
limits=TransactionLimits(
|
||
|
max_priority_fee_per_gas=proof.tx_proof.value().max_priority_fee_per_gas,
|
||
|
max_fee_per_gas=proof.tx_proof.value().max_fee_per_gas,
|
||
|
gas=proof.tx_proof.value().gas,
|
||
|
),
|
||
|
)
|
||
|
case 2:
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().nonce.hash_tree_root(),
|
||
|
proof.tx_proof.value().max_priority_fee_per_gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().max_fee_per_gas.hash_tree_root(),
|
||
|
proof.tx_proof.value().gas_limit.hash_tree_root(),
|
||
|
proof.tx_proof.value().destination.hash_tree_root(),
|
||
|
proof.tx_proof.value().amount.hash_tree_root(),
|
||
|
cfg.chain_id.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP1559_INFO_PROOF_INDICES,
|
||
|
EIP1559_INFO_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
to = proof.tx_proof.value().destination
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
info = TransactionInfo(
|
||
|
tx_index=proof.tx_index,
|
||
|
nonce=proof.tx_proof.value().nonce,
|
||
|
tx_value=proof.tx_proof.value().amount,
|
||
|
limits=TransactionLimits(
|
||
|
max_priority_fee_per_gas=proof.tx_proof.value().max_priority_fee_per_gas,
|
||
|
max_fee_per_gas=proof.tx_proof.value().max_fee_per_gas,
|
||
|
gas=proof.tx_proof.value().gas_limit,
|
||
|
),
|
||
|
)
|
||
|
case 1:
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().nonce.hash_tree_root(),
|
||
|
proof.tx_proof.value().gas_price.hash_tree_root(),
|
||
|
proof.tx_proof.value().gas_limit.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
cfg.chain_id.hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
EIP2930_INFO_PROOF_INDICES,
|
||
|
EIP2930_INFO_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
to = proof.tx_proof.value().to
|
||
|
signature = proof.tx_proof.value().signature
|
||
|
info = TransactionInfo(
|
||
|
tx_index=proof.tx_index,
|
||
|
nonce=proof.tx_proof.value().nonce,
|
||
|
tx_value=proof.tx_proof.value().value,
|
||
|
limits=TransactionLimits(
|
||
|
max_priority_fee_per_gas=proof.tx_proof.value().gas_price,
|
||
|
max_fee_per_gas=proof.tx_proof.value().gas_price,
|
||
|
gas=proof.tx_proof.value().gas_limit,
|
||
|
),
|
||
|
)
|
||
|
case 0:
|
||
|
sig_root = calculate_multi_merkle_root(
|
||
|
[
|
||
|
proof.tx_proof.value().nonce.hash_tree_root(),
|
||
|
proof.tx_proof.value().gasprice.hash_tree_root(),
|
||
|
proof.tx_proof.value().startgas.hash_tree_root(),
|
||
|
proof.tx_proof.value().to.hash_tree_root(),
|
||
|
proof.tx_proof.value().value.hash_tree_root(),
|
||
|
zero_hashes[1],
|
||
|
],
|
||
|
proof.tx_proof.value().multi_branch,
|
||
|
LEGACY_INFO_PROOF_INDICES,
|
||
|
LEGACY_INFO_PROOF_HELPER_INDICES,
|
||
|
)
|
||
|
to = proof.tx_proof.value().to
|
||
|
signature = ECDSASignature(
|
||
|
y_parity=(proof.tx_proof.value().signature.v & 0x1) == 0,
|
||
|
r = proof.tx_proof.value().signature.r,
|
||
|
s = proof.tx_proof.value().signature.s,
|
||
|
)
|
||
|
info = TransactionInfo(
|
||
|
tx_index=proof.tx_index,
|
||
|
nonce=proof.tx_proof.value().nonce,
|
||
|
tx_value=proof.tx_proof.value().value,
|
||
|
limits=TransactionLimits(
|
||
|
max_priority_fee_per_gas=proof.tx_proof.value().gasprice,
|
||
|
max_fee_per_gas=proof.tx_proof.value().gasprice,
|
||
|
gas=proof.tx_proof.value().startgas,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
ecdsa = ECDSA()
|
||
|
recover_sig = ecdsa.ecdsa_recoverable_deserialize(
|
||
|
signature.r.to_bytes(32, 'big') + signature.s.to_bytes(32, 'big'),
|
||
|
0x01 if signature.y_parity else 0,
|
||
|
)
|
||
|
public_key = PublicKey(ecdsa.ecdsa_recover(sig_root, recover_sig, raw=True))
|
||
|
uncompressed = public_key.serialize(compressed=False)
|
||
|
info.tx_from = ExecutionAddress(keccak(uncompressed)[12:32])
|
||
|
|
||
|
match to.selector():
|
||
|
case 1:
|
||
|
info.tx_to = DestinationAddress(
|
||
|
destination_type=DESTINATION_TYPE_REGULAR,
|
||
|
address=to.value(),
|
||
|
)
|
||
|
case 0:
|
||
|
info.tx_to = DestinationAddress(
|
||
|
destination_type=DESTINATION_TYPE_CREATE,
|
||
|
address=compute_contract_address(info.tx_from, info.nonce),
|
||
|
)
|
||
|
|
||
|
info.tx_hash = calculate_multi_merkle_root(
|
||
|
[
|
||
|
sig_root,
|
||
|
proof.tx_proof.value().signature.hash_tree_root(),
|
||
|
],
|
||
|
[],
|
||
|
[
|
||
|
GeneralizedIndex(2),
|
||
|
GeneralizedIndex(3),
|
||
|
],
|
||
|
[],
|
||
|
)
|
||
|
|
||
|
tx_gindex = GeneralizedIndex(MAX_TRANSACTIONS_PER_PAYLOAD * 2 + uint64(proof.tx_index))
|
||
|
assert calculate_multi_merkle_root(
|
||
|
[
|
||
|
info.tx_hash.hash_tree_root(),
|
||
|
uint8(proof.tx_proof.selector()).hash_tree_root(),
|
||
|
],
|
||
|
proof.tx_branch,
|
||
|
[
|
||
|
tx_gindex * 2 + 0,
|
||
|
tx_gindex * 2 + 1,
|
||
|
],
|
||
|
get_helper_indices([tx_gindex]),
|
||
|
) == transactions_root
|
||
|
|
||
|
return info
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
print('transactions_root')
|
||
|
print(f'0x{transactions_root.hex()}')
|
||
|
|
||
|
print()
|
||
|
for tx_index in range(len(transaction_proofs)):
|
||
|
print(f'{tx_index} - TransactionProof')
|
||
|
expected_tx_hash = transaction_proofs[tx_index].tx_root
|
||
|
verify_transaction_proof(
|
||
|
transaction_proofs[tx_index], cfg, transactions_root,
|
||
|
expected_tx_hash)
|
||
|
print(f'tx_index = {transaction_proofs[tx_index].tx_index}')
|
||
|
|
||
|
print()
|
||
|
for tx_index in range(len(amount_proofs)):
|
||
|
print(f'{tx_index} - AmountProof')
|
||
|
expected_tx_to = ExecutionAddress(bytes.fromhex('d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'))
|
||
|
expected_tx_value_min = 1_000_000_000
|
||
|
verify_amount_proof(
|
||
|
amount_proofs[tx_index], cfg, transactions_root,
|
||
|
expected_tx_to, expected_tx_value_min,
|
||
|
)
|
||
|
print(f'OK')
|
||
|
|
||
|
print()
|
||
|
for tx_index in range(len(sender_proofs)):
|
||
|
print(f'{tx_index} - SenderProof')
|
||
|
expected_tx_to = ExecutionAddress(bytes.fromhex('d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'))
|
||
|
expected_tx_value_min = 1_000_000_000
|
||
|
tx_from = verify_sender_proof(
|
||
|
sender_proofs[tx_index], cfg, transactions_root,
|
||
|
expected_tx_to, expected_tx_value_min,
|
||
|
)
|
||
|
print(f'tx_from = 0x{tx_from.hex()}')
|
||
|
|
||
|
print()
|
||
|
for tx_index in range(len(info_proofs)):
|
||
|
print(f'{tx_index} - InfoProof')
|
||
|
info = verify_info_proof(
|
||
|
info_proofs[tx_index], cfg, transactions_root,
|
||
|
)
|
||
|
print(f'info = {info}')
|