dash/test/functional/feature_asset_locks.py

616 lines
27 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Copyright (c) 2022-2024 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import copy
import struct
from decimal import Decimal
from io import BytesIO
from test_framework.blocktools import (
create_block,
create_coinbase,
)
from test_framework.authproxy import JSONRPCException
from test_framework.key import ECKey
from test_framework.messages import (
CAssetLockTx,
CAssetUnlockTx,
COIN,
COutPoint,
CTransaction,
CTxIn,
CTxOut,
FromHex,
hash256,
ser_string,
)
from test_framework.script import (
CScript,
OP_CHECKSIG,
OP_DUP,
OP_EQUALVERIFY,
OP_HASH160,
OP_RETURN,
hash160,
)
from test_framework.test_framework import DashTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
get_bip9_details,
hex_str_to_bytes,
)
llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM
tiny_amount = int(Decimal("0.0007") * COIN)
blocks_in_one_day = 576
class AssetLocksTest(DashTestFramework):
def set_test_params(self):
fix: make llmq_test_instantsend great again (#5832) ## Issue being fixed or feature implemented Running 3 nodes on RegTest as platform does uses do not let to create `llmq_test_instantsend` quorum: ``` 1. switch to `llmq_test_instantsend`: + self.extra_args = [["-llmqtestinstantsenddip0024=llmq_test_instantsend"]] * 5 2. removed cycle-quorum related code: - self.move_to_next_cycle() - self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount())) - self.move_to_next_cycle() - self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount())) - self.move_to_next_cycle() - self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount())) - - self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103) 3. added new quorum: + self.mine_quorum(llmq_type_name='llmq_test_instantsend', llmq_type=104) and eventually it stucked, no quorum happens 2024-01-13T19:18:49.317000Z TestFramework (INFO): Expected quorum_0 at:984 2024-01-13T19:18:49.317000Z TestFramework (INFO): Expected quorum_0 hash:6788e18f0235a5c85f3d3c6233fe132a80e74a2912256db3ad876a8ebf026048 2024-01-13T19:18:49.317000Z TestFramework (INFO): quorumIndex 0: Waiting for phase 1 (init) <frozen> ``` ## What was done? Updated condition to enable "llmq_test_instantsend": - it is RegTest and DIP0024 is not active - it is RegTest, DIP0024 is active, and specified as `llmqTypeDIP0024InstantSend` ## How Has This Been Tested? Run unit and functional tests. Beside that functional test feature_asset_locks.py now uses this quorum for instant send and that's an arrow that hit 2 birds: we have test for command line option `-llmqtestinstantsenddip0024` and code of feature_asset_locks.py is simplified. ## Breaking Changes yes, that's a bugfix that fix quorum `llmq_test_instantsend` absentance on regtest after dip-0024 activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_
2024-01-19 16:14:04 +01:00
self.set_dash_test_params(5, 3, [["-whitelist=127.0.0.1", "-llmqtestinstantsenddip0024=llmq_test_instantsend"]] * 5, evo_count=3)
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def create_assetlock(self, coin, amount, pubkey):
node_wallet = self.nodes[0]
inputs = [CTxIn(COutPoint(int(coin["txid"], 16), coin["vout"]))]
credit_outputs = []
tmp_amount = amount
if tmp_amount > COIN:
tmp_amount -= COIN
credit_outputs.append(CTxOut(COIN, CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])))
credit_outputs.append(CTxOut(tmp_amount, CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])))
lockTx_payload = CAssetLockTx(1, credit_outputs)
remaining = int(COIN * coin['amount']) - tiny_amount - amount
tx_output_ret = CTxOut(amount, CScript([OP_RETURN, b""]))
tx_output = CTxOut(remaining, CScript([pubkey, OP_CHECKSIG]))
lock_tx = CTransaction()
lock_tx.vin = inputs
lock_tx.vout = [tx_output, tx_output_ret] if remaining > 0 else [tx_output_ret]
lock_tx.nVersion = 3
lock_tx.nType = 8 # asset lock type
lock_tx.vExtraPayload = lockTx_payload.serialize()
lock_tx = node_wallet.signrawtransactionwithwallet(lock_tx.serialize().hex())
return FromHex(CTransaction(), lock_tx["hex"])
def create_assetunlock(self, index, withdrawal, pubkey=None, fee=tiny_amount):
node_wallet = self.nodes[0]
mninfo = self.mninfo
assert_greater_than(int(withdrawal), fee)
tx_output = CTxOut(int(withdrawal) - fee, CScript([pubkey, OP_CHECKSIG]))
# request ID = sha256("plwdtx", index)
request_id_buf = ser_string(b"plwdtx") + struct.pack("<Q", index)
request_id = hash256(request_id_buf)[::-1].hex()
height = node_wallet.getblockcount()
self.log.info(f"Creating asset unlock: {llmq_type_test} {request_id}")
quorumHash = mninfo[0].node.quorum("selectquorum", llmq_type_test, request_id)["quorumHash"]
self.log.info(f"Used quorum hash: {quorumHash}")
unlockTx_payload = CAssetUnlockTx(
version = 1,
index = index,
fee = fee,
requestedHeight = height,
quorumHash = int(quorumHash, 16),
quorumSig = b'\00' * 96)
unlock_tx = CTransaction()
unlock_tx.vin = []
unlock_tx.vout = [tx_output]
unlock_tx.nVersion = 3
unlock_tx.nType = 9 # asset unlock type
unlock_tx.vExtraPayload = unlockTx_payload.serialize()
unlock_tx.calc_sha256()
msgHash = format(unlock_tx.sha256, '064x')
recsig = self.get_recovered_sig(request_id, msgHash, llmq_type=llmq_type_test)
unlockTx_payload.quorumSig = bytearray.fromhex(recsig["sig"])
unlock_tx.vExtraPayload = unlockTx_payload.serialize()
return unlock_tx
def get_credit_pool_balance(self, node = None, block_hash = None):
if node is None:
node = self.nodes[0]
if block_hash is None:
block_hash = node.getbestblockhash()
block = node.getblock(block_hash)
return int(COIN * block['cbTx']['creditPoolBalance'])
def validate_credit_pool_balance(self, expected = None, block_hash = None):
for node in self.nodes:
locked = self.get_credit_pool_balance(node=node, block_hash=block_hash)
if expected is None:
expected = locked
else:
assert_equal(expected, locked)
self.log.info(f"Credit pool amount matched with '{expected}'")
return expected
def check_mempool_size(self):
self.sync_mempools()
for node in self.nodes:
assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
def check_mempool_result(self, result_expected, tx):
"""Wrapper to check result of testmempoolaccept on node_0's mempool"""
result_expected['txid'] = tx.rehash()
if result_expected['allowed']:
result_expected['vsize'] = tx.get_vsize()
result_test = self.nodes[0].testmempoolaccept([tx.serialize().hex()])
assert_equal([result_expected], result_test)
self.check_mempool_size()
def create_and_check_block(self, txes, expected_error = None):
node_wallet = self.nodes[0]
best_block_hash = node_wallet.getbestblockhash()
best_block = node_wallet.getblock(best_block_hash)
tip = int(best_block_hash, 16)
height = best_block["height"] + 1
block_time = best_block["time"] + 1
cbb = create_coinbase(height, dip4_activated=True, v20_activated=True)
gbt = node_wallet.getblocktemplate()
cbb.vExtraPayload = hex_str_to_bytes(gbt["coinbase_payload"])
cbb.rehash()
block = create_block(tip, cbb, block_time, version=4)
# Add quorum commitments from block template
for tx_obj in gbt["transactions"]:
tx = FromHex(CTransaction(), tx_obj["data"])
if tx.nType == 6:
block.vtx.append(tx)
for tx in txes:
block.vtx.append(tx)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
result = node_wallet.submitblock(block.serialize().hex())
if result != expected_error:
raise AssertionError('mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result))
def set_sporks(self):
spork_enabled = 0
spork_disabled = 4070908800
self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", spork_enabled)
self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", spork_disabled)
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", spork_disabled)
self.wait_for_sporks_same()
def ensure_tx_is_not_mined(self, tx_id):
try:
for node in self.nodes:
node.gettransaction(tx_id)
raise AssertionError("Transaction should not be mined")
except JSONRPCException as e:
assert "Invalid or non-wallet transaction id" in e.error['message']
def send_tx_simple(self, tx):
return self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
def send_tx(self, tx, expected_error = None, reason = None):
try:
self.log.info(f"Send tx with expected_error:'{expected_error}'...")
tx_res = self.send_tx_simple(tx)
if expected_error is None:
self.sync_mempools()
return tx_res
# failure didn't happen, but expected:
message = "Transaction should not be accepted"
if reason is not None:
message += ": " + reason
raise AssertionError(message)
except JSONRPCException as e:
assert expected_error in e.error['message']
def slowly_generate_batch(self, amount):
self.log.info(f"Slowly generate {amount} blocks")
while amount > 0:
self.log.info(f"Generating batch of blocks {amount} left")
next = min(10, amount)
amount -= next
self.bump_mocktime(next)
self.nodes[1].generate(next)
self.sync_all()
def run_test(self):
node_wallet = self.nodes[0]
node = self.nodes[1]
self.set_sporks()
self.activate_v19(expected_activation_height=900)
self.log.info("Activated v19 at height:" + str(node.getblockcount()))
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)
self.wait_for_sporks_same()
fix: make llmq_test_instantsend great again (#5832) ## Issue being fixed or feature implemented Running 3 nodes on RegTest as platform does uses do not let to create `llmq_test_instantsend` quorum: ``` 1. switch to `llmq_test_instantsend`: + self.extra_args = [["-llmqtestinstantsenddip0024=llmq_test_instantsend"]] * 5 2. removed cycle-quorum related code: - self.move_to_next_cycle() - self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount())) - self.move_to_next_cycle() - self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount())) - self.move_to_next_cycle() - self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount())) - - self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103) 3. added new quorum: + self.mine_quorum(llmq_type_name='llmq_test_instantsend', llmq_type=104) and eventually it stucked, no quorum happens 2024-01-13T19:18:49.317000Z TestFramework (INFO): Expected quorum_0 at:984 2024-01-13T19:18:49.317000Z TestFramework (INFO): Expected quorum_0 hash:6788e18f0235a5c85f3d3c6233fe132a80e74a2912256db3ad876a8ebf026048 2024-01-13T19:18:49.317000Z TestFramework (INFO): quorumIndex 0: Waiting for phase 1 (init) <frozen> ``` ## What was done? Updated condition to enable "llmq_test_instantsend": - it is RegTest and DIP0024 is not active - it is RegTest, DIP0024 is active, and specified as `llmqTypeDIP0024InstantSend` ## How Has This Been Tested? Run unit and functional tests. Beside that functional test feature_asset_locks.py now uses this quorum for instant send and that's an arrow that hit 2 birds: we have test for command line option `-llmqtestinstantsenddip0024` and code of feature_asset_locks.py is simplified. ## Breaking Changes yes, that's a bugfix that fix quorum `llmq_test_instantsend` absentance on regtest after dip-0024 activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_
2024-01-19 16:14:04 +01:00
self.mine_quorum(llmq_type_name='llmq_test_instantsend', llmq_type=104)
Merge #19674: refactor: test: use throwaway _ variable for unused loop counters dac7a111bdd3b0233d94cf68dae7a8bfc6ac9c64 refactor: test: use _ variable for unused loop counters (Sebastian Falbesoner) Pull request description: This tiny PR substitutes Python loops in the form of `for x in range(N): ...` by `for _ in range(N): ...` where applicable. The idea is indicating to the reader that a block (or statement, in list comprehensions) is just repeated N times, and that the loop counter is not used in the body, hence using the throwaway variable. This is already done quite often in the current tests (see e.g. `$ git grep "for _ in range("`). Another alternative would be using `itertools.repeat` (according to Python core developer Raymond Hettinger it's [even faster](https://twitter.com/raymondh/status/1144527183341375488)), but that doesn't seem to be widespread in use and I'm not sure about a readability increase. The only drawback I see is that whenever one wants to debug loop iterations, one would need to introduce a loop variable again. Reviewing this is basically a no-brainer, since tests would fail immediately if a a substitution has taken place on a loop where the variable is used. Instances to replace were found by `$ git grep "for.*in range("` and manually checked. ACKs for top commit: darosior: ACK dac7a111bdd3b0233d94cf68dae7a8bfc6ac9c64 instagibbs: manual inspection ACK https://github.com/bitcoin/bitcoin/pull/19674/commits/dac7a111bdd3b0233d94cf68dae7a8bfc6ac9c64 practicalswift: ACK dac7a111bdd3b0233d94cf68dae7a8bfc6ac9c64 -- the updated code is easier to reason about since the throwaway nature of a variable is expressed explicitly (using the Pythonic `_` idiom) instead of implicitly. Explicit is better than implicit was we all know by now :) Tree-SHA512: 5f43ded9ce14e5e00b3876ec445b90acda1842f813149ae7bafa93f3ac3d510bb778e2c701187fd2c73585e6b87797bb2d2987139bd1a9ba7d58775a59392406
2020-08-11 02:50:34 +02:00
for _ in range(3):
self.dynamically_add_masternode(evo=True)
node.generate(8)
self.sync_blocks()
self.set_sporks()
self.activate_v20()
node.generate(1)
self.sync_all()
self.mempool_size = 0
key = ECKey()
key.generate()
pubkey = key.get_pubkey().get_bytes()
self.test_asset_locks(node_wallet, node, pubkey)
self.test_asset_unlocks(node_wallet, node, pubkey)
self.test_withdrawal_limits(node_wallet, node, pubkey)
self.test_mn_rr(node_wallet, node, pubkey)
def test_asset_locks(self, node_wallet, node, pubkey):
self.log.info("Testing asset lock...")
locked_1 = 10 * COIN + 141421
locked_2 = 10 * COIN + 314159
coins = node_wallet.listunspent(query_options={'minimumAmount': Decimal(str(locked_2 / COIN))})
coin = coins.pop()
asset_lock_tx = self.create_assetlock(coin, locked_1, pubkey)
self.check_mempool_result(tx=asset_lock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
self.validate_credit_pool_balance(0)
txid_in_block = self.send_tx(asset_lock_tx)
assert "assetLockTx" in node.getrawtransaction(txid_in_block, 1)
self.validate_credit_pool_balance(0)
node.generate(1)
assert_equal(self.get_credit_pool_balance(node=node), locked_1)
self.log.info("Generate a number of blocks to ensure this is the longest chain for later in the test when we reconsiderblock")
node.generate(12)
self.sync_all()
self.validate_credit_pool_balance(locked_1)
# tx is mined, let's get blockhash
self.log.info("Invalidate block with asset lock tx...")
self.block_hash_1 = node_wallet.gettransaction(txid_in_block)['blockhash']
for inode in self.nodes:
inode.invalidateblock(self.block_hash_1)
assert_equal(self.get_credit_pool_balance(node=inode), 0)
node.generate(3)
self.sync_all()
self.validate_credit_pool_balance(0)
self.log.info("Resubmit asset lock tx to new chain...")
# NEW tx appears
asset_lock_tx_2 = self.create_assetlock(coin, locked_2, pubkey)
txid_in_block = self.send_tx(asset_lock_tx_2)
node.generate(1)
self.sync_all()
self.validate_credit_pool_balance(locked_2)
self.log.info("Reconsider old blocks...")
for inode in self.nodes:
inode.reconsiderblock(self.block_hash_1)
self.validate_credit_pool_balance(locked_1)
self.sync_all()
self.log.info('Mine block with incorrect credit-pool value...')
coin = coins.pop()
extra_lock_tx = self.create_assetlock(coin, COIN, pubkey)
self.create_and_check_block([extra_lock_tx], expected_error = 'bad-cbtx-assetlocked-amount')
self.log.info("Mine a quorum...")
self.mine_quorum(llmq_type_name='llmq_test_platform', llmq_type=106, expected_connections=2, expected_members=3, expected_contributions=3, expected_complaints=0, expected_justifications=0, expected_commitments=3 )
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
self.validate_credit_pool_balance(locked_1)
def test_asset_unlocks(self, node_wallet, node, pubkey):
self.log.info("Testing asset unlock...")
self.log.info("Generating several txes by same quorum....")
locked = self.get_credit_pool_balance()
self.validate_credit_pool_balance(locked)
asset_unlock_tx = self.create_assetunlock(101, COIN, pubkey)
asset_unlock_tx_late = self.create_assetunlock(102, COIN, pubkey)
asset_unlock_tx_too_late = self.create_assetunlock(103, COIN, pubkey)
asset_unlock_tx_too_big_fee = self.create_assetunlock(104, COIN, pubkey, fee=int(Decimal("0.1") * COIN))
asset_unlock_tx_zero_fee = self.create_assetunlock(105, COIN, pubkey, fee=0)
asset_unlock_tx_duplicate_index = copy.deepcopy(asset_unlock_tx)
# modify this tx with duplicated index to make a hash of tx different, otherwise tx would be refused too early
asset_unlock_tx_duplicate_index.vout[0].nValue += COIN
too_late_height = node.getblockcount() + 48
self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
self.check_mempool_result(tx=asset_unlock_tx_too_big_fee,
result_expected={'allowed': False, 'reject-reason' : 'max-fee-exceeded'})
self.check_mempool_result(tx=asset_unlock_tx_zero_fee,
result_expected={'allowed': False, 'reject-reason' : 'bad-txns-assetunlock-fee-outofrange'})
# not-verified is a correct faiure from mempool. Mempool knows nothing about CreditPool indexes and he just report that signature is not validated
self.check_mempool_result(tx=asset_unlock_tx_duplicate_index,
result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-not-verified'})
self.log.info("Validating that we calculate payload hash correctly: ask quorum forcely by message hash...")
asset_unlock_tx_payload = CAssetUnlockTx()
asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload))
assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].node.quorum("selectquorum", llmq_type_test, 'e6c7a809d79f78ea85b72d5df7e9bd592aecf151e679d6e976b74f053a7f9056')["quorumHash"], 16))
self.log.info("Test no IS for asset unlock...")
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)
self.wait_for_sporks_same()
txid = self.send_tx(asset_unlock_tx)
is_id = node_wallet.sendtoaddress(node_wallet.getnewaddress(), 1)
for node in self.nodes:
self.wait_for_instantlock(is_id, node)
feat(rpc): Asset Unlock status by index (#5776) ## Issue being fixed or feature implemented Platform in the scope of credit withdrawals, need a way to get the status of an Asset Unlock by index. ## What was done? A new RPC was created `getassetunlockchainlocks` that accepts Asset Unlock indexes array as parameter and return corresponding status for each index. The possible outcomes per each index are: - `chainlocked`: If the Asset Unlock index is mined on a Chainlocked block. - `mined`: If no Chainlock information is available, and the Asset Unlock index is mined. - `mempooled`: If the Asset Unlock index is in the mempool. - `unknown`: If none of the above are valid. Note: This RPC is whitelisted for the Platform RPC user. ## How Has This Been Tested? Inserted on `feature_asset_locks.py` covering cases where Asset Unlock txs are in mempool, mined and not present. ## Breaking Changes no ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: thephez <thephez@users.noreply.github.com> Co-authored-by: Konstantin Akimov <knstqq@gmail.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: pasta <pasta@dashboost.org>
2023-12-22 21:27:00 +01:00
tip = self.nodes[0].getblockcount()
indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"])
assert_equal([{'index': 101, 'status': 'mempooled'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_no_height)
indexes_statuses_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"], tip)
assert_equal([{'index': 101, 'status': 'unknown'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_height)
feat(rpc): Asset Unlock status by index (#5776) ## Issue being fixed or feature implemented Platform in the scope of credit withdrawals, need a way to get the status of an Asset Unlock by index. ## What was done? A new RPC was created `getassetunlockchainlocks` that accepts Asset Unlock indexes array as parameter and return corresponding status for each index. The possible outcomes per each index are: - `chainlocked`: If the Asset Unlock index is mined on a Chainlocked block. - `mined`: If no Chainlock information is available, and the Asset Unlock index is mined. - `mempooled`: If the Asset Unlock index is in the mempool. - `unknown`: If none of the above are valid. Note: This RPC is whitelisted for the Platform RPC user. ## How Has This Been Tested? Inserted on `feature_asset_locks.py` covering cases where Asset Unlock txs are in mempool, mined and not present. ## Breaking Changes no ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: thephez <thephez@users.noreply.github.com> Co-authored-by: Konstantin Akimov <knstqq@gmail.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: pasta <pasta@dashboost.org>
2023-12-22 21:27:00 +01:00
rawtx = node.getrawtransaction(txid, 1)
rawtx_is = node.getrawtransaction(is_id, 1)
assert_equal(rawtx["instantlock"], False)
assert_equal(rawtx_is["instantlock"], True)
assert_equal(rawtx["chainlock"], False)
assert_equal(rawtx_is["chainlock"], False)
assert not "confirmations" in rawtx
assert not "confirmations" in rawtx_is
# disable back IS
self.set_sporks()
assert "assetUnlockTx" in node.getrawtransaction(txid, 1)
self.mempool_size += 2
self.check_mempool_size()
self.validate_credit_pool_balance(locked)
node.generate(1)
self.sync_all()
assert_equal(rawtx["instantlock"], False)
assert_equal(rawtx["chainlock"], False)
rawtx = node.getrawtransaction(txid, 1)
assert_equal(rawtx["confirmations"], 1)
self.validate_credit_pool_balance(locked - COIN)
self.mempool_size -= 2
self.check_mempool_size()
block_asset_unlock = node.getrawtransaction(asset_unlock_tx.rehash(), 1)['blockhash']
self.send_tx(asset_unlock_tx,
expected_error = "Transaction already in block chain",
reason = "double copy")
self.log.info("Mining next quorum to check tx 'asset_unlock_tx_late' is still valid...")
self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106)
self.log.info("Checking credit pool amount is same...")
self.validate_credit_pool_balance(locked - 1 * COIN)
self.check_mempool_result(tx=asset_unlock_tx_late, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
self.log.info("Checking credit pool amount still is same...")
self.validate_credit_pool_balance(locked - 1 * COIN)
self.send_tx(asset_unlock_tx_late)
node.generate(1)
self.sync_all()
self.validate_credit_pool_balance(locked - 2 * COIN)
self.log.info("Generating many blocks to make quorum far behind (even still active)...")
self.slowly_generate_batch(too_late_height - node.getblockcount() - 1)
self.check_mempool_result(tx=asset_unlock_tx_too_late, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
node.generate(1)
self.sync_all()
self.check_mempool_result(tx=asset_unlock_tx_too_late,
result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-too-late'})
self.log.info("Checking that two quorums later it is too late because quorum is not active...")
self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106)
self.log.info("Expecting new reject-reason...")
self.check_mempool_result(tx=asset_unlock_tx_too_late,
result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-not-active-quorum'})
block_to_reconsider = node.getbestblockhash()
self.log.info("Test block invalidation with asset unlock tx...")
for inode in self.nodes:
inode.invalidateblock(block_asset_unlock)
self.validate_credit_pool_balance(locked)
self.slowly_generate_batch(50)
self.validate_credit_pool_balance(locked)
for inode in self.nodes:
inode.reconsiderblock(block_to_reconsider)
self.validate_credit_pool_balance(locked - 2 * COIN)
self.log.info("Forcibly mining asset_unlock_tx_too_late and ensure block is invalid...")
self.create_and_check_block([asset_unlock_tx_too_late], expected_error = "bad-assetunlock-not-active-quorum")
node.generate(1)
self.sync_all()
self.validate_credit_pool_balance(locked - 2 * COIN)
self.validate_credit_pool_balance(block_hash=self.block_hash_1, expected=locked)
self.log.info("Forcibly mine asset_unlock_tx_full and ensure block is invalid...")
self.create_and_check_block([asset_unlock_tx_duplicate_index], expected_error = "bad-assetunlock-duplicated-index")
def test_withdrawal_limits(self, node_wallet, node, pubkey):
self.log.info("Testing withdrawal limits...")
self.log.info("Too big withdrawal is expected to not be mined")
asset_unlock_tx_full = self.create_assetunlock(201, 1 + self.get_credit_pool_balance(), pubkey)
self.log.info("Checking that transaction with exceeding amount accepted by mempool...")
# Mempool doesn't know about the size of the credit pool
self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
txid_in_block = self.send_tx(asset_unlock_tx_full)
node.generate(1)
self.sync_all()
self.ensure_tx_is_not_mined(txid_in_block)
self.log.info("Forcibly mine asset_unlock_tx_full and ensure block is invalid...")
self.create_and_check_block([asset_unlock_tx_full], expected_error = "failed-creditpool-unlock-too-much")
self.mempool_size += 1
asset_unlock_tx_full = self.create_assetunlock(301, self.get_credit_pool_balance(), pubkey)
self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
txid_in_block = self.send_tx(asset_unlock_tx_full)
node.generate(1)
self.sync_all()
self.log.info("Check txid_in_block was mined...")
block = node.getblock(node.getbestblockhash())
assert txid_in_block in block['tx']
self.validate_credit_pool_balance(0)
self.log.info("Fast forward to the next day to reset all current unlock limits...")
self.slowly_generate_batch(blocks_in_one_day + 1)
self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106)
total = self.get_credit_pool_balance()
coins = node_wallet.listunspent()
while total <= 10_900 * COIN:
self.log.info(f"Collecting coins in pool... Collected {total}/{10_900 * COIN}")
coin = coins.pop()
to_lock = int(coin['amount'] * COIN) - tiny_amount
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
if to_lock > 99 * COIN:
to_lock = 99 * COIN
total += to_lock
tx = self.create_assetlock(coin, to_lock, pubkey)
self.send_tx_simple(tx)
self.sync_mempools()
node.generate(1)
self.sync_all()
credit_pool_balance_1 = self.get_credit_pool_balance()
assert_greater_than(credit_pool_balance_1, 10_900 * COIN)
limit_amount_1 = 1000 * COIN
# take most of limit by one big tx for faster testing and
# create several tiny withdrawal with exactly 1 *invalid* / causes spend above limit tx
withdrawals = [600 * COIN, 131 * COIN, 131 * COIN, 131 * COIN, 131 * COIN]
amount_to_withdraw_1 = sum(withdrawals)
index = 400
for next_amount in withdrawals:
index += 1
asset_unlock_tx = self.create_assetunlock(index, next_amount, pubkey)
self.send_tx_simple(asset_unlock_tx)
if index == 401:
self.sync_mempools()
node.generate(1)
self.sync_mempools()
node.generate(1)
self.sync_all()
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
self.log.info(f"MN_RR status: {get_bip9_details(node, 'mn_rr')}")
new_total = self.get_credit_pool_balance()
amount_actually_withdrawn = total - new_total
block = node.getblock(node.getbestblockhash())
self.log.info("Testing that we tried to withdraw more than we could...")
assert_greater_than(amount_to_withdraw_1, amount_actually_withdrawn)
self.log.info("Checking that we tried to withdraw more than the limit...")
assert_greater_than(amount_to_withdraw_1, limit_amount_1)
self.log.info("Checking we didn't actually withdraw more than allowed by the limit...")
assert_greater_than_or_equal(limit_amount_1, amount_actually_withdrawn)
assert_equal(amount_actually_withdrawn, 993 * COIN)
node.generate(1)
self.sync_all()
self.log.info("Checking that exactly 1 tx stayed in mempool...")
self.mempool_size = 1
self.check_mempool_size()
assert_equal(new_total, self.get_credit_pool_balance())
self.log.info("Fast forward to next day again...")
self.slowly_generate_batch(blocks_in_one_day - 2)
self.log.info("Checking mempool is empty now...")
self.mempool_size = 0
self.check_mempool_size()
self.log.info("Creating new asset-unlock tx. It should be mined exactly 1 block after")
credit_pool_balance_2 = self.get_credit_pool_balance()
limit_amount_2 = credit_pool_balance_2 // 10
index += 1
asset_unlock_tx = self.create_assetunlock(index, limit_amount_2, pubkey)
self.send_tx(asset_unlock_tx)
node.generate(1)
self.sync_all()
assert_equal(new_total, self.get_credit_pool_balance())
node.generate(1)
self.sync_all()
new_total -= limit_amount_2
assert_equal(new_total, self.get_credit_pool_balance())
self.log.info("Trying to withdraw more... expecting to fail")
index += 1
asset_unlock_tx = self.create_assetunlock(index, COIN * 100, pubkey)
self.send_tx(asset_unlock_tx)
node.generate(1)
self.sync_all()
tip = self.nodes[0].getblockcount()
indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "103"])
assert_equal([{'index': 101, 'status': 'mined'}, {'index': 102, 'status': 'mined'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_no_height)
indexes_statuses_height = self.nodes[0].getassetunlockstatuses(["101", "102", "103"], tip)
assert_equal([{'index': 101, 'status': 'chainlocked'}, {'index': 102, 'status': 'chainlocked'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_height)
feat(rpc): Asset Unlock status by index (#5776) ## Issue being fixed or feature implemented Platform in the scope of credit withdrawals, need a way to get the status of an Asset Unlock by index. ## What was done? A new RPC was created `getassetunlockchainlocks` that accepts Asset Unlock indexes array as parameter and return corresponding status for each index. The possible outcomes per each index are: - `chainlocked`: If the Asset Unlock index is mined on a Chainlocked block. - `mined`: If no Chainlock information is available, and the Asset Unlock index is mined. - `mempooled`: If the Asset Unlock index is in the mempool. - `unknown`: If none of the above are valid. Note: This RPC is whitelisted for the Platform RPC user. ## How Has This Been Tested? Inserted on `feature_asset_locks.py` covering cases where Asset Unlock txs are in mempool, mined and not present. ## Breaking Changes no ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: thephez <thephez@users.noreply.github.com> Co-authored-by: Konstantin Akimov <knstqq@gmail.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: pasta <pasta@dashboost.org>
2023-12-22 21:27:00 +01:00
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...")
self.slowly_generate_batch(60)
self.log.info("Checking that credit pool is not changed...")
assert_equal(new_total, self.get_credit_pool_balance())
self.check_mempool_size()
def test_mn_rr(self, node_wallet, node, pubkey):
self.log.info("Activate mn_rr...")
locked = self.get_credit_pool_balance()
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
self.activate_mn_rr(expected_activation_height=node.getblockcount() + 12 * 3)
self.log.info(f'height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}')
assert_equal(locked, self.get_credit_pool_balance())
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597) Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
2023-10-18 05:31:40 +02:00
bt = node.getblocktemplate()
platform_reward = bt['masternode'][0]['amount']
assert_equal(bt['masternode'][0]['script'], '6a') # empty OP_RETURN
owner_reward = bt['masternode'][1]['amount']
operator_reward = bt['masternode'][2]['amount'] if len(bt['masternode']) == 3 else 0
all_mn_rewards = platform_reward + owner_reward + operator_reward
assert_equal(all_mn_rewards, bt['coinbasevalue'] * 3 // 4) # 75/25 mn/miner reward split
assert_equal(platform_reward, all_mn_rewards * 375 // 1000) # 0.375 platform share
assert_equal(platform_reward, 31916328)
assert_equal(locked, self.get_credit_pool_balance())
node.generate(1)
self.sync_all()
locked += platform_reward
assert_equal(locked, self.get_credit_pool_balance())
coins = node_wallet.listunspent(query_options={'minimumAmount': 1})
coin = coins.pop()
self.send_tx(self.create_assetlock(coin, COIN, pubkey))
locked += platform_reward + COIN
node.generate(1)
self.sync_all()
assert_equal(locked, self.get_credit_pool_balance())
if __name__ == '__main__':
AssetLocksTest().main()