def __init__(self, node=None): """Pass a node to use an address from that node's wallet. Pass None to generate a local address""" if node is None: self.privkey = cashlib.randombytes(32) else: addr = node.getnewaddress() privb58 = node.dumpprivkey(addr) self.privkey = decodeBase58(privb58)[1:-5] self.pubkey = cashlib.pubkey(self.privkey) self.hash = cashlib.addrbin(self.pubkey)
def run_test(self): self.basicSchnorrSigning() self.nodes[0].generate(15) self.sync_blocks() self.nodes[1].generate(15) self.sync_blocks() self.nodes[2].generate(15) self.sync_blocks() self.nodes[0].generate(100) self.sync_blocks() logging.info("Schnorr signature transaction generation and commitment") resultWallet = [] alltx = [] wallets = [self.nodes[0].listunspent(), self.nodes[1].listunspent()] for txcount in range(0, 2): inputs = [x[0] for x in wallets] for x in wallets: # Remove this utxo so we don't use it in the next time around del x[0] privb58 = [ self.nodes[0].dumpprivkey(inputs[0]["address"]), self.nodes[1].dumpprivkey(inputs[1]["address"]) ] privkeys = [decodeBase58(x)[1:-5] for x in privb58] pubkeys = [cashlib.pubkey(x) for x in privkeys] for doubleSpend in range(0, 2): # Double spend this many times tx = CTransaction() for i in inputs: tx.vin.append( CTxIn(COutPoint(i["txid"], i["vout"]), b"", 0xffffffff - doubleSpend) ) # subtracting doubleSpend changes the tx slightly destPrivKey = cashlib.randombytes(32) destPubKey = cashlib.pubkey(destPrivKey) destHash = cashlib.addrbin(destPubKey) output = CScript([ OP_DUP, OP_HASH160, destHash, OP_EQUALVERIFY, OP_CHECKSIG ]) amt = int(sum([x["amount"] for x in inputs]) * cashlib.BCH) tx.vout.append(CTxOut(amt, output)) sighashtype = 0x41 n = 0 for i, priv in zip(inputs, privkeys): sig = cashlib.signTxInputSchnorr(tx, n, i["amount"], i["scriptPubKey"], priv, sighashtype) tx.vin[n].scriptSig = cashlib.spendscript(sig) # P2PK n += 1 txhex = hexlify(tx.serialize()).decode("utf-8") txid = self.nodes[0].enqueuerawtransaction(txhex) if doubleSpend == 0: resultWallet.append([ destPrivKey, destPubKey, amt, PlaceHolder(txid), 0, output ]) alltx.append(txhex) # because enqueuerawtransaction and propagation is asynchronous we need to wait for it waitFor(10, lambda: self.nodes[1].getmempoolinfo()['size'] == txcount + 1) mp = [i.getmempoolinfo() for i in self.nodes] assert txcount + 1 == mp[0]['size'] == mp[1]['size'] nonSchnorrBlkHash = self.nodes[0].getbestblockhash() self.nodes[0].generate(1) assert self.nodes[0].getmempoolinfo()['size'] == 0 waitFor( 10, lambda: self.nodes[0].getbestblockhash() == self.nodes[1]. getbestblockhash()) FEEINSAT = 5000 # now spend all the new utxos again for spendLoop in range(0, 2): incomingWallet = resultWallet resultWallet = [] logging.info("spend iteration %d, num transactions %d" % (spendLoop, len(incomingWallet))) for w in incomingWallet: txidHolder = PlaceHolder() tx = CTransaction() tx.vin.append( CTxIn(COutPoint(w[3].data, w[4]), b"", 0xffffffff)) NOUTS = 10 - spendLoop * 2 if NOUTS < 0: NOUTS = 1 amtPerOut = int((w[2] - FEEINSAT) / NOUTS) for outIdx in range(0, NOUTS): destPrivKey = cashlib.randombytes(32) destPubKey = cashlib.pubkey(destPrivKey) destHash = cashlib.addrbin(destPubKey) output = CScript([ OP_DUP, OP_HASH160, destHash, OP_EQUALVERIFY, OP_CHECKSIG ]) tx.vout.append(CTxOut(amtPerOut, output)) resultWallet.append([ destPrivKey, destPubKey, amtPerOut, txidHolder, outIdx, output ]) sighashtype = 0x41 n = 0 sig = cashlib.signTxInputSchnorr(tx, n, w[2], w[5], w[0], sighashtype) tx.vin[n].scriptSig = cashlib.spendscript(sig, w[1]) # P2PKH txhex = hexlify(tx.serialize()).decode("utf-8") txid = self.nodes[1].enqueuerawtransaction(txhex) alltx.append(txhex) txidHolder.data = txid # because enqueuerawtransaction and propagation is asynchronous we need to wait for it waitFor( 10, lambda: self.nodes[0].getmempoolinfo()['size'] == len( incomingWallet)) while self.nodes[0].getmempoolinfo()['size'] != 0: self.nodes[0].generate(1) waitFor( 10, lambda: self.nodes[0].getbestblockhash() == self.nodes[1]. getbestblockhash())
def run_test(self): faulted = False try: cashlib.spendscript(OP_1) except AssertionError: faulted = True pass assert faulted, "only data in spend scripts" try: cashlib.signTxInput(b"", 0, 5, b"", b"", cashlib.SIGHASH_ALL) except AssertionError: faulted = True pass assert faulted, "not signing with bitcoin cash forkid" # grab inputs from 2 different full nodes and sign a single tx that spends them both wallets = [self.nodes[0].listunspent(), self.nodes[1].listunspent()] inputs = [x[0] for x in wallets] privb58 = [self.nodes[0].dumpprivkey(inputs[0]["address"]), self.nodes[1].dumpprivkey(inputs[1]["address"])] privkeys = [decodeBase58(x)[1:-5] for x in privb58] pubkeys = [cashlib.pubkey(x) for x in privkeys] tx = CTransaction() for i in inputs: tx.vin.append(CTxIn(COutPoint(i["txid"], i["vout"]), b"", 0xffffffff)) destPrivKey = cashlib.randombytes(32) destPubKey = cashlib.pubkey(destPrivKey) destHash = cashlib.addrbin(destPubKey) output = CScript([OP_DUP, OP_HASH160, destHash, OP_EQUALVERIFY, OP_CHECKSIG]) amt = int(sum([x["amount"] for x in inputs]) * BCH) tx.vout.append(CTxOut(amt, output)) sighashtype = 0x41 n = 0 for i, priv in zip(inputs, privkeys): sig = cashlib.signTxInput(tx, n, i["amount"], i["scriptPubKey"], priv, sighashtype) tx.vin[n].scriptSig = cashlib.spendscript(sig) # P2PK n += 1 txhex = hexlify(tx.serialize()).decode("utf-8") txid = self.nodes[0].enqueuerawtransaction(txhex) assert txid == hexlify(cashlib.txid(txhex)[::-1]).decode("utf-8") # Now spend the created output to an anyone can spend address tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(cashlib.txid(txhex), 0), b"", 0xffffffff)) tx2.vout.append(CTxOut(amt, CScript([OP_1]))) sig2 = cashlib.signTxInput(tx2, 0, amt, output, destPrivKey, sighashtype) tx2.vin[0].scriptSig = cashlib.spendscript(sig2, destPubKey) tx2id = self.nodes[0].enqueuerawtransaction(hexlify(tx2.serialize()).decode("utf-8")) # Check that all tx were created, and commit them waitFor(20, lambda: self.nodes[0].getmempoolinfo()["size"] == 2) blk = self.nodes[0].generate(1) self.sync_blocks() assert self.nodes[0].getmempoolinfo()["size"] == 0 assert self.nodes[1].getmempoolinfo()["size"] == 0
def run_test(self): self.runScriptMachineTests() faulted = False try: cashlib.spendscript(OP_1) except AssertionError: faulted = True pass assert faulted, "only data in spend scripts" try: cashlib.signTxInput(b"", 0, 5, b"", b"", cashlib.SIGHASH_ALL) except AssertionError: faulted = True pass assert faulted, "not signing with bitcoin cash forkid" # grab inputs from 2 different full nodes and sign a single tx that spends them both wallets = [self.nodes[0].listunspent(), self.nodes[1].listunspent()] inputs = [x[0] for x in wallets] privb58 = [ self.nodes[0].dumpprivkey(inputs[0]["address"]), self.nodes[1].dumpprivkey(inputs[1]["address"]) ] privkeys = [decodeBase58(x)[1:-5] for x in privb58] pubkeys = [cashlib.pubkey(x) for x in privkeys] tx = CTransaction() for i in inputs: tx.vin.append( CTxIn(COutPoint(i["txid"], i["vout"]), b"", 0xffffffff)) destPrivKey = cashlib.randombytes(32) destPubKey = cashlib.pubkey(destPrivKey) destHash = cashlib.addrbin(destPubKey) output = CScript( [OP_DUP, OP_HASH160, destHash, OP_EQUALVERIFY, OP_CHECKSIG]) amt = int(sum([x["amount"] for x in inputs]) * cashlib.BCH) tx.vout.append(CTxOut(amt, output)) sighashtype = 0x41 n = 0 for i, priv in zip(inputs, privkeys): sig = cashlib.signTxInput(tx, n, i["amount"], i["scriptPubKey"], priv, sighashtype) tx.vin[n].scriptSig = cashlib.spendscript(sig) # P2PK n += 1 txhex = hexlify(tx.serialize()).decode("utf-8") txid = self.nodes[0].enqueuerawtransaction(txhex) assert txid == hexlify(cashlib.txid(txhex)[::-1]).decode("utf-8") # Now spend the created output to an anyone can spend address tx2 = CTransaction() tx2.vin.append( CTxIn(COutPoint(cashlib.txid(txhex), 0), b"", 0xffffffff)) tx2.vout.append(CTxOut(amt, CScript([OP_1]))) sig2 = cashlib.signTxInput(tx2, 0, amt, output, destPrivKey, sighashtype) tx2.vin[0].scriptSig = cashlib.spendscript(sig2, destPubKey) # Local script interpreter: # Check that the spend works in a transaction-aware script machine txbad = copy.deepcopy(tx2) badsig = list(sig2) badsig[10] = 1 # mess up the sig badsig[11] = 2 txbad.vin[0].scriptSig = cashlib.spendscript(bytes(badsig), destPubKey) # try a bad script (sig check should fail) sm = cashlib.ScriptMachine(tx=tx2, inputIdx=0, inputAmount=tx.vout[0].nValue) ret = sm.eval(txbad.vin[0].scriptSig) assert (ret) ret = sm.eval(tx.vout[0].scriptPubKey) assert (not ret) assert (sm.error()[0] == cashlib.ScriptError.SCRIPT_ERR_SIG_NULLFAIL) # try a good spend script sm.reset() ret = sm.eval(tx2.vin[0].scriptSig) assert (ret) ret = sm.eval(tx.vout[0].scriptPubKey) assert (ret) # commit the created transaction tx2id = self.nodes[0].enqueuerawtransaction( hexlify(tx2.serialize()).decode("utf-8")) # Check that all tx were created, and commit them waitFor(20, lambda: self.nodes[0].getmempoolinfo()["size"] == 2) blk = self.nodes[0].generate(1) self.sync_blocks() assert self.nodes[0].getmempoolinfo()["size"] == 0 assert self.nodes[1].getmempoolinfo()["size"] == 0