mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-05 05:15:12 +00:00
create Transaction class
This commit is contained in:
parent
b4bb3c7449
commit
ea7718fc59
4 changed files with 101 additions and 64 deletions
48
electrum
48
electrum
|
@ -711,38 +711,44 @@ if __name__ == '__main__':
|
||||||
import ast
|
import ast
|
||||||
inputs = ast.literal_eval(args[1])
|
inputs = ast.literal_eval(args[1])
|
||||||
outputs = ast.literal_eval(args[2])
|
outputs = ast.literal_eval(args[2])
|
||||||
inputs = map(lambda x: (None, None, x["txid"], x["vout"], None, None), inputs)
|
# convert to own format
|
||||||
outputs = map(lambda x: (x[0],int(x[1]*1e8)), outputs.items())
|
for i in inputs:
|
||||||
tx = raw_tx(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
|
i['tx_hash'] = i['txid']
|
||||||
|
i['index'] = i['vout']
|
||||||
|
outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
|
||||||
|
tx = Transaction.from_io(inputs, outputs)
|
||||||
print_msg( tx )
|
print_msg( tx )
|
||||||
|
|
||||||
|
|
||||||
elif cmd == 'decoderawtransaction':
|
elif cmd == 'decoderawtransaction':
|
||||||
print_json( bitcoin.deserialize(args[1]) )
|
tx = Transaction(args[1])
|
||||||
|
print_json( tx.deserialize() )
|
||||||
|
|
||||||
|
|
||||||
elif cmd == 'signrawtransaction':
|
elif cmd == 'signrawtransaction':
|
||||||
d = bitcoin.deserialize(args[1])
|
tx = Transaction(args[1])
|
||||||
txouts = args[2] if len(args)>2 else []
|
txouts = args[2] if len(args)>2 else []
|
||||||
private_keys = args[3] if len(args)>3 else []
|
private_keys = args[3] if len(args)>3 else {}
|
||||||
|
|
||||||
inputs = []
|
|
||||||
for x in d['inputs']:
|
|
||||||
txid = x["prevout_hash"]
|
|
||||||
nout = x["prevout_n"]
|
|
||||||
tx = wallet.transactions.get(txid)
|
|
||||||
txout = tx['outputs'][nout]
|
|
||||||
addr = txout['address']
|
|
||||||
v = txout['value']
|
|
||||||
inputs.append( (addr, v, txid, nout, txout['raw_output_script'], [(None,None)] ) )
|
|
||||||
|
|
||||||
outputs = map(lambda x: (x['address'],x['value']), d['outputs'])
|
if not private_keys:
|
||||||
print_error("inputs", inputs)
|
for txin in tx.inputs:
|
||||||
print_error("outputs", outputs)
|
txid = txin["prevout_hash"]
|
||||||
|
index = txin["prevout_n"]
|
||||||
tx = wallet.signed_tx( inputs, outputs, password )
|
utx = wallet.transactions.get(txid)
|
||||||
|
txout = utx['outputs'][index]
|
||||||
|
txin['address'] = txout['address']
|
||||||
|
txin['raw_output_script'] = txout['raw_output_script']
|
||||||
|
# convert to own format
|
||||||
|
txin['tx_hash'] = txin['prevout_hash']
|
||||||
|
txin['index'] = txin['prevout_n']
|
||||||
|
secexp, compressed = wallet.get_private_key(txin['address'], password)
|
||||||
|
private_keys[addr] = (secexp,compressed)
|
||||||
|
|
||||||
|
tx.sign( private_keys )
|
||||||
print_msg(tx)
|
print_msg(tx)
|
||||||
|
|
||||||
|
|
||||||
if cmd not in offline_commands and not options.offline:
|
if cmd not in offline_commands and not options.offline:
|
||||||
synchronizer.stop()
|
synchronizer.stop()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,6 @@ from verifier import WalletVerifier
|
||||||
from interface import Interface, pick_random_server, DEFAULT_SERVERS
|
from interface import Interface, pick_random_server, DEFAULT_SERVERS
|
||||||
from simple_config import SimpleConfig
|
from simple_config import SimpleConfig
|
||||||
import bitcoin
|
import bitcoin
|
||||||
|
from bitcoin import Transaction
|
||||||
from mnemonic import mn_encode as mnemonic_encode
|
from mnemonic import mn_encode as mnemonic_encode
|
||||||
from mnemonic import mn_decode as mnemonic_decode
|
from mnemonic import mn_decode as mnemonic_decode
|
||||||
|
|
|
@ -317,12 +317,14 @@ def tx_filter(s):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def raw_tx( inputs, outputs, for_sig = None ):
|
def raw_tx( inputs, outputs, for_sig = None ):
|
||||||
|
|
||||||
s = int_to_hex(1,4) # version
|
s = int_to_hex(1,4) # version
|
||||||
s += var_int( len(inputs) ) # number of inputs
|
s += var_int( len(inputs) ) # number of inputs
|
||||||
for i in range(len(inputs)):
|
for i in range(len(inputs)):
|
||||||
_, _, p_hash, p_index, p_script, pubkeysig = inputs[i]
|
txin = inputs[i]
|
||||||
s += p_hash.decode('hex')[::-1].encode('hex') # prev hash
|
pubkeysig = txin.get('pubkeysig',[])
|
||||||
s += int_to_hex(p_index,4) # prev index
|
s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash
|
||||||
|
s += int_to_hex(txin['index'],4) # prev index
|
||||||
|
|
||||||
if for_sig is None:
|
if for_sig is None:
|
||||||
if len(pubkeysig) == 1:
|
if len(pubkeysig) == 1:
|
||||||
|
@ -350,7 +352,7 @@ def raw_tx( inputs, outputs, for_sig = None ):
|
||||||
if len(pubkeysig) > 1:
|
if len(pubkeysig) > 1:
|
||||||
script = multisig_script(pubkeysig) # p2sh uses the inner script
|
script = multisig_script(pubkeysig) # p2sh uses the inner script
|
||||||
else:
|
else:
|
||||||
script = p_script # scriptsig
|
script = txin['raw_output_script'] # scriptsig
|
||||||
else:
|
else:
|
||||||
script=''
|
script=''
|
||||||
s += var_int( len(tx_filter(script))/2 ) # script length
|
s += var_int( len(tx_filter(script))/2 ) # script length
|
||||||
|
@ -382,11 +384,6 @@ def raw_tx( inputs, outputs, for_sig = None ):
|
||||||
return tx_filter(s)
|
return tx_filter(s)
|
||||||
|
|
||||||
|
|
||||||
def deserialize(raw_tx):
|
|
||||||
import deserialize
|
|
||||||
vds = deserialize.BCDataStream()
|
|
||||||
vds.write(raw_tx.decode('hex'))
|
|
||||||
return deserialize.parse_Transaction(vds)
|
|
||||||
|
|
||||||
|
|
||||||
def multisig_script(public_keys, num=None):
|
def multisig_script(public_keys, num=None):
|
||||||
|
@ -419,6 +416,56 @@ def multisig_script(public_keys, num=None):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Transaction:
|
||||||
|
|
||||||
|
def __init__(self, raw):
|
||||||
|
self.raw = raw
|
||||||
|
self.deserialize()
|
||||||
|
self.inputs = self.d['inputs']
|
||||||
|
self.outputs = self.d['outputs']
|
||||||
|
self.outputs = map(lambda x: (x['address'],x['value']), self.outputs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_io(klass, inputs, outputs):
|
||||||
|
raw = raw_tx(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
|
||||||
|
self = klass(raw)
|
||||||
|
self.inputs = inputs
|
||||||
|
self.outputs = outputs
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.raw
|
||||||
|
|
||||||
|
def hash(self):
|
||||||
|
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
|
||||||
|
|
||||||
|
def sign(self, private_keys):
|
||||||
|
|
||||||
|
for i in range(len(self.inputs)):
|
||||||
|
txin = self.inputs[i]
|
||||||
|
secexp, compressed = private_keys[txin['address']]
|
||||||
|
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
|
||||||
|
public_key = private_key.get_verifying_key()
|
||||||
|
pkey = EC_KEY(secexp)
|
||||||
|
pubkey = GetPubKey(pkey.pubkey, compressed)
|
||||||
|
tx = raw_tx( self.inputs, self.outputs, for_sig = i )
|
||||||
|
sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
|
||||||
|
assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
|
||||||
|
self.inputs[i]["pubkeysig"] = [(pubkey, sig)]
|
||||||
|
|
||||||
|
self.raw = raw_tx( self.inputs, self.outputs )
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize(self):
|
||||||
|
import deserialize
|
||||||
|
vds = deserialize.BCDataStream()
|
||||||
|
vds.write(self.raw.decode('hex'))
|
||||||
|
self.d = deserialize.parse_Transaction(vds)
|
||||||
|
return self.d
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_bip32():
|
def test_bip32():
|
||||||
seed = "ff000000000000000000000000000000".decode('hex')
|
seed = "ff000000000000000000000000000000".decode('hex')
|
||||||
master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
|
master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
|
||||||
|
|
|
@ -210,7 +210,7 @@ class Wallet:
|
||||||
n = self.change_addresses.index(address)
|
n = self.change_addresses.index(address)
|
||||||
for_change = True
|
for_change = True
|
||||||
else:
|
else:
|
||||||
raise BaseException("unknown address")
|
raise BaseException("unknown address", address)
|
||||||
|
|
||||||
seed = self.pw_decode( self.seed, password)
|
seed = self.pw_decode( self.seed, password)
|
||||||
if not seed: return None
|
if not seed: return None
|
||||||
|
@ -609,7 +609,8 @@ class Wallet:
|
||||||
addr = item.get('address')
|
addr = item.get('address')
|
||||||
v = item.get('value')
|
v = item.get('value')
|
||||||
total += v
|
total += v
|
||||||
inputs.append((addr, v, item['tx_hash'], item['index'], item['raw_output_script'], [(None,None)] ))
|
item['pubkeysig'] = [(None, None)]
|
||||||
|
inputs.append( item )
|
||||||
fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
|
fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
|
||||||
if total >= amount + fee: break
|
if total >= amount + fee: break
|
||||||
else:
|
else:
|
||||||
|
@ -628,22 +629,6 @@ class Wallet:
|
||||||
outputs[posn:posn] = [( change_addr, change_amount)]
|
outputs[posn:posn] = [( change_addr, change_amount)]
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
def sign_inputs( self, inputs, outputs, password ):
|
|
||||||
s_inputs = []
|
|
||||||
for i in range(len(inputs)):
|
|
||||||
addr, v, p_hash, p_pos, p_scriptPubKey, _ = inputs[i]
|
|
||||||
secexp, compressed = self.get_private_key(addr, password)
|
|
||||||
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
|
|
||||||
public_key = private_key.get_verifying_key()
|
|
||||||
|
|
||||||
pkey = EC_KEY(secexp)
|
|
||||||
pubkey = GetPubKey(pkey.pubkey, compressed)
|
|
||||||
|
|
||||||
tx = raw_tx( inputs, outputs, for_sig = i )
|
|
||||||
sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
|
|
||||||
assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
|
|
||||||
s_inputs.append( (addr, v, p_hash, p_pos, p_scriptPubKey, [(pubkey, sig)] ) )
|
|
||||||
return s_inputs
|
|
||||||
|
|
||||||
def pw_encode(self, s, password):
|
def pw_encode(self, s, password):
|
||||||
if password:
|
if password:
|
||||||
|
@ -823,7 +808,7 @@ class Wallet:
|
||||||
raise ValueError("Not enough funds")
|
raise ValueError("Not enough funds")
|
||||||
|
|
||||||
if not self.use_change and not change_addr:
|
if not self.use_change and not change_addr:
|
||||||
change_addr = inputs[-1][0]
|
change_addr = inputs[-1]['address']
|
||||||
print_error( "Sending change to", change_addr )
|
print_error( "Sending change to", change_addr )
|
||||||
outputs = self.add_tx_change(outputs, amount, fee, total, change_addr)
|
outputs = self.add_tx_change(outputs, amount, fee, total, change_addr)
|
||||||
|
|
||||||
|
@ -843,9 +828,14 @@ class Wallet:
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
def signed_tx(self, inputs, outputs, password):
|
def signed_tx(self, inputs, outputs, password):
|
||||||
s_inputs = self.sign_inputs( inputs, outputs, password )
|
tx = Transaction.from_io(inputs, outputs)
|
||||||
tx = raw_tx( s_inputs, outputs )
|
private_keys = {}
|
||||||
return tx
|
for txin in tx.inputs:
|
||||||
|
addr = txin['address']
|
||||||
|
secexp, compressed = self.get_private_key(addr, password)
|
||||||
|
private_keys[addr] = (secexp,compressed)
|
||||||
|
tx.sign(private_keys)
|
||||||
|
return str(tx)
|
||||||
|
|
||||||
def sendtx(self, tx):
|
def sendtx(self, tx):
|
||||||
# synchronous
|
# synchronous
|
||||||
|
@ -1344,7 +1334,11 @@ class WalletSynchronizer(threading.Thread):
|
||||||
elif method == 'blockchain.transaction.get':
|
elif method == 'blockchain.transaction.get':
|
||||||
tx_hash = params[0]
|
tx_hash = params[0]
|
||||||
tx_height = params[1]
|
tx_height = params[1]
|
||||||
d = self.deserialize_tx(tx_hash, tx_height, result)
|
assert tx_hash == hash_encode(Hash(result.decode('hex')))
|
||||||
|
tx = Transaction(result)
|
||||||
|
d = tx.deserialize()
|
||||||
|
d['height'] = tx_height
|
||||||
|
d['tx_hash'] = tx_hash
|
||||||
self.wallet.receive_tx_callback(tx_hash, d)
|
self.wallet.receive_tx_callback(tx_hash, d)
|
||||||
self.was_updated = True
|
self.was_updated = True
|
||||||
requested_tx.remove( (tx_hash, tx_height) )
|
requested_tx.remove( (tx_hash, tx_height) )
|
||||||
|
@ -1365,14 +1359,3 @@ class WalletSynchronizer(threading.Thread):
|
||||||
self.was_updated = False
|
self.was_updated = False
|
||||||
|
|
||||||
|
|
||||||
def deserialize_tx(self, tx_hash, tx_height, raw_tx):
|
|
||||||
|
|
||||||
assert tx_hash == hash_encode(Hash(raw_tx.decode('hex')))
|
|
||||||
import deserialize
|
|
||||||
vds = deserialize.BCDataStream()
|
|
||||||
vds.write(raw_tx.decode('hex'))
|
|
||||||
d = deserialize.parse_Transaction(vds)
|
|
||||||
d['height'] = tx_height
|
|
||||||
d['tx_hash'] = tx_hash
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue