def createConflictingTx(dests, source, count, fee=1): """ Create "count" conflicting transactions that spend the "source" to "dests" evenly. Conflicting tx are created by varying the fee. Change the base "fee" if you want which is actually the fee PER dest. source: a dictionary in RPC listunspent format, with additional "privkey" field which is the private key in bytes dests: a list of PayDest objects. count: the number of conflicting tx to return fee: what to deduct as the fee (in Satoshi) """ generatedTx = [] hexOP_DUP = OP_DUP.toHex() binOP_DUP = ord(OP_DUP.toBin()) for c in range(count): w = source if 1: tx = CTransaction() tx.vin.append( CTxIn(COutPoint(w["txid"], w["vout"]), b"", 0xffffffff)) amt = int(w["satoshi"] / len(dests)) - ( fee + c) # really total fee ends up fee*dest i = 0 for d in dests: script = CScript( [OP_DUP, OP_HASH160, d.hash, OP_EQUALVERIFY, OP_CHECKSIG]) tx.vout.append(CTxOut(amt, script)) i += 1 sighashtype = 0x41 sig = cashlib.signTxInput(tx, 0, w["satoshi"], w["scriptPubKey"], w["privkey"], sighashtype) # construct the signature script -- it may be one of 2 types if w["scriptPubKey"][0:2] == hexOP_DUP or w["scriptPubKey"][ 0] == binOP_DUP: # P2PKH starts with OP_DUP tx.vin[0].scriptSig = cashlib.spendscript(sig, w["pubkey"]) # P2PKH else: tx.vin[0].scriptSig = cashlib.spendscript(sig) # P2PK generatedTx.append(tx) return generatedTx
def createTx(dests, sources, node, maxx=None, fee=1, nextWallet=None, generatedTx=None): """ Create "maxx" transactions that spend from individual "sources" to every "dests" evenly (many fan-out transactions). If "generatedTx" is a list the created transactions are put into it. Otherwise they are sent to "node". If "nextWallet" is a list the outputs of all these created tx are put into it in a format compatible with "sources" (you can use nextWallet as the sources input in a subsequent call to this function). Change the base "fee" if you want which is actually the fee PER dest. sources: list of dictionaries in RPC listunspent format, with optional additional "privkey" field which is the private key in bytes. If "privkey" does not exist, "node" is asked for it. dests: a list of PayDest objects. fee: what to deduct as the fee (in Satoshi) nextWallet: [output] pass an empty list to get a valid wallet if all the createdTx are committed. generatedTx: [output] pass an empty list to skip submitting the tx to node, and instead return them in this list. returns the number of transactions created. """ hexOP_DUP = OP_DUP.toHex() binOP_DUP = ord(OP_DUP.toBin()) count = 0 for w in sources: nextOuts = [] if not count is None and count == maxx: break # if sources is from a bitcoind wallet, I need to grab some info in order to sign if not "privkey" in w: privb58 = node.dumpprivkey(w["address"]) privkey = decodeBase58(privb58)[1:-5] pubkey = cashlib.pubkey(privkey) w["privkey"] = privkey w["pubkey"] = pubkey tx = CTransaction() tx.vin.append(CTxIn(COutPoint(w["txid"], w["vout"]), b"", 0xffffffff)) amt = int( w["satoshi"] / len(dests)) - fee # really fee ends up fee*dest i = 0 for d in dests: script = CScript( [OP_DUP, OP_HASH160, d.hash, OP_EQUALVERIFY, OP_CHECKSIG]) tx.vout.append(CTxOut(amt, script)) nextOuts.append({ "vout": i, "privkey": d.privkey, "scriptPubKey": script, "satoshi": amt, "pubkey": d.pubkey }) i += 1 sighashtype = 0x41 n = 0 # print("amountin: %d amountout: %d outscript: %s" % (w["satoshi"], amt, w["scriptPubKey"])) sig = cashlib.signTxInput(tx, n, w["satoshi"], w["scriptPubKey"], w["privkey"], sighashtype) if w["scriptPubKey"][0:2] == hexOP_DUP or w["scriptPubKey"][ 0] == binOP_DUP: # P2PKH starts with OP_DUP tx.vin[n].scriptSig = cashlib.spendscript(sig, w["pubkey"]) # P2PKH else: tx.vin[n].scriptSig = cashlib.spendscript(sig) # P2PK if not type(generatedTx) is list: # submit these tx to the node txhex = hexlify(tx.serialize()).decode("utf-8") txid = None try: txid = node.enqueuerawtransaction(txhex) except JSONRPCException as e: logging.error("TX submission failed because %s" % str(e)) logging.error("tx was: %s" % txhex) logging.error("amountin: %d amountout: %d outscript: %s" % (w["satoshi"], amt, w["scriptPubKey"])) raise else: # return them in generatedTx generatedTx.append(tx) for out in nextOuts: tx.rehash() out["txid"] = tx.hash # I've already filled nextOuts with all the other needed fields if type(nextWallet) is list: nextWallet += nextOuts count += 1 return count
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