Skip to content

Commit

Permalink
Merge pull request #81 from DeFiCh/feature/listaccounthistory
Browse files Browse the repository at this point in the history
rpc listaccounthistory
  • Loading branch information
monstrobishi authored Oct 31, 2020
2 parents d989a74 + d48988e commit 4d8d4c4
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ DEFI_CORE_H = \
limitedmap.h \
logging.h \
masternodes/accounts.h \
masternodes/accountshistory.h \
masternodes/anchors.h \
masternodes/balances.h \
masternodes/communityaccounttypes.h \
Expand Down Expand Up @@ -362,6 +363,7 @@ libdefi_server_a_SOURCES = \
init.cpp \
dbwrapper.cpp \
masternodes/accounts.cpp \
masternodes/accountshistory.cpp \
masternodes/anchors.cpp \
masternodes/criminals.cpp \
masternodes/govvariables/lp_daily_dfi_reward.cpp \
Expand Down
2 changes: 1 addition & 1 deletion src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
const auto txType = GuessCustomTxType(tx, dummy);

if (NotAllowedToFail(txType)) {
auto res = ApplyCustomTx(const_cast<CCustomCSView&>(*mnview), inputs, tx, Params(), nSpendHeight, true); // note for 'isCheck == true' here
auto res = ApplyCustomTx(const_cast<CCustomCSView&>(*mnview), inputs, tx, Params(), nSpendHeight, 0, true); // note for 'isCheck == true' here; 'zero' for txn is dummy value
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-customtx", res.msg);
}
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ void SetupServerArgs()
hidden_args.emplace_back("-sysperms");
#endif
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-acindex", strprintf("Maintain a full account history index, tracking all accounts balances changes. Used by the listaccounthistory rpc call (default: %u)", false), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
Expand Down
1 change: 1 addition & 0 deletions src/masternodes/accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ Res CAccountsView::SubBalances(CScript const & owner, CBalances const & balances
}
return Res::Ok();
}

59 changes: 59 additions & 0 deletions src/masternodes/accountshistory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2020 DeFi Blockchain Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <masternodes/accountshistory.h>
#include <masternodes/accounts.h>
#include <key_io.h>

/// @attention make sure that it does not overlap with those in masternodes.cpp/tokens.cpp/undos.cpp/accounts.cpp !!!
const unsigned char CAccountsHistoryView::ByAccountHistoryKey::prefix = 'h'; // don't intersects with CMintedHeadersView::MintedHeaders::prefix due to different DB

void CAccountsHistoryView::ForEachAccountHistory(std::function<bool(CScript const & owner, uint32_t height, uint32_t txn, uint256 const & txid, unsigned char category, TAmounts const & diffs)> callback, AccountHistoryKey start) const
{
ForEach<ByAccountHistoryKey, AccountHistoryKey, AccountHistoryValue>([&callback] (AccountHistoryKey const & key, AccountHistoryValue const & val) {
return callback(key.owner,key.blockHeight, key.txn, val.txid, val.category, val.diff);
}, start);
}

Res CAccountsHistoryView::SetAccountHistory(const CScript & owner, uint32_t height, uint32_t txn, const uint256 & txid, unsigned char category, TAmounts const & diff)
{
//// left for debug:
// std::string ownerStr;
// CTxDestination dest;
// if (!ExtractDestination(owner, dest)) {
// ownerStr = owner.GetHex();
// } else
// ownerStr = EncodeDestination(dest);
// LogPrintf("DEBUG: SetAccountHistory: owner: %s, ownerStr: %s, block: %i, txn: %d, txid: %s, diffs: %ld\n", owner.GetHex().c_str(), ownerStr.c_str(), height, txn, txid.ToString().c_str(), diff.size());

WriteBy<ByAccountHistoryKey>(AccountHistoryKey{owner, height, txn}, AccountHistoryValue{txid, category, diff});
return Res::Ok();
}

bool CAccountsHistoryView::TrackAffectedAccounts(CStorageKV const & before, MapKV const & diff, uint32_t height, uint32_t txn, const uint256 & txid, unsigned char category) {
if (!gArgs.GetBoolArg("-acindex", false))
return false;

std::map<CScript, TAmounts> balancesDiff;
using TKey = std::pair<unsigned char, BalanceKey>;

for (auto it = diff.lower_bound({CAccountsView::ByBalanceKey::prefix}); it != diff.end() && it->first.at(0) == CAccountsView::ByBalanceKey::prefix; ++it) {
CAmount oldAmount = 0, newAmount = 0;

if (it->second) {
BytesToDbType(*it->second, newAmount);
}
TBytes beforeVal;
if (before.Read(it->first, beforeVal)) {
BytesToDbType(beforeVal, oldAmount);
}
TKey balanceKey;
BytesToDbType(it->first, balanceKey);
balancesDiff[balanceKey.second.owner][balanceKey.second.tokenID] = newAmount - oldAmount;
}
for (auto const & kv : balancesDiff) {
SetAccountHistory(kv.first, height, txn, txid, category, kv.second);
}
return true;
}
67 changes: 67 additions & 0 deletions src/masternodes/accountshistory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2020 DeFi Blockchain Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef DEFI_MASTERNODES_ACCOUNTSHISTORY_H
#define DEFI_MASTERNODES_ACCOUNTSHISTORY_H

#include <flushablestorage.h>
#include <masternodes/res.h>
#include <amount.h>
#include <script/script.h>
#include <uint256.h>


struct AccountHistoryKey {
CScript owner;
uint32_t blockHeight;
uint32_t txn; // for order in block

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(owner);

if (ser_action.ForRead()) {
READWRITE(WrapBigEndian(blockHeight));
blockHeight = ~blockHeight;
READWRITE(WrapBigEndian(txn));
txn = ~txn;
}
else {
uint32_t blockHeight_ = ~blockHeight;
READWRITE(WrapBigEndian(blockHeight_));
uint32_t txn_ = ~txn;
READWRITE(WrapBigEndian(txn_));
}
}
};

struct AccountHistoryValue {
uint256 txid;
unsigned char category;
TAmounts diff;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(txid);
READWRITE(category);
READWRITE(diff);
}
};

class CAccountsHistoryView : public virtual CStorageView
{
public:
Res SetAccountHistory(CScript const & owner, uint32_t height, uint32_t txn, uint256 const & txid, unsigned char category, TAmounts const & diff);
void ForEachAccountHistory(std::function<bool(CScript const & owner, uint32_t height, uint32_t txn, uint256 const & txid, unsigned char category, TAmounts const & diff)> callback, AccountHistoryKey start) const;
bool TrackAffectedAccounts(CStorageKV const & before, MapKV const & diff, uint32_t height, uint32_t txn, const uint256 & txid, unsigned char category);

// tags
struct ByAccountHistoryKey { static const unsigned char prefix; };
};

#endif //DEFI_MASTERNODES_ACCOUNTSHISTORY_H
1 change: 0 additions & 1 deletion src/masternodes/balances.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,4 @@ struct BalanceKey {
READWRITE(WrapBigEndian(tokenID.v));
}
};

#endif //DEFI_MASTERNODES_BALANCES_H
2 changes: 2 additions & 0 deletions src/masternodes/masternodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <pubkey.h>
#include <serialize.h>
#include <masternodes/accounts.h>
#include <masternodes/accountshistory.h>
#include <masternodes/incentivefunding.h>
#include <masternodes/tokens.h>
#include <masternodes/undos.h>
Expand Down Expand Up @@ -183,6 +184,7 @@ class CCustomCSView
, public CAnchorRewardsView
, public CTokensView
, public CAccountsView
, public CAccountsHistoryView
, public CCommunityBalancesView
, public CUndosView
, public CPoolPairView
Expand Down
7 changes: 4 additions & 3 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ bool HasFoundationAuth(CTransaction const & tx, CCoinsViewCache const & coins, C
return false;
}

Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CTransaction const & tx, Consensus::Params const & consensusParams, uint32_t height, bool isCheck)
Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CTransaction const & tx, Consensus::Params const & consensusParams, uint32_t height, uint32_t txn, bool isCheck)
{
Res res = Res::Ok();

Expand All @@ -124,11 +124,11 @@ Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CT
}

CCustomCSView mnview(base_mnview);

CustomTxType guess;
try {
// Check if it is custom tx with metadata
std::vector<unsigned char> metadata;
CustomTxType guess = GuessCustomTxType(tx, metadata);
guess = GuessCustomTxType(tx, metadata);
switch (guess)
{
case CustomTxType::CreateMasternode:
Expand Down Expand Up @@ -195,6 +195,7 @@ Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CT

// construct undo
auto& flushable = dynamic_cast<CFlushableStorageKV&>(mnview.GetRaw());
mnview.TrackAffectedAccounts(base_mnview.GetRaw(), flushable.GetRaw(), height, txn, tx.GetHash(), (unsigned char) guess);
auto undo = CUndo::Construct(base_mnview.GetRaw(), flushable.GetRaw());
// flush changes
mnview.Flush();
Expand Down
32 changes: 29 additions & 3 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,43 @@ enum class CustomTxType : unsigned char
AccountToUtxos = 'b',
AccountToAccount = 'B',
//set governance variable
SetGovVariable = 'G'
SetGovVariable = 'G',

// this is not the real tx type (!) but special category for accounts/history tracking
NonTxRewards = '+'
};

inline CustomTxType CustomTxCodeToType(unsigned char ch) {
char const txtypes[] = "CRTMNnpuslrUbBG";
char const txtypes[] = "CRTMNnpuslrUbBG+";
if (memchr(txtypes, ch, strlen(txtypes)))
return static_cast<CustomTxType>(ch);
else
return CustomTxType::None;
}

inline std::string ToString(CustomTxType type) {
switch (type)
{
case CustomTxType::CreateMasternode: return "CreateMasternode";
case CustomTxType::ResignMasternode: return "ResignMasternode";
case CustomTxType::CreateToken: return "CreateToken";
case CustomTxType::UpdateToken: return "UpdateToken";
case CustomTxType::UpdateTokenAny: return "UpdateTokenAny";
case CustomTxType::MintToken: return "MintToken";
case CustomTxType::CreatePoolPair: return "CreatePoolPair";
case CustomTxType::UpdatePoolPair: return "UpdatePoolPair";
case CustomTxType::PoolSwap: return "PoolSwap";
case CustomTxType::AddPoolLiquidity: return "AddPoolLiquidity";
case CustomTxType::RemovePoolLiquidity: return "RemovePoolLiquidity";
case CustomTxType::UtxosToAccount: return "UtxosToAccount";
case CustomTxType::AccountToUtxos: return "AccountToUtxos";
case CustomTxType::AccountToAccount: return "AccountToAccount";
case CustomTxType::SetGovVariable: return "SetGovVariable";
case CustomTxType::NonTxRewards: return "Rewards";
default: return "None";
}
}

inline bool NotAllowedToFail(CustomTxType txType) {
return txType == CustomTxType::MintToken || txType == CustomTxType::AccountToUtxos;
}
Expand All @@ -85,7 +111,7 @@ bool HasAuth(CTransaction const & tx, CKeyID const & auth);
bool HasAuth(CTransaction const & tx, CCoinsViewCache const & coins, CScript const & auth);
bool HasCollateralAuth(CTransaction const & tx, CCoinsViewCache const & coins, uint256 const & collateralTx);

Res ApplyCustomTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, const Consensus::Params& consensusParams, uint32_t height, bool isCheck = true);
Res ApplyCustomTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, const Consensus::Params& consensusParams, uint32_t height, uint32_t txn, bool isCheck = true);
//! Deep check (and write)
Res ApplyCreateMasternodeTx(CCustomCSView & mnview, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);
Res ApplyResignMasternodeTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);
Expand Down
Loading

0 comments on commit 4d8d4c4

Please sign in to comment.