def get_all_used_addresses(threshold=1, account=0, verbose=False, index=0): addresses = [] missing = 0 while missing <= threshold: keypath = energi.serialize_pathd( energi.create_pathd(account=account, index=index)) public_key = ledger.get_public_key(keypath) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) find_change = False if not is_unused(address) or not is_unused(uncompressed_address): ae = address_entry(address, uncompressed_address, public_key, index=index, account=account) if verbose: print('found used: %s' % ae['path']) addresses.append(ae) missing = 0 find_change = True if find_change: addresses += get_all_used_change(index, threshold, account, verbose) missing += 1 index += 1 return addresses
def search_address_path(address_in, account=0): if isinstance(address_in, str): address_in = bytes([ord(c) for c in address_in]) index = 0 change = 0 state = 0 while True: keypath = energi.serialize_pathd( energi.create_pathd(index=index, change=change, account=account)) public_key = ledger.get_public_key(keypath) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) if address_in == address or address_in == uncompressed_address: return energi.create_pathd(index=index, change=change, account=account) unused = is_unused(address) and is_unused(uncompressed_address) if state == 0: if unused: return None state = 1 change = 1 elif state == 1: if unused: index += 1 change = 0 state = 0 else: change += 1
def get_all_used_change(for_index, threshold=1, account=0, verbose=False): addresses = [] missing = 0 change = 1 while missing <= threshold: keypath = energi.serialize_pathd( energi.create_pathd(index=for_index, change=change, account=account)) public_key = ledger.get_public_key(keypath) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) if not is_unused(address) or not is_unused(uncompressed_address): ae = address_entry(address, uncompressed_address, public_key, change=change, index=for_index, account=account) if verbose: print('found used: %s' % ae['path']) addresses.append(ae) missing = 0 missing += 1 change += 1 return addresses
def create_mnb(mne): collat_outpoint = Transaction.COutPoint(serialize.hs2b(mne['txid']), mne['nout']) mnp = Masternode.CMasternodePing(collat_outpoint) mn_privkey = energi.decode_address(mne['mn_privkey']) block_hash = Masternode.get_block_hash(13) mnp.sign(block_hash, mn_privkey) mnb = Masternode.CMasternodeBroadcast() mnb.outpoint = collat_outpoint mnb.addr = parse_ipport(mne['ip']) mn_private_key = secp256k1.PrivateKey(mn_privkey, raw = True) mn_public_key = mn_private_key.pubkey.serialize(compressed = False) co_ae = walletdb.get_addr_txid(mne['txid'], mne['nout']) if co_ae is None: raise RuntimeError('cannot find address for txid: %s' % mne['txid']) co_public_key = co_ae['pubkey'] mnb.pubkey_collateral = energi.compress_public_key(co_public_key) mnb.pubkey_masternode = mn_public_key mnb.last_ping = mnp print('Signing on ledger:') mnb.hw_sign(energi.serialize_pathd(co_ae)) print('MasternodeBroadcast:\n%s' % mnb) return mnb
def get_address(ae, display=False): keypath = energi.serialize_pathd(ae) public_key = ledger.get_public_key(keypath, display) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) return address_entry(address, uncompressed_address, public_key, ae['purpose'], ae['coin'], ae['account'], ae['change'], ae['index'])
def get_next_change(for_index, account=0): i = 1 while True: keypath = energi.serialize_pathd( energi.create_pathd(index=for_index, change=i, account=account)) public_key = ledger.get_public_key(keypath) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) if is_unused(address) and is_unused(uncompressed_address): return address_entry(address, uncompressed_address, public_key, index=for_index, change=i, account=account) i += 1
def get_next_unused(index=0, n=1, account=0): count = 0 i = index rl = [] while count < n: keypath = energi.serialize_pathd( energi.create_pathd(index=i, account=0)) public_key = ledger.get_public_key(keypath) address = energi.encode_address(energi.compress_public_key(public_key)) uncompressed_address = energi.encode_address(public_key) if is_unused(address) and is_unused(uncompressed_address): rl.append( address_entry(address, uncompressed_address, public_key, index=i, account=account)) count += 1 i += 1 return rl
def create_tx(address_to, value_sats, addr_d, fee_minimum = 0, used_inputs = []): # fee_minimum is in Sats _NRGSAT = 10**8 # extract utxos from addr_d (not from the change address though) utxol = [] for a in addr_d: if 'change' != a and 'utxos' in addr_d[a]: for u in addr_d[a]['utxos']: utxol.append(u) # calculate transfer amounts fee_est = eel.get_fee_estimate() * _NRGSAT if fee_est < fee_minimum: fee_est = fee_minimum fee_amt = int(fee_est) balance = sum([u['satoshis'] for u in utxol]) send_amt = value_sats if value_sats is not None else balance - fee_amt # Use as many unspent txs as we need amt = 0 ul = [] for u in utxol: ul.append(u) used_inputs.append(u) amt += u['satoshis'] if amt >= send_amt + fee_amt: # assuming 1kB tx break if amt < send_amt + fee_amt: raise RuntimeError('error: wallet balance insufficient: %d' % amt) # build transaction tx = CTransaction() tx.vin = [] for u in ul: h = serialize.hs2b(u['txid']) op = COutPoint(h, u['nout']) tx.vin.append(CTxIn(outpoint = op, nSequence = 2**32 - 1)) # randomize input order (somewhat) random.shuffle(tx.vin) txout = CTxOut(send_amt, script.standard_p2pkh_pkh(energi.decode_address(address_to))) tx.vout = [txout] change_path = None if send_amt + fee_amt < amt: change_pubkey = energi.compress_public_key(addr_d['change']['public_key']) change_out = CTxOut(amt - (send_amt + fee_amt), script.standard_p2pkh(change_pubkey)) tx.vout.append(change_out) change_path = energi.serialize_pathd(addr_d['change']) # randomize output order (somewhat) random.shuffle(tx.vout) txs = sign_tx(tx, address_d = addr_d, change_path = change_path) print('Transaction signed. Verifying.') if not verify_tx(txs): raise RuntimeError('transaction did not verify') print('Verified.') return txs.serialize()
def sign_tx(tx_in, address_d, change_path = None, txid_d = None): tx = CTransaction(tx_in) # First, we need a trusted input blob for each vin[i]. sys.stdout.write('Loading input transactions (%d)... ' % len(tx.vin)); sys.stdout.flush() til = [] for i in range(len(tx.vin)): d = {} # get the txid for the output this input refers to txid = tx.vin[i].prevout.hash txid_hs = serialize.b2hs(txid[::-1]) # and the index into vout n = tx.vin[i].prevout.n # get the transaction from energid tx_i_hex = eel.get_hex_transaction(txid_hs) if txid_d is None else txid_d[txid_hs]['hex'] tx_i = CTransaction().deserialize(BytesIO(serialize.hs2b(tx_i_hex))) # save scriptPubKey for later d['scriptPubKey'] = tx_i.vout[n].scriptPubKey # we'll send this in chunks; the first includes up to vin len buf = struct.pack('<i', tx_i.nVersion) + serialize.ser_compact_size(len(tx_i.vin)) r = ledger.call_get_trusted_input_first(n, buf) if r != b'': raise RuntimeError('get_trusted_input_first: %s' % r) # now send vin for j in range(len(tx_i.vin)): buf = tx_i.vin[j].serialize() r = ledger.call_get_trusted_input_next(buf) if r != b'': raise RuntimeError('get_trusted_input_next: %s' % r) # send len of vout and vout[0] buf = serialize.ser_compact_size(len(tx_i.vout)) + tx_i.vout[0].serialize() r = ledger.call_get_trusted_input_next(buf) if r != b'': raise RuntimeError('get_trusted_input_next (0): %s' % (r)) # send the rest of vout for j in range(1, len(tx_i.vout)): buf = tx_i.vout[j].serialize() r = ledger.call_get_trusted_input_next(buf) if r != b'': raise RuntimeError('get_trusted_input_next (%d): %s' % (j, r)) # send locktime buf = struct.pack('<I', tx_i.nLockTime) r = ledger.call_get_trusted_input_next(buf) if r == b'': raise RuntimeError('bad trusted input response') d['tib'] = r til.append(d) # Second, we need a signature to put in each vin[i].scriptSig. sigs = [] for i in range(len(tx.vin)): tx_i = CTransaction(tx) for v in tx_i.vin: v.scriptSig = b'' tx_i.vin[i].scriptSig = til[i]['scriptPubKey'] # get (hash160) public key we need to sign with if not script.is_standard(til[i]['scriptPubKey']): raise RuntimeError('we can only include P2PKH transactions') sd = script.disass(til[i]['scriptPubKey']) # save for later hpubkey = sd[2]['data'] # again, we'll send the transaction in chunks buf = struct.pack('<i', tx_i.nVersion) + serialize.ser_compact_size(len(tx_i.vin)) + bytes([1, len(til[0]['tib'])]) + til[0]['tib'] + serialize.ser_string(tx_i.vin[0].scriptSig) + struct.pack('<I', tx_i.vin[0].nSequence) r = ledger.call_hash_input_start_first(buf) if b'' != r: raise RuntimeError('hash_input_start_first: %s' % r) for j in range(1, len(tx_i.vin)): buf = bytes([1, len(til[j]['tib'])]) + til[j]['tib'] + serialize.ser_string(tx_i.vin[j].scriptSig) + struct.pack('<I', tx_i.vin[j].nSequence) r = ledger.call_hash_input_start_next(buf) if b'' != r: raise RuntimeError('hash_input_start_next: %s' % r) # okay, all the inputs have been given; now the outputs buf = serialize.ser_vector(tx_i.vout) bufl = [buf[x:x + 200] for x in range(0, len(buf), 200)] # can be broken up arbitrarily # everything but the very last one for b in bufl[:-1]: r = ledger.call_hash_input_finalize_full(b) if b'' != r: raise RuntimeError('hash_input_finalize_full: %s' % r) # register the change path if we have it if change_path is not None: r = ledger.call_hash_input_finalize_full_change(change_path) if b'' != r: raise RuntimeError('hash_input_finalize_full_change: %s' % r) sys.stdout.write('\nSign input %d: ' % (i + 1)); sys.stdout.flush() # now the last part of the outputs r = ledger.call_hash_input_finalize_full_last(bufl[-1]) if r != bytearray(b'\x00\x00'): raise RuntimeError('hash_input_finalize_full: %s' % r) sys.stdout.write('signed'); sys.stdout.flush() # get the address path for the given hashed pubkey addr = energi.address_repr(hpubkey) keypath = energi.serialize_pathd(address_d[addr]) # now sign buf = serialize.hs2b(keypath) + b'\x00' + struct.pack('>I', tx_i.nLockTime) + bytes([_hashtype_d['SIGHASH_ALL']]) r = ledger.call_hash_sign(buf) # last byte is hashtype # save for scriptSig pubkey = address_d[addr]['pubkey'] if 'pubkey' in address_d[addr] else address_d[addr]['public_key'] if energi.hash160(pubkey) != hpubkey: pubkey = energi.compress_public_key(pubkey) if energi.hash160(pubkey) != hpubkey: raise RuntimeError('address confusion') sigs.append({'signature': r, 'pubkey': pubkey}) sys.stdout.write('\n'); sys.stdout.flush() # Finally, everything should be signed. Construct scriptSig for each vin for i in range(len(tx.vin)): tx.vin[i].scriptSig = script.standard_scriptsig(sigs[i]['signature'], sigs[i]['pubkey']) return tx