Classes, validation and update logic for CProUpRegTX

This commit is contained in:
Alexander Block 2018-03-19 08:44:00 +01:00
parent 32951f795c
commit 1c68d11077
10 changed files with 234 additions and 20 deletions

View File

@ -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)) {
@ -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());
}
}

View File

@ -72,6 +72,21 @@ public:
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 &&

View File

@ -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;
@ -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
@ -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;

View File

@ -93,8 +93,46 @@ public:
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);

View File

@ -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");
@ -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
}
@ -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
}

View File

@ -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 */

View File

@ -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()) {

View File

@ -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;
@ -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();
@ -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) {
@ -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)) {
@ -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);
}
}
@ -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;
}

View File

@ -580,6 +580,7 @@ public:
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);

View File

@ -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)