Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions src/assets/assetdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ static const char ASSET_FLAG = 'A';
static const char ASSET_ADDRESS_QUANTITY_FLAG = 'B';
static const char MY_ASSET_FLAG = 'M';
static const char BLOCK_ASSET_UNDO_DATA = 'U';
static const char MEMPOOL_REISSUED_TX = 'Z';

CAssetsDB::CAssetsDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "assets", nCacheSize, fMemory, fWipe) {
}
Expand Down Expand Up @@ -77,17 +78,34 @@ bool CAssetsDB::WriteBlockUndoAssetData(const uint256& blockhash, const std::vec
return Write(std::make_pair(BLOCK_ASSET_UNDO_DATA, blockhash), vIPFSHashes);
}

bool CAssetsDB::ReadBlockUndoAssetData(const uint256 &blockhash,
std::vector<std::pair<std::string, std::string> > &vIPFSHashes) {

bool CAssetsDB::ReadBlockUndoAssetData(const uint256 &blockhash, std::vector<std::pair<std::string, std::string> > &vIPFSHashes)
{
// If it exists, return the read value.
if (Exists(std::make_pair(BLOCK_ASSET_UNDO_DATA, blockhash)))
return Read(std::make_pair(BLOCK_ASSET_UNDO_DATA, blockhash), vIPFSHashes);
return Read(std::make_pair(BLOCK_ASSET_UNDO_DATA, blockhash), vIPFSHashes);

// If it doesn't exist, we just return true because we don't want to fail just because it didn't exist in the db
return true;
}

bool CAssetsDB::WriteReissuedMempoolState()
{
return Write(MEMPOOL_REISSUED_TX, mapReissuedAssets);
}

bool CAssetsDB::ReadReissuedMempoolState()
{
mapReissuedAssets.clear();
mapReissuedTx.clear();
// If it exists, return the read value.
bool rv = Read(MEMPOOL_REISSUED_TX, mapReissuedAssets);
if (rv) {
for (auto pair : mapReissuedAssets)
mapReissuedTx.insert(std::make_pair(pair.second, pair.first));
}
return rv;
}

bool CAssetsDB::LoadAssets()
{
std::unique_ptr<CDBIterator> pcursor(NewIterator());
Expand Down
2 changes: 2 additions & 0 deletions src/assets/assetdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ class CAssetsDB : public CDBWrapper
bool WriteMyAssetsData(const std::string &strName, const std::set<COutPoint>& setOuts);
bool WriteAssetAddressQuantity(const std::string& assetName, const std::string& address, const CAmount& quantity);
bool WriteBlockUndoAssetData(const uint256& blockhash, const std::vector<std::pair<std::string, std::string> >& vIPFSHashes);
bool WriteReissuedMempoolState();

// Read from database functions
bool ReadAssetData(const std::string& strName, CNewAsset& asset);
bool ReadMyAssetsData(const std::string &strName, std::set<COutPoint>& setOuts);
bool ReadAssetAddressQuantity(const std::string& assetName, const std::string& address, CAmount& quantity);
bool ReadBlockUndoAssetData(const uint256& blockhash, std::vector<std::pair<std::string, std::string> >& vIPFSHashes);
bool ReadReissuedMempoolState();

// Erase from database functions
bool EraseAssetData(const std::string& assetName);
Expand Down
3 changes: 3 additions & 0 deletions src/assets/assets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include "utilmoneystr.h"
#include "coins.h"

std::map<uint256, std::string> mapReissuedTx;
std::map<std::string, uint256> mapReissuedAssets;

// excluding owner tag ('!')
static const auto MAX_NAME_LENGTH = 30;

Expand Down
5 changes: 5 additions & 0 deletions src/assets/assets.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ struct CAssetOutputEntry;
// 50000 * 82 Bytes == 4.1 Mb
#define MAX_CACHE_ASSETS_SIZE 50000

// Create map that store that state of current reissued transaction that the mempool as accepted.
// If an asset name is in this map, any other reissue transactions wont be accepted into the mempool
extern std::map<uint256, std::string> mapReissuedTx;
extern std::map<std::string, uint256> mapReissuedAssets;

class CAssets {
public:
std::map<std::string, std::set<COutPoint> > mapMyUnspentAssets; // Asset Name -> COutPoint
Expand Down
9 changes: 8 additions & 1 deletion src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
}

//! Check to make sure that the inputs and outputs CAmount match exactly.
bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, const bool fRunningUnitTests)
bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, std::vector<std::pair<std::string, uint256> >& vPairReissueAssets, const bool fRunningUnitTests)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
Expand Down Expand Up @@ -415,6 +415,13 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c
"bad-txns" + strError);
}
}

if (mapReissuedAssets.count(reissue.strName)) {
if (mapReissuedAssets.at(reissue.strName) != tx.GetHash())
return state.DoS(100, false, REJECT_INVALID, "bad-tx-reissue-chaining-not-allowed");
} else {
vPairReissueAssets.emplace_back(std::make_pair(reissue.strName, tx.GetHash()));
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/consensus/tx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class CCoinsViewCache;
class CTransaction;
class CValidationState;
class CAssetsCache;
class CTxOut;
class uint256;

/** Transaction validation functions */

Expand All @@ -32,7 +34,7 @@ namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);

/** RVN START */
bool CheckTxAssets(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, const bool fRunningUnitTests = false);
bool CheckTxAssets(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, std::vector<std::pair<std::string, uint256> >& vPairReissueAssets, const bool fRunningUnitTests = false);
/** RVN END */
} // namespace Consensus

Expand Down
3 changes: 3 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}

if (!passetsdb->ReadReissuedMempoolState())
LogPrintf("Database failed to load last Reissued Mempool State. Will have to start from empty state");

LogPrintf("Loaded Assets from database without error\nCache of assets size: %d\nNumber of assets I have: %d\n", passetsCache->Size(), passets->mapMyUnspentAssets.size());

if (fReset) {
Expand Down
17 changes: 11 additions & 6 deletions src/test/assets/asset_tx_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// The inputs are spending 1000 Assets
// The outputs are assigning a destination to 1000 Assets
// This test should pass because all assets are assigned a destination
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, true), "CheckTxAssets Failed");
std::vector<std::pair<std::string, uint256>> vReissueAssets;
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, vReissueAssets, true), "CheckTxAssets Failed");
}

BOOST_AUTO_TEST_CASE(asset_tx_not_valid) {
Expand Down Expand Up @@ -109,7 +110,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// The inputs of this transaction are spending 1000 Assets
// The outputs are assigning a destination to only 100 Assets
// This should fail because 900 Assets aren't being assigned a destination (Trying to burn 900 Assets)
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx, state, coins, true), "CheckTxAssets should of failed");
std::vector<std::pair<std::string, uint256>> vReissueAssets;
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx, state, coins, vReissueAssets, true), "CheckTxAssets should of failed");
}

BOOST_AUTO_TEST_CASE(asset_tx_valid_multiple_outs) {
Expand Down Expand Up @@ -165,7 +167,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// The inputs are spending 1000 Assets
// The outputs are assigned 100 Assets to 10 destinations (10 * 100) = 1000
// This test should pass all assets that are being spent are assigned to a destination
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, true), "CheckTxAssets failed");
std::vector<std::pair<std::string, uint256>> vReissueAssets;
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, vReissueAssets, true), "CheckTxAssets failed");
}

BOOST_AUTO_TEST_CASE(asset_tx_multiple_outs_invalid) {
Expand Down Expand Up @@ -221,7 +224,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// The inputs are spending 1000 Assets
// The outputs are assigning 100 Assets to 12 destinations (12 * 100 = 1200)
// This test should fail because the Outputs are greater than the inputs
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx, state, coins, true), "CheckTxAssets passed when it should of failed");
std::vector<std::pair<std::string, uint256>> vReissueAssets;
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx, state, coins, vReissueAssets, true), "CheckTxAssets passed when it should of failed");
}

BOOST_AUTO_TEST_CASE(asset_tx_multiple_assets) {
Expand Down Expand Up @@ -336,7 +340,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// The inputs are spending 3000 Assets (1000 of each RAVEN, RAVENTEST, RAVENTESTTEST)
// The outputs are spending 100 Assets to 10 destinations (10 * 100 = 1000) (of each RAVEN, RAVENTEST, RAVENTESTTEST)
// This test should pass because for each asset that is spent. It is assigned a destination
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, true), "CheckTxAssets Failed");
std::vector<std::pair<std::string, uint256>> vReissueAssets;
BOOST_CHECK_MESSAGE(Consensus::CheckTxAssets(tx, state, coins, vReissueAssets, true), "CheckTxAssets Failed");


// Try it not but only spend 900 of each asset instead of 1000
Expand Down Expand Up @@ -388,7 +393,7 @@ BOOST_FIXTURE_TEST_SUITE(asset_tx_tests, BasicTestingSetup)
// Check the transaction that contains inputs that are spending 1000 Assets for 3 different assets
// While only outputs only contain 900 Assets being sent to a destination
// This should fail because 100 of each Asset isn't being sent to a destination (Trying to burn 100 Assets each)
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx2, state, coins, true), "CheckTxAssets should of failed");
BOOST_CHECK_MESSAGE(!Consensus::CheckTxAssets(tx2, state, coins, vReissueAssets, true), "CheckTxAssets should of failed");
}

BOOST_AUTO_TEST_SUITE_END()
13 changes: 12 additions & 1 deletion src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,16 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash, false);}
removeAddressIndex(hash);
removeSpentIndex(hash);

/** RVN START */
// If the transaction being removed from the mempool is locking other reissues. Free them
if (mapReissuedTx.count(hash)) {
if (mapReissuedAssets.count(mapReissuedTx.at(hash))) {
mapReissuedAssets.erase(mapReissuedTx.at((hash)));
mapReissuedTx.erase(hash);
}
}
/** RVN END */
}

// Calculates descendants of entry that are not already in setDescendants, and adds to
Expand Down Expand Up @@ -771,7 +781,8 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
/** RVN START */
if (AreAssetsDeployed()) {
bool fCheckAssets = Consensus::CheckTxAssets(tx, state, mempoolDuplicate);
std::vector<std::pair<std::string, uint256>> vReissueAssets;
bool fCheckAssets = Consensus::CheckTxAssets(tx, state, mempoolDuplicate, vReissueAssets);
assert(fCheckResult && fCheckAssets);
} else
assert(fCheckResult);
Expand Down
24 changes: 22 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,9 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
{
const CTransaction& tx = *ptx;
const uint256 hash = tx.GetHash();

/** RVN START */
std::vector<std::pair<std::string, uint256>> vReissueAssets;
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
Expand Down Expand Up @@ -605,7 +608,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}

if (AreAssetsDeployed()) {
if (!Consensus::CheckTxAssets(tx, state, view))
if (!Consensus::CheckTxAssets(tx, state, view, vReissueAssets))
return error("%s: Consensus::CheckTxAssets: %s, %s", __func__, tx.GetHash().ToString(),
FormatStateMessage(state));
}
Expand Down Expand Up @@ -913,6 +916,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (!pool.exists(hash))
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
}

for (auto out : vReissueAssets) {
mapReissuedAssets.insert(out);
mapReissuedTx.insert(std::make_pair(out.second, out.first));
}
}

GetMainSignals().TransactionAddedToMempool(ptx);
Expand Down Expand Up @@ -2104,7 +2112,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
}

if (AreAssetsDeployed()) {
if (!Consensus::CheckTxAssets(tx, state, view)) {
std::vector<std::pair<std::string, uint256>> vReissueAssets;
if (!Consensus::CheckTxAssets(tx, state, view, vReissueAssets)) {
return error("%s: Consensus::CheckTxAssets: %s, %s", __func__, tx.GetHash().ToString(),
FormatStateMessage(state));
}
Expand Down Expand Up @@ -2492,6 +2501,9 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return AbortNode(state, "Failed to write to asset database");
}
}
// Write the reissue mempool data to database
if (passetsdb)
passetsdb->WriteReissuedMempoolState();
/** RVN END */

nLastFlush = nNow;
Expand Down Expand Up @@ -2754,6 +2766,14 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}

for (auto tx : blockConnecting.vtx) {
uint256 txHash = tx->GetHash();
if (mapReissuedTx.count(txHash)) {
mapReissuedAssets.erase(mapReissuedTx.at(txHash));
mapReissuedTx.erase(txHash);
}
}
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
bool flushed = view.Flush();
Expand Down
33 changes: 31 additions & 2 deletions test/functional/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from test_framework.util import (
assert_equal,
assert_is_hash_string,
assert_raises_rpc_error
)

import string
Expand Down Expand Up @@ -180,7 +181,6 @@ def run_test(self):
self.sync_all()

chain_assets = n1.listassets(asset="CHAIN1*", verbose=False)
print(len(chain_assets))
assert_equal(len(chain_assets), 13)

self.log.info("Issuing chained assets in width issue()...")
Expand All @@ -199,8 +199,37 @@ def run_test(self):
self.sync_all()

chain_assets = n1.listassets(asset="CHAIN2/*", verbose=False)
print(len(chain_assets))
assert_equal(len(chain_assets), 26)


self.log.info("Chaining reissue transactions...")
address0 = n0.getnewaddress()
n0.issue(asset_name="CHAIN_REISSUE", qty=1000, to_address=address0, change_address="", \
units=4, reissuable=True, has_ipfs=False)

n0.generate(1)
self.sync_all()

n0.reissue(asset_name="CHAIN_REISSUE", qty=1000, to_address=address0, change_address="", \
reissuable=True)
assert_raises_rpc_error(-4, "Error: The transaction was rejected! Reason given: bad-tx-reissue-chaining-not-allowed", n0.reissue, "CHAIN_REISSUE", 1000, address0, "", True)

n0.generate(1)
self.sync_all()

n0.reissue(asset_name="CHAIN_REISSUE", qty=1000, to_address=address0, change_address="", \
reissuable=True)

n0.generate(1)
self.sync_all()

assetdata = n0.getassetdata("CHAIN_REISSUE")
assert_equal(assetdata["name"], "CHAIN_REISSUE")
assert_equal(assetdata["amount"], 3000)
assert_equal(assetdata["units"], 4)
assert_equal(assetdata["reissuable"], 1)
assert_equal(assetdata["has_ipfs"], 0)


if __name__ == '__main__':
AssetTest().main()