def validate(self): """ Hook called during instantiation to ensure that all transaction parameters pass validation rules. """ if self.intrinsic_gas > self.gas: raise ValidationError("Insufficient gas") self.check_signature_validity()
def validate_transaction_signature(transaction: BaseTransaction) -> None: if is_eip_155_signed_transaction(transaction): v = extract_signature_v(transaction.v) else: v = transaction.v canonical_v = v - 27 vrs = (canonical_v, transaction.r, transaction.s) signature = keys.Signature(vrs=vrs) message = transaction.get_message_for_signing() try: public_key = signature.recover_public_key_from_msg(message) except BadSignature as e: raise ValidationError("Bad Signature: {0}".format(str(e))) if not signature.verify_msg(message, public_key): raise ValidationError("Invalid Signature")
def validate_header_params_for_configuration(header_params): extra_fields = set(header_params.keys()).difference(ALLOWED_HEADER_FIELDS) if extra_fields: raise ValidationError( "The `configure_header` method may only be used with the fields ({0}). " "The provided fields ({1}) are not supported".format( ", ".join(tuple(sorted(ALLOWED_HEADER_FIELDS))), ", ".join(tuple(sorted(extra_fields))), ))
def validate_length(value, length): if not len(value) == length: raise ValidationError( "Value must be of length {0}. Got {1} of length {2}".format( length, value, len(value), ) )
def validate_length(value, length, title="Value"): if not len(value) == length: raise ValidationError( "{title} must be of length {0}. Got {1} of length {2}".format( length, value, len(value), title=title, ))
def validate_gte(value, minimum, title="Value"): if value < minimum: raise ValidationError( "{title} {0} is not greater than or equal to {1}".format( value, minimum, title=title, )) validate_is_integer(value)
def validate_length_lte(value, maximum_length): if len(value) > maximum_length: raise ValidationError( "Value must be of length less than or equal to {0}. " "Got {1} of length {2}".format( maximum_length, value, len(value), ))
def validate_lte(value, maximum, title="Value"): if value > maximum: raise ValidationError( "{title} {0} is not less than or equal to {1}".format( value, maximum, title=title, )) validate_is_integer(value, title=title)
def validate_frontier_transaction_against_header(_vm, base_header, transaction): if base_header.gas_used + transaction.gas > base_header.gas_limit: raise ValidationError( "Transaction exceeds gas limit: using {}, bringing total to {}, but limit is {}".format( transaction.gas, base_header.gas_used + transaction.gas, base_header.gas_limit, ) )
def validate(self): if not self.is_genesis: parent_header = self.get_parent_header() # 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( "Block timestamp is equal to the parent block's timestamp") super(FrontierBlock, self).validate()
def validate_chain(self, chain: Tuple[BlockHeader, ...]) -> None: parent = self.chaindb.get_block_header_by_hash(chain[0].parent_hash) for header in chain: if header.parent_hash != parent.hash: raise ValidationError( "Invalid header chain; {} has parent {}, but expected {}". format(header, header.parent_hash, parent.hash)) vm_class = self.get_vm_class_for_block_number(header.block_number) vm_class.validate_header(header, parent) parent = header
def validate_transaction_access_list(access_list, title="Access List"): for item in access_list: if len(item) == 0: raise ValidationError( "{0} entry must at least specify an account address.".format( title)) address, *prefixes = item validate_canonical_address(address, title="Address in {0}".format(title)) for prefix in prefixes: validate_is_bytes(prefix, title="Storage prefix in {0}".format(title)) if len(prefix) > 32: raise ValidationError( "Storage prefix in {0} must be 32 bytes or shorter. Got: {1}" .format( title, prefix, ))
def get_parent_chain(self, block): try: parent_header = self.get_block_header_by_hash( block.header.parent_hash) except BlockNotFound: raise ValidationError("Parent ({0}) of block {1} not found".format( block.header.parent_hash, block.header.hash)) init_header = self.create_header_from_parent(parent_header) return type(self)(self.db, init_header)
def check_shard_id( shard: Shard, header_or_collation: Union[CollationHeader, Collation]) -> None: if header_or_collation.shard_id != shard.shard_id: raise ValidationError( "Header or collation belongs to shard {} instead of shard {}". format( header_or_collation.shard_id, shard.shard_id, ))
def validate_frontier_transaction(state, transaction): gas_cost = transaction.gas * transaction.gas_price sender_balance = state.account_db.get_balance(transaction.sender) if sender_balance < gas_cost: raise ValidationError( "Sender account balance cannot afford txn gas: `{0}`".format( transaction.sender)) total_cost = transaction.value + gas_cost if sender_balance < total_cost: raise ValidationError("Sender account balance cannot afford txn") if state.gas_used + transaction.gas > state.gas_limit: raise ValidationError("Transaction exceeds gas limit") if state.account_db.get_nonce(transaction.sender) != transaction.nonce: raise ValidationError("Invalid transaction nonce")
def validate_transaction(self, transaction): # Validate the transaction if transaction.intrinsic_gas > transaction.gas: raise ValidationError("Insufficient gas") transaction.validate() self.vm_state.validate_transaction(transaction) return transaction
def validate_uncle(self, block, uncle, uncle_parent): """ Validate the given uncle in the context of the given block. """ if uncle.block_number >= block.number: raise ValidationError( "Uncle number ({0}) is higher than block number ({1})".format( uncle.block_number, block.number)) if uncle.block_number != uncle_parent.block_number + 1: raise ValidationError( "Uncle number ({0}) is not one above ancestor's number ({1})".format( uncle.block_number, uncle_parent.block_number)) if uncle.timestamp < uncle_parent.timestamp: raise ValidationError( "Uncle timestamp ({0}) is before ancestor's timestamp ({1})".format( uncle.timestamp, uncle_parent.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))
def check_pow(block_number, mining_hash, mix_hash, nonce, difficulty): validate_length(mix_hash, 32) validate_length(mining_hash, 32) validate_length(nonce, 8) cache = get_cache(block_number) mining_output = hashimoto_light(block_number, cache, mining_hash, big_endian_to_int(nonce)) if mining_output[b'mix digest'] != mix_hash: raise ValidationError("mix hash mistmatch; {0} != {1}".format( encode_hex(mining_output[b'mix digest']), encode_hex(mix_hash))) result = big_endian_to_int(mining_output[b'result']) validate_lte(result, 2**256 // difficulty)
async def _validate_header(self, header): if header.is_genesis: raise ValidationError("Peer sent a genesis header {} that we didn't ask for".format( header, )) else: async_header = self.headerdb.coro_get_block_header_by_hash(header.parent_hash) parent_header = await self.wait(async_header) VM = self.chain_class.get_vm_class_for_block_number(header.block_number) # TODO push validation into process pool executor VM.validate_header(header, parent_header)
def refund_gas(self, amount): if amount < 0: raise ValidationError("Gas refund amount must be positive") self.gas_refunded += amount self.logger.trace( 'GAS REFUND: %s + %s -> %s', self.gas_refunded - amount, amount, self.gas_refunded, )
def validate_frontier_transaction(vm, transaction): gas_cost = transaction.gas * transaction.gas_price with vm.state.state_db(read_only=True) as state_db: sender_balance = state_db.get_balance(transaction.sender) if sender_balance < gas_cost: raise ValidationError( "Sender account balance cannot afford txn gas: `{0}`".format( transaction.sender)) total_cost = transaction.value + gas_cost if sender_balance < total_cost: raise ValidationError("Sender account balance cannot afford txn") if vm.block.header.gas_used + transaction.gas > vm.block.header.gas_limit: raise ValidationError("Transaction exceeds gas limit") with vm.state.state_db(read_only=True) as state_db: if state_db.get_nonce(transaction.sender) != transaction.nonce: raise ValidationError("Invalid transaction nonce")
def return_gas(self, amount): if amount < 0: raise ValidationError("Gas return amount must be positive") self.gas_remaining += amount self.logger.trace( 'GAS RETURNED: %s + %s -> %s', self.gas_remaining - amount, amount, self.gas_remaining, )
def check_pow(block_number: int, mining_hash: Hash32, mix_hash: Hash32, nonce: bytes, difficulty: int) -> None: validate_length(mix_hash, 32, title="Mix Hash") validate_length(mining_hash, 32, title="Mining Hash") validate_length(nonce, 8, title="POW Nonce") cache = get_cache(block_number) mining_output = hashimoto_light(block_number, cache, mining_hash, big_endian_to_int(nonce)) if mining_output[b'mix digest'] != mix_hash: raise ValidationError("mix hash mismatch; {0} != {1}".format( encode_hex(mining_output[b'mix digest']), encode_hex(mix_hash))) result = big_endian_to_int(mining_output[b'result']) validate_lte(result, 2**256 // difficulty, title="POW Difficulty")
def validate_uncles(self, block: BaseBlock) -> None: """ Validate the uncles for the given block. """ # Check for duplicates uncle_groups = groupby(operator.attrgetter('hash'), block.uncles) duplicate_uncles = tuple(sorted( hash for hash, twins in uncle_groups.items() if len(twins) > 1 )) if duplicate_uncles: raise ValidationError( "Block contains duplicate uncles:\n" " - {0}".format(' - '.join(duplicate_uncles)) ) recent_ancestors = tuple( ancestor for ancestor in self.get_ancestors(MAX_UNCLE_DEPTH + 1, header=block.header) ) recent_ancestor_hashes = {ancestor.hash for ancestor in recent_ancestors} recent_uncle_hashes = _extract_uncle_hashes(recent_ancestors) for uncle in block.uncles: if uncle.hash == block.hash: raise ValidationError("Uncle has same hash as block") # ensure the uncle has not already been included. if uncle.hash in recent_uncle_hashes: raise ValidationError( "Duplicate uncle: {0}".format(encode_hex(uncle.hash)) ) # ensure that the uncle is not one of the canonical chain blocks. if uncle.hash in recent_ancestor_hashes: raise ValidationError( "Uncle {0} cannot be an ancestor of {1}".format( encode_hex(uncle.hash), encode_hex(block.hash))) # ensure that the uncle was built off of one of the canonical chain # blocks. if uncle.parent_hash not in recent_ancestor_hashes or ( uncle.parent_hash == block.header.parent_hash): raise ValidationError( "Uncle's parent {0} is not an ancestor of {1}".format( encode_hex(uncle.parent_hash), encode_hex(block.hash))) # Now perform VM level validation of the uncle self.validate_seal(uncle) try: uncle_parent = self.get_block_header_by_hash(uncle.parent_hash) except HeaderNotFound: raise ValidationError( "Uncle ancestor not found: {0}".format(uncle.parent_hash) ) uncle_vm_class = self.get_vm_class_for_block_number(uncle.block_number) uncle_vm_class.validate_uncle(block, uncle, uncle_parent)
def validate_uncle(self, block, uncle): if uncle.block_number >= block.number: raise ValidationError( "Uncle number ({0}) is higher than block number ({1})".format( uncle.block_number, block.number)) try: parent_header = get_block_header_by_hash(uncle.parent_hash, self.chaindb) except BlockNotFound: raise ValidationError( "Uncle ancestor not found: {0}".format(uncle.parent_hash)) 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))
def get_chain_at_block_parent(self, block: BaseBlock) -> BaseChain: """ Returns a `Chain` instance with the given block's parent at the chain head. """ try: parent_header = self.get_block_header_by_hash( block.header.parent_hash) except HeaderNotFound: raise ValidationError("Parent ({0}) of block {1} not found".format( block.header.parent_hash, block.header.hash)) init_header = self.create_header_from_parent(parent_header) return type(self)(self.chaindb.db, init_header)
def validate_unique(values): if not isdistinct(values): duplicates = pipe( values, frequencies, # get the frequencies partial(valfilter, lambda v: v > 1), # filter to ones that occure > 1 sorted, # sort them tuple, # cast them to an immutiable form ) raise ValidationError( "The values provided are not unique. Duplicates: {0}".format( ', '.join((str(value) for value in duplicates))))
def _process_point(data_buffer, exponent): x1, y1, x2_i, x2_r, y2_i, y2_r = _extract_point(data_buffer) p1 = validate_point(x1, y1) for v in (x2_i, x2_r, y2_i, y2_r): if v >= bn128.field_modulus: raise ValidationError("value greater than field modulus") fq2_x = bn128.FQ2([x2_r, x2_i]) fq2_y = bn128.FQ2([y2_r, y2_i]) if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()): p2 = (fq2_x, fq2_y, bn128.FQ2.one()) if not bn128.is_on_curve(p2, bn128.b2): raise ValidationError("point is not on curve") else: p2 = ZERO if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero(): raise ValidationError("TODO: what case is this?????") return exponent * bn128.pairing(p2, p1, final_exponentiate=False)
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))
async def fetch_headers(self, start_block: int, peer: LESPeer) -> List[BlockHeader]: if start_block == GENESIS_BLOCK_NUMBER: raise ValidationError("Must not attempt to download genesis header") for i in range(self.max_consecutive_timeouts): try: return await self._fetch_headers_starting_at(peer, start_block) except TimeoutError: self.logger.info( "Timeout when fetching headers from %s (attempt %d of %d)", peer, i + 1, self.max_consecutive_timeouts) # TODO: Figure out what's a good value to use here. await asyncio.sleep(0.5) raise TooManyTimeouts()