Example #1
0
    def test_native_P2WSH_SIGHASH_SINGLE_ANYONECANPAY(self):
        tx = TEST_CASES[2]
        deserialized = deserialize(tx['unsigned'])
        serialized = serialize(deserialized)
        self.assertEqual(serialized, tx['unsigned'])
        self.assertEqual(deserialized['locktime'], tx['locktime'])
        ins = self.get_pybtc_vins(tx)
        outs = self.get_pybtc_outs(tx)
        generated_tx = mktx(ins, outs)
        stripped_tx = strip_witness_data(generated_tx)
        self.assertEqual(stripped_tx, serialized)
        priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey'])
        partially_signed = segwit_sign(generated_tx,
                                       0,
                                       priv0,
                                       int(0.16777215 * 10**8),
                                       hashcode=SIGHASH_SINGLE
                                       | SIGHASH_ANYONECANPAY,
                                       script=tx['ins'][0]['txinwitness'][1])

        signed = segwit_sign(partially_signed,
                             1,
                             priv0,
                             int(0.16777215 * 10**8),
                             hashcode=SIGHASH_SINGLE | SIGHASH_ANYONECANPAY,
                             script=tx['ins'][1]['txinwitness'][1],
                             separator_index=tx['ins'][1]['separator'])
        self.assertEqual(signed, tx['signed'])
        print('[Native P2WSH] SIGHASH_SINGLE OK')
Example #2
0
    def test_native_P2WSH_SIGHASH_SINGLE_ANYONECANPAY(self):
        tx = TEST_CASES[2]
        deserialized = deserialize(tx['unsigned'])
        serialized = serialize(deserialized)
        self.assertEqual(serialized, tx['unsigned'])
        self.assertEqual(deserialized['locktime'], tx['locktime'])
        ins = self.get_pybtc_vins(tx)
        outs = self.get_pybtc_outs(tx)
        generated_tx = mktx(ins, outs)
        stripped_tx = strip_witness_data(generated_tx)
        self.assertEqual(stripped_tx, serialized)
        priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey'])
        partially_signed = segwit_sign(generated_tx,
                                       0,
                                       priv0,
                                       int(0.16777215 * 10**8),
                                       hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY,
                                       script=tx['ins'][0]['txinwitness'][1])

        signed = segwit_sign(partially_signed,
                             1,
                             priv0,
                             int(0.16777215 * 10 ** 8),
                             hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY,
                             script=tx['ins'][1]['txinwitness'][1],
                             separator_index=tx['ins'][1]['separator'])
        self.assertEqual(signed, tx['signed'])
        print('[Native P2WSH] SIGHASH_SINGLE OK')
Example #3
0
 def test_native_P2WPKH_SIGHASH_ALL(self):
     tx = TEST_CASES[0]
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs, locktime=tx['locktime'])
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     partially_signed = p2pk_sign(
         stripped_tx, 0,
         self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey']))
     signed = segwit_sign(
         partially_signed, 1,
         self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey']),
         tx['ins'][1]['amount'] * 10**8)
     self.assertEqual(signed, tx['signed'])
     print('[Native P2WPKH] SIGHASH_ALL OK')
Example #4
0
 def test_native_P2WPKH_SIGHASH_ALL(self):
     tx = TEST_CASES[0]
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs, locktime=tx['locktime'])
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     partially_signed = p2pk_sign(stripped_tx,
                                  0,
                                  self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey']))
     signed = segwit_sign(partially_signed,
                          1,
                          self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey']),
                          tx['ins'][1]['amount'] * 10**8)
     self.assertEqual(signed, tx['signed'])
     print('[Native P2WPKH] SIGHASH_ALL OK')
Example #5
0
def moveTokens(tokenid, outputs, change):
    """ move given amount of tokens to given outputs.
    tokenid: name of the token to deal with
    outputs is a list of (output, amount) pairs.
    change is the receiving address for change.
    Returns a pair (TokenTransaction, OnchainTransaction) or None if an error occured.
    Does not change the DB.

    Autocreates a mint transaction if the outputs exceed the available funds and there is a blank output plus the corresponding private key available.
    """
    log.info("Trying to move %s tokens to %s outputs plus one change address",
             tokenid, len(outputs))
    token_addr = tokenval.tok2addr(tokenid)

    for x in outputs:
        if x[1] < 0:
            log.error("Negative outputs not allowed.")
            return None, None

    # total needed amount of tokens
    needed = sum(x[1] for x in outputs)
    log.info("Total needed amount of tokens: %d", needed)

    # sum of incoming BCH satoshis
    bch_insum = 0

    # sum on incoming tokens
    token_insum = 0

    utxo_avail = tokenval.unspentOutputsForToken(token_addr)
    log.info("Known UTXOs for token %s: %d.", tokenid, len(utxo_avail))
    # collect enough inputs until we've got everything we need
    privs = {}

    mints = []
    bch_inputs = []
    bch_outputs = []

    for utxo in utxo_avail:
        value, bch_value = takeUtxo(utxo,
                                    privs,
                                    bch_inputs,
                                    skip_if_blank=True)
        if value is None or bch_value is None:
            log.info("Skipping UTXO %s.", utxo)
            continue
        token_insum += value
        bch_insum += bch_value
        if token_insum >= needed:
            break

    log.info("Collected %d inputs for a total of %d %s and %d satoshis.",
             len(bch_inputs), token_insum, tokenid, bch_insum)

    if token_insum < needed:
        # FIXME: code dup
        log.info("Less tokens %d available than needed %d.", token_insum,
                 needed)
        blank = tokenval.unspentBlank(token_addr)
        if blank is None:
            log.error(
                "And no blank outputs are available to create new tokens.")
            return None, None
        value, bch_value = takeUtxo(blank, privs, bch_inputs)

        assert (value is None)
        if bch_value is None:
            log.info("Blank could not be taken.")
            return None, None
        mints.append((blank.txhash, blank.outnum, needed - token_insum))
        log.info("Taking blank unspent output %s and minting %d %s.", blank,
                 needed - token_insum, tokenid)

        token_insum += needed - token_insum
        bch_insum += bch_value

    l = len(outputs) + 1  # +1 for change output

    miner_fee = 200 * l  # total miner fee for transaction

    min_funds = 1000

    log.info("Total miner fee: %d", miner_fee)

    if bch_insum < l * min_funds + miner_fee:  # 1 satoshi per token output plus miner fee available
        log.info(
            "Needing extra inputs for miner fees, having %d satoshis and needing %d satoshis.",
            bch_insum, l * min_funds + miner_fee)
        for utxo in tokenval.allUnspentOutputs():
            if ("TOKEN_" + utxo.token_addr
                    in tokenval.db) or utxo.value is not None:
                log.info("Skipping UTXO %s as it is a token output.", utxo)
                continue
            value, bch_value = takeUtxo(utxo, privs, bch_inputs)
            assert (value is None)
            if bch_value is None:
                continue
            bch_insum += bch_value
            if bch_insum >= l * min_funds + miner_fee:
                break
    if bch_insum < l * min_funds + miner_fee:  # 1 satoshi per token output plus miner fee available
        log.info(
            "Not enough extra inputs available, having %d satoshis and needing %d satoshis",
            bch_insum, l * min_funds + miner_fee)
        return None, None

    tok_outputs = []

    for out in outputs:
        addr, val = out
        bch_outputs.append({"value": min_funds, "address": addr})
        log.info("Adding output %s with %d tokens.", addr, val)
        tok_outputs.append(val)

    bch_outputs.append({
        "value": min_funds + bch_insum - miner_fee - l * min_funds,
        "address": change
    })
    log.info("Adding output %s with %d tokens.", change, token_insum - needed)
    tok_outputs.append(token_insum - needed)

    log.info("Token mints: %d", len(mints))
    log.info("Token outputs: %d", len(outputs))
    toktxn = tokenval.TokenTransaction(token_addr, mints, tok_outputs,
                                       list(privs.values()))

    log.info("BCH transaction inputs: %s", bch_inputs)
    log.info("BCH transaction outputs: %s", bch_outputs)

    bchtxn = bitcoin.mktx(bch_inputs, bch_outputs)
    log.info("Created unsigned BCH transaction.")
    bchtxn = bitcoin.transaction.mk_opreturn(codecs.decode(toktxn.hsh, "hex"),
                                             bchtxn)
    log.info("Added OP_RETURN pointer.")

    for i, bch_in in enumerate(bch_inputs):
        prevoutstr = bch_in["output"]
        # bch_inputs.append(
        #     { "output" : prevoutstr,
        #       "amount" : utxo.bch_value})

        log.info("Signing BCH input %d: %s", i, bch_in)
        bchtxn = bitcoin.segwit_sign(bchtxn,
                                     i,
                                     privs[prevoutstr],
                                     bch_in["amount"],
                                     hashcode=bitcoin.SIGHASH_ALL
                                     | bitcoin.SIGHASH_FORKID,
                                     separator_index=None)

    octxn = tokenval.OnchainTransaction(bchtxn)

    return toktxn, octxn