Exemplo n.º 1
0
 def txWithTxid(txid):
     txInOne = msgtx.TxIn(msgtx.OutPoint(ByteArray("ff"), 0, 0),
                          valueIn=1)
     txInTwo = msgtx.TxIn(None, valueIn=3)
     txOutOne = msgtx.TxOut(pkScript=stakeSubmission, value=3)
     txOutTwo = msgtx.TxOut()
     txOutThree = msgtx.TxOut()
     txOutFour = msgtx.TxOut()
     txOutFive = msgtx.TxOut()
     txsIn = [txInOne, txInTwo]
     txsOut = [txOutOne, txOutTwo, txOutThree, txOutFour, txOutFive]
     return msgtx.MsgTx(reversed(ByteArray(txid)), None, None, txsIn,
                        txsOut, None, None)
Exemplo n.º 2
0
    def test_tx_from_hex(self, prepareLogger):
        tx = msgtx.MsgTx(
            cachedHash=None,
            serType=0,
            version=123,
            txIn=[
                msgtx.TxIn(
                    previousOutPoint=msgtx.OutPoint(txHash=newHash(),
                                                    idx=5,
                                                    tree=1),
                    sequence=msgtx.MaxTxInSequenceNum,
                    valueIn=500,
                    blockHeight=111,
                    blockIndex=12,
                    signatureScript=newHash(),
                ),
            ],
            txOut=[
                msgtx.TxOut(value=321, pkScript=newHash(), version=0),
                msgtx.TxOut(value=654, pkScript=newHash(), version=0),
                msgtx.TxOut(value=987, pkScript=newHash(), version=0),
            ],
            lockTime=int(time.time()),
            expiry=int(time.time()) + 86400,
        )

        b = tx.serialize()
        reTx = msgtx.MsgTx.unblob(b)

        assert tx.serType == reTx.serType
        assert tx.version == reTx.version
        assert tx.lockTime == reTx.lockTime
        assert tx.expiry == reTx.expiry

        for i, txIn in enumerate(tx.txIn):
            reTxIn = reTx.txIn[i]
            assert txIn.previousOutPoint.hash == reTxIn.previousOutPoint.hash
            assert txIn.previousOutPoint.index == reTxIn.previousOutPoint.index
            assert txIn.previousOutPoint.tree == reTxIn.previousOutPoint.tree
            assert txIn.sequence == reTxIn.sequence
            assert txIn.valueIn == reTxIn.valueIn
            assert txIn.blockHeight == reTxIn.blockHeight
            assert txIn.blockIndex == reTxIn.blockIndex
            assert txIn.signatureScript == reTxIn.signatureScript

        for i, txOut in enumerate(tx.txOut):
            reTxOut = reTx.txOut[i]
            assert txOut.value == reTxOut.value
            assert txOut.version == reTxOut.version
            assert txOut.pkScript == reTxOut.pkScript
Exemplo n.º 3
0
            def utxosource(amt, filter):
                nextVal = 10
                total = 0
                utxos = []

                while total < amt:
                    atoms = int(nextVal * 1e8)
                    privKey = Curve.generateKey()
                    pkHash = crypto.hash160(
                        privKey.pub.serializeCompressed().b)
                    addr = addrlib.AddressPubKeyHash(pkHash, testnet)
                    addrs.append(addr)
                    addrString = addr.string()
                    keys[addrString] = privKey
                    pkScript = txscript.makePayToAddrScript(
                        addrString, testnet)
                    txHash = rando.newHash()
                    txid = reversed(txHash).hex()
                    utxos.append(
                        account.UTXO(
                            address=addrString,
                            txHash=txHash,
                            vout=0,
                            ts=int(time.time()),
                            scriptPubKey=pkScript,
                            satoshis=atoms,
                        ))
                    tx = msgtx.MsgTx.new()
                    tx.addTxOut(msgtx.TxOut(value=atoms, pkScript=pkScript))
                    txs[txid] = tx
                    total += atoms
                    nextVal *= 2
                return utxos, True
Exemplo n.º 4
0
    def submitProof(self, addr, proof, redemptionAddr):
        addrStr = addr.string()
        signingKeyB, doubleHashB = self.db(self.selectKeyByProof_, addrStr, proof.b)
        signingKey = crypto.privKeyFromBytes(ByteArray(bytes(signingKeyB)))

        # Prepare the redemption script
        redeemScript = ByteArray(opcode.OP_SHA256)
        redeemScript += txscript.addData(bytes(doubleHashB))
        redeemScript += opcode.OP_EQUALVERIFY
        redeemScript += txscript.addData(signingKey.pub.serializeCompressed())
        redeemScript += opcode.OP_CHECKSIG

        # Collect all outputs for the address. We could do this from the cache,
        # but we'll generate a new call to dcrdata instead to make sure we have
        # the freshest data.
        utxos = self.blockchain.UTXOs([addrStr])
        if len(utxos) == 0:
            return dict(
              error="challenge is either unfunded or has already been redeemed",
              code=1,
            )

        rewardTx = msgtx.MsgTx.new()
        reward = 0
        for utxo in utxos:
          reward += utxo.satoshis
          prevOut = msgtx.OutPoint(utxo.txHash, utxo.vout, msgtx.TxTreeRegular)
          rewardTx.addTxIn(msgtx.TxIn(prevOut, valueIn=utxo.satoshis))

        # sigScript = txscript.addData(answerHash) + txscript.addData(script)
        # rewardTx.addTxIn(msgtx.TxIn(prevOut, signatureScript=sigScript))

        # Add the reward output with zero value for now.
        txout = msgtx.TxOut(pkScript=txscript.payToAddrScript(redemptionAddr))
        rewardTx.addTxOut(txout)

        # Get the serialized size of the transaction. Since there are no signatures
        # involved, the size is known exactly. Use the size to calculate transaction
        # fees.
        # 1 + 73 = signature
        # 1 + 32 = answer hash
        # 1 + 70 = redeem script
        sigScriptSize = 1 + 73 + 1 + 32 + 1 + 70
        maxSize = rewardTx.serializeSize() + len(utxos)*sigScriptSize
        fees = feeRate * maxSize
        if reward <= fees:
            return makeErr(f"reward, {reward}, must cover fees {fees}, tx size = {maxSize} bytes, fee rate = {feeRate} atoms/byte")
        netReward = reward - fees
        # Set the value on the reward output.
        txout.value = netReward

        for idx, txIn in enumerate(rewardTx.txIn):
          sig = txscript.rawTxInSignature(rewardTx, idx, redeemScript, txscript.SigHashAll, signingKey.key)
          sigScript = txscript.addData(sig) + txscript.addData(DummyHash) + txscript.addData(redeemScript)
          txIn.signatureScript = sigScript

        return {
            "txHex": rewardTx.serialize().hex(),
        }
Exemplo n.º 5
0
def test_checkoutput():
    # Amount is zero.
    tx = msgtx.TxOut()
    with pytest.raises(DecredError):
        dcrdata.checkOutput(tx, 0)

    # Amount is negative.
    tx = msgtx.TxOut(-1)
    with pytest.raises(DecredError):
        dcrdata.checkOutput(tx, 0)

    # Amount is too large.
    tx = msgtx.TxOut(txscript.MaxAmount + 1)
    with pytest.raises(DecredError):
        dcrdata.checkOutput(tx, 0)

    # Tx is dust output.
    script = ByteArray([opcode.OP_RETURN, opcode.OP_NOP])
    tx = msgtx.TxOut(value=1, pkScript=script)
    with pytest.raises(DecredError):
        dcrdata.checkOutput(tx, 0)
Exemplo n.º 6
0
    def test_ticketFromTx(self):
        stakeSubmission = ByteArray(opcode.OP_SSTX)
        stakeSubmission += opcode.OP_HASH160
        stakeSubmission += opcode.OP_DATA_20
        stakeSubmission += 1 << (8 * 19)
        stakeSubmission += opcode.OP_EQUAL

        tx = msgtx.MsgTx.new()
        tx.txOut = [msgtx.TxOut(pkScript=stakeSubmission, value=3)]
        ticket = account.UTXO.ticketFromTx(tx, nets.testnet)
        assert ticket.tinfo.status == "mempool"
        tinfo = account.TicketInfo("no_status", None, 0, 0)
        ticket = account.UTXO.ticketFromTx(tx, nets.testnet, None, tinfo)
        assert ticket.tinfo.status == "unconfirmed"
Exemplo n.º 7
0
    def test_tx_hash(self, prepareLogger):
        """
        test_tx_hash tests the ability to generate the hash of a transaction
        accurately.
        """
        # Hash of first transaction from block 113875.
        wantHash = reversed(
            ByteArray(
                "4538fc1618badd058ee88fd020984451024858796be0a1ed111877f887e1bd53"
            ))

        msgTx = msgtx.MsgTx.new()
        txIn = msgtx.TxIn(
            previousOutPoint=msgtx.OutPoint(
                txHash=None,
                idx=0xFFFFFFFF,
                tree=msgtx.TxTreeRegular,
            ),
            sequence=0xFFFFFFFF,
            valueIn=5000000000,
            blockHeight=0x3F3F3F3F,
            blockIndex=0x2E2E2E2E,
            signatureScript=ByteArray(
                [0x04, 0x31, 0xDC, 0x00, 0x1B, 0x01, 0x62]),
        )
        # fmt: off
        txOut = msgtx.TxOut(
            value=5000000000,
            version=0xF0F0,
            pkScript=ByteArray([
                0x41,  # OP_DATA_65
                0X04,
                0XD6,
                0X4B,
                0XDF,
                0XD0,
                0X9E,
                0XB1,
                0XC5,
                0XFE,
                0X29,
                0X5A,
                0XBD,
                0XEB,
                0X1D,
                0XCA,
                0X42,
                0X81,
                0XBE,
                0X98,
                0X8E,
                0X2D,
                0XA0,
                0XB6,
                0XC1,
                0XC6,
                0XA5,
                0X9D,
                0XC2,
                0X26,
                0XC2,
                0X86,
                0X24,
                0XE1,
                0X81,
                0X75,
                0XE8,
                0X51,
                0XC9,
                0X6B,
                0X97,
                0X3D,
                0X81,
                0XB0,
                0X1C,
                0XC3,
                0X1F,
                0X04,
                0X78,
                0X34,
                0XBC,
                0X06,
                0XD6,
                0XD6,
                0XED,
                0XF6,
                0X20,
                0XD1,
                0X84,
                0X24,
                0X1A,
                0X6A,
                0XED,
                0X8B,
                0X63,
                0xA6,  # 65-byte signature
                0xAC,  # OP_CHECKSIG
            ]),
        )
        # fmt: on
        msgTx.addTxIn(txIn)
        msgTx.addTxOut(txOut)
        msgTx.lockTime = 0
        msgTx.expiry = 0
        # Check that this is the very first tx in the chain.
        assert msgTx.looksLikeCoinbase()
        # Ensure the hash produced is expected.
        assert msgTx.hash() == wantHash
Exemplo n.º 8
0
def multiTx():
    """
    multiTx is a MsgTx with an input and output and used in various tests.
    """
    return msgtx.MsgTx(
        cachedHash=None,
        serType=wire.TxSerializeFull,
        version=1,
        txIn=[
            msgtx.TxIn(
                previousOutPoint=msgtx.OutPoint(
                    txHash=None,
                    idx=0xFFFFFFFF,
                    tree=0,
                ),
                sequence=0xFFFFFFFF,
                valueIn=0x1212121212121212,
                blockHeight=0x15151515,
                blockIndex=0x34343434,
                signatureScript=ByteArray(
                    [0x04, 0x31, 0xDC, 0x00, 0x1B, 0x01, 0x62]),
            ),
        ],
        txOut=[
            msgtx.TxOut(
                value=0x12A05F200,
                version=0xABAB,
                pkScript=ByteArray([
                    0x41,  # OP_DATA_65
                    0X04,
                    0XD6,
                    0X4B,
                    0XDF,
                    0XD0,
                    0X9E,
                    0XB1,
                    0XC5,
                    0XFE,
                    0X29,
                    0X5A,
                    0XBD,
                    0XEB,
                    0X1D,
                    0XCA,
                    0X42,
                    0X81,
                    0XBE,
                    0X98,
                    0X8E,
                    0X2D,
                    0XA0,
                    0XB6,
                    0XC1,
                    0XC6,
                    0XA5,
                    0X9D,
                    0XC2,
                    0X26,
                    0XC2,
                    0X86,
                    0X24,
                    0XE1,
                    0X81,
                    0X75,
                    0XE8,
                    0X51,
                    0XC9,
                    0X6B,
                    0X97,
                    0X3D,
                    0X81,
                    0XB0,
                    0X1C,
                    0XC3,
                    0X1F,
                    0X04,
                    0X78,
                    0X34,
                    0XBC,
                    0X06,
                    0XD6,
                    0XD6,
                    0XED,
                    0XF6,
                    0X20,
                    0XD1,
                    0X84,
                    0X24,
                    0X1A,
                    0X6A,
                    0XED,
                    0X8B,
                    0X63,
                    0xA6,  # 65-byte signature
                    0xAC,  # OP_CHECKSIG
                ]),
            ),
            msgtx.TxOut(
                value=0x5F5E100,
                version=0xBCBC,
                pkScript=ByteArray([
                    0x41,  # OP_DATA_65
                    0X04,
                    0XD6,
                    0X4B,
                    0XDF,
                    0XD0,
                    0X9E,
                    0XB1,
                    0XC5,
                    0XFE,
                    0X29,
                    0X5A,
                    0XBD,
                    0XEB,
                    0X1D,
                    0XCA,
                    0X42,
                    0X81,
                    0XBE,
                    0X98,
                    0X8E,
                    0X2D,
                    0XA0,
                    0XB6,
                    0XC1,
                    0XC6,
                    0XA5,
                    0X9D,
                    0XC2,
                    0X26,
                    0XC2,
                    0X86,
                    0X24,
                    0XE1,
                    0X81,
                    0X75,
                    0XE8,
                    0X51,
                    0XC9,
                    0X6B,
                    0X97,
                    0X3D,
                    0X81,
                    0XB0,
                    0X1C,
                    0XC3,
                    0X1F,
                    0X04,
                    0X78,
                    0X34,
                    0XBC,
                    0X06,
                    0XD6,
                    0XD6,
                    0XED,
                    0XF6,
                    0X20,
                    0XD1,
                    0X84,
                    0X24,
                    0X1A,
                    0X6A,
                    0XED,
                    0X8B,
                    0X63,
                    0xA6,  # 65-byte signature
                    0xAC,  # OP_CHECKSIG
                ]),
            ),
        ],
        lockTime=0,
        expiry=0,
    )
Exemplo n.º 9
0
    def test_main(self, monkeypatch):
        """
        Test account functionality.
        """
        # Set up globals for test.
        monkeypatch.setattr(account, "DefaultGapLimit", 2)

        db = KeyValueDatabase(":memory:").child("tmp")
        acct = self.newAccount(db)
        acct.unlock(self.cryptoKey)
        acct.generateGapAddresses()
        for _ in range(20):
            acct.nextExternalAddress()
        satoshis = int(round(5 * 1e8))
        txHash = rando.newHash()
        txid = reversed(txHash).hex()
        vout = 2
        address = acct.nextExternalAddress()

        utxo = account.UTXO(
            address=address,
            txHash=txHash,
            vout=vout,
            scriptPubKey=ByteArray(0),
            satoshis=satoshis,
            maturity=1,
        )

        # A helper function to get the current utxo count.
        utxocount = lambda: len(list(acct.utxoscan()))

        # Add the utxo
        acct.addUTXO(utxo)
        # Check the count and balance categories.
        assert utxocount() == 1
        assert acct.calcBalance(1).total == satoshis
        assert acct.calcBalance(1).available == satoshis
        assert acct.calcBalance(0).available == 0
        # Helper functions.
        assert acct.caresAboutTxid(txid)
        assert not acct.caresAboutTxid("")
        acct.addUTXO(utxo)
        assert utxocount() == 1
        acct.spendUTXO(utxo)
        assert utxocount() == 0

        b = acct.serialize()
        reAcct = account.Account.unblob(b.b)

        assert acct.pubKeyEncrypted == reAcct.pubKeyEncrypted
        assert acct.privKeyEncrypted == reAcct.privKeyEncrypted
        assert acct.name == reAcct.name
        assert acct.coinID == reAcct.coinID
        assert acct.netID == reAcct.netID
        assert acct.gapLimit == reAcct.gapLimit
        assert acct.cursorExt == reAcct.cursorExt
        assert acct.cursorInt == reAcct.cursorInt
        assert acct.gapLimit == reAcct.gapLimit

        # Create a faux blockchain for the account.
        class Blockchain:
            txidsForAddr = lambda addr: []
            UTXOs = lambda addrs: []
            tipHeight = 5

        acct.blockchain = Blockchain

        class Signals:
            b = None

            @classmethod
            def balance(cls, b):
                cls.b = b

        acct.signals = Signals

        # Add a txid for this first address
        txid = rando.newHash().hex()
        zerothAddr = acct.externalAddresses[0]
        acct.addTxid(zerothAddr, txid)
        # Add a voting service provider
        vspKey = rando.newKey().hex()
        ticketAddr = "ticketAddr"
        pi = PurchaseInfo("addr", 1.0, ByteArray(b"scripthashscript"),
                          ticketAddr, 1, 0, 2)
        vsp = VotingServiceProvider("https://myvsp.com", vspKey,
                                    nets.mainnet.Name, pi)
        with pytest.raises(DecredError):
            acct.setPool(None)
        acct.setPool(vsp)
        acct.setNewPool(vsp)
        # Add UTXOs
        utxos = [account.UTXO.parse(u) for u in self.dcrdataUTXOs]
        acct.resolveUTXOs(utxos)
        ticketStats = acct.ticketStats()
        assert ticketStats.count == 0
        assert ticketStats.value == 0

        # Check that the addresses txid and the vsp load from the database.
        acct.txs.clear()
        acct.stakePools.clear()
        acct.utxos.clear()
        assert acct.stakePool() is None
        acct.load(db, Blockchain, Signals)
        assert zerothAddr in acct.txs
        assert len(acct.txs[zerothAddr]) == 1
        assert acct.txs[zerothAddr][0] == txid
        assert len(acct.stakePools) == 1
        assert acct.stakePool().apiKey == vspKey
        assert acct.calcBalance().available == self.utxoTotal
        assert len(acct.getUTXOs(self.utxoTotal)[0]) == 2
        assert len(acct.getUTXOs(self.utxoTotal, lambda u: False)[0]) == 0
        assert acct.lastSeen(acct.externalAddresses, -1) == 0
        branch, idx = acct.branchAndIndex(zerothAddr)
        assert branch == account.EXTERNAL_BRANCH
        assert idx == 0
        branch, idx = acct.branchAndIndex(acct.internalAddresses[0])
        assert branch == account.INTERNAL_BRANCH
        assert idx == 0
        checkKey = acct.privKey.child(account.EXTERNAL_BRANCH).child(0).key
        with pytest.raises(DecredError):
            acct.privKeyForAddress("")
        assert acct.privKeyForAddress(zerothAddr).key == checkKey
        ticketAddrs = acct.addTicketAddresses([])
        assert len(ticketAddrs) == 1
        assert ticketAddrs[0] == ticketAddr
        assert acct.hasPool()

        # Exercise setNode
        acct.setNode(1)
        assert acct.node == 1

        # Add a coinbase transaction output to the account.
        coinbase = newCoinbaseTx()
        cbUTXO = account.UTXO("addr",
                              ByteArray(b"id"),
                              1,
                              height=5,
                              satoshis=int(1e8))
        coinbase.addTxOut(msgtx.TxOut(int(1e8)))
        coinbaseID = coinbase.id()
        cbUTXO.txid = coinbaseID
        acct.addUTXO(cbUTXO)
        acct.addMempoolTx(coinbase)
        assert coinbaseID in acct.mempool
        # Send a block signal and have the transaction confirmed.
        sig = {"message": {"block": {"Tx": [{"TxID": coinbaseID}]}}}
        acct.blockchain.tipHeight = 5
        acct.blockchain.tx = lambda *a: coinbase
        acct.blockSignal(sig)
        assert coinbaseID not in acct.mempool
        assert cbUTXO.key() in acct.utxos
        maturity = 5 + nets.mainnet.CoinbaseMaturity
        assert acct.utxos[cbUTXO.key()].maturity == maturity
        assert acct.calcBalance(
            maturity).available == self.utxoTotal + cbUTXO.satoshis
        assert acct.calcBalance(0).available == self.utxoTotal
        acct.spendUTXOs([cbUTXO])
        assert acct.calcBalance(maturity).available == self.utxoTotal

        # make a ticket and a vote
        ticket = account.UTXO.parse(dcrdataUTXO)
        utxo.setTicketInfo(dcrdataTinfo)
        utxo.scriptPubKey = ticketScript
        utxo.parseScriptClass()
        acct.addUTXO(ticket)
        expVal = acct.calcBalance().available - ticket.satoshis
        voteTx = msgtx.MsgTx.new()
        voteTx.addTxIn(
            msgtx.TxIn(
                msgtx.OutPoint(reversed(ByteArray(ticket.txid)), ticket.vout,
                               0)))
        acct.blockchain.tx = lambda *a: voteTx
        acct.addressSignal(ticketAddr, "somehash")
        assert Signals.b.available == expVal

        # Detect a new utxo for the account.
        newVal = int(5e8)
        expVal = acct.calcBalance().available + newVal
        addr = acct.externalAddresses[1]
        a = addrlib.decodeAddress(addr, nets.mainnet)
        script = txscript.payToAddrScript(a)
        newTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(newVal, script)
        newTx.addTxOut(op)
        acct.blockchain.tx = lambda *a: newTx
        utxo = account.UTXO(addr, ByteArray(b"txid"), 0, satoshis=newVal)
        acct.blockchain.txVout = lambda *a: utxo
        acct.addressSignal(addr, "somehash")
        assert Signals.b.available == expVal

        # test syncing. add a single output for external address #2
        acct.stakePool().getPurchaseInfo = lambda: None
        acct.stakePool().authorize = lambda a: True
        newVal = int(3e8)
        addr = acct.externalAddresses[2]
        a = addrlib.decodeAddress(addr, nets.mainnet)
        script = txscript.payToAddrScript(a)
        newTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(newVal, script)
        newTx.addTxOut(op)
        utxo = account.UTXO(addr, ByteArray(b"txid"), 0, satoshis=newVal)

        def t4a(*a):
            acct.blockchain.txidsForAddr = lambda *a: []
            return [newTx.id()]

        acct.blockchain.txidsForAddr = t4a

        def utxos4a(*a):
            acct.blockchain.UTXOs = lambda *a: []
            return [utxo]

        acct.blockchain.UTXOs = utxos4a
        acct.blockchain.subscribeAddresses = lambda addrs, receiver: None
        acct.blockchain.subscribeBlocks = lambda a: None
        acct.sync()
        assert Signals.b.available == newVal

        # spend the utxo by sending it. Reusing newTx for convenience.
        changeVal = int(5e7)
        addr = acct.internalAddresses[0]
        change = account.UTXO(addr,
                              ByteArray(b"newtxid"),
                              0,
                              satoshis=changeVal)
        acct.blockchain.sendToAddress = lambda *a: (newTx, [utxo], [change])
        acct.sendToAddress(1, "recipient")
        assert Signals.b.available == changeVal

        # purchase some tickets. Reusing newTx for convenience again.
        ticket = account.UTXO.parse(dcrdataUTXO)
        ticket.setTicketInfo(dcrdataTinfo)
        ticket.scriptPubKey = ticketScript
        ticket.parseScriptClass()
        acct.blockchain.purchaseTickets = lambda *a: ([newTx, []], [],
                                                      [ticket])
        acct.signals.spentTickets = lambda: True
        acct.purchaseTickets(1, 1)
        assert Signals.b.total == changeVal + ticket.satoshis

        # revoke the ticket
        ticketTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(ticket.satoshis, ticket.scriptPubKey)
        ticketTx.addTxOut(op)
        ticket.tinfo.status = "missed"
        redeemHash = addrlib.AddressScriptHash(
            txscript.extractStakeScriptHash(ticketScript, opcode.OP_SSTX),
            nets.mainnet,
        )
        acct.stakePool().purchaseInfo.ticketAddress = redeemHash.string()
        revoked = False

        def rev(*a):
            nonlocal revoked
            revoked = True

        acct.blockchain.tx = lambda *a: ticketTx
        acct.blockchain.revokeTicket = rev
        acct.revokeTickets()
        assert revoked
Exemplo n.º 10
0
    # challenge address.
    if hash2x != doubleHash:
        print("'{}' is the wrong answer.".format(answer))
        continue

    print("\nCorrect answer!")

    # Build the transaction.
    rewardTx = msgtx.MsgTx.new()
    for utxo in utxos:
        prevOut = msgtx.OutPoint(reversed(ByteArray(utxo["txid"])),
                                 int(utxo["vout"]), msgtx.TxTreeRegular)
        rewardTx.addTxIn(msgtx.TxIn(prevOut, valueIn=utxo["satoshis"]))

    # Add the reward output with zero value for now.
    txout = msgtx.TxOut(pkScript=txscript.payToAddrScript(rewardAddr))
    rewardTx.addTxOut(txout)

    # Get the serialized size of the transaction. Since there are no signatures
    # involved, the size is known exactly. Use the size to calculate transaction
    # fees.
    # 1 + 73 = signature
    # 1 + 32 = answer hash
    # 1 + 70 = redeem script
    sigScriptSize = 1 + 73 + 1 + 32 + 1 + 70
    maxSize = rewardTx.serializeSize() + len(utxos) * sigScriptSize
    fees = feeRate * maxSize
    if reward <= fees:
        raise AssertionError(f"reward must be > fees")
    netReward = reward - fees
    # Set the value on the reward output.