move transaction code and fix issue #280

This commit is contained in:
thomasv 2013-09-04 16:46:27 +02:00
parent d2099d94e5
commit afac84e231
8 changed files with 747 additions and 701 deletions

View File

@ -41,7 +41,7 @@ except:
sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o gui/icons_rc.py'") sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o gui/icons_rc.py'")
from electrum.wallet import format_satoshis from electrum.wallet import format_satoshis
from electrum.bitcoin import Transaction, is_valid from electrum import Transaction
from electrum import mnemonic from electrum import mnemonic
from electrum import util, bitcoin, commands, Interface, Wallet from electrum import util, bitcoin, commands, Interface, Wallet
from electrum import SimpleConfig, Wallet, WalletStorage from electrum import SimpleConfig, Wallet, WalletStorage

View File

@ -8,7 +8,8 @@ from interface import Interface, pick_random_server, DEFAULT_SERVERS
from simple_config import SimpleConfig from simple_config import SimpleConfig
import bitcoin import bitcoin
import account import account
from bitcoin import Transaction, EC_KEY, is_valid from transaction 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
from commands import protected_commands, known_commands, offline_commands, Commands from commands import protected_commands, known_commands, offline_commands, Commands

View File

@ -460,300 +460,6 @@ def bip32_private_key(sequence, k, chain):
MIN_RELAY_TX_FEE = 10000 MIN_RELAY_TX_FEE = 10000
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)
self.input_info = None
self.is_complete = True
@classmethod
def from_io(klass, inputs, outputs):
raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
self = klass(raw)
self.is_complete = False
self.inputs = inputs
self.outputs = outputs
extras = []
for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') }
extras.append(e)
self.input_info = extras
return self
def __str__(self):
return self.raw
@classmethod
def multisig_script(klass, public_keys, num=None):
n = len(public_keys)
if num is None: num = n
# supports only "2 of 2", and "2 of 3" transactions
assert num <= n and n in [2,3]
if num==2:
s = '52'
elif num == 3:
s = '53'
else:
raise
for k in public_keys:
s += var_int(len(k)/2)
s += k
if n==2:
s += '52'
elif n==3:
s += '53'
else:
raise
s += 'ae'
return s
@classmethod
def serialize( klass, inputs, outputs, for_sig = None ):
s = int_to_hex(1,4) # version
s += var_int( len(inputs) ) # number of inputs
for i in range(len(inputs)):
txin = inputs[i]
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:
signatures = txin['signatures']
pubkeys = txin['pubkeys']
if not txin.get('redeemScript'):
pubkey = pubkeys[0]
sig = signatures[0]
sig = sig + '01' # hashtype
script = op_push(len(sig)/2)
script += sig
script += op_push(len(pubkey)/2)
script += pubkey
else:
script = '00' # op_0
for sig in signatures:
sig = sig + '01'
script += op_push(len(sig)/2)
script += sig
redeem_script = klass.multisig_script(pubkeys,2)
script += op_push(len(redeem_script)/2)
script += redeem_script
elif for_sig==i:
if txin.get('redeemScript'):
script = txin['redeemScript'] # p2sh uses the inner script
else:
script = txin['raw_output_script'] # scriptsig
else:
script=''
s += var_int( len(script)/2 ) # script length
s += script
s += "ffffffff" # sequence
s += var_int( len(outputs) ) # number of outputs
for output in outputs:
addr, amount = output
s += int_to_hex( amount, 8) # amount
addrtype, hash_160 = bc_address_to_hash_160(addr)
if addrtype == 0:
script = '76a9' # op_dup, op_hash_160
script += '14' # push 0x14 bytes
script += hash_160.encode('hex')
script += '88ac' # op_equalverify, op_checksig
elif addrtype == 5:
script = 'a9' # op_hash_160
script += '14' # push 0x14 bytes
script += hash_160.encode('hex')
script += '87' # op_equal
else:
raise
s += var_int( len(script)/2 ) # script length
s += script # script
s += int_to_hex(0,4) # lock time
if for_sig is not None and for_sig != -1:
s += int_to_hex(1, 4) # hash type
return s
def for_sig(self,i):
return self.serialize(self.inputs, self.outputs, for_sig = i)
def hash(self):
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
def sign(self, keypairs):
import deserialize
is_complete = True
print_error("tx.sign(), keypairs:", keypairs)
for i, txin in enumerate(self.inputs):
# if the input is multisig, parse redeem script
redeem_script = txin.get('redeemScript')
num, redeem_pubkeys = deserialize.parse_redeemScript(redeem_script) if redeem_script else (1, [txin.get('redeemPubkey')])
# add pubkeys
txin["pubkeys"] = redeem_pubkeys
# get list of already existing signatures
signatures = txin.get("signatures",[])
# continue if this txin is complete
if len(signatures) == num:
continue
tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
for pubkey in redeem_pubkeys:
# check if we have the corresponding private key
if pubkey in keypairs.keys():
# add signature
sec = keypairs[pubkey]
compressed = is_compressed(sec)
pkey = regenerate_key(sec)
secexp = pkey.secret
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
public_key = private_key.get_verifying_key()
sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
signatures.append( sig.encode('hex') )
print_error("adding signature for", pubkey)
txin["signatures"] = signatures
is_complete = is_complete and len(signatures) == num
self.is_complete = is_complete
self.raw = self.serialize( 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 has_address(self, addr):
found = False
for txin in self.inputs:
if addr == txin.get('address'):
found = True
break
for txout in self.outputs:
if addr == txout[0]:
found = True
break
return found
def get_value(self, addresses, prevout_values):
# return the balance for that tx
is_relevant = False
is_send = False
is_pruned = False
is_partial = False
v_in = v_out = v_out_mine = 0
for item in self.inputs:
addr = item.get('address')
if addr in addresses:
is_send = True
is_relevant = True
key = item['prevout_hash'] + ':%d'%item['prevout_n']
value = prevout_values.get( key )
if value is None:
is_pruned = True
else:
v_in += value
else:
is_partial = True
if not is_send: is_partial = False
for item in self.outputs:
addr, value = item
v_out += value
if addr in addresses:
v_out_mine += value
is_relevant = True
if is_pruned:
# some inputs are mine:
fee = None
if is_send:
v = v_out_mine - v_out
else:
# no input is mine
v = v_out_mine
else:
v = v_out_mine - v_in
if is_partial:
# some inputs are mine, but not all
fee = None
is_send = v < 0
else:
# all inputs are mine
fee = v_out - v_in
return is_relevant, is_send, v, fee
def as_dict(self):
import json
out = {
"hex":self.raw,
"complete":self.is_complete
}
if not self.is_complete:
extras = []
for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'],
'scriptPubKey':i.get('raw_output_script'),
'KeyID':i.get('KeyID'),
'redeemScript':i.get('redeemScript'),
'signatures':i.get('signatures'),
'pubkeys':i.get('pubkeys'),
}
extras.append(e)
self.input_info = extras
if self.input_info:
out['input_info'] = json.dumps(self.input_info).replace(' ','')
return out
def requires_fee(self, verifier):
# see https://en.bitcoin.it/wiki/Transaction_fees
threshold = 57600000
size = len(self.raw)/2
if size >= 10000:
return True
for o in self.outputs:
value = o[1]
if value < 1000000:
return True
sum = 0
for i in self.inputs:
age = verifier.get_confirmations(i["tx_hash"])[0]
sum += i["value"] * age
priority = sum / size
print_error(priority, threshold)
return priority < threshold
def test_bip32(seed, sequence): def test_bip32(seed, sequence):

View File

@ -1,392 +0,0 @@
# this code comes from ABE. it can probably be simplified
#
#
from bitcoin import public_key_to_bc_address, hash_160_to_bc_address, hash_encode, hash_160
from util import print_error
#import socket
import time
import struct
#
# Workalike python implementation of Bitcoin's CDataStream class.
#
import struct
import StringIO
import mmap
class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
class BCDataStream(object):
def __init__(self):
self.input = None
self.read_cursor = 0
def clear(self):
self.input = None
self.read_cursor = 0
def write(self, bytes): # Initialize with string of bytes
if self.input is None:
self.input = bytes
else:
self.input += bytes
def map_file(self, file, start): # Initialize with bytes from file
self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
self.read_cursor = start
def seek_file(self, position):
self.read_cursor = position
def close_file(self):
self.input.close()
def read_string(self):
# Strings are encoded depending on length:
# 0 to 252 : 1-byte-length followed by bytes (if any)
# 253 to 65,535 : byte'253' 2-byte-length followed by bytes
# 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes
# ... and the Bitcoin client is coded to understand:
# greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
# ... but I don't think it actually handles any strings that big.
if self.input is None:
raise SerializationError("call write(bytes) before trying to deserialize")
try:
length = self.read_compact_size()
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return self.read_bytes(length)
def write_string(self, string):
# Length-encoded as with read-string
self.write_compact_size(len(string))
self.write(string)
def read_bytes(self, length):
try:
result = self.input[self.read_cursor:self.read_cursor+length]
self.read_cursor += length
return result
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return ''
def read_boolean(self): return self.read_bytes(1)[0] != chr(0)
def read_int16(self): return self._read_num('<h')
def read_uint16(self): return self._read_num('<H')
def read_int32(self): return self._read_num('<i')
def read_uint32(self): return self._read_num('<I')
def read_int64(self): return self._read_num('<q')
def read_uint64(self): return self._read_num('<Q')
def write_boolean(self, val): return self.write(chr(1) if val else chr(0))
def write_int16(self, val): return self._write_num('<h', val)
def write_uint16(self, val): return self._write_num('<H', val)
def write_int32(self, val): return self._write_num('<i', val)
def write_uint32(self, val): return self._write_num('<I', val)
def write_int64(self, val): return self._write_num('<q', val)
def write_uint64(self, val): return self._write_num('<Q', val)
def read_compact_size(self):
size = ord(self.input[self.read_cursor])
self.read_cursor += 1
if size == 253:
size = self._read_num('<H')
elif size == 254:
size = self._read_num('<I')
elif size == 255:
size = self._read_num('<Q')
return size
def write_compact_size(self, size):
if size < 0:
raise SerializationError("attempt to write size < 0")
elif size < 253:
self.write(chr(size))
elif size < 2**16:
self.write('\xfd')
self._write_num('<H', size)
elif size < 2**32:
self.write('\xfe')
self._write_num('<I', size)
elif size < 2**64:
self.write('\xff')
self._write_num('<Q', size)
def _read_num(self, format):
(i,) = struct.unpack_from(format, self.input, self.read_cursor)
self.read_cursor += struct.calcsize(format)
return i
def _write_num(self, format, num):
s = struct.pack(format, num)
self.write(s)
#
# enum-like type
# From the Python Cookbook, downloaded from http://code.activestate.com/recipes/67107/
#
import types, string, exceptions
class EnumException(exceptions.Exception):
pass
class Enumeration:
def __init__(self, name, enumList):
self.__doc__ = name
lookup = { }
reverseLookup = { }
i = 0
uniqueNames = [ ]
uniqueValues = [ ]
for x in enumList:
if type(x) == types.TupleType:
x, i = x
if type(x) != types.StringType:
raise EnumException, "enum name is not a string: " + x
if type(i) != types.IntType:
raise EnumException, "enum value is not an integer: " + i
if x in uniqueNames:
raise EnumException, "enum name is not unique: " + x
if i in uniqueValues:
raise EnumException, "enum value is not unique for " + x
uniqueNames.append(x)
uniqueValues.append(i)
lookup[x] = i
reverseLookup[i] = x
i = i + 1
self.lookup = lookup
self.reverseLookup = reverseLookup
def __getattr__(self, attr):
if not self.lookup.has_key(attr):
raise AttributeError
return self.lookup[attr]
def whatis(self, value):
return self.reverseLookup[value]
# This function comes from bitcointools, bct-LICENSE.txt.
def long_hex(bytes):
return bytes.encode('hex_codec')
# This function comes from bitcointools, bct-LICENSE.txt.
def short_hex(bytes):
t = bytes.encode('hex_codec')
if len(t) < 11:
return t
return t[0:4]+"..."+t[-4:]
def parse_TxIn(vds):
d = {}
d['prevout_hash'] = hash_encode(vds.read_bytes(32))
d['prevout_n'] = vds.read_uint32()
scriptSig = vds.read_bytes(vds.read_compact_size())
d['sequence'] = vds.read_uint32()
if scriptSig:
pubkeys, signatures, address = get_address_from_input_script(scriptSig)
else:
pubkeys = []
signatures = []
address = None
d['address'] = address
d['signatures'] = signatures
return d
def parse_TxOut(vds, i):
d = {}
d['value'] = vds.read_int64()
scriptPubKey = vds.read_bytes(vds.read_compact_size())
d['address'] = get_address_from_output_script(scriptPubKey)
d['raw_output_script'] = scriptPubKey.encode('hex')
d['index'] = i
return d
def parse_Transaction(vds):
d = {}
start = vds.read_cursor
d['version'] = vds.read_int32()
n_vin = vds.read_compact_size()
d['inputs'] = []
for i in xrange(n_vin):
d['inputs'].append(parse_TxIn(vds))
n_vout = vds.read_compact_size()
d['outputs'] = []
for i in xrange(n_vout):
d['outputs'].append(parse_TxOut(vds, i))
d['lockTime'] = vds.read_uint32()
return d
def parse_redeemScript(bytes):
dec = [ x for x in script_GetOp(bytes.decode('hex')) ]
# 2 of 2
match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec, match):
pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex') ]
return 2, pubkeys
# 2 of 3
match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec, match):
pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex'), dec[3][1].encode('hex') ]
return 2, pubkeys
opcodes = Enumeration("Opcodes", [
("OP_0", 0), ("OP_PUSHDATA1",76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED",
"OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7",
"OP_8", "OP_9", "OP_10", "OP_11", "OP_12", "OP_13", "OP_14", "OP_15", "OP_16",
"OP_NOP", "OP_VER", "OP_IF", "OP_NOTIF", "OP_VERIF", "OP_VERNOTIF", "OP_ELSE", "OP_ENDIF", "OP_VERIFY",
"OP_RETURN", "OP_TOALTSTACK", "OP_FROMALTSTACK", "OP_2DROP", "OP_2DUP", "OP_3DUP", "OP_2OVER", "OP_2ROT", "OP_2SWAP",
"OP_IFDUP", "OP_DEPTH", "OP_DROP", "OP_DUP", "OP_NIP", "OP_OVER", "OP_PICK", "OP_ROLL", "OP_ROT",
"OP_SWAP", "OP_TUCK", "OP_CAT", "OP_SUBSTR", "OP_LEFT", "OP_RIGHT", "OP_SIZE", "OP_INVERT", "OP_AND",
"OP_OR", "OP_XOR", "OP_EQUAL", "OP_EQUALVERIFY", "OP_RESERVED1", "OP_RESERVED2", "OP_1ADD", "OP_1SUB", "OP_2MUL",
"OP_2DIV", "OP_NEGATE", "OP_ABS", "OP_NOT", "OP_0NOTEQUAL", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV",
"OP_MOD", "OP_LSHIFT", "OP_RSHIFT", "OP_BOOLAND", "OP_BOOLOR",
"OP_NUMEQUAL", "OP_NUMEQUALVERIFY", "OP_NUMNOTEQUAL", "OP_LESSTHAN",
"OP_GREATERTHAN", "OP_LESSTHANOREQUAL", "OP_GREATERTHANOREQUAL", "OP_MIN", "OP_MAX",
"OP_WITHIN", "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160",
"OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG",
"OP_CHECKMULTISIGVERIFY",
("OP_SINGLEBYTE_END", 0xF0),
("OP_DOUBLEBYTE_BEGIN", 0xF000),
"OP_PUBKEY", "OP_PUBKEYHASH",
("OP_INVALIDOPCODE", 0xFFFF),
])
def script_GetOp(bytes):
i = 0
while i < len(bytes):
vch = None
opcode = ord(bytes[i])
i += 1
if opcode >= opcodes.OP_SINGLEBYTE_END:
opcode <<= 8
opcode |= ord(bytes[i])
i += 1
if opcode <= opcodes.OP_PUSHDATA4:
nSize = opcode
if opcode == opcodes.OP_PUSHDATA1:
nSize = ord(bytes[i])
i += 1
elif opcode == opcodes.OP_PUSHDATA2:
(nSize,) = struct.unpack_from('<H', bytes, i)
i += 2
elif opcode == opcodes.OP_PUSHDATA4:
(nSize,) = struct.unpack_from('<I', bytes, i)
i += 4
vch = bytes[i:i+nSize]
i += nSize
yield (opcode, vch, i)
def script_GetOpName(opcode):
return (opcodes.whatis(opcode)).replace("OP_", "")
def decode_script(bytes):
result = ''
for (opcode, vch, i) in script_GetOp(bytes):
if len(result) > 0: result += " "
if opcode <= opcodes.OP_PUSHDATA4:
result += "%d:"%(opcode,)
result += short_hex(vch)
else:
result += script_GetOpName(opcode)
return result
def match_decoded(decoded, to_match):
if len(decoded) != len(to_match):
return False;
for i in range(len(decoded)):
if to_match[i] == opcodes.OP_PUSHDATA4 and decoded[i][0] <= opcodes.OP_PUSHDATA4 and decoded[i][0]>0:
continue # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent.
if to_match[i] != decoded[i][0]:
return False
return True
def get_address_from_input_script(bytes):
try:
decoded = [ x for x in script_GetOp(bytes) ]
except:
# coinbase transactions raise an exception
print_error("cannot find address in input script", bytes.encode('hex'))
return [], [], "(None)"
# non-generated TxIn transactions push a signature
# (seventy-something bytes) and then their public key
# (65 bytes) onto the stack:
match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ]
if match_decoded(decoded, match):
return None, None, public_key_to_bc_address(decoded[1][1])
# p2sh transaction, 2 of n
match = [ opcodes.OP_0 ]
while len(match) < len(decoded):
match.append(opcodes.OP_PUSHDATA4)
if match_decoded(decoded, match):
redeemScript = decoded[-1][1]
num = len(match) - 2
signatures = map(lambda x:x[1][:-1].encode('hex'), decoded[1:-1])
dec2 = [ x for x in script_GetOp(redeemScript) ]
# 2 of 2
match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec2, match2):
pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex') ]
return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5)
# 2 of 3
match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec2, match2):
pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex'), dec2[3][1].encode('hex') ]
return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5)
print_error("cannot find address in input script", bytes.encode('hex'))
return [], [], "(None)"
def get_address_from_output_script(bytes):
decoded = [ x for x in script_GetOp(bytes) ]
# The Genesis Block, self-payments, and pay-by-IP-address payments look like:
# 65 BYTES:... CHECKSIG
match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ]
if match_decoded(decoded, match):
return public_key_to_bc_address(decoded[0][1])
# Pay-by-Bitcoin-address TxOuts look like:
# DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG
match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ]
if match_decoded(decoded, match):
return hash_160_to_bc_address(decoded[2][1])
# p2sh
match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ]
if match_decoded(decoded, match):
return hash_160_to_bc_address(decoded[1][1],5)
return "(None)"

715
lib/transaction.py Normal file
View File

@ -0,0 +1,715 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Note: The deserialization code originally comes from ABE.
from bitcoin import *
from util import print_error
import time
import struct
#
# Workalike python implementation of Bitcoin's CDataStream class.
#
import struct
import StringIO
import mmap
class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
class BCDataStream(object):
def __init__(self):
self.input = None
self.read_cursor = 0
def clear(self):
self.input = None
self.read_cursor = 0
def write(self, bytes): # Initialize with string of bytes
if self.input is None:
self.input = bytes
else:
self.input += bytes
def map_file(self, file, start): # Initialize with bytes from file
self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
self.read_cursor = start
def seek_file(self, position):
self.read_cursor = position
def close_file(self):
self.input.close()
def read_string(self):
# Strings are encoded depending on length:
# 0 to 252 : 1-byte-length followed by bytes (if any)
# 253 to 65,535 : byte'253' 2-byte-length followed by bytes
# 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes
# ... and the Bitcoin client is coded to understand:
# greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
# ... but I don't think it actually handles any strings that big.
if self.input is None:
raise SerializationError("call write(bytes) before trying to deserialize")
try:
length = self.read_compact_size()
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return self.read_bytes(length)
def write_string(self, string):
# Length-encoded as with read-string
self.write_compact_size(len(string))
self.write(string)
def read_bytes(self, length):
try:
result = self.input[self.read_cursor:self.read_cursor+length]
self.read_cursor += length
return result
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return ''
def read_boolean(self): return self.read_bytes(1)[0] != chr(0)
def read_int16(self): return self._read_num('<h')
def read_uint16(self): return self._read_num('<H')
def read_int32(self): return self._read_num('<i')
def read_uint32(self): return self._read_num('<I')
def read_int64(self): return self._read_num('<q')
def read_uint64(self): return self._read_num('<Q')
def write_boolean(self, val): return self.write(chr(1) if val else chr(0))
def write_int16(self, val): return self._write_num('<h', val)
def write_uint16(self, val): return self._write_num('<H', val)
def write_int32(self, val): return self._write_num('<i', val)
def write_uint32(self, val): return self._write_num('<I', val)
def write_int64(self, val): return self._write_num('<q', val)
def write_uint64(self, val): return self._write_num('<Q', val)
def read_compact_size(self):
size = ord(self.input[self.read_cursor])
self.read_cursor += 1
if size == 253:
size = self._read_num('<H')
elif size == 254:
size = self._read_num('<I')
elif size == 255:
size = self._read_num('<Q')
return size
def write_compact_size(self, size):
if size < 0:
raise SerializationError("attempt to write size < 0")
elif size < 253:
self.write(chr(size))
elif size < 2**16:
self.write('\xfd')
self._write_num('<H', size)
elif size < 2**32:
self.write('\xfe')
self._write_num('<I', size)
elif size < 2**64:
self.write('\xff')
self._write_num('<Q', size)
def _read_num(self, format):
(i,) = struct.unpack_from(format, self.input, self.read_cursor)
self.read_cursor += struct.calcsize(format)
return i
def _write_num(self, format, num):
s = struct.pack(format, num)
self.write(s)
#
# enum-like type
# From the Python Cookbook, downloaded from http://code.activestate.com/recipes/67107/
#
import types, string, exceptions
class EnumException(exceptions.Exception):
pass
class Enumeration:
def __init__(self, name, enumList):
self.__doc__ = name
lookup = { }
reverseLookup = { }
i = 0
uniqueNames = [ ]
uniqueValues = [ ]
for x in enumList:
if type(x) == types.TupleType:
x, i = x
if type(x) != types.StringType:
raise EnumException, "enum name is not a string: " + x
if type(i) != types.IntType:
raise EnumException, "enum value is not an integer: " + i
if x in uniqueNames:
raise EnumException, "enum name is not unique: " + x
if i in uniqueValues:
raise EnumException, "enum value is not unique for " + x
uniqueNames.append(x)
uniqueValues.append(i)
lookup[x] = i
reverseLookup[i] = x
i = i + 1
self.lookup = lookup
self.reverseLookup = reverseLookup
def __getattr__(self, attr):
if not self.lookup.has_key(attr):
raise AttributeError
return self.lookup[attr]
def whatis(self, value):
return self.reverseLookup[value]
# This function comes from bitcointools, bct-LICENSE.txt.
def long_hex(bytes):
return bytes.encode('hex_codec')
# This function comes from bitcointools, bct-LICENSE.txt.
def short_hex(bytes):
t = bytes.encode('hex_codec')
if len(t) < 11:
return t
return t[0:4]+"..."+t[-4:]
def parse_redeemScript(bytes):
dec = [ x for x in script_GetOp(bytes.decode('hex')) ]
# 2 of 2
match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec, match):
pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex') ]
return 2, pubkeys
# 2 of 3
match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec, match):
pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex'), dec[3][1].encode('hex') ]
return 2, pubkeys
opcodes = Enumeration("Opcodes", [
("OP_0", 0), ("OP_PUSHDATA1",76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED",
"OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7",
"OP_8", "OP_9", "OP_10", "OP_11", "OP_12", "OP_13", "OP_14", "OP_15", "OP_16",
"OP_NOP", "OP_VER", "OP_IF", "OP_NOTIF", "OP_VERIF", "OP_VERNOTIF", "OP_ELSE", "OP_ENDIF", "OP_VERIFY",
"OP_RETURN", "OP_TOALTSTACK", "OP_FROMALTSTACK", "OP_2DROP", "OP_2DUP", "OP_3DUP", "OP_2OVER", "OP_2ROT", "OP_2SWAP",
"OP_IFDUP", "OP_DEPTH", "OP_DROP", "OP_DUP", "OP_NIP", "OP_OVER", "OP_PICK", "OP_ROLL", "OP_ROT",
"OP_SWAP", "OP_TUCK", "OP_CAT", "OP_SUBSTR", "OP_LEFT", "OP_RIGHT", "OP_SIZE", "OP_INVERT", "OP_AND",
"OP_OR", "OP_XOR", "OP_EQUAL", "OP_EQUALVERIFY", "OP_RESERVED1", "OP_RESERVED2", "OP_1ADD", "OP_1SUB", "OP_2MUL",
"OP_2DIV", "OP_NEGATE", "OP_ABS", "OP_NOT", "OP_0NOTEQUAL", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV",
"OP_MOD", "OP_LSHIFT", "OP_RSHIFT", "OP_BOOLAND", "OP_BOOLOR",
"OP_NUMEQUAL", "OP_NUMEQUALVERIFY", "OP_NUMNOTEQUAL", "OP_LESSTHAN",
"OP_GREATERTHAN", "OP_LESSTHANOREQUAL", "OP_GREATERTHANOREQUAL", "OP_MIN", "OP_MAX",
"OP_WITHIN", "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160",
"OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG",
"OP_CHECKMULTISIGVERIFY",
("OP_SINGLEBYTE_END", 0xF0),
("OP_DOUBLEBYTE_BEGIN", 0xF000),
"OP_PUBKEY", "OP_PUBKEYHASH",
("OP_INVALIDOPCODE", 0xFFFF),
])
def script_GetOp(bytes):
i = 0
while i < len(bytes):
vch = None
opcode = ord(bytes[i])
i += 1
if opcode >= opcodes.OP_SINGLEBYTE_END:
opcode <<= 8
opcode |= ord(bytes[i])
i += 1
if opcode <= opcodes.OP_PUSHDATA4:
nSize = opcode
if opcode == opcodes.OP_PUSHDATA1:
nSize = ord(bytes[i])
i += 1
elif opcode == opcodes.OP_PUSHDATA2:
(nSize,) = struct.unpack_from('<H', bytes, i)
i += 2
elif opcode == opcodes.OP_PUSHDATA4:
(nSize,) = struct.unpack_from('<I', bytes, i)
i += 4
vch = bytes[i:i+nSize]
i += nSize
yield (opcode, vch, i)
def script_GetOpName(opcode):
return (opcodes.whatis(opcode)).replace("OP_", "")
def decode_script(bytes):
result = ''
for (opcode, vch, i) in script_GetOp(bytes):
if len(result) > 0: result += " "
if opcode <= opcodes.OP_PUSHDATA4:
result += "%d:"%(opcode,)
result += short_hex(vch)
else:
result += script_GetOpName(opcode)
return result
def match_decoded(decoded, to_match):
if len(decoded) != len(to_match):
return False;
for i in range(len(decoded)):
if to_match[i] == opcodes.OP_PUSHDATA4 and decoded[i][0] <= opcodes.OP_PUSHDATA4 and decoded[i][0]>0:
continue # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent.
if to_match[i] != decoded[i][0]:
return False
return True
def get_address_from_input_script(bytes):
try:
decoded = [ x for x in script_GetOp(bytes) ]
except:
# coinbase transactions raise an exception
print_error("cannot find address in input script", bytes.encode('hex'))
return [], [], "(None)"
# payto_pubkey
match = [ opcodes.OP_PUSHDATA4 ]
if match_decoded(decoded, match):
return None, None, "(pubkey)"
# non-generated TxIn transactions push a signature
# (seventy-something bytes) and then their public key
# (65 bytes) onto the stack:
match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ]
if match_decoded(decoded, match):
return None, None, public_key_to_bc_address(decoded[1][1])
# p2sh transaction, 2 of n
match = [ opcodes.OP_0 ]
while len(match) < len(decoded):
match.append(opcodes.OP_PUSHDATA4)
if match_decoded(decoded, match):
redeemScript = decoded[-1][1]
num = len(match) - 2
signatures = map(lambda x:x[1][:-1].encode('hex'), decoded[1:-1])
dec2 = [ x for x in script_GetOp(redeemScript) ]
# 2 of 2
match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec2, match2):
pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex') ]
return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5)
# 2 of 3
match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
if match_decoded(dec2, match2):
pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex'), dec2[3][1].encode('hex') ]
return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5)
print_error("cannot find address in input script", bytes.encode('hex'))
return [], [], "(None)"
def get_address_from_output_script(bytes):
decoded = [ x for x in script_GetOp(bytes) ]
# The Genesis Block, self-payments, and pay-by-IP-address payments look like:
# 65 BYTES:... CHECKSIG
match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ]
if match_decoded(decoded, match):
return True, public_key_to_bc_address(decoded[0][1])
# Pay-by-Bitcoin-address TxOuts look like:
# DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG
match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ]
if match_decoded(decoded, match):
return False, hash_160_to_bc_address(decoded[2][1])
# p2sh
match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ]
if match_decoded(decoded, match):
return False, hash_160_to_bc_address(decoded[1][1],5)
return False, "(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)
self.input_info = None
self.is_complete = True
@classmethod
def from_io(klass, inputs, outputs):
raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
self = klass(raw)
self.is_complete = False
self.inputs = inputs
self.outputs = outputs
extras = []
for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') }
extras.append(e)
self.input_info = extras
return self
def __str__(self):
return self.raw
@classmethod
def multisig_script(klass, public_keys, num=None):
n = len(public_keys)
if num is None: num = n
# supports only "2 of 2", and "2 of 3" transactions
assert num <= n and n in [2,3]
if num==2:
s = '52'
elif num == 3:
s = '53'
else:
raise
for k in public_keys:
s += var_int(len(k)/2)
s += k
if n==2:
s += '52'
elif n==3:
s += '53'
else:
raise
s += 'ae'
return s
@classmethod
def serialize( klass, inputs, outputs, for_sig = None ):
s = int_to_hex(1,4) # version
s += var_int( len(inputs) ) # number of inputs
for i in range(len(inputs)):
txin = inputs[i]
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:
signatures = txin['signatures']
pubkeys = txin['pubkeys']
if not txin.get('redeemScript'):
pubkey = pubkeys[0]
sig = signatures[0]
sig = sig + '01' # hashtype
script = op_push(len(sig)/2)
script += sig
script += op_push(len(pubkey)/2)
script += pubkey
else:
script = '00' # op_0
for sig in signatures:
sig = sig + '01'
script += op_push(len(sig)/2)
script += sig
redeem_script = klass.multisig_script(pubkeys,2)
script += op_push(len(redeem_script)/2)
script += redeem_script
elif for_sig==i:
if txin.get('redeemScript'):
script = txin['redeemScript'] # p2sh uses the inner script
else:
script = txin['raw_output_script'] # scriptsig
else:
script=''
s += var_int( len(script)/2 ) # script length
s += script
s += "ffffffff" # sequence
s += var_int( len(outputs) ) # number of outputs
for output in outputs:
addr, amount = output
s += int_to_hex( amount, 8) # amount
addrtype, hash_160 = bc_address_to_hash_160(addr)
if addrtype == 0:
script = '76a9' # op_dup, op_hash_160
script += '14' # push 0x14 bytes
script += hash_160.encode('hex')
script += '88ac' # op_equalverify, op_checksig
elif addrtype == 5:
script = 'a9' # op_hash_160
script += '14' # push 0x14 bytes
script += hash_160.encode('hex')
script += '87' # op_equal
else:
raise
s += var_int( len(script)/2 ) # script length
s += script # script
s += int_to_hex(0,4) # lock time
if for_sig is not None and for_sig != -1:
s += int_to_hex(1, 4) # hash type
return s
def for_sig(self,i):
return self.serialize(self.inputs, self.outputs, for_sig = i)
def hash(self):
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
def sign(self, keypairs):
is_complete = True
print_error("tx.sign(), keypairs:", keypairs)
for i, txin in enumerate(self.inputs):
# if the input is multisig, parse redeem script
redeem_script = txin.get('redeemScript')
num, redeem_pubkeys = parse_redeemScript(redeem_script) if redeem_script else (1, [txin.get('redeemPubkey')])
# add pubkeys
txin["pubkeys"] = redeem_pubkeys
# get list of already existing signatures
signatures = txin.get("signatures",[])
# continue if this txin is complete
if len(signatures) == num:
continue
tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
for pubkey in redeem_pubkeys:
# check if we have the corresponding private key
if pubkey in keypairs.keys():
# add signature
sec = keypairs[pubkey]
compressed = is_compressed(sec)
pkey = regenerate_key(sec)
secexp = pkey.secret
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
public_key = private_key.get_verifying_key()
sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
signatures.append( sig.encode('hex') )
print_error("adding signature for", pubkey)
txin["signatures"] = signatures
is_complete = is_complete and len(signatures) == num
self.is_complete = is_complete
self.raw = self.serialize( self.inputs, self.outputs )
def deserialize(self):
vds = BCDataStream()
vds.write(self.raw.decode('hex'))
d = {}
start = vds.read_cursor
d['version'] = vds.read_int32()
n_vin = vds.read_compact_size()
d['inputs'] = []
for i in xrange(n_vin):
d['inputs'].append(self.parse_input(vds))
n_vout = vds.read_compact_size()
d['outputs'] = []
for i in xrange(n_vout):
d['outputs'].append(self.parse_output(vds, i))
d['lockTime'] = vds.read_uint32()
self.d = d
return self.d
def parse_input(self, vds):
d = {}
d['prevout_hash'] = hash_encode(vds.read_bytes(32))
d['prevout_n'] = vds.read_uint32()
scriptSig = vds.read_bytes(vds.read_compact_size())
d['sequence'] = vds.read_uint32()
if scriptSig:
pubkeys, signatures, address = get_address_from_input_script(scriptSig)
else:
pubkeys = []
signatures = []
address = None
d['address'] = address
d['signatures'] = signatures
return d
def parse_output(self, vds, i):
d = {}
d['value'] = vds.read_int64()
scriptPubKey = vds.read_bytes(vds.read_compact_size())
is_pubkey, address = get_address_from_output_script(scriptPubKey)
d['is_pubkey'] = is_pubkey
d['address'] = address
d['raw_output_script'] = scriptPubKey.encode('hex')
d['index'] = i
return d
def add_extra_addresses(self, txlist):
for i in self.inputs:
if i.get("address") == "(pubkey)":
prev_tx = txlist.get(i.get('prevout_hash'))
if prev_tx:
address, value = prev_tx.outputs[i.get('prevout_n')]
print_error("found pay-to-pubkey address:", address)
i["address"] = address
def has_address(self, addr):
found = False
for txin in self.inputs:
if addr == txin.get('address'):
found = True
break
for txout in self.outputs:
if addr == txout[0]:
found = True
break
return found
def get_value(self, addresses, prevout_values):
# return the balance for that tx
is_relevant = False
is_send = False
is_pruned = False
is_partial = False
v_in = v_out = v_out_mine = 0
for item in self.inputs:
addr = item.get('address')
if addr in addresses:
is_send = True
is_relevant = True
key = item['prevout_hash'] + ':%d'%item['prevout_n']
value = prevout_values.get( key )
if value is None:
is_pruned = True
else:
v_in += value
else:
is_partial = True
if not is_send: is_partial = False
for item in self.outputs:
addr, value = item
v_out += value
if addr in addresses:
v_out_mine += value
is_relevant = True
if is_pruned:
# some inputs are mine:
fee = None
if is_send:
v = v_out_mine - v_out
else:
# no input is mine
v = v_out_mine
else:
v = v_out_mine - v_in
if is_partial:
# some inputs are mine, but not all
fee = None
is_send = v < 0
else:
# all inputs are mine
fee = v_out - v_in
return is_relevant, is_send, v, fee
def as_dict(self):
import json
out = {
"hex":self.raw,
"complete":self.is_complete
}
if not self.is_complete:
extras = []
for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'],
'scriptPubKey':i.get('raw_output_script'),
'KeyID':i.get('KeyID'),
'redeemScript':i.get('redeemScript'),
'signatures':i.get('signatures'),
'pubkeys':i.get('pubkeys'),
}
extras.append(e)
self.input_info = extras
if self.input_info:
out['input_info'] = json.dumps(self.input_info).replace(' ','')
return out
def requires_fee(self, verifier):
# see https://en.bitcoin.it/wiki/Transaction_fees
threshold = 57600000
size = len(self.raw)/2
if size >= 10000:
return True
for o in self.outputs:
value = o[1]
if value < 1000000:
return True
sum = 0
for i in self.inputs:
age = verifier.get_confirmations(i["tx_hash"])[0]
sum += i["value"] * age
priority = sum / size
print_error(priority, threshold)
return priority < threshold

View File

@ -33,6 +33,7 @@ import time
from util import print_msg, print_error, format_satoshis from util import print_msg, print_error, format_satoshis
from bitcoin import * from bitcoin import *
from account import * from account import *
from transaction import Transaction
# AES encryption # AES encryption
EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
@ -164,12 +165,17 @@ class Wallet:
self.load_accounts() self.load_accounts()
self.transactions = {} self.transactions = {}
tx = storage.get('transactions',{}) tx_list = self.storage.get('transactions',{})
try: for k,v in tx_list.items():
for k,v in tx.items(): self.transactions[k] = Transaction(v) tx = Transaction(v)
except: try:
print_msg("Warning: Cannot deserialize transactions. skipping") tx = Transaction(v)
except:
print_msg("Warning: Cannot deserialize transactions. skipping")
continue
self.add_transaction(tx)
# not saved # not saved
self.prevout_values = {} # my own transaction outputs self.prevout_values = {} # my own transaction outputs
self.spent_outputs = [] self.spent_outputs = []
@ -194,6 +200,17 @@ class Wallet:
self.transactions.pop(tx_hash) self.transactions.pop(tx_hash)
def add_transaction(self, tx):
h = tx.hash()
self.transactions[h] = tx
# find the address corresponding to pay-to-pubkey inputs
tx.add_extra_addresses(self.transactions)
for o in tx.d.get('outputs'):
if o.get('is_pubkey'):
for tx2 in self.transactions.values():
tx2.add_extra_addresses({h:tx})
def set_up_to_date(self,b): def set_up_to_date(self,b):
with self.lock: self.up_to_date = b with self.lock: self.up_to_date = b
@ -521,7 +538,7 @@ class Wallet:
def signrawtransaction(self, tx, input_info, private_keys, password): def signrawtransaction(self, tx, input_info, private_keys, password):
import deserialize
unspent_coins = self.get_unspent_coins() unspent_coins = self.get_unspent_coins()
seed = self.decode_seed(password) seed = self.decode_seed(password)
@ -587,7 +604,7 @@ class Wallet:
if redeem_script: if redeem_script:
addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5) addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5)
else: else:
addr = deserialize.get_address_from_output_script(txin["raw_output_script"].decode('hex')) addr = transaction.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
txin['address'] = addr txin['address'] = addr
# add private keys that are in the wallet # add private keys that are in the wallet
@ -1002,10 +1019,8 @@ class Wallet:
return return
with self.transaction_lock: with self.transaction_lock:
self.transactions[tx_hash] = tx self.add_transaction(tx)
self.interface.pending_transactions_for_notifications.append(tx) self.interface.pending_transactions_for_notifications.append(tx)
self.save_transactions() self.save_transactions()
if self.verifier and tx_height>0: if self.verifier and tx_height>0:
self.verifier.add(tx_hash, tx_height) self.verifier.add(tx_hash, tx_height)

View File

@ -5,7 +5,8 @@ from PyQt4.QtCore import Qt
from electrum_gui.i18n import _ from electrum_gui.i18n import _
import re import re
from electrum.bitcoin import MIN_RELAY_TX_FEE, Transaction, is_valid from electrum import Transaction
from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
from electrum_gui.qrcodewidget import QRCodeWidget from electrum_gui.qrcodewidget import QRCodeWidget
import electrum_gui.bmp import electrum_gui.bmp
import json import json

View File

@ -67,7 +67,7 @@ setup(name = "Electrum",
'electrum.util', 'electrum.util',
'electrum.account', 'electrum.account',
'electrum.bitcoin', 'electrum.bitcoin',
'electrum.deserialize', 'electrum.transaction',
'electrum.verifier', 'electrum.verifier',
'electrum_gui.gui_gtk', 'electrum_gui.gui_gtk',
'electrum_gui.qt_console', 'electrum_gui.qt_console',