diff --git a/src/Makefile.am b/src/Makefile.am index 4cd8c18efa..f181677e72 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/governance/common.cpp b/src/governance/common.cpp new file mode 100644 index 0000000000..81d1b3f1c0 --- /dev/null +++ b/src/governance/common.cpp @@ -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 + +#include +#include +#include +#include + +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 diff --git a/src/governance/common.h b/src/governance/common.h new file mode 100644 index 0000000000..a68bff24ae --- /dev/null +++ b/src/governance/common.h @@ -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 +#include + +#include + +#include +#include + +/** + * 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 : 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 vchSig; + + /// Data field - can be used for anything + std::vector 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 diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 61b48096bb..912903f8a5 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -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 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 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. diff --git a/src/governance/object.h b/src/governance/object.h index 497f62e50b..142b9e4d9f 100644 --- a/src/governance/object.h +++ b/src/governance/object.h @@ -5,11 +5,11 @@ #ifndef BITCOIN_GOVERNANCE_OBJECT_H #define BITCOIN_GOVERNANCE_OBJECT_H +#include #include #include #include #include -#include #include @@ -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 : 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 vchData; - - /// Masternode info for signed objects - COutPoint masternodeOutpoint; - std::vector 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); diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 734c8e0e99..92be583100 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -29,6 +29,7 @@ class CFeeRate; class CJClientManager; class CKey; class CWallet; +class UniValue; enum class FeeReason; enum class TransactionError; enum isminetype : unsigned int; diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index da2bda6942..a0e06fa070 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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 vecObjects = wallet->GetGovernanceObjects(); + std::vector 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); }); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2d24a589ed..c67cb20248 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include @@ -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 CWallet::GetGovernanceObjects() +std::vector CWallet::GetGovernanceObjects() { AssertLockHeld(cs_wallet); - std::vector vecObjects; + std::vector vecObjects; vecObjects.reserve(m_gobjects.size()); for (auto& obj : m_gobjects) { vecObjects.push_back(&obj.second); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1fcc65952c..ad3e9335ff 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -8,6 +8,7 @@ #define BITCOIN_WALLET_WALLET_H #include +#include #include #include #include @@ -27,8 +28,6 @@ #include #include -#include - #include #include #include @@ -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 m_gobjects; + std::map m_gobjects; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1278,11 +1277,11 @@ public: void notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr& 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 GetGovernanceObjects(); + std::vector GetGovernanceObjects(); /** * Blocks until the wallet state is up-to-date to /at least/ the current diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index ddaa6cb949..6038cef7d2 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include @@ -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; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index b7863fc660..7026b131a6 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -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);