def compareConstructedTX(self):
        #    def test_online(self):
        #        self.maxDiff = None
        op = operations.Asset_reserve(
            **{
                "fee": {
                    "amount": 0,
                    "asset_id": "1.3.0"
                },
                "payer": "1.2.0",
                "amount_to_reserve": {
                    "amount": 1234567890,
                    "asset_id": "1.3.0"
                },
                "extensions": []
            })
        ops = [Operation(op)]
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], "BTS")
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        from grapheneapi.grapheneapi import GrapheneAPI
        rpc = GrapheneAPI("localhost", 8092)
        compare = rpc.serialize_transaction(tx.json())
        print(compare[:-130])
        print(txWire[:-130])
        print(txWire[:-130] == compare[:-130])
        self.assertEqual(compare[:-130], txWire[:-130])
Пример #2
0
    def doit(self, printWire=False):
        ops = [Operation(self.op)]
        tx = Signed_Transaction(
            ref_block_num=ref_block_num,
            ref_block_prefix=ref_block_prefix,
            expiration=expiration,
            operations=ops,
        )
        pprint(tx.json())
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")

        if printWire:
            print()
            print(txWire)
            print()

        # Test against Bitshares backened
        live = bitshares.rpc.get_transaction_hex(tx.json())

        # Compare expected result with online backend
        self.assertEqual(live[:-130], self.cm[:-130])

        # Compare expected result with online result
        self.assertEqual(live[:-130], txWire[:-130])

        # Compare expected result with test unit
        self.assertEqual(self.cm[:-130], txWire[:-130])
    def compareConstructedTX(self):
        self.maxDiff = None
        self.op = operations.Call_order_update(
            **{
                "fee": {"amount": 100, "asset_id": "1.3.0"},
                "delta_debt": {"amount": 10000, "asset_id": "1.3.22"},
                "delta_collateral": {"amount": 100000000, "asset_id": "1.3.0"},
                "funding_account": "1.2.29",
                "extensions": {"target_collateral_ratio": 12345},
            }
        )
        ops = [Operation(self.op)]
        tx = Signed_Transaction(
            ref_block_num=ref_block_num,
            ref_block_prefix=ref_block_prefix,
            expiration=expiration,
            operations=ops,
        )
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        # Test against Bitshares backened
        self.cm = bitshares.rpc.get_transaction_hex(tx.json())

        print("soll: %s" % self.cm[:-130])
        print("ist:  %s" % txWire[:-130])
        print(txWire[:-130] == self.cm[:-130])
        self.assertEqual(self.cm[:-130], txWire[:-130])
Пример #4
0
    def compareConstructedTX(self):
        self.maxDiff = None
        self.op = operations.Committee_member_create(**{
            "fee": {
                "amount": 0,
                "asset_id": "1.3.0"
            },
            "committee_member_account": "1.2.0",
            "url": "foobar"
        })
        ops = [Operation(self.op)]
        tx = Signed_Transaction(
            ref_block_num=ref_block_num,
            ref_block_prefix=ref_block_prefix,
            expiration=expiration,
            operations=ops
        )
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        from grapheneapi.grapheneapi import GrapheneAPI
        rpc = GrapheneAPI("localhost", 8092)
        self.cm = rpc.serialize_transaction(tx.json())
        print("soll: %s" % self.cm[:-130])
        print("ist:  %s" % txWire[:-130])
        print(txWire[:-130] == self.cm[:-130])
        self.assertEqual(self.cm[:-130], txWire[:-130])
Пример #5
0
    def compareConstructedTX(self):
        self.maxDiff = None
        self.op = operations.Custom(
            **{
                "fee": {
                    "amount": 0,
                    "asset_id": "1.3.0"
                },
                "payer": "1.2.0",
                "required_auths": ["1.2.100", "1.2.101"],
                "id": "35235",
                "data": hexlify(b"Foobar").decode("ascii")
            })
        ops = [Operation(self.op)]
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        # Test against Bitshares backened
        self.cm = bitshares.rpc.get_transaction_hex(tx.json())

        print("soll: %s" % self.cm[:-130])
        print("ist:  %s" % txWire[:-130])
        print(txWire[:-130] == self.cm[:-130])
        self.assertEqual(self.cm[:-130], txWire[:-130])
Пример #6
0
    def compareConstructedTX(self):
        self.maxDiff = None
        self.op = operations.Bid_collateral(**{
            'fee': {'amount': 100,
                    'asset_id': '1.3.0'},
            'additional_collateral': {
                'amount': 10000,
                'asset_id': '1.3.22'},
            'debt_covered': {
                'amount': 100000000,
                'asset_id': '1.3.0'},
            'bidder': '1.2.29',
            'extensions': []
        })
        ops = [Operation(self.op)]
        tx = Signed_Transaction(
            ref_block_num=ref_block_num,
            ref_block_prefix=ref_block_prefix,
            expiration=expiration,
            operations=ops
        )
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], "BTS")
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        from grapheneapi.grapheneapi import GrapheneAPI
        rpc = GrapheneAPI("localhost", 8092)
        self.cm = rpc.serialize_transaction(tx.json())
        print("soll: %s" % self.cm[:-130])
        print("ist:  %s" % txWire[:-130])
        print(txWire[:-130] == self.cm[:-130])
        self.assertEqual(self.cm[:-130], txWire[:-130])
    def constructTx(self):
        """ Construct the actual transaction and store it in the class's dict
            store
        """
        if self.bitshares.proposer:
            ops = [operations.Op_wrapper(op=o) for o in list(self.ops)]
            proposer = Account(self.bitshares.proposer,
                               bitshares_instance=self.bitshares)
            ops = operations.Proposal_create(
                **{
                    "fee": {
                        "amount": 0,
                        "asset_id": "1.3.0"
                    },
                    "fee_paying_account":
                    proposer["id"],
                    "expiration_time":
                    transactions.formatTimeFromNow(
                        self.bitshares.proposal_expiration),
                    "proposed_ops": [o.json() for o in ops],
                    "extensions": []
                })
            ops = [Operation(ops)]
        else:
            ops = [Operation(o) for o in list(self.ops)]

        ops = transactions.addRequiredFees(self.bitshares.rpc, ops)
        expiration = transactions.formatTimeFromNow(self.bitshares.expiration)
        ref_block_num, ref_block_prefix = transactions.getBlockParams(
            self.bitshares.rpc)
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        super(TransactionBuilder, self).__init__(tx.json())
    def compareConstructedTX(self):
        #    def test_online(self):
        #        self.maxDiff = None
        op = operations.Worker_create(
            **{
                "fee": {
                    "amount": 0,
                    "asset_id": "1.3.0"
                },
                "owner": "1.2.0",
                "work_begin_date": "1970-01-01T00:00:00",
                "work_end_date": "1970-01-01T00:00:00",
                "daily_pay": 0,
                "name": "Myname",
                "url": "myURL",
                "initializer": [1, {
                    "pay_vesting_period_days": 125
                }]
            })
        ops = [Operation(op)]
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], "BTS")
        txWire = hexlify(bytes(tx)).decode("ascii")
        print("=" * 80)
        pprint(tx.json())
        print("=" * 80)

        from grapheneapi.grapheneapi import GrapheneAPI
        rpc = GrapheneAPI("localhost", 8092)
        compare = rpc.serialize_transaction(tx.json())
        print("soll: %s" % compare[:-130])
        print("ist:  %s" % txWire[:-130])
        print(txWire[:-130] == compare[:-130])
        self.assertEqual(compare[:-130], txWire[:-130])
Пример #9
0
 async def __construct_tx(self, fee_asset_id, expiration):
     """ 构造转账操作
     """
     ops = [Operation(o) for o in list(self.ops)]
     expiration = transactions.formatTimeFromNow(expiration)
     ops = await transactions.add_required_fees(self.node_api, ops, fee_asset_id)
     ref_block_num, ref_block_prefix = await transactions.get_block_params(self.node_api)
     tx = Signed_Transaction(
         ref_block_num=ref_block_num,
         ref_block_prefix=ref_block_prefix,
         expiration=expiration,
         operations=ops
     )
     super(Builder, self).__init__(tx.json())
Пример #10
0
    async def sign(self, fee_asset_id, expiration=30):
        """ 执行签名
        """
        await self.__construct_tx(fee_asset_id, expiration)
        operations.default_prefix = self.node_api.chain_params['prefix']

        try:
            signedtx = Signed_Transaction(**self.json())
        except:
            raise ValueError('Invalid TransactionBuilder Format')

        if not any(self.wifs):
            raise MissingKeyError

        signedtx.sign(self.wifs, chain=self.node_api.chain_params)
        self['signatures'].extend(signedtx.json().get('signatures'))
Пример #11
0
    def doit(self, printWire=False):
        ops = [Operation(self.op)]
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")
        if printWire:
            print()
            print(txWire)
            print()
        self.assertEqual(self.cm[:-130], txWire[:-130])

        # Test against Bitshares backened
        bitshares = BitShares()
        self.cm = bitshares.rpc.get_transaction_hex(tx.json())
        self.assertEqual(self.cm[:-130], txWire[:-130])
    def sign(self):
        """ Sign a provided transaction with the provided key(s)

            :param dict tx: The transaction to be signed and returned
            :param string wifs: One or many wif keys to use for signing
                a transaction. If not present, the keys will be loaded
                from the wallet as defined in "missing_signatures" key
                of the transactions.
        """
        self.constructTx()

        if "operations" not in self or not self["operations"]:
            return

        # Legacy compatibility!
        # If we are doing a proposal, obtain the account from the proposer_id
        if self.blockchain.proposer:
            proposer = Account(self.blockchain.proposer,
                               blockchain_instance=self.blockchain)
            self.wifs = set()
            self.signing_accounts = list()
            self.appendSigner(proposer["id"], "active")

        # We need to set the default prefix, otherwise pubkeys are
        # presented wrongly!
        if self.blockchain.rpc:
            operations.default_prefix = (
                self.blockchain.rpc.chain_params["prefix"])
        elif "blockchain" in self:
            operations.default_prefix = self["blockchain"]["prefix"]

        try:
            signedtx = Signed_Transaction(**self.json())
        except:
            raise ValueError("Invalid TransactionBuilder Format")

        if not any(self.wifs):
            raise MissingKeyError

        signedtx.sign(self.wifs, chain=self.blockchain.rpc.chain_params)
        self["signatures"].extend(signedtx.json().get("signatures"))
        return signedtx
Пример #13
0
    def doit(self, printWire=False):
        ops = [Operation(self.op)]
        tx = Signed_Transaction(ref_block_num=ref_block_num,
                                ref_block_prefix=ref_block_prefix,
                                expiration=expiration,
                                operations=ops)
        tx = tx.sign([wif], chain=prefix)
        tx.verify([PrivateKey(wif).pubkey], prefix)
        txWire = hexlify(bytes(tx)).decode("ascii")
        if printWire:
            print()
            print(txWire)
            print()
        self.assertEqual(self.cm[:-130], txWire[:-130])

        if TEST_AGAINST_CLI_WALLET:
            from grapheneapi.grapheneapi import GrapheneAPI
            rpc = GrapheneAPI("localhost", 8092)
            self.cm = rpc.serialize_transaction(tx.json())
            # print("soll: %s" % self.cm[:-130])
            # print("ist:  %s" % txWire[:-130])
            # print(txWire[:-130] == self.cm[:-130])
            self.assertEqual(self.cm[:-130], txWire[:-130])
Пример #14
0
class TransactionBuilder(dict):
    """ This class simplifies the creation of transactions by adding
        operations and signers.
    """
    def __init__(self, tx={}, proposer=None, bitshares_instance=None):
        self.bitshares = bitshares_instance or shared_bitshares_instance()
        self.clear()
        if not isinstance(tx, dict):
            raise ValueError("Invalid TransactionBuilder Format")
        super(TransactionBuilder, self).__init__(tx)
        # Do we need to reconstruct the tx from self.ops?
        self._require_reconstruction = True

    def is_empty(self):
        return not (len(self.ops) > 0)

    def list_operations(self):
        return [Operation(o) for o in self.ops]

    def _is_signed(self):
        return "signatures" in self and self["signatures"]

    def _is_constructed(self):
        return "expiration" in self and self["expiration"]

    def _is_require_reconstruction(self):
        return self._require_reconstruction

    def _set_require_reconstruction(self):
        self._require_reconstruction = True

    def _unset_require_reconstruction(self):
        self._require_reconstruction = False

    def __repr__(self):
        return str(self)

    def __str__(self):
        return str(self.json())

    def __getitem__(self, key):
        if key not in self:
            self.constructTx()
        return dict(self).__getitem__(key)

    def get_parent(self):
        """ TransactionBuilders don't have parents, they are their own parent
        """
        return self

    def json(self):
        """ Show the transaction as plain json
        """
        if not self._is_constructed() or self._is_require_reconstruction():
            self.constructTx()
        return dict(self)

    def appendOps(self, ops, append_to=None):
        """ Append op(s) to the transaction builder

            :param list ops: One or a list of operations
        """
        if isinstance(ops, list):
            self.ops.extend(ops)
        else:
            self.ops.append(ops)
        self._set_require_reconstruction()

    def appendSigner(self, account, permission):
        """ Try to obtain the wif key from the wallet by telling which account
            and permission is supposed to sign the transaction
        """
        assert permission in ["active", "owner"], "Invalid permission"
        account = Account(account, bitshares_instance=self.bitshares)
        required_treshold = account[permission]["weight_threshold"]

        if self.bitshares.wallet.locked():
            raise WalletLocked()

        def fetchkeys(account, perm, level=0):
            if level > 2:
                return []
            r = []
            for authority in account[perm]["key_auths"]:
                try:
                    wif = self.bitshares.wallet.getPrivateKeyForPublicKey(
                        authority[0])
                    r.append([wif, authority[1]])
                except Exception:
                    pass

            if sum([x[1] for x in r]) < required_treshold:
                # go one level deeper
                for authority in account[perm]["account_auths"]:
                    auth_account = Account(authority[0],
                                           bitshares_instance=self.bitshares)
                    r.extend(fetchkeys(auth_account, perm, level + 1))

            return r

        if account not in self.signing_accounts:
            # is the account an instance of public key?
            if isinstance(account, PublicKey):
                self.wifs.add(
                    self.bitshares.wallet.getPrivateKeyForPublicKey(
                        str(account)))
            else:
                account = Account(account, bitshares_instance=self.bitshares)
                required_treshold = account[permission]["weight_threshold"]
                keys = fetchkeys(account, permission)
                if permission != "owner":
                    keys.extend(fetchkeys(account, "owner"))
                for x in keys:
                    self.wifs.add(x[0])

            self.signing_accounts.append(account)

    def appendWif(self, wif):
        """ Add a wif that should be used for signing of the transaction.
        """
        if wif:
            try:
                PrivateKey(wif)
                self.wifs.add(wif)
            except:
                raise InvalidWifError

    def constructTx(self):
        """ Construct the actual transaction and store it in the class's dict
            store
        """
        ops = list()
        for op in self.ops:
            if isinstance(op, ProposalBuilder):
                # This operation is a proposal an needs to be deal with
                # differently
                proposals = op.get_raw()
                if proposals:
                    ops.append(proposals)
            else:
                # otherwise, we simply wrap ops into Operations
                ops.extend([Operation(op)])

        # We no wrap everything into an actual transaction
        ops = transactions.addRequiredFees(self.bitshares.rpc, ops)
        expiration = transactions.formatTimeFromNow(self.bitshares.expiration)
        ref_block_num, ref_block_prefix = transactions.getBlockParams(
            self.bitshares.rpc)
        self.tx = Signed_Transaction(ref_block_num=ref_block_num,
                                     ref_block_prefix=ref_block_prefix,
                                     expiration=expiration,
                                     operations=ops)
        super(TransactionBuilder, self).__init__(self.tx.json())
        self._unset_require_reconstruction()

    def sign(self):
        """ Sign a provided transaction witht he provided key(s)

            :param dict tx: The transaction to be signed and returned
            :param string wifs: One or many wif keys to use for signing
                a transaction. If not present, the keys will be loaded
                from the wallet as defined in "missing_signatures" key
                of the transactions.
        """
        self.constructTx()

        if "operations" not in self or not self["operations"]:
            return

        # Legacy compatibility!
        # If we are doing a proposal, obtain the account from the proposer_id
        if self.bitshares.proposer:
            proposer = Account(self.bitshares.proposer,
                               bitshares_instance=self.bitshares)
            self.wifs = set()
            self.signing_accounts = list()
            self.appendSigner(proposer["id"], "active")

        # We need to set the default prefix, otherwise pubkeys are
        # presented wrongly!
        if self.bitshares.rpc:
            operations.default_prefix = (
                self.bitshares.rpc.chain_params["prefix"])
        elif "blockchain" in self:
            operations.default_prefix = self["blockchain"]["prefix"]

        try:
            signedtx = Signed_Transaction(**self.json())
        except:
            raise ValueError("Invalid TransactionBuilder Format")

        if not any(self.wifs):
            raise MissingKeyError

        signedtx.sign(self.wifs, chain=self.bitshares.rpc.chain_params)
        self["signatures"].extend(signedtx.json().get("signatures"))

    def verify_authority(self):
        """ Verify the authority of the signed transaction
        """
        try:
            if not self.bitshares.rpc.verify_authority(self.json()):
                raise InsufficientAuthorityError
        except Exception as e:
            raise e

    def broadcast(self):
        """ Broadcast a transaction to the bitshares network

            :param tx tx: Signed transaction to broadcast
        """
        # Cannot broadcast an empty transaction
        if not self._is_signed():
            self.sign()

        if "operations" not in self or not self["operations"]:
            return

        ret = self.json()

        if self.bitshares.nobroadcast:
            log.warning("Not broadcasting anything!")
            self.clear()
            return ret

        # Broadcast
        try:
            if self.bitshares.blocking:
                ret = self.bitshares.rpc.broadcast_transaction_synchronous(
                    ret, api="network_broadcast")
                ret.update(**ret["trx"])
            else:
                self.bitshares.rpc.broadcast_transaction(
                    ret, api="network_broadcast")
        except Exception as e:
            raise e

        self.clear()
        return ret

    def clear(self):
        """ Clear the transaction builder and start from scratch
        """
        self.ops = []
        self.wifs = set()
        self.signing_accounts = []
        # This makes sure that _is_constructed will return False afterwards
        self["expiration"] = None
        super(TransactionBuilder, self).__init__({})

    def addSigningInformation(self, account, permission):
        """ This is a private method that adds side information to a
            unsigned/partial transaction in order to simplify later
            signing (e.g. for multisig or coldstorage)

            FIXME: Does not work with owner keys!
        """
        self.constructTx()
        self["blockchain"] = self.bitshares.rpc.chain_params

        if isinstance(account, PublicKey):
            self["missing_signatures"] = [str(account)]
        else:
            accountObj = Account(account)
            authority = accountObj[permission]
            # We add a required_authorities to be able to identify
            # how to sign later. This is an array, because we
            # may later want to allow multiple operations per tx
            self.update(
                {"required_authorities": {
                    accountObj["name"]: authority
                }})
            for account_auth in authority["account_auths"]:
                account_auth_account = Account(account_auth[0])
                self["required_authorities"].update(
                    {account_auth[0]: account_auth_account.get(permission)})

            # Try to resolve required signatures for offline signing
            self["missing_signatures"] = [x[0] for x in authority["key_auths"]]
            # Add one recursion of keys from account_auths:
            for account_auth in authority["account_auths"]:
                account_auth_account = Account(account_auth[0])
                self["missing_signatures"].extend([
                    x[0] for x in account_auth_account[permission]["key_auths"]
                ])

    def appendMissingSignatures(self):
        """ Store which accounts/keys are supposed to sign the transaction

            This method is used for an offline-signer!
        """
        missing_signatures = self.get("missing_signatures", [])
        for pub in missing_signatures:
            wif = self.bitshares.wallet.getPrivateKeyForPublicKey(pub)
            if wif:
                self.appendWif(wif)
    def test_jsonLoading(self):
        data1 = {
            "expiration":
            expiration,
            "ref_block_num":
            ref_block_num,
            "ref_block_prefix":
            ref_block_prefix,
            "extensions": [],
            "operations": [[
                0, {
                    "amount": {
                        "amount": 1000000,
                        "asset_id": "1.3.4"
                    },
                    "extensions": [],
                    "fee": {
                        "amount": 0,
                        "asset_id": "1.3.0"
                    },
                    "from": "1.2.0",
                    "memo": {
                        "from":
                        "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
                        "message":
                        "fa5b6e83079a878e499e2e52a76a7739e9de40986a8e3bd8a68ce316cee50b21",
                        "nonce":
                        5862723643998573708,
                        "to":
                        "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
                    },
                    "to": "1.2.1"
                }
            ]],
            "signatures": [
                "1f6c1e8df5faf18c3b057ce713ec92f9" +
                "b487c1ba58138daabc0038741b402c93" +
                "0d63d8d63861740215b4f65eb8ac9185" +
                "a3987f8239b962181237f47189e21102" + "af"
            ]
        }
        a = Signed_Transaction(data1.copy())
        data2 = a.json()

        check1 = data1
        check2 = data2
        for key in [
                "expiration", "extensions", "ref_block_num",
                "ref_block_prefix", "signatures"
        ]:
            self.assertEqual(check1[key], check2[key])

        check1 = data1["operations"][0][1]
        check2 = data2["operations"][0][1]
        for key in ["from", "to"]:
            self.assertEqual(check1[key], check2[key])

        check1 = data1["operations"][0][1]["memo"]
        check2 = data2["operations"][0][1]["memo"]
        for key in check1:
            self.assertEqual(check1[key], check2[key])