diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py index 7bfb1d76a8b7..b358960bd31c 100755 --- a/contrib/testgen/gen_key_io_test_vectors.py +++ b/contrib/testgen/gen_key_io_test_vectors.py @@ -62,42 +62,42 @@ # templates for valid bech32 sequences bech32_templates = [ # hrp, version, witprog_size, metadata, encoding, output_prefix - ('bc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix), - ('bc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix), - ('bc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix), - ('bc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)), - ('tb', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix), - ('tb', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix), - ('tb', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix), - ('tb', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)), - ('tb', 0, 20, (False, 'signet', None, True), Encoding.BECH32, p2wpkh_prefix), - ('tb', 0, 32, (False, 'signet', None, True), Encoding.BECH32, p2wsh_prefix), - ('tb', 1, 32, (False, 'signet', None, True), Encoding.BECH32M, p2tr_prefix), - ('tb', 3, 32, (False, 'signet', None, True), Encoding.BECH32M, (OP_3, 32)), - ('bcrt', 0, 20, (False, 'regtest', None, True), Encoding.BECH32, p2wpkh_prefix), - ('bcrt', 0, 32, (False, 'regtest', None, True), Encoding.BECH32, p2wsh_prefix), - ('bcrt', 1, 32, (False, 'regtest', None, True), Encoding.BECH32M, p2tr_prefix), - ('bcrt', 16, 40, (False, 'regtest', None, True), Encoding.BECH32M, (OP_16, 40)) + ('cc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix), + ('cc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix), + ('cc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix), + ('cc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)), + ('tc', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix), + ('tc', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix), + ('tc', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix), + ('tc', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)), + ('tc', 0, 20, (False, 'signet', None, True), Encoding.BECH32, p2wpkh_prefix), + ('tc', 0, 32, (False, 'signet', None, True), Encoding.BECH32, p2wsh_prefix), + ('tc', 1, 32, (False, 'signet', None, True), Encoding.BECH32M, p2tr_prefix), + ('tc', 3, 32, (False, 'signet', None, True), Encoding.BECH32M, (OP_3, 32)), + ('ccrt', 0, 20, (False, 'regtest', None, True), Encoding.BECH32, p2wpkh_prefix), + ('ccrt', 0, 32, (False, 'regtest', None, True), Encoding.BECH32, p2wsh_prefix), + ('ccrt', 1, 32, (False, 'regtest', None, True), Encoding.BECH32M, p2tr_prefix), + ('ccrt', 16, 40, (False, 'regtest', None, True), Encoding.BECH32M, (OP_16, 40)) ] # templates for invalid bech32 sequences bech32_ng_templates = [ # hrp, version, witprog_size, encoding, invalid_bech32, invalid_checksum, invalid_char - ('tc', 0, 20, Encoding.BECH32, False, False, False), - ('bt', 1, 32, Encoding.BECH32M, False, False, False), - ('tb', 17, 32, Encoding.BECH32M, False, False, False), - ('bcrt', 3, 1, Encoding.BECH32M, False, False, False), - ('bc', 15, 41, Encoding.BECH32M, False, False, False), - ('tb', 0, 16, Encoding.BECH32, False, False, False), - ('bcrt', 0, 32, Encoding.BECH32, True, False, False), - ('bc', 0, 16, Encoding.BECH32, True, False, False), - ('tb', 0, 32, Encoding.BECH32, False, True, False), - ('bcrt', 0, 20, Encoding.BECH32, False, False, True), - ('bc', 0, 20, Encoding.BECH32M, False, False, False), - ('tb', 0, 32, Encoding.BECH32M, False, False, False), - ('bcrt', 0, 20, Encoding.BECH32M, False, False, False), - ('bc', 1, 32, Encoding.BECH32, False, False, False), - ('tb', 2, 16, Encoding.BECH32, False, False, False), - ('bcrt', 16, 20, Encoding.BECH32, False, False, False), + ('cc', 0, 20, Encoding.BECH32, False, False, False), + ('ct', 1, 32, Encoding.BECH32M, False, False, False), + ('tc', 17, 32, Encoding.BECH32M, False, False, False), + ('ccrt', 3, 1, Encoding.BECH32M, False, False, False), + ('cc', 15, 41, Encoding.BECH32M, False, False, False), + ('tc', 0, 16, Encoding.BECH32, False, False, False), + ('ccrt', 0, 32, Encoding.BECH32, True, False, False), + ('cc', 0, 16, Encoding.BECH32, True, False, False), + ('tc', 0, 32, Encoding.BECH32, False, True, False), + ('ccrt', 0, 20, Encoding.BECH32, False, False, True), + ('cc', 0, 20, Encoding.BECH32M, False, False, False), + ('tc', 0, 32, Encoding.BECH32M, False, False, False), + ('ccrt', 0, 20, Encoding.BECH32M, False, False, False), + ('cc', 1, 32, Encoding.BECH32, False, False, False), + ('tc', 2, 16, Encoding.BECH32, False, False, False), + ('ccrt', 16, 20, Encoding.BECH32, False, False, False), ] def is_valid(v): @@ -119,7 +119,7 @@ def is_valid(v): def is_valid_bech32(v): '''Check vector v for bech32 validity''' - for hrp in ['bc', 'tb', 'bcrt']: + for hrp in ['cc', 'tc', 'ccrt']: if decode_segwit_address(hrp, v) != (None, None): return True return False diff --git a/generate_blockfilters.py b/generate_blockfilters.py new file mode 100644 index 000000000000..3d24fadc474f --- /dev/null +++ b/generate_blockfilters.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +""" +Generate blockfilters.json.h from custom block data +Usage: python3 generate_blockfilters.py > blockfilters.json.h +""" + +import sys +import json + +def hex_to_c_array(hex_string): + """Convert hex string to C-style byte array""" + # Remove any whitespace or prefixes + hex_string = hex_string.replace('0x', '').replace(' ', '').replace('\n', '') + + # Convert to bytes + if len(hex_string) % 2 != 0: + raise ValueError("Hex string must have even length") + + bytes_data = bytes.fromhex(hex_string) + + # Format as C array + c_array = [] + for i, byte in enumerate(bytes_data): + c_array.append(f"'\\x{byte:02x}'") + + return c_array + +def format_c_array_lines(c_array, indent=4): + """Format C array into lines of reasonable length""" + lines = [] + current_line = " " * indent + + for i, item in enumerate(c_array): + if i > 0: + current_line += ", " + + # Start new line if current line is getting too long + if len(current_line) > 70 and i > 0: + lines.append(current_line.rstrip(", ")) + current_line = " " * indent + + current_line += item + + if current_line.strip(): + lines.append(current_line) + + return ",\n".join(lines) + +def generate_blockfilters_header(block_hex, block_height=0, block_hash=None, + prev_basic_header=None, basic_filter=None, + basic_header=None, notes="Custom block"): + """Generate the complete blockfilters.json.h file""" + + # Default values if not provided + if block_hash is None: + block_hash = "0" * 64 + if prev_basic_header is None: + prev_basic_header = "0" * 64 + if basic_filter is None: + basic_filter = "0150ac20" + if basic_header is None: + basic_header = "cd6ff3436ab73a37671e2232b42c8bce4c5597112d736e88555effd3aabcc526" + + # Create block filter test entry matching the exact format + test_data = [ + [ + "Block Height,Block Hash,Block,[Prev Output Scripts for Block],Previous Basic Header,Basic Filter,Basic Header,Notes" + ], + [ + block_height, + block_hash, + block_hex, + [], # Previous output scripts + prev_basic_header, + basic_filter, + basic_header, + notes + ] + ] + + # Convert to JSON with proper formatting + json_string = json.dumps(test_data, separators=(',', ',')) + + # Convert JSON string to C array + c_array = hex_to_c_array(json_string.encode('utf-8').hex()) + + # Generate header + header = """// Copyright (c) 2019-2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_DATA_BLOCKFILTERS_JSON_H +#define BITCOIN_TEST_DATA_BLOCKFILTERS_JSON_H + +namespace json_tests { +static const char *blockfilters_json = +""" + + footer = """; +} // namespace json_tests + +#endif // BITCOIN_TEST_DATA_BLOCKFILTERS_JSON_H +""" + + # Format the C array with proper indentation + formatted_array = format_c_array_lines(c_array) + + return header + formatted_array + footer + +def main(): + if len(sys.argv) < 2: + print("Usage: python3 generate_blockfilters.py [height] [hash] [prev_header] [filter] [header] [notes]", file=sys.stderr) + print("Example: python3 generate_blockfilters.py 0100000000000000... 0 > blockfilters.json.h", file=sys.stderr) + print("\nAll parameters after block_hex are optional:", file=sys.stderr) + print(" height: Block height (default: 0)", file=sys.stderr) + print(" hash: Block hash (default: all zeros)", file=sys.stderr) + print(" prev_header: Previous basic header (default: all zeros)", file=sys.stderr) + print(" filter: Basic filter hex (default: 0150ac20)", file=sys.stderr) + print(" header: Basic header hash (default: genesis hash)", file=sys.stderr) + print(" notes: Description (default: 'Custom block')", file=sys.stderr) + sys.exit(1) + + block_hex = sys.argv[1] + + # Optional parameters + block_height = int(sys.argv[2]) if len(sys.argv) > 2 else 0 + block_hash = sys.argv[3] if len(sys.argv) > 3 else None + prev_basic_header = sys.argv[4] if len(sys.argv) > 4 else None + basic_filter = sys.argv[5] if len(sys.argv) > 5 else None + basic_header = sys.argv[6] if len(sys.argv) > 6 else None + notes = sys.argv[7] if len(sys.argv) > 7 else "Custom block" + + output = generate_blockfilters_header( + block_hex, block_height, block_hash, + prev_basic_header, basic_filter, basic_header, notes + ) + print(output) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/coins.cpp b/src/coins.cpp index 48ed877ff882..56d5fe2590b3 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -120,7 +120,7 @@ void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coi if (inserted) CCoinsCacheEntry::SetDirty(*it, m_sentinel); } -void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const CAmount preconfRefund, uint32_t nAssetID, const CAmount amountAssetIn, int nControlN, uint32_t nNewAssetID, bool check_for_overwrite) +void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const CAmount preconfRefund, std::vector nAssetID, const CAmount amountAssetIn, int nControlN, std::vector nNewAssetID, bool check_for_overwrite) { bool fCoinbase = tx.IsCoinBase(); const Txid& txid = tx.GetHash(); @@ -130,7 +130,7 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const DataStream stream(stack[2]); CAmount value; stream >> value; - cache.AddCoin(tx.vin[0].prevout, Coin(CTxOut(value, CScript(stack[0].begin(), stack[0].end())), nHeight, fCoinbase, false, false, false, true, 0), false); + cache.AddCoin(tx.vin[0].prevout, Coin(CTxOut(value, CScript(stack[0].begin(), stack[0].end())), nHeight, fCoinbase, false, false, false, true, std::vector{}), false); } if (amountAssetIn > 0) { @@ -139,7 +139,7 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const if (tx.version == TRANSACTION_PRECONF_VERSION && !tx.IsCoinBase()) { bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, 0)) : fCoinbase; CTxOut refund(preconfRefund, tx.vout[0].scriptPubKey); - cache.AddCoin(COutPoint(txid, 0), Coin(refund, nHeight, fCoinbase, false, false, true, false, 0), overwrite); + cache.AddCoin(COutPoint(txid, 0), Coin(refund, nHeight, fCoinbase, false, false, true, false, std::vector{}), overwrite); } // Label BitAsset outputs until we account for all BitAsset input @@ -149,8 +149,8 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; bool fAsset = amountAssetIn > amountAssetOut; bool fControl = nControlN >= 0 && (int)i == nControlN; - uint32_t nID = nNewAssetID ? nNewAssetID : nAssetID; - cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : 0), overwrite); + std::vector nID = !nNewAssetID.empty() ? nNewAssetID : nAssetID; + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : std::vector{}), overwrite); if (fAsset) amountAssetOut += tx.vout[i].nValue; } @@ -163,19 +163,19 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, const for (size_t i = 0; i < tx.vout.size(); ++i) { bool fAsset = fNewAsset && i < 2; bool fControl = fNewAsset && i == 0; - uint32_t nID = nNewAssetID ? nNewAssetID : nAssetID; + std::vector nID = !nNewAssetID.empty() ? nNewAssetID : nAssetID; bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; if (tx.version == TRANSACTION_PRECONF_VERSION && i == 0 && !tx.IsCoinBase()) { CTxOut refund(preconfRefund, tx.vout[i].scriptPubKey); - cache.AddCoin(COutPoint(txid, i), Coin(refund, nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : 0), overwrite); + cache.AddCoin(COutPoint(txid, i), Coin(refund, nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : std::vector{}), overwrite); } else { - cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : 0), overwrite); + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fAsset, fControl, tx.version == TRANSACTION_PRECONF_VERSION ? true : false, false, fAsset ? nID : std::vector{}), overwrite); } } } } -bool CCoinsViewCache::SpendCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, bool& isPreconf, uint32_t& nAssetID, Coin* moveout) +bool CCoinsViewCache::SpendCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, bool& isPreconf, std::vector& nAssetID, Coin* moveout) { CCoinsMap::iterator it = FetchCoin(outpoint); if (it == cacheCoins.end()) return false; @@ -202,7 +202,7 @@ bool CCoinsViewCache::SpendCoin(const COutPoint& outpoint, bool& fBitAsset, bool return true; } -bool CCoinsViewCache::getAssetCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, uint32_t& nAssetID, Coin* moveout) +bool CCoinsViewCache::getAssetCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, std::vector& nAssetID, Coin* moveout) { CCoinsMap::iterator it = FetchCoin(outpoint); if (it == cacheCoins.end()) return false; diff --git a/src/coins.h b/src/coins.h index 55c8f377383d..33062a739bc5 100644 --- a/src/coins.h +++ b/src/coins.h @@ -55,12 +55,12 @@ class Coin //! Is this a pegin transaction bool isPegin; - uint32_t nAssetID; + std::vector nAssetID; //! construct a Coin from a CTxOut and height/coinbase information. - Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fBitAssetIn = false, bool fBitAssetControlIn = false, bool isPreconfIn = false, bool isPeginIn = false, uint32_t nAssetIDIn = 0) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fBitAsset(fBitAssetIn), fBitAssetControl(fBitAssetControlIn), isPreconf(isPreconfIn), isPegin(isPeginIn), nAssetID(nAssetIDIn) {} - Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fBitAssetIn = false, bool fBitAssetControlIn = false, bool isPreconfIn = false, bool isPeginIn = false, uint32_t nAssetIDIn = 0) : out(outIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fBitAsset(fBitAssetIn), fBitAssetControl(fBitAssetControlIn), isPreconf(isPreconfIn), isPegin(isPeginIn), nAssetID(nAssetIDIn) {} + Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fBitAssetIn = false, bool fBitAssetControlIn = false, bool isPreconfIn = false, bool isPeginIn = false, std::vector nAssetIDIn = {}) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fBitAsset(fBitAssetIn), fBitAssetControl(fBitAssetControlIn), isPreconf(isPreconfIn), isPegin(isPeginIn), nAssetID(nAssetIDIn) {} + Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fBitAssetIn = false, bool fBitAssetControlIn = false, bool isPreconfIn = false, bool isPeginIn = false, std::vector nAssetIDIn = {}) : out(outIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fBitAsset(fBitAssetIn), fBitAssetControl(fBitAssetControlIn), isPreconf(isPreconfIn), isPegin(isPeginIn), nAssetID(nAssetIDIn) {} void Clear() { @@ -71,11 +71,11 @@ class Coin fBitAssetControl = false; isPreconf = false; isPegin = false; - nAssetID = 0; + nAssetID.clear(); } //! empty constructor - Coin() : fCoinBase(false), nHeight(0), fBitAsset(false), fBitAssetControl(false), isPreconf(false), isPegin(false), nAssetID(0) {} + Coin() : fCoinBase(false), nHeight(0), fBitAsset(false), fBitAssetControl(false), isPreconf(false), isPegin(false), nAssetID({}) {} bool IsCoinBase() const @@ -103,7 +103,7 @@ class Coin return isPegin; } - uint32_t GetAssetID() const + std::vector GetAssetID() const { return nAssetID; } @@ -500,14 +500,14 @@ class CCoinsViewCache : public CCoinsViewBacked * If no unspent output exists for the passed outpoint, this call * has no effect. */ - bool SpendCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, bool& isPreconf, uint32_t& nAssetID, Coin* moveto = nullptr); + bool SpendCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, bool& isPreconf, std::vector& nAssetID, Coin* moveto = nullptr); /** * get asset coin. Pass moveto in order to get the deleted data. * If no unspent output exists for the passed outpoint, this call * has no effect. */ - bool getAssetCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, uint32_t& nAssetID, Coin* moveto = nullptr); + bool getAssetCoin(const COutPoint& outpoint, bool& fBitAsset, bool& fBitAssetControl, std::vector& nAssetID, Coin* moveto = nullptr); bool isPeginSpent(const COutPoint& outpoint) const; @@ -567,7 +567,7 @@ class CCoinsViewCache : public CCoinsViewBacked //! an overwrite. // TODO: pass in a boolean to limit these possible overwrites to known // (pre-BIP34) cases. -void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight = 0, const CAmount preconfRefund = CAmount(0), uint32_t nAssetID = 0, const CAmount amountAssetIn = 0, int nControlN = -1, uint32_t nNewAssetID = 0, bool check = false); +void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight = 0, const CAmount preconfRefund = CAmount(0), std::vector nAssetID = {}, const CAmount amountAssetIn = 0, int nControlN = -1, std::vector nNewAssetID = {}, bool check = false); //! Utility function to find any unspent output with a given txid. //! This function can be quite expensive because in the event of a transaction diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 87e20bdfb856..2e1955363e3a 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -223,7 +223,6 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(std::move(leaves)); } - uint256 SignedBlockMerkleRoot(const SignedBlock& block, bool* mutated) { std::vector leaves; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index bbd2eb34acc5..0780e98854d0 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -159,7 +159,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i return nSigOps; } -bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool isConnectBlock) +bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, CAmount& utxoFee, bool isConnectBlock) { if (tx.version == TRANSACTION_PEGIN_VERSION) { for (const CTxIn& txin : tx.vin) { @@ -210,12 +210,18 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, assert(!coin.IsSpent()); } - if (tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION || tx.version == 2) { + if (tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION) { if (coin.IsBitAsset()) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-coins-not-exist"); } } + if(tx.version == TRANSACTION_PRECONF_VERSION || tx.version == 2) { + if (coin.IsBitAsset() || coin.IsBitAssetController()) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-coins-not-exist"); + } + } + if (!coin.isPreconf) { // If prev is coinbase, check that it's matured if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { @@ -223,6 +229,15 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); } } + + if(coin.IsBitAsset() && tx.vin[i].prevout.assetId.empty()){ + return state.Invalid(TxValidationResult::TX_CONSENSUS, "Asset information is missing"); + } + + if(coin.IsBitAsset() && tx.vin[i].prevout.assetId != coin.GetAssetID()){ + return state.Invalid(TxValidationResult::TX_CONSENSUS, "Asset mistmatch"); + } + // Check for negative or overflow input values if (!coin.IsBitAssetController()) { nValueIn += coin.out.nValue; @@ -242,7 +257,12 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, if (!MoneyRange(txfee_aux)) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange"); } + txfee = txfee_aux; + + if((tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION || tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION) && tx.vin.size() < tx.vout.size()) { + utxoFee = CAmount(tx.vout.size() - tx.vin.size()) * UTXO_FEE; + } } return true; } diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index ed744cffe219..dc22fcfbd23f 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -24,7 +24,7 @@ namespace Consensus { * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool isConnectBlock = false); +[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, CAmount& utxoFee, bool isConnectBlock = false); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/coordinate/coordinate_assets.cpp b/src/coordinate/coordinate_assets.cpp index e69de29bb2d1..a76b349ccabe 100644 --- a/src/coordinate/coordinate_assets.cpp +++ b/src/coordinate/coordinate_assets.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +std::vector CreateAssetId(uint64_t blockNumber, uint16_t assetIndex) { + std::vector assetId; + + // Convert blockNumber → 8 bytes (big-endian) + for (int i = 0; i < 8; ++i) { + assetId.push_back((blockNumber >> (56 - i * 8)) & 0xFF); + } + + // Convert assetIndex → 3-digit string + std::stringstream ss; + ss << std::setw(3) << std::setfill('0') << assetIndex; + std::string indexStr = ss.str(); + + // Append string as ASCII bytes + for (char c : indexStr) { + assetId.push_back(static_cast(c)); + } + + return assetId; +} + +void ParseAssetId(const std::vector& assetId, uint64_t &blockNumber, uint16_t &assetIndex) { + if (assetId.size() != 11) { + throw std::runtime_error("Invalid assetId length"); + } + + // Decode blockNumber (first 8 bytes, big-endian) + blockNumber = 0; + for (int i = 0; i < 8; ++i) { + blockNumber = (blockNumber << 8) | assetId[i]; + } + + // Decode assetIndex (last 3 bytes as string) + std::string indexStr(assetId.begin() + 8, assetId.end()); + assetIndex = static_cast(std::stoi(indexStr)); +} + + +uint256 getAssetHash(const std::vector& assetId) { + unsigned char bytes[32] = {0}; // uint256 requires 32 bytes + + size_t copySize = assetId.size(); + if (copySize > 32) copySize = 32; // truncate if too long + + // Copy bytes into the end of the array (big-endian padding) + std::memcpy(bytes + (32 - copySize), assetId.data(), copySize); + + return uint256{bytes}; +} diff --git a/src/coordinate/coordinate_assets.h b/src/coordinate/coordinate_assets.h index 50f7772b7591..dfa7860984ba 100644 --- a/src/coordinate/coordinate_assets.h +++ b/src/coordinate/coordinate_assets.h @@ -34,7 +34,7 @@ inline void SerializeAsset(const CoordinateType& assetData, Stream& s) struct CoordinateAsset { public: - uint32_t nID; /*!< Asset unique number */ + std::vector nID; /*!< Asset unique buffer */ uint32_t assetType; /*!< Asset type - (e.g. 0 - tokens, 1 - nft, 1 - blob nft) */ uint32_t precision; /*!< Precision Number - (0..8) */ std::string strTicker; /*!< Asset symbol. */ @@ -70,15 +70,20 @@ struct CoordinateAsset { void SetNull() { - nID = 0; + nID.clear(); assetType = 0; precision = 0; strTicker = ""; strHeadline = ""; - payload.SetNull(); - txid.SetNull(); + payload = uint256{}; + txid = uint256{}; nSupply = 0; strController = ""; strOwner = ""; } }; + + +std::vector CreateAssetId(uint64_t blockNumber, uint16_t assetIndex); +void ParseAssetId(const std::vector& assetId, uint64_t &blockNumber, uint16_t &assetIndex); +uint256 getAssetHash(const std::vector& assetId); \ No newline at end of file diff --git a/src/coordinate/coordinate_mempool_entry.cpp b/src/coordinate/coordinate_mempool_entry.cpp index 68b517f29c7a..6c37a2ac7ac5 100644 --- a/src/coordinate/coordinate_mempool_entry.cpp +++ b/src/coordinate/coordinate_mempool_entry.cpp @@ -15,7 +15,7 @@ bool getMempoolAsset(uint256 txid, uint32_t voutIn, CoordinateMempoolEntry* asse if (it == coordinateMempoolEntry.end()) return false; *assetMempoolObj = std::move(*it); - return assetMempoolObj->assetID == 0 ? false : true; + return assetMempoolObj->assetID.empty() ? false : true; } /** @@ -44,20 +44,19 @@ void includeMempoolAsset(const CTransaction& tx, Chainstate& m_active_chainstate { if (tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION) { CoordinateMempoolEntry assetMempoolObj; - assetMempoolObj.assetID = UINT32_MAX; + assetMempoolObj.assetID = {}; assetMempoolObj.txid = tx.GetHash(); assetMempoolObj.vout = 1; assetMempoolObj.nValue = tx.vout[1].nValue; coordinateMempoolEntry.push_back(assetMempoolObj); return; } - uint32_t currentAssetID = 0; + std::vector currentAssetID; CAmount amountAssetIn = 0; bool has_asset_amount = getAssetWithAmount(tx, m_active_chainstate, amountAssetIn, currentAssetID); if (has_asset_amount) { CAmount amountAssetOut = 0; - size_t startValue = tx.version == TRANSACTION_PRECONF_VERSION ? 1 : 0; - for (unsigned long i = startValue; i < tx.vout.size(); i++) { + for (unsigned long i = 0; i < tx.vout.size(); i++) { if (amountAssetOut == amountAssetIn) { break; } @@ -74,11 +73,11 @@ void includeMempoolAsset(const CTransaction& tx, Chainstate& m_active_chainstate /** * This is the function which used to get asset total amount */ -bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, CAmount& amountAssetIn, uint32_t& currentAssetID) +bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, CAmount& amountAssetIn, std::vector& currentAssetID) { CCoinsViewCache& mapInputs = m_active_chainstate.CoinsTip(); for (unsigned int i = 0; i < tx.vin.size(); i++) { - uint32_t nAssetID = 0; + std::vector nAssetID; bool fBitAsset = false; bool fBitAssetControl = false; CoordinateMempoolEntry assetMempoolObj; @@ -90,7 +89,7 @@ bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, Coin coin; if (mapInputs.getAssetCoin(tx.vin[i].prevout, fBitAsset, fBitAssetControl, nAssetID, &coin)) { if (fBitAssetControl) { - currentAssetID = 0; + currentAssetID = {}; break; } if (fBitAsset) { @@ -100,13 +99,13 @@ bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, } - if (!nAssetID) { + if (nAssetID.empty()) { break; } currentAssetID = nAssetID; } - return currentAssetID > 0 ? true : false; + return !currentAssetID.empty() ? true : false; } /** @@ -117,15 +116,14 @@ int getAssetOutputCount(const CTransaction& tx, Chainstate& m_active_chainstate) if (tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION) { return 2; } - if (tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION || tx.version == TRANSACTION_PRECONF_VERSION) { + if (tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION) { uint32_t totalOutputs = 0; - uint32_t currentAssetID = 0; + std::vector currentAssetID; CAmount amountAssetIn = 0; bool has_asset_amount = getAssetWithAmount(tx, m_active_chainstate, amountAssetIn, currentAssetID); if (has_asset_amount) { CAmount amountAssetOut = 0; - size_t startValue = tx.version == TRANSACTION_PRECONF_VERSION ? 1 : 0; - for (unsigned int i = startValue; i < tx.vout.size(); i++) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { if (amountAssetOut == amountAssetIn) { break; } diff --git a/src/coordinate/coordinate_mempool_entry.h b/src/coordinate/coordinate_mempool_entry.h index 0b72cc265b06..9931ac7327f1 100644 --- a/src/coordinate/coordinate_mempool_entry.h +++ b/src/coordinate/coordinate_mempool_entry.h @@ -26,7 +26,7 @@ inline void SerializeCoordinateMempoolEntry(const CoordinateMempoolEntryType& as struct CoordinateMempoolEntry { public: - uint32_t assetID; /*!< Asset unique number */ + std::vector assetID; /*!< Asset unique number */ uint256 txid; /*!< Asset Mempool txid*/ int32_t vout; /*!< Asset Mempool Transaction vout*/ CAmount nValue; /*!< Asset Mempool Transaction value*/ @@ -56,7 +56,7 @@ struct CoordinateMempoolEntry { void SetNull() { - assetID = 0; + assetID.clear(); txid.SetNull(); vout = -1; nValue = -1; @@ -78,7 +78,7 @@ bool getMempoolAsset(uint256 txid, uint32_t voutIn, CoordinateMempoolEntry* asse * @param[in] amountAssetIn returns total asset amount used in transaction * @param[in] currentAssetID returns current asset id */ -bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, CAmount& amountAssetIn, uint32_t& currentAssetID); +bool getAssetWithAmount(const CTransaction& tx, Chainstate& m_active_chainstate, CAmount& amountAssetIn, std::vector& currentAssetID); /** * Remove all asset transaction based on txid diff --git a/src/coordinate/coordinate_preconf.cpp b/src/coordinate/coordinate_preconf.cpp index 50b7b4e98936..953ad13eccb2 100644 --- a/src/coordinate/coordinate_preconf.cpp +++ b/src/coordinate/coordinate_preconf.cpp @@ -21,7 +21,7 @@ std::vector finalizedSignedBlockPeers; CCoinsView coins_view; CCoinsViewCache preconfView(&coins_view); // temporary minfee -CAmount preconfMinFee = 5; +CAmount preconfMinFee = DEFAULT_SIGNED_BLOCK_RESERVE; CoordinatePreConfBlock getNextPreConfSigList(ChainstateManager& chainman) { diff --git a/src/core_write.cpp b/src/core_write.cpp index d631d3c5a7b9..3b6247282b31 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -205,6 +205,7 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry in.pushKV("coinbase", HexStr(txin.scriptSig)); } else { in.pushKV("txid", txin.prevout.hash.GetHex()); + in.pushKV("assetid", HexStr(txin.prevout.assetId)); in.pushKV("vout", (int64_t)txin.prevout.n); UniValue o(UniValue::VOBJ); o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true)); diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp index b94c97d3aee3..f35f3b4109ad 100644 --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -152,7 +152,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block) for (uint32_t j = 0; j < tx->vout.size(); ++j) { const CTxOut& out{tx->vout[j]}; - Coin coin{out, block.height, tx->IsCoinBase(), false, false, false, false, 0}; + Coin coin{out, block.height, tx->IsCoinBase(), false, false, false, false, std::vector{}}; COutPoint outpoint{tx->GetHash(), j}; // Skip unspendable coins @@ -434,7 +434,7 @@ bool CoinStatsIndex::ReverseBlock(const interfaces::BlockInfo& block) for (uint32_t j = 0; j < tx->vout.size(); ++j) { const CTxOut& out{tx->vout[j]}; COutPoint outpoint{tx->GetHash(), j}; - Coin coin{out, block.height, tx->IsCoinBase(), false, false, false, false, 0}; + Coin coin{out, block.height, tx->IsCoinBase(), false, false, false, false, std::vector{}}; // Skip unspendable coins if (coin.out.scriptPubKey.IsUnspendable()) { diff --git a/src/node/miner.cpp b/src/node/miner.cpp index d80475ccde8c..0acc2b7e8d29 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -389,6 +389,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda const int64_t MAX_CONSECUTIVE_FAILURES = 1000; constexpr int32_t BLOCK_FULL_ENOUGH_WEIGHT_DELTA = 4000; int64_t nConsecutiveFailed = 0; + int16_t assetIncr = 0; while (mi != mempool.mapTx.get().end() || !mapModifiedTx.empty()) { // First try to find a new transaction in mapTx to evaluate. @@ -498,6 +499,14 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda SortForBlock(ancestors, sortedEntries); for (size_t i = 0; i < sortedEntries.size(); ++i) { + + CTransactionRef txRef = sortedEntries[i]->GetSharedTx(); + if(txRef->version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION) { + if(assetIncr == NUMOF_NEWASSET_IN_BLOCK) { + continue; + } + assetIncr++; + } AddToBlock(sortedEntries[i]); // Erase from the modified set, if present mapModifiedTx.erase(sortedEntries[i]); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index c505b50e31fe..48fc9b82eb20 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -71,7 +71,13 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) std::vector GetDust(const CTransaction& tx, CFeeRate dust_relay_rate) { std::vector dust_outputs; - for (uint32_t i{0}; i < tx.vout.size(); ++i) { + uint32_t startIndex = 0; + if (tx.version == TRANSACTION_COORDINATE_ASSET_CREATE_VERSION) { + startIndex = 2; + } else if (tx.version == TRANSACTION_PRECONF_VERSION) { + startIndex = 1; + } + for (uint32_t i{startIndex}; i < tx.vout.size(); ++i) { if (IsDust(tx.vout[i], dust_relay_rate)) dust_outputs.push_back(i); } return dust_outputs; @@ -167,7 +173,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional& max_dat // Only MAX_DUST_OUTPUTS_PER_TX dust is permitted(on otherwise valid ephemeral dust) if (GetDust(tx, dust_relay_fee).size() > MAX_DUST_OUTPUTS_PER_TX) { - if (tx.version != TRANSACTION_COORDINATE_ASSET_CREATE_VERSION && tx.version != TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION && tx.version != TRANSACTION_PRECONF_VERSION) { + if (tx.version != TRANSACTION_COORDINATE_ASSET_CREATE_VERSION && tx.version != TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION) { reason = "dust"; return false; } @@ -183,11 +189,11 @@ bool AreCoordinateTransactionStandard(const CTransaction& tx, CCoinsViewCache& m } LogPrintf("transaction version is %i \n", tx.version); CAmount amountAssetInOut = CAmount(0); - uint32_t currentAssetID = 0; + std::vector currentAssetID = {}; for (unsigned int i = 0; i < tx.vin.size(); i++) { bool fBitAsset = false; bool fBitAssetControl = false; - uint32_t nAssetID = 0; + std::vector nAssetID = {}; Coin coin; CAmount coinValue = 0; @@ -210,7 +216,7 @@ bool AreCoordinateTransactionStandard(const CTransaction& tx, CCoinsViewCache& m } - if (tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION || tx.version == TRANSACTION_PRECONF_VERSION) { + if (tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION) { // check first input is asset if (fBitAssetControl) { LogPrintf("Asset controller value not accepted \n"); @@ -223,7 +229,7 @@ bool AreCoordinateTransactionStandard(const CTransaction& tx, CCoinsViewCache& m currentAssetID = nAssetID; } else { // prevent to include multiple asset id - if (currentAssetID != nAssetID) { + if (getAssetHash(currentAssetID) != getAssetHash(nAssetID)) { LogPrintf(" Multiple asset is detected and it is invalid \n"); return false; } @@ -243,14 +249,13 @@ bool AreCoordinateTransactionStandard(const CTransaction& tx, CCoinsViewCache& m return false; } - if (amountAssetInOut > 0 && !(tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION || tx.version == TRANSACTION_PRECONF_VERSION)) { - LogPrintf("Invalid transaction hold asset inptu \n"); + if (amountAssetInOut > 0 && !(tx.version == TRANSACTION_COORDINATE_ASSET_TRANSFER_VERSION)) { + LogPrintf("Invalid transaction hold asset input \n"); } if (amountAssetInOut > 0) { CAmount amountAssetOut = CAmount(0); - size_t startValue = tx.version == TRANSACTION_PRECONF_VERSION ? 1 : 0; - for (unsigned int i = startValue; i < tx.vout.size(); i++) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { if (amountAssetOut == amountAssetInOut) { break; } @@ -265,7 +270,6 @@ bool AreCoordinateTransactionStandard(const CTransaction& tx, CCoinsViewCache& m return true; } - /** * Check the total number of non-witness sigops across the whole transaction, as per BIP54. */ diff --git a/src/policy/policy.h b/src/policy/policy.h index 041ca7527302..0ec20ef33d27 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -6,12 +6,12 @@ #ifndef BITCOIN_POLICY_POLICY_H #define BITCOIN_POLICY_POLICY_H + #include #include #include #include