Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TierTwo] Deterministic masternode lists #2273

Merged
merged 25 commits into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b92de4d
[Consensus] Reject special txes before V6 enforcement
random-zebra Mar 23, 2021
e067443
[Build] Add "immer" functional/immutable containers library
random-zebra Jan 26, 2021
3f74c88
[Build] CMake: add immer headers
random-zebra Mar 26, 2021
6fb2496
Define hash function for uint256 to be used in STL-like containers
random-zebra Jan 26, 2021
693772a
[Core] Introduce ProRegTx payload
random-zebra Jan 24, 2021
0c064f8
[Tests] Add tests for SetTxPayload/GetTxPayload and CheckStringSig
random-zebra Jan 26, 2021
50c277b
Implement std::unordered_map/set compatible hasher classes for salted
codablock Feb 26, 2019
ae33972
[Core] Deterministic Masternode List implementation
random-zebra Jan 26, 2021
48c66dc
Conflict handling for ProRegTx in mempool
codablock Feb 14, 2018
9ac0f37
[Tests] Introduce evo_deterministicmns_tests.cpp
random-zebra Jan 27, 2021
7f2d2b5
[MOVE-ONLY] move special_tx_validation_test inside evo_* test file
random-zebra Jan 28, 2021
180a311
[Validation] Connect deterministic manager to block processing
random-zebra Feb 8, 2021
c013bf1
[Refactoring] Add evo Notification Interface
random-zebra Feb 8, 2021
41869ec
[Refactoring] Add IsDIP3Enforced/LegacyMNObsolete funcs to DMNManager
random-zebra Feb 8, 2021
ddad010
[Validation] Check duplicate unique-properties for masternodes
random-zebra Mar 8, 2021
66302c3
[Tests] Check deterministic masternodes unique properties
random-zebra Mar 11, 2021
ca9d10c
Implement CActiveDeterministicMasternodeManager
codablock Feb 15, 2018
6cd5b0c
[Refactoring] encapsulate activeMNINfo inside activeMNManager
random-zebra Feb 14, 2021
dcb5df9
[Refactoring] CADMM: GetLocalAddress static
random-zebra Feb 14, 2021
0520ea9
[Init] Add -mnoperatorprivatekey to flag the active mn as deterministic
random-zebra Feb 10, 2021
a1cb227
[BUG] Add proper virtual dtor for CActiveDeterministicMasternodeManager
random-zebra Feb 11, 2021
170ab92
[Tests] Check for protx inputs hash in evo_specialtx_tests
random-zebra Apr 16, 2021
e659d7e
[Trivial] Fix styling/logging nits
random-zebra Apr 18, 2021
e0ff60f
[Consensus] Serialize the whole scriptPayout for ProReg sign string
random-zebra Apr 19, 2021
6dbdf37
[Consensus] Serialize also shield inputs for ProReg inputshash
random-zebra Apr 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ file(GLOB CONSENSUS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/*.h)
file(GLOB CTAES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/ctaes/*.h)
file(GLOB ZRUST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/rust/include/*.h)
file(GLOB SAPLING_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/sapling/*.h)
file(GLOB IMMER_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/immer/*.h)
file(GLOB EVO_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/evo/*.h)

source_group("BitcoinHeaders" FILES
Expand All @@ -187,6 +188,7 @@ source_group("BitcoinHeaders" FILES
${CTAES_HEADERS}
${ZRUST_HEADERS}
${SAPLING_HEADERS}
${IMMER_HEADERS}
${EVO_HEADERS}
./src/support/cleanse.h
)
Expand Down Expand Up @@ -382,7 +384,10 @@ set(COMMON_SOURCES
./src/coins.cpp
./src/key_io.cpp
./src/compressor.cpp
./src/evo/deterministicmns.cpp
./src/evo/evonotificationinterface.cpp
./src/evo/evodb.cpp
./src/evo/providertx.cpp
./src/evo/specialtx.cpp
./src/consensus/merkle.cpp
./src/consensus/zerocoin_verify.cpp
Expand All @@ -400,6 +405,7 @@ set(COMMON_SOURCES
./src/policy/feerate.cpp
./src/protocol.cpp
./src/pubkey.cpp
./src/saltedhasher.cpp
./src/scheduler.cpp
./src/script/interpreter.cpp
./src/script/script.cpp
Expand Down
11 changes: 11 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ BITCOIN_CORE_H = \
cuckoocache.h \
crypter.h \
cyclingvector.h \
evo/deterministicmns.h \
evo/evodb.h \
evo/evonotificationinterface.h \
evo/providertx.h \
evo/specialtx.h \
pairresult.h \
addressbook.h \
Expand Down Expand Up @@ -247,6 +250,7 @@ BITCOIN_CORE_H = \
rpc/protocol.h \
rpc/register.h \
rpc/server.h \
saltedhasher.h \
scheduler.h \
script/interpreter.h \
script/keyorigin.h \
Expand Down Expand Up @@ -325,7 +329,10 @@ libbitcoin_server_a_SOURCES = \
consensus/params.cpp \
consensus/tx_verify.cpp \
consensus/zerocoin_verify.cpp \
evo/deterministicmns.cpp \
evo/evodb.cpp \
evo/evonotificationinterface.cpp \
evo/providertx.cpp \
evo/specialtx.cpp \
httprpc.cpp \
httpserver.cpp \
Expand Down Expand Up @@ -512,6 +519,7 @@ libbitcoin_common_a_SOURCES = \
policy/feerate.cpp \
protocol.cpp \
pubkey.cpp \
saltedhasher.cpp \
scheduler.cpp \
script/interpreter.cpp \
script/script.cpp \
Expand Down Expand Up @@ -735,7 +743,10 @@ CTAES_DIST += crypto/ctaes/ctaes.h
CTAES_DIST += crypto/ctaes/README.md
CTAES_DIST += crypto/ctaes/test.c

IMMER_DIST = immer

EXTRA_DIST = $(CTAES_DIST) rust
EXTRA_DIST += $(IMMER_DIST)


config/pivx-config.h: config/stamp-h1
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ BITCOIN_TESTS =\
test/crypto_tests.cpp \
test/cuckoocache_tests.cpp \
test/DoS_tests.cpp \
test/evo_deterministicmns_tests.cpp \
test/evo_specialtx_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
test/key_tests.cpp \
Expand Down
180 changes: 180 additions & 0 deletions src/activemasternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "activemasternode.h"

#include "addrman.h"
#include "evo/providertx.h"
#include "evo/deterministicmns.h"
#include "masternode-sync.h"
#include "masternode.h"
#include "masternodeconfig.h"
Expand All @@ -14,6 +16,180 @@
#include "netbase.h"
#include "protocol.h"

// Keep track of the active Masternode
CActiveDeterministicMasternodeManager* activeMasternodeManager{nullptr};

static bool GetLocalAddress(CService& addrRet)
{
// First try to find whatever local address is specified by externalip option
bool fFound = GetLocal(addrRet) && CActiveDeterministicMasternodeManager::IsValidNetAddr(addrRet);
if (!fFound && Params().IsRegTestNet()) {
if (Lookup("127.0.0.1", addrRet, GetListenPort(), false)) {
fFound = true;
}
}
if(!fFound) {
// If we have some peers, let's try to find our local address from one of them
g_connman->ForEachNodeContinueIf([&fFound, &addrRet](CNode* pnode) {
if (pnode->addr.IsIPv4())
fFound = GetLocal(addrRet, &pnode->addr) && CActiveDeterministicMasternodeManager::IsValidNetAddr(addrRet);
return !fFound;
});
}
return fFound;
}

std::string CActiveDeterministicMasternodeManager::GetStatus() const
{
switch (state) {
case MASTERNODE_WAITING_FOR_PROTX: return "Waiting for ProTx to appear on-chain";
case MASTERNODE_POSE_BANNED: return "Masternode was PoSe banned";
case MASTERNODE_REMOVED: return "Masternode removed from list";
case MASTERNODE_OPERATOR_KEY_CHANGED: return "Operator key changed or revoked";
case MASTERNODE_PROTX_IP_CHANGED: return "IP address specified in ProTx changed";
case MASTERNODE_READY: return "Ready";
case MASTERNODE_ERROR: return "Error. " + strError;
default: return "Unknown";
}
}

OperationResult CActiveDeterministicMasternodeManager::SetOperatorKey(const std::string& strMNOperatorPrivKey)
{

LOCK(cs_main); // Lock cs_main so the node doesn't perform any action while we setup the Masternode
LogPrintf("Initializing deterministic masternode...\n");
if (strMNOperatorPrivKey.empty()) {
return errorOut("ERROR: Masternode operator priv key cannot be empty.");
}
if (!CMessageSigner::GetKeysFromSecret(strMNOperatorPrivKey, info.keyOperator, info.keyIDOperator)) {
return errorOut(_("Invalid mnoperatorprivatekey. Please see the documentation."));
}
return OperationResult(true);
}

void CActiveDeterministicMasternodeManager::Init()
{
if (!fMasterNode || !deterministicMNManager->IsDIP3Enforced())
return;

LOCK(cs_main);

// Check that our local network configuration is correct
if (!fListen) {
// listen option is probably overwritten by smth else, no good
state = MASTERNODE_ERROR;
strError = "Masternode must accept connections from outside. Make sure listen configuration option is not overwritten by some another parameter.";
LogPrintf("%s ERROR: %s\n", __func__, strError);
return;
}

if (!GetLocalAddress(info.service)) {
state = MASTERNODE_ERROR;
strError = "Can't detect valid external address. Please consider using the externalip configuration option if problem persists. Make sure to use IPv4 address only.";
LogPrintf("%s ERROR: %s\n", __func__, strError);
return;
}

CDeterministicMNList mnList = deterministicMNManager->GetListAtChainTip();

CDeterministicMNCPtr dmn = mnList.GetMNByOperatorKey(info.keyIDOperator);
if (!dmn) {
// MN not appeared on the chain yet
return;
}

if (!mnList.IsMNValid(dmn->proTxHash)) {
if (mnList.IsMNPoSeBanned(dmn->proTxHash)) {
state = MASTERNODE_POSE_BANNED;
} else {
state = MASTERNODE_REMOVED;
}
return;
}

LogPrintf("%s: proTxHash=%s, proTx=%s\n", __func__, dmn->proTxHash.ToString(), dmn->ToString());

if (info.service != dmn->pdmnState->addr) {
state = MASTERNODE_ERROR;
strError = strprintf("Local address %s does not match the address from ProTx (%s)",
info.service.ToStringIPPort(), dmn->pdmnState->addr.ToStringIPPort());
LogPrintf("%s ERROR: %s\n", __func__, strError);
return;
}

if (!Params().IsRegTestNet()) {
// Check socket connectivity
const std::string& strService = info.service.ToString();
LogPrintf("%s: Checking inbound connection to '%s'\n", __func__, strService);
SOCKET hSocket;
bool fConnected = ConnectSocket(info.service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket);
CloseSocket(hSocket);

if (!fConnected) {
state = MASTERNODE_ERROR;
LogPrintf("%s ERROR: Could not connect to %s\n", __func__, strService);
return;
}
}

info.proTxHash = dmn->proTxHash;
state = MASTERNODE_READY;
}

void CActiveDeterministicMasternodeManager::Reset(masternode_state_t _state)
{
state = _state;
SetNullProTx();
// MN might have reappeared in same block with a new ProTx
Init();
}

void CActiveDeterministicMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
{
if (fInitialDownload)
return;

if (!fMasterNode || !deterministicMNManager->IsDIP3Enforced())
return;

if (state == MASTERNODE_READY) {
auto oldMNList = deterministicMNManager->GetListForBlock(pindexNew->pprev);
auto newMNList = deterministicMNManager->GetListForBlock(pindexNew);
if (!newMNList.IsMNValid(info.proTxHash)) {
// MN disappeared from MN list
Reset(MASTERNODE_REMOVED);
return;
}

auto oldDmn = oldMNList.GetMN(info.proTxHash);
auto newDmn = newMNList.GetMN(info.proTxHash);
if (newDmn->pdmnState->keyIDOperator != oldDmn->pdmnState->keyIDOperator) {
// MN operator key changed or revoked
Reset(MASTERNODE_OPERATOR_KEY_CHANGED);
return;
}

if (newDmn->pdmnState->addr != oldDmn->pdmnState->addr) {
// MN IP changed
Reset(MASTERNODE_PROTX_IP_CHANGED);
return;
}
} else {
// MN might have (re)appeared with a new ProTx or we've found some peers
// and figured out our local address
Init();
}
}

bool CActiveDeterministicMasternodeManager::IsValidNetAddr(const CService& addrIn)
{
// TODO: check IPv6 and TOR addresses
return Params().IsRegTestNet() || (addrIn.IsIPv4() && IsReachable(addrIn) && addrIn.IsRoutable());
}


/********* LEGACY *********/

OperationResult initMasternode(const std::string& _strMasterNodePrivKey, const std::string& _strMasterNodeAddr, bool isFromInit)
{
if (!isFromInit && fMasterNode) {
Expand Down Expand Up @@ -79,6 +255,10 @@ OperationResult initMasternode(const std::string& _strMasterNodePrivKey, const s
void CActiveMasternode::ManageStatus()
{
if (!fMasterNode) return;
if (activeMasternodeManager != nullptr) {
// Deterministic masternode
return;
}

LogPrint(BCLog::MASTERNODE, "CActiveMasternode::ManageStatus() - Begin\n");

Expand Down
54 changes: 53 additions & 1 deletion src/activemasternode.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,69 @@
#include "net.h"
#include "operationresult.h"
#include "sync.h"
#include "validationinterface.h"
#include "wallet/wallet.h"

class CActiveDeterministicMasternodeManager;

#define ACTIVE_MASTERNODE_INITIAL 0 // initial state
#define ACTIVE_MASTERNODE_SYNC_IN_PROCESS 1
#define ACTIVE_MASTERNODE_NOT_CAPABLE 3
#define ACTIVE_MASTERNODE_STARTED 4

extern CActiveDeterministicMasternodeManager* activeMasternodeManager;

struct CActiveMasternodeInfo
{
// Keys for the active Masternode
CKeyID keyIDOperator;
CKey keyOperator;
// Initialized while registering Masternode
uint256 proTxHash{UINT256_ZERO};
CService service;
};

class CActiveDeterministicMasternodeManager : public CValidationInterface
{
public:
enum masternode_state_t {
MASTERNODE_WAITING_FOR_PROTX,
MASTERNODE_POSE_BANNED,
MASTERNODE_REMOVED,
MASTERNODE_OPERATOR_KEY_CHANGED,
MASTERNODE_PROTX_IP_CHANGED,
MASTERNODE_READY,
MASTERNODE_ERROR,
};

private:
masternode_state_t state{MASTERNODE_WAITING_FOR_PROTX};
std::string strError;
CActiveMasternodeInfo info;

public:
virtual ~CActiveDeterministicMasternodeManager() = default;
virtual void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload);

void Init();
void Reset(masternode_state_t _state);
// Sets the Deterministic Masternode Operator's private/public key
OperationResult SetOperatorKey(const std::string& strMNOperatorPrivKey);
void SetNullProTx() { info.proTxHash = UINT256_ZERO; }

const CActiveMasternodeInfo* GetInfo() const { return &info; }
masternode_state_t GetState() const { return state; }
std::string GetStatus() const;
bool IsReady() const { return state == MASTERNODE_READY; }

static bool IsValidNetAddr(const CService& addrIn);
};

// Responsible for initializing the masternode
OperationResult initMasternode(const std::string& strMasterNodePrivKey, const std::string& strMasterNodeAddr, bool isFromInit);

// Responsible for activating the Masternode and pinging the network

// Responsible for activating the Masternode and pinging the network (legacy MN list)
class CActiveMasternode
{
private:
Expand Down
Loading