예제 #1
0
    def get_transactions(self, address_list, limit=100, min_block=None):
        """ Provides transactions associated with each address in address_list.

        Args:
            address_list (list(str)): List of Base58Check encoded Bitcoin
            addresses.
            limit (int): Maximum number of transactions to return.
            min_block (int): Block height from which to start getting
            transactions. If None, will get transactions from the
            entire blockchain.

        Returns:
            dict: A dict keyed by address with each value being a list of
            Transaction objects.
        """
        last_block_index = self.get_block_height()
        ret = defaultdict(list)
        total_items = limit
        for addresses in self._list_chunks(address_list, 199):
            fr = 0
            to = min(50, limit)

            while fr < total_items:
                req = "addrs/" + ",".join(addresses) + \
                      "/txs?from=%d&to=%d" % (fr, to)

                r = self._request("GET", req)
                txn_data = r.json()

                if "totalItems" in txn_data:
                    total_items = txn_data["totalItems"]

                fr = txn_data["to"]
                to = fr + 50

                for data in txn_data['items']:
                    if "vin" not in data or "vout" not in data:
                        continue
                    block_hash = None
                    block = None
                    if data['confirmations'] > 0:
                        block = last_block_index - data['confirmations'] + 1
                        block_hash = Hash(data['blockhash'])

                    metadata = dict(block=block,
                                    block_hash=block_hash,
                                    network_time=data.get("time", None),
                                    confirmations=data['confirmations'])

                    if min_block and block:
                        if block < min_block:
                            continue

                    txn, addr_keys = self.txn_from_json(data)
                    for addr in addr_keys:
                        if addr in addresses:
                            ret[addr].append(
                                dict(metadata=metadata, transaction=txn))

        return ret
def test_op_checklocktimeverify():
    prev_txn_hash = Hash(
        '6eae1e03964799c4e29039db459ea4fad4df57c2b06f730b60032a48fb075620')
    txn_input = TransactionInput(prev_txn_hash, 0, Script(""), 1)

    addr = "1HJiL6AYYmtkbJzC9bxAorznWijwNK5Z8E"
    out_script_pub_key = Script.build_p2pkh(utils.address_to_key_hash(addr)[1])
    txn_output = TransactionOutput(9000, out_script_pub_key)

    # Create the txn
    txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, [txn_input],
                      [txn_output], 367987)

    # This is one more (367988) so it should fail
    s = Script("0x749d05 OP_CHECKLOCKTIMEVERIFY")

    si = ScriptInterpreter(txn=txn,
                           input_index=0,
                           sub_script=out_script_pub_key)
    si.run_script(s)

    assert not si.valid

    # This is negative, so it should fail
    s = Script("0xfff74d05 OP_CHECKLOCKTIMEVERIFY")

    si = ScriptInterpreter(txn=txn,
                           input_index=0,
                           sub_script=out_script_pub_key)
    si.run_script(s)

    assert not si.valid

    # This is one less (367986) so it should pass
    s = Script("0x729d05 OP_CHECKLOCKTIMEVERIFY")

    si = ScriptInterpreter(txn=txn,
                           input_index=0,
                           sub_script=out_script_pub_key)
    si.run_script(s)

    assert not si.stop

    # Now reformulate the txn so that the input is finalized
    txn_input.sequence_num = 0xffffffff
    si.run_script(s)

    assert not si.valid

    # The last check is if there are mismatching notions of locktime
    txn_input.sequence_num = 1
    txn.lock_time = 500000001
    si = ScriptInterpreter(txn=txn,
                           input_index=0,
                           sub_script=out_script_pub_key)
    si.run_script(s)

    assert not si.valid
예제 #3
0
    def set_txn_side_effect_for_hd_discovery(self):
        # For each used account, there are at least 2 calls required:
        # 1 for the first DISCOVERY_INCREMENT payout addresses and 1
        # for the first DISCOVERY_INCREMENT change
        # addresses. Depending on the number of used addresses for the
        # account, this will change.

        effects = []

        n = self._num_used_accounts
        if n == 0:
            n = 1

        for acct_num in range(n):
            for change in [0, 1]:
                num_used = self._num_used_addresses[acct_num][change]
                r = math.ceil((num_used + HDAccount.GAP_LIMIT) /
                              self.address_increment)
                k = 'change_addresses' if change else 'payout_addresses'
                addr_list = self._acct_keys[acct_num][k]

                if change:
                    metadata = dict(block=234790 + r,
                                    block_hash=Hash("000000000000000007d57f03ebe36dbe4f87ab2f340e93b45999ab249b6dc0df"),
                                    confirmations=23890 - r)
                else:
                    metadata = dict(block=None,
                                    block_hash=None,
                                    confirmations=0)

                if r == 0:
                    r = 1
                for i in range(r):
                    start = i * self.address_increment
                    end = (i + 1) * self.address_increment
                    addr_range = range(start, end)

                    out = TransactionOutput(value=100000,
                                            script=Script.build_p2pkh(
                                                address_to_key_hash(
                                                    addr_list[i])[1]))
                    dummy_txn = Transaction(1,
                                            [],
                                            [out],
                                            0)

                    m = MockTxnDict(num_used=num_used,
                                    addr_range=addr_range,
                                    addr_list=addr_list,
                                    used_value=[dict(metadata=metadata,
                                                     transaction=dummy_txn)],
                                    unused_value=[])
                    effects.append(m)

        self.get_transactions.side_effect = effects

        return len(effects)
예제 #4
0
    def __init__(self, height, version, prev_block_hash, time, bits, nonce, txns):
        self.block_header = BlockHeader(version,
                                        prev_block_hash,
                                        Hash(bytes(32)),  # Fake merkle_root for now
                                        time,
                                        bits,
                                        nonce)            # Fake nonce also

        self.height = height
        self.txns = txns

        self.merkle_tree = None
        self.invalidate()
예제 #5
0
    def from_bytes(b):
        """ Creates a BlockHeader object from a serialized
        bytestream. This function "eats" the bytestream and
        returns the remainder of the stream after deserializing
        the fields of the BlockHeader.

        Args:
            b (bytes): bytes beginning with the (4-byte) version.

        Returns:
            bh, b (tuple): A tuple containing two elements - a BlockHeader object
            and the remainder of the bytestream after deserialization.
        """
        version, b = unpack_u32(b)
        prev_block_hash, b = Hash(b[0:32]), b[32:]
        merkle_root_hash, b = Hash(b[0:32]), b[32:]
        time, b = unpack_u32(b)
        bits, b = unpack_u32(b)
        nonce, b = unpack_u32(b)

        return (BlockHeader(version, prev_block_hash, merkle_root_hash, time,
                            bits, nonce), b)
예제 #6
0
def test_smallest_first():
    utxos_by_addr = {}

    addr_base = "1LQ1TjCKJN8GXsYtsqnREqs5Z4eaPCu5p"
    h = Hash(
        "a2972893f1be1f54d68a9228d9706ff8f202bb80f488f4dd46c0fe37c1e42415")
    for i in range(10):
        addr = addr_base + str(i)
        utxos_by_addr[addr] = [
            UnspentTransactionOutput(transaction_hash=h,
                                     outpoint_index=random.randint(0, 10),
                                     value=i * 20000,
                                     scr=Script(""),
                                     confirmations=10)
        ]

    utxos = []
    for addr, addr_utxos in utxos_by_addr.items():
        utxos += addr_utxos

    amount = 1000000
    selected, fees = utxo_selector_smallest_first(utxos_by_addr=utxos_by_addr,
                                                  amount=amount,
                                                  num_outputs=2)
    assert not selected

    amount = 100000
    selected, fees = utxo_selector_smallest_first(utxos_by_addr=utxos_by_addr,
                                                  amount=amount,
                                                  num_outputs=2)

    sum_selected = 0
    remaining = []
    selected_list = []
    for addr, utxo_list in selected.items():
        sum_selected += sum([utxo.value for utxo in utxo_list])
        selected_list += utxo_list

    assert sum_selected >= amount

    remaining = [u for u in utxos if u not in selected_list]

    largest_selected = 0
    for s in selected_list:
        if s.value > largest_selected:
            largest_selected = s.value

    # Make sure that the largest of the selected is <= min(remaining)
    assert remaining == [] or largest_selected <= min(
        [u.value for u in remaining])
예제 #7
0
    def __init__(self, height, version, prev_block_hash, time, bits, merkle_edge, cb_txn=None):
        self.block_header = BlockHeader(version,
                                        prev_block_hash,
                                        Hash(bytes(32)),  # Fake merkle_root for now
                                        time,
                                        bits,
                                        0)                # Fake nonce also
        self.height = height
        self.merkle_edge = merkle_edge

        if cb_txn is not None:
            self.coinbase_transaction = cb_txn
        else:
            self._cb_txn = None
예제 #8
0
def mine_work(work_msg, enonce1, enonce2_size):
    """ Mine the work using a CPU to find a valid solution.

    Loop until the CPU finds a valid solution of the given work.

    Todo:
        Slow down the click echo when on a 21BC.

    Args:
        work_msg (WorkNotification): the work given by the pool API
        enonce1 (bytes): extra nonce required to make the coinbase transaction
        enonce2_size (int): size of the extra nonce 2 in bytes
    """
    pool_target = utils.bits_to_target(work_msg.bits_pool)
    for enonce2_num in range(0, 2**(enonce2_size * 8)):
        enonce2 = enonce2_num.to_bytes(enonce2_size, byteorder="big")

        cb_txn, _ = Transaction.from_bytes(work_msg.coinb1 + enonce1 +
                                           enonce2 + work_msg.coinb2)
        cb = CompactBlock(
            work_msg.height,
            work_msg.version,
            Hash(work_msg.prev_block_hash),
            work_msg.ntime,
            work_msg.nbits,  # lower difficulty work for testing
            work_msg.merkle_edge,
            cb_txn)

        row_counter = 0
        for nonce in range(0xffffffff):

            if nonce % 6e3 == 0:
                logger.info(click.style(u'█', fg='green'), nl=False)
                row_counter += 1
            if row_counter > 40:
                row_counter = 0
                logger.info("")

            cb.block_header.nonce = nonce
            h = cb.block_header.hash.to_int('little')
            if h < pool_target:
                share = Share(enonce2=enonce2,
                              nonce=nonce,
                              work_id=work_msg.work_id,
                              otime=int(time.time()))
                # adds a new line at the end of progress bar
                logger.info("")
                return share

        logger.info("Exhausted enonce1 space. Changing enonce2")
예제 #9
0
    def set_txn_side_effect_for_index(self, account_index, address_index,
                                      change):
        dummy_txn = Transaction(1, [], [], 0)
        metadata = dict(block_height=234790,
                        block_hash=Hash("000000000000000007d57f03ebe36dbe4f87ab2f340e93b45999ab249b6dc0df"),
                        confirmations=23890)

        k = 'change_addresses' if change else 'payout_addresses'
        addr_list = self._acct_keys[account_index][k]
        mtd = MockTxnDict(num_used=address_index + 1,
                          addr_range=range(address_index, address_index + 1),
                          addr_list=addr_list,
                          used_value=[dict(metadata=metadata,
                                           transaction=dummy_txn)],
                          unused_value=[])
        self.get_transactions.side_effect = [mtd]
예제 #10
0
    def get_utxos(self, addresses, include_unconfirmed=False):
        """ Returns a dict containing the UTXOs for the desired
            addresses

        Args:
            addresses (list): List of addresses to get balances for
            include_unconfirmed (bool): True if unconfirmed
                transactions should be included in the balance.

        Returns:
            dict: Keys are addresses, values are lists of
                UnspentTransactionOutput objects for the address.
        """
        unconfirmed_mask = self.UNSPENT | self.UNCONFIRMED | self.PROVISIONAL
        rv = {}
        for addr in addresses:
            # Get the list of unspent deposits.
            if addr not in self._deposits_for_addr:
                continue

            for txid in self._deposits_for_addr[addr]:
                for i in self._deposits_for_addr[addr][txid]:
                    # Look up the status in the outputs cache
                    if txid not in self._outputs_cache or \
                       i not in self._outputs_cache[txid]:
                        raise Exception("Don't have information for %r:%r" %
                                        (txid, i))

                    status = self._outputs_cache[txid][i]['status']
                    if status & self.UNSPENT:
                        if (status & unconfirmed_mask and include_unconfirmed) or \
                           (status == self.UNSPENT and not include_unconfirmed):
                            out = self._outputs_cache[txid][i]['output']
                            utxo = UnspentTransactionOutput(
                                transaction_hash=Hash(txid),
                                outpoint_index=i,
                                value=out.value,
                                scr=out.script,
                                confirmations=self._txn_cache[txid].
                                confirmations)

                            if addr not in rv:
                                rv[addr] = []
                            rv[addr].append(utxo)

        return rv
예제 #11
0
    def get_transactions(self, address_list, limit=100, min_block=None):
        """ Provides transactions associated with each address in address_list.

        Args:
            address_list (list): List of Base58Check encoded Bitcoin
                addresses.
            limit (int): Maximum number of transactions to return.
            min_block (int): Block height from which to start getting
                transactions. If None, will get transactions from the
                entire blockchain.

        Returns:
            dict: A dict keyed by address with each value being a list of
            Transaction objects.
        """
        ret = defaultdict(list)
        for addresses in self._list_chunks(address_list, 199):
            path = "addresses/" + ",".join(addresses) \
                   + "/transactions?limit={}".format(limit)
            if min_block:
                path += "&min_block={}".format(min_block)

            r = self._request("GET", path)
            txn_data = r.json()

            for data in txn_data:
                block_hash = None
                if data['block_hash']:
                    block_hash = Hash(data['block_hash'])
                metadata = dict(
                    block=data['block_height'],
                    block_hash=block_hash,
                    network_time=timegm(
                        arrow.get(
                            data['chain_received_at']).datetime.timetuple()),
                    confirmations=data['confirmations'])

                txn, addr_keys = self.txn_from_json(data)
                for addr in addr_keys:
                    if addr in addresses:
                        ret[addr].append(
                            dict(metadata=metadata, transaction=txn))

        return ret
예제 #12
0
    def _deserialize(wt_dict):
        # Private, only for internal wallet usage
        wt = WalletTransaction.from_hex(wt_dict['transaction'])
        if 'metadata' in wt_dict:
            m = wt_dict['metadata']
        else:
            m = wt_dict
        wt.block = m['block']
        if m['block_hash'] is not None:
            wt.block_hash = Hash(m['block_hash'])
        wt.confirmations = m['confirmations']
        wt.network_time = m['network_time']
        if 'value' in m:
            wt.value = m['value']
        if 'fees' in m:
            wt.fees = m['fees']
        if 'provisional' in m:
            wt.provisional = m['provisional']

        return wt
예제 #13
0
    def from_bytes(b):
        """ Deserializes a byte stream into a TransactionInput.

        Args:
            b (bytes): byte stream starting with the outpoint.

        Returns:
            tuple:
                 First element of the tuple is the TransactionInput
                 object and the second is the remaining byte stream.
        """
        outpoint = b[0:32]
        outpoint_index, b1 = unpack_u32(b[32:])
        script, b1 = Script.from_bytes(b1)
        sequence_num, b1 = unpack_u32(b1)

        return (
            TransactionInput(Hash(outpoint),
                             outpoint_index,
                             script,
                             sequence_num),
            b1
        )
예제 #14
0
    def txn_from_json(txn_json):
        """ Returns a new Transaction from a JSON-serialized transaction

        Args:
            txn_json:
                JSON with the following format:

                    {
                    "hash": "0bf0de38c26195919179f...",
                    "block_hash": "000000000000000...",
                    "block_height": 303404,
                    "block_time": "2014-05-30T23:54:55Z",
                    "chain_received_at": "2015-08-13T10:52:21.718Z",
                    "confirmations": 69389,
                    "lock_time": 0,
                    "inputs": [
                      {
                        "transaction_hash": "0bf0de38c2619...",
                        "output_hash": "b84a66c46e24fe71f9...",
                        "output_index": 0,
                        "value": 300000,
                        "addresses": [
                          "3L7dKYQGNoZub928CJ8NC2WfrM8U8GGBjr"
                        ],
                        "script_signature": "03046022100de7b67b9...",
                        "script_signature_hex": "00493046022100de7b...",
                        "sequence": 4294967295
                      }
                    ],
                    "outputs": [
                      {
                        "transaction_hash": "0bf0de38c261959...",
                        "output_index": 0,
                        "value": 290000,
                        "addresses": [
                          "1K4nPxBMy6sv7jssTvDLJWk1ADHBZEoUVb"
                        ],
                        "script": "OP_DUP OP_HASH160 c629680b8d...",
                        "script_hex": "76a914c629680b8d13...",
                        "script_type": "pubkeyhash",
                        "required_signatures": 1,
                        "spent": false,
                        "spending_transaction": null
                      }
                    ],
                    "fees": 10000,
                    "amount": 290000
                    },
                    Transaction.DEFAULT_TRANSACTION_VERSION

        Returns:
            two1.bitcoin.Transaction:
                a deserialized transaction derived
                from the provided json.

        """
        inputs = []
        outputs = []
        addr_keys = set()
        for i in txn_json["inputs"]:
            if 'coinbase' in i:
                inputs.append(
                    CoinbaseInput(height=txn_json["block_height"] or 0,
                                  raw_script=bytes.fromhex(i['coinbase']),
                                  sequence=i['sequence'],
                                  block_version=1))
            else:
                # Script length etc. are not returned so we need to
                # prepend that.
                script, _ = Script.from_bytes(
                    pack_var_str(bytes.fromhex(i["script_signature_hex"])))
                inputs.append(
                    TransactionInput(Hash(i["output_hash"]), i["output_index"],
                                     script, i["sequence"]))
            if "addresses" in i:
                addr_keys.add(i["addresses"][0])

        for i in txn_json["outputs"]:
            script, _ = Script.from_bytes(
                pack_var_str(bytes.fromhex(i["script_hex"])))
            outputs.append(TransactionOutput(i["value"], script))
            if "addresses" in i:
                addr_keys.add(i["addresses"][0])

        txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, inputs,
                          outputs, txn_json["lock_time"])

        return txn, addr_keys
def test_txns():
    txn = WalletTransaction.from_hex(
        '01000000029ccb0665ec780f8b05bf2315a48dfb154dc41f91e8046a59f1c75656826dea5d000000006b483045022100f4d2161473f9d0ba4b5cdbc9e5b7b1d8fca32e3b6bede307352bef6aaa3a08cd022023d8444f78f69de6fd0f6cc391a7ca4de3dc4181220932d01511eb1129fee09e01210328bd51733a7d5bee05368680adef9aaa3f9bb716ec716d5896b1d80afb734d6cffffffff2424cb910235b2059d59023aecfebf6fce4eee31c637e9a0b350491849688727020000006a473044022072de3d707f98adfed3266e0261750cd7b5162732e525d7df17f4e55a55e953b902205046b597acf7acf41e725b459ba6cfe8c03a9d877375cdf483cab9620f92961101210291cbb1304614d86b15f4e8f39e9d8299cd0304ff8b81b5bcf6d9a6f32be649bbffffffff0240420f00000000001976a91434fe777d676fceb3509584c1d7b9f13ee56514d488ace05a0000000000001976a9145237ba33122495420711b3f2cc0463dbb24c9d3988ac00000000'
    )  # nopep8

    txn.block = 374440
    txn.block_hash = Hash(
        '0000000000000000038ee0066680705455d500f287f6c56db7a979c2426a4c02')
    txn.confirmations = 7533

    cm.insert_txn(txn)

    txid = "3779f27a81cdbc435ac258ce5076c211e7a953027aab42573b1b7ce9e50abe8e"
    assert txid in cm._txn_cache

    in_addrs = [
        "1DpCouKa2evX3f2aELUy7iNdsrYuLLaqWy",
        "1GcmBmvYWJKLFHxrTtx5DqQLV7oHQAkH2c"
    ]

    out_addrs = [("15qCydrcqURADXJHrtMW9m6SpPTa3kqkQb", 1000000),
                 ("18VjAjZ7Au8U75LCHT7aH7mTwKETZwHTpi", 23264)]

    assert len(cm._txns_by_addr.keys()) == 4
    for i, a in enumerate(out_addrs):
        assert a[0] in cm._txns_by_addr
        assert list(cm._deposits_for_addr[a[0]][txid]) == [i]

    for i, a in enumerate(in_addrs):
        assert list(cm._spends_for_addr[a][txid]) == [i]

    # Check input and output caches
    assert txid in cm._inputs_cache
    assert len(cm._inputs_cache[txid]) == 2

    assert txid in cm._outputs_cache
    assert len(cm._outputs_cache[txid]) == 2
    assert cm._outputs_cache[txid][0]['output'] is not None
    assert cm._outputs_cache[txid][1]['output'] is not None
    assert cm._outputs_cache[txid][0]['status'] == CacheManager.UNSPENT
    assert cm._outputs_cache[txid][1]['status'] == CacheManager.UNSPENT

    out_txid1 = "5dea6d825656c7f1596a04e8911fc44d15fb8da41523bf058b0f78ec6506cb9c"
    assert out_txid1 in cm._outputs_cache
    assert cm._outputs_cache[out_txid1][0]['status'] == CacheManager.SPENT

    out_txid2 = "27876849184950b3a0e937c631ee4ece6fbffeec3a02599d05b2350291cb2424"
    assert out_txid2 in cm._outputs_cache
    assert len(cm._outputs_cache[out_txid2].keys()) == 1
    assert cm._outputs_cache[out_txid2][2]['status'] == CacheManager.SPENT

    assert cm.has_txns()
    assert cm.has_txns(0)
    assert not cm.has_txns(1)

    assert cm.have_transaction(txid)
    assert not cm.have_transaction(out_txid1)
    assert not cm.have_transaction(out_txid2)
    assert cm.get_transaction(txid) == txn
    assert cm.get_transaction(out_txid1) is None
    assert cm.get_transaction(out_txid2) is None

    # Check balances on addresses
    addr_balances = cm.get_balances([a[0] for a in out_addrs])
    for addr, exp_bal in out_addrs:
        assert addr_balances[addr] == exp_bal

    # Add a second transaction that deposits into addresses we have,
    # but insert it as unconfirmed
    txn_hex = "01000000028a9acc005a2158758e44242eee8c18fee7a43cda39a358cc783fb578cfa7cf5f000000006a47304402204a00fcb746f90095c1c50e048f1b0616b421617ca27a7a7465d4086a1623731802202404d0fce1b74f41ce1e3c63f61c8574c8cf2a5eae24ac4df714775168a9118c012102d8bfe3fd2d01f3a2b1380c34ccadcd318cafd1246f41258d7d244f409fb44c93ffffffff15857ef158778f603d34bcff74bd7935cb9d6b4a0147eea008be3f67bd395830020000006a4730440220466f93d784aa24bf497929433777fa283a7cd0000625179d3e9f5c75db4ad10f022022857a607665408a5521cbdf145c1fecd85c427ccf939c49f9a0d828a934e2530121021b5c9a9e6c97b4222c97da5a642e3531bce01b757cfdc9b29ac7d1cbf2d10710ffffffff0240420f00000000001976a91433a0a86dd9dab9902157d8d64e05fc8e0dfba16388ac7a1d0300000000001976a914134ca7427089b8f661efc9806a8418f72e57167f88ac00000000"  # nopep8

    txn = WalletTransaction.from_hex(txn_hex)
    cm.insert_txn(txn)

    txid = "d24f3b9f0aa7b6484bcea563f4c254bd24e8163906cbffc727c2b2dad43af61e"
    assert txid in cm._txn_cache

    in_addrs = [
        "1Ezv6YmYsZvALUaRcZRf8hBdxYni6cm78X",
        "16Mcvb7fYhif94d1RHCn5AE2dm1oXCGnH6"
    ]

    out_addrs = [("15hyvVXH2eJnakwhpqKBf5oTCa3o2bp8m8", 1000000),
                 ("12m3fcaabUgYwWcodgVZUGH6ntFqVrHk5C", 204154)]

    for i, a in enumerate(out_addrs):
        assert a[0] in cm._txns_by_addr
        assert list(cm._deposits_for_addr[a[0]][txid]) == [i]

    for i, a in enumerate(in_addrs):
        assert list(cm._spends_for_addr[a][txid]) == [i]

    # Check input and output caches
    assert txid in cm._inputs_cache
    assert len(cm._inputs_cache[txid]) == 2

    assert txid in cm._outputs_cache
    assert cm._outputs_cache[txid][0]['output'] is not None
    assert cm._outputs_cache[txid][1]['output'] is not None
    assert cm._outputs_cache[txid][0][
        'status'] == CacheManager.UNSPENT | CacheManager.UNCONFIRMED
    assert cm._outputs_cache[txid][1][
        'status'] == CacheManager.UNSPENT | CacheManager.UNCONFIRMED

    out_txid1 = "5fcfa7cf78b53f78cc58a339da3ca4e7fe188cee2e24448e7558215a00cc9a8a"
    assert out_txid1 in cm._outputs_cache
    assert cm._outputs_cache[out_txid1][0][
        'status'] == CacheManager.SPENT | CacheManager.UNCONFIRMED

    out_txid2 = "305839bd673fbe08a0ee47014a6b9dcb3579bd74ffbc343d608f7758f17e8515"
    assert out_txid2 in cm._outputs_cache
    assert len(cm._outputs_cache[out_txid2].keys()) == 1
    assert cm._outputs_cache[out_txid2][2][
        'status'] == CacheManager.SPENT | CacheManager.UNCONFIRMED

    # Check that confirmed balances are 0 for the out_addrs
    out_a = [a[0] for a in out_addrs]
    conf_addr_balances = cm.get_balances(out_a)
    unconf_addr_balances = cm.get_balances(out_a, True)
    for addr, exp_bal in out_addrs:
        assert conf_addr_balances[addr] == 0
        assert unconf_addr_balances[addr] == exp_bal

    # Check utxos
    conf_addr_utxos = cm.get_utxos(out_a)
    unconf_addr_utxos = cm.get_utxos(out_a, True)
    for addr, exp_bal in out_addrs:
        assert addr not in conf_addr_utxos
        assert addr in unconf_addr_utxos
        assert len(unconf_addr_utxos[addr]) == 1
        utxo = unconf_addr_utxos[addr][0]
        assert utxo.value == exp_bal
        assert utxo.num_confirmations == 0

    # Reinsert the transaction with it as confirmed now
    txn = WalletTransaction.from_hex(txn_hex)
    txn.block = 374442
    txn.block_hash = Hash(
        '000000000000000001de250dcfa47f8313aec2f1f41a56f4fb0d099eb497c2b2')
    txn.confirmations = 7684
    cm.insert_txn(txn)

    assert cm._outputs_cache[txid][0]['status'] == CacheManager.UNSPENT
    assert cm._outputs_cache[txid][1]['status'] == CacheManager.UNSPENT
    assert cm._outputs_cache[out_txid1][0]['status'] == CacheManager.SPENT
    assert cm._outputs_cache[out_txid2][2]['status'] == CacheManager.SPENT

    # Check the balances again
    conf_addr_balances = cm.get_balances(out_a)
    unconf_addr_balances = cm.get_balances(out_a, True)
    for addr, exp_bal in out_addrs:
        assert conf_addr_balances[addr] == exp_bal
        assert unconf_addr_balances[addr] == exp_bal

    # Check utxos again
    conf_addr_utxos = cm.get_utxos(out_a)
    unconf_addr_utxos = cm.get_utxos(out_a, True)
    for addr, exp_bal in out_addrs:
        assert addr in conf_addr_utxos
        assert addr in unconf_addr_utxos
        assert len(conf_addr_utxos[addr]) == 1
        assert len(unconf_addr_utxos[addr]) == 1
        utxo = conf_addr_utxos[addr][0]
        assert utxo.value == exp_bal
        assert utxo.num_confirmations == 7684

    # Insert a transaction that spends from one of the out addrs in
    # the above transactions.

    # 1. Insert it provisionally
    # 2. Re-insert as unconfirmed
    # 3. Re-insert as confirmed

    txn_hex = "01000000021ef63ad4dab2c227c7ffcb063916e824bd54c2f463a5ce4b48b6a70a9f3b4fd2000000006a473044022051008f06f1fc5783364712c7bf175c383ebb92c1001ba9f744f5170d5af00bb9022012baa83b3611b2c0e637d2f5e62dd3f6f4debfca805f8a42df6719a67614824d0121027fc10ccde9240463a86c983d2c8d1301311c9debf510119418b0da7b6fdb7ee7ffffffff8ebe0ae5e97c1b3b5742ab7a0253a9e711c27650ce58c25a43bccd817af27937000000006a473044022076fd5835628d4867b489c4c7afa885de33417a3536276b3f7066155b1bd79c15022030a218c2ca35b27e2beefb2298a0bf6fc9eabe93e07f388a1a3aee878025a7b6012102bed99adff9710dbc3e9f7966037d5824ffb134aeba70aec70e34e7eeb6547a94ffffffff0240420f00000000001976a914952e023bf19047e9a014af4ec067667695d8c99488acf8340f00000000001976a914743281d388add04da28e10a12af09c853f98609888ac00000000"  # nopep8

    txn = WalletTransaction.from_hex(txn_hex)

    # First test with a very short expiration
    cm.insert_txn(txn, mark_provisional=True, expiration=1)

    txid = "6fd3c96d466cd465b40e59be14d023c27f1d0ca13075119d3d6baeebfc587b8c"
    assert txid in cm._txn_cache
    assert cm._txn_cache[txid].provisional
    time.sleep(1.5)
    cm.prune_provisional_txns()
    assert txid not in cm._txn_cache

    # Now do default expiration
    cm.insert_txn(txn, mark_provisional=True)

    assert txid in cm._txn_cache

    in_addrs = [
        "15hyvVXH2eJnakwhpqKBf5oTCa3o2bp8m8",
        "15qCydrcqURADXJHrtMW9m6SpPTa3kqkQb"
    ]

    out_addrs = [("1EbnoKrmUEe3hsK9gTVfgYAming6BuqM3L", 1000000),
                 ("1BbPtYsbBPFRCwnU5RuMTttraghXQ5JSZm", 996600)]

    for i, a in enumerate(out_addrs):
        assert a[0] in cm._txns_by_addr
        assert list(cm._deposits_for_addr[a[0]][txid]) == [i]

    for i, a in enumerate(in_addrs):
        assert list(cm._spends_for_addr[a][txid]) == [i]

    # Check input and output caches
    assert txid in cm._inputs_cache
    assert len(cm._inputs_cache[txid]) == 2

    assert txid in cm._outputs_cache
    assert cm._outputs_cache[txid][0]['output'] is not None
    assert cm._outputs_cache[txid][1]['output'] is not None
    assert cm._outputs_cache[txid][0][
        'status'] == CacheManager.UNSPENT | CacheManager.PROVISIONAL | CacheManager.UNCONFIRMED  # nopep8
    assert cm._outputs_cache[txid][1][
        'status'] == CacheManager.UNSPENT | CacheManager.PROVISIONAL | CacheManager.UNCONFIRMED  # nopep8

    out_txid1 = "d24f3b9f0aa7b6484bcea563f4c254bd24e8163906cbffc727c2b2dad43af61e"
    assert out_txid1 in cm._outputs_cache
    assert cm._outputs_cache[out_txid1][0][
        'status'] == CacheManager.SPENT | CacheManager.PROVISIONAL | CacheManager.UNCONFIRMED  # nopep8

    out_txid2 = "3779f27a81cdbc435ac258ce5076c211e7a953027aab42573b1b7ce9e50abe8e"
    assert out_txid2 in cm._outputs_cache
    assert len(cm._outputs_cache[out_txid2].keys()) == 2
    assert cm._outputs_cache[out_txid2][0][
        'status'] == CacheManager.SPENT | CacheManager.PROVISIONAL | CacheManager.UNCONFIRMED  # nopep8

    # Check that confirmed balances are 0 for the out_addrs
    out_a = [a[0] for a in out_addrs]
    conf_addr_balances = cm.get_balances(out_a)
    unconf_addr_balances = cm.get_balances(out_a, True)
    for addr, exp_bal in out_addrs:
        assert conf_addr_balances[addr] == 0
        assert unconf_addr_balances[addr] == exp_bal

    # Check utxos
    conf_addr_utxos = cm.get_utxos(out_a)
    unconf_addr_utxos = cm.get_utxos(out_a, True)
    for addr, exp_bal in out_addrs:
        assert addr not in conf_addr_utxos
        assert addr in unconf_addr_utxos
        assert len(unconf_addr_utxos[addr]) == 1
        utxo = unconf_addr_utxos[addr][0]
        assert utxo.value == exp_bal
        assert utxo.num_confirmations == 0

    # Re-insert as unconfirmed
    txn = WalletTransaction.from_hex(txn_hex)
    cm.insert_txn(txn, mark_provisional=False)

    # Only the statuses should change, so check those.
    assert cm._outputs_cache[txid][0][
        'status'] == CacheManager.UNSPENT | CacheManager.UNCONFIRMED
    assert cm._outputs_cache[txid][1][
        'status'] == CacheManager.UNSPENT | CacheManager.UNCONFIRMED
    assert cm._outputs_cache[out_txid1][0][
        'status'] == CacheManager.SPENT | CacheManager.UNCONFIRMED
    assert cm._outputs_cache[out_txid2][0][
        'status'] == CacheManager.SPENT | CacheManager.UNCONFIRMED

    # Re-insert as confirmed
    txn = WalletTransaction.from_hex(txn_hex)
    txn.block = 374445
    txn.block_hash = Hash(
        "000000000000000004c241778cbbc269e912df5fe8d856efaea916daa82d2575")
    txn.confirmations = 7781
    cm.insert_txn(txn, mark_provisional=False)

    # Only the statuses should change, so check those.
    assert cm._outputs_cache[txid][0]['status'] == CacheManager.UNSPENT
    assert cm._outputs_cache[txid][1]['status'] == CacheManager.UNSPENT
    assert cm._outputs_cache[out_txid1][0]['status'] == CacheManager.SPENT
    assert cm._outputs_cache[out_txid2][0]['status'] == CacheManager.SPENT

    # Check balances
    out_a = [a[0] for a in out_addrs]
    conf_addr_balances = cm.get_balances(out_a)
    unconf_addr_balances = cm.get_balances(out_a, True)
    for addr, exp_bal in out_addrs:
        assert conf_addr_balances[addr] == exp_bal
        assert unconf_addr_balances[addr] == exp_bal

    # Check utxos
    conf_addr_utxos = cm.get_utxos(out_a)
    unconf_addr_utxos = cm.get_utxos(out_a, True)
    for addr, exp_bal in out_addrs:
        assert addr in conf_addr_utxos
        assert addr in unconf_addr_utxos
        assert len(unconf_addr_utxos[addr]) == 1
        utxo = conf_addr_utxos[addr][0]
        assert utxo.value == exp_bal
        assert utxo.num_confirmations == 7781

    # Check utxos for all addresses that have deposits - we should only have 4
    addrs = [
        "1DpCouKa2evX3f2aELUy7iNdsrYuLLaqWy",
        "1GcmBmvYWJKLFHxrTtx5DqQLV7oHQAkH2c",
        "15hyvVXH2eJnakwhpqKBf5oTCa3o2bp8m8",
        "15qCydrcqURADXJHrtMW9m6SpPTa3kqkQb",
        "1EbnoKrmUEe3hsK9gTVfgYAming6BuqM3L",
        "1BbPtYsbBPFRCwnU5RuMTttraghXQ5JSZm",
        "1Ezv6YmYsZvALUaRcZRf8hBdxYni6cm78X",
        "16Mcvb7fYhif94d1RHCn5AE2dm1oXCGnH6",
        "12m3fcaabUgYwWcodgVZUGH6ntFqVrHk5C",
        "18VjAjZ7Au8U75LCHT7aH7mTwKETZwHTpi"
    ]

    conf_utxos = cm.get_utxos(addrs)
    assert len(conf_utxos) == 4
    utxo_addrs_values = [("18VjAjZ7Au8U75LCHT7aH7mTwKETZwHTpi", 23264),
                         ("12m3fcaabUgYwWcodgVZUGH6ntFqVrHk5C", 204154),
                         ("1EbnoKrmUEe3hsK9gTVfgYAming6BuqM3L", 1000000),
                         ("1BbPtYsbBPFRCwnU5RuMTttraghXQ5JSZm", 996600)]

    for a, value in utxo_addrs_values:
        assert a in conf_utxos
        assert len(conf_utxos[a]) == 1
        assert conf_utxos[a][0].value == value

    assert "15hyvVXH2eJnakwhpqKBf5oTCa3o2bp8m8" not in conf_utxos
    assert "15qCydrcqURADXJHrtMW9m6SpPTa3kqkQb" not in conf_utxos

    # Now delete the last transaction
    cm._delete_txn(txid)

    assert txid not in cm._txn_cache
    assert txid not in cm._inputs_cache
    assert txid not in cm._outputs_cache

    for in_addr in in_addrs:
        assert in_addr not in cm._spends_for_addr

    for out_addr, _ in out_addrs:
        assert out_addr not in cm._deposits_for_addr

    for out_txid, index in [(out_txid1, 0), (out_txid2, 0)]:
        out = cm._outputs_cache[out_txid][index]
        assert out['status'] == CacheManager.UNSPENT
        assert out['spend_txid'] is None
        assert out['spend_index'] is None
예제 #16
0
    def txn_from_json(txn_json):
        """ Returns a new Transaction from a JSON-serialized transaction

        Args:
            txn_json: JSON with the following format:

        {
        "hash": "0bf0de38c26195919179f...",
        "block_hash": "000000000000000...",
        "block_height": 303404,
        "block_time": "2014-05-30T23:54:55Z",
        "chain_received_at": "2015-08-13T10:52:21.718Z",
        "confirmations": 69389,
        "lock_time": 0,
        "inputs": [
          {
            "transaction_hash": "0bf0de38c2619...",
            "output_hash": "b84a66c46e24fe71f9...",
            "output_index": 0,
            "value": 300000,
            "addresses": [
              "3L7dKYQGNoZub928CJ8NC2WfrM8U8GGBjr"
            ],
            "script_signature": "03046022100de7b67b9...",
            "script_signature_hex": "00493046022100de7b...",
            "sequence": 4294967295
          }
        ],
        "outputs": [
          {
            "transaction_hash": "0bf0de38c261959...",
            "output_index": 0,
            "value": 290000,
            "addresses": [
              "1K4nPxBMy6sv7jssTvDLJWk1ADHBZEoUVb"
            ],
            "script": "OP_DUP OP_HASH160 c629680b8d...",
            "script_hex": "76a914c629680b8d13...",
            "script_type": "pubkeyhash",
            "required_signatures": 1,
            "spent": false,
            "spending_transaction": null
          }
        ],
        "fees": 10000,
        "amount": 290000
        },
        Transaction.DEFAULT_TRANSACTION_VERSION

        Returns:
            two1.bitcoin.Transaction: a deserialized transaction derived
                from the provided json.

        """
        inputs = []
        outputs = []
        addr_keys = set()

        for i in sorted(txn_json["vin"], key=lambda i: i["n"]):
            if 'coinbase' in i:
                inputs.append(
                    CoinbaseInput(height=0,
                                  raw_script=bytes.fromhex(i['coinbase']),
                                  sequence=i['sequence'],
                                  block_version=1))
            else:
                script = Script.from_hex(i["scriptSig"]["hex"])
                inputs.append(
                    TransactionInput(Hash(i["txid"]), i["vout"], script,
                                     i["sequence"]))
            if "addr" in i:
                addr_keys.add(i["addr"])

        for o in sorted(txn_json["vout"], key=lambda o: o["n"]):
            script = Script.from_hex(o["scriptPubKey"]["hex"])
            value = int(
                decimal.Decimal(str(o["value"])) * decimal.Decimal('1e8'))
            outputs.append(TransactionOutput(value, script))

            if "addresses" in o["scriptPubKey"]:
                for a in o["scriptPubKey"]["addresses"]:
                    addr_keys.add(a)

        txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, inputs,
                          outputs, txn_json["locktime"])

        assert txn.hash == Hash(txn_json['txid'])

        return txn, addr_keys
예제 #17
0
class CoinbaseInput(TransactionInput):
    """ See https://bitcoin.org/en/developer-reference#coinbase

    Args:
        height (uint): The height of the block coinbase is part of
                       will go into.  Not required for version 1
                       blocks.
        raw_script (bytes): the bytes of the coinbase script. For
                            block_version > 1 the height portion
                            should NOT be included in this script.
        sequence (int): Unless you are Satoshi with a version 1 block,
                        the default is fine. If you are Satoshi, send
                        me some of your private keys and set this to
                        0.
        block_version (int): The version of the block this coinbase is
                             a part of or will go into. If raw_script
                             already contains the height of the block,
                             this must be 1.
    """
    NULL_OUTPOINT = Hash(bytes(32))
    MAX_INT = 0xffffffff

    def __init__(self, height, raw_script, sequence=MAX_INT, block_version=3):
        self.height = height
        if block_version == 1:
            scr = raw_script
        else:
            scr = Script.build_push_int(self.height) + raw_script

        # Coinbase scripts are basically whatever, so we don't
        # try to create a script object from them.

        super().__init__(self.NULL_OUTPOINT,
                         self.MAX_INT,
                         scr,
                         sequence)

    def get_addresses(self, testnet=False):
        """ Returns all addresses associated with the script in this input.

        Args:
            testnet (bool): True if the transaction is a testnet transaction.

        Returns:
            list (str): A list of all addresses found in the script.
        """
        return []

    def __str__(self):
        """ Returns a human readable formatting of this input.

        Returns:
            s (str): A string containing the human readable input.
        """
        return (
            "CoinbaseInput(" +
            "Outpoint: %s " % (self.outpoint) +
            "Outpoint Index: 0x%08x " % (self.outpoint_index) +
            "Script: %s " % (bytes_to_str(self.script)) +
            "Sequence: 0x%08x)" % (self.sequence_num))

    def __bytes__(self):
        """ Serializes the object into a byte stream.

        Returns:
            b (bytes): byte stream containing the serialized coinbase input.
        """
        return (
            bytes(self.outpoint) +
            pack_u32(self.outpoint_index) +
            pack_var_str(self.script) +
            pack_u32(self.sequence_num)
        )