diff --git a/src/addressindex.h b/src/addressindex.h index 8091c6a6a4..12e49f9de6 100644 --- a/src/addressindex.h +++ b/src/addressindex.h @@ -10,19 +10,23 @@ #include "amount.h" #include "script/script.h" +static const std::string RVN = "RVN"; + struct CAddressUnspentKey { unsigned int type; uint160 hashBytes; + std::string asset; uint256 txhash; size_t index; size_t GetSerializeSize() const { - return 57; + return 57 + asset.size(); } template void Serialize(Stream& s) const { ser_writedata8(s, type); hashBytes.Serialize(s); + ::Serialize(s, asset); txhash.Serialize(s); ser_writedata32(s, index); } @@ -30,6 +34,7 @@ struct CAddressUnspentKey { void Unserialize(Stream& s) { type = ser_readdata8(s); hashBytes.Unserialize(s); + ::Unserialize(s, asset); txhash.Unserialize(s); index = ser_readdata32(s); } @@ -37,6 +42,15 @@ struct CAddressUnspentKey { CAddressUnspentKey(unsigned int addressType, uint160 addressHash, uint256 txid, size_t indexValue) { type = addressType; hashBytes = addressHash; + asset = RVN; + txhash = txid; + index = indexValue; + } + + CAddressUnspentKey(unsigned int addressType, uint160 addressHash, std::string assetName, uint256 txid, size_t indexValue) { + type = addressType; + hashBytes = addressHash; + asset = assetName; txhash = txid; index = indexValue; } @@ -48,6 +62,7 @@ struct CAddressUnspentKey { void SetNull() { type = 0; hashBytes.SetNull(); + asset.clear(); txhash.SetNull(); index = 0; } @@ -91,6 +106,7 @@ struct CAddressUnspentValue { struct CAddressIndexKey { unsigned int type; uint160 hashBytes; + std::string asset; int blockHeight; unsigned int txindex; uint256 txhash; @@ -98,12 +114,13 @@ struct CAddressIndexKey { bool spending; size_t GetSerializeSize() const { - return 66; + return 34 + asset.size(); } template void Serialize(Stream& s) const { ser_writedata8(s, type); hashBytes.Serialize(s); + ::Serialize(s, asset); // Heights are stored big-endian for key sorting in LevelDB ser_writedata32be(s, blockHeight); ser_writedata32be(s, txindex); @@ -116,6 +133,7 @@ struct CAddressIndexKey { void Unserialize(Stream& s) { type = ser_readdata8(s); hashBytes.Unserialize(s); + ::Unserialize(s, asset); blockHeight = ser_readdata32be(s); txindex = ser_readdata32be(s); txhash.Unserialize(s); @@ -128,6 +146,19 @@ struct CAddressIndexKey { uint256 txid, size_t indexValue, bool isSpending) { type = addressType; hashBytes = addressHash; + asset = RVN; + blockHeight = height; + txindex = blockindex; + txhash = txid; + index = indexValue; + spending = isSpending; + } + + CAddressIndexKey(unsigned int addressType, uint160 addressHash, std::string assetName, int height, int blockindex, + uint256 txid, size_t indexValue, bool isSpending) { + type = addressType; + hashBytes = addressHash; + asset = assetName; blockHeight = height; txindex = blockindex; txhash = txid; @@ -142,6 +173,7 @@ struct CAddressIndexKey { void SetNull() { type = 0; hashBytes.SetNull(); + asset.clear(); blockHeight = 0; txindex = 0; txhash.SetNull(); @@ -184,30 +216,85 @@ struct CAddressIndexIteratorKey { } }; +struct CAddressIndexIteratorAssetKey { + unsigned int type; + uint160 hashBytes; + std::string asset; + + size_t GetSerializeSize() const { + return 21 + asset.size(); + } + template + void Serialize(Stream& s) const { + ser_writedata8(s, type); + hashBytes.Serialize(s); + ::Serialize(s, asset); + } + template + void Unserialize(Stream& s) { + type = ser_readdata8(s); + hashBytes.Unserialize(s); + ::Unserialize(s, asset); + } + + CAddressIndexIteratorAssetKey(unsigned int addressType, uint160 addressHash) { + type = addressType; + hashBytes = addressHash; + asset = RVN; + } + + CAddressIndexIteratorAssetKey(unsigned int addressType, uint160 addressHash, std::string assetName) { + type = addressType; + hashBytes = addressHash; + asset = assetName; + } + + CAddressIndexIteratorAssetKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + asset.clear(); + } +}; + struct CAddressIndexIteratorHeightKey { unsigned int type; uint160 hashBytes; + std::string asset; int blockHeight; size_t GetSerializeSize() const { - return 25; + return 25 + asset.size(); } template void Serialize(Stream& s) const { ser_writedata8(s, type); hashBytes.Serialize(s); + ::Serialize(s, asset); ser_writedata32be(s, blockHeight); } template void Unserialize(Stream& s) { type = ser_readdata8(s); hashBytes.Unserialize(s); + ::Unserialize(s, asset); blockHeight = ser_readdata32be(s); } CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, int height) { type = addressType; hashBytes = addressHash; + asset = RVN; + blockHeight = height; + } + + CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, std::string assetName, int height) { + type = addressType; + hashBytes = addressHash; + asset = assetName; blockHeight = height; } @@ -218,6 +305,7 @@ struct CAddressIndexIteratorHeightKey { void SetNull() { type = 0; hashBytes.SetNull(); + asset.clear(); blockHeight = 0; } }; @@ -248,21 +336,43 @@ struct CMempoolAddressDeltaKey { int type; uint160 addressBytes; + std::string asset; uint256 txhash; unsigned int index; int spending; + CMempoolAddressDeltaKey(int addressType, uint160 addressHash, std::string assetName, + uint256 hash, unsigned int i, int s) { + type = addressType; + addressBytes = addressHash; + asset = assetName; + txhash = hash; + index = i; + spending = s; + } + CMempoolAddressDeltaKey(int addressType, uint160 addressHash, uint256 hash, unsigned int i, int s) { type = addressType; addressBytes = addressHash; + asset = ""; txhash = hash; index = i; spending = s; } + CMempoolAddressDeltaKey(int addressType, uint160 addressHash, std::string assetName) { + type = addressType; + addressBytes = addressHash; + asset = assetName; + txhash.SetNull(); + index = 0; + spending = 0; + } + CMempoolAddressDeltaKey(int addressType, uint160 addressHash) { type = addressType; addressBytes = addressHash; + asset = ""; txhash.SetNull(); index = 0; spending = 0; @@ -274,14 +384,18 @@ struct CMempoolAddressDeltaKeyCompare bool operator()(const CMempoolAddressDeltaKey& a, const CMempoolAddressDeltaKey& b) const { if (a.type == b.type) { if (a.addressBytes == b.addressBytes) { - if (a.txhash == b.txhash) { - if (a.index == b.index) { - return a.spending < b.spending; + if (a.asset == b.asset) { + if (a.txhash == b.txhash) { + if (a.index == b.index) { + return a.spending < b.spending; + } else { + return a.index < b.index; + } } else { - return a.index < b.index; + return a.txhash < b.txhash; } } else { - return a.txhash < b.txhash; + return a.asset < b.asset; } } else { return a.addressBytes < b.addressBytes; diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index db8d1b5abc..8e7aaf3ab6 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -2899,4 +2899,61 @@ void GetTxOutAssetTypes(const std::vector& vout, int& issues, int& reiss reissues++; } } +} + +bool ParseAssetScript(CScript scriptPubKey, uint160 &hashBytes, std::string &assetName, CAmount &assetAmount) { + int nType; + bool fIsOwner; + int _nStartingPoint; + std::string _strAddress; + bool isAsset = false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner, _nStartingPoint)) { + if (nType == TX_NEW_ASSET) { + if (fIsOwner) { + if (OwnerAssetFromScript(scriptPubKey, assetName, _strAddress)) { + assetAmount = OWNER_ASSET_AMOUNT; + isAsset = true; + } else { + LogPrintf("%s : Couldn't get new owner asset from script: %s", __func__, HexStr(scriptPubKey)); + } + } else { + CNewAsset asset; + if (AssetFromScript(scriptPubKey, asset, _strAddress)) { + assetName = asset.strName; + assetAmount = asset.nAmount; + isAsset = true; + } else { + LogPrintf("%s : Couldn't get new asset from script: %s", __func__, HexStr(scriptPubKey)); + } + } + } else if (nType == TX_REISSUE_ASSET) { + CReissueAsset asset; + if (ReissueAssetFromScript(scriptPubKey, asset, _strAddress)) { + assetName = asset.strName; + assetAmount = asset.nAmount; + isAsset = true; + } else { + LogPrintf("%s : Couldn't get reissue asset from script: %s", __func__, HexStr(scriptPubKey)); + } + } else if (nType == TX_TRANSFER_ASSET) { + CAssetTransfer asset; + if (TransferAssetFromScript(scriptPubKey, asset, _strAddress)) { + assetName = asset.strName; + assetAmount = asset.nAmount; + isAsset = true; + } else { + LogPrintf("%s : Couldn't get transfer asset from script: %s", __func__, HexStr(scriptPubKey)); + } + } else { + LogPrintf("%s : Unsupported asset type: %s", __func__, nType); + } + } else { +// LogPrintf("%s : Found no asset in script: %s", __func__, HexStr(scriptPubKey)); + } + if (isAsset) { +// LogPrintf("%s : Found assets in script at address %s : %s (%s)", __func__, _strAddress, assetName, assetAmount); + hashBytes = uint160(std::vector (scriptPubKey.begin()+3, scriptPubKey.begin()+23)); + return true; + } + return false; } \ No newline at end of file diff --git a/src/assets/assets.h b/src/assets/assets.h index 1b0603c8fd..988eebbc1d 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -397,4 +397,7 @@ bool CreateAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const s bool CreateReissueAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const CReissueAsset& asset, const std::string& address, std::pair& error, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRequired); bool CreateTransferAssetTransaction(CWallet* pwallet, const CCoinControl& coinControl, const std::vector< std::pair >vTransfers, const std::string& changeAddress, std::pair& error, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRequired); bool SendAssetTransaction(CWallet* pwallet, CWalletTx& transaction, CReserveKey& reserveKey, std::pair& error, std::string& txid); + +/** Helper method for extracting address bytes, asset name and amount from an asset script */ +bool ParseAssetScript(CScript scriptPubKey, uint160 &hashBytes, std::string &assetName, CAmount &assetAmount); #endif //RAVENCOIN_ASSET_PROTOCOL_H diff --git a/src/assets/assettypes.h b/src/assets/assettypes.h index d8ba7d01ec..927d680de9 100644 --- a/src/assets/assettypes.h +++ b/src/assets/assettypes.h @@ -11,6 +11,7 @@ #include #include #include "amount.h" +#include "script/standard.h" #include "primitives/transaction.h" #define MAX_UNIT 8 diff --git a/src/core_write.cpp b/src/core_write.cpp index 97b917a328..b74e0b1302 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -17,6 +17,7 @@ #include "util.h" #include "utilmoneystr.h" #include "utilstrencodings.h" +#include "assets/assets.h" std::string ValueFromAmountString(const CAmount& amount, const int8_t units) { @@ -165,6 +166,54 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("reqSigs", nRequired); out.pushKV("type", GetTxnOutputType(type)); + /** RVN START */ + if (type == TX_NEW_ASSET || type == TX_TRANSFER_ASSET || type == TX_REISSUE_ASSET) { + UniValue assetInfo(UniValue::VOBJ); + + std::string name; + CAmount amount; + std::string _assetAddress; + + if (GetAssetInfoFromScript(scriptPubKey, name, amount)) { + assetInfo.pushKV("name", name); + assetInfo.pushKV("amount", ValueFromAmount(amount)); + + switch (type) { + case TX_NEW_ASSET: + if (IsAssetNameAnOwner(name)) { + // pwnd n00b + } else { + CNewAsset asset; + if (AssetFromScript(scriptPubKey, asset, _assetAddress)) { + assetInfo.pushKV("units", asset.units); + assetInfo.pushKV("reissuable", asset.nReissuable > 0 ? true : false); + if (asset.nHasIPFS > 0) { + assetInfo.pushKV("ipfs_hash", EncodeIPFS(asset.strIPFSHash)); + } + } + } + break; + case TX_TRANSFER_ASSET: + break; + case TX_REISSUE_ASSET: + CReissueAsset asset; + if (ReissueAssetFromScript(scriptPubKey, asset, _assetAddress)) { + if (asset.nUnits >= 0) { + assetInfo.pushKV("units", asset.nUnits); + } + assetInfo.pushKV("reissuable", asset.nReissuable > 0 ? true : false); + if (!asset.strIPFSHash.empty()) { + assetInfo.pushKV("ipfs_hash", EncodeIPFS(asset.strIPFSHash)); + } + } + break; + } + } + + out.pushKV("asset", assetInfo); + } + /** RVN END */ + UniValue a(UniValue::VARR); for (const CTxDestination& addr : addresses) { a.push_back(EncodeDestination(addr)); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4d78cf7b8f..154811267b 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -149,10 +149,13 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblockhashes", 2, "options" }, { "getspentinfo", 0, "txid_index"}, { "getaddresstxids", 0, "addresses"}, + { "getaddresstxids", 1, "includeAssets"}, { "getaddressbalance", 0, "addresses"}, + { "getaddressbalance", 1, "includeAssets"}, { "getaddressdeltas", 0, "addresses"}, { "getaddressutxos", 0, "addresses"}, { "getaddressmempool", 0, "addresses"}, + { "getaddressmempool", 1, "includeAssets"}, { "bumpfee", 1, "options" }, { "logging", 0, "include" }, { "logging", 1, "exclude" }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 765fbea738..30d390779c 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -711,7 +711,7 @@ bool timestampSort(std::pair a, UniValue getaddressmempool(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) + if (request.fHelp || request.params.size() > 2) throw std::runtime_error( "getaddressmempool\n" "\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n" @@ -722,11 +722,13 @@ UniValue getaddressmempool(const JSONRPCRequest& request) " \"address\" (string) The base58check encoded address\n" " ,...\n" " ]\n" - "}\n" + "},\n" + "\"includeAssets\" (boolean, optional, default false) If true this will return an expanded result which includes asset deltas\n" "\nResult:\n" "[\n" " {\n" " \"address\" (string) The base58check encoded address\n" + " \"assetName\" (string) The name of the associated asset (RVN for Ravencoin)\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" " \"satoshis\" (number) The difference of satoshis\n" @@ -738,6 +740,8 @@ UniValue getaddressmempool(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}', true") + + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}, true") ); std::vector > addresses; @@ -746,10 +750,25 @@ UniValue getaddressmempool(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } + bool includeAssets = false; + if (request.params.size() > 1) { + includeAssets = request.params[1].get_bool(); + } + + if (includeAssets) + if (!AreAssetsDeployed()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Assets aren't active. includeAssets can't be true."); + std::vector > indexes; - if (!mempool.getAddressIndex(addresses, indexes)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + if (includeAssets) { + if (!mempool.getAddressIndex(addresses, indexes)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!mempool.getAddressIndex(addresses, RVN, indexes)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } } std::sort(indexes.begin(), indexes.end(), timestampSort); @@ -766,6 +785,7 @@ UniValue getaddressmempool(const JSONRPCRequest& request) UniValue delta(UniValue::VOBJ); delta.push_back(Pair("address", address)); + delta.push_back(Pair("assetName", it->first.asset)); delta.push_back(Pair("txid", it->first.txhash.GetHex())); delta.push_back(Pair("index", (int)it->first.index)); delta.push_back(Pair("satoshis", it->second.amount)); @@ -793,12 +813,14 @@ UniValue getaddressutxos(const JSONRPCRequest& request) " \"address\" (string) The base58check encoded address\n" " ,...\n" " ],\n" - " \"chainInfo\" (boolean) Include chain info with results\n" + " \"chainInfo\", (boolean, optional, default false) Include chain info with results\n" + " \"assetName\" (string, optional) Get UTXOs for a particular asset instead of RVN ('*' for all assets).\n" "}\n" "\nResult\n" "[\n" " {\n" " \"address\" (string) The address base58check encoded\n" + " \"assetName\" (string) The asset associated with the UTXOs (RVN for Ravencoin)\n" " \"txid\" (string) The output txid\n" " \"height\" (number) The block height\n" " \"outputIndex\" (number) The output index\n" @@ -809,14 +831,23 @@ UniValue getaddressutxos(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"],\"assetName\":\"MY_ASSET\"}'") + + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"],\"assetName\":\"MY_ASSET\"}") ); bool includeChainInfo = false; + std::string assetName = RVN; if (request.params[0].isObject()) { UniValue chainInfo = find_value(request.params[0].get_obj(), "chainInfo"); if (chainInfo.isBool()) { includeChainInfo = chainInfo.get_bool(); } + UniValue assetNameParam = find_value(request.params[0].get_obj(), "assetName"); + if (assetNameParam.isStr()) { + if (!AreAssetsDeployed()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Assets aren't active. assetName can't be specified."); + assetName = assetNameParam.get_str(); + } } std::vector > addresses; @@ -828,8 +859,14 @@ UniValue getaddressutxos(const JSONRPCRequest& request) std::vector > unspentOutputs; for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { - if (!GetAddressUnspent((*it).first, (*it).second, unspentOutputs)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + if (assetName == "*") { + if (!GetAddressUnspent((*it).first, (*it).second, unspentOutputs)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressUnspent((*it).first, (*it).second, assetName, unspentOutputs)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } } } @@ -844,7 +881,16 @@ UniValue getaddressutxos(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); } + std::string assetNameOut = "RVN"; + if (assetName != "RVN") { + CAmount _amount; + if (!GetAssetInfoFromScript(it->second.script, assetNameOut, _amount)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't decode asset script"); + } + } + output.push_back(Pair("address", address)); + output.push_back(Pair("assetName", assetNameOut)); output.push_back(Pair("txid", it->first.txhash.GetHex())); output.push_back(Pair("outputIndex", (int)it->first.index)); output.push_back(Pair("script", HexStr(it->second.script.begin(), it->second.script.end()))); @@ -882,10 +928,12 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) " \"start\" (number) The start block height\n" " \"end\" (number) The end block height\n" " \"chainInfo\" (boolean) Include chain info in results, only applies if start and end specified\n" + " \"assetName\" (string, optional) Get deltas for a particular asset instead of RVN.\n" "}\n" "\nResult:\n" "[\n" " {\n" + " \"assetName\" (string) The asset associated with the deltas (RVN for Ravencoin)\n" " \"satoshis\" (number) The difference of satoshis\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" @@ -896,6 +944,8 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"],\"assetName\":\"MY_ASSET\"}'") + + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"],\"assetName\":\"MY_ASSET\"}") ); @@ -908,6 +958,14 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) includeChainInfo = chainInfo.get_bool(); } + std::string assetName = RVN; + UniValue assetNameParam = find_value(request.params[0].get_obj(), "assetName"); + if (assetNameParam.isStr()) { + if (!AreAssetsDeployed()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Assets aren't active. assetName can't be specified."); + assetName = assetNameParam.get_str(); + } + int start = 0; int end = 0; @@ -932,11 +990,11 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { if (start > 0 && end > 0) { - if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) { + if (!GetAddressIndex((*it).first, (*it).second, assetName, addressIndex, start, end)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); } } else { - if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + if (!GetAddressIndex((*it).first, (*it).second, assetName, addressIndex)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); } } @@ -951,6 +1009,7 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) } UniValue delta(UniValue::VOBJ); + delta.push_back(Pair("assetName", it->first.asset)); delta.push_back(Pair("satoshis", it->second)); delta.push_back(Pair("txid", it->first.txhash.GetHex())); delta.push_back(Pair("index", (int)it->first.index)); @@ -993,26 +1052,38 @@ UniValue getaddressdeltas(const JSONRPCRequest& request) UniValue getaddressbalance(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) + if (request.fHelp || request.params.size() > 2) throw std::runtime_error( "getaddressbalance\n" "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n" "\nArguments:\n" "{\n" - " \"addresses\"\n" + " \"addresses:\"\n" " [\n" " \"address\" (string) The base58check encoded address\n" " ,...\n" " ]\n" - "}\n" + "},\n" + "\"includeAssets\" (boolean, optional, default false) If true this will return an expanded result which includes asset balances\n" + "\n" "\nResult:\n" "{\n" " \"balance\" (string) The current balance in satoshis\n" " \"received\" (string) The total number of satoshis received (including change)\n" "}\n" + "OR\n" + "[\n" + " {\n" + " \"assetName\" (string) The asset associated with the balance (RVN for Ravencoin)\n" + " \"balance\" (string) The current balance in satoshis\n" + " \"received\" (string) The total number of satoshis received (including change)\n" + " },...\n" + "\n]" "\nExamples:\n" + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}', true") + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}, true") ); std::vector > addresses; @@ -1021,35 +1092,83 @@ UniValue getaddressbalance(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } - std::vector > addressIndex; + bool includeAssets = false; + if (request.params.size() > 1) { + includeAssets = request.params[1].get_bool(); + } - for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { - if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + if (includeAssets) { + if (!AreAssetsDeployed()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Assets aren't active. includeAssets can't be true."); + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } } - } - CAmount balance = 0; - CAmount received = 0; + //assetName -> (received, balance) + std::map> balances; - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { - if (it->second > 0) { - received += it->second; + for (std::vector >::const_iterator it = addressIndex.begin(); + it != addressIndex.end(); it++) { + std::string assetName = it->first.asset; + if (balances.count(assetName) == 0) { + balances[assetName] = std::make_pair(0, 0); + } + if (it->second > 0) { + balances[assetName].first += it->second; + } + balances[assetName].second += it->second; } - balance += it->second; - } - UniValue result(UniValue::VOBJ); - result.push_back(Pair("balance", balance)); - result.push_back(Pair("received", received)); + UniValue result(UniValue::VARR); - return result; + for (std::map>::const_iterator it = balances.begin(); + it != balances.end(); it++) { + UniValue balance(UniValue::VOBJ); + balance.push_back(Pair("assetName", it->first)); + balance.push_back(Pair("balance", it->second.second)); + balance.push_back(Pair("received", it->second.first)); + result.push_back(balance); + } + + return result; + + } else { + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (!GetAddressIndex((*it).first, (*it).second, RVN, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + + CAmount balance = 0; + CAmount received = 0; + + for (std::vector >::const_iterator it = addressIndex.begin(); + it != addressIndex.end(); it++) { + if (it->second > 0) { + received += it->second; + } + balance += it->second; + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("balance", balance)); + result.push_back(Pair("received", received)); + + return result; + } } UniValue getaddresstxids(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) + if (request.fHelp || request.params.size() > 2) throw std::runtime_error( "getaddresstxids\n" "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n" @@ -1060,9 +1179,10 @@ UniValue getaddresstxids(const JSONRPCRequest& request) " \"address\" (string) The base58check encoded address\n" " ,...\n" " ]\n" - " \"start\" (number) The start block height\n" - " \"end\" (number) The end block height\n" - "}\n" + " \"start\" (number, optional) The start block height\n" + " \"end\" (number, optional) The end block height\n" + "},\n" + "\"includeAssets\" (boolean, optional, default false) If true this will return an expanded result which includes asset transactions\n" "\nResult:\n" "[\n" " \"transactionid\" (string) The transaction id\n" @@ -1071,6 +1191,8 @@ UniValue getaddresstxids(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}', true") + + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}, true") ); std::vector > addresses; @@ -1090,16 +1212,37 @@ UniValue getaddresstxids(const JSONRPCRequest& request) } } + bool includeAssets = false; + if (request.params.size() > 1) { + includeAssets = request.params[1].get_bool(); + } + + if (includeAssets) + if (!AreAssetsDeployed()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Assets aren't active. includeAssets can't be true."); + std::vector > addressIndex; for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { - if (start > 0 && end > 0) { - if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + if (includeAssets) { + if (start > 0 && end > 0) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } } } else { - if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + if (start > 0 && end > 0) { + if (!GetAddressIndex((*it).first, (*it).second, RVN, addressIndex, start, end)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressIndex((*it).first, (*it).second, RVN, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } } } } @@ -1189,11 +1332,11 @@ static const CRPCCommand commands[] = { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, /* Address index */ - { "addressindex", "getaddressmempool", &getaddressmempool, {} }, - { "addressindex", "getaddressutxos", &getaddressutxos, {} }, - { "addressindex", "getaddressdeltas", &getaddressdeltas, {} }, - { "addressindex", "getaddresstxids", &getaddresstxids, {} }, - { "addressindex", "getaddressbalance", &getaddressbalance, {} }, + { "addressindex", "getaddressmempool", &getaddressmempool, {"addresses","includeAssets"} }, + { "addressindex", "getaddressutxos", &getaddressutxos, {"addresses"} }, + { "addressindex", "getaddressdeltas", &getaddressdeltas, {"addresses"} }, + { "addressindex", "getaddresstxids", &getaddresstxids, {"addresses","includeAssets"} }, + { "addressindex", "getaddressbalance", &getaddressbalance, {"addresses","includeAssets"} }, /* Blockchain */ { "blockchain", "getspentinfo", &getspentinfo, {} }, diff --git a/src/txdb.cpp b/src/txdb.cpp index 41fb9d3f0b..00d7d37bf4 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -282,6 +282,33 @@ bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector > &unspentOutputs) { + + boost::scoped_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorAssetKey(type, addressHash, assetName))); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash + && (assetName.empty() || key.second.asset == assetName)) { + CAddressUnspentValue nValue; + if (pcursor->GetValue(nValue)) { + unspentOutputs.push_back(std::make_pair(key.second, nValue)); + pcursor->Next(); + } else { + return error("failed to get address unspent value"); + } + } else { + break; + } + } + + return true; +} + bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector > &unspentOutputs) { @@ -295,7 +322,9 @@ bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash) { CAddressUnspentValue nValue; if (pcursor->GetValue(nValue)) { - unspentOutputs.push_back(std::make_pair(key.second, nValue)); + if (key.second.asset != "RVN") { + unspentOutputs.push_back(std::make_pair(key.second, nValue)); + } pcursor->Next(); } else { return error("failed to get address unspent value"); @@ -322,14 +351,17 @@ bool CBlockTreeDB::EraseAddressIndex(const std::vector > &addressIndex, int start, int end) { boost::scoped_ptr pcursor(NewIterator()); - if (start > 0 && end > 0) { - pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start))); + if (!assetName.empty() && start > 0 && end > 0) { + pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, + CAddressIndexIteratorHeightKey(type, addressHash, assetName, start))); + } else if (!assetName.empty()) { + pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorAssetKey(type, addressHash, assetName))); } else { pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash))); } @@ -337,7 +369,8 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, while (pcursor->Valid()) { boost::this_thread::interruption_point(); std::pair key; - if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash) { + if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash + && (assetName.empty() || key.second.asset == assetName)) { if (end > 0 && key.second.blockHeight > end) { break; } @@ -356,6 +389,13 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, return true; } +bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, + std::vector > &addressIndex, + int start, int end) { + + return CBlockTreeDB::ReadAddressIndex(addressHash, type, "", addressIndex, start, end); +} + bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) { CDBBatch batch(*this); batch.Write(std::make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0); diff --git a/src/txdb.h b/src/txdb.h index a976fd831f..03a743dfb7 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -128,10 +128,15 @@ class CBlockTreeDB : public CDBWrapper bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); bool UpdateSpentIndex(const std::vector >&vect); bool UpdateAddressUnspentIndex(const std::vector >&vect); + bool ReadAddressUnspentIndex(uint160 addressHash, int type, std::string assetName, + std::vector > &vect); bool ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector > &vect); bool WriteAddressIndex(const std::vector > &vect); bool EraseAddressIndex(const std::vector > &vect); + bool ReadAddressIndex(uint160 addressHash, int type, std::string assetName, + std::vector > &addressIndex, + int start = 0, int end = 0); bool ReadAddressIndex(uint160 addressHash, int type, std::vector > &addressIndex, int start = 0, int end = 0); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 6a9e796ecf..3c0fc0a677 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -430,22 +430,36 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC const CTxOut &prevout = view.AccessCoin(input.prevout).out; if (prevout.scriptPubKey.IsPayToScriptHash()) { std::vector hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22); - CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDeltaKey key(2, uint160(hashBytes), RVN, txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(std::make_pair(key, delta)); inserted.push_back(key); } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { std::vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); - CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDeltaKey key(1, uint160(hashBytes), RVN, txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(std::make_pair(key, delta)); inserted.push_back(key); } else if (prevout.scriptPubKey.IsPayToPublicKey()) { uint160 hashBytes(Hash160(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.end()-1)); - CMempoolAddressDeltaKey key(1, hashBytes, txhash, j, 1); + CMempoolAddressDeltaKey key(1, hashBytes, RVN, txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(std::make_pair(key, delta)); inserted.push_back(key); + } else { + /** RVN START */ + if (AreAssetsDeployed()) { + uint160 hashBytes; + std::string assetName; + CAmount assetAmount; + if (ParseAssetScript(prevout.scriptPubKey, hashBytes, assetName, assetAmount)) { + CMempoolAddressDeltaKey key(1, hashBytes, assetName, txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), assetAmount * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(std::make_pair(key, delta)); + inserted.push_back(key); + } + } + /** RVN END */ } } @@ -453,27 +467,57 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC const CTxOut &out = tx.vout[k]; if (out.scriptPubKey.IsPayToScriptHash()) { std::vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); - CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0); + CMempoolAddressDeltaKey key(2, uint160(hashBytes), RVN, txhash, k, 0); mapAddress.insert(std::make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { std::vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); std::pair ret; - CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0); + CMempoolAddressDeltaKey key(1, uint160(hashBytes), RVN, txhash, k, 0); mapAddress.insert(std::make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); } else if (out.scriptPubKey.IsPayToPublicKey()) { uint160 hashBytes(Hash160(out.scriptPubKey.begin()+1, out.scriptPubKey.end()-1)); std::pair ret; - CMempoolAddressDeltaKey key(1, hashBytes, txhash, k, 0); + CMempoolAddressDeltaKey key(1, hashBytes, RVN, txhash, k, 0); mapAddress.insert(std::make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); + } else { + /** RVN START */ + if (AreAssetsDeployed()) { + uint160 hashBytes; + std::string assetName; + CAmount assetAmount; + if (ParseAssetScript(out.scriptPubKey, hashBytes, assetName, assetAmount)) { + std::pair ret; + CMempoolAddressDeltaKey key(1, hashBytes, assetName, txhash, k, 0); + mapAddress.insert(std::make_pair(key, CMempoolAddressDelta(entry.GetTime(), assetAmount))); + inserted.push_back(key); + } + } + /** RVN END */ } } mapAddressInserted.insert(std::make_pair(txhash, inserted)); } +bool CTxMemPool::getAddressIndex(std::vector > &addresses, std::string assetName, + std::vector > &results) +{ + LOCK(cs); + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + addressDeltaMap::iterator ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first, + assetName)); + while (ait != mapAddress.end() && (*ait).first.addressBytes == (*it).first && (*ait).first.type == (*it).second + && (*ait).first.asset == assetName) { + results.push_back(*ait); + ait++; + } + } + return true; +} + bool CTxMemPool::getAddressIndex(std::vector > &addresses, std::vector > &results) { diff --git a/src/txmempool.h b/src/txmempool.h index 42c07f5562..fba8c18814 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -535,6 +535,8 @@ class CTxMemPool bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true); void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); + bool getAddressIndex(std::vector > &addresses, std::string assetName, + std::vector > &results); bool getAddressIndex(std::vector > &addresses, std::vector > &results); bool removeAddressIndex(const uint256 txhash); diff --git a/src/validation.cpp b/src/validation.cpp index cf078b51a7..b7cb530c74 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1004,6 +1004,18 @@ bool HashOnchainActive(const uint256 &hash) return true; } +bool GetAddressIndex(uint160 addressHash, int type, std::string assetName, + std::vector > &addressIndex, int start, int end) +{ + if (!fAddressIndex) + return error("address index not enabled"); + + if (!pblocktree->ReadAddressIndex(addressHash, type, assetName, addressIndex, start, end)) + return error("unable to get txids for address"); + + return true; +} + bool GetAddressIndex(uint160 addressHash, int type, std::vector > &addressIndex, int start, int end) { @@ -1016,6 +1028,18 @@ bool GetAddressIndex(uint160 addressHash, int type, return true; } +bool GetAddressUnspent(uint160 addressHash, int type, std::string assetName, + std::vector > &unspentOutputs) +{ + if (!fAddressIndex) + return error("address index not enabled"); + + if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, assetName, unspentOutputs)) + return error("unable to get txids for address"); + + return true; +} + bool GetAddressUnspent(uint160 addressHash, int type, std::vector > &unspentOutputs) { @@ -1671,11 +1695,32 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* addressIndex.push_back(std::make_pair(CAddressIndexKey(1, hashBytes, pindex->nHeight, i, hash, k, false), out.nValue)); addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(1, hashBytes, hash, k), CAddressUnspentValue())); } else { - continue; + /** RVN START */ + if (AreAssetsDeployed()) { + std::string assetName; + CAmount assetAmount; + uint160 hashBytes; + + if (ParseAssetScript(out.scriptPubKey, hashBytes, assetName, assetAmount)) { +// std::cout << "ConnectBlock(): pushing assets onto addressIndex: " << "1" << ", " << hashBytes.GetHex() << ", " << assetName << ", " << pindex->nHeight +// << ", " << i << ", " << hash.GetHex() << ", " << k << ", " << "true" << ", " << assetAmount << std::endl; + + // undo receiving activity + addressIndex.push_back(std::make_pair( + CAddressIndexKey(1, uint160(hashBytes), assetName, pindex->nHeight, i, hash, k, + false), assetAmount)); + + // undo unspent index + addressUnspentIndex.push_back( + std::make_pair(CAddressUnspentKey(1, uint160(hashBytes), assetName, hash, k), + CAddressUnspentValue())); + } else { + continue; + } + } + /** RVN END */ } - } - } // Check that all outputs are available and match the outputs in the block itself @@ -1839,7 +1884,31 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* addressIndex.push_back(std::make_pair(CAddressIndexKey(1, hashBytes, pindex->nHeight, i, hash, j, false), prevout.nValue)); addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(1, hashBytes, hash, j), CAddressUnspentValue())); } else { - continue; + /** RVN START */ + if (AreAssetsDeployed()) { + std::string assetName; + CAmount assetAmount; + uint160 hashBytes; + + if (ParseAssetScript(prevout.scriptPubKey, hashBytes, assetName, assetAmount)) { +// std::cout << "ConnectBlock(): pushing assets onto addressIndex: " << "1" << ", " << hashBytes.GetHex() << ", " << assetName << ", " << pindex->nHeight +// << ", " << i << ", " << hash.GetHex() << ", " << j << ", " << "true" << ", " << assetAmount * -1 << std::endl; + + // undo spending activity + addressIndex.push_back(std::make_pair( + CAddressIndexKey(1, uint160(hashBytes), assetName, pindex->nHeight, i, hash, j, + true), assetAmount * -1)); + + // restore unspent index + addressUnspentIndex.push_back(std::make_pair( + CAddressUnspentKey(1, uint160(hashBytes), assetName, input.prevout.hash, + input.prevout.n), + CAddressUnspentValue(assetAmount, prevout.scriptPubKey, undo.nHeight))); + } else { + continue; + } + } + /** RVN END */ } } } @@ -2177,6 +2246,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd const CTxOut &prevout = view.AccessCoin(tx.vin[j].prevout).out; uint160 hashBytes; int addressType; + bool isAsset = false; + std::string assetName; + CAmount assetAmount; if (prevout.scriptPubKey.IsPayToScriptHash()) { hashBytes = uint160(std::vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); @@ -2185,20 +2257,43 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd hashBytes = uint160(std::vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); addressType = 1; } else if (prevout.scriptPubKey.IsPayToPublicKey()) { - hashBytes = Hash160(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.end()-1); + hashBytes = Hash160(prevout.scriptPubKey.begin() + 1, prevout.scriptPubKey.end() - 1); addressType = 1; } else { - hashBytes.SetNull(); - addressType = 0; + /** RVN START */ + if (AreAssetsDeployed()) { + hashBytes.SetNull(); + addressType = 0; + + if (ParseAssetScript(prevout.scriptPubKey, hashBytes, assetName, assetAmount)) { + addressType = 1; + isAsset = true; + } + } + /** RVN END */ } if (fAddressIndex && addressType > 0) { - // record spending activity - addressIndex.push_back(std::make_pair(CAddressIndexKey(addressType, hashBytes, pindex->nHeight, i, txhash, j, true), prevout.nValue * -1)); - - // remove address from unspent index - addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(addressType, hashBytes, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); + /** RVN START */ + if (isAsset) { +// std::cout << "ConnectBlock(): pushing assets onto addressIndex: " << "1" << ", " << hashBytes.GetHex() << ", " << assetName << ", " << pindex->nHeight +// << ", " << i << ", " << txhash.GetHex() << ", " << j << ", " << "true" << ", " << assetAmount * -1 << std::endl; + + // record spending activity + addressIndex.push_back(std::make_pair(CAddressIndexKey(addressType, hashBytes, assetName, pindex->nHeight, i, txhash, j, true), assetAmount * -1)); + + // remove address from unspent index + addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(addressType, hashBytes, assetName, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); + /** RVN END */ + } else { + // record spending activity + addressIndex.push_back(std::make_pair(CAddressIndexKey(addressType, hashBytes, pindex->nHeight, i, txhash, j, true), prevout.nValue * -1)); + + // remove address from unspent index + addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(addressType, hashBytes, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); + } } + /** RVN END */ if (fSpentIndex) { // add the spent index to determine the txid and input that spent an output @@ -2320,13 +2415,40 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); } else if (out.scriptPubKey.IsPayToPublicKey()) { - uint160 hashBytes(Hash160(out.scriptPubKey.begin()+1, out.scriptPubKey.end()-1)); - addressIndex.push_back(std::make_pair(CAddressIndexKey(1, hashBytes, pindex->nHeight, i, txhash, k, false), out.nValue)); - addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(1, hashBytes, txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + uint160 hashBytes(Hash160(out.scriptPubKey.begin() + 1, out.scriptPubKey.end() - 1)); + addressIndex.push_back( + std::make_pair(CAddressIndexKey(1, hashBytes, pindex->nHeight, i, txhash, k, false), + out.nValue)); + addressUnspentIndex.push_back(std::make_pair(CAddressUnspentKey(1, hashBytes, txhash, k), + CAddressUnspentValue(out.nValue, out.scriptPubKey, + pindex->nHeight))); } else { - continue; + /** RVN START */ + if (AreAssetsDeployed()) { + std::string assetName; + CAmount assetAmount; + uint160 hashBytes; + + if (ParseAssetScript(out.scriptPubKey, hashBytes, assetName, assetAmount)) { +// std::cout << "ConnectBlock(): pushing assets onto addressIndex: " << "1" << ", " << hashBytes.GetHex() << ", " << assetName << ", " << pindex->nHeight +// << ", " << i << ", " << txhash.GetHex() << ", " << k << ", " << "true" << ", " << assetAmount << std::endl; + + // record receiving activity + addressIndex.push_back(std::make_pair( + CAddressIndexKey(1, hashBytes, assetName, pindex->nHeight, i, txhash, k, false), + assetAmount)); + + // record unspent output + addressUnspentIndex.push_back( + std::make_pair(CAddressUnspentKey(1, hashBytes, assetName, txhash, k), + CAddressUnspentValue(assetAmount, out.scriptPubKey, + pindex->nHeight))); + } + } else { + continue; + } + /** RVN END */ } - } } diff --git a/src/validation.h b/src/validation.h index 9869cfd23e..ed37bbc6f6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -415,9 +415,14 @@ void InitScriptExecutionCache(); bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes); bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); bool HashOnchainActive(const uint256 &hash); +bool GetAddressIndex(uint160 addressHash, int type, std::string assetName, + std::vector > &addressIndex, + int start = 0, int end = 0); bool GetAddressIndex(uint160 addressHash, int type, std::vector > &addressIndex, int start = 0, int end = 0); +bool GetAddressUnspent(uint160 addressHash, int type, std::string assetName, + std::vector > &unspentOutputs); bool GetAddressUnspent(uint160 addressHash, int type, std::vector > &unspentOutputs); diff --git a/test/functional/feature_rawassettransactions.py b/test/functional/feature_rawassettransactions.py index f949f042f7..460f6d0a45 100755 --- a/test/functional/feature_rawassettransactions.py +++ b/test/functional/feature_rawassettransactions.py @@ -750,7 +750,7 @@ def issue_invalid_address_test(self): } hex = n0.createrawtransaction(inputs, outputs) signed_hex = n0.signrawtransaction(hex)['hex'] - assert_raises_rpc_error(-26, "bad-txns-issue-burn-not-found", \ + assert_raises_rpc_error(-26, "bad-txns-issue-owner-burn-not-found", \ n0.sendrawtransaction, signed_hex) ############################################ @@ -772,7 +772,7 @@ def issue_invalid_address_test(self): } hex = n0.createrawtransaction(inputs, outputs) signed_hex = n0.signrawtransaction(hex)['hex'] - assert_raises_rpc_error(-26, "bad-txns-issue-burn-not-found", \ + assert_raises_rpc_error(-26, "bad-txns-issue-owner-burn-not-found", \ n0.sendrawtransaction, signed_hex) ############################################ @@ -794,7 +794,7 @@ def issue_invalid_address_test(self): } hex = n0.createrawtransaction(inputs, outputs) signed_hex = n0.signrawtransaction(hex)['hex'] - assert_raises_rpc_error(-26, "bad-txns-issue-burn-not-found", \ + assert_raises_rpc_error(-26, "bad-txns-issue-owner-burn-not-found", \ n0.sendrawtransaction, signed_hex) ############################################ @@ -1679,6 +1679,74 @@ def atomic_swaps_test(self): assert_equal(unspent_asset_amount2 - amount, n2.listmyassets()[anduin]) + def getrawtransaction(self): + self.log.info("Testing asset info in getrawtransaction...") + n0 = self.nodes[0] + + asset_name = "RAW" + asset_amount = 1000 + units = 2 + units2 = 4 + reissuable = True + ipfs_hash = "QmTqu3Lk3gmTsQVtjU7rYYM37EAW4xNmbuEAp2Mjr4AV7E" + ipfs_hash2 = "QmQ7DysAQmy92cyQrkb5y1M96pGG1fKxnRkiB19qWSmH75" + + tx_id = n0.issue(asset_name, asset_amount, "", "", units, reissuable, True, ipfs_hash)[0] + n0.generate(1) + raw_json = n0.getrawtransaction(tx_id, True) + asset_out_script = raw_json['vout'][-1]['scriptPubKey'] + assert_contains_key('asset', asset_out_script) + asset_section = asset_out_script['asset'] + assert_equal(asset_name, asset_section['name']) + assert_equal(asset_amount, asset_section['amount']) + assert_equal(units, asset_section['units']); + assert_equal(reissuable, asset_section['reissuable']) + assert_equal(ipfs_hash, asset_section['ipfs_hash']) + + asset_out_script = raw_json['vout'][-2]['scriptPubKey'] + assert_contains_key('asset', asset_out_script) + asset_section = asset_out_script['asset'] + assert_equal(asset_name + "!", asset_section['name']) + assert_equal(1, asset_section['amount']) + assert_does_not_contain_key('units', asset_section) + assert_does_not_contain_key('reissuable', asset_section) + assert_does_not_contain_key('ipfs_hash', asset_section) + + address = n0.getnewaddress() + tx_id = n0.reissue(asset_name, asset_amount, address, "", True, -1, ipfs_hash2)[0] + n0.generate(1) + raw_json = n0.getrawtransaction(tx_id, True) + asset_out_script = raw_json['vout'][-1]['scriptPubKey'] + assert_contains_key('asset', asset_out_script) + asset_section = asset_out_script['asset'] + assert_equal(asset_name, asset_section['name']) + assert_equal(asset_amount, asset_section['amount']) + assert_does_not_contain_key('units', asset_section) + assert_equal(ipfs_hash2, asset_section['ipfs_hash']) + + address = n0.getnewaddress() + tx_id = n0.reissue(asset_name, asset_amount, address, "", False, units2)[0] + n0.generate(1) + raw_json = n0.getrawtransaction(tx_id, True) + asset_out_script = raw_json['vout'][-1]['scriptPubKey'] + assert_contains_key('asset', asset_out_script) + asset_section = asset_out_script['asset'] + assert_equal(asset_name, asset_section['name']) + assert_equal(asset_amount, asset_section['amount']) + assert_equal(units2, asset_section['units']) + assert_does_not_contain_key('ipfs_hash', asset_section) + + address = n0.getnewaddress() + tx_id = n0.transfer(asset_name, asset_amount, address)[0] + n0.generate(1) + raw_json = n0.getrawtransaction(tx_id, True) + asset_out_script = raw_json['vout'][1]['scriptPubKey'] + assert_contains_key('asset', asset_out_script) + asset_section = asset_out_script['asset'] + assert_equal(asset_name, asset_section['name']) + assert_equal(asset_amount, asset_section['amount']) + + def run_test(self): self.activate_assets() self.issue_reissue_transfer_test() @@ -1694,6 +1762,8 @@ def run_test(self): self.issue_sub_invalid_address_test() self.issue_multiple_outputs_test() self.issue_sub_multiple_outputs_test() + self.getrawtransaction() + if __name__ == '__main__': diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 389a688d6e..7b4c7a8fd1 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -38,6 +38,10 @@ def assert_contains_pair(key, val, dict): if not (key in dict and val == dict[key]): raise AssertionError("k/v pair (%s,%s) not in dict" % (key, val)) +def assert_contains_key(key, dict): + if not (key in dict): + raise AssertionError("key %s is not in dict" % (key)) + def assert_does_not_contain_key(key, dict): if (key in dict): raise AssertionError("key %s is in dict" % (key))