Skip to content

Commit

Permalink
Classes, validation and update logic for CProUpRegTX
Browse files Browse the repository at this point in the history
  • Loading branch information
codablock committed Aug 31, 2018
1 parent 32951f7 commit 1c68d11
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 20 deletions.
32 changes: 28 additions & 4 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C

newList.AddMN(dmn);

LogPrintf("CDeterministicMNManager::%s -- MN %s added to MN list. nHeight=%d, mapCurMNs.size=%d\n",
__func__, tx.GetHash().ToString(), nHeight, newList.size());
LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n",
__func__, tx.GetHash().ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
Expand Down Expand Up @@ -433,8 +433,32 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C

newList.UpdateMN(proTx.proTxHash, newState);

LogPrintf("CDeterministicMNManager::%s -- MN %s updated with addr=%s, nProtocolVersion=%d. height=%d\n",
__func__, proTx.proTxHash.ToString(), proTx.addr.ToString(false), proTx.nProtocolVersion, height);
LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}

CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->keyIDOperator != proTx.keyIDOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
}
newState->keyIDOperator = proTx.keyIDOperator;
newState->keyIDVoting = proTx.keyIDVoting;
newState->scriptPayout = proTx.scriptPayout;

newList.UpdateMN(proTx.proTxHash, newState);

LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ class CDeterministicMNState
READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
}

void ResetOperatorFields()
{
keyIDOperator.SetNull();
addr = CService();
nProtocolVersion = 0;
scriptOperatorPayout = CScript();
revocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
}
void BanIfNotBanned(int height)
{
if (nPoSeBanHeight == -1) {
nPoSeBanHeight = height;
}
}

bool operator==(const CDeterministicMNState& rhs) const
{
return nRegisteredHeight == rhs.nRegisteredHeight &&
Expand Down
101 changes: 99 additions & 2 deletions src/evo/providertx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,69 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa
return true;
}

bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
{
AssertLockHeld(cs_main);

CProUpRegTx ptx;
if (!GetTxPayload(tx, ptx))
return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload");

if (ptx.nVersion > CProRegTx::CURRENT_VERSION)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");

if (ptx.keyIDOperator.IsNull() || ptx.keyIDVoting.IsNull())
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null");
// we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list)
if (!ptx.scriptPayout.IsPayToPublicKeyHash())
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee");

CTxDestination payoutDest;
if (!ExtractDestination(ptx.scriptPayout, payoutDest)) {
// should not happen as we checked script types before
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest");
}

if (pindexPrev) {
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash());
auto dmn = mnList.GetMN(ptx.proTxHash);
if (!dmn)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");

// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDOperator) || payoutDest == CTxDestination(ptx.keyIDVoting)) {
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
}

// This is a temporary restriction that will be lifted later
// It is required while we are transitioning from the old MN list to the deterministic list
CTransactionRef proRegTx;
uint256 tmpHashBlock;
if (!GetTransaction(ptx.proTxHash, proRegTx, Params().GetConsensus(), tmpHashBlock))
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payee-collateral");
if (proRegTx->vout[dmn->nCollateralIndex].scriptPubKey != ptx.scriptPayout)
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral");

if (mnList.HasUniqueProperty(ptx.keyIDOperator)) {
auto otherDmn = mnList.GetUniquePropertyMN(ptx.keyIDOperator);
if (ptx.proTxHash != otherDmn->proTxHash) {
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
}
}

if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexPrev->nHeight)) {
if (dmn->pdmnState->keyIDOwner != ptx.keyIDOperator || dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) {
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
}
}

if (!CheckInputsHashAndSig(tx, ptx, dmn->pdmnState->keyIDOwner, state))
return false;
}

return true;
}

std::string CProRegTx::ToString() const
{
CTxDestination dest;
Expand Down Expand Up @@ -190,8 +253,14 @@ void CProRegTx::ToJson(UniValue& obj) const

std::string CProUpServTx::ToString() const
{
return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s)",
nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString());
CTxDestination dest;
std::string payee = "unknown";
if (ExtractDestination(scriptOperatorPayout, dest)) {
payee = CBitcoinAddress(dest).ToString();
}

return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s, operatorPayoutAddress=%s)",
nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString(), payee);
}

void CProUpServTx::ToJson(UniValue& obj) const
Expand All @@ -210,6 +279,34 @@ void CProUpServTx::ToJson(UniValue& obj) const
obj.push_back(Pair("inputsHash", inputsHash.ToString()));
}

std::string CProUpRegTx::ToString() const
{
CTxDestination dest;
std::string payee = "unknown";
if (ExtractDestination(scriptPayout, dest)) {
payee = CBitcoinAddress(dest).ToString();
}

return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, keyIDOperator=%s, keyIDVoting=%s, payoutAddress=%s)",
nVersion, proTxHash.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), payee);
}

void CProUpRegTx::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
obj.push_back(Pair("version", nVersion));
obj.push_back(Pair("proTxHash", proTxHash.ToString()));
obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString()));
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
CTxDestination dest;
if (ExtractDestination(scriptPayout, dest)) {
CBitcoinAddress bitcoinAddress(dest);
obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString()));
}
obj.push_back(Pair("inputsHash", inputsHash.ToString()));
}

bool IsProTxCollateral(const CTransaction& tx, uint32_t n)
{
return GetProTxCollateralIndex(tx) == n;
Expand Down
38 changes: 38 additions & 0 deletions src/evo/providertx.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,46 @@ class CProUpServTx
void ToJson(UniValue& obj) const;
};

class CProUpRegTx
{
public:
static const uint16_t CURRENT_VERSION = 1;

public:
uint16_t nVersion{CURRENT_VERSION}; // message version
uint256 proTxHash;
CKeyID keyIDOperator;
CKeyID keyIDVoting;
CScript scriptPayout;
uint256 inputsHash; // replay protection
std::vector<unsigned char> vchSig;

public:
ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(nVersion);
READWRITE(proTxHash);
READWRITE(keyIDOperator);
READWRITE(keyIDVoting);
READWRITE(*(CScriptBase*)(&scriptPayout));
READWRITE(inputsHash);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
}

public:
std::string ToString() const;
void ToJson(UniValue& obj) const;
};


bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);

bool IsProTxCollateral(const CTransaction& tx, uint32_t n);
uint32_t GetProTxCollateralIndex(const CTransaction& tx);
Expand Down
4 changes: 4 additions & 0 deletions src/evo/specialtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
return CheckProRegTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
return CheckProUpServTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
return CheckProUpRegTx(tx, pindexPrev, state);
}

return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
Expand All @@ -42,6 +44,7 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER:
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
return true; // handled in batches per block
}

Expand All @@ -56,6 +59,7 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER:
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
return true; // handled in batches per block
}

Expand Down
1 change: 1 addition & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum {
TRANSACTION_NORMAL = 0,
TRANSACTION_PROVIDER_REGISTER = 1,
TRANSACTION_PROVIDER_UPDATE_SERVICE = 2,
TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3,
};

/** An outpoint - a combination of a transaction hash and an index n into its vout */
Expand Down
7 changes: 7 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpServTx", proTxObj));
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (GetTxPayload(tx, proTx)) {
UniValue proTxObj;
proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpRegTx", proTxObj));
}
}

if (!hashBlock.IsNull()) {
Expand Down
52 changes: 39 additions & 13 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
assert(false);
}
mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}
mapProTxPubKeyIDs.emplace(proTx.keyIDOperator, tx.GetHash());
}

return true;
Expand Down Expand Up @@ -646,6 +652,12 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
assert(false);
}
mapProTxAddresses.erase(proTx.addr);
} else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false);
}
mapProTxPubKeyIDs.erase(proTx.keyIDOperator);
}

totalTxSize -= it->GetTxSize();
Expand Down Expand Up @@ -774,6 +786,16 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
}
}

void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId)
{
if (mapProTxPubKeyIDs.count(keyId)) {
uint256 conflictHash = mapProTxPubKeyIDs[keyId];
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
}

void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
{
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
Expand All @@ -788,18 +810,8 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
if (mapProTxPubKeyIDs.count(proTx.keyIDOwner)) {
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOwner];
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
if (mapProTxPubKeyIDs.count(proTx.keyIDOperator)) {
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOperator];
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
removeProTxPubKeyConflicts(tx, proTx.keyIDOwner);
removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
Expand All @@ -812,6 +824,13 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}

removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
}
}

Expand Down Expand Up @@ -1103,7 +1122,14 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx))
assert(false);
return mapProTxAddresses.count(proTx.addr) && mapProTxAddresses[proTx.addr] != proTx.proTxHash;
auto it = mapProTxAddresses.find(proTx.addr);
return it != mapProTxAddresses.end() && it->second != proTx.proTxHash;
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx))
assert(false);
auto it = mapProTxPubKeyIDs.find(proTx.keyIDOperator);
return it != mapProTxPubKeyIDs.end() && it->second != proTx.proTxHash;
}
return false;
}
Expand Down
1 change: 1 addition & 0 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ class CTxMemPool
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId);
void removeProTxConflicts(const CTransaction &tx);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);

Expand Down
3 changes: 2 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
if (tx.nVersion >= 3) {
if (tx.nType != TRANSACTION_NORMAL &&
tx.nType != TRANSACTION_PROVIDER_REGISTER &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) {
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type");
}
if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL)
Expand Down

0 comments on commit 1c68d11

Please sign in to comment.