2019-01-29 15:53:14 +01:00
|
|
|
// Copyright (c) 2018-2019 The Dash Core developers
|
2018-02-13 13:36:36 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2018-11-06 09:54:23 +01:00
|
|
|
#include "deterministicmns.h"
|
2018-02-13 13:36:36 +01:00
|
|
|
#include "providertx.h"
|
|
|
|
#include "specialtx.h"
|
|
|
|
|
2018-11-06 09:54:23 +01:00
|
|
|
#include "base58.h"
|
2018-02-13 13:36:36 +01:00
|
|
|
#include "chainparams.h"
|
2018-11-06 09:54:23 +01:00
|
|
|
#include "clientversion.h"
|
2018-02-13 13:36:36 +01:00
|
|
|
#include "core_io.h"
|
2018-11-06 09:54:23 +01:00
|
|
|
#include "hash.h"
|
|
|
|
#include "messagesigner.h"
|
2018-02-13 13:36:36 +01:00
|
|
|
#include "script/standard.h"
|
2018-11-06 09:54:23 +01:00
|
|
|
#include "streams.h"
|
|
|
|
#include "univalue.h"
|
|
|
|
#include "validation.h"
|
2018-02-13 13:36:36 +01:00
|
|
|
|
|
|
|
template <typename ProTx>
|
2018-11-10 10:54:16 +01:00
|
|
|
static bool CheckService(const uint256& proTxHash, const ProTx& proTx, CValidationState& state)
|
2018-02-13 13:36:36 +01:00
|
|
|
{
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!proTx.addr.IsValid()) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
|
|
|
if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !proTx.addr.IsRoutable()) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2019-07-04 00:07:07 +02:00
|
|
|
static int mainnetDefaultPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort();
|
2018-12-21 16:21:12 +01:00
|
|
|
if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
|
|
|
|
if (proTx.addr.GetPort() != mainnetDefaultPort) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port");
|
|
|
|
}
|
|
|
|
} else if (proTx.addr.GetPort() == mainnetDefaultPort) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port");
|
|
|
|
}
|
|
|
|
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!proTx.addr.IsIPv4()) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ProTx>
|
2018-11-01 22:57:10 +01:00
|
|
|
static bool CheckHashSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state)
|
2018-10-21 21:45:16 +02:00
|
|
|
{
|
|
|
|
std::string strError;
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError)) {
|
2018-10-21 21:45:16 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-10-21 21:45:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ProTx>
|
2018-11-01 22:57:10 +01:00
|
|
|
static bool CheckStringSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state)
|
|
|
|
{
|
|
|
|
std::string strError;
|
2018-11-06 09:54:23 +01:00
|
|
|
if (!CMessageSigner::VerifyMessage(keyID, proTx.vchSig, proTx.MakeSignString(), strError)) {
|
2018-11-01 22:57:10 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ProTx>
|
|
|
|
static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValidationState& state)
|
2018-10-21 21:45:16 +02:00
|
|
|
{
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) {
|
2018-10-21 21:45:16 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false);
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-10-21 21:45:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-25 16:29:50 +02:00
|
|
|
template <typename ProTx>
|
2018-11-06 09:54:23 +01:00
|
|
|
static bool CheckInputsHash(const CTransaction& tx, const ProTx& proTx, CValidationState& state)
|
2018-02-13 13:36:36 +01:00
|
|
|
{
|
|
|
|
uint256 inputsHash = CalcTxInputsHash(tx);
|
2018-10-26 07:03:14 +02:00
|
|
|
if (inputsHash != proTx.inputsHash) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-10-25 16:29:50 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-13 13:36:36 +01:00
|
|
|
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
|
|
|
{
|
2018-11-14 14:59:10 +01:00
|
|
|
if (tx.nType != TRANSACTION_PROVIDER_REGISTER) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
|
|
|
CProRegTx ptx;
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!GetTxPayload(tx, ptx)) {
|
2018-11-13 13:46:43 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-11-22 06:05:32 +01:00
|
|
|
if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
|
|
|
if (ptx.nType != 0) {
|
2018-10-23 13:15:38 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
|
|
|
if (ptx.nMode != 0) {
|
2018-10-23 13:15:38 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-10-26 07:03:14 +02:00
|
|
|
if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-11-14 14:31:56 +01:00
|
|
|
if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-08-31 15:31:59 +02:00
|
|
|
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");
|
|
|
|
}
|
2018-11-14 14:53:41 +01:00
|
|
|
// don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server)
|
2018-10-21 21:45:16 +02:00
|
|
|
if (payoutDest == CTxDestination(ptx.keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) {
|
2018-08-31 15:31:59 +02:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
|
|
|
|
}
|
|
|
|
|
2018-11-04 12:55:50 +01:00
|
|
|
// It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later
|
2018-02-13 13:36:36 +01:00
|
|
|
// If any of both is set, it must be valid however
|
2018-11-10 10:54:16 +01:00
|
|
|
if (ptx.addr != CService() && !CheckService(tx.GetHash(), ptx, state)) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return false;
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-10-26 07:03:14 +02:00
|
|
|
if (ptx.nOperatorReward > 10000) {
|
2018-02-13 13:36:36 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
2018-11-14 14:53:41 +01:00
|
|
|
CTxDestination collateralTxDest;
|
2018-10-25 16:29:50 +02:00
|
|
|
CKeyID keyForPayloadSig;
|
2018-11-10 10:54:16 +01:00
|
|
|
COutPoint collateralOutpoint;
|
2018-10-25 16:29:50 +02:00
|
|
|
|
|
|
|
if (!ptx.collateralOutpoint.hash.IsNull()) {
|
|
|
|
Coin coin;
|
|
|
|
if (!GetUTXOCoin(ptx.collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
|
|
|
|
}
|
|
|
|
|
2018-11-14 14:53:41 +01:00
|
|
|
if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
|
2018-10-25 16:29:50 +02:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH.
|
|
|
|
// Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx
|
2018-11-14 14:53:41 +01:00
|
|
|
if (!CBitcoinAddress(collateralTxDest).GetKeyID(keyForPayloadSig)) {
|
2018-10-25 16:29:50 +02:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-pkh");
|
|
|
|
}
|
2018-11-10 10:54:16 +01:00
|
|
|
|
|
|
|
collateralOutpoint = ptx.collateralOutpoint;
|
|
|
|
} else {
|
2018-11-14 14:49:32 +01:00
|
|
|
if (ptx.collateralOutpoint.n >= tx.vout.size()) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index");
|
|
|
|
}
|
|
|
|
if (tx.vout[ptx.collateralOutpoint.n].nValue != 1000 * COIN) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
|
|
|
|
}
|
2018-11-14 14:53:41 +01:00
|
|
|
|
|
|
|
if (!ExtractDestination(tx.vout[ptx.collateralOutpoint.n].scriptPubKey, collateralTxDest)) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
|
|
|
}
|
|
|
|
|
2018-11-10 10:54:16 +01:00
|
|
|
collateralOutpoint = COutPoint(tx.GetHash(), ptx.collateralOutpoint.n);
|
2018-10-25 16:29:50 +02:00
|
|
|
}
|
|
|
|
|
2018-11-14 14:53:41 +01:00
|
|
|
// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
|
|
|
|
// this check applies to internal and external collateral, but internal collaterals are not necessarely a P2PKH
|
|
|
|
if (collateralTxDest == CTxDestination(ptx.keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
|
|
|
}
|
|
|
|
|
2018-02-14 14:43:03 +01:00
|
|
|
if (pindexPrev) {
|
2019-07-09 07:59:57 +02:00
|
|
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
2018-11-10 10:54:16 +01:00
|
|
|
|
|
|
|
// only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
|
|
|
|
if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->collateralOutpoint != collateralOutpoint) {
|
|
|
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
2018-02-14 14:43:03 +01:00
|
|
|
}
|
|
|
|
|
2018-11-10 10:54:16 +01:00
|
|
|
// never allow duplicate keys, even if this ProTx would replace an existing MN
|
|
|
|
if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.pubKeyOperator)) {
|
|
|
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
2018-10-25 16:29:50 +02:00
|
|
|
}
|
|
|
|
|
2019-01-29 15:54:38 +01:00
|
|
|
if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) {
|
2018-10-21 21:45:16 +02:00
|
|
|
if (ptx.keyIDOwner != ptx.keyIDVoting) {
|
2018-02-14 14:43:03 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:57:10 +01:00
|
|
|
if (!CheckInputsHash(tx, ptx, state)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-25 16:29:50 +02:00
|
|
|
if (!keyForPayloadSig.IsNull()) {
|
|
|
|
// collateral is not part of this ProRegTx, so we must verify ownership of the collateral
|
2018-11-01 22:57:10 +01:00
|
|
|
if (!CheckStringSig(ptx, keyForPayloadSig, state)) {
|
2018-10-25 16:29:50 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// collateral is part of this ProRegTx, so we know the collateral is owned by the issuer
|
|
|
|
if (!ptx.vchSig.empty()) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 12:14:11 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
|
|
|
{
|
2018-11-14 14:59:10 +01:00
|
|
|
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
|
|
|
|
CProUpServTx ptx;
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!GetTxPayload(tx, ptx)) {
|
2018-11-13 13:46:43 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
|
2018-11-22 06:05:32 +01:00
|
|
|
if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) {
|
2018-03-12 12:14:11 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
|
2018-11-10 10:54:16 +01:00
|
|
|
if (!CheckService(ptx.proTxHash, ptx, state)) {
|
2018-03-12 12:14:11 +01:00
|
|
|
return false;
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
|
|
|
|
if (pindexPrev) {
|
2019-07-09 07:59:57 +02:00
|
|
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
2018-10-25 16:29:50 +02:00
|
|
|
auto mn = mnList.GetMN(ptx.proTxHash);
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!mn) {
|
2018-03-12 12:14:11 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
|
2018-11-10 10:54:16 +01:00
|
|
|
// don't allow updating to addresses already used by other MNs
|
|
|
|
if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) {
|
|
|
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
|
|
|
}
|
|
|
|
|
2018-03-12 12:14:11 +01:00
|
|
|
if (ptx.scriptOperatorPayout != CScript()) {
|
|
|
|
if (mn->nOperatorReward == 0) {
|
|
|
|
// don't allow to set operator reward payee in case no operatorReward was set
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
|
|
|
|
}
|
2018-11-15 13:24:46 +01:00
|
|
|
if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) {
|
2018-03-12 12:14:11 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// we can only check the signature if pindexPrev != NULL and the MN is known
|
2018-11-01 22:57:10 +01:00
|
|
|
if (!CheckInputsHash(tx, ptx, state)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-13 11:01:26 +02:00
|
|
|
if (!CheckHashSig(ptx, mn->pdmnState->pubKeyOperator.Get(), state)) {
|
2018-03-12 12:14:11 +01:00
|
|
|
return false;
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-12 12:14:11 +01:00
|
|
|
}
|
2018-02-13 13:36:36 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-19 08:44:00 +01:00
|
|
|
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
|
|
|
{
|
2018-11-14 14:59:10 +01:00
|
|
|
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
|
|
|
CProUpRegTx ptx;
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!GetTxPayload(tx, ptx)) {
|
2018-11-13 13:46:43 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
2018-11-22 06:05:32 +01:00
|
|
|
if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
|
|
|
if (ptx.nMode != 0) {
|
2018-10-23 13:15:38 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-11-14 14:31:56 +01:00
|
|
|
if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
|
|
|
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) {
|
2019-07-09 07:59:57 +02:00
|
|
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
2018-03-19 08:44:00 +01:00
|
|
|
auto dmn = mnList.GetMN(ptx.proTxHash);
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!dmn) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
2018-11-14 14:53:41 +01:00
|
|
|
// don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server)
|
2018-10-21 21:45:16 +02:00
|
|
|
if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
|
|
|
|
}
|
|
|
|
|
2018-09-12 13:13:00 +02:00
|
|
|
Coin coin;
|
2018-10-25 16:29:50 +02:00
|
|
|
if (!GetUTXOCoin(dmn->collateralOutpoint, coin)) {
|
2018-11-14 14:53:41 +01:00
|
|
|
// this should never happen (there would be no dmn otherwise)
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral");
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
|
|
|
|
CTxDestination collateralTxDest;
|
|
|
|
if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
|
|
|
}
|
|
|
|
if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) {
|
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
2018-09-12 13:13:00 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
|
2018-10-21 21:45:16 +02:00
|
|
|
if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) {
|
|
|
|
auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator);
|
2018-03-19 08:44:00 +01:00
|
|
|
if (ptx.proTxHash != otherDmn->proTxHash) {
|
|
|
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 15:54:38 +01:00
|
|
|
if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) {
|
2018-10-21 21:45:16 +02:00
|
|
|
if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:57:10 +01:00
|
|
|
if (!CheckInputsHash(tx, ptx, state)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!CheckHashSig(ptx, dmn->pdmnState->keyIDOwner, state)) {
|
2018-03-19 08:44:00 +01:00
|
|
|
return false;
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 08:44:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-19 12:29:59 +01:00
|
|
|
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
|
|
|
{
|
2018-11-14 14:59:10 +01:00
|
|
|
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
|
|
|
}
|
2018-03-19 12:29:59 +01:00
|
|
|
|
|
|
|
CProUpRevTx ptx;
|
2018-10-26 07:03:14 +02:00
|
|
|
if (!GetTxPayload(tx, ptx)) {
|
2018-11-13 13:46:43 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 12:29:59 +01:00
|
|
|
|
2018-11-22 06:05:32 +01:00
|
|
|
if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) {
|
2018-03-19 12:29:59 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 12:29:59 +01:00
|
|
|
|
2018-09-28 09:55:11 +02:00
|
|
|
// ptx.nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since
|
|
|
|
// ptx.nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0
|
2018-10-26 07:03:14 +02:00
|
|
|
if (ptx.nReason > CProUpRevTx::REASON_LAST) {
|
2018-03-19 12:29:59 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-reason");
|
2018-10-26 07:03:14 +02:00
|
|
|
}
|
2018-03-19 12:29:59 +01:00
|
|
|
|
|
|
|
if (pindexPrev) {
|
2019-07-09 07:59:57 +02:00
|
|
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
2018-03-19 12:29:59 +01:00
|
|
|
auto dmn = mnList.GetMN(ptx.proTxHash);
|
|
|
|
if (!dmn)
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
|
|
|
|
2018-11-01 22:57:10 +01:00
|
|
|
if (!CheckInputsHash(tx, ptx, state))
|
|
|
|
return false;
|
2019-06-13 11:01:26 +02:00
|
|
|
if (!CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator.Get(), state))
|
2018-03-19 12:29:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:57:10 +01:00
|
|
|
std::string CProRegTx::MakeSignString() const
|
|
|
|
{
|
|
|
|
std::string s;
|
|
|
|
|
|
|
|
// We only include the important stuff in the string form...
|
|
|
|
|
|
|
|
CTxDestination destPayout;
|
|
|
|
CBitcoinAddress addrPayout;
|
|
|
|
std::string strPayout;
|
|
|
|
if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) {
|
|
|
|
strPayout = addrPayout.ToString();
|
|
|
|
} else {
|
|
|
|
strPayout = HexStr(scriptPayout.begin(), scriptPayout.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
s += strPayout + "|";
|
|
|
|
s += strprintf("%d", nOperatorReward) + "|";
|
|
|
|
s += CBitcoinAddress(keyIDOwner).ToString() + "|";
|
|
|
|
s += CBitcoinAddress(keyIDVoting).ToString() + "|";
|
|
|
|
|
|
|
|
// ... and also the full hash of the payload as a protection agains malleability and replays
|
|
|
|
s += ::SerializeHash(*this).ToString();
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2018-02-13 13:36:36 +01:00
|
|
|
std::string CProRegTx::ToString() const
|
|
|
|
{
|
|
|
|
CTxDestination dest;
|
|
|
|
std::string payee = "unknown";
|
|
|
|
if (ExtractDestination(scriptPayout, dest)) {
|
|
|
|
payee = CBitcoinAddress(dest).ToString();
|
|
|
|
}
|
|
|
|
|
2019-01-11 11:05:58 +01:00
|
|
|
return strprintf("CProRegTx(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s)",
|
|
|
|
nVersion, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, CBitcoinAddress(keyIDOwner).ToString(), pubKeyOperator.ToString(), CBitcoinAddress(keyIDVoting).ToString(), payee);
|
2018-02-13 13:36:36 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 12:14:11 +01:00
|
|
|
std::string CProUpServTx::ToString() const
|
|
|
|
{
|
2018-03-19 08:44:00 +01:00
|
|
|
CTxDestination dest;
|
|
|
|
std::string payee = "unknown";
|
|
|
|
if (ExtractDestination(scriptOperatorPayout, dest)) {
|
|
|
|
payee = CBitcoinAddress(dest).ToString();
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:15:38 +02:00
|
|
|
return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, addr=%s, operatorPayoutAddress=%s)",
|
2018-11-06 09:54:23 +01:00
|
|
|
nVersion, proTxHash.ToString(), addr.ToString(), payee);
|
2018-03-12 12:14:11 +01:00
|
|
|
}
|
|
|
|
|
2018-03-19 08:44:00 +01:00
|
|
|
std::string CProUpRegTx::ToString() const
|
|
|
|
{
|
|
|
|
CTxDestination dest;
|
|
|
|
std::string payee = "unknown";
|
|
|
|
if (ExtractDestination(scriptPayout, dest)) {
|
|
|
|
payee = CBitcoinAddress(dest).ToString();
|
|
|
|
}
|
|
|
|
|
2019-01-11 11:05:58 +01:00
|
|
|
return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
|
|
|
|
nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), CBitcoinAddress(keyIDVoting).ToString(), payee);
|
2018-03-19 08:44:00 +01:00
|
|
|
}
|
|
|
|
|
2018-03-19 12:29:59 +01:00
|
|
|
std::string CProUpRevTx::ToString() const
|
|
|
|
{
|
|
|
|
return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)",
|
2018-11-06 09:54:23 +01:00
|
|
|
nVersion, proTxHash.ToString(), nReason);
|
2018-03-19 12:29:59 +01:00
|
|
|
}
|