Skip to content

Commit 6f502e4

Browse files
committed
fix: check validity of asset-locked amount in coin base tx for new block
1 parent b037dd3 commit 6f502e4

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

src/evo/creditpool.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,18 @@ CCreditPoolDiff::CCreditPoolDiff(CCreditPool starter, const CBlockIndex *pindex,
251251
assert(pindex);
252252
}
253253

254+
bool CCreditPoolDiff::setTarget(const CTransaction& tx, TxValidationState& state)
255+
{
256+
CCbTx cbTx;
257+
if (!GetTxPayload(tx, cbTx)) {
258+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload");
259+
}
260+
261+
if (cbTx.nVersion == 3) {
262+
targetLocked = cbTx.assetLockedAmount;
263+
}
264+
return true;
265+
}
254266

255267
bool CCreditPoolDiff::lock(const CTransaction& tx, TxValidationState& state)
256268
{
@@ -298,6 +310,8 @@ bool CCreditPoolDiff::unlock(const CTransaction& tx, TxValidationState& state)
298310

299311
bool CCreditPoolDiff::processTransaction(const CTransaction& tx, TxValidationState& state) {
300312
if (tx.nVersion != 3) return true;
313+
if (tx.nType == TRANSACTION_COINBASE) return setTarget(tx, state);
314+
301315
if (tx.nType != TRANSACTION_ASSET_LOCK && tx.nType != TRANSACTION_ASSET_UNLOCK) return true;
302316

303317
if (!CheckAssetLockUnlockTx(tx, pindex, this->pool, state)) {

src/evo/creditpool.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ class CCreditPoolDiff {
104104
CAmount sessionLocked{0};
105105
CAmount sessionUnlocked{0};
106106

107+
// target value is used to validate CbTx. If values mismatched, block is invalid
108+
std::optional<CAmount> targetLocked;
109+
107110
const CBlockIndex *pindex{nullptr};
108111
public:
109112
explicit CCreditPoolDiff(CCreditPool starter, const CBlockIndex *pindex, const Consensus::Params& consensusParams);
@@ -119,7 +122,16 @@ class CCreditPoolDiff {
119122
return pool.locked + sessionLocked - sessionUnlocked;
120123
}
121124

125+
const std::optional<CAmount>& getTargetLocked() const {
126+
return targetLocked;
127+
}
128+
129+
std::string ToString() const {
130+
return strprintf("CCreditPoolDiff(target=%lld,sessionLocked=%lld,sessionUnlocked=%lld,newIndexes=%lld,pool=%s", getTargetLocked() ? *getTargetLocked() : -1, sessionLocked, sessionUnlocked, newIndexes.size(), pool.ToString());
131+
}
132+
122133
private:
134+
bool setTarget(const CTransaction& tx, TxValidationState& state);
123135
bool lock(const CTransaction& tx, TxValidationState& state);
124136
bool unlock(const CTransaction& tx, TxValidationState& state);
125137
};

src/evo/specialtxman.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll
156156
return false;
157157
}
158158
}
159+
if (creditPoolDiff) {
160+
CAmount locked_proposed{0};
161+
if(creditPoolDiff->getTargetLocked()) locked_proposed = *creditPoolDiff->getTargetLocked();
162+
163+
CAmount locked_calculated = creditPoolDiff->getTotalLocked();
164+
if (locked_proposed != locked_calculated) {
165+
LogPrintf("%s: mismatched locked amount in CbTx: %lld against re-calculated: %lld\n", __func__, locked_proposed, locked_calculated);
166+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-assetlocked-amount");
167+
}
168+
}
159169

160170
int64_t nTime2 = GetTimeMicros();
161171
nTimeLoop += nTime2 - nTime1;

test/functional/feature_asset_locks.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def check_mempool_result(self, result_expected, tx):
142142
assert_equal([result_expected], result_test)
143143
self.check_mempool_size()
144144

145-
def create_and_check_block(self, tx, expected_error = None):
145+
def create_and_check_block(self, txes, expected_error = None):
146146
node = self.nodes[0]
147147
best_block_hash = node.getbestblockhash()
148148
best_block = node.getblock(best_block_hash)
@@ -151,8 +151,10 @@ def create_and_check_block(self, tx, expected_error = None):
151151
block_time = best_block["time"] + 1
152152

153153
cbb = create_coinbase(height, dip4_activated=True, v20_activated=True)
154+
cbb.calc_sha256()
154155
block = create_block(tip, cbb, block_time, version=3)
155-
block.vtx.append(tx)
156+
for tx in txes:
157+
block.vtx.append(tx)
156158
block.hashMerkleRoot = block.calc_merkle_root()
157159
block.solve()
158160
result = node.submitblock(block.serialize().hex())
@@ -265,6 +267,10 @@ def run_test(self):
265267
assert_equal(get_credit_pool_amount(node), locked_1)
266268
self.sync_all()
267269

270+
self.log.info('Mine block with incorrect credit-poool value...')
271+
extra_lock_tx = create_assetlock(node, coin, COIN, pubkey)
272+
self.create_and_check_block([extra_lock_tx], expected_error = 'bad-cbtx-assetlocked-amount')
273+
268274
self.log.info("Mine a quorum...")
269275
self.mine_quorum()
270276
assert_equal(get_credit_pool_amount(node), locked_1)
@@ -362,7 +368,7 @@ def run_test(self):
362368
assert_equal(get_credit_pool_amount(node), locked_1 - 3 * COIN)
363369

364370
# Forcibly mine asset_unlock_tx_too_late and ensure block is invalid
365-
self.create_and_check_block(asset_unlock_tx_too_late, expected_error = "bad-assetunlock-not-active-quorum")
371+
self.create_and_check_block([asset_unlock_tx_too_late], expected_error = "bad-assetunlock-not-active-quorum")
366372

367373
node.generate(1)
368374
self.sync_all()
@@ -384,7 +390,7 @@ def run_test(self):
384390
self.ensure_tx_is_not_mined(txid_in_block)
385391

386392
# Forcibly mine asset_unlock_tx_full and ensure block is invalid
387-
self.create_and_check_block(asset_unlock_tx_full, expected_error = "failed-creditpool-unlock-too-much")
393+
self.create_and_check_block([asset_unlock_tx_full], expected_error = "failed-creditpool-unlock-too-much")
388394

389395
self.mempool_size += 1
390396
asset_unlock_tx_full = create_assetunlock(node, self.mninfo, 301, get_credit_pool_amount(node), pubkey)
@@ -404,7 +410,7 @@ def run_test(self):
404410
reason = "double index")
405411

406412
# Forcibly mine asset_unlock_tx_full and ensure block is invalid
407-
self.create_and_check_block(asset_unlock_tx_duplicate_index, expected_error = "bad-assetunlock-duplicated-index")
413+
self.create_and_check_block([asset_unlock_tx_duplicate_index], expected_error = "bad-assetunlock-duplicated-index")
408414

409415
self.log.info("Fast forward to the next day to reset all current unlock limits...")
410416
self.slowly_generate_batch(blocks_in_one_day + 1)

0 commit comments

Comments
 (0)