Example #1
0
 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)
Example #2
0
    def basicSchnorrSigning(self):
        # First try a canned sig (taken from schnorr.py)
        privkey = bytes.fromhex(
            "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747")

        pubkey = schnorr.getpubkey(privkey, compressed=True)
        assert pubkey == bytes.fromhex(
            "030b4c866585dd868a9d62348a9cd008d6a312937048fff31670e7e920cfc7a744"
        )

        msg = b"Very deterministic message"
        msghash = hash256(msg)
        assert msghash == bytes.fromhex(
            "5255683da567900bfd3e786ed8836a4e7763c221bf1ac20ece2a5171b9199e8a")

        sig = schnorr.sign(privkey, msghash)
        assert sig == bytes.fromhex(
            "2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d111347b81d0717571846de67ad3d913a8fdf9d8f3f73161a4c48ae81cb183b214765feb86e255ce"
        )
        sig2 = cashlib.signHashSchnorr(privkey, msghash)
        assert sig2 == sig

        logging.info("random Schnorr signature comparison")
        # Next try random signatures
        for i in range(1, 1000):
            privkey = cashlib.randombytes(32)
            pubkey = schnorr.getpubkey(privkey, compressed=True)
            pubkey2 = cashlib.pubkey(privkey)
            assert pubkey == pubkey2

            msg = cashlib.randombytes(random.randint(0, 10000))
            hsh = cashlib.hash256(msg)

            sigpy = schnorr.sign(privkey, hsh)
            sigcashlib = cashlib.signHashSchnorr(privkey, hsh)
            assert sigpy == sigcashlib
Example #3
0
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
Example #4
0
    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
Example #5
0
    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())
Example #6
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