685 lines
29 KiB
Python
685 lines
29 KiB
Python
"""
|
|
Old code will be removed
|
|
"""
|
|
|
|
|
|
import io
|
|
import json
|
|
import math
|
|
from .opcodes import *
|
|
from .tools import *
|
|
from .consensus import *
|
|
from binascii import hexlify, unhexlify
|
|
|
|
k = 0
|
|
|
|
|
|
|
|
|
|
|
|
class OLDTransaction():
|
|
def __init__(self, version = 1, tx_in = [], tx_out = [] , lock_time = 0,
|
|
hash=None, size = 0, timestamp = None,
|
|
marker = None, flag = None, witness = [],
|
|
whash = None, vsize = None):
|
|
self.hash = hash
|
|
self.whash = whash
|
|
self.vsize = vsize
|
|
self.witness = witness
|
|
self.marker = marker
|
|
self.flag = flag
|
|
self.valid = True
|
|
self.lock = False
|
|
self.in_sum = None
|
|
self.tx_fee = None
|
|
self.version = version
|
|
self.tx_in_count = len(tx_in)
|
|
self.tx_in = list(tx_in)
|
|
self.tx_out_count = len(tx_out)
|
|
self.tx_out = list(tx_out)
|
|
self.lock_time = lock_time
|
|
if self.tx_in:
|
|
self.coinbase = self.tx_in[0].coinbase
|
|
else:
|
|
self.coinbase = None
|
|
if self.coinbase:
|
|
self.whash = b"\x00" * 32
|
|
self.double_spend = 0
|
|
self.data = None
|
|
self.ip = None
|
|
self.size = size
|
|
if timestamp is not None : self.timestamp = timestamp
|
|
else: self.timestamp = int(time.time())
|
|
self.op_sig_count = 0
|
|
self.sum_value_age = 0
|
|
self.total_outs_value = 0
|
|
for i in self.tx_out:
|
|
self.op_sig_count += i.pk_script.op_sig_count
|
|
if i.pk_script.type=="NULL_DATA":
|
|
self.data = i.pk_script.data
|
|
for out in self.tx_out:
|
|
self.total_outs_value += out.value
|
|
if not self.tx_in:
|
|
self.witness = list()
|
|
if witness is None:
|
|
self.witness = (Witness.deserialize(b"\x00") for i in range(len(tx_in)))
|
|
if hash is None :
|
|
self.recalculate_txid()
|
|
|
|
def recalculate_txid(self):
|
|
self.tx_in_count = len(self.tx_in)
|
|
self.tx_out_count = len(self.tx_out)
|
|
t = self.serialize(segwit=False)
|
|
t2 = self.serialize(segwit=True)
|
|
self.hash = double_sha256(t)
|
|
if self.coinbase:
|
|
self.whash = b"\x00" * 32
|
|
else:
|
|
self.whash = double_sha256(t2)
|
|
self.size = len(t)
|
|
self.vsize = math.ceil((self.size * 3 + self.size) / 4)
|
|
|
|
def txid(self):
|
|
return rh2s(self.hash)
|
|
|
|
def add_input(self, tx_hash, output_number,
|
|
sequence = 0xffffffff,
|
|
sig_script = b"",
|
|
amount = None,
|
|
private_key = None):
|
|
self.tx_in.append(Input((tx_hash, output_number), sig_script, sequence, amount, private_key))
|
|
self.witness.append(Witness.deserialize(b"\x00"))
|
|
self.tx_in_count += 1
|
|
self.recalculate_txid()
|
|
|
|
def add_output_script(self, amount, script):
|
|
if type(script)==str:
|
|
script = unhexlify(script)
|
|
self.tx_out.append(Output(amount,script))
|
|
self.tx_out_count += 1
|
|
self.recalculate_txid()
|
|
|
|
def add_output_address(self, amount, address, testnet = False):
|
|
assert is_address_valid(address, testnet)
|
|
output_type = address_type(address, True)
|
|
if output_type == 0:
|
|
self.add_P2PKH_output(amount, address)
|
|
elif output_type == 1:
|
|
self.add_P2SH_output(amount, address)
|
|
elif output_type == 5:
|
|
self.add_P2WPKH_output(amount, address)
|
|
elif output_type == 6:
|
|
self.add_P2WSH_output(amount, address)
|
|
|
|
|
|
def add_output_hash(self, amount, output_hash, output_type, witness_version = 0):
|
|
if type(output_type)==str:
|
|
output_type = SCRIPT_TYPES[output_type]
|
|
if output_hash == str:
|
|
output_hash = unhexlify(output_hash)
|
|
assert output_type in (0, 1, 5, 6)
|
|
if output_type == 0:
|
|
self.add_P2PKH_output(amount, output_hash)
|
|
elif output_type == 1:
|
|
self.add_P2SH_output(amount, output_hash)
|
|
elif output_type == 5:
|
|
self.add_P2WPKH_output(amount, output_hash, witness_version)
|
|
elif output_type == 6:
|
|
self.add_P2WSH_output(amount, output_hash, witness_version)
|
|
|
|
|
|
def add_P2WPKH_output(self, amount, p2wpkh_address, witness_version = 0):
|
|
if type(p2wpkh_address)==str:
|
|
assert address_type(p2wpkh_address) == 'P2WPKH'
|
|
witness_version = get_witness_version(p2wpkh_address)
|
|
p2wpkh_address = address_to_hash(p2wpkh_address)
|
|
assert len(p2wpkh_address) == 20
|
|
self.tx_out.append(Output(amount,
|
|
bytes([witness_version]) + b'\x14' + p2wpkh_address))
|
|
self.tx_out_count += 1
|
|
self.recalculate_txid()
|
|
|
|
def add_P2WSH_output(self, amount, p2wsh_address, witness_version = 0):
|
|
if type(p2wsh_address)==str:
|
|
assert address_type(p2wsh_address) == 'P2WSH'
|
|
witness_version = get_witness_version(p2wsh_address)
|
|
p2wsh_address = address_to_hash(p2wsh_address)
|
|
assert len(p2wsh_address) == 32
|
|
self.tx_out.append(Output(amount,
|
|
bytes([witness_version]) + b'\x20' + p2wsh_address))
|
|
self.tx_out_count += 1
|
|
self.recalculate_txid()
|
|
|
|
def add_P2SH_output(self, amount, p2sh_address):
|
|
if type(p2sh_address)==str:
|
|
assert address_type(p2sh_address) == 'P2SH'
|
|
p2sh_address = decode_base58(p2sh_address)[1:-4]
|
|
if len(p2sh_address) != 20:
|
|
raise Exception("Invalid output hash160")
|
|
self.tx_out.append(Output(amount,
|
|
OPCODE["OP_HASH160"] + b'\x14' + p2sh_address + OPCODE["OP_EQUAL"]))
|
|
self.tx_out_count += 1
|
|
self.recalculate_txid()
|
|
|
|
|
|
def add_P2PKH_output(self, amount, p2pkh_address):
|
|
if type(p2pkh_address)==str:
|
|
assert address_type(p2pkh_address) == 'P2PKH'
|
|
p2pkh_address = decode_base58(p2pkh_address)[1:-4]
|
|
if len(p2pkh_address) != 20:
|
|
raise p2pkh_address("Invalid output hash160")
|
|
self.tx_out.append(Output(amount,
|
|
OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] + b'\x14' + \
|
|
p2pkh_address + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"]))
|
|
self.tx_out_count += 1
|
|
self.recalculate_txid()
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
return 'Transaction object [%s] [%s]'% (hexlify(self.hash[::-1]),id(self))
|
|
|
|
|
|
def serialize(self, segwit = True, hex = False):
|
|
version = self.version.to_bytes(4,'little')
|
|
ninputs = int_to_var_int(self.tx_in_count)
|
|
inputs = [i.serialize() for i in self.tx_in]
|
|
nouts = int_to_var_int(len(self.tx_out))
|
|
outputs = [o.serialize() for o in self.tx_out]
|
|
marke_flag = b"\x00\x01" if segwit else b""
|
|
witness = b""
|
|
if segwit:
|
|
for w in self.witness:
|
|
witness += w.serialize()
|
|
result = version + marke_flag + ninputs + b''.join(inputs) +\
|
|
nouts + b''.join(outputs) + witness + self.lock_time.to_bytes(4,'little')
|
|
if hex:
|
|
return hexlify(result).decode()
|
|
else:
|
|
return result
|
|
|
|
def sign_P2SHP2WPKH_input(self, sighash_type, input_index, private_key = None, amount = None):
|
|
if type(private_key) == str:
|
|
private_key = wif_to_private_key(private_key)
|
|
if amount is not None:
|
|
self.tx_in[input_index].amount = amount
|
|
else:
|
|
amount = self.tx_in[input_index].amount
|
|
if private_key is not None:
|
|
self.tx_in[input_index].private_key = private_key
|
|
else:
|
|
private_key = self.tx_in[input_index].private_key
|
|
pubkey = private_to_public_key(private_key, True)
|
|
pubkey_hash160 = hash160(pubkey)
|
|
scriptCode = b"\x19" + OPCODE["OP_DUP"] + OPCODE["OP_HASH160"]
|
|
scriptCode += b'\x14' + pubkey_hash160 + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"]
|
|
self.tx_in[input_index].sig_script = Script(b'\x16\x00\x14' + pubkey_hash160) # P2WPKHredeemScript
|
|
sighash = self.sighash_segwit(sighash_type, input_index, scriptCode, amount)
|
|
signature = sign_message(sighash, private_key) + sighash_type.to_bytes(1,'little')
|
|
self.witness[input_index] = Witness([signature, pubkey])
|
|
self.recalculate_txid()
|
|
|
|
def sign_P2PKH_input(self, sighash_type, input_index, compressed = True, private_key = None):
|
|
if private_key is not None:
|
|
self.tx_in[input_index].private_key = private_key
|
|
else:
|
|
private_key = self.tx_in[input_index].private_key
|
|
pubkey = private_to_public_key(private_key, compressed)
|
|
pubkey_hash160 = hash160(pubkey)
|
|
scriptCode = OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] + b'\x14' + \
|
|
pubkey_hash160 + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"]
|
|
sighash = self.sighash(sighash_type, input_index, scriptCode)
|
|
signature = sign_message(sighash, private_key) + sighash_type.to_bytes(1, 'little')
|
|
sig_script = len(signature).to_bytes(1, 'little') + signature + \
|
|
len(pubkey).to_bytes(1, 'little') + pubkey
|
|
self.tx_in[input_index].sig_script = Script(sig_script)
|
|
self.recalculate_txid()
|
|
|
|
def sighash(self, sighash_type, input_index, scriptCode, hex = False):
|
|
if type(scriptCode) == str:
|
|
scriptCode = unhexlify(scriptCode)
|
|
if len(self.tx_in) - 1 < input_index:
|
|
raise Exception('Input not exist')
|
|
preimage = bytearray()
|
|
if ((sighash_type&31) == SIGHASH_SINGLE) and (input_index>(len(self.tx_out)-1)):
|
|
return double_sha256(b'\x01'+b'\x00'*31 + sighash_type.to_bytes(4, 'little'))
|
|
preimage += self.version.to_bytes(4,'little')
|
|
preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(self.tx_in_count)
|
|
for number, i in enumerate(self.tx_in):
|
|
if (sighash_type & SIGHASH_ANYONECANPAY) and (input_index != number): continue
|
|
input = i.outpoint[0]+i.outpoint[1].to_bytes(4,'little')
|
|
if sighash_type == 0 or input_index == number:
|
|
input += ((int_to_var_int(len(scriptCode)) + scriptCode) if sighash_type else \
|
|
(int_to_var_int(len(i.sig_script.raw)) + i.sig_script.raw)) + i.sequence.to_bytes(4, 'little')
|
|
else:
|
|
input += b'\x00' + (i.sequence.to_bytes(4,'little') if \
|
|
((sighash_type&31) == SIGHASH_ALL) else b'\x00\x00\x00\x00')
|
|
preimage += input
|
|
preimage += b'\x00' if (sighash_type&31) == SIGHASH_NONE else (int_to_var_int(input_index + 1) if \
|
|
(sighash_type&31) == SIGHASH_SINGLE else int_to_var_int(self.tx_out_count))
|
|
if (sighash_type&31) != SIGHASH_NONE:
|
|
for number, i in enumerate(self.tx_out):
|
|
if number > input_index and (sighash_type&31) == SIGHASH_SINGLE: continue
|
|
preimage +=(b'\xff'*8+b'\x00' if (sighash_type&31) == SIGHASH_SINGLE and (input_index != number)\
|
|
else i.value.to_bytes(8,'little') + int_to_var_int(len(i.pk_script.raw)) + i.pk_script.raw)
|
|
preimage += self.lock_time.to_bytes(4,'little')
|
|
preimage += sighash_type.to_bytes(4, 'little')
|
|
return double_sha256(preimage) if not hex else hexlify(double_sha256(preimage)).decode()
|
|
|
|
|
|
def sighash_segwit(self, sighash_type, input_index, scriptCode, amount, hex = False):
|
|
if type(scriptCode) == str:
|
|
scriptCode = unhexlify(scriptCode)
|
|
if len(self.tx_in)-1 < input_index:
|
|
raise Exception('Input not exist')
|
|
preimage = bytearray()
|
|
# 1. nVersion of the transaction (4-byte little endian)
|
|
preimage += self.version.to_bytes(4,'little')
|
|
# 2. hashPrevouts (32-byte hash)
|
|
# 3. hashSequence (32-byte hash)
|
|
# 4. outpoint (32-byte hash + 4-byte little endian)
|
|
# 5. scriptCode of the input (serialized as scripts inside CTxOuts)
|
|
# 6. value of the output spent by this input (8-byte little endian)
|
|
# 7. nSequence of the input (4-byte little endian)
|
|
hp = bytearray()
|
|
hs = bytearray()
|
|
for n, i in enumerate(self.tx_in):
|
|
if not (sighash_type & SIGHASH_ANYONECANPAY):
|
|
hp += i.outpoint[0] + i.outpoint[1].to_bytes(4,'little')
|
|
if (sighash_type&31) != SIGHASH_SINGLE and (sighash_type&31) != SIGHASH_NONE:
|
|
hs += i.sequence.to_bytes(4,'little')
|
|
if n == input_index:
|
|
outpoint = i.outpoint[0]+i.outpoint[1].to_bytes(4,'little')
|
|
nSequence = i.sequence.to_bytes(4,'little')
|
|
hashPrevouts = double_sha256(hp) if hp else b'\x00'*32
|
|
hashSequence = double_sha256(hs) if hs else b'\x00'*32
|
|
value = amount.to_bytes(8,'little')
|
|
# 8. hashOutputs (32-byte hash)
|
|
ho = bytearray()
|
|
for n, o in enumerate(self.tx_out):
|
|
if (sighash_type&31) != SIGHASH_SINGLE and (sighash_type&31) != SIGHASH_NONE:
|
|
ho += o.value.to_bytes(8,'little') + int_to_var_int(len(o.pk_script.raw)) + o.pk_script.raw
|
|
elif (sighash_type&31) == SIGHASH_SINGLE and input_index < len(self.tx_out):
|
|
if input_index == n:
|
|
ho += o.value.to_bytes(8, 'little') + int_to_var_int(len(o.pk_script.raw)) + o.pk_script.raw
|
|
hashOutputs = double_sha256(ho) if ho else b'\x00'*32
|
|
preimage += hashPrevouts + hashSequence + outpoint + scriptCode + value + nSequence + hashOutputs
|
|
preimage += self.lock_time.to_bytes(4, 'little')
|
|
preimage += sighash_type.to_bytes(4, 'little')
|
|
return double_sha256(preimage) if not hex else hexlify(double_sha256(preimage)).decode()
|
|
|
|
|
|
def json(self, testnet = False):
|
|
r = dict()
|
|
r["txid"] = rh2s(self.hash)
|
|
r["wtxid"] = r["txid"] if self.whash is None else rh2s(self.whash)
|
|
r["size"] = self.size
|
|
r["vsize"] = self.vsize
|
|
r["version"] = self.version
|
|
r["locktime"] = self.lock_time
|
|
r["vin"] = list()
|
|
r["vout"] = list()
|
|
for i in self.tx_in:
|
|
input = {"txid": rh2s(i.outpoint[0]),
|
|
"vout": i.outpoint[1],
|
|
"scriptSig": {"hex": hexlify(i.sig_script.raw).decode(),
|
|
"asm": i.sig_script.asm},
|
|
"sequence": i.sequence}
|
|
if i.coinbase:
|
|
input["coinbase"] = hexlify(i.sig_script.raw).decode()
|
|
r["vin"].append(input)
|
|
if self.witness is not None:
|
|
for index, w in enumerate(self.witness):
|
|
r["vin"][index]["witness"] = w.hex()
|
|
for index, o in enumerate(self.tx_out):
|
|
out = {"value": o.value,
|
|
"n": index,
|
|
"scriptPubKey": {"hex": hexlify(o.pk_script.raw).decode()},
|
|
"asm": o.pk_script.asm,
|
|
"type": o.pk_script.type}
|
|
if self.witness is not None:
|
|
out["witnessVersion"] = o.pk_script.witness_version
|
|
out["address"] = []
|
|
sh = False
|
|
if o.pk_script.ntype in (1,6):
|
|
sh =True
|
|
for a in o.pk_script.address:
|
|
out["address"].append(hash_to_address(a,
|
|
testnet=testnet,
|
|
script_hash= sh,
|
|
witness_version=o.pk_script.witness_version))
|
|
|
|
r["vout"].append(out)
|
|
|
|
return json.dumps(r)
|
|
|
|
|
|
@classmethod
|
|
def deserialize(cls, stream):
|
|
stream = get_stream(stream)
|
|
raw_tx = bytearray()
|
|
raw_wtx = bytearray()
|
|
start = stream.tell()
|
|
version = int.from_bytes(stream.read(4), 'little')
|
|
marker = stream.read(1)
|
|
flag = stream.read(1)
|
|
if marker == b"\x00" and flag == b"\x01":
|
|
# segwit format
|
|
point1 = stream.tell()
|
|
tx_in = read_var_list(stream, Input)
|
|
tx_out = read_var_list(stream, Output)
|
|
point2 = stream.tell()
|
|
inputs_count = len(tx_in)
|
|
witness = [Witness.deserialize(stream) for i in range(inputs_count)]
|
|
point3 = stream.tell()
|
|
lock_time = int.from_bytes(stream.read(4), 'little')
|
|
# calculate tx_id hash
|
|
size = stream.tell() - start
|
|
stream.seek(start)
|
|
raw_tx += stream.read(4)
|
|
stream.seek(2,1)
|
|
raw_tx += stream.read(point2 - point1)
|
|
stream.seek(point3-point2, 1)
|
|
raw_tx += stream.read(4)
|
|
tx_id = double_sha256(raw_tx)
|
|
for w in witness:
|
|
if not w.empty:
|
|
# caluculate wtx_id
|
|
stream.seek(start)
|
|
data = stream.read(size)
|
|
wtx_id = double_sha256(data)
|
|
break
|
|
else:
|
|
wtx_id = tx_id
|
|
vsize = math.ceil((size * 3 + size) / 4)
|
|
else:
|
|
stream.seek(start)
|
|
marker = b"\x00"
|
|
flag = b"\x01"
|
|
version = int.from_bytes(stream.read(4), 'little')
|
|
tx_in = read_var_list(stream, Input)
|
|
tx_out = read_var_list(stream, Output)
|
|
witness = [Witness.deserialize(b"\x00") for i in range(len(tx_in))]
|
|
lock_time = int.from_bytes(stream.read(4), 'little')
|
|
size = stream.tell() - start
|
|
stream.seek(start)
|
|
data = stream.read(size)
|
|
tx_id = double_sha256(data)
|
|
wtx_id = tx_id
|
|
vsize = size
|
|
|
|
return cls(version, tx_in, tx_out, lock_time,
|
|
hash = tx_id, size = size,
|
|
marker = marker, flag = flag,
|
|
witness = witness, whash = wtx_id, vsize = vsize)
|
|
|
|
|
|
class OLDBlock():
|
|
def __init__(self, version, prev_block, merkle_root,
|
|
timestamp, bits, nonce, txs, block_size, hash = None, header = None):
|
|
qt = time.time()
|
|
self.hash = hash
|
|
self.header = header
|
|
self.version = version
|
|
self.nversion = int.from_bytes(version,'little')
|
|
self.prev_block = prev_block
|
|
self.merkle_root = merkle_root
|
|
self.timestamp = timestamp
|
|
self.bits = bits
|
|
self.nonce = nonce
|
|
|
|
self.transactions = txs
|
|
self.tx_hash_list = list()
|
|
self.size = block_size
|
|
self.weight = block_size
|
|
self.height = None
|
|
self.amount = 0
|
|
self.fee = 0
|
|
self.sigop = 0
|
|
|
|
|
|
self.target = None
|
|
self.fee = 0
|
|
self.witness_root_hash = None
|
|
|
|
if txs:
|
|
if txs[0].coinbase:
|
|
if self.nversion > 1:
|
|
self.height = int.from_bytes(txs[0].tx_in[0].sig_script.raw[1:5], "little")
|
|
self.coinbase = txs[0].tx_in[0].sig_script.raw[5:]
|
|
else:
|
|
self.coinbase = txs[0].tx_in[0].sig_script.raw
|
|
try:
|
|
for out in txs[0].tx_out:
|
|
if out.pk_script.ntype == 3:
|
|
if b'\xaa!\xa9\xed' == out.pk_script.data[:4]:
|
|
self.witness_root_hash = out.pk_script.data[4:36]
|
|
except:
|
|
pass
|
|
print("t ", time.time() - qt)
|
|
|
|
def calculate_commitment(self, witness = None):
|
|
wtxid_list = [b"\x00" * 32,]
|
|
print(len(self.transactions))
|
|
if self.transactions and not (len(self.transactions) == 1 and self.transactions[0].coinbase):
|
|
for tx in self.transactions[0 if not self.transactions[0].coinbase else 1:]:
|
|
wtxid_list.append(tx.whash)
|
|
if witness is None:
|
|
return double_sha256(merkleroot(wtxid_list) + self.transactions[0].witness[0].witness[0])
|
|
else:
|
|
return double_sha256(merkleroot(wtxid_list) + witness)
|
|
|
|
def create_coinbase_transaction(self, block_height, outputs, coinbase_message = b"", insert = True):
|
|
tx = Transaction(version = 1,tx_in = [], tx_out = [], witness= [] )
|
|
coinbase = b'\x03' + block_height.to_bytes(4,'little') + coinbase_message
|
|
if len(coinbase) > 100:
|
|
raise Exception("coinbase is to long")
|
|
coinbase_input = Input((b'\x00'*32 ,0xffffffff), coinbase, 0xffffffff)
|
|
tx.tx_in = [coinbase_input]
|
|
tx.witness = [Witness([b'\x00'*32])]
|
|
commitment = self.calculate_commitment(tx.witness[0].witness[0])
|
|
for o in outputs:
|
|
if type(o[1]) == str:
|
|
tx.tx_out.append(Output(o[0], address_to_script(o[1])))
|
|
else:
|
|
tx.tx_out.append(Output(o[0], o[1]))
|
|
tx.tx_out.append(Output(0, b'j$\xaa!\xa9\xed' + commitment))
|
|
tx.coinbase = True
|
|
tx.recalculate_txid()
|
|
if insert:
|
|
if self.transactions:
|
|
if self.transactions[0].coinbase:
|
|
self.transactions[0] = tx
|
|
self.tx_hash_list[0] = tx.hash
|
|
else:
|
|
self.transactions.insert(0,tx)
|
|
self.tx_hash_list.insert(0, tx.hash)
|
|
else:
|
|
self.transactions.insert(0, tx)
|
|
self.tx_hash_list.insert(0, tx.hash)
|
|
return tx
|
|
|
|
def split_coinbase(self, extranonce_size = 8, extranonce_start = -8):
|
|
tx = self.transactions[0].serialize()
|
|
len_coinbase = len(self.transactions[0].tx_in[0].sig_script.raw)
|
|
if extranonce_start < 0:
|
|
extranonce_start = len_coinbase + extranonce_start
|
|
return tx[:44 + extranonce_start], tx[44 + extranonce_start + extranonce_size:]
|
|
|
|
@classmethod
|
|
def deserialize(cls, stream):
|
|
stream = get_stream(stream)
|
|
header = stream.read(80)
|
|
stream.seek(-80, 1)
|
|
kwargs = {
|
|
'hash': double_sha256(header),
|
|
'version': stream.read(4),
|
|
'prev_block': stream.read(32),
|
|
'merkle_root': stream.read(32),
|
|
'timestamp': int.from_bytes(stream.read(4), 'little'),
|
|
'bits': stream.read(4),
|
|
'nonce': stream.read(4)}
|
|
t = time.time()
|
|
kwargs['txs'] = read_var_list(stream, Transaction)
|
|
print("tx ",time.time() - t)
|
|
kwargs['block_size'] = stream.tell()
|
|
kwargs['header'] = header
|
|
global k
|
|
print(">.>.>.",k)
|
|
return cls(**kwargs)
|
|
|
|
def serialize(self, hex = False):
|
|
block = self.version + \
|
|
self.prev_block + \
|
|
self.merkle_root + \
|
|
self.timestamp.to_bytes(4,'little') + \
|
|
self.bits + \
|
|
self.nonce + \
|
|
int_to_var_int(len (self.transactions))
|
|
for t in self.transactions:
|
|
if t.hash == t.whash:
|
|
block += t.serialize(segwit = 0)
|
|
else:
|
|
block += t.serialize(segwit = 1)
|
|
if hex:
|
|
return hexlify(block).decode()
|
|
else:
|
|
return block
|
|
|
|
|
|
class BlockTemplate():
|
|
def __init__(self, data, coinbase_output_address, testnet = False, coinbase_message = "",
|
|
extranonce1 = "00000000",
|
|
extranonce1_size = 4,
|
|
extranonce2_size = 4):
|
|
self.testnet = testnet
|
|
self.version = hexlify(data["version"].to_bytes(4, "big")).decode()
|
|
self.previous_block_hash = hexlify(reverse_hash(s2rh(data["previousblockhash"]))).decode()
|
|
self.time = hexlify(data["curtime"].to_bytes(4, "big")).decode()
|
|
self.bits = data["bits"]
|
|
self.height = data["height"]
|
|
self.block_reward = 50 * 100000000 >> data["height"] // 210000
|
|
self.coinbasevalue = self.block_reward
|
|
self.extranonce1 = extranonce1
|
|
self.extranonce1_size = extranonce1_size
|
|
self.extranonce2 = "00000000"
|
|
self.extranonce2_size = extranonce2_size
|
|
self.coinbase_output_address = coinbase_output_address
|
|
self.sigoplimit = data["sigoplimit"]
|
|
self.weightlimit = data["weightlimit"]
|
|
self.sigop= 0
|
|
self.weight = 0
|
|
if type(coinbase_message) == bytes:
|
|
coinbase_message = hexlify(coinbase_message).decode()
|
|
self.coinbase_message = coinbase_message
|
|
|
|
self.transactions = list(data["transactions"])
|
|
self.txid_list = list()
|
|
self.scan_tx_list()
|
|
self.coinbase_tx = self.create_coinbase_transaction()
|
|
self.coinb1, self.coinb2 = self.split_coinbase()
|
|
self.target = bits_to_target(self.bits)
|
|
self.difficulty = target_to_difficulty(self.target)
|
|
self.merkle_branches = [hexlify(i).decode() for i in merkle_branches([self.coinbase_tx.hash,] + self.txid_list)]
|
|
|
|
|
|
def scan_tx_list(self):
|
|
self.coinbasevalue = self.block_reward
|
|
self.sigop = 0
|
|
self.weight = 0
|
|
self.txid_list = list()
|
|
for tx in self.transactions:
|
|
txid = s2rh(tx["txid"])
|
|
self.coinbasevalue += tx["fee"]
|
|
self.weight += tx["weight"]
|
|
self.sigop += tx["sigops"]
|
|
self.txid_list.append(txid)
|
|
|
|
def calculate_commitment(self, witness):
|
|
wtxid_list = [b"\x00" * 32,]
|
|
if self.transactions:
|
|
for tx in self.transactions:
|
|
wtxid_list.append(s2rh(tx["hash"]))
|
|
return double_sha256(merkleroot(wtxid_list) + witness)
|
|
|
|
def split_coinbase(self):
|
|
tx = self.coinbase_tx.serialize(segwit=0)
|
|
len_coinbase = len(self.coinbase_tx.tx_in[0].sig_script.raw)
|
|
extranonce_len = self.extranonce1_size + self.extranonce2_size
|
|
return hexlify(tx[:42 + len_coinbase - extranonce_len]).decode(),\
|
|
hexlify(tx[42 + len_coinbase:]).decode()
|
|
|
|
|
|
def create_coinbase_transaction(self):
|
|
tx = Transaction(version = 1,tx_in = [], tx_out = [], witness= [])
|
|
coinbase = b'\x03' + self.height.to_bytes(4,'little') + unhexlify(self.coinbase_message)
|
|
coinbase += b"\x00" * (self.extranonce1_size + self.extranonce2_size)
|
|
assert len(coinbase) <= 100
|
|
tx.tx_in = [Input((b'\x00'*32 ,0xffffffff), coinbase, 0xffffffff)]
|
|
tx.witness = [Witness([b'\x00'*32])]
|
|
commitment = self.calculate_commitment(tx.witness[0].witness[0])
|
|
tx.add_output_address(self.coinbasevalue, self.coinbase_output_address, self.testnet)
|
|
tx.add_output_script(0, b'j$\xaa!\xa9\xed' + commitment)
|
|
tx.coinbase = True
|
|
tx.recalculate_txid()
|
|
return tx
|
|
|
|
def get_job(self, job_id, clean_jobs = True):
|
|
"""
|
|
job_id - ID of the job. Use this ID while submitting share generated from this job.
|
|
prevhash - Hash of previous block.
|
|
coinb1 - Initial part of coinbase transaction.
|
|
coinb2 - Final part of coinbase transaction.
|
|
merkle_branch - List of hashes, will be used for calculation of merkle root. This is not a list of all
|
|
transactions, it only contains prepared hashes of steps of merkle tree algorithm. Please read some
|
|
materials for understanding how merkle trees calculation works.
|
|
version - Bitcoin block version.
|
|
nbits - Encoded current network difficulty
|
|
ntime - Current ntime/
|
|
clean_jobs - When true, server indicates that submitting shares from previous jobs don't have a
|
|
sense and such shares will be rejected. When this flag is set, miner should also drop all previous
|
|
jobs, so job_ids can be eventually rotated.
|
|
|
|
"""
|
|
return [job_id,
|
|
self.previous_block_hash,
|
|
self.coinb1,
|
|
self.coinb2,
|
|
self.merkle_branches,
|
|
self.version,
|
|
self.bits,
|
|
self.time,
|
|
clean_jobs]
|
|
|
|
def submit_job(self, extra_nonce_1, extra_nonce_2, nonce, time):
|
|
version = s2rh(self.version)
|
|
prev_hash = s2rh_step4(self.previous_block_hash)
|
|
cb = self.coinb1 + extra_nonce_1 + extra_nonce_2 + self.coinb2
|
|
time = s2rh(time)
|
|
bits = s2rh(self.bits)
|
|
nonce = s2rh(nonce)
|
|
cbh = double_sha256(unhexlify(cb))
|
|
merkle_root = merkleroot_from_branches(self.merkle_branches, cbh)
|
|
print("merkle_root ", hexlify(merkle_root))
|
|
print("branches ", self.merkle_branches)
|
|
header = version + prev_hash + merkle_root + time + bits + nonce
|
|
block = hexlify(header).decode()
|
|
block += hexlify(int_to_var_int(len (self.transactions) + 1)).decode()
|
|
block += cb
|
|
for t in self.transactions:
|
|
block += t["data"]
|
|
return double_sha256(header,1), block
|
|
|
|
def build_orphan(self, hash, ntime):
|
|
self.previous_block_hash = hexlify(reverse_hash(s2rh(hash))).decode()
|
|
self.time = hexlify(ntime.to_bytes(4, "big")).decode()
|
|
self.height += 1
|
|
self.transactions = list()
|
|
self.txid_list = list()
|
|
self.scan_tx_list()
|
|
self.coinbase_tx = self.create_coinbase_transaction()
|
|
self.coinb1, self.coinb2 = self.split_coinbase()
|
|
self.target = bits2target(self.bits)
|
|
self.difficulty = target2difficulty(self.target)
|
|
self.merkle_branches = [hexlify(i).decode() for i in merkle_branches([self.coinbase_tx.hash, ] + self.txid_list)] |