def test_have_two_proposals(self): bts = self.bts tx1 = bts.new_tx() # Proposal 1 proposal1 = bts.new_proposal(tx1, proposer="init0") op = operations.Transfer( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "from": "1.2.0", "to": "1.2.0", "amount": { "amount": 0, "asset_id": "1.3.0" }, "prefix": "TEST" }) for i in range(0, 3): proposal1.appendOps(op) # Proposal 1 proposal2 = bts.new_proposal(tx1, proposer="init0") op = operations.Transfer( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "from": "1.2.0", "to": "1.2.0", "amount": { "amount": 5555555, "asset_id": "1.3.0" }, "prefix": "TEST" }) for i in range(0, 2): proposal2.appendOps(op) tx = tx1.json() self.assertEqual(len(tx["operations"]), 2) # 2 proposals # Test proposal 1 prop = tx["operations"][0] self.assertEqual(prop[0], 22) ps = prop[1] self.assertEqual(len(ps["proposed_ops"]), 3) for i in range(0, 3): self.assertEqual(ps["proposed_ops"][i]["op"][0], 0) # Test proposal 2 prop = tx["operations"][1] self.assertEqual(prop[0], 22) ps = prop[1] self.assertEqual(len(ps["proposed_ops"]), 2) for i in range(0, 2): self.assertEqual(ps["proposed_ops"][i]["op"][0], 0)
def do_batch(dat): """ Make a batch of transfers :param dat: :return: """ import config snap_info = pickle.loads(Redisdb.get("snapshot_info")) try: Bitshares = config.BitShares(node=config.WSS_NODE, wif=dat['form']['key']) oaccount = Bitshares.wallet.getAccounts()[0] except Exception as err: print(err.__repr__()) Redisdb.rpush("messages", "*|launch_error|key not found") print(0) return False for job in dat['job']: try: wif = dat['key'] pub = format(account.PrivateKey(wif).pubkey, config.PREFIX) to_account_id = job[0].decode('utf8') amount = 10 asset_id = dat['asset']['id'] message = "abcdefgABCDEFG0123456789" nonce = "5862723643998573708" fee = objects.Asset(amount=0, asset_id="1.3.0") amount = objects.Asset(amount=int(amount), asset_id=asset_id) encrypted_memo = memo.encode_memo( account.PrivateKey(wif), account.PublicKey(pub, prefix=config.PREFIX), nonce, message) memoStruct = { "from": pub, "to": pub, "nonce": nonce, "message": encrypted_memo, } memoObj = objects.Memo(**memoStruct) tmp = operations.Transfer( **{ "fee": fee, "from": dat['from'], "to": to_account_id, "amount": amount, "memo": memoObj, "prefix": config.PREFIX }) print() #test = Bitshares.transfer(job[0].decode('utf8'), float(job[1]), dat['asset'], "", dat['from']) except Exception as err: print(err.__repr__()) print(0) Redisdb.decr("batch_jobs", int(1)) Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Jobs queue: {}".format(int(Redisdb.get("batch_jobs"))))
def appendTransferOpToTx(builder, from_name, to_name, amount, symbol): ## TODO: Cleanup exception catching for better user feedback try: account = Account(from_name, blockchain_instance=blockchain) amountAsset = Amount(amount, symbol, blockchain_instance=blockchain) to = Account(to_name, blockchain_instance=blockchain) except NumRetriesReached: Logger.Write("ERROR: Can't reach API node: 'NumRetries' reached. Check network connection.") raise except: Logger.Write("Problem locating source or destination account, or asset. Might not exist.") raise memoObj = Memo(from_account=account, to_account=to, blockchain_instance=blockchain) memo_text = "" #"Signed by BitShares App on Ledger Nano S!" op = operations.Transfer( **{ "fee": {"amount": 0, "asset_id": "1.3.0"}, "from": account["id"], "to": to["id"], "amount": {"amount": int(amountAsset), "asset_id": amountAsset.asset["id"]}, "memo": memoObj.encrypt(memo_text), } ) builder.appendOps(op) return builder
def test_add_one_proposal_two_ops(self): tx1 = bitshares.new_tx() proposal1 = bitshares.new_proposal(tx1, proposer="init0") op = operations.Transfer( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "from": "1.2.0", "to": "1.2.0", "amount": { "amount": 0, "asset_id": "1.3.0" }, "prefix": "TEST", }) proposal1.appendOps(op) proposal1.appendOps(op) tx = tx1.json() self.assertEqual(tx["operations"][0][0], 22) self.assertEqual(len(tx["operations"]), 1) ps = tx["operations"][0][1] self.assertEqual(len(ps["proposed_ops"]), 2) self.assertEqual(ps["proposed_ops"][0]["op"][0], 0) self.assertEqual(ps["proposed_ops"][1]["op"][0], 0)
def obtain_raw_tx(): # if old_operation is None: _memo = memo.encrypt(memo_plain) _expiration = Config.get("bitshares", "transaction_expiration_in_sec", 60 * 60 * 24) # 24 hours # else: # memo_encrypted = memo.encrypt(memo_plain) op = operations.Transfer(**{ "fee": { "amount": 0, "asset_id": "1.3.0" }, # will be replaced "from": from_account["id"], "to": to_account["id"], "amount": amount.json(), "memo": _memo, "prefix": bitshares_instance.prefix }) tx = TransactionBuilder( bitshares_instance=bitshares_instance ) tx.appendOps(op) tx.set_expiration(_expiration) # Build the transaction, obtain fee to be paid tx.constructTx() return tx.json()
def test_Transfer(self): pub = format(account.PrivateKey(wif).pubkey, prefix) from_account_id = "1.2.0" to_account_id = "1.2.1" amount = 1000000 asset_id = "1.3.4" message = "abcdefgABCDEFG0123456789" nonce = "5862723643998573708" fee = objects.Asset(amount=0, asset_id="1.3.0") amount = objects.Asset(amount=int(amount), asset_id=asset_id) encrypted_memo = memo.encode_memo( account.PrivateKey(wif), account.PublicKey(pub, prefix=prefix), nonce, message) memoStruct = { "from": pub, "to": pub, "nonce": nonce, "message": encrypted_memo, "chain": prefix } memoObj = objects.Memo(**memoStruct) op = operations.Transfer( **{ "fee": fee, "from": from_account_id, "to": to_account_id, "amount": amount, "memo": memoObj }) 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") compare = ("f68585abf4dce7c804570100000000000000000000000140420" "f0000000000040102c0ded2bc1f1305fb0faac5e6c03ee3a192" "4234985427b6167ca569d13df435cf02c0ded2bc1f1305fb0fa" "ac5e6c03ee3a1924234985427b6167ca569d13df435cf8c94d1" "9817945c5120fa5b6e83079a878e499e2e52a76a7739e9de409" "86a8e3bd8a68ce316cee50b210000011f39e3fa7071b795491e" "3b6851d61e7c959be92cc7deb5d8491cf1c3c8c99a1eb44553c" "348fb8f5001a78b18233ac66727e32fc776d48e92d9639d64f6" "8e641948") self.assertEqual(compare[:-130], txWire[:-130])
def test_transfer(self): pub = format(account.PrivateKey(wif).pubkey, prefix) from_account_id = "1.2.0" to_account_id = "1.2.1" amount = 1000000 asset_id = "1.3.4" message = "abcdefgABCDEFG0123456789" nonce = "5862723643998573708" fee = objects.Asset(amount=0, asset_id="1.3.0") amount = objects.Asset(amount=int(amount), asset_id=asset_id) self.op = operations.Transfer( **{ "fee": fee, "from": from_account_id, "to": to_account_id, "amount": amount, "memo": { "from": pub, "to": pub, "nonce": nonce, "message": memo.encode_memo( account.PrivateKey(wif), account.PublicKey(pub, prefix=prefix), nonce, message, ), }, "prefix": prefix, }) self.cm = ("f68585abf4dce7c804570100000000000000000000000140420" "f0000000000040102c0ded2bc1f1305fb0faac5e6c03ee3a192" "4234985427b6167ca569d13df435cf02c0ded2bc1f1305fb0fa" "ac5e6c03ee3a1924234985427b6167ca569d13df435cf8c94d1" "9817945c5120fa5b6e83079a878e499e2e52a76a7739e9de409" "86a8e3bd8a68ce316cee50b210000011f39e3fa7071b795491e" "3b6851d61e7c959be92cc7deb5d8491cf1c3c8c99a1eb44553c" "348fb8f5001a78b18233ac66727e32fc776d48e92d9639d64f6" "8e641948") self.doit()
def append_transfer_tx(append_to, dest_account_name): # # `append_to` is a TransactionBuilder object. (E.g. from BitShares.new_tx()) # `dest_account_name` is a string account name. # account = Account(tip_sender, blockchain_instance=blockchain) amount = Amount(tip_amount, tip_asset_symbol, blockchain_instance=blockchain) try: to = Account(dest_account_name, blockchain_instance=blockchain) except NumRetriesReached: Logger.Write( "ERROR: Can't reach API node: 'NumRetries' reached. Check network connection." ) raise except: Logger.Write("Problem locating destination account. Might not exist.") raise memoObj = Memo(from_account=account, to_account=to, blockchain_instance=blockchain) memo_text = "" #"Signed by BitShares App on Ledger Nano S!" op = operations.Transfer( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "from": account["id"], "to": to["id"], "amount": { "amount": int(amount), "asset_id": amount.asset["id"] }, "memo": memoObj.encrypt(memo_text), }) append_to.appendOps(op) return append_to
def transfer(self, to, amount, asset, memo="", account=None): """ Transfer an asset to another account. :param str to: Recipient :param float amount: Amount to transfer :param str asset: Asset to transfer :param str memo: (optional) Memo, may begin with `#` for encrypted messaging :param str account: (optional) the source account for the transfer if not ``default_account`` """ from .memo import Memo if not account: if "default_account" in config: account = config["default_account"] if not account: raise ValueError("You need to provide an account") account = Account(account, bitshares_instance=self) amount = Amount(amount, asset, bitshares_instance=self) to = Account(to, bitshares_instance=self) memoObj = Memo(from_account=account, to_account=to, bitshares_instance=self) op = operations.Transfer( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "from": account["id"], "to": to["id"], "amount": { "amount": int(amount), "asset_id": amount.asset["id"] }, "memo": memoObj.encrypt(memo), "prefix": self.rpc.chain_params["prefix"] }) return self.finalizeOp(op, account, "active")
async def __on_send_asset(self, to, asset_id, amount, memo=''): """ 发送资产 """ # 加密备注 to_id = None memo_data = None if memo != '': to_id, memo_data = await self.__encrypt_memo(to, memo) else: to_account = await self.node_api.get_account_by_name(to) to_id = to_account['id'] # 计算转账数量 asset = await self.get_asset_info(asset_id) amount = amount * 10 ** asset['precision'] # 生成转账操作 prefix = self.node_api.chain_params['prefix'] op = operations.Transfer(**{ 'to': to_id, 'from': self.account['id'], 'amount': { 'amount': amount, 'asset_id': asset_id }, 'prefix': prefix, 'memo': memo_data, 'fee': {'amount': 0, 'asset_id': asset_id} }) # 执行转账操作 txbuffer = builder.Builder(self.node_api) txbuffer.append_ops(op) await txbuffer.append_signer(self.account, self.wifkey, prefix, 'active') await txbuffer.sign(asset_id, expiration=600) res = await txbuffer.broadcast() return res.json()
async def send_to(self, to, asset, amount, memo=''): ''' 发送资产 ''' # 加密备注 to_id = None memo_data = None if memo != '': to_id, memo_data = await self._encrypt_memo(to, memo) else: to_account = await self.client.get_account_by_name(to) to_id = to_account['id'] # 计算转账数量 amount = amount * 10 ** asset['precision'] # 生成转账操作 prefix = self.client.chain_params['prefix'] op = operations.Transfer(**{ 'to': to_id, 'from': self.account['id'], 'amount': { 'amount': amount, 'asset_id': asset['id'] }, 'prefix': prefix, 'memo': memo_data, 'fee': {'amount': 0, 'asset_id': asset['id']} }) # 执行转账操作 txbuffer = Builder(self.client) txbuffer.append_ops(op) await txbuffer.append_signer(self.account, SysConfig().active_key, prefix, 'active') signedtx = await txbuffer.sign(asset['id'], expiration=600) await txbuffer.broadcast() return signedtx.id
def send(self, amount, address, memo=None, from_address=None) -> dict: """ Send tokens to a given address/account, optionally specifying a memo. The Bitshares network transaction fee will be subtracted from the amount before sending. There must be a valid :py:class:`models.CryptoKeyPair` in the database for both 'active' and 'memo' keys for the from_address account, or an AuthorityMissing exception will be thrown. Example - send 1.23 BUILDTEAM from @someguy123 to @privex with memo 'hello' >>> s = BitsharesManager('BUILDTEAM') >>> s.send(from_address='someguy123', address='privex', amount=Decimal('1.23'), memo='hello') :param Decimal amount: Amount of tokens to send, as a Decimal() :param address: Account to send the tokens to :param from_address: Account to send the tokens from :param memo: Memo to send tokens with :raises AttributeError: When both `from_address` and `self.coin.our_account` are blank. :raises ArithmeticError: When the amount is lower than the lowest amount allowed by the token's precision (after subtracting the network transaction fee) :raises AuthorityMissing: Cannot send because we don't have authority to (missing key etc.) :raises AccountNotFound: The requested account/address doesn't exist :raises TokenNotFound: When the requested token `symbol` does not exist :raises NotEnoughBalance: The account `from_address` does not have enough balance to send this amount. :return dict: Result Information Format:: { txid:str - Transaction ID - None if not known, coin:str - Symbol that was sent, amount:Decimal - The amount that was sent (after fees), fee:Decimal - TX Fee that was taken from the amount, from:str - The account/address the coins were sent from, send_type:str - Should be statically set to "send" } """ # Try from_address first. If that's empty, try using self.coin.our_account. If both are empty, abort. if empty(from_address): if empty(self.coin.our_account): raise AttributeError( "Both 'from_address' and 'coin.our_account' are empty. Cannot send." ) from_address = self.coin.our_account # make sure we have the necessary private keys loaded (memo key for encrypting memo, active key for sending coins) self.set_wallet_keys(from_address, ['memo', 'active']) asset_obj = self.get_asset_obj(self.symbol) if asset_obj is None: raise exceptions.TokenNotFound( f'Failed to send because {self.symbol} is an invalid token symbol.' ) # trim input amount to the token's precision just to be safe str_amount = ('{0:.' + str(asset_obj['precision']) + 'f}').format(amount) amount = Decimal(str_amount) if not self.is_amount_above_minimum(amount, asset_obj['precision']): raise ArithmeticError( f'Failed to send because {amount} is less than the minimum amount allowed for {self.symbol} tokens.' ) from_account = self.get_account_obj(from_address) if from_account is None: raise exceptions.AccountNotFound( f'Failed to send because source account {from_address} could not be found.' ) to_account = self.get_account_obj(address) if to_account is None: raise exceptions.AccountNotFound( f'Failed to send because destination account {address} could not be found.' ) # verify from_account balance is sufficient for the transaction from_account_balance = self.get_decimal_from_amount( from_account.balance(self.symbol)) if from_account_balance < amount: raise exceptions.NotEnoughBalance( f'Failed to send because source account {from_address} balance {from_account_balance} {self.symbol} is less than amount to send ({amount} {self.symbol}).' ) amount_obj = Amount(str_amount, self.symbol, blockchain_instance=self.bitshares) try: if memo is None: memo = '' memo_obj = Memo(from_account=from_account, to_account=to_account, blockchain_instance=self.bitshares) encrypted_memo = memo_obj.encrypt(memo) # build preliminary transaction object, without network transaction fee op = operations.Transfer( **{ 'fee': { 'amount': 0, 'asset_id': amount_obj.asset['id'] }, 'from': from_account['id'], 'to': to_account['id'], 'amount': { 'amount': int(amount_obj), 'asset_id': amount_obj.asset['id'] }, 'memo': encrypted_memo, 'prefix': self.bitshares.prefix, }) # calculate how much the transaction fee is - rather clunky method here but it works ops = [self.bitshares.txbuffer.operation_class(op)] ops_with_fees = self.bitshares.txbuffer.add_required_fees( ops, asset_id=amount_obj.asset['id']) raw_fee_amount = Decimal( str(ops_with_fees[0][1].data['fee']['amount'])) fee_amount_str = '{0:f}'.format( raw_fee_amount / (10**amount_obj.asset['precision'])) fee_amount = Amount(fee_amount_str, self.symbol, blockchain_instance=self.bitshares) amount_obj = amount_obj - fee_amount # verify the amount still makes sense after subtracting the transaction fee if int(amount_obj) < 1: raise ArithmeticError( f'Failed to send because {amount} is less than the network transaction fee of {fee_amount_str} {self.symbol} tokens.' ) # correct the transaction object to take into account the transaction fee adj_op = operations.Transfer( **{ 'fee': { 'amount': int(fee_amount), 'asset_id': amount_obj.asset['id'] }, 'from': from_account['id'], 'to': to_account['id'], 'amount': { 'amount': int(amount_obj), 'asset_id': amount_obj.asset['id'] }, 'memo': encrypted_memo, 'prefix': self.bitshares.prefix, }) log.debug( 'doing Bitshares transaction - from_address[%s], address[%s], amount[%s %s], fee_amount[%s], amount_obj[%s], memo[%s]', from_address, address, str_amount, self.symbol, fee_amount, amount_obj, memo) # and finally, do the op! self.bitshares.finalizeOp(adj_op, from_address, "active", fee_asset=amount_obj.asset['id']) result = self.bitshares.broadcast() except KeyNotFound as e: raise exceptions.AuthorityMissing(str(e)) return { 'txid': None, # transaction ID is not readily available from the Bitshares API 'coin': self.orig_symbol, 'amount': self.get_decimal_from_amount(amount_obj), 'fee': self.get_decimal_from_amount(fee_amount), 'from': from_address, 'send_type': 'send' }
def test_proposal_delete_operation(INSTANCE, cleartxpool): instance = INSTANCE reset_wallet(instance) # create two accounts logging.info('create new accounts') accounts = create_accounts(instance, num=2) if accounts == False: logging.info('create account error') assert 0 # get accounts information accountName1 = accounts[0]['account'] accountName2 = accounts[1]['account'] activeKey1 = accounts[0]['active']['wif_priv_key'] activeKey2 = accounts[1]['active']['wif_priv_key'] account1 = cybex.Account(accountName1, cybex_instance=instance) account2 = cybex.Account(accountName2, cybex_instance=instance) # add active key instance.wallet.addPrivateKey(activeKey1) instance.wallet.addPrivateKey(activeKey2) # transfer 3 CYB to account1 and account2 from nathan acc = instance.const['master_account'] valu = 3 instance.transfer(accountName1, valu, 'CYB', '', acc) instance.transfer(accountName2, valu, 'CYB', '', acc) logging.info('transfer %s CYB from %s to %s and %s' % (valu, acc, account1, account2)) account1.refresh() account2.refresh() assert int(account1.balances[0]) == valu * 10 ** account1.balances[0].asset.precision assert int(account2.balances[0]) == valu * 10 ** account2.balances[0].asset.precision # proposal with two transaction instance.proposer = accountName1 instance.proposal_expiration = int(60*60*24) fee = instance.fee[0]['fee']['fee']/100000 logging.info('fee: %s' % fee) amount1 = 100000 amount2 = 200000 transfer1 = btsops.Transfer(**{ "fee": {"amount": fee, "asset_id":"1.3.0"}, "from": dict(account1)["id"], "to": dict(account2)["id"], "amount": {"amount": amount1, "asset_id": "1.3.0"}, "extensions": [], }) transfer2 = btsops.Transfer(**{ "fee": {"amount": fee, "asset_id":"1.3.0"}, "from": dict(account2)["id"], "to": dict(account1)["id"], "amount": {"amount": amount2, "asset_id": "1.3.0"}, "extensions": [], }) instance.finalizeOp([transfer1, transfer2], account1, "active") logging.info('%s create a proposal' % account1) account1.refresh() p_id = dict(account1.proposals[0])['id'] # logging.info(dict(account1.proposals[0])) logging.info('proposal id: %s' % p_id) logging.info('proposal: %s transfer %s CYB to %s, %s transfer %s CYB to %s' % (account1, amount1/100000, account2, account2, amount2/100000, account1)) instance.proposer = None logging.info('after %s proposal, %s balance cyb: %s' % (account1, account1, account1.balance('CYB'))) logging.info('after %s proposal, %s balance cyb: %s' % (account1, account2, account2.balance('CYB'))) # check proposal assert len(dict(account1.proposals[0])['required_active_approvals']) == 2 assert len(dict(account1.proposals[0])['available_active_approvals']) == 0 assert account1.balance('CYB') == valu - fee assert account2.balance('CYB') == valu logging.info('this proposal has not been executed') # add account1 sign logging.info('%s accept the proposal' % (account1)) instance.approveproposal([p_id], account=accountName1, approver=accountName1) account1.refresh() account2.refresh() assert len(dict(account1.proposals[0])['required_active_approvals']) == 2 assert len(dict(account1.proposals[0])['available_active_approvals']) == 1 assert account1.balance('CYB') == valu - 2*fee assert account2.balance('CYB') == valu logging.info('after %s sign, %s balance cyb: %s' % (account1, account1, account1.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account1, account2, account2.balance('CYB'))) logging.info('this proposal is not executed yet') logging.info('%s dissapprove proposal' % (account1)) instance.disapproveproposal([p_id], account=accountName1, approver=accountName1) account1.refresh() account2.refresh() logging.info('after %s disapprove proposal, %s balance: %s' % (account1, account1, account1.balance('CYB'))) logging.info('after %s disapprove proposal, %s balance: %s' % (account1, account2, account2.balance('CYB'))) logging.info('%s delete this proposal' % (account1)) instance.proposal_delete(proposal_id=p_id, account=accountName1) # check account1 balance account1.refresh() account2.refresh() logging.info('%s balance: %s' % (account1, account1.balance('CYB'))) logging.info('%s balance: %s' % (account2, account2.balance('CYB'))) assert account2.balance('CYB') == valu assert account1.balance('CYB') == valu -3*fee - 0.001
def test_proposal_one_operation_with_multisign(INSTANCE, cleartxpool): instance = INSTANCE reset_wallet(instance) # create two accounts logging.info('create new accounts') accounts = create_accounts(instance, num=4) if accounts == False: logging.info('create account error') assert 0 # get accounts information accountName1 = accounts[0]['account'] accountName2 = accounts[1]['account'] accountName3 = accounts[2]['account'] accountName4 = accounts[3]['account'] activeKey1 = accounts[0]['active']['wif_priv_key'] activeKey2 = accounts[1]['active']['wif_priv_key'] activeKey3 = accounts[2]['active']['wif_priv_key'] activeKey4 = accounts[3]['active']['wif_priv_key'] activePub1 = accounts[0]['active']['pub_key'] activePub2 = accounts[1]['active']['pub_key'] activePub3 = accounts[2]['active']['pub_key'] account1 = cybex.Account(accountName1, cybex_instance=instance) account3 = cybex.Account(accountName3, cybex_instance=instance) account4 = cybex.Account(accountName4, cybex_instance=instance) # add active key instance.wallet.addPrivateKey(activeKey1) instance.wallet.addPrivateKey(activeKey2) # transfer 3 CYB to account1 and account2 from nathan acc = instance.const['master_account'] valu = 3 instance.transfer(accountName1, valu, 'CYB', '', acc) instance.transfer(accountName2, valu, 'CYB', '', acc) instance.transfer(accountName3, valu, 'CYB', '', acc) instance.transfer(accountName4, valu, 'CYB', '', acc) logging.info('transfer %s CYB from %s to %s, %s, %s and %s' % (valu, acc, account1, cybex.Account(accountName2, cybex_instance=instance), account3, account4)) account1.refresh() account3.refresh() account4.refresh() assert int(account1.balances[0]) == valu * 10 ** account1.balances[0].asset.precision assert int(account3.balances[0]) == valu * 10 ** account3.balances[0].asset.precision assert int(account4.balances[0]) == valu * 10 ** account4.balances[0].asset.precision # make account2 a multisign account, controled by account3 and account4 obj = {'weight_threshold':2, 'account_auths':[[account3['id'], 1], [account4['id'], 1]], 'key_auths':[[activePub2, 1]], 'address_auths':[]} update_active_keys(instance, obj, account=accountName2) account2 = cybex.Account(accountName2, cybex_instance=instance) account2.refresh() assert account2['active']['weight_threshold'] == 2 # proposal with one transaction instance.proposer = accountName1 instance.proposal_expiration = int(60*60*24) fee = instance.fee[0]['fee']['fee']/100000 logging.info('fee: %s' % fee) amount = 200000 transfer = btsops.Transfer(**{ "fee": {"amount": fee, "asset_id":"1.3.0"}, "from": dict(account2)["id"], "to": dict(account1)["id"], "amount": {"amount": amount, "asset_id": "1.3.0"}, "extensions": [], }) instance.finalizeOp([transfer], account1, "active") logging.info('%s create a proposal' % account1) account2.refresh() # logging.info(dict(account2.proposals[0])) p_id = dict(account2.proposals[0])['id'] logging.info('proposal id: %s' % p_id) logging.info('proposal: %s transfer %s CYB to %s' % (account2, amount/100000, account1)) instance.proposer = None logging.info('after %s proposal, %s balance cyb: %s' % (account1, account1, account1.balance('CYB'))) logging.info('after %s proposal, %s balance cyb: %s' % (account1, account2, account2.balance('CYB'))) logging.info('after %s proposal, %s balance cyb: %s' % (account3, account3, account3.balance('CYB'))) logging.info('after %s proposal, %s balance cyb: %s' % (account3, account4, account4.balance('CYB'))) # check proposal assert len(dict(account2.proposals[0])['required_active_approvals']) == 1 assert len(dict(account2.proposals[0])['available_active_approvals']) == 0 assert account1.balance('CYB') == valu - fee assert account2.balance('CYB') == valu logging.info('this proposal has not been executed') # time.sleep(60) # add account3 sign # instance.wallet.removePrivateKeyFromPublicKey(activePub1) # instance.wallet.removePrivateKeyFromPublicKey(activePub2) instance.wallet.addPrivateKey(activeKey3) logging.info('%s accept the proposal' % (account3)) instance.approveproposal(p_id, account=accountName3, approver=accountName3) account1.refresh() account2.refresh() account3.refresh() account4.refresh() logging.info('after %s sign, %s balance cyb: %s' % (account3, account1, account1.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account3, account2, account2.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account3, account3, account3.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account3, account4, account4.balance('CYB'))) assert len(dict(account2.proposals[0])['required_active_approvals']) == 1 assert len(dict(account2.proposals[0])['available_active_approvals']) == 1 assert account1.balance('CYB') == valu - fee assert account2.balance('CYB') == valu assert account3.balance('CYB') == valu - fee assert account4.balance('CYB') == valu logging.info('this proposal has not been executed') # time.sleep(60) # add account4 sign # instance.wallet.removePrivateKeyFromPublicKey(activePub3) instance.wallet.addPrivateKey(activeKey4) logging.info('%s accept the proposal' % (account4)) instance.approveproposal(p_id, account=accountName4, approver=accountName4) account1.refresh() account2.refresh() account3.refresh() account4.refresh() logging.info('after %s sign, %s balance cyb: %s' % (account4, account1, account1.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account4, account2, account2.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account4, account3, account3.balance('CYB'))) logging.info('after %s sign, %s balance cyb: %s' % (account4, account4, account4.balance('CYB'))) assert account1.balance('CYB') == valu - fee + amount/100000 assert account2.balance('CYB') == valu - fee - amount/100000 assert account3.balance('CYB') == valu - fee assert account4.balance('CYB') == valu - fee logging.info('this proposal has been executed successfully')