def _sign_edit_validator(transaction_dict, private_key): """ Sign an edit validator transaction See sign_staking_transaction for details """ # preliminary steps if transaction_dict['directive'] != Directive.EditValidator: raise TypeError('Only EditValidator is supported by _sign_create_or_edit_validator') # first common step account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) # encode the stakeMsg description = [ sanitized_transaction.pop('name'), sanitized_transaction.pop('identity'), sanitized_transaction.pop('website'), sanitized_transaction.pop('security-contact'), sanitized_transaction.pop('details'), ] sanitized_transaction['stakeMsg'] = \ apply_formatters_to_sequence( [ hexstr_if_str(to_bytes), # address identity, # description identity, # new rate (it's in a list so can't do hexstr_if_str) hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped hexstr_if_str(to_bytes), # key to remove hexstr_if_str(to_bytes), # key to add ], [ convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), description, [ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')) ], math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly math.floor(sanitized_transaction.pop('max-total-delegation')), sanitized_transaction.pop('bls-key-to-remove'), sanitized_transaction.pop('bls-key-to-add') ] ) return _sign_transaction_generic(account, sanitized_transaction, EditValidator)
def _sign_collect_rewards(transaction_dict, private_key): """ Sign a collect rewards transaction See sign_staking_transaction for details """ # preliminary steps if transaction_dict['directive'] != Directive.CollectRewards: raise TypeError('Only CollectRewards is supported by _sign_collect_rewards') # first common step account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) # encode the stakeMsg sanitized_transaction['stakeMsg'] = \ [hexstr_if_str(to_bytes)(convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')))] return _sign_transaction_generic(account, sanitized_transaction, CollectRewards)
def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): """ Sign a delegate or undelegate transaction See sign_staking_transaction for details """ # preliminary steps if transaction_dict['directive'] not in [ Directive.Delegate, Directive.Undelegate ]: raise TypeError('Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate') # first common step account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) # encode the stakeMsg sanitized_transaction['stakeMsg'] = \ apply_formatters_to_sequence( [ hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int) ], [ convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')), convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), sanitized_transaction.pop('amount'), ] ) return _sign_transaction_generic(account, sanitized_transaction, DelegateOrUndelegate)
def _recover_hash(self, message_hash, vrs=None, signature=None): hash_bytes = HexBytes(message_hash) if len(hash_bytes) != 32: raise ValueError("The message hash must be exactly 32-bytes") if vrs is not None: v, r, s = map(hexstr_if_str(to_int), vrs) v_standard = to_standard_v(v) signature_obj = self._keys.Signature(vrs=(v_standard, r, s)) elif signature is not None: signature_bytes = HexBytes(signature) signature_bytes_standard = to_standard_signature_bytes(signature_bytes) signature_obj = self._keys.Signature(signature_bytes=signature_bytes_standard) else: raise TypeError("You must supply the vrs tuple or the signature bytes") pubkey = signature_obj.recover_public_key_from_msg_hash(hash_bytes) return pubkey.to_checksum_address()
def from_dict(cls, dictionary: Dict[str, Any]): """Builds a TypedTransaction from a dictionary. Verifies the dictionary is well formed.""" dictionary = set_transaction_type_if_needed(dictionary) if not ('type' in dictionary and is_int_or_prefixed_hexstr(dictionary['type'])): raise ValueError("missing or incorrect transaction type") # Switch on the transaction type to choose the correct constructor. transaction_type = pipe(dictionary['type'], hexstr_if_str(to_int)) transaction: Any if transaction_type == AccessListTransaction.transaction_type: transaction = AccessListTransaction elif transaction_type == DynamicFeeTransaction.transaction_type: transaction = DynamicFeeTransaction else: raise TypeError("Unknown Transaction type: %s" % transaction_type) return cls( transaction_type=transaction_type, transaction=transaction.from_dict(dictionary), )
def _sign_create_validator(transaction_dict, private_key): """ Sign a create validator transaction See sign_staking_transaction for details """ # preliminary steps if transaction_dict['directive'] != Directive.CreateValidator: raise TypeError('Only CreateValidator is supported by _sign_create_or_edit_validator') # first common step account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) # encode the stakeMsg description = [ sanitized_transaction.pop('name'), sanitized_transaction.pop('identity'), sanitized_transaction.pop('website'), sanitized_transaction.pop('security-contact'), sanitized_transaction.pop('details'), ] commission = apply_formatter_to_array( hexstr_if_str(to_int), # formatter [ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')), _convert_staking_percentage_to_number(sanitized_transaction.pop('max-rate')), _convert_staking_percentage_to_number(sanitized_transaction.pop('max-change-rate')), ] ) commission = [ [element] for element in commission ] bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter sanitized_transaction.pop('bls-public-keys') ) sanitized_transaction['stakeMsg'] = \ apply_formatters_to_sequence( [ hexstr_if_str(to_bytes), # address identity, # description identity, # commission rates hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped identity, # bls public keys hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) ], [ convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), description, commission, math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly math.floor(sanitized_transaction.pop('max-total-delegation')), bls_keys, math.floor(sanitized_transaction.pop('amount')), ] ) return _sign_transaction_generic(account, sanitized_transaction, CreateValidator)
return False def is_none(val): return val is None TRANSACTION_DEFAULTS = { 'to': b'', 'value': 0, 'data': b'', } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gas': hexstr_if_str(to_int), 'to': apply_one_of_formatters(( (is_string, hexstr_if_str(to_bytes)), (is_bytes, identity), (is_none, lambda val: b''), )), 'value': hexstr_if_str(to_int), 'data': hexstr_if_str(to_bytes), 'v':
return 0 else: return int(value, 16) else: return int(value) @functools.lru_cache(maxsize=128) def normalize_to_address(value: AnyStr) -> Address: if value: return to_canonical_address(value) else: return CREATE_CONTRACT_ADDRESS robust_decode_hex = hexstr_if_str(to_bytes) # # Containers # def dict_normalizer(formatters: Dict[Any, Callable[..., Any]], required: Iterable[Any]=None, optional: Iterable[Any]=None) -> Normalizer: all_keys = set(formatters.keys()) if required is None and optional is None: required_set_form = all_keys elif required is not None and optional is not None: raise ValueError("Both required and optional keys specified")
def _mk_raw_params(**kwargs): return { 'genesis': merge(PARAMS_DEFAULTS, valmap(hexstr_if_str(to_hex), kwargs)), }
from eth_account._utils.signing import (sign_transaction_hash) from eth_account._utils.transactions import ( Transaction as SignedEthereumTxData, UnsignedTransaction as UnsignedEthereumTxData, TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, TRANSACTION_DEFAULTS, chain_id_to_v, UNSIGNED_TRANSACTION_FIELDS) from cytoolz import (dissoc, pipe, merge, partial) from eth_account.datastructures import (SignedTransaction) from .util import (chain_id_to_int, convert_one_to_hex) HARMONY_FORMATTERS = dict( ETHEREUM_FORMATTERS, shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction toShardID=hexstr_if_str(to_int), # which may be cross shard ) class UnsignedHarmonyTxData(HashableRLP): fields = ( ('nonce', big_endian_int), ('gasPrice', big_endian_int), ('gas', big_endian_int), ('shardID', big_endian_int), ('toShardID', big_endian_int), ('to', Binary.fixed_length(20, allow_empty=True)), ('value', big_endian_int), ('data', binary), )
def recoverHash(self, message_hash, vrs=None, signature=None): ''' Get the address of the account that signed the message with the given hash. You must specify exactly one of: vrs or signature :param message_hash: the hash of the message that you want to verify :type message_hash: hex str or bytes or int :param vrs: the three pieces generated by an elliptic curve signature :type vrs: tuple(v, r, s), each element is hex str, bytes or int :param signature: signature bytes concatenated as r+s+v :type signature: hex str or bytes or int :returns: address of signer, hex-encoded & checksummed :rtype: str .. code-block:: python >>> msg = "I♥SF" >>> msghash = '0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750' >>> vrs = ( 28, '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recoverHash(msghash, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' # All of these recover calls are equivalent: # variations on msghash >>> msghash = b"\\x14v\\xab\\xb7E\\xd4#\\xbf\\t'?\\x1a\\xfd\\x88}\\x95\\x11\\x81\\xd2Z\\xdcf\\xc4\\x83JpI\\x19\\x11\\xb7\\xf7P" # noqa: E501 >>> Account.recoverHash(msghash, vrs=vrs) >>> msghash = 0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750 >>> Account.recoverHash(msghash, vrs=vrs) # variations on vrs >>> vrs = ( '0x1c', '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recoverHash(msghash, vrs=vrs) >>> vrs = ( b'\\x1c', b'\\xe6\\xca\\x9b\\xbaX\\xc8\\x86\\x11\\xfa\\xd6jl\\xe8\\xf9\\x96\\x90\\x81\\x95Y8\\x07\\xc4\\xb3\\x8b\\xd5(\\xd2\\xcf\\xf0\\x9dN\\xb3', # noqa: E501 b'>[\\xfb\\xbfM>9\\xb1\\xa2\\xfd\\x81jv\\x80\\xc1\\x9e\\xbe\\xba\\xf3\\xa1A\\xb29\\x93J\\xd4<\\xb3?\\xce\\xc8\\xce') # noqa: E501 >>> Account.recoverHash(msghash, vrs=vrs) >>> vrs = ( 0x1c, 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3, 0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce) >>> Account.recoverHash(msghash, vrs=vrs) # variations on signature >>> signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c' # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) >>> signature = b'\\xe6\\xca\\x9b\\xbaX\\xc8\\x86\\x11\\xfa\\xd6jl\\xe8\\xf9\\x96\\x90\\x81\\x95Y8\\x07\\xc4\\xb3\\x8b\\xd5(\\xd2\\xcf\\xf0\\x9dN\\xb3>[\\xfb\\xbfM>9\\xb1\\xa2\\xfd\\x81jv\\x80\\xc1\\x9e\\xbe\\xba\\xf3\\xa1A\\xb29\\x93J\\xd4<\\xb3?\\xce\\xc8\\xce\\x1c' # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) >>> signature = 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) ''' hash_bytes = HexBytes(message_hash) if len(hash_bytes) != 32: raise ValueError("The message hash must be exactly 32-bytes") if vrs is not None: v, r, s = map(hexstr_if_str(to_int), vrs) v_standard = to_standard_v(v) signature_obj = self._keys.Signature(vrs=(v_standard, r, s)) elif signature is not None: signature_bytes = HexBytes(signature) signature_bytes_standard = to_standard_signature_bytes( signature_bytes) signature_obj = self._keys.Signature( signature_bytes=signature_bytes_standard) else: raise TypeError( "You must supply the vrs tuple or the signature bytes") pubkey = signature_obj.recover_public_key_from_msg_hash(hash_bytes) return pubkey.to_checksum_address()
from .transaction_utils import ( set_transaction_type_if_needed, transaction_rlp_to_rpc_structure, transaction_rpc_to_rlp_structure, ) from .validation import ( LEGACY_TRANSACTION_FORMATTERS, LEGACY_TRANSACTION_VALID_VALUES, is_int_or_prefixed_hexstr, is_rpc_structured_access_list, ) TYPED_TRANSACTION_FORMATTERS = merge( LEGACY_TRANSACTION_FORMATTERS, { 'chainId': hexstr_if_str(to_int), 'type': hexstr_if_str(to_int), 'accessList': apply_formatter_to_array( apply_formatters_to_dict( { "address": apply_one_of_formatters(( (is_string, hexstr_if_str(to_bytes)), (is_bytes, identity), )), "storageKeys": apply_formatter_to_array(hexstr_if_str(to_int)) } ), ), 'maxPriorityFeePerGas': hexstr_if_str(to_int), 'maxFeePerGas': hexstr_if_str(to_int), },
def encode_transaction(unsigned_transaction, vrs): (v, r, s) = vrs chain_naive_transaction = dissoc(vars(unsigned_transaction), 'v', 'r', 's') signed_transaction = Transaction(v=v, r=r, s=s, **chain_naive_transaction) return rlp.encode(signed_transaction) TRANSACTION_DEFAULTS = { 'value': 0, 'data': b'', } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gas': hexstr_if_str(to_int), 'to': hexstr_if_str(to_bytes), 'value': hexstr_if_str(to_int), 'data': hexstr_if_str(to_bytes), 'v': hexstr_if_str(to_int), 'r': hexstr_if_str(to_int), 's': hexstr_if_str(to_int), } def chain_id_to_v(transaction_dict): # See EIP 155 chain_id = transaction_dict.pop('chainId') if chain_id is None:
return False def is_none(val): return val is None TRANSACTION_DEFAULTS = { 'to': b'', 'value': 0, 'data': b'', 'chainId': None, } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gas': hexstr_if_str(to_int), 'to': apply_one_of_formatters(( (is_string, hexstr_if_str(to_bytes)), (is_bytes, identity), (is_none, lambda val: b''), )), 'value': hexstr_if_str(to_int), 'data': hexstr_if_str(to_bytes), 'v': hexstr_if_str(to_int), 'r': hexstr_if_str(to_int), 's': hexstr_if_str(to_int), } TRANSACTION_VALID_VALUES = {
class Directive( Enum ): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 def _generate_next_value_(name, start, count, last_values): return count CreateValidator = auto() EditValidator = auto() Delegate = auto() Undelegate = auto() CollectRewards = auto() FORMATTERS = { 'directive': hexstr_if_str( to_int), # delegatorAddress is already formatted before the call 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gasLimit': hexstr_if_str(to_int), 'chainId': hexstr_if_str(to_int), } class CollectRewards: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( ('directive', big_endian_int), ('stakeMsg', CountableList(Binary.fixed_length(20, allow_empty=True))),
return 0 else: return int(value, 16) else: return int(value) @functools.lru_cache(maxsize=128) def normalize_to_address(value): if value: return to_canonical_address(value) else: return CREATE_CONTRACT_ADDRESS robust_decode_hex = hexstr_if_str(to_bytes) # # Pytest fixture generation # def idfn(fixture_params): """ Function for pytest to produce uniform names for fixtures. """ return ":".join((str(item) for item in fixture_params)) def get_fixtures_file_hash(all_fixture_paths): """ Returns the MD5 hash of the fixture files. Used for cache busting.