def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) # Enforce non-tightly-packed arguments for signing # (0x00 left pad) # https://github.com/ethereum/web3.py/issues/466 v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r_bytes = pad_left(r_bytes, 32, b"\0") r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s_bytes = pad_left(s_bytes, 32, b"\0") s = big_endian_to_int(s_bytes) # Make sure we use bytes data and zero padding stays # good across different systems r_hex = binascii.hexlify(r_bytes).decode("ascii") s_hex = binascii.hexlify(s_bytes).decode("ascii") # Convert to Etheruem address format addr = utils.big_endian_to_int( utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) # Return various bits about signing so it's easier to debug return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "r_hex": "0x" + r_hex, "s_hex": "0x" + s_hex, "address_bitcoin": addr, "address_ethereum": get_ethereum_address_from_private_key(priv), "public_key": pub, "hash": msghash, "payload": binascii.hexlify(bytes([v] + list(r_bytes) + list(s_bytes, ))) }
def sender(self): if not self._sender: if not is_bitcoin_available() or not is_secp256k1_available(): raise ImportError( "In order to derive the sender for transactions the " "`bitcoin` and `secp256k1` packages must be installed." ) from bitcoin import N from secp256k1 import PublicKey, ALL_FLAGS # Determine sender if self.v: has_invalid_signature_values = ( self.r >= N or self.s >= N or self.v < 27 or self.v > 28 or self.r == 0 or self.s == 0 ) if has_invalid_signature_values: raise ValueError("Invalid signature values!") rlpdata = rlp.encode(self, UnsignedTransaction) rawhash = keccak(rlpdata) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( pad_left( int_to_big_endian(self.r), 32, b'\x00', ) + pad_left( int_to_big_endian(self.s), 32, b'\x00', ), self.v - 27 ), raw=True ) pub = pk.serialize(compressed=False) except Exception: raise ValueError("Invalid signature values (x^3+7 is non-residue)") if pub[1:] == b"\x00" * (len(pub) - 1): raise ValueError("Invalid signature (zero privkey cannot sign)") self._sender = to_normalized_address(keccak(pub[1:])[-20:]) assert self.sender == self._sender else: self._sender = 0 return self._sender
def sender(self): if not self._sender: if not is_bitcoin_available() or not is_secp256k1_available(): raise ImportError( "In order to derive the sender for transactions the " "`bitcoin` and `secp256k1` packages must be installed.") from bitcoin import N from secp256k1 import PublicKey, ALL_FLAGS # Determine sender if self.v: has_invalid_signature_values = (self.r >= N or self.s >= N or self.v < 27 or self.v > 28 or self.r == 0 or self.s == 0) if has_invalid_signature_values: raise ValueError("Invalid signature values!") rlpdata = rlp.encode(self, UnsignedTransaction) rawhash = keccak(rlpdata) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( pad_left( int_to_big_endian(self.r), 32, b'\x00', ) + pad_left( int_to_big_endian(self.s), 32, b'\x00', ), self.v - 27), raw=True) pub = pk.serialize(compressed=False) except Exception: raise ValueError( "Invalid signature values (x^3+7 is non-residue)") if pub[1:] == b"\x00" * (len(pub) - 1): raise ValueError( "Invalid signature (zero privkey cannot sign)") self._sender = to_normalized_address(keccak(pub[1:])[-20:]) assert self.sender == self._sender else: self._sender = 0 return self._sender
def get_default_account_keys(): keys = KeyAPI() for i in range(1, 11): pk_bytes = pad_left(int_to_big_endian(i), 32, b'\x00') private_key = keys.PrivateKey(pk_bytes) yield private_key
def main(**kwargs): project = Project() chain_name = kwargs['chain'] owner = kwargs['owner'] challenge_period = kwargs['challenge_period'] supply = kwargs['supply'] token_name = kwargs['token_name'] token_decimals = kwargs['token_decimals'] token_symbol = kwargs['token_symbol'] token_address = kwargs['token_address'] supply *= 10**(token_decimals) txn_wait = 250 assert challenge_period >= 500, 'Challenge period should be >= 500 blocks' if chain_name == 'rinkeby': txn_wait = 500 print( '''Make sure {} chain is running, you can connect to it and it is synced, or you'll get timeout'''.format(chain_name)) with project.get_chain(chain_name) as chain: web3 = chain.web3 print('Web3 provider is', web3.currentProvider) owner = owner or web3.eth.accounts[0] assert owner assert web3.eth.getBalance( owner) > 0, 'Account with insuficient funds.' print('Owner is', owner) token = chain.provider.get_contract_factory('CustomToken') if not token_address: txhash = token.deploy( args=[supply, token_name, token_symbol, token_decimals], transaction={'from': owner}) receipt = check_succesful_tx(chain.web3, txhash, txn_wait) token_address = receipt['contractAddress'] print(token_name, 'address is', token_address) microraiden_contract = chain.provider.get_contract_factory( 'RaidenMicroTransferChannels') txhash = microraiden_contract.deploy( args=[token_address, challenge_period]) receipt = check_succesful_tx(chain.web3, txhash, txn_wait) microraiden_address = receipt['contractAddress'] print('RaidenMicroTransferChannels address is', microraiden_address) abi_encoded_args = encode_hex( pad_left(pack(remove_0x_prefix(token_address), challenge_period), 128, '0')) print('RaidenMicroTransferChannels arguments', token_address, challenge_period) print('RaidenMicroTransferChannels abi encoded constructor arguments:', abi_encoded_args)
def robust_decode_hex(value): unprefixed_value = remove_0x_prefix(value) if len(unprefixed_value) % 2: return decode_hex( pad_left(unprefixed_value, len(unprefixed_value) + 1, b'0')) else: return decode_hex(unprefixed_value)
def getTokens(**kwargs): project = Project() # print(kwargs) chain_name = kwargs['chain'] owner = kwargs['owner'] challenge_period = kwargs['challenge_period'] supply = kwargs['supply'] senders = kwargs['senders'] sender_addresses = kwargs['sender_addresses'].split(',') token_name = kwargs['token_name'] token_decimals = kwargs['token_decimals'] token_symbol = kwargs['token_symbol'] token_address = kwargs['token_address'] supply *= 10**(token_decimals) token_assign = int(supply / (len(sender_addresses) + senders)) txn_wait = 250 event_wait = 50 if chain_name == 'rinkeby': txn_wait = 500 event_wait = 500 print( "Make sure {} chain is running, you can connect to it and it is synced, or you'll get timeout" .format(chain_name)) with project.get_chain(chain_name) as chain: web3 = chain.web3 owner = owner or web3.eth.accounts[0] print('Web3 provider is', web3.currentProvider) if not token_address: token = chain.provider.get_contract_factory('ERC223Token') txhash = token.deploy( args=[supply, token_name, token_decimals, token_symbol], transaction={'from': owner}) receipt = check_succesful_tx(chain.web3, txhash, txn_wait) token_address = receipt['contractAddress'] print(token_name, ' address is', token_address) channel_factory = chain.provider.get_contract_factory( 'RaidenMicroTransferChannels') txhash = channel_factory.deploy(args=[token_address, challenge_period]) print('RaidenMicroTransferChannels arguments', token_address, challenge_period) padded_token_address = pad_left(remove_0x_prefix(token_address), 64, '0') print('RaidenMicroTransferChannels abi encoded arguments:', encode_hex(pack(padded_token_address, challenge_period))) receipt = check_succesful_tx(chain.web3, txhash, txn_wait) cf_address = receipt['contractAddress'] print('RaidenMicroTransferChannels address is', cf_address) priv_keys = [] addresses = [] # we cannot retrieve private keys from configured chains # therefore: create 5 wallets (sample addresses with private keys) # store in separate arrays for i in range(senders - 1): priv_key, address = createWallet() priv_keys.append(priv_key) addresses.append('0x' + address) # send tokens to each new wallet for sender in addresses: token(token_address).transact({ 'from': owner }).transfer(sender, token_assign) # also send tokens to sender addresses for sender in sender_addresses: token(token_address).transact({ 'from': owner }).transfer(sender, token_assign) print('Senders have each been issued', token_assign, ' tokens') # check if it works: # 1. get message balance hash for address[0] balance_msg = "Receiver: " + addresses[ 0] + ", Balance: 10000, Channel ID: 100" # 2. sign the hash with private key corresponding to address[0] balance_msg_sig, addr = sign.check(balance_msg, binascii.unhexlify(priv_keys[0])) # 3. check if ECVerify and ec_recovered address are equal ec_recovered_addr = channel_factory( cf_address).call().verifyBalanceProof(addresses[0], 100, 10000, balance_msg_sig) print('EC_RECOVERED_ADDR:', ec_recovered_addr) print('FIRST WALLET ADDR:', addresses[0]) assert ec_recovered_addr == addresses[0] print('Wait for confirmation...') transfer_filter = token.on('Transfer') wait(transfer_filter, event_wait) print('BALANCE:', token(token_address).call().balanceOf(addresses[0])) assert token(token_address).call().balanceOf(addresses[0]) > 0 # return arrays with generated wallets (private keys first, then addresses, so that priv_key[0] <-> address[0] return (priv_keys, addresses, token(token_address))
def pad_left_hex(value, num_bytes): return pad_left(value, num_bytes * 2, '0')
def _pad32(value): return pad_left(value, 32, b'\x00')
def random_private_key(bound): """Randomly gnerate a private key smaller than a certain bound.""" n = random.randint(1, bound) private_key = encode_hex(pad_left(int_to_big_endian(n), 32, '\0')) return private_key
def msg_hash(nextkey, gas, addr, value, data): msg = b''.join([pad_left(nextkey, 32, b'\x00'), pad_left(gas, 32, b'\x00'), pad_left(addr, 20, b'\x00'), pad_left(value, 32, b'\x00')])+data #print('Expected: ',"0x"+msg.hex(), len("0x"+msg.hex())) return eth_utils.keccak(msg)