config['tdm_IP'], config['tdm_RPC'], block)) j = json.loads(ret.text) return j if __name__ == '__main__': block = {} while True: latest = tendermint_latest_block() for blk in range(last_block + 1, latest + 1): print('blk', blk) tdm_block = get_tendermint_block(blk) apphash = tdm_block['result']['block_meta']['header']['app_hash'] sedes = CountableList( List([ rlp.sedes.binary, rlp.sedes.big_endian_int, rlp.sedes.big_endian_int, rlp.sedes.binary, rlp.sedes.binary ])) d = rlp.decode(bytes.fromhex(apphash), sedes) for i in d: block[i[1]] = Block(i[0], i[1], bool(i[2]), i[3], i[4]) plasma_latest_block = i[1] txs = tdm_block['result']['block']['data']['txs'] for tx in txs: decoded_tx = base64.b64decode(tx).decode() if decoded_tx[0] == '1': decoded_tx = decoded_tx[1:] sig = decoded_tx[:130] blkNum = int(decoded_tx[130:], 16) block[blkNum].add_signature(sig) # block[i[1]].submit()
class Blocks(rlp.Serializable): fields = [ ("reqid", big_endian_int), ("blocks", CountableList(Block)), ]
class CompactBlock(rlp.Serializable): fields = [ ("block_header", BlockHeader), ("nonce", big_endian_int), ("tx_short_ids", CountableList(big_endian_int)), ]
def serialize(cls, obj): return CountableList(Transaction).serialize(obj.transactions)
class BlockHeaders(rlp.Serializable): fields = [ ("reqid", big_endian_int), ("headers", CountableList(BlockHeader)), ]
class Hello(rlp.Serializable): fields = [("network_id", big_endian_int), ("capabilities", CountableList(Capability)), ("node_endpoint", NodeEndpoint)]
class BaseBeaconBlock(rlp.Serializable): fields = [ # Slot number ('slot', uint64), # Proposer RANDAO reveal ('randao_reveal', hash32), # Recent PoW receipt root ('candidate_pow_receipt_root', hash32), # Skip list of previous beacon block hashes # i'th item is the most recent ancestor whose slot is a multiple of 2**i for i = 0, ..., 31 ('ancestor_hashes', CountableList(hash32)), # State root ('state_root', hash32), # Attestations ('attestations', CountableList(AttestationRecord)), # Specials (e.g. logouts, penalties) ('specials', CountableList(SpecialRecord)), # Proposer signature ('proposer_signature', CountableList(uint256)), ] def __init__(self, slot: int, randao_reveal: Hash32, candidate_pow_receipt_root: Hash32, ancestor_hashes: Sequence[Hash32], state_root: Hash32, attestations: Sequence[AttestationRecord], specials: Sequence[SpecialRecord], proposer_signature: Sequence[int] = None) -> None: if proposer_signature is None: proposer_signature = (0, 0) super().__init__( slot=slot, randao_reveal=randao_reveal, candidate_pow_receipt_root=candidate_pow_receipt_root, ancestor_hashes=ancestor_hashes, state_root=state_root, attestations=attestations, specials=specials, proposer_signature=proposer_signature, ) def __repr__(self) -> str: return '<Block #{0} {1}>'.format( self.slot, encode_hex(self.hash)[2:10], ) _hash = None @property def hash(self) -> Hash32: if self._hash is None: self._hash = blake(rlp.encode(self)) return self._hash @property def num_attestations(self) -> int: return len(self.attestations) @property def parent_hash(self) -> Hash32: if not self.ancestor_hashes: return None else: return self.ancestor_hashes[0]
class MimbleBlock(rlp.Serializable): blknum = 0 diff_tx = None fields = [ ('transaction_set', CountableList(MimbleTx)), ] def __init__(self, blknum=0): self.blknum = blknum def add_tx(self, tx): if (self.diff_tx == None): self.diff_tx = tx else: self.diff_tx = self.diff_tx.combine(tx) def get_total_commitment(self): commitment = NullPoint if (self.diff_tx != None): for i in range(0, len(self.diff_tx.inputs)): commitment = add(commitment, self.diff_tx.inputs[i].commitment) commitment = neg(commitment) for i in range(0, len(self.diff_tx.outputs)): commitment = add(commitment, self.diff_tx.outputs[i].commitment) return commitment def get_total_signature(self): if (self.diff_tx != None): pub_key = Schnorr.recover_multiple(self.diff_tx.sigs) else: pub_key = NullPoint return pub_key def get_bulletproofs(self): if (self.diff_tx != None): bp = [] for i in range(0, len(self.diff_tx.outputs)): bp = bp + [self.diff_tx.outputs[i].bp] return bp else: return None def verify(self, verifyBP=True): import time t0, t1, t2 = 0, 0, 0 if (self.diff_tx != None): ms = time.time() if (not self.diff_tx.verify()): return False t0 = time.time() - ms if (verifyBP): bp = self.get_bulletproofs() if (bp == None): return False if (len(bp) != len(self.diff_tx.outputs)): return False print("Verifying " + str(len(bp)) + " bulletproofs...", end="") ms = time.time() if (bp != None and not BulletProof.VerifyMulti(bp)): return False t1 = time.time() - ms t2 = t1 / len(bp) print("Done!") print("verify() => " + str(t0+t1) + "s (" + str(t2) + "s per bulletproof)") return True def print(self): print("MimbleBlock:") print("Block Number: " + str(self.blknum)) if (self.diff_tx != None): print("Total Commitment: " + hex(CompressPoint(self.get_total_commitment()))) print("Multisignature: " + hex(CompressPoint(self.get_total_signature()))) print("Diff Tx:") self.diff_tx.print() else: print("No Transactions")
class MimbleTx(rlp.Serializable): fields = [ ('inputs', CountableList(TxOutput)), ('outputs', CountableList(TxOutput)), ('sig', CountableList(Schnorr)), ('offset', big_endian_int) ] def __init__(self, inputs, outputs, sigs, offset=0): self.inputs = inputs self.outputs = outputs self.sigs = sigs self.offset = offset def verify(self): #Add up commitments commitment = NullPoint for i in range(0, len(self.inputs)): commitment = add(commitment, self.inputs[i].commitment) commitment = neg(commitment) for i in range(0, len(self.outputs)): commitment = add(commitment, self.outputs[i].commitment) #Add up Schnorr public keys, see if remainder is signed (x known s.t. remainder = x*) remainder = add(Schnorr.recover_multiple(self.sigs), multiply(G, self.offset)) if (not eq(commitment, remainder)): return False return True def combine(tx1, tx2): #Naively combine tx's inp12 = tx1.inputs + tx2.inputs out12 = tx1.outputs + tx2.outputs sigs12 = tx1.sigs + tx2.sigs off12 = sAdd(tx1.offset, tx2.offset) #Remove common inputs and outputs (cut-through) i = 0 while i < len(inp12): j = 0 while j < len(out12): if (eq(inp12[i].commitment, out12[j].commitment)): inp12 = inp12[:i] + inp12[i+1:] out12 = out12[:j] + out12[j+1:] i -= 1 j -= 1 j += 1 i += 1 #Return new Tx return MimbleTx(inp12, out12, sigs12, off12) def create_random(input_count, output_count, createBP=True, max_value=1000): import time (v_in, v_out, bf_in, bf_out, off, rem) = GetRandomTxValues(input_count, output_count, max_value=max_value) inputs1 = TxOutput.Create(v_in, bf_in) if (createBP): print("Creating " + str(output_count) + " bulletproofs...", end="") ms = time.time() outputs1 = TxOutput.Create(v_out, bf_out, createBP=createBP) if (createBP): t0 = time.time() - ms print("Done!") print("create() => " + str(t0) + "s (" + str(t0 / output_count) + "s per bulletproof)") sigs1 = [Schnorr.sign(rem)] tx = MimbleTx(inputs1, outputs1, sigs1, off) return tx def create_known(v_in, bf_in, v_out, bf_out, createBP=True): import time #Calculate remainder inputs1 = TxOutput.Create(v_in, bf_in) if (createBP): print("Creating " + str(len(bf_out)) + " bulletproofs...", end="") ms = time.time() outputs1 = TxOutput.Create(v_out, bf_out, createBP=createBP) off = getRandom() rem = sSub(vSum(bf_out), sAdd(vSum(bf_in), off)) if (createBP): t0 = time.time() - ms print("Done!") print("create() => " + str(t0) + "s (" + str(t0 / len(bf_out)) + "s per bulletproof)") sig1 = [Schnorr.sign(rem)] tx = MimbleTx(inputs1, outputs1, sig1, off) return tx def print(self, detailed=False): print("Inputs: " + str(len(self.inputs))) if (detailed): for i in range(0, len(self.inputs)): print("\t" + str(i) + ": " + hex(CompressPoint(self.inputs[i].commitment))) print("Outputs: " + str(len(self.outputs))) if (detailed): for i in range(0, len(self.outputs)): print("\t" + str(i) + ": " + hex(CompressPoint(self.outputs[i].commitment))) print("Sigs: " + str(len(self.sigs))) if (detailed): for i in range(0, len(self.sigs)): print("Sig " + str(i) + ":") print("\tR: "+ hex(CompressPoint(self.sigs[i].R))) print("\ts: "+ hex(CompressPoint(self.sigs[i].s))) print("Sig Offset: " + hex(self.offset))
class AccountAccesses(rlp.Serializable): fields = [ ('account', address), ('storage_keys', CountableList(BigEndianInt(32))), ]
class SpuriousDragonBlock(HomesteadBlock): transaction_class = SpuriousDragonTransaction fields = [('header', BlockHeader), ('transactions', CountableList(transaction_class)), ('uncles', CountableList(BlockHeader))]
class AccessListTransaction(rlp.Serializable, SignedTransactionMethods, SignedTransactionAPI): _type_id = ACCESS_LIST_TRANSACTION_TYPE fields = [ ('chain_id', big_endian_int), ('nonce', big_endian_int), ('gas_price', big_endian_int), ('gas', big_endian_int), ('to', address), ('value', big_endian_int), ('data', binary), ('access_list', CountableList(AccountAccesses)), ('y_parity', big_endian_int), ('r', big_endian_int), ('s', big_endian_int), ] def get_sender(self) -> Address: return extract_transaction_sender(self) def get_message_for_signing(self) -> bytes: unsigned = UnsignedAccessListTransaction( self.chain_id, self.nonce, self.gas_price, self.gas, self.to, self.value, self.data, self.access_list, ) payload = rlp.encode(unsigned) return self._type_byte + payload def check_signature_validity(self) -> None: validate_transaction_signature(self) @cached_property def _type_byte(self) -> bytes: return to_bytes(self._type_id) @cached_property def hash(self) -> Hash32: raise NotImplementedError("Call hash() on the TypedTransaction instead") def get_intrinsic_gas(self) -> int: core_gas = calculate_intrinsic_gas(ISTANBUL_TX_GAS_SCHEDULE, self) num_addresses = len(self.access_list) preload_address_costs = ACCESS_LIST_ADDRESS_COST_EIP_2930 * num_addresses num_slots = sum(len(slots) for _, slots in self.access_list) preload_slot_costs = ACCESS_LIST_STORAGE_KEY_COST_EIP_2930 * num_slots return core_gas + preload_address_costs + preload_slot_costs def encode(self) -> bytes: return rlp.encode(self) def make_receipt( self, status: bytes, gas_used: int, log_entries: Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]) -> ReceiptAPI: logs = [ Log(address, topics, data) for address, topics, data in log_entries ] # TypedTransaction is responsible for wrapping this into a TypedReceipt return Receipt( state_root=status, gas_used=gas_used, logs=logs, ) # Old transactions are treated as setting both max-fees as the gas price @property def max_priority_fee_per_gas(self) -> int: return self.gas_price @property def max_fee_per_gas(self) -> int: return self.gas_price
class ArrowGlacierBlock(LondonBlock): transaction_builder: Type[ TransactionBuilderAPI] = ArrowGlacierTransactionBuilder fields = [('header', ArrowGlacierBlockHeader), ('transactions', CountableList(transaction_builder)), ('uncles', CountableList(LondonBackwardsHeader))]
class BaseBeaconBlock(rlp.Serializable, Configurable, ABC): block_body_class = BaseBeaconBlockBody fields = [ # # Header # ('slot', uint64), ('parent_root', hash32), ('state_root', hash32), ('randao_reveal', hash32), ('candidate_pow_receipt_root', hash32), ('signature', CountableList(uint384)), # # Body # ('body', block_body_class), ] def __init__(self, slot: SlotNumber, parent_root: Hash32, state_root: Hash32, randao_reveal: Hash32, candidate_pow_receipt_root: Hash32, body: BaseBeaconBlockBody, signature: BLSSignature = EMPTY_SIGNATURE) -> None: super().__init__( slot=slot, parent_root=parent_root, state_root=state_root, randao_reveal=randao_reveal, candidate_pow_receipt_root=candidate_pow_receipt_root, signature=signature, body=body, ) def __repr__(self) -> str: return '<Block #{0} {1}>'.format( self.slot, encode_hex(self.root)[2:10], ) _hash = None @property def hash(self) -> Hash32: if self._hash is None: self._hash = hash_eth2(rlp.encode(self)) return self._hash @property def root(self) -> Hash32: # Alias of `hash`. # Using flat hash, might change to SSZ tree hash. return self.hash @property def num_attestations(self) -> int: return len(self.body.attestations) @property def block_without_signature_root(self) -> Hash32: return self.copy(signature=EMPTY_SIGNATURE).root @classmethod @abstractmethod def from_root(cls, root: Hash32, chaindb: 'BaseBeaconChainDB') -> 'BaseBeaconBlock': """ Return the block denoted by the given block root. """ raise NotImplementedError("Must be implemented by subclasses")
class GetBlockTxn(rlp.Serializable): fields = [ ("reqid", big_endian_int), ("block_hash", hash32), ("indexes", CountableList(big_endian_int)), ]
class BeaconState(rlp.Serializable): """ Note: using RLP until we have standardized serialization format. """ fields = [ # Misc ('slot', uint64), ('genesis_time', uint64), ('fork', Fork), # For versioning hard forks # Validator registry ('validator_registry', CountableList(ValidatorRecord)), ('validator_balances', CountableList(uint64)), ('validator_registry_update_slot', uint64), ('validator_registry_exit_count', uint64), ('validator_registry_delta_chain_tip', hash32), # For light clients to easily track delta # Randomness and committees ('latest_randao_mixes', CountableList(hash32)), ('latest_vdf_outputs', CountableList(hash32)), # TODO Remove `persistent_committee_reassignments` ('persistent_committees', CountableList(CountableList(uint24))), ('persistent_committee_reassignments', CountableList(ShardReassignmentRecord)), ('previous_epoch_start_shard', uint64), ('current_epoch_start_shard', uint64), ('previous_epoch_calculation_slot', uint64), ('current_epoch_calculation_slot', uint64), ('previous_epoch_randao_mix', hash32), ('current_epoch_randao_mix', hash32), # Custody challenges ('custody_challenges', CountableList(CustodyChallenge)), # Finality ('previous_justified_slot', uint64), ('justified_slot', uint64), # Note: justification_bitfield is meant to be defined as an integer type, # so its bit operation in Python and is easier to specify and implement. ('justification_bitfield', uint64), ('finalized_slot', uint64), # Recent state ('latest_crosslinks', CountableList(CrosslinkRecord)), ('latest_block_roots', CountableList(hash32) ), # Needed to process attestations, older to newer # noqa: E501 ('latest_penalized_balances', CountableList(uint64) ), # Balances penalized at every withdrawal period # noqa: E501 ('latest_attestations', CountableList(PendingAttestationRecord)), ( 'batched_block_roots', CountableList(Hash32) ), # allow for a log-sized Merkle proof from any block to any historical block root" # noqa: E501 # Ethereum 1.0 chain ('latest_eth1_data', Eth1Data), ('eth1_data_votes', CountableList(Eth1DataVote)), ] def __init__( self, *, # Misc slot: SlotNumber, genesis_time: Timestamp, fork: Fork, # Validator registry validator_registry: Sequence[ValidatorRecord], validator_balances: Sequence[Gwei], validator_registry_update_slot: SlotNumber, validator_registry_exit_count: int, validator_registry_delta_chain_tip: Hash32, # Randomness and committees latest_randao_mixes: Sequence[Hash32], latest_vdf_outputs: Sequence[Hash32], persistent_committees: Sequence[Sequence[ValidatorIndex]], persistent_committee_reassignments: Sequence[ ShardReassignmentRecord], previous_epoch_start_shard: ShardNumber, current_epoch_start_shard: ShardNumber, previous_epoch_calculation_slot: SlotNumber, current_epoch_calculation_slot: SlotNumber, previous_epoch_randao_mix: Hash32, current_epoch_randao_mix: Hash32, # Custody challenges custody_challenges: Sequence[CustodyChallenge], # Finality previous_justified_slot: SlotNumber, justified_slot: SlotNumber, justification_bitfield: int, finalized_slot: SlotNumber, # Recent state latest_crosslinks: Sequence[CrosslinkRecord], latest_block_roots: Sequence[Hash32], latest_penalized_balances: Sequence[Gwei], batched_block_roots: Sequence[Hash32], latest_attestations: Sequence[PendingAttestationRecord], # Ethereum 1.0 chain latest_eth1_data: Eth1Data, eth1_data_votes: Sequence[Eth1DataVote]) -> None: if len(validator_registry) != len(validator_balances): raise ValueError( "The length of validator_registry and validator_balances should be the same." ) super().__init__( # Misc slot=slot, genesis_time=genesis_time, fork=fork, # Validator registry validator_registry=validator_registry, validator_balances=validator_balances, validator_registry_update_slot=validator_registry_update_slot, validator_registry_exit_count=validator_registry_exit_count, validator_registry_delta_chain_tip= validator_registry_delta_chain_tip, # Randomness and committees latest_randao_mixes=latest_randao_mixes, latest_vdf_outputs=latest_vdf_outputs, persistent_committees=persistent_committees, persistent_committee_reassignments= persistent_committee_reassignments, previous_epoch_start_shard=previous_epoch_start_shard, current_epoch_start_shard=current_epoch_start_shard, previous_epoch_calculation_slot=previous_epoch_calculation_slot, current_epoch_calculation_slot=current_epoch_calculation_slot, previous_epoch_randao_mix=previous_epoch_randao_mix, current_epoch_randao_mix=current_epoch_randao_mix, # Proof of Custody custody_challenges=custody_challenges, # Finality previous_justified_slot=previous_justified_slot, justified_slot=justified_slot, justification_bitfield=justification_bitfield, finalized_slot=finalized_slot, # Recent state latest_crosslinks=latest_crosslinks, latest_block_roots=latest_block_roots, latest_penalized_balances=latest_penalized_balances, latest_attestations=latest_attestations, batched_block_roots=batched_block_roots, # Ethereum 1.0 chain latest_eth1_data=latest_eth1_data, eth1_data_votes=eth1_data_votes, ) def __repr__(self) -> str: return 'BeaconState #{0}>'.format(encode_hex(self.root)[2:10], ) _hash = None @property def hash(self) -> Hash32: if self._hash is None: self._hash = hash_eth2(rlp.encode(self)) return self._hash @property def root(self) -> Hash32: # Alias of `hash`. # Using flat hash, might change to SSZ tree hash. return self.hash @property def num_validators(self) -> int: return len(self.validator_registry) @property def num_crosslinks(self) -> int: return len(self.latest_crosslinks) @classmethod def create_filled_state( cls, *, genesis_start_shard: ShardNumber, genesis_slot: SlotNumber, shard_count: int, latest_block_roots_length: int, latest_randao_mixes_length: int, latest_penalized_exit_length: int, activated_genesis_validators: Sequence[ValidatorRecord] = (), genesis_balances: Sequence[Gwei] = () ) -> 'BeaconState': return cls( # Misc slot=genesis_slot, genesis_time=Timestamp(0), fork=Fork( previous_version=0, current_version=0, slot=genesis_slot, ), # Validator registry validator_registry=activated_genesis_validators, validator_balances=genesis_balances, validator_registry_update_slot=genesis_slot, validator_registry_exit_count=0, validator_registry_delta_chain_tip=ZERO_HASH32, # Randomness and committees latest_randao_mixes=tuple( ZERO_HASH32 for _ in range(latest_randao_mixes_length)), latest_vdf_outputs=(), persistent_committees=(), persistent_committee_reassignments=(), previous_epoch_start_shard=genesis_start_shard, current_epoch_start_shard=genesis_start_shard, previous_epoch_calculation_slot=genesis_slot, current_epoch_calculation_slot=genesis_slot, previous_epoch_randao_mix=ZERO_HASH32, current_epoch_randao_mix=ZERO_HASH32, # Custody challenges custody_challenges=(), # Finality previous_justified_slot=genesis_slot, justified_slot=genesis_slot, justification_bitfield=genesis_slot, finalized_slot=genesis_slot, # Recent state latest_crosslinks=tuple( CrosslinkRecord( slot=genesis_slot, shard_block_root=ZERO_HASH32, ) for _ in range(shard_count)), latest_block_roots=tuple( ZERO_HASH32 for _ in range(latest_block_roots_length)), latest_penalized_balances=(Gwei(0), ) * latest_penalized_exit_length, latest_attestations=(), batched_block_roots=(), # Ethereum 1.0 chain data latest_eth1_data=Eth1Data.create_empty_data(), eth1_data_votes=(), ) def update_validator_registry(self, validator_index: ValidatorIndex, validator: ValidatorRecord) -> 'BeaconState': """ Replace ``self.validator_registry[validator_index]`` with ``validator``. """ if validator_index >= self.num_validators or validator_index < 0: raise IndexError("Incorrect validator index") validator_registry = list(self.validator_registry) validator_registry[validator_index] = validator updated_state = self.copy( validator_registry=tuple(validator_registry), ) return updated_state def update_validator_balance(self, validator_index: ValidatorIndex, balance: Gwei) -> 'BeaconState': """ Update the balance of validator of the given ``validator_index``. """ if validator_index >= self.num_validators or validator_index < 0: raise IndexError("Incorrect validator index") validator_balances = list(self.validator_balances) validator_balances[validator_index] = balance updated_state = self.copy( validator_balances=tuple(validator_balances), ) return updated_state def update_validator(self, validator_index: ValidatorIndex, validator: ValidatorRecord, balance: Gwei) -> 'BeaconState': """ Update the ``ValidatorRecord`` and balance of validator of the given ``validator_index``. """ state = self.update_validator_registry(validator_index, validator) state = state.update_validator_balance(validator_index, balance) return state
class GetBlockTxnResponse(rlp.Serializable): fields = [("reqid", big_endian_int), ("block_hash", hash32), ("block_txn", CountableList(Transaction))]
class BlockBody(rlp.Serializable): fields = [('transactions', CountableList(Transaction)), ('uncles', CountableList(BlockHeader))]
class Log(rlp.Serializable): fields = [('address', Binary.fixed_length(20, allow_empty=True)), ('topics', CountableList(BigEndianInt(32))), ('data', Binary())]
class FrontierBlock(BaseBlock): fields = [('header', BlockHeader), ('transactions', CountableList(FrontierTransaction)), ('uncles', CountableList(BlockHeader))] db = None bloom_filter = None def __init__(self, header, db, transactions=None, uncles=None): self.db = db if transactions is None: transactions = [] if uncles is None: uncles = [] self.bloom_filter = BloomFilter(header.bloom) self.transaction_db = Trie(db=self.db, root_hash=header.transaction_root) self.receipt_db = Trie(db=self.db, root_hash=header.receipt_root) super(FrontierBlock, self).__init__( header=header, transactions=transactions, uncles=uncles, ) # TODO: should perform block validation at this point? def validate_gas_limit(self): gas_limit = self.header.gas_limit if gas_limit < GAS_LIMIT_MINIMUM: raise ValidationError("Gas limit {0} is below minimum {1}".format( gas_limit, GAS_LIMIT_MINIMUM)) if gas_limit > GAS_LIMIT_MAXIMUM: raise ValidationError("Gas limit {0} is above maximum {1}".format( gas_limit, GAS_LIMIT_MAXIMUM)) parent_gas_limit = self.get_parent_header().gas_limit diff = gas_limit - parent_gas_limit if diff > (parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR): raise ValidationError( "Gas limit {0} difference to parent {1} is too big {2}".format( gas_limit, parent_gas_limit, diff)) def validate(self): if not self.is_genesis: parent_header = self.get_parent_header() self.validate_gas_limit() validate_length_lte(self.header.extra_data, 32) # timestamp if self.header.timestamp < parent_header.timestamp: raise ValidationError( "`timestamp` is before the parent block's timestamp.\n" "- block : {0}\n" "- parent : {1}. ".format( self.header.timestamp, parent_header.timestamp, )) elif self.header.timestamp == parent_header.timestamp: raise ValidationError( "`timestamp` is equal to the parent block's timestamp\n" "- block : {0}\n" "- parent: {1}. ".format( self.header.timestamp, parent_header.timestamp, )) # XXX: Should these and some other checks be moved into # VM.validate_block(), as they apply to all block flavours? if len(self.uncles) > MAX_UNCLES: raise ValidationError( "Blocks may have a maximum of {0} uncles. Found " "{1}.".format(MAX_UNCLES, len(self.uncles))) for uncle in self.uncles: self.validate_uncle(uncle) if self.header.state_root not in self.db: raise ValidationError("`state_root` was not found in the db.\n" "- state_root: {0}".format( self.header.state_root, )) local_uncle_hash = keccak(rlp.encode(self.uncles)) if local_uncle_hash != self.header.uncles_hash: raise ValidationError( "`uncles_hash` and block `uncles` do not match.\n" " - num_uncles : {0}\n" " - block uncle_hash : {1}\n" " - header uncle_hash: {2}".format( len(self.uncles), local_uncle_hash, self.header.uncle_hash, )) super(FrontierBlock, self).validate() def validate_uncle(self, uncle): if uncle.block_number >= self.number: raise ValidationError( "Uncle number ({0}) is higher than block number ({1})".format( uncle.block_number, self.number)) try: uncle_parent = self.db.get(uncle.parent_hash) except KeyError: raise ValidationError("Uncle ancestor not found: {0}".format( uncle.parent_hash)) parent_header = rlp.decode(uncle_parent, sedes=BlockHeader) if uncle.block_number != parent_header.block_number + 1: raise ValidationError( "Uncle number ({0}) is not one above ancestor's number ({1})". format(uncle.block_number, parent_header.block_number)) if uncle.timestamp < parent_header.timestamp: raise ValidationError( "Uncle timestamp ({0}) is before ancestor's timestamp ({1})". format(uncle.timestamp, parent_header.timestamp)) if uncle.gas_used > uncle.gas_limit: raise ValidationError( "Uncle's gas usage ({0}) is above the limit ({1})".format( uncle.gas_used, uncle.gas_limit)) # # Helpers # @property def number(self): return self.header.block_number @property def hash(self): return self.header.hash def get_parent_header(self): parent_header = rlp.decode( self.db.get(self.header.parent_hash), sedes=BlockHeader, ) return parent_header # # Transaction class for this block class # transaction_class = FrontierTransaction @classmethod def get_transaction_class(cls): return cls.transaction_class # # Gas Usage API # def get_cumulative_gas_used(self): """ Note return value of this function can be cached based on `self.receipt_db.root_hash` """ if len(self.transactions): return self.receipts[-1].gas_used else: return 0 # # Receipts API # @property def receipts(self): return get_receipts_from_db(self.receipt_db, Receipt) # # Header API # @classmethod def from_header(cls, header, db): """ Returns the block denoted by the given block header. """ if header.uncles_hash == EMPTY_UNCLE_HASH: uncles = [] else: uncles = rlp.decode( db.get(header.uncles_hash), sedes=CountableList(BlockHeader), ) transaction_db = Trie(db, root_hash=header.transaction_root) transactions = get_transactions_from_db(transaction_db, cls.get_transaction_class()) return cls( header=header, transactions=transactions, uncles=uncles, db=db, ) # # Execution API # def add_transaction(self, transaction, computation): logs = [ Log(address, topics, data) for address, topics, data in computation.get_log_entries() ] if computation.error: tx_gas_used = transaction.gas else: gas_remaining = computation.get_gas_remaining() gas_refund = computation.get_gas_refund() tx_gas_used = (transaction.gas - gas_remaining) - min( gas_refund, (transaction.gas - gas_remaining) // 2, ) gas_used = self.header.gas_used + tx_gas_used receipt = Receipt( state_root=self.header.state_root, gas_used=gas_used, logs=logs, ) transaction_idx = len(self.transactions) index_key = rlp.encode(transaction_idx, sedes=rlp.sedes.big_endian_int) self.transactions.append(transaction) self.transaction_db[index_key] = rlp.encode(transaction) self.receipt_db[index_key] = rlp.encode(receipt) self.bloom_filter |= receipt.bloom self.header.transaction_root = self.transaction_db.root_hash self.header.receipt_root = self.receipt_db.root_hash self.header.bloom = int(self.bloom_filter) self.header.gas_used = gas_used return self def add_uncle(self, uncle): self.uncles.append(uncle) self.header.uncles_hash = keccak(rlp.encode(self.uncles)) return self # TODO: Check with Piper what's the use case for having the mine() method allowing callsites # to override header attributes, since the only place it's used we don't pass any kwarg and # hence it just performs block-level validation. def mine(self, **kwargs): """ - `uncles_hash` - `state_root` - `transaction_root` - `receipt_root` - `bloom` - `gas_used` - `extra_data` - `mix_hash` - `nonce` """ if 'uncles' in kwargs: self.uncles = kwargs.pop('uncles') kwargs.setdefault('uncles_hash', keccak(rlp.encode(self.uncles))) header = self.header provided_fields = set(kwargs.keys()) known_fields = set(tuple(zip(*BlockHeader.fields))[0]) unknown_fields = provided_fields.difference(known_fields) if unknown_fields: raise AttributeError( "Unable to set the field(s) {0} on the `BlockHeader` class. " "Received the following unexpected fields: {0}.".format( ", ".join(known_fields), ", ".join(unknown_fields), )) for key, value in kwargs.items(): setattr(header, key, value) # Perform validation self.validate() return self
def deserialize(cls, serial): return cls(block_hashes=CountableList(hash32).deserialize(serial))
class BaseMessage(): """Prefix for our message :\x55 is the multicodec tag for raw based :\x01 is the version number in unsigned varint """ prefix = b'\x55\x01' authenticators_sedes = CountableList(raw) def __init__(self): """Initialization, BaseMessage shall NOT on wire should ONLY be called directly when parsing a frame """ self.verified = False @property: def raw_auth(self): if not self.auth: auth = b'' elif self.use_signature: auth = self.auth else: assert type(message.auth) is list auth = rlp.encode(message.auth, cls.authenticators_sedes) return auth def authenticate(self, node): if self.use_signature: self.auth = node.principal.sign(self.content_digest) else: self.auth = node.gen_authenticators(self.content_digest) return self.auth def verify(self, node, peer_principal): pp = peer_principal if self.verified: pass elif self.use_signature: self.verified = pp.verify(self.content_digest, self.auth) else: if len(node.replica_principals) != len(self.auth): self.verified = False else: assert pp.index == self.sender self.verified = (pp.gen_hmac('in', self.content_digest) == self.auth[node.sender])) return self.verified @property def tag(self): return MessageTag[type(self).__name__] @property def sender_type: if hasattr(self, 'extra'): return 'Replica' if (self.extra >> 4) & 1) else 'Client' else: return 'Replica' @property def frame_head(self): return (self.prefix + self.tag.to_bytes(1, byteorder='big'))
def deserialize(cls, serial): return cls(transactions=CountableList(Transaction).deserialize(serial))
class FrontierBlock(BaseBlock): transaction_class = FrontierTransaction fields = [ ('header', BlockHeader), ('transactions', CountableList(transaction_class)), ('uncles', CountableList(BlockHeader)) ] bloom_filter = None def __init__(self, header, transactions=None, uncles=None): if transactions is None: transactions = [] if uncles is None: uncles = [] self.bloom_filter = BloomFilter(header.bloom) super(FrontierBlock, self).__init__( header=header, transactions=transactions, uncles=uncles, ) # TODO: should perform block validation at this point? # # Helpers # @property def number(self): return self.header.block_number @property def hash(self): return self.header.hash # # Transaction class for this block class # @classmethod def get_transaction_class(cls): return cls.transaction_class # # Receipts API # def get_receipts(self, chaindb): return chaindb.get_receipts(self.header, Receipt) # # Header API # @classmethod def from_header(cls, header, chaindb): """ Returns the block denoted by the given block header. """ if header.uncles_hash == EMPTY_UNCLE_HASH: uncles = [] # type: List[bytes] else: uncles = chaindb.get_block_uncles(header.uncles_hash) transactions = chaindb.get_block_transactions(header, cls.get_transaction_class()) return cls( header=header, transactions=transactions, uncles=uncles, ) # # Execution API # def add_uncle(self, uncle): self.uncles.append(uncle) self.header.uncles_hash = keccak(rlp.encode(self.uncles)) return self
class TerminalBlockHashes(rlp.Serializable): fields = [ ("reqid", big_endian_int), ("hashes", CountableList(hash32)), ]
def serialize(cls, obj): return CountableList(hash32).serialize(obj.block_hashes)
class GetCompactBlocks(rlp.Serializable): fields = [ ("reqid", big_endian_int), ("hashes", CountableList(hash32)), ]
class BlockHeader(rlp.Serializable): fields = [ ("parent_hash", binary), ("height", big_endian_int), ("timestamp", big_endian_int), ("author", binary), ("transactions_root", binary), ("deferred_state_root", binary), ("deferred_receipts_root", binary), ("deferred_logs_bloom_hash", binary), ("blame", big_endian_int), ("difficulty", big_endian_int), ("adaptive", big_endian_int), ("gas_limit", big_endian_int), ("referee_hashes", CountableList(binary)), ("nonce", big_endian_int), ] def __init__( self, parent_hash=default_config['GENESIS_PREVHASH'], height=0, timestamp=0, author=default_config['GENESIS_COINBASE'], transactions_root=trie.BLANK_ROOT, deferred_state_root=sha3(rlp.encode(trie.state_root())), deferred_receipts_root=trie.BLANK_ROOT, deferred_logs_bloom_hash=default_config['GENESIS_LOGS_BLOOM_HASH'], blame=0, difficulty=default_config['GENESIS_DIFFICULTY'], gas_limit=0, referee_hashes=[], adaptive=0, nonce=0): # at the beginning of a method, locals() is a dict of all arguments fields = { k: v for k, v in locals().items() if k not in ['self', '__class__'] } self.block = None super(BlockHeader, self).__init__(**fields) @property def hash(self): return sha3(rlp.encode(self.rlp_part())) def get_hex_hash(self): return eth_utils.encode_hex(self.hash) def problem_hash(self): return sha3(rlp.encode(self.without_nonce())) def pow_decimal(self): return bytes_to_int(sha3(rlp.encode([self.problem_hash(), self.nonce]))) def without_nonce(self): fields = { field: getattr(self, field) for field in BlockHeaderWithoutNonce._meta.field_names } return BlockHeaderWithoutNonce(**fields) def rlp_part(self): fields = { field: getattr(self, field) for field in BlockHeaderRlpPart._meta.field_names } return BlockHeaderRlpPart(**fields)
class GetCompactBlocksResponse(rlp.Serializable): fields = [("reqid", big_endian_int), ("compact_blocks", CountableList(CompactBlock)), ("blocks", CountableList(Block))]
class FrontierBlock(BaseBlock): transaction_builder = FrontierTransaction receipt_builder = Receipt fields = [ ('header', BlockHeader), ('transactions', CountableList(transaction_builder)), ('uncles', CountableList(BlockHeader)) ] bloom_filter = None def __init__(self, header: BlockHeaderAPI, transactions: Sequence[SignedTransactionAPI] = None, uncles: Sequence[BlockHeaderAPI] = None) -> None: if transactions is None: transactions = [] if uncles is None: uncles = [] self.bloom_filter = BloomFilter(header.bloom) super().__init__( header=header, transactions=transactions, uncles=uncles, ) # TODO: should perform block validation at this point? # # Helpers # @property def number(self) -> BlockNumber: return self.header.block_number @property def hash(self) -> Hash32: return self.header.hash # # Transaction class for this block class # @classmethod def get_transaction_builder(cls) -> Type[TransactionBuilderAPI]: return cls.transaction_builder @classmethod def get_receipt_builder(cls) -> Type[ReceiptBuilderAPI]: return cls.receipt_builder # # Receipts API # def get_receipts(self, chaindb: ChainDatabaseAPI) -> Tuple[ReceiptAPI, ...]: return chaindb.get_receipts(self.header, self.get_receipt_builder()) # # Header API # @classmethod def from_header(cls, header: BlockHeaderAPI, chaindb: ChainDatabaseAPI) -> "FrontierBlock": """ Returns the block denoted by the given block header. :raise eth.exceptions.BlockNotFound: if transactions or uncle headers are missing """ if header.uncles_hash == EMPTY_UNCLE_HASH: uncles: Tuple[BlockHeaderAPI, ...] = () else: try: uncles = chaindb.get_block_uncles(header.uncles_hash) except HeaderNotFound as exc: raise BlockNotFound(f"Uncles not found in database for {header}: {exc}") from exc try: transactions = chaindb.get_block_transactions(header, cls.get_transaction_builder()) except MissingTrieNode as exc: raise BlockNotFound(f"Transactions not found in database for {header}: {exc}") from exc return cls( header=header, transactions=transactions, uncles=uncles, )