in python/ccf/ledger.py [0:0]
def add_transaction(self, transaction):
"""
To validate the ledger, ledger transactions need to be added via this method.
Depending on the tables that were part of the transaction, it does different things.
When transaction contains signature table, it starts the verification process and verifies that the root of merkle tree was signed by a node which was part of the network.
It also matches the root of the merkle tree that this class maintains with the one extracted from the ledger.
If any of the above checks fail, this method throws.
"""
transaction_public_domain = transaction.get_public_domain()
tables = transaction_public_domain.get_tables()
# Add contributing nodes certs and update nodes network trust status for verification
node_certs = {}
if NODES_TABLE_NAME in tables:
node_table = tables[NODES_TABLE_NAME]
for node_id, node_info in node_table.items():
node_id = node_id.decode()
if node_info is None:
# Node has been removed from the store
self.node_activity_status.pop(node_id)
continue
node_info = json.loads(node_info)
# Add the self-signed node certificate (only available in 1.x,
# refer to node endorsed certificates table otherwise)
if "cert" in node_info:
node_certs[node_id] = node_info["cert"].encode()
self.node_certificates[node_id] = node_certs[node_id]
# Update node trust status
# Also record the seqno at which the node status changed to
# track when a primary node should stop issuing signatures
self.node_activity_status[node_id] = (
node_info["status"],
transaction_public_domain.get_seqno(),
)
if ENDORSED_NODE_CERTIFICATES_TABLE_NAME in tables:
node_endorsed_certificates_tables = tables[
ENDORSED_NODE_CERTIFICATES_TABLE_NAME
]
for (
node_id,
endorsed_node_cert,
) in node_endorsed_certificates_tables.items():
node_id = node_id.decode()
assert (
node_id not in node_certs
), f"Only one of node self-signed certificate and endorsed certificate should be recorded for node {node_id}"
if endorsed_node_cert is None:
# Node has been removed from the store
self.node_certificates.pop(node_id)
else:
self.node_certificates[node_id] = endorsed_node_cert
# This is a merkle root/signature tx if the table exists
if SIGNATURE_TX_TABLE_NAME in tables:
self.signature_count += 1
signature_table = tables[SIGNATURE_TX_TABLE_NAME]
for _, signature in signature_table.items():
signature = json.loads(signature)
current_seqno = signature["seqno"]
current_view = signature["view"]
signing_node = signature["node"]
# Get binary representations for the cert, existing root, and signature
cert = self.node_certificates[signing_node]
existing_root = bytes.fromhex(signature["root"])
sig = base64.b64decode(signature["sig"])
tx_info = TxBundleInfo(
self.merkle,
existing_root,
cert,
sig,
self.node_activity_status,
signing_node,
)
# validations for 1, 2 and 3
# throws if ledger validation failed.
self._verify_tx_set(tx_info)
self.last_verified_seqno = current_seqno
self.last_verified_view = current_view
# Checks complete, add this transaction to tree
self.merkle.add_leaf(transaction.get_tx_digest(), False)