37 changed files with 64 additions and 505 deletions
@ -1,458 +0,0 @@ |
|||||||
"""Module to support bitcoin operations using a |
|
||||||
different codebase than joinmarket's own. |
|
||||||
""" |
|
||||||
from __future__ import (absolute_import, division, |
|
||||||
print_function, unicode_literals) |
|
||||||
from builtins import * |
|
||||||
#Protocol constants |
|
||||||
BTC_P2PK_VBYTE = {"mainnet": b'\x00', "testnet": b'\x6f'} |
|
||||||
BTC_P2SH_VBYTE = {"mainnet": b'\x05', "testnet": b'\xc4'} |
|
||||||
PODLE_COMMIT_FILE = None |
|
||||||
|
|
||||||
from jmbase.support import get_log |
|
||||||
log = get_log() |
|
||||||
|
|
||||||
#Required only for PoDLE calculation: |
|
||||||
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 |
|
||||||
|
|
||||||
interface = "joinmarket-joinmarket" |
|
||||||
|
|
||||||
try: |
|
||||||
from jmbitcoin import * |
|
||||||
bjm = True |
|
||||||
except ImportError: |
|
||||||
#TODO figure out the right flexibility structure |
|
||||||
|
|
||||||
interface = "joinmarket-electrum" |
|
||||||
|
|
||||||
if interface != "joinmarket-electrum": |
|
||||||
raise NotImplementedError |
|
||||||
|
|
||||||
not_supported_string = "not supported by: " + interface |
|
||||||
|
|
||||||
# Base switching |
|
||||||
code_strings = { |
|
||||||
2: '01', |
|
||||||
10: '0123456789', |
|
||||||
16: '0123456789abcdef', |
|
||||||
32: 'abcdefghijklmnopqrstuvwxyz234567', |
|
||||||
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz', |
|
||||||
256: ''.join([chr(x) for x in range(256)]) |
|
||||||
} |
|
||||||
def get_code_string(base): |
|
||||||
if base in code_strings: |
|
||||||
return code_strings[base] |
|
||||||
else: |
|
||||||
raise ValueError("Invalid base!") |
|
||||||
|
|
||||||
def encode(val, base, minlen=0): |
|
||||||
base, minlen = int(base), int(minlen) |
|
||||||
code_string = get_code_string(base) |
|
||||||
result = "" |
|
||||||
while val > 0: |
|
||||||
result = code_string[val % base] + result |
|
||||||
val //= base |
|
||||||
return code_string[0] * max(minlen - len(result), 0) + result |
|
||||||
|
|
||||||
def decode(string, base): |
|
||||||
base = int(base) |
|
||||||
code_string = get_code_string(base) |
|
||||||
result = 0 |
|
||||||
if base == 16: |
|
||||||
string = string.lower() |
|
||||||
while len(string) > 0: |
|
||||||
result *= base |
|
||||||
result += code_string.find(string[0]) |
|
||||||
string = string[1:] |
|
||||||
return result |
|
||||||
|
|
||||||
#Electrum specific code starts here |
|
||||||
import electrum.bitcoin as ebt |
|
||||||
import electrum.transaction as etr |
|
||||||
from ecdsa.ecdsa import point_is_valid |
|
||||||
from ecdsa.util import string_to_number, sigdecode_der, sigencode_der |
|
||||||
from ecdsa import VerifyingKey, BadSignatureError, BadDigestError |
|
||||||
from ecdsa.curves import SECP256k1 |
|
||||||
from ecdsa.numbertheory import square_root_mod_prime |
|
||||||
from ecdsa.ellipticcurve import Point |
|
||||||
|
|
||||||
class PPubKey(object): |
|
||||||
def __init__(self, serP): |
|
||||||
self._point = ebt.ser_to_point(serP) |
|
||||||
def serialize(self): |
|
||||||
return ebt.point_to_ser(self._point) |
|
||||||
|
|
||||||
class PPrivKey(object): |
|
||||||
def __init__(self, scalar): |
|
||||||
self._privkey = ebt.EC_KEY(scalar) |
|
||||||
self.private_key = scalar |
|
||||||
self.pubkey = PPubKey(binascii.unhexlify( |
|
||||||
self._privkey.get_public_key())) |
|
||||||
|
|
||||||
podle_PublicKey_class = PPubKey |
|
||||||
podle_PrivateKey_class = PPrivKey |
|
||||||
|
|
||||||
def podle_PublicKey(P): |
|
||||||
return PPubKey(P) |
|
||||||
|
|
||||||
def podle_PrivateKey(priv): |
|
||||||
return PPrivKey(priv) |
|
||||||
|
|
||||||
def multiply(s, pub, usehex, rawpub=True, return_serialized=True): |
|
||||||
"""s should be 32 byte scalar, pub should be of type |
|
||||||
podle_PublicKey_class |
|
||||||
""" |
|
||||||
if usehex: |
|
||||||
s = binascii.unhexlify(s) |
|
||||||
pub = binascii.unhexlify(pub) |
|
||||||
ppub = PPubKey(pub) |
|
||||||
p = ppub._point |
|
||||||
s_int = decode(s, 256) |
|
||||||
m = p * s_int |
|
||||||
r = PPubKey(ebt.point_to_ser(m)) |
|
||||||
if return_serialized: |
|
||||||
return r.serialize() |
|
||||||
return r |
|
||||||
|
|
||||||
def add_pubkeys(pubkeys, usehex): |
|
||||||
"""Pubkeys should be a list (for compatibility). |
|
||||||
""" |
|
||||||
#Not supporting more than 2 items for now, not needed. |
|
||||||
assert len(pubkeys) == 2 |
|
||||||
if usehex: |
|
||||||
pubkeys = [binascii.unhexlify(x) for x in pubkeys] |
|
||||||
p1pt, p2pt = [ebt.ser_to_point(x) for x in pubkeys] |
|
||||||
sumpt = p1pt + p2pt |
|
||||||
return ebt.point_to_ser(sumpt) |
|
||||||
|
|
||||||
def getG(compressed=True): |
|
||||||
scalar = "\x00"*31 + "\x01" |
|
||||||
return binascii.unhexlify( |
|
||||||
ebt.EC_KEY(scalar).get_public_key(compressed=compressed)) |
|
||||||
|
|
||||||
def sign(tx): |
|
||||||
#transaction signing is handled by the wallet for Electrum |
|
||||||
raise NotImplementedError("sign " + not_supported_string) |
|
||||||
|
|
||||||
def get_version_byte(inp): |
|
||||||
leadingzbytes = len(re.match('^1*', inp).group(0)) |
|
||||||
data = b'\x00' * leadingzbytes + b58check_to_bin(inp, version=True) |
|
||||||
return ord(data[0]) |
|
||||||
|
|
||||||
def b58check_to_bin(addr, version=False): |
|
||||||
"""optionally include the version byte for get_version_byte. |
|
||||||
""" |
|
||||||
if not version: |
|
||||||
return ebt.DecodeBase58Check(addr)[1:] |
|
||||||
else: |
|
||||||
return ebt.DecodeBase58Check(addr) |
|
||||||
|
|
||||||
def address_to_script(addr): |
|
||||||
return etr.Transaction.pay_script(ebt.TYPE_ADDRESS, addr) |
|
||||||
|
|
||||||
def script_to_address(script): |
|
||||||
bin_script = binascii.unhexlify(script) |
|
||||||
res = etr.get_address_from_output_script(bin_script) |
|
||||||
if not res[0] == ebt.TYPE_ADDRESS: |
|
||||||
raise ValueError("Invalid script for bitcoin address") |
|
||||||
return res[1] |
|
||||||
|
|
||||||
def bin_dbl_sha256(x): |
|
||||||
return ebt.sha256(ebt.sha256(x)) |
|
||||||
|
|
||||||
def dbl_sha256(x): |
|
||||||
return binascii.hexlify(bin_dbl_sha256(x)).decode('ascii') |
|
||||||
|
|
||||||
def verify_tx_input(tx, i, script, sig, pub): |
|
||||||
pub, sig, script = (binascii.unhexlify(x) for x in [pub, sig, script]) |
|
||||||
t = etr.Transaction(tx) |
|
||||||
t.deserialize() |
|
||||||
#to prepare for verification (to do the txhash for modtx) |
|
||||||
#we need to have the "address" field set in the input. |
|
||||||
typ, addr = etr.get_address_from_output_script(script) |
|
||||||
if not typ == ebt.TYPE_ADDRESS: |
|
||||||
#Don't support non-p2sh, non-p2pkh for now |
|
||||||
log.debug("Invalid script") |
|
||||||
return False |
|
||||||
t.inputs()[i]["address"] = addr |
|
||||||
t.inputs()[i]["type"] = 'p2pkh' |
|
||||||
txforsig = etr.Hash(t.serialize_preimage(i).decode('hex')) |
|
||||||
ecdsa_pub = get_ecdsa_verifying_key(pub) |
|
||||||
if not ecdsa_pub: |
|
||||||
return False |
|
||||||
try: |
|
||||||
verified = ecdsa_pub.verify_digest(sig, txforsig, |
|
||||||
sigdecode = sigdecode_der) |
|
||||||
except (BadSignatureError, BadDigestError): |
|
||||||
return False |
|
||||||
return True |
|
||||||
|
|
||||||
def get_ecdsa_verifying_key(pub): |
|
||||||
#some shenanigans required to validate a transaction sig; see |
|
||||||
#python.ecdsa PR #54. This will be a lot simpler when that's merged. |
|
||||||
#https://github.com/warner/python-ecdsa/pull/54/files |
|
||||||
if not pub[0] in ["\x02", "\x03"]: |
|
||||||
log.debug("Invalid pubkey") |
|
||||||
return None |
|
||||||
is_even = pub.startswith('\x02') |
|
||||||
x = string_to_number(pub[1:]) |
|
||||||
order = SECP256k1.order |
|
||||||
p = SECP256k1.curve.p() |
|
||||||
alpha = (pow(x, 3, p) + (SECP256k1.curve.a() * x) + SECP256k1.curve.b()) % p |
|
||||||
beta = square_root_mod_prime(alpha, p) |
|
||||||
if is_even == bool(beta & 1): |
|
||||||
y = p - beta |
|
||||||
else: |
|
||||||
y = beta |
|
||||||
if not point_is_valid(SECP256k1.generator, x, y): |
|
||||||
return None |
|
||||||
|
|
||||||
point = Point(SECP256k1.curve, x, y, order) |
|
||||||
return VerifyingKey.from_public_point(point, SECP256k1, |
|
||||||
hashfunc=hashlib.sha256) |
|
||||||
|
|
||||||
def ecdsa_verify(msg, sig, pub, usehex=True): |
|
||||||
sig = base64.b64decode(sig) |
|
||||||
if usehex: |
|
||||||
pub = binascii.unhexlify(pub) |
|
||||||
verif_key = get_ecdsa_verifying_key(pub) |
|
||||||
return verif_key.verify_digest(sig, |
|
||||||
ebt.Hash(ebt.msg_magic(msg)), |
|
||||||
sigdecode = sigdecode_der) |
|
||||||
|
|
||||||
def ecdsa_sign(msg, priv, usehex=True): |
|
||||||
if usehex: |
|
||||||
priv = binascii.unhexlify(priv) |
|
||||||
compressed = False |
|
||||||
if len(priv) == 33 and priv[-1]=="\x01": |
|
||||||
compressed = True |
|
||||||
signkey = ebt.EC_KEY(priv[:32]) |
|
||||||
private_key = ebt.MySigningKey.from_secret_exponent(signkey.secret, |
|
||||||
curve=SECP256k1) |
|
||||||
sig = private_key.sign_digest_deterministic(ebt.Hash(ebt.msg_magic(msg)), |
|
||||||
hashfunc=hashlib.sha256, |
|
||||||
sigencode = sigencode_der) |
|
||||||
return base64.b64encode(sig) |
|
||||||
|
|
||||||
def serialize(txobj): |
|
||||||
#It is a rather chunky matter to re-use electrum.transaction code |
|
||||||
#to do serialization, it has a very different approach. Hence some |
|
||||||
#code duplication here with bitcoin-joinmarket. However we use the |
|
||||||
#number encoding functions from Electrum. Also, this is always in hex. |
|
||||||
o = [] |
|
||||||
o.append(ebt.int_to_hex(txobj["version"], 4)) |
|
||||||
o.append(ebt.var_int(len(txobj["ins"]))) |
|
||||||
for inp in txobj["ins"]: |
|
||||||
binhash = binascii.unhexlify(inp["outpoint"]["hash"]) |
|
||||||
binhash = binhash[::-1] |
|
||||||
o.append(binascii.hexlify(binhash).decode('ascii')) |
|
||||||
o.append(ebt.int_to_hex(inp["outpoint"]["index"], 4)) |
|
||||||
o.append(ebt.var_int(len(inp["script"])/2) + inp["script"]) |
|
||||||
o.append(ebt.int_to_hex(inp["sequence"], 4)) |
|
||||||
o.append(ebt.var_int(len(txobj["outs"]))) |
|
||||||
for out in txobj["outs"]: |
|
||||||
o.append(ebt.int_to_hex(out["value"], 8)) |
|
||||||
o.append(ebt.var_int(len(out["script"])/2) + out["script"]) |
|
||||||
o.append(ebt.int_to_hex(txobj["locktime"], 4)) |
|
||||||
return ''.join(o) |
|
||||||
|
|
||||||
def deserialize_script(scriptSig): |
|
||||||
#Assumes P2PKH scriptSig |
|
||||||
d = {} |
|
||||||
etr.parse_scriptSig(d, binascii.unhexlify(scriptSig)) |
|
||||||
return (d["signatures"][0], d["pubkeys"][0]) |
|
||||||
|
|
||||||
def deserialize(txhex): |
|
||||||
t = etr.deserialize(txhex) |
|
||||||
#translation from Electrum deserialization |
|
||||||
#to pybitcointools form as used in joinmarket |
|
||||||
#pybitcointools structure: |
|
||||||
#obj = {"ins": [..], "outs": [..], "locktime": int} |
|
||||||
#where ins elements are: |
|
||||||
#{"outpoint": {"hash": bigendian32,"index": int}, |
|
||||||
#"script": hex,"sequence": int} |
|
||||||
#and outs elements are: |
|
||||||
#{"script": hex, "value": int} |
|
||||||
# |
|
||||||
#while electrum.transaction.deserialize returns object |
|
||||||
#like: |
|
||||||
#{"version": int, "inputs": [..], "outputs": [..], "lockTime": int} |
|
||||||
obj = {} |
|
||||||
obj["version"] = t["version"] |
|
||||||
obj["locktime"] = t["lockTime"] |
|
||||||
obj["ins"] = [] |
|
||||||
obj["outs"] = [] |
|
||||||
for i in t["inputs"]: |
|
||||||
outpoint = {"hash": i["prevout_hash"], "index": i["prevout_n"]} |
|
||||||
scr = i["scriptSig"] |
|
||||||
sequence = i["sequence"] |
|
||||||
obj["ins"].append({"outpoint": outpoint, "script": scr, "sequence": sequence}) |
|
||||||
for i in t["outputs"]: |
|
||||||
obj["outs"].append({"script": i["scriptPubKey"], "value": i["value"]}) |
|
||||||
return obj |
|
||||||
|
|
||||||
def privkey_to_pubkey(privkey, usehex=True): |
|
||||||
if usehex: |
|
||||||
privkey = binascii.unhexlify(privkey) |
|
||||||
if len(privkey)==33 and privkey[-1] == "\x01": |
|
||||||
compressed = True |
|
||||||
privkey = privkey[:32] |
|
||||||
elif len(privkey)==32: |
|
||||||
compressed=False |
|
||||||
else: |
|
||||||
raise ValueError("Invalid private key") |
|
||||||
sec = ebt.SecretToASecret(privkey, compressed=compressed) |
|
||||||
|
|
||||||
retval = ebt.public_key_from_private_key(sec) |
|
||||||
if usehex: |
|
||||||
return retval |
|
||||||
return binascii.unhexlify(retval) |
|
||||||
|
|
||||||
privtopub = privkey_to_pubkey |
|
||||||
|
|
||||||
def privkey_to_address(privkey, magicbyte=0): |
|
||||||
pubkey = privkey_to_pubkey(privkey) |
|
||||||
return pubkey_to_address(pubkey, magicbyte) |
|
||||||
|
|
||||||
privtoaddr = privkey_to_address |
|
||||||
|
|
||||||
def pubkey_to_address(pub, magicbyte=0): |
|
||||||
h160 = ebt.hash_160(pub.decode('hex')) |
|
||||||
return ebt.hash_160_to_bc_address(h160, addrtype=magicbyte) |
|
||||||
|
|
||||||
pubtoaddr = pubkey_to_address |
|
||||||
|
|
||||||
def from_wif_privkey(privkey, vbyte=0): |
|
||||||
#converts a WIF compressed privkey to a hex private key |
|
||||||
return binascii.hexlify(ebt.ASecretToSecret(privkey)).decode('ascii') |
|
||||||
|
|
||||||
def txhash(txhex): |
|
||||||
t = etr.Transaction(txhex) |
|
||||||
return t.txid() |
|
||||||
|
|
||||||
#A simple copy-paste for now; move into support.py perhaps? TODO |
|
||||||
def estimate_tx_size(ins, outs, txtype='p2pkh'): |
|
||||||
'''Estimate transaction size. |
|
||||||
Assuming p2pkh: |
|
||||||
out: 8+1+3+2+20=34, in: 1+32+4+1+1+~73+1+1+33=147, |
|
||||||
ver:4,seq:4, +2 (len in,out) |
|
||||||
total ~= 34*len_out + 147*len_in + 10 (sig sizes vary slightly) |
|
||||||
''' |
|
||||||
if txtype == 'p2pkh': |
|
||||||
return 10 + ins * 147 + 34 * outs |
|
||||||
else: |
|
||||||
raise NotImplementedError("Non p2pkh transaction size estimation not" + |
|
||||||
"yet implemented") |
|
||||||
|
|
||||||
def mktx(ins, outs): |
|
||||||
#Copy-paste from bitcoin-joinmarket |
|
||||||
txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []} |
|
||||||
for i in ins: |
|
||||||
if isinstance(i, dict) and "outpoint" in i: |
|
||||||
txobj["ins"].append(i) |
|
||||||
else: |
|
||||||
if isinstance(i, dict) and "output" in i: |
|
||||||
i = i["output"] |
|
||||||
txobj["ins"].append({ |
|
||||||
"outpoint": {"hash": i[:64], |
|
||||||
"index": int(i[65:])}, |
|
||||||
"script": "", |
|
||||||
"sequence": 4294967295 |
|
||||||
}) |
|
||||||
for o in outs: |
|
||||||
if not isinstance(o, dict): |
|
||||||
addr = o[:o.find(':')] |
|
||||||
val = int(o[o.find(':') + 1:]) |
|
||||||
o = {} |
|
||||||
if re.match('^[0-9a-fA-F]*$', addr): |
|
||||||
o["script"] = addr |
|
||||||
else: |
|
||||||
o["address"] = addr |
|
||||||
o["value"] = val |
|
||||||
|
|
||||||
outobj = {} |
|
||||||
if "address" in o: |
|
||||||
outobj["script"] = address_to_script(o["address"]) |
|
||||||
elif "script" in o: |
|
||||||
outobj["script"] = o["script"] |
|
||||||
else: |
|
||||||
raise Exception("Could not find 'address' or 'script' in output.") |
|
||||||
outobj["value"] = o["value"] |
|
||||||
txobj["outs"].append(outobj) |
|
||||||
|
|
||||||
return serialize(txobj) |
|
||||||
|
|
||||||
def set_commitment_file(file_location): |
|
||||||
global PODLE_COMMIT_FILE |
|
||||||
PODLE_COMMIT_FILE = file_location |
|
||||||
|
|
||||||
def test_btc(): |
|
||||||
#Sign and verify test (for message signing in joinmarket handshake) |
|
||||||
print("Using interface " + interface) |
|
||||||
priv = dbl_sha256("hello") + "01" |
|
||||||
x = ecdsa_sign("helloxxx", priv) |
|
||||||
log.debug("Got: " + x) |
|
||||||
y = ecdsa_verify("helloxxx", x, privkey_to_pubkey(priv)) |
|
||||||
log.debug("Sig ver: " + str(y)) |
|
||||||
assert y |
|
||||||
|
|
||||||
#address/script conversion test |
|
||||||
test_addr = "1LT6rwv26bV7mgvRosoSCyGM7ttVRsYidP" |
|
||||||
#Electrum has no support for testnet! |
|
||||||
#test_test_addr = "mgvipZr8kX7fZFQU7QsKTCJT9QCfaiswV7" |
|
||||||
assert script_to_address(address_to_script(test_addr))==test_addr |
|
||||||
assert get_version_byte(test_addr)==0 |
|
||||||
|
|
||||||
#Transaction creation test. |
|
||||||
raw_valid_tx = "01000000064cdfe43ad43b187b738644363144784a09bf6d408012409cf9934591109a789b060000006b483045022100d4309edbb8253e62fb59462f2ff5c3445923e0299bf1a15ac5f7db3da5752bee022066f3f219de7e6ee56c3d600da757ec1051cbd11b42969b8935ae35642b6a2e84012102e94b49525342110266a1dc7651221507318c4cb914ede004b3098650e9b951b6ffffffffc2a9b3e8285c2e7aaee2ea50f792172c920c43a675fa8e8d70976727c8752adf030000006a47304402202763d8ad9e41c99c5af587c69d267493773dc9567519a64db8b707af5daf07f0022011729c6d241ad5abe48687d084644bd442b5f9038db04fb28da674126183aca5012102d2cbeb9386fd201bc6eecf27b2858f7bc27462cd9b43ae464e9ef3281f97a3e0ffffffffa787e89792a93111ff08f5a083234c7c2410bd69b6eef42be0fc5f026a3a1cf0030000006b483045022100c3b86d7acadf1be3d8ea6706daedb842b09732621e830440481370d423703741022009fd0f90a07babd481f1011ec883b2aa248c6a4a433599c5b203c6b93fc03b67012103f9a47d3958281b6749921fdf6d9edde0176342c00ced7caacab9ab3a64795086ffffffff23fb90cebcb1784a7a4a0a35489356ba64cf95c0afdc5a0f0184dc22668ff41f050000006b483045022100ea698e5952e23ffdf6d58bdc73e91c555867e3ad99ac9b583f492882395ace9a0220705abe597972d45923fe0515695dd7b99dcfa50e69d49c03a8126180fd263bc70121036532aa886851548a5b62bff29b4c36bfdc33e68c7dbee8efb4b440e50c5ebc6effffffffd401de8afd8fd323ab6abd9db1d261ac69e7c1d2be7f1a40004e7659b7d6cd9b030000006b483045022100b09c4e7f227f2f86d1965edbc4c92b9058243300f3bc62a3169591aacb60ca4d0220390d0d7ae2ee7dab200e166337c65d4a62b576dc4fa138ce40efd240c57346fc0121034cd59665d736d927d9613c7624f8d616d483b06ab8993446f6119f18e22731feffffffff38b8b3ae5fe9ef09c9f1583c9d6cc128bbd2639d49aca97b7686a74ba91bb32a040000006a4730440220105d93aba953edf008cc5b16ac81c10d97db6e59a3e13062ceef7cc1fbffd2ad022027b14b4162d70c4448bec7cb086b4e52880b51b282de98019ec3038153e25ed0012102cdbfb52b3e164203845f72391a3a58205834a3ad473a9d9878488dc1594aa0d4ffffffff087edb0000000000001976a914a1e5f40c6171e91183533f16bbda35e45182bcfa88ac80d97800000000001976a91482985ea6f877d70692072af967af305005fc86fd88ac80d97800000000001976a914a698b206b9f654974afd2056c85c52f88e4c2b2488ac9970af05000000001976a914b05dbb0ede1191e2871209affd8a5922e0a3275288ac80d97800000000001976a914619b3b22b7b66220d22907b8600724aecc49f03488acabc80000000000001976a914911c8c57eb12aa2c1cdce92f82c7e0405a2f3c6988ac80d97800000000001976a91464cd0ed04862f2b7101e9394285d2b3066e5e4dc88ac13b14100000000001976a9143f81fa4fd890845882fbb5226539d9643c99f0f488ac00000000" |
|
||||||
rvtxid = "4489a8cc933cb4e94915ead5b57b4aa707212c1f7b317187b500491e068c7887" |
|
||||||
if interface == "joinmarket-electrum": |
|
||||||
t = etr.Transaction(raw_valid_tx) |
|
||||||
assert rvtxid == t.hash() |
|
||||||
|
|
||||||
#Transaction deserialization/serialization test |
|
||||||
#Electrum requires this call to fill out Transactionfields |
|
||||||
t.deserialize() |
|
||||||
#log.debug("Got inputs: " + str(t.inputs)) |
|
||||||
ourdeser = deserialize(t.raw) |
|
||||||
ourraw = serialize(ourdeser) |
|
||||||
#log.debug("Recreated: \n" + ourraw) |
|
||||||
assert ourraw == raw_valid_tx |
|
||||||
#double check round trip too |
|
||||||
assert deserialize(ourraw) == ourdeser |
|
||||||
txinslist = t.inputs() |
|
||||||
elif interface == "joinmarket-joinmarket": |
|
||||||
assert serialize(deserialize(raw_valid_tx)) == raw_valid_tx |
|
||||||
t = deserialize(raw_valid_tx) |
|
||||||
txinslist = t["ins"] |
|
||||||
else: |
|
||||||
raise NotImplementedError("No such interface?") |
|
||||||
|
|
||||||
#Transaction signature verification tests. |
|
||||||
#All currently assuming 100% p2pkh. |
|
||||||
for i, tin in enumerate(txinslist): |
|
||||||
if interface == "joinmarket-electrum": |
|
||||||
script = address_to_script(tin["address"]) |
|
||||||
sig = tin["signatures"][0] |
|
||||||
pub = tin["pubkeys"][0] |
|
||||||
elif interface == "joinmarket-joinmarket": |
|
||||||
log.debug("Joinmarket working with this script: " + tin["script"]) |
|
||||||
scriptSig = tin["script"] |
|
||||||
#We need to parse out the pubkey, convert to address, then convert |
|
||||||
#to a pubkeyscript; this assumes p2pkh. Note that this is handled |
|
||||||
#internally by the joinmarket blockchain/maker/taker code, so only |
|
||||||
#for tests. |
|
||||||
pub = scriptSig[-66:] |
|
||||||
script = address_to_script(pubkey_to_address(pub)) |
|
||||||
log.debug("Converted to this addr script: " + script) |
|
||||||
#drop the length bytes from the start of sig and pub |
|
||||||
sig = scriptSig[2:-68] |
|
||||||
else: |
|
||||||
raise NotImplementedError("No such interface?") |
|
||||||
log.debug("Got sig, script, pub: " + " ".join([sig, script, pub])) |
|
||||||
assert verify_tx_input(raw_valid_tx, i, script, sig, pub) |
|
||||||
log.debug("Sig at: " + str(i) + " OK.") |
|
||||||
|
|
||||||
#Note there are no transaction signing tests, as |
|
||||||
#this is done by the wallet in this interface. |
|
||||||
log.debug("All tests passed.") |
|
||||||
Loading…
Reference in new issue