| name | blockchain-cryptography |
| description | Guide complet de la cryptographie blockchain — Merkle Trees, BLS signatures, Schnorr, Account Abstraction, Threshold Signatures, zk-Rollups, MEV, et protocoles blockchain. |
| category | cybersecurite |
| tags | ["blockchain","merkle-tree","bls","schnorr","threshold","ethereum","bitcoin","cryptography"] |
Cryptographie Blockchain — Guide Approfondi
Sommaire
- Merkle Trees et Patricia Merkle Tries
- Signatures dans Bitcoin
- Signatures dans Ethereum
- BLS Aggregation (Ethereum 2.0)
- Schnorr et Taproot (Bitcoin)
- Account Abstraction (EIP-4337)
- Threshold Cryptography en Blockchain
- zk-Rollups Cryptographie
- MEV et Protocoles
1. Merkle Trees et Patricia Merkle Tries
1.1 Binary Merkle Tree
Structure arborescente où chaque nœud est un hash de ses enfants.
Root = H(H01 || H23)
/ \
H01 = H(H0 || H1) H23 = H(H2 || H3)
/ \ / \
H0=H(Tx0) H1=H(Tx1) H2=H(Tx2) H3=H(Tx3)
Propriétés :
- Vérification d'un élément :
O(log n) hashs
- Preuve : le chemin (siblings) de la feuille à la racine
- SPV (Simplified Payment Verification) : un nœud léger peut vérifier une transaction avec seulement la racine de Merkle
import hashlib
class MerkleTree:
def __init__(self, leaves):
self.leaves = [hashlib.sha256(l).digest() for l in leaves]
self.nodes = self._build(self.leaves)
self.root = self.nodes[-1][0] if self.nodes else None
def _build(self, level):
levels = [level]
while len(level) > 1:
next_level = []
for i in range(0, len(level), 2):
left = level[i]
right = level[i + 1] if i + 1 < len(level) else left
combined = hashlib.sha256(left + right).digest()
next_level.append(combined)
levels.append(next_level)
level = next_level
return levels
def get_proof(self, index):
"""Retourne le chemin de preuve pour une feuille donnée"""
proof = []
for level in self.nodes[:-1]:
sibling_idx = index ^ index % == index ^
sibling_idx < (level):
proof.append(level[sibling_idx])
index //=
proof
():
current = hashlib.sha256(leaf).digest()
sibling proof:
index % == :
current = hashlib.sha256(current + sibling).digest()
:
current = hashlib.sha256(sibling + current).digest()
index //=
current == root
1.2 Merkle Patricia Trie (Ethereum)
Structure plus complexe utilisée par Ethereum pour le State Trie :
def ethereum_state_proof(state_root, address, storage_key, provider):
"""
Preuve de l'état d'un contrat :
1. Prove_Account(account_key) → preuve dans le state trie
2. Prove_Storage(storage_key) → preuve dans le storage trie du contrat
La preuve combine les deux.
"""
account_proof = provider.eth_getProof(address, [storage_key], "latest")
return account_proof
1.3 Verkle Trees (Ethereum en route)
Verkle Trees = Merkle Trees avec Vector Commitments (elliptic curve commitments au lieu de hash).
def verkle_commit(values, basis_G, basis_Q):
"""Verkle commitment sur k valeurs"""
C = sum(v * g for v, g in zip(values, basis_G))
C += blinding * basis_Q
return C
2. Signatures dans Bitcoin
2.1 ECDSA dans Bitcoin
Bitcoin utilise ECDSA sur secp256k1 :
Courbe : y² = x³ + 7 mod p
p = 2²⁵⁶ - 2³² - 2⁹ - 2⁸ - 2⁷ - 2⁶ - 2⁴ - 1 (Fermat prime)
Ordre n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
G = (Gx, Gy)
Format de signature : DER-encodée (avant SegWit)
bitcoin-cli decoderawtransaction <tx_hex>
Low-S value : Bitcoin exige s < n/2 pour éviter le malleability (BIP 62, BIP 146).
2.2 SIGHASH Types
SIGHASH_ALL = 0x01
SIGHASH_NONE = 0x02
SIGHASH_SINGLE = 0x03
SIGHASH_ANYONECANPAY = 0x80
2.3 ScriptPubKey (P2PKH, P2SH, P2WPKH)
OP_DUP OP_HASH160 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG
OP_HASH160 <20-byte-script-hash> OP_EQUAL
3. Signatures dans Ethereum
3.1 ECDSA sur secp256k1
Ethereum utilise ECDSA de manière légèrement différente de Bitcoin :
from eth_keys import keys
from eth_account.messages import encode_defunct
def ethereum_sign(message, private_key_hex):
"""Signature Ethereum (ECDSA sur secp256k1)"""
pk = keys.PrivateKey(bytes.fromhex(private_key_hex))
prefixed = encode_defunct(text=message)
signature = pk.sign_msg(prefixed)
return {
'r': hex(signature.r),
's': hex(signature.s),
'v': signature.v,
}
def recover_address(message, signature):
pk = keys.PublicKey.recover_from_msg(
encode_defunct(text=message),
signature
)
return pk.to_checksum_address()
def eip155_signature(tx, chain_id=1):
"""v = 2*chain_id + 35 + recovery_id"""
v = 2 * chain_id + 35 + recovery_id
return (r, s, v)
3.2 EIP-712 — Typed Data Signing
Standard pour les signatures structurées (Ethereum EIP-712) :
from eth_account.messages import encode_typed_data
def sign_eip712(private_key, domain, types, value):
"""
Signature structurée EIP-712.
Permet aux utilisateurs de voir ce qu'ils signent dans MetaMask.
"""
typed_data = {
"domain": domain,
"types": types,
"message": value,
}
encoded = encode_typed_data(typed_data)
signed = private_key.signHash(encoded)
return signed
3.3 EIP-1559 — Fee Market
Le nouveau modèle de frais change la structure de signature :
tx_1559 = {
'chainId': chain_id,
'nonce': nonce,
'maxPriorityFeePerGas': priority_fee,
'maxFeePerGas': max_fee,
'gas': gas_limit,
'to': address,
'value': amount,
'data': data,
'accessList': access_list,
'signature': (v, r, s)
}
4. BLS Aggregation (Ethereum 2.0)
4.1 BLS12-381 dans Ethereum
Ethereum 2.0 utilise BLS12-381 pour la vérification de validateur :
from eth2spec.utils import bls
def eth2_sign_attestation(validator_key, slot, block_root, source_epoch, target_epoch):
"""Signe une attestation pour un validateur Ethereum 2.0"""
domain = compute_domain(DOMAIN_BEACON_ATTESTER, fork_version, genesis_validators_root)
signing_root = compute_signing_root(attestation_data, domain)
return bls.Sign(validator_key, signing_root)
Fast Aggregate Verification :
def verify_aggregate_attestation(pubkeys, signature, message):
"""
Vérifie une signature BLS agrégée pour N validateurs.
Une seule équation de pairing au lieu de N.
"""
return bls.FastAggregateVerify(pubkeys, message, signature)
4.2 Petites optimisations BLS
def randomize_bls(signature, random_r):
"""BLS signature randomization (pour la vie privée)"""
return signature + random_r * H(public_key)
pop = bls.Sign(private_key, public_key.to_bytes())
assert bls.Verify(public_key, public_key.to_bytes(), pop)
4.3 Échange de clés Distributed Key Generation (DKG)
5. Schnorr et Taproot (Bitcoin)
5.1 BIP 340 — Schnorr Signatures
Bitcoin adopte Schnorr via Taproot (BIP 340, 341) :
import hashlib
def schnorr_sign(message, private_key, public_key):
"""
Schnorr sur secp256k1 : BIP 340
Spécificités : clé publique en x-only (32 octets)
"""
k = nonce_deterministic(private_key, message)
R = point_mul(G, k)
r = R.x
e = int.from_bytes(hashlib.sha256(
r.to_bytes(32, 'big') +
public_key.x.to_bytes(32, 'big') +
message
).digest(), 'big') % n
s = (k + e * private_key) % n
return (r, s)
def schnorr_verify(public_key, message, signature):
"""
Vérification Schnorr (BIP 340)
s·G = R + e·P ? où e = H(R || P || m)
"""
r, s = signature
e = int.from_bytes(hashlib.sha256(
r.to_bytes(32, 'big') +
public_key.x.to_bytes(32, 'big') +
message
).digest(), 'big') % n
sG = point_mul(G, s)
eP = point_mul(public_key, e)
R = point_add(sG, neg(eP))
return R.x == r
5.2 Taproot (BIP 341)
Combinaison de clé publique + script caché :
def taproot_output(internal_key, script_tree):
"""
Crée une adresse Taproot.
P = internal_key + tweak
tweak = H(internal_key || merkle_root || 0)
"""
merkle_root = compute_merkle_root(script_tree)
tweak = int.from_bytes(tagged_hash(
"TapTweak",
internal_key.x.to_bytes(32, 'big') + merkle_root
), 'big') % n
output_key = point_add(internal_key, point_mul(G, tweak))
return output_key
6. Account Abstraction (EIP-4337)
6.1 Principe
Permet aux contrats d'agir comme des comptes externes :
class UserOperation:
sender: Address
nonce: int
initCode: bytes
callData: bytes
callGasLimit: int
verificationGasLimit: int
preVerificationGas: int
maxFeePerGas: int
maxPriorityFeePerGas: int
paymasterAndData: bytes
signature: bytes
def hash(self):
return keccak256(abi.encode(
self.sender, self.nonce,
keccak256(self.initCode),
keccak256(self.callData),
self.callGasLimit,
self.verificationGasLimit,
self.preVerificationGas,
self.maxFeePerGas,
self.maxPriorityFeePerGas,
keccak256(self.paymasterAndData)
))
6.2 Signature Aggregation
7. Threshold Cryptography en Blockchain
7.1 Distributed Key Generation (DKG)
Exemple : protocole de threshold signing pour wallets multi-sig :
def dkg_protocol(participants, threshold, curve):
"""
DKG distribué : n participants, t threshold.
Personne ne connaît la clé privée complète.
"""
return public_key, {i: secret_share_i for i in participants}
def threshold_sign(sk_share, message, participants_indices, n, t):
"""
Signature threshold : t des n participants signent
Chaque participant produit une signature partielle via Lagrange.
"""
partial_sig = bls_sign(sk_share, message)
def lagrange_coeff(i, indices):
num = 1
den = 1
for j in indices:
if j != i:
num *= j
den *= j - i
return num * modular_inverse(den, n)
combined_sig = 1
for i, sig_part in zip(participants_indices, partial_sigs):
lambda_i = lagrange_coeff(i, participants_indices)
combined_sig *= sig_part ** lambda_i
return combined_sig
7.2 Applications Wallet
8. zk-Rollups Cryptographie
8.1 Circuits ZK pour Blockchains
def zk_rollup_proof(batch_txs, old_state_root, new_state_root, batch_commitment):
"""
Le circuit ZK d'un rollup vérifie :
1. Chaque transaction a une signature valide (ECDSA vérification dans le circuit)
2. Les transitions d'état sont correctes (add, sub, transfer)
3. new_state_root = f(old_state_root, batch_txs)
4. Les signatures sont agrégées (si BLS)
"""
pass
8.2 Proof Recursion
9. MEV et Protocoles
9.1 MEV (Maximal Extractable Value)
9.2 Chiffrement des Transactions
9.3 Light Client Verification
Références