예제 #1
0
    def test_p2sh_address_creation(self):
        x1_first_btc_key = bip32_public_derivation(self.wallet.master_public_keys.get("x1/"), "", "/0/0/0")
        x2_first_btc_key = bip32_public_derivation(self.wallet.master_public_keys.get("x2/"), "", "/0/0/0")
        x_pubkeys = [x1_first_btc_key, x2_first_btc_key]
        raw_pubkeys = map( lambda x: deserialize_xkey(x)[4], x_pubkeys )
        pubkeys = map( lambda x: x.encode('hex'), raw_pubkeys )

        # Compare redeem script to manually calculated one
        redeem_script = Transaction.multisig_script(sorted(pubkeys), 2)
        self.assertEqual('522102ee780aa224c9fe54caff984205077b7cca08ced3188a3f3c639d83deda6b9a592103124429ddbed55593d0abea0d0d3d283eca4546e40017b2945f4666c561b494ba52ae',
            redeem_script)

        p2sh_addr = hash_160_to_bc_address( hash_160(redeem_script.decode('hex')), self.wallet.active_chain.p2sh_version )
        self.assertEqual('3MqemPAHZDGLr537QBvU7i4dRFY3Xvad7X', p2sh_addr)

        # switch chains
        self._switch_chain("MZC")
        x1_first_mzc_key = bip32_public_derivation(self.wallet.master_public_keys.get("x1/"), "", "/13/0/0")
        x2_first_mzc_key = bip32_public_derivation(self.wallet.master_public_keys.get("x2/"), "", "/13/0/0")
        x_pubkeys = [x1_first_mzc_key, x2_first_mzc_key]
        raw_pubkeys = map( lambda x: deserialize_xkey(x)[4], x_pubkeys )
        pubkeys = map( lambda x: x.encode('hex'), raw_pubkeys )

        # Compare redeem script to manually calculated one
        redeem_script = Transaction.multisig_script(sorted(pubkeys), 2)
        self.assertEqual('5221027bdb7f5c42096580442e63235434bcc9ddf9689bbeb917705cd0edf9c6e264292102919725862f59a43274443ea11d7a8e25c15147213dcb6186c24d8629d37d6d8d52ae',
            redeem_script)

        p2sh_addr = hash_160_to_bc_address( hash_160(redeem_script.decode('hex')), self.wallet.active_chain.p2sh_version )
        self.assertEqual('4jjXnsGuWLH3YgnagWH12kK7HjDtsBv8SQ', p2sh_addr)
예제 #2
0
 def createTransaction(self, password, destList):
     """Create a new transaction and send it to the RelayNode
        destList is a list of tuples
        Each tuples is like : (str_address, value)
        The last transaction is the rest of the wallet send to the new user address
     """
     self.checkUpdate()
     newAddr = Address()
     newAddr.encryptPrivateKey(password)
     total = sum([i[1] for i in destList])
     if total <= self.count:
         destList.append((str(newAddr), (self.count - total)))
         transac = Transaction(self.addr.public(), destList)
         self.addr.decryptPrivateKey(password)
         transac.sign(self.addr)
         debug('valid: ' + ('True' if transac.is_signed() else 'False'))
         self.addr.encryptPrivateKey(password)
         if not self.relay.submit_transaction(transac):
             return False
         self.addrList.append(newAddr)
         self.addr = newAddr
         add_address(self.user_ID, self.addr, len(self.addrList) - 1)
         return True
     else:
         return False
예제 #3
0
    def test_deserialize(self):
        tx = Transaction(self.coinstake_tx)
        tx.deserialize()

        self.assertEquals(tx.output_value(), 1437000000)
        self.assertEquals(tx.version, 2)
        self.assertEquals(tx.time, 1430932848)
        self.assertEquals(tx.clamspeech, 'https://Just-Dice.com')
예제 #4
0
    def handle_request(self, request):
        try:
            request_type = request.get('type')

            if request_type not in self.no_auth_check_handlers:
                username = request.get('auth', {}).get('username')
                token = request.get('auth', {}).get('auth_token')
                if not username or not token:
                    return construct_result(400, 'Bad request')

                with Transaction() as tr:
                    try:
                        user = User.read_by_name(tr.cursor, username)
                    except ItemNotFoundError:
                        return construct_result(404, 'User not found')

                if not self.auth_handler.verify_auth_token(user.user_id, token):
                    return construct_result(403, 'Bad auth token, you should sign in')

                self.context['user_id'] = user.user_id

            return self.type_to_handler.get(request_type, lambda _: construct_result(400, 'Bad request'))(request)
        except KeyboardInterrupt:
            raise
        except Exception as e:
            logging.exception('Unhandled exception during request handling occured: {}'.format(e))
            return construct_result(500, 'Error occured')
예제 #5
0
def transfer(bc, relay, from_addr, to_addr, amount):
    f = get_address(from_addr)
    if f is None:
        exit('no address '+str(from_addr)+' in database')
    
    total = bc.get_amount_of_address(f)
    if total < amount:
        exit('not enough money for this transfer')
    
    pwd = getpass.getpass()
    while not f.decryptPrivateKey(pwd):
        print('Invalid password')
        pwd = getpass.getpass()
    
    receivers = [(to_addr, amount)]
    if total > amount:
        new = Address()
        new.encryptPrivateKey(pwd)
        db.add_address('client', new, 0)
        print('created new address '+str(new)+' to receive remaining funds')
        receivers.append((str(new), total - amount))
    t = Transaction(f, receivers)
    if relay.submit_transaction(t):
        print('transaction sent to the network')
    else:
        print('error sending transaction')
예제 #6
0
    def __init__(self, seed, *args):
        self.seed = seed
        self.wallet = Wallet(utils.hash(str(self.seed)))
        self.neighbours = set()
        self.DAG = {}

        root_transaction = Transaction('0000', 'root', 25000000000)
        self.attach_transaction(root_transaction, ['root'])
예제 #7
0
파일: spender.py 프로젝트: gustavonalle/bfs
    def create_tx(self):
        tx = Transaction(version=self.tx_version,
                         sig_hash_type=self.sig_hash_type,
                         sig_hash_type_pre_image=self.sig_hash_type_pre_image,
                         sign_style=self.sign_type)
        keys = []
        for utxo in self.utxos:
            pub_key = utxo.private_key.create_pub_key()
            keys.append(KeyPair(utxo.private_key, pub_key))
            tx_input = TransactionInput(utxo.tx_hash,
                                        utxo.index,
                                        utxo.address,
                                        get_spend_type(utxo.address),
                                        prev_amount=utxo.amount_btc,
                                        pub_key=pub_key)
            tx.add_inputs(tx_input)

        for destination in self.destinations:
            tx_output = TransactionOutput(
                btc_to_satoshis(destination.amount_btc), destination.address,
                get_spend_type(destination.address))
            tx.add_outputs(tx_output)

        signed = tx.sign(*keys)
        return signed.serialize()
예제 #8
0
    def make_transaction(self, receiving_addr, value):
        '''
            1. Pick at least two transactions in Tangle to confirm
            2. Check picked transactions for conflict
            3. Approve non conflicting transactions
            4. Perform proof of work
            5. Send transaction to tangle 
        '''
        confirmed_transactions = self.confirm_transactions()

        if len(confirmed_transactions) >= 2:
            proof_of_work = self.proof_of_work(''.join(confirmed_transactions))
            new_transaction = Transaction(self.wallet.generate_address(),
                                          receiving_addr, value)
            new_transaction.pow = proof_of_work
            return self.attach_transaction(new_transaction,
                                           confirmed_transactions)

        return {'msg': 'transaction failed'}
예제 #9
0
def construct_follows_list_response(follows, followers_mode=False):
    res = []
    for follow in follows:
        with Transaction() as tr:
            try:
                user_id = follow.user_id if not followers_mode else follow.follower_id
                res.append(User.read_by_pk(tr.cursor, user_id).username)
            except ItemNotFoundError:
                continue
    return res
예제 #10
0
    def handle_post(self, request):
        text = request.get('text')
        if not text:
            return construct_result(400, 'Bad request')

        ts = int(time.time())

        with Transaction() as tr:
            Post(self.context['user_id'], None, text, ts).create_me(tr.cursor)

        return construct_result()
예제 #11
0
 def post_transaction(self, json):
     t = Transaction.fromJson(json)
     if t is None:
         debug('JSON error')
         self.no_response(400)  # Bad Request
     elif not t.is_valid(self.server.blockchain):
         debug('invalid blockchain transaction')
         self.no_response(400)  # Bad Request
     else:
         self.server.transactions.append(t)
         self.no_response(200)  # OK
         self.broadcast_transaction(t)
예제 #12
0
    def test_pending_transactions(self):
        receiver = generate_key()

        tx = Transaction(None, txtype=TxType.COINBASE)
        tx.add_out(10, receiver.publickey())

        blockchain = Blockchain()
        blockchain.add(tx)

        pending_tx = blockchain.get_pending_transactions()

        self.assertEqual(len(filter(lambda x: tx.hash, pending_tx)), 1)

        miner = Miner(blockchain)
        miner.reward_addr = receiver.publickey()
        newb = miner._mine()

        pending_tx = blockchain.get_pending_transactions()

        self.assertEqual(len(filter(lambda x: tx.hash, pending_tx)), 0)
        self.assertEqual(len(filter(lambda x: tx.hash, newb.transactions)), 2)
예제 #13
0
파일: tangle.py 프로젝트: aliteke/Tango
    def __init__(self, isgenesis=True, *args):
        # represent the "sites" containing transactions
        # self.tngl = nx.DiGraph()

        if isgenesis:
            self.DAG = OrderedDict()
            genesis_tx = Transaction('genesis_address', 'genesis_address',
                                     21000000)
            genesis_tx_key = self.attach_transaction(genesis_tx,
                                                     ['genesis_tx'])

            founding_txA = Transaction('genesis_address', 'founderA_address',
                                       7000000)
            self.attach_transaction(founding_txA, [genesis_tx_key])

            founding_txB = Transaction('genesis_address', 'founderB_address',
                                       7000000)
            self.attach_transaction(founding_txB, [genesis_tx_key])

        else:
            print "[+] Genesis tangle is already done, connect and sync from neighbours"
예제 #14
0
def construct_posts_list_response(posts):
    res = []
    for post in posts:
        post_dict = post.to_dict()
        with Transaction() as tr:
            try:
                post_dict['username'] = User.read_by_pk(
                    tr.cursor, post_dict.pop('user_id')).username
            except ItemNotFoundError:
                continue
        res.append(post_dict)
    return res
예제 #15
0
def transaction_submit():
    """Submit a new transaction to the pool."""
    try:
        if not request.json:
            abort(400, "Invalid transaction")

        tx = Transaction.deserialise_dict(request.json)
        tx.validate(CHAIN.q)
        CHAIN.q.add_transaction_to_pending(tx)
    except TransactionValidationError as exc:
        abort(400, "Invalid transaction: {}".format(exc))
    except BlockChainError:
        abort(500, "Blockchain error")
예제 #16
0
    def handle_get_user_posts(self, request):
        username = request.get('username')
        if not username:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            try:
                user = User.read_by_name(tr.cursor, username)
            except ItemNotFoundError:
                return construct_result(404, 'User not found')

            posts = Post.get_user_posts(tr.cursor, user.user_id)

        return construct_result(200, construct_posts_list_response(posts))
예제 #17
0
    def handle_get_following_users(self, request):
        username = request.get('username')
        if not username:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            try:
                user = User.read_by_name(tr.cursor, username)
            except ItemNotFoundError:
                return construct_result(404, 'User not found')

            follows = Follow.get_following_users(tr.cursor, user.user_id)

        return construct_result(200, construct_follows_list_response(follows, followers_mode=True))
예제 #18
0
    def handle_follow(self, request):
        username_to_follow = request.get('username_to_follow')
        if not username_to_follow:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            try:
                user_to_follow = User.read_by_name(tr.cursor, username_to_follow)
            except ItemNotFoundError:
                return construct_result(404, 'User not found')

            Follow(None, user_to_follow.user_id, self.context['user_id']).create_me(tr.cursor)

        return construct_result()
예제 #19
0
 def fromJson(json_data):
     try:
         data = json.loads(json_data)
         b = Block(data["previous_hash"], data["miner_address"])
         ts = []
         for t in data["transactions"]:
             ts.append(Transaction.fromJson(t))
         b.set_transactions(ts)
         b.set_proof(data["proof"])
         return b
     # JSON ValueError for decode
     # KeyError if no accessed key
     except (ValueError, KeyError, TypeError):
         return None
예제 #20
0
def maketx(inputs, outputs):
    """
    Make a transaction from the given inputs and outputs list. Saves the serialized tx to `alltxes` and returns the key (its txid).
    """
    tx = Transaction.from_io(inputs, outputs)
    txid = tx.txid()
    raw = str(tx)  # ensure we work with serialized version.
    #txid = Transaction(raw).txid() # re-deserialize to make sure we get the right txid

    alltxes[txid] = raw

    print("Generated %.10s... (%d -> %d)" % (txid, len(inputs), len(outputs)))

    return txid
예제 #21
0
    def handle_like(self, request):
        post_id = request.get('post_id')
        if not post_id:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            try:
                post = Post.read_by_pk(tr.cursor, post_id)
            except ItemNotFoundError:
                return construct_result(404, 'Post not found')

            post.likes += 1
            post.update_me(tr.cursor)

        return construct_result()
예제 #22
0
    def handle_signin(self, request):
        username = request.get('username')
        password = request.get('password')
        if not username or not password:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            try:
                user = User.read_by_name(tr.cursor, username)
            except ItemNotFoundError:
                return construct_result(404, 'User not found')

            if not self.auth_handler.verify_password(password, user.password_key):
                return construct_result(400, 'Bad password')

        return construct_result(200, {'auth_token': self.auth_handler.get_auth_token(user.user_id)})
예제 #23
0
 def get_transactions(self):
     """send GET request to relay/transactions/ and parse it
     """
     r = get_request(self.url+'transactions/')
     if r is None:
         return None
     if not r.status_code == 200:
         return None
     try:
         json_ts = r.json()
     except ValueError:
         return None
     ts = []
     for json in json_ts:
         t = Transaction.fromJson(json)
         if not t is None:
             ts.append(t)
     return ts
예제 #24
0
    def handle_signup(self, request):
        username = request.get('username')
        password = request.get('password')
        if not username or not password:
            return construct_result(400, 'Bad request')

        with Transaction() as tr:
            no_users = False
            try:
                user = User.read_by_name(tr.cursor, username)
            except ItemNotFoundError:
                no_users = True

            if not no_users:
                return construct_result(400, 'User already exists')

            User(None, username, self.auth_handler.get_password_key(password)).create_me(tr.cursor)

        return construct_result()
예제 #25
0
    def handle_admin_info(self, request):
        with Transaction() as tr:
            try:
                user = User.read_by_pk(tr.cursor, self.context['user_id'])
            except ItemNotFoundError:
                return construct_result(404, 'User not found')

            if not user.is_admin:
                return construct_result(403, 'Access denied')

            users = User.read_all(tr.cursor)
            posts = Post.read_all(tr.cursor)
            follows = Follow.read_all(tr.cursor)

        return construct_result(
            200,
            {
                'users': [user.to_dict() for user in users],
                'posts': [post.to_dict() for post in posts],
                'folows': [follow.to_dict() for follow in follows],
            }
        )
예제 #26
0
    def create_new_block(self):
        """
        Create a fresh new block that contains all valid
        pending transactions.
        """
        newb = Block(self.blockchain.top().hash)
        newb.index = self.blockchain.top().index + 1

        newb.transactions.extend(self.blockchain.get_pending_transactions())

        tx = Transaction(None, txtype=TxType.COINBASE)
        tx.add_out(MINING_REWARD, self.reward_addr)
        tx.finalize()

        newb.transactions.append(tx)

        return newb
예제 #27
0
    def _test_fraudulent_tx(self, victim, blockchain):
        """
        Try transaction from somebody else's "wallet"
        """

        miner_key = generate_key()
        perpetrator = generate_key()

        utxo = blockchain.scan_unspent_transactions(victim.publickey())

        perpetrator_owns = blockchain.scan_unspent_transactions(
            perpetrator.publickey())
        self.assertEqual(len(perpetrator_owns), 0)  # Sanity check

        # Let's handcraft tx that tries to steal victim's coins.
        tx = Transaction(perpetrator)  # Sign it with random key

        utxo = blockchain.scan_unspent_transactions(
            victim.publickey())  # Get victim's utxo

        debit = sum(map(lambda x: x['value'], utxo))

        for credit in utxo:
            tx.add_in(credit['hash'], sign(perpetrator, credit['hash']),
                      victim.publickey(), credit['value'])

        tx.add_out(debit, perpetrator.publickey())

        try:
            blockchain.add(tx)
            self.fail("Should've thrown UnauthorizedTxException")
        except UnauthorizedTxException:
            pass

        miner = Miner(blockchain)
        miner.reward_addr = miner_key.publickey()
        _ = miner._mine()

        perpetrator_owns = blockchain.scan_unspent_transactions(
            perpetrator.publickey())

        # Should own nothing. This tx should've failed.
        self.assertEqual(len(perpetrator_owns), 0)
    def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_write):
        wallet1a = WalletIntegrityHelper.create_multisig_wallet(
            keystore.from_seed(
                'bitter grass shiver impose acquire brush forget axis eager alone wine silver',
                '', True),
            keystore.from_xpub(
                'Vpub5fcdcgEwTJmbmqAktuK8Kyq92fMf7sWkcP6oqAii2tG47dNbfkGEGUbfS9NuZaRywLkHE6EmUksrqo32ZL3ouLN1HTar6oRiHpDzKMAF1tf'
            ),
            keystore.from_xpub(
                'Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra'
            ),
            gap_limit=2)
        wallet1b = WalletIntegrityHelper.create_multisig_wallet(
            keystore.from_seed(
                'snow nest raise royal more walk demise rotate smooth spirit canyon gun',
                '', True),
            keystore.from_xpub(
                'Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra'
            ),
            keystore.from_xpub(
                'Vpub5gSKXzxK7FeKQedu2q1z9oJWxqvX72AArW3HSWpEhc8othDH8xMDu28gr7gf17sp492BuJod8Tn7anjvJrKpETwqnQqX7CS8fcYyUtedEMk'
            ),
            gap_limit=2)
        # ^ third seed: hedgehog sunset update estate number jungle amount piano friend donate upper wool
        wallet2a = WalletIntegrityHelper.create_multisig_wallet(
            # bip39: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose, der: m/1234'/1'/0', p2wsh-p2sh multisig
            keystore.from_xprv(
                'Uprv9CvELvByqm8k2dpecJVjgLMX1z5DufEjY4fBC5YvdGF5WjGCa7GVJJ2fYni1tyuF7Hw83E6W2ZBjAhaFLZv2ri3rEsubkCd5avg4EHKoDBN'
            ),
            keystore.from_xpub(
                'Upub5Qb8ik4Cnu8g97KLXKgVXHqY6tH8emQvqtBncjSKsyfTZuorPtTZgX7ovKKZHuuVGBVd1MTTBkWez1XXt2weN1sWBz6SfgRPQYEkNgz81QF'
            ),
            gap_limit=2)
        wallet2b = WalletIntegrityHelper.create_multisig_wallet(
            # bip39: square page wood spy oil story rebel give milk screen slide shuffle, der: m/1234'/1'/0', p2wsh-p2sh multisig
            keystore.from_xprv(
                'Uprv9BbnKEXJxXaNvdEsRJ9VA9toYrSeFJh5UfGBpM2iKe8Uh7UhrM9K8ioL53s8gvCoGfirHHaqpABDAE7VUNw8LNU1DMJKVoWyeNKu9XcDC19'
            ),
            keystore.from_xpub(
                'Upub5RuakRisg8h3F7u7iL2k3UJFa1uiK7xauHamzTxYBbn4PXbM7eajr6M9Q2VCr6cVGhfhqWQqxnABvtSATuVM1xzxk4nA189jJwzaMn1QX7V'
            ),
            gap_limit=2)

        # bootstrap wallet1
        funding_tx = Transaction(
            '01000000000101a41aae475d026c9255200082c7fad26dc47771275b0afba238dccda98a597bd20000000000fdffffff02400d0300000000002200203c43ac80d6e3015cf378bf6bac0c22456723d6050bef324ec641e7762440c63c9dcd410000000000160014824626055515f3ed1d2cfc9152d2e70685c71e8f02483045022100b9f39fad57d07ce1e18251424034f21f10f20e59931041b5167ae343ce973cf602200fefb727fa0ffd25b353f1bcdae2395898fe407b692c62f5885afbf52fa06f5701210301a28f68511ace43114b674371257bb599fd2c686c4b19544870b1799c954b40e9c11300'
        )
        funding_txid = funding_tx.txid()
        funding_output_value = 200000
        self.assertEqual(
            'd2bd6c9d332db8e2c50aa521cd50f963fba214645aab2f7556e061a412103e21',
            funding_txid)
        wallet1a.receive_tx_callback(funding_txid, funding_tx,
                                     TX_HEIGHT_UNCONFIRMED)

        # wallet1 -> wallet2
        outputs = [(bitcoin.TYPE_ADDRESS, wallet2a.get_receiving_address(),
                    165000)]
        tx = wallet1a.mktx(outputs=outputs,
                           password=None,
                           config=self.config,
                           fee=5000)
        tx = Transaction(
            tx.serialize())  # simulates moving partial txn between cosigners
        self.assertFalse(tx.is_complete())
        wallet1b.sign_transaction(tx, password=None)

        self.assertTrue(tx.is_complete())
        self.assertTrue(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet1a.is_mine(tx.inputs()[0]['address']),
                         wallet1a.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet1a.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet1a.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            '6e9c3cd8788bdb970a124ea06136d52bc01cec4f9b1e217627d5e90ebe77d049',
            tx_copy.txid())

        wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
        wallet2a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet2 -> wallet1
        outputs = [(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(),
                    100000)]
        tx = wallet2a.mktx(outputs=outputs,
                           password=None,
                           config=self.config,
                           fee=5000)
        tx = Transaction(
            tx.serialize())  # simulates moving partial txn between cosigners
        self.assertFalse(tx.is_complete())
        wallet2b.sign_transaction(tx, password=None)

        self.assertTrue(tx.is_complete())
        self.assertTrue(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet2a.is_mine(tx.inputs()[0]['address']),
                         wallet2a.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet2a.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet2a.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            '84b0dcb43022385f7a10e2710e5625a2be3cd6e390387b6100b55500d5eea8f6',
            tx_copy.txid())

        wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
        wallet2a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet level checks
        self.assertEqual((0, funding_output_value - 165000 - 5000 + 100000, 0),
                         wallet1a.get_balance())
        self.assertEqual((0, 165000 - 5000 - 100000, 0),
                         wallet2a.get_balance())
    def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(
            self, mock_write):
        wallet1a = WalletIntegrityHelper.create_multisig_wallet(
            keystore.from_seed(
                'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure',
                '', True),
            keystore.from_xpub(
                'tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'
            ),
            keystore.from_xpub(
                'tpubD6NzVbkrYhZ4XJzYkhsCbDCcZRmDAKSD7bXi9mdCni7acVt45fxbTVZyU6jRGh29ULKTjoapkfFsSJvQHitcVKbQgzgkkYsAmaovcro7Mhf'
            ),
            gap_limit=2)
        wallet1b = WalletIntegrityHelper.create_multisig_wallet(
            keystore.from_seed(
                'cycle rocket west magnet parrot shuffle foot correct salt library feed song',
                '', True),
            keystore.from_xpub(
                'tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'
            ),
            keystore.from_xpub(
                'tpubD6NzVbkrYhZ4YARFMEZPckrqJkw59GZD1PXtQnw14ukvWDofR7Z1HMeSCxfYEZVvg4VdZ8zGok5VxHwdrLqew5cMdQntWc5mT7mh1CSgrnX'
            ),
            gap_limit=2)
        # ^ third seed: ghost into match ivory badge robot record tackle radar elbow traffic loud
        wallet2 = self.create_standard_wallet_from_seed(
            'powerful random nobody notice nothing important anyway look away hidden message over'
        )

        # bootstrap wallet1
        funding_tx = Transaction(
            '010000000001014121f99dc02f0364d2dab3d08905ff4c36fc76c55437fd90b769c35cc18618280100000000fdffffff02d4c22d00000000001600143fd1bc5d32245850c8cb5be5b09c73ccbb9a0f75001bb7000000000017a91480c2353f6a7bc3c71e99e062655b19adb3dd2e4887024830450221008781c78df0c9d4b5ea057333195d5d76bc29494d773f14fa80e27d2f288b2c360220762531614799b6f0fb8d539b18cb5232ab4253dd4385435157b28a44ff63810d0121033de77d21926e09efd04047ae2d39dbd3fb9db446e8b7ed53e0f70f9c9478f735dac11300'
        )
        funding_txid = funding_tx.txid()
        funding_output_value = 12000000
        self.assertEqual(
            'b25cd55687c9e528c2cfd546054f35fb6741f7cf32d600f07dfecdf2e1d42071',
            funding_txid)
        wallet1a.receive_tx_callback(funding_txid, funding_tx,
                                     TX_HEIGHT_UNCONFIRMED)

        # wallet1 -> wallet2
        outputs = [(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(),
                    370000)]
        tx = wallet1a.mktx(outputs=outputs,
                           password=None,
                           config=self.config,
                           fee=5000)
        tx = Transaction(
            tx.serialize())  # simulates moving partial txn between cosigners
        self.assertFalse(tx.is_complete())
        wallet1b.sign_transaction(tx, password=None)

        self.assertTrue(tx.is_complete())
        self.assertFalse(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet1a.is_mine(tx.inputs()[0]['address']),
                         wallet1a.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet1a.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet1a.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            '26f3bdd0402e1cff19126244ebe3d32722cef0db507c7229ca8754f5e06ef25d',
            tx_copy.txid())

        wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
        wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet2 -> wallet1
        outputs = [(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(),
                    100000)]
        tx = wallet2.mktx(outputs=outputs,
                          password=None,
                          config=self.config,
                          fee=5000)

        self.assertTrue(tx.is_complete())
        self.assertFalse(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet2.is_mine(tx.inputs()[0]['address']),
                         wallet2.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet2.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet2.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            'c573b3f8464a4ed40dfc79d0889a780f44e917beef7a75883b2427c2987f3e95',
            tx_copy.txid())

        wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
        wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet level checks
        self.assertEqual((0, funding_output_value - 370000 - 5000 + 100000, 0),
                         wallet1a.get_balance())
        self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance())
    def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_write):
        wallet1 = self.create_standard_wallet_from_seed(
            'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
        )
        wallet2 = self.create_standard_wallet_from_seed(
            'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
        )

        # bootstrap wallet1
        funding_tx = Transaction(
            '01000000014576dacce264c24d81887642b726f5d64aa7825b21b350c7b75a57f337da6845010000006b483045022100a3f8b6155c71a98ad9986edd6161b20d24fad99b6463c23b463856c0ee54826d02200f606017fd987696ebbe5200daedde922eee264325a184d5bbda965ba5160821012102e5c473c051dae31043c335266d0ef89c1daab2f34d885cc7706b267f3269c609ffffffff0240420f00000000001600148a28bddb7f61864bdcf58b2ad13d5aeb3abc3c42a2ddb90e000000001976a914c384950342cb6f8df55175b48586838b03130fad88ac00000000'
        )
        funding_txid = funding_tx.txid()
        funding_output_value = 1000000
        self.assertEqual(
            'add2535aedcbb5ba79cc2260868bb9e57f328738ca192937f2c92e0e94c19203',
            funding_txid)
        wallet1.receive_tx_callback(funding_txid, funding_tx,
                                    TX_HEIGHT_UNCONFIRMED)

        # wallet1 -> wallet2
        outputs = [(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(),
                    250000)]
        tx = wallet1.mktx(outputs=outputs,
                          password=None,
                          config=self.config,
                          fee=5000)

        self.assertTrue(tx.is_complete())
        self.assertTrue(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet1.is_mine(tx.inputs()[0]['address']),
                         wallet1.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet1.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet1.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            '3c06ae4d9be8226a472b3e7f7c127c7e3016f525d658d26106b80b4c7e3228e2',
            tx_copy.txid())

        wallet1.receive_tx_callback(
            tx.txid(), tx,
            TX_HEIGHT_UNCONFIRMED)  # TX_HEIGHT_UNCONF_PARENT but nvm
        wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet2 -> wallet1
        outputs = [(bitcoin.TYPE_ADDRESS, wallet1.get_receiving_address(),
                    100000)]
        tx = wallet2.mktx(outputs=outputs,
                          password=None,
                          config=self.config,
                          fee=5000)

        self.assertTrue(tx.is_complete())
        self.assertFalse(tx.is_segwit())
        self.assertEqual(1, len(tx.inputs()))
        tx_copy = Transaction(tx.serialize())
        self.assertEqual(wallet2.is_mine(tx.inputs()[0]['address']),
                         wallet2.is_mine(tx_copy.inputs()[0]['address']))
        self.assertTrue(wallet2.is_mine(tx.inputs()[0]['address']))
        self.assertEqual(wallet2.txin_type, tx_copy.inputs()[0]['type'])
        self.assertEqual(tx.wtxid(), tx_copy.wtxid())
        self.assertEqual(
            '5f25707571eb776bdf14142f9966bf2a681906e0a79501edbb99a972c2ceb972',
            tx_copy.txid())

        wallet1.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
        wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)

        # wallet level checks
        self.assertEqual((0, funding_output_value - 250000 - 5000 + 100000, 0),
                         wallet1.get_balance())
        self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance())
예제 #31
0
    def test_transaction(self):

        wallet_keys = {
            'xpub6GDQf5vZmrpQvD4ixNdqHmgSZ76Uo2Cg5isBupnvZpnNbhdRhgdhq9hkfCSKRE31rGfYuXNfZ5gTamFkj1GXt6k87MD1hUn28tuvLHY71Bk':
            'L2FQCaHPwgS4CmAf6bKtbjVWDbcHv42c72by1zLEyLuDrUG22CwM',
            'xpub6GLs33TeHkrLSTJ2uxiMLnuqxCHG9iBFCwjTwyg4EvzyxUi78U1sXxxRPUQfLGNEZRT3yYKEwR39ZbUtofEgcmTLtJdSetnFiQPEwTZRW5y':
            'L1aD1WSA3UGU56sAmjfYVj1rK3fnSzWKj2wTsDTN1DpgUgPVwQCa',
            'xpub6Fc64k9RTc79yD7xErF2yKSdUraGBhGWDt1FomUFVCFg52165LZvvoGL59hebJBGtauFqNL5zMeRgPGV29sfQp6XqoiiD9E53UDatBhFZuk':
            'L25eCRYrDNVNrTZV1XZhmaZti3dVsZz3egm1R7LsPHRdpYuyLYKE'
            #            'xpub6GiSmJrVQC5q2m5cRcZvUgnGUsfeEqi3eEfUTgacnA33G8PAQnJeeMzpNAGUA9JL3goUui7BY52rVPwhmxMALkmFh5ZuwJDKrPv8ERkG3CK': 'L3jUPoR7fUwB9mwfuqqF79mHDpj5rpygQhdWntJ9ShZ9nbyRab5h'
        }

        acc = self.wallet.accounts["0"]
        redeem_script = '53210278a1a7de63493a8c8e0e7f4ebb13fd2a8144db25bb3bc2e5f44127a851a389332102ee780aa224c9fe54caff984205077b7cca08ced3188a3f3c639d83deda6b9a592103124429ddbed55593d0abea0d0d3d283eca4546e40017b2945f4666c561b494ba210312872f0aa80fa1a9bc7df77fa5be310f5441f7bfec798fe19209b04954dec8da54ae'

        coins = [{
            'address': '32SbQbBthcMzRSpCw1ifAXqWviJGH5HKUk',
            'value': 600000,
            'prevout_n': 0,
            'prevout_hash':
            '1111111111111111111111111111111111111111111111111111111111111111',
            'height': 100000,
            'coinbase': 'True'
        }]

        txin = coins[0]

        x_pubkeys = map(lambda x: bip32_public_derivation(x, "", "/0/0/0"),
                        self.wallet.master_public_keys.values())
        pubkeys = map(lambda x: deserialize_xkey(x)[4].encode('hex'),
                      x_pubkeys)

        s = ''.join(map(lambda x: int_to_hex(x, 2), (0, 0)))
        x_pubkeys = map(
            lambda x: 'ff' + DecodeBase58Check(x).encode('hex') + s, x_pubkeys)
        pubkeys, x_pubkeys = zip(*sorted(zip(pubkeys, x_pubkeys)))
        txin['pubkeys'] = list(pubkeys)
        txin['x_pubkeys'] = list(x_pubkeys)
        txin['signatures'] = [None] * len(pubkeys)
        txin['redeemScript'] = redeem_script
        txin['num_sig'] = acc.multisig_m

        outputs = [('address', '1PyXgL1qmZPuxcVi9CcguQb3v7WUvQZBud', 500000)]
        inputs = []
        tx = Transaction(inputs, outputs)
        tx.add_input(txin)
        self.wallet.sign_transaction(tx, "secret")

        #
        ins = tx.inputs_to_sign()
        keypairs = {}
        sec = None
        for innard in ins:
            # this is easier than the commented loop below
            in_xpub, _ = BIP32_Account.parse_xpubkey(innard)
            if wallet_keys.get(in_xpub):
                keypairs[innard] = wallet_keys[in_xpub]
            # ...


#            in_xpub, in_seq = BIP32_Account.parse_xpubkey(innard)
#            sec = None
#            for k, vaccount in self.wallet.accounts.items():
#                acc_v = vaccount.get_master_pubkeys()[0]
#                acc_xpub = bip32_public_derivation(acc_v, "", "/0/0")
#                if in_xpub == acc_xpub:
#                    pk = vaccount.get_private_key(in_seq, self.wallet, "secret")
#                    sec = pk[0]

#            if sec:
#                keypairs [ innard ] = sec

        if keypairs:
            tx.sign(keypairs)
        self.assertEqual(
            '0100000001111111111111111111111111111111111111111111111111111111111111111100000000fd6701004730440220774e80fda89895d8bf3ac39c38f39456d31c1e857dc1c77c000f4de6c3de15fe02207b6d13b5ba17eadeb607f3ca56f693a0b777dae668584cefec0910a8bc90869a0147304402205e80562254972f873b5b59b1cdc81e422c7a2959d8868e5a54238fbfdf6f107002200204eef593812453ae2c22334c409f9ef25523cf9619399eb2d3c249673443dc01483045022100a81e69796aa5e5ae0d8924047e3c81a8dd64dfbc791caba6728ac7820aa114da022060b85875fd58223b7c61ef45fac2567a9f76934f947e4d03d927f5b078e1fb45014c8b53210278a1a7de63493a8c8e0e7f4ebb13fd2a8144db25bb3bc2e5f44127a851a389332102ee780aa224c9fe54caff984205077b7cca08ced3188a3f3c639d83deda6b9a592103124429ddbed55593d0abea0d0d3d283eca4546e40017b2945f4666c561b494ba210312872f0aa80fa1a9bc7df77fa5be310f5441f7bfec798fe19209b04954dec8da54aeffffffff0120a10700000000001976a914fc03ab7c28d17349f084f7cadde4dafc356918d388ac00000000',
            str(tx))

        ###########
        #
        serialized_tx = str(tx)
        tx2 = Transaction.deserialize(serialized_tx,
                                      active_chain=self.wallet.active_chain)
        self.assertEquals(4, len(tx2.inputs[0]['x_pubkeys']))
        self.assertEquals(3, tx2.inputs[0]['num_sig'])
예제 #32
0
    def test_coinstake_marker(self):
        raw_tx = '02000000704d4a55010000000000000000000000000000000000000000000000000000000000000000ffffffff04033df406ffffffff010000000000000000000000000000'
        tx = Transaction(raw_tx)
        tx.deserialize()

        self.assertEquals(tx.inputs[0]['is_coinbase'], True)
예제 #33
0
    def test_coinstake(self):
        tx = Transaction(self.coinstake_tx)
        tx.deserialize()

        self.assertEquals(tx.is_coinstake, True)
예제 #34
0
 def test_peercoin_serialize(self):
     chainparams.set_active_chain('PPC')
     rawtx = '0100000058e4615501a367e883a383167e64c84e9c068ba5c091672e434784982f877eede589cb7e53000000006a473044022043b9aee9187effd7e6c7bc444b09162570f17e36b4a9c02cf722126cc0efa3d502200b3ba14c809fa9a6f7f835cbdbbb70f2f43f6b30beaf91eec6b8b5981c80cea50121025edf500f18f9f2b3f175f823fa996fbb2ec52982a9aeb1dc2e388a651054fb0fffffffff0257be0100000000001976a91495efca2c6a6f0e0f0ce9530219b48607a962e77788ac45702000000000001976a914f28abfb465126d6772dcb4403b9e1ad2ea28a03488ac00000000'
     tx = Transaction.deserialize(rawtx, chainparams.get_active_chain())
     self.assertEqual(tx.serialize()[8:16], rawtx[8:16])
예제 #35
0
 def test_serialize(self):
     chainparams.set_active_chain('BTC')
     rawtx = '010000000198fb6f1c46eb0416b6a91bc9d0b8dfa554a96b23f00070f430bbf6d05b26ea16010000006c493046022100d00ff7bb2e9d41ef200ceb2fd874468b09a21e549ead6f2a74f35ad02ce25df8022100b1307da37c806ae638502c60d0ebbbe6da2ae9bb03f8798953059269e0ef3b46012102e7d08484e6c4c26bd2a3aabab09c65bbdcb4a6bba0ee5cf7008ef19b9540f818ffffffff0200743ba40b0000001976a91429a158767437cd82ccf4bd3e34ecd16c267fc36388aca0c01319180000001976a9140b31340661bb7a4165736ca2fc6509164b1dc96488ac00000000'
     tx = Transaction.deserialize(rawtx, chainparams.get_active_chain())
     self.assertEqual(str(tx), rawtx)
예제 #36
0
    def test_transaction(self):

        wallet_keys = {
            'xpub6GDQf5vZmrpQvD4ixNdqHmgSZ76Uo2Cg5isBupnvZpnNbhdRhgdhq9hkfCSKRE31rGfYuXNfZ5gTamFkj1GXt6k87MD1hUn28tuvLHY71Bk': 'L2FQCaHPwgS4CmAf6bKtbjVWDbcHv42c72by1zLEyLuDrUG22CwM',
            'xpub6GLs33TeHkrLSTJ2uxiMLnuqxCHG9iBFCwjTwyg4EvzyxUi78U1sXxxRPUQfLGNEZRT3yYKEwR39ZbUtofEgcmTLtJdSetnFiQPEwTZRW5y': 'L1aD1WSA3UGU56sAmjfYVj1rK3fnSzWKj2wTsDTN1DpgUgPVwQCa',
            'xpub6Fc64k9RTc79yD7xErF2yKSdUraGBhGWDt1FomUFVCFg52165LZvvoGL59hebJBGtauFqNL5zMeRgPGV29sfQp6XqoiiD9E53UDatBhFZuk': 'L25eCRYrDNVNrTZV1XZhmaZti3dVsZz3egm1R7LsPHRdpYuyLYKE'
#            'xpub6GiSmJrVQC5q2m5cRcZvUgnGUsfeEqi3eEfUTgacnA33G8PAQnJeeMzpNAGUA9JL3goUui7BY52rVPwhmxMALkmFh5ZuwJDKrPv8ERkG3CK': 'L3jUPoR7fUwB9mwfuqqF79mHDpj5rpygQhdWntJ9ShZ9nbyRab5h'
        }

        acc = self.wallet.accounts["0"]
        redeem_script = '53210278a1a7de63493a8c8e0e7f4ebb13fd2a8144db25bb3bc2e5f44127a851a389332102ee780aa224c9fe54caff984205077b7cca08ced3188a3f3c639d83deda6b9a592103124429ddbed55593d0abea0d0d3d283eca4546e40017b2945f4666c561b494ba210312872f0aa80fa1a9bc7df77fa5be310f5441f7bfec798fe19209b04954dec8da54ae'

        coins = [ {'address': '32SbQbBthcMzRSpCw1ifAXqWviJGH5HKUk',
            'value': 600000,
            'prevout_n': 0,
            'prevout_hash': '1111111111111111111111111111111111111111111111111111111111111111',
            'height': 100000,
            'coinbase': 'True'
            } ]

        txin = coins[0]

        x_pubkeys = map( lambda x:bip32_public_derivation(x, "", "/0/0/0"), self.wallet.master_public_keys.values() )
        pubkeys = map(lambda x: deserialize_xkey(x)[4].encode('hex'), x_pubkeys)

        s = ''.join( map(lambda x: int_to_hex(x, 2), (0, 0)))
        x_pubkeys = map( lambda x: 'ff' + DecodeBase58Check(x).encode('hex') + s, x_pubkeys)
        pubkeys, x_pubkeys = zip( *sorted(zip(pubkeys, x_pubkeys)))
        txin['pubkeys'] = list(pubkeys)
        txin['x_pubkeys'] = list(x_pubkeys)
        txin['signatures'] = [None] * len(pubkeys)
        txin['redeemScript'] = redeem_script
        txin['num_sig'] = acc.multisig_m

        outputs = [ ('address', '1PyXgL1qmZPuxcVi9CcguQb3v7WUvQZBud', 500000) ]
        inputs = []
        tx = Transaction(inputs, outputs)
        tx.add_input(txin)
        self.wallet.sign_transaction(tx, "secret")

        #
        ins = tx.inputs_to_sign()
        keypairs = {}
        sec = None
        for innard in ins:
            # this is easier than the commented loop below
            in_xpub, _ = BIP32_Account.parse_xpubkey(innard)
            if wallet_keys.get(in_xpub):
                keypairs[ innard ] = wallet_keys[in_xpub]
            # ...
#            in_xpub, in_seq = BIP32_Account.parse_xpubkey(innard)
#            sec = None
#            for k, vaccount in self.wallet.accounts.items():
#                acc_v = vaccount.get_master_pubkeys()[0]
#                acc_xpub = bip32_public_derivation(acc_v, "", "/0/0")
#                if in_xpub == acc_xpub:
#                    pk = vaccount.get_private_key(in_seq, self.wallet, "secret")
#                    sec = pk[0]

#            if sec:
#                keypairs [ innard ] = sec

        if keypairs:
            tx.sign(keypairs)
        self.assertEqual('0100000001111111111111111111111111111111111111111111111111111111111111111100000000fd6701004730440220774e80fda89895d8bf3ac39c38f39456d31c1e857dc1c77c000f4de6c3de15fe02207b6d13b5ba17eadeb607f3ca56f693a0b777dae668584cefec0910a8bc90869a0147304402205e80562254972f873b5b59b1cdc81e422c7a2959d8868e5a54238fbfdf6f107002200204eef593812453ae2c22334c409f9ef25523cf9619399eb2d3c249673443dc01483045022100a81e69796aa5e5ae0d8924047e3c81a8dd64dfbc791caba6728ac7820aa114da022060b85875fd58223b7c61ef45fac2567a9f76934f947e4d03d927f5b078e1fb45014c8b53210278a1a7de63493a8c8e0e7f4ebb13fd2a8144db25bb3bc2e5f44127a851a389332102ee780aa224c9fe54caff984205077b7cca08ced3188a3f3c639d83deda6b9a592103124429ddbed55593d0abea0d0d3d283eca4546e40017b2945f4666c561b494ba210312872f0aa80fa1a9bc7df77fa5be310f5441f7bfec798fe19209b04954dec8da54aeffffffff0120a10700000000001976a914fc03ab7c28d17349f084f7cadde4dafc356918d388ac00000000', str(tx))

        ###########
        #
        serialized_tx = str(tx)
        tx2 = Transaction.deserialize(serialized_tx, active_chain = self.wallet.active_chain)
        self.assertEquals(4, len(  tx2.inputs[0]['x_pubkeys'])  )
        self.assertEquals(3, tx2.inputs[0]['num_sig']  )