refactor: untie governance/object and wallet implementation

This commit is contained in:
Konstantin Akimov 2023-12-01 16:59:14 +07:00 committed by pasta
parent 78a69043c6
commit 64a153d634
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
11 changed files with 223 additions and 139 deletions

View File

@ -187,6 +187,7 @@ BITCOIN_CORE_H = \
dsnotificationinterface.h \
governance/governance.h \
governance/classes.h \
governance/common.h \
governance/exceptions.h \
governance/object.h \
governance/validators.h \
@ -702,6 +703,7 @@ libbitcoin_common_a_SOURCES = \
core_read.cpp \
core_write.cpp \
deploymentinfo.cpp \
governance/common.cpp \
key.cpp \
key_io.cpp \
merkleblock.cpp \

73
src/governance/common.cpp Normal file
View File

@ -0,0 +1,73 @@
// Copyright (c) 2014-2023 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <governance/common.h>
#include <util/strencodings.h>
#include <util/underlying.h>
#include <hash.h>
#include <univalue.h>
namespace Governance
{
Object::Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex) :
hashParent{nHashParent},
revision{nRevision},
time{nTime},
collateralHash{nCollateralHash},
masternodeOutpoint{},
vchSig{},
vchData{ParseHex(strDataHex)}
{
}
uint256 Object::GetHash() const
{
// Note: doesn't match serialization
// CREATE HASH OF ALL IMPORTANT PIECES OF DATA
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
ss << hashParent;
ss << revision;
ss << time;
ss << HexStr(vchData);
ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing
ss << vchSig;
// fee_tx is left out on purpose
return ss.GetHash();
}
UniValue Object::ToJson() const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("objectHash", GetHash().ToString());
obj.pushKV("parentHash", hashParent.ToString());
obj.pushKV("collateralHash", collateralHash.ToString());
obj.pushKV("createdAt", time);
obj.pushKV("revision", revision);
UniValue data;
if (!data.read(GetDataAsPlainString())) {
data.clear();
data.setObject();
data.pushKV("plain", GetDataAsPlainString());
}
data.pushKV("hex", GetDataAsHexString());
obj.pushKV("data", data);
return obj;
}
std::string Object::GetDataAsHexString() const
{
return HexStr(vchData);
}
std::string Object::GetDataAsPlainString() const
{
return std::string(vchData.begin(), vchData.end());
}
} // namespace Governance

93
src/governance/common.h Normal file
View File

@ -0,0 +1,93 @@
// Copyright (c) 2014-2023 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_GOVERNANCE_COMMON_H
#define BITCOIN_GOVERNANCE_COMMON_H
#include <primitives/transaction.h>
#include <uint256.h>
#include <serialize.h>
#include <string>
#include <vector>
/**
* This module is a public interface of governance module that can be used
* in other components such as wallet
*/
class UniValue;
enum class GovernanceObject : int {
UNKNOWN = 0,
PROPOSAL,
TRIGGER
};
template<> struct is_serializable_enum<GovernanceObject> : std::true_type {};
namespace Governance
{
class Object
{
Object() :
type{GovernanceObject::UNKNOWN},
hashParent{},
revision{0},
time{0},
collateralHash{},
vchData{}
{
}
Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex);
UniValue ToJson() const;
uint256 GetHash() const;
std::string GetDataAsHexString() const;
std::string GetDataAsPlainString() const;
/// Object typecode
GovernanceObject type;
/// parent object, 0 is root
uint256 hashParent;
/// object revision in the system
int revision;
/// time this object was created
int64_t time;
/// fee-tx
uint256 collateralHash;
/// Masternode info for signed objects
COutPoint masternodeOutpoint;
std::vector<unsigned char> vchSig;
/// Data field - can be used for anything
std::vector<unsigned char> vchData;
SERIALIZE_METHODS(Object, obj)
{
READWRITE(
obj.hashParent,
obj.revision,
obj.time,
obj.collateralHash,
obj.vchData,
obj.type,
obj.masternodeOutpoint
);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(obj.vchSig);
}
}
};
} // namespace Governance
#endif

View File

@ -24,15 +24,8 @@
CGovernanceObject::CGovernanceObject() :
cs(),
nObjectType(GovernanceObject::UNKNOWN),
nHashParent(),
nRevision(0),
nTime(0),
m_obj{},
nDeletionTime(0),
nCollateralHash(),
vchData(),
masternodeOutpoint(),
vchSig(),
fCachedLocalValidity(false),
strLocalValidityError(),
fCachedFunding(false),
@ -51,15 +44,8 @@ CGovernanceObject::CGovernanceObject() :
CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, int64_t nTimeIn, const uint256& nCollateralHashIn, const std::string& strDataHexIn) :
cs(),
nObjectType(GovernanceObject::UNKNOWN),
nHashParent(nHashParentIn),
nRevision(nRevisionIn),
nTime(nTimeIn),
m_obj{nHashParentIn, nRevisionIn, nTimeIn, nCollateralHashIn, strDataHexIn},
nDeletionTime(0),
nCollateralHash(nCollateralHashIn),
vchData(ParseHex(strDataHexIn)),
masternodeOutpoint(),
vchSig(),
fCachedLocalValidity(false),
strLocalValidityError(),
fCachedFunding(false),
@ -78,15 +64,8 @@ CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevision
CGovernanceObject::CGovernanceObject(const CGovernanceObject& other) :
cs(),
nObjectType(other.nObjectType),
nHashParent(other.nHashParent),
nRevision(other.nRevision),
nTime(other.nTime),
m_obj{other.m_obj},
nDeletionTime(other.nDeletionTime),
nCollateralHash(other.nCollateralHash),
vchData(other.vchData),
masternodeOutpoint(other.masternodeOutpoint),
vchSig(other.vchSig),
fCachedLocalValidity(other.fCachedLocalValidity),
strLocalValidityError(other.strLocalValidityError),
fCachedFunding(other.fCachedFunding),
@ -186,7 +165,7 @@ bool CGovernanceObject::ProcessVote(const CGovernanceVote& vote, CGovernanceExce
nVoteTimeUpdate = nNow;
}
bool onlyVotingKeyAllowed = nObjectType == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
bool onlyVotingKeyAllowed = m_obj.type == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
// Finally check that the vote is actually valid (done last because of cost of signature verification)
if (!vote.IsValid(onlyVotingKeyAllowed)) {
@ -247,7 +226,7 @@ std::set<uint256> CGovernanceObject::RemoveInvalidVotes(const COutPoint& mnOutpo
return {};
}
auto removedVotes = fileVotes.RemoveInvalidVotes(mnOutpoint, nObjectType == GovernanceObject::PROPOSAL);
auto removedVotes = fileVotes.RemoveInvalidVotes(mnOutpoint, m_obj.type == GovernanceObject::PROPOSAL);
if (removedVotes.empty()) {
return {};
}
@ -278,20 +257,7 @@ std::set<uint256> CGovernanceObject::RemoveInvalidVotes(const COutPoint& mnOutpo
uint256 CGovernanceObject::GetHash() const
{
// Note: doesn't match serialization
// CREATE HASH OF ALL IMPORTANT PIECES OF DATA
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
ss << nHashParent;
ss << nRevision;
ss << nTime;
ss << GetDataAsHexString();
ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing
ss << vchSig;
// fee_tx is left out on purpose
return ss.GetHash();
return m_obj.GetHash();
}
uint256 CGovernanceObject::GetDataHash() const
@ -309,7 +275,7 @@ uint256 CGovernanceObject::GetSignatureHash() const
void CGovernanceObject::SetMasternodeOutpoint(const COutPoint& outpoint)
{
masternodeOutpoint = outpoint;
m_obj.masternodeOutpoint = outpoint;
}
bool CGovernanceObject::Sign(const CBLSSecretKey& key)
@ -318,14 +284,14 @@ bool CGovernanceObject::Sign(const CBLSSecretKey& key)
if (!sig.IsValid()) {
return false;
}
vchSig = sig.ToByteVector(false);
m_obj.vchSig = sig.ToByteVector(false);
return true;
}
bool CGovernanceObject::CheckSignature(const CBLSPublicKey& pubKey) const
{
CBLSSignature sig;
sig.SetByteVector(vchSig, false);
sig.SetByteVector(m_obj.vchSig, false);
if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), false)) {
LogPrintf("CGovernanceObject::CheckSignature -- VerifyInsecure() failed\n");
return false;
@ -341,7 +307,7 @@ bool CGovernanceObject::CheckSignature(const CBLSPublicKey& pubKey) const
UniValue CGovernanceObject::GetJSONObject() const
{
UniValue obj(UniValue::VOBJ);
if (vchData.empty()) {
if (m_obj.vchData.empty()) {
return obj;
}
@ -369,7 +335,7 @@ UniValue CGovernanceObject::GetJSONObject() const
void CGovernanceObject::LoadData()
{
if (vchData.empty()) {
if (m_obj.vchData.empty()) {
return;
}
@ -379,7 +345,7 @@ void CGovernanceObject::LoadData()
GetData(objResult);
LogPrint(BCLog::GOBJECT, "CGovernanceObject::LoadData -- GetDataAsPlainString = %s\n", GetDataAsPlainString());
UniValue obj = GetJSONObject();
nObjectType = GovernanceObject(obj["type"].get_int());
m_obj.type = GovernanceObject(obj["type"].get_int());
} catch (std::exception& e) {
fUnparsable = true;
std::ostringstream ostr;
@ -417,36 +383,19 @@ void CGovernanceObject::GetData(UniValue& objResult) const
* --------------------------------------------------------
*
*/
std::string CGovernanceObject::GetDataAsHexString() const
{
return HexStr(vchData);
return m_obj.GetDataAsHexString();
}
std::string CGovernanceObject::GetDataAsPlainString() const
{
return std::string(vchData.begin(), vchData.end());
return m_obj.GetDataAsPlainString();
}
UniValue CGovernanceObject::ToJson() const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("objectHash", GetHash().ToString());
obj.pushKV("parentHash", nHashParent.ToString());
obj.pushKV("collateralHash", GetCollateralHash().ToString());
obj.pushKV("createdAt", GetCreationTime());
obj.pushKV("revision", nRevision);
UniValue data;
if (!data.read(GetDataAsPlainString())) {
data.clear();
data.setObject();
data.pushKV("plain", GetDataAsPlainString());
data.pushKV("hex", GetDataAsHexString());
} else {
data.pushKV("hex", GetDataAsHexString());
}
obj.pushKV("data", data);
return obj;
return m_obj.ToJson();
}
void CGovernanceObject::UpdateLocalValidity()
@ -475,7 +424,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
return false;
}
switch (nObjectType) {
switch (m_obj.type) {
case GovernanceObject::PROPOSAL: {
CProposalValidator validator(GetDataAsHexString());
// Note: It's ok to have expired proposals
@ -499,8 +448,8 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
auto mnList = deterministicMNManager->GetListAtChainTip();
std::string strOutpoint = masternodeOutpoint.ToStringShort();
auto dmn = mnList.GetMNByCollateral(masternodeOutpoint);
std::string strOutpoint = m_obj.masternodeOutpoint.ToStringShort();
auto dmn = mnList.GetMNByCollateral(m_obj.masternodeOutpoint);
if (!dmn) {
strError = "Failed to find Masternode by UTXO, missing masternode=" + strOutpoint;
return false;
@ -515,7 +464,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
return true;
}
default: {
strError = strprintf("Invalid object type %d", ToUnderlying(nObjectType));
strError = strprintf("Invalid object type %d", ToUnderlying(m_obj.type));
return false;
}
}
@ -524,7 +473,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
CAmount CGovernanceObject::GetMinCollateralFee() const
{
// Only 1 type has a fee for the moment but switch statement allows for future object types
switch (nObjectType) {
switch (m_obj.type) {
case GovernanceObject::PROPOSAL: {
return GOVERNANCE_PROPOSAL_FEE_TX;
}
@ -547,9 +496,9 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
// RETRIEVE TRANSACTION IN QUESTION
uint256 nBlockHash;
CTransactionRef txCollateral = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, nCollateralHash, Params().GetConsensus(), nBlockHash);
CTransactionRef txCollateral = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, m_obj.collateralHash, Params().GetConsensus(), nBlockHash);
if (!txCollateral) {
strError = strprintf("Can't find collateral tx %s", nCollateralHash.ToString());
strError = strprintf("Can't find collateral tx %s", m_obj.collateralHash.ToString());
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
return false;
}
@ -694,7 +643,7 @@ void CGovernanceObject::Relay(CConnman& connman) const
}
int minProtoVersion = MIN_PEER_PROTO_VERSION;
if (nObjectType == GovernanceObject::PROPOSAL) {
if (m_obj.type == GovernanceObject::PROPOSAL) {
// We know this proposal is valid locally, otherwise we would not get to the point we should relay it.
// But we don't want to relay it to pre-GOVSCRIPT_PROTO_VERSION peers if payment_address is p2sh
// because they won't accept it anyway and will simply ban us eventually.

View File

@ -5,11 +5,11 @@
#ifndef BITCOIN_GOVERNANCE_OBJECT_H
#define BITCOIN_GOVERNANCE_OBJECT_H
#include <governance/common.h>
#include <governance/exceptions.h>
#include <governance/vote.h>
#include <governance/votedb.h>
#include <sync.h>
#include <util/underlying.h>
#include <univalue.h>
@ -26,13 +26,6 @@ extern RecursiveMutex cs_main;
static constexpr double GOVERNANCE_FILTER_FP_RATE = 0.001;
enum class GovernanceObject : int {
UNKNOWN = 0,
PROPOSAL,
TRIGGER
};
template<> struct is_serializable_enum<GovernanceObject> : std::true_type {};
static constexpr CAmount GOVERNANCE_PROPOSAL_FEE_TX = (1 * COIN);
@ -104,30 +97,11 @@ private:
/// critical section to protect the inner data structures
mutable RecursiveMutex cs;
/// Object typecode
GovernanceObject nObjectType;
/// parent object, 0 is root
uint256 nHashParent;
/// object revision in the system
int nRevision;
/// time this object was created
int64_t nTime;
Governance::Object m_obj;
/// time this object was marked for deletion
int64_t nDeletionTime;
/// fee-tx
uint256 nCollateralHash;
/// Data field - can be used for anything
std::vector<unsigned char> vchData;
/// Masternode info for signed objects
COutPoint masternodeOutpoint;
std::vector<unsigned char> vchSig;
/// is valid by blockchain
bool fCachedLocalValidity;
@ -173,7 +147,7 @@ public:
int64_t GetCreationTime() const
{
return nTime;
return m_obj.time;
}
int64_t GetDeletionTime() const
@ -183,17 +157,17 @@ public:
GovernanceObject GetObjectType() const
{
return nObjectType;
return m_obj.type;
}
const uint256& GetCollateralHash() const
{
return nCollateralHash;
return m_obj.collateralHash;
}
const COutPoint& GetMasternodeOutpoint() const
{
return masternodeOutpoint;
return m_obj.masternodeOutpoint;
}
bool IsSetCachedFunding() const
@ -296,18 +270,7 @@ public:
SERIALIZE_METHODS(CGovernanceObject, obj)
{
// SERIALIZE DATA FOR SAVING/LOADING OR NETWORK FUNCTIONS
READWRITE(
obj.nHashParent,
obj.nRevision,
obj.nTime,
obj.nCollateralHash,
obj.vchData,
obj.nObjectType,
obj.masternodeOutpoint
);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(obj.vchSig);
}
READWRITE(obj.m_obj);
if (s.GetType() & SER_DISK) {
// Only include these for the disk file format
READWRITE(obj.nDeletionTime, obj.fExpired, obj.mapCurrentMNVotes, obj.fileVotes);

View File

@ -29,6 +29,7 @@ class CFeeRate;
class CJClientManager;
class CKey;
class CWallet;
class UniValue;
enum class FeeReason;
enum class TransactionError;
enum isminetype : unsigned int;

View File

@ -18,6 +18,7 @@
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <governance/common.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
@ -227,7 +228,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INTERNAL_ERROR, err);
}
if (!wallet->WriteGovernanceObject({hashParent, nRevision, nTime, tx->GetHash(), strDataHex})) {
if (!wallet->WriteGovernanceObject(Governance::Object{hashParent, nRevision, nTime, tx->GetHash(), strDataHex})) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "WriteGovernanceObject failed");
}
@ -267,11 +268,11 @@ static UniValue gobject_list_prepared(const JSONRPCRequest& request)
}
// Get a list of all prepared governance objects stored in the wallet
LOCK(wallet->cs_wallet);
std::vector<const CGovernanceObject*> vecObjects = wallet->GetGovernanceObjects();
std::vector<const Governance::Object*> vecObjects = wallet->GetGovernanceObjects();
// Sort the vector by the object creation time/hex data
std::sort(vecObjects.begin(), vecObjects.end(), [](const CGovernanceObject* a, const CGovernanceObject* b) {
bool fGreater = a->GetCreationTime() > b->GetCreationTime();
bool fEqual = a->GetCreationTime() == b->GetCreationTime();
std::sort(vecObjects.begin(), vecObjects.end(), [](const Governance::Object* a, const Governance::Object* b) {
bool fGreater = a->time > b->time;
bool fEqual = a->time == b->time;
bool fHexGreater = a->GetDataAsHexString() > b->GetDataAsHexString();
return fGreater || (fEqual && fHexGreater);
});

View File

@ -45,7 +45,6 @@
#include <coinjoin/client.h>
#include <coinjoin/options.h>
#include <evo/providertx.h>
#include <governance/governance.h>
#include <masternode/sync.h>
#include <univalue.h>
@ -5091,23 +5090,23 @@ void CWallet::notifyChainLock(const CBlockIndex* pindexChainLock, const std::sha
NotifyChainLockReceived(pindexChainLock->nHeight);
}
bool CWallet::LoadGovernanceObject(const CGovernanceObject& obj)
bool CWallet::LoadGovernanceObject(const Governance::Object& obj)
{
AssertLockHeld(cs_wallet);
return m_gobjects.emplace(obj.GetHash(), obj).second;
}
bool CWallet::WriteGovernanceObject(const CGovernanceObject& obj)
bool CWallet::WriteGovernanceObject(const Governance::Object& obj)
{
AssertLockHeld(cs_wallet);
WalletBatch batch(GetDatabase());
return batch.WriteGovernanceObject(obj) && LoadGovernanceObject(obj);
}
std::vector<const CGovernanceObject*> CWallet::GetGovernanceObjects()
std::vector<const Governance::Object*> CWallet::GetGovernanceObjects()
{
AssertLockHeld(cs_wallet);
std::vector<const CGovernanceObject*> vecObjects;
std::vector<const Governance::Object*> vecObjects;
vecObjects.reserve(m_gobjects.size());
for (auto& obj : m_gobjects) {
vecObjects.push_back(&obj.second);

View File

@ -8,6 +8,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
#include <governance/common.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <policy/feerate.h>
@ -27,8 +28,6 @@
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
#include <governance/object.h>
#include <algorithm>
#include <atomic>
#include <map>
@ -828,7 +827,7 @@ public:
const std::string& GetName() const { return m_name; }
// Map from governance object hash to governance object, they are added by gobject_prepare.
std::map<uint256, CGovernanceObject> m_gobjects;
std::map<uint256, Governance::Object> m_gobjects;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
@ -1278,11 +1277,11 @@ public:
void notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const llmq::CChainLockSig>& clsig) override;
/** Load a CGovernanceObject into m_gobjects. */
bool LoadGovernanceObject(const CGovernanceObject& obj);
bool LoadGovernanceObject(const Governance::Object& obj);
/** Store a CGovernanceObject in the wallet database. This should only be used by governance objects that are created by this wallet via `gobject prepare`. */
bool WriteGovernanceObject(const CGovernanceObject& obj);
bool WriteGovernanceObject(const Governance::Object& obj);
/** Returns a vector containing pointers to the governance objects in m_gobjects */
std::vector<const CGovernanceObject*> GetGovernanceObjects();
std::vector<const Governance::Object*> GetGovernanceObjects();
/**
* Blocks until the wallet state is up-to-date to /at least/ the current

View File

@ -8,7 +8,7 @@
#include <key_io.h>
#include <fs.h>
#include <governance/object.h>
#include <governance/common.h>
#include <hdchain.h>
#include <protocol.h>
#include <serialize.h>
@ -216,7 +216,7 @@ bool WalletBatch::WriteCoinJoinSalt(const uint256& salt)
return WriteIC(DBKeys::COINJOIN_SALT, salt);
}
bool WalletBatch::WriteGovernanceObject(const CGovernanceObject& obj)
bool WalletBatch::WriteGovernanceObject(const Governance::Object& obj)
{
return WriteIC(std::make_pair(DBKeys::G_OBJECT, obj.GetHash()), obj, false);
}
@ -493,7 +493,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
} else if (strType == DBKeys::G_OBJECT) {
uint256 nObjectHash;
CGovernanceObject obj;
Governance::Object obj;
ssKey >> nObjectHash;
ssValue >> obj;

View File

@ -30,7 +30,6 @@
static const bool DEFAULT_FLUSHWALLET = true;
struct CBlockLocator;
class CGovernanceObject;
class CHDChain;
class CHDPubKey;
class CKeyPool;
@ -41,6 +40,11 @@ class CWalletTx;
class uint160;
class uint256;
namespace Governance
{
class Object;
} // namespace Governance
/** Error statuses for the wallet database */
enum class DBErrors
{
@ -200,7 +204,7 @@ public:
bool WriteCoinJoinSalt(const uint256& salt);
/** Write a CGovernanceObject to the database */
bool WriteGovernanceObject(const CGovernanceObject& obj);
bool WriteGovernanceObject(const Governance::Object& obj);
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);