Skip to content

Commit

Permalink
Returned back old logic of asset unlock limits accordingly DIP0027 + …
Browse files Browse the repository at this point in the history
…written more optimal now
  • Loading branch information
knst committed Nov 28, 2022
1 parent ad2f0f0 commit 53f5d52
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 48 deletions.
90 changes: 50 additions & 40 deletions src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,36 +67,15 @@ void CCreditPoolManager::addToCache(const uint256& block_hash, int height, Credi
}
}

CreditPoolCb CCreditPoolManager::getCreditPool(const CBlockIndex* block_index, const Consensus::Params& consensusParams)
{
// TODO knst
// latelyUnlocked -> replace to sessionUnlocked
// sum up throught 576 blocks; can't keep total unlocked because CAmount will be over-flowed very soon
// keep in cache - sessionUnlocked; everything would be happy

bool isDIP0027AssetLocksActive = llmq::utils::IsV19Active(block_index);
if (!isDIP0027AssetLocksActive) {
return {0, 0, {}};
}

uint256 block_hash = block_index->GetBlockHash();
int block_height = block_index->nHeight;
{
auto pool = getFromCache(block_hash, block_height);
if (pool) { return pool.value(); }
}
CreditPoolCb prev = getCreditPool(block_index->pprev, consensusParams);
static bool getStagingDataFromBlock(const CBlockIndex *block_index, const Consensus::Params& consensusParams, CAmount &locked, CAmount &blockUnlocked, SkipSet &indexes) {
CBlock block;
if (!ReadBlockFromDisk(block, block_index, consensusParams)) {
throw std::runtime_error("failed-getcbforblock-read");
}
// Should not fail if V19 (DIP0027) are active but happens for Unit Tests
if (block.vtx[0]->nVersion != 3) {
LOCK(cs_cache);
CreditPoolCb pool{0, 0, {}};
return pool;
return false;
}

assert(!block.vtx.empty());
assert(block.vtx[0]->nVersion == 3);
assert(!block.vtx[0]->vExtraPayload.empty());
Expand All @@ -105,9 +84,8 @@ CreditPoolCb CCreditPoolManager::getCreditPool(const CBlockIndex* block_index, c
if (!GetTxPayload(block.vtx[0]->vExtraPayload, cbTx)) {
throw std::runtime_error("failed-getcbforblock-cbtx-payload");
}
locked = cbTx.assetLockedAmount;

CAmount blockUnlocked{0};
SkipSet indexes;
for (CTransactionRef tx : block.vtx) {
if (tx->nVersion != 3 || tx->nType != TRANSACTION_ASSET_UNLOCK) continue;

Expand All @@ -120,31 +98,63 @@ CreditPoolCb CCreditPoolManager::getCreditPool(const CBlockIndex* block_index, c
blockUnlocked += unlocked;
indexes.add(index);
}
return true;
}

const CAmount locked = cbTx.assetLockedAmount;
const CAmount previousLimit = prev.currentLimit;
if (previousLimit < blockUnlocked) {
throw std::runtime_error(strprintf("%s: getCreditPool failed because previous block %s exceed limits", __func__, block_hash.ToString()));
CreditPoolCb CCreditPoolManager::getCreditPool(const CBlockIndex* block_index, const Consensus::Params& consensusParams)
{
bool isDIP0027AssetLocksActive = llmq::utils::IsV19Active(block_index);
if (!isDIP0027AssetLocksActive) {
return {0, 0, {}};
}
// # max(100, min(.10 * assetlockpool, 1000))
CAmount currentLimitCandidate = locked;
if (currentLimitCandidate > LimitAmountLow) {
currentLimitCandidate = std::max(LimitAmountLow, locked / 10);

uint256 block_hash = block_index->GetBlockHash();
int block_height = block_index->nHeight;
{
auto pool = getFromCache(block_hash, block_height);
if (pool) { return pool.value(); }
}
currentLimitCandidate = std::min(currentLimitCandidate, LimitAmountHigh);
CreditPoolCb prev = getCreditPool(block_index->pprev, consensusParams);

CAmount currentLimit = previousLimit - blockUnlocked + currentLimitCandidate / LimitBlocksToTrace;
if (currentLimit > locked) {
currentLimit = locked;
CAmount blockUnlocked{0};
CAmount locked{0};
SkipSet indexes;
if (!getStagingDataFromBlock(block_index, consensusParams, locked, blockUnlocked, indexes)) {
CreditPoolCb pool{0, 0, {}};
addToCache(block_hash, block_height, pool);
return pool;
}

const CBlockIndex* distant_block = block_index;
for (size_t i = 0; i < CCreditPoolManager::LimitBlocksToTrace; ++i) {
distant_block = distant_block->pprev;
if (distant_block == nullptr) break;
}
CAmount distantUnlocked{0};
if (distant_block) {
CAmount distantLocked{0};
SkipSet distantIndexes;
if (!getStagingDataFromBlock(distant_block, consensusParams, distantLocked, distantUnlocked, distantIndexes)) distantUnlocked = 0;
}

// # max(100, min(.10 * assetlockpool, 1000))
CAmount currentLimit = locked;
CAmount latelyUnlocked = prev.latelyUnlocked + blockUnlocked - distantUnlocked;
if (currentLimit + latelyUnlocked > LimitAmountLow) {
currentLimit = std::max(LimitAmountLow, locked / 10) - latelyUnlocked;
if (currentLimit < 0) currentLimit = 0;
}
currentLimit = std::min(currentLimit, LimitAmountHigh - latelyUnlocked);

assert(currentLimit >= 0);

if (currentLimit || previousLimit || locked) {
if (currentLimit || latelyUnlocked || locked) {
LogPrintf("getCreditPool asset unlock limits on height: %d locked: %d.%08d limit: %d.%08d previous: %d.%08d\n", block_index->nHeight, locked / COIN, locked % COIN,
currentLimit / COIN, currentLimit % COIN,
previousLimit / COIN, previousLimit % COIN);
latelyUnlocked / COIN, latelyUnlocked % COIN);
}

CreditPoolCb pool{locked, currentLimit, indexes};
CreditPoolCb pool{locked, currentLimit, latelyUnlocked, indexes};
addToCache(block_hash, block_height, pool);
return pool;
}
Expand Down
6 changes: 4 additions & 2 deletions src/evo/creditpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ struct SkipSet {
struct CreditPoolCb {
CAmount locked{0};

// needs for logic of limits of unlocks
CAmount currentLimit{0};

CAmount latelyUnlocked{0};
SkipSet indexes{};

SERIALIZE_METHODS(CreditPoolCb, obj)
{
READWRITE(
obj.locked,
obj.currentLimit,
obj.latelyUnlocked,
obj.indexes
);
}
Expand Down Expand Up @@ -97,7 +99,7 @@ class CCreditPoolManager

CEvoDB& evoDb;

static constexpr int DISK_SNAPSHOT_PERIOD = 72; // once per 3 hours
static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day
private:
std::optional<CreditPoolCb> getFromCache(const uint256& block_hash, int height);
void addToCache(const uint256& block_hash, int height, CreditPoolCb pool);
Expand Down
15 changes: 9 additions & 6 deletions test/functional/feature_asset_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def run_test(self):

self.log.info("Testing asset unlock...")
# These should all have been generated by the same quorum
asset_unlock_tx = create_assetunlock(node, self.mninfo, 101, COIN, pubkey)
asset_unlock_tx = create_assetunlock(node, self.mninfo, 130, COIN, pubkey)
asset_unlock_tx_late = create_assetunlock(node, self.mninfo, 102, COIN, pubkey)
asset_unlock_tx_too_late = create_assetunlock(node, self.mninfo, 103, COIN, pubkey)
asset_unlock_tx_inactive_quorum = create_assetunlock(node, self.mninfo, 104, COIN, pubkey)
Expand Down Expand Up @@ -380,22 +380,25 @@ def run_test(self):
self.sync_all()
self.mine_quorum()
total = get_credit_pool_amount(node)
while total <= 10_500 * COIN:
while total <= 10_900 * COIN:
coin = coins.pop()
to_lock = int(coin['amount'] * COIN) - tiny_amount
if to_lock > 50 * COIN:
to_lock = 50 * COIN
total += to_lock
tx = create_assetlock(node, coin, to_lock, pubkey)
node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
node.generate(1)
self.sync_all()
credit_pool_amount_1 = get_credit_pool_amount(node)
assert_greater_than(credit_pool_amount_1, 10_500 * COIN)
assert_greater_than(credit_pool_amount_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
amount_to_withdraw_1 = 1002 * COIN
withdrawals = [600 * COIN, 101 * COIN, 101 * COIN, 101 * COIN, 101 * COIN]
amount_to_withdraw_1 = sum(withdrawals)
index = 400
for next_amount in [990 * COIN, 3 * COIN, 3 * COIN, 3 * COIN, 3 * COIN]:
for next_amount in withdrawals:
index += 1
asset_unlock_tx = create_assetunlock(node, self.mninfo, index, next_amount, pubkey)
node.sendrawtransaction(hexstring=asset_unlock_tx.serialize().hex(), maxfeerate=0)
Expand All @@ -415,7 +418,7 @@ def run_test(self):
# Check we didn't actually withdraw more than allowed by the limit
assert_greater_than_or_equal(limit_amount_1, amount_actually_withdrawn)
assert_greater_than(1000 * COIN, amount_actually_withdrawn)
assert_equal(amount_actually_withdrawn, 999 * COIN)
assert_equal(amount_actually_withdrawn, 903 * COIN)
node.generate(1)
self.sync_all()
# one tx should stay in mempool for awhile until is not invalidated by height
Expand Down

0 comments on commit 53f5d52

Please sign in to comment.