Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions doc/release-notes-7600.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
RPC changes
-----------

- Normal and Evo `protx` registration and maintenance commands now share a
typed provider-transaction implementation with other wallet frontends. RPC
names and successful result formats are unchanged. Fixed: when a wallet
cannot completely sign the inputs it selected (e.g. `protx register_submit`
run in a different wallet than the one that prepared the registration),
the command now fails with a clear wallet error naming the problem instead
of reporting success with a partially signed transaction or attempting a
broadcast that failed mempool acceptance with a bare `-26` error. The
external-signing workflow (`protx register_prepare` followed by
`protx register_submit`) is unchanged.
`protx update_service` on a masternode whose state does not yield a usable
default fee source now returns an explicit "specify feeSourceAddress"
parameter error instead of an internal error. (#7600)
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ BITCOIN_CORE_H = \
evo/mnhftx.h \
evo/netinfo.h \
evo/providertx.h \
evo/providertx_service.h \
evo/simplifiedmns.h \
evo/smldiff.h \
evo/specialtx.h \
Expand Down Expand Up @@ -281,6 +282,7 @@ BITCOIN_CORE_H = \
interfaces/init.h \
interfaces/ipc.h \
interfaces/node.h \
interfaces/providertx.h \
interfaces/wallet.h \
kernel/blockmanager_opts.h \
kernel/chain.h \
Expand Down Expand Up @@ -539,6 +541,7 @@ libbitcoin_node_a_SOURCES = \
evo/mnauth.cpp \
evo/mnhftx.cpp \
evo/providertx.cpp \
evo/providertx_service.cpp \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/specialtx.cpp \
Expand Down
97 changes: 86 additions & 11 deletions src/evo/providertx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,30 +79,105 @@ bool IsPayoutListKeySafe(const MasternodePayoutShares& payouts, const CTxDestina
return true;
}

template <typename ProTx>
bool IsNetInfoTriviallyValid(const ProTx& proTx, TxValidationState& state)
static bool IsNetInfoTriviallyValid(const std::shared_ptr<NetInfoInterface>& net_info, MnType type, TxValidationState& state)
{
if (!proTx.netInfo->HasEntries(NetInfoPurpose::CORE_P2P)) {
if (!net_info->HasEntries(NetInfoPurpose::CORE_P2P)) {
// Mandatory for all nodes
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
}
if (proTx.nType == MnType::Regular) {
if (type == MnType::Regular) {
// Regular nodes shouldn't populate Platform-specific fields
if (proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
if (net_info->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) || net_info->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
}
}
if (proTx.netInfo->CanStorePlatform() && proTx.nType == MnType::Evo) {
if (net_info->CanStorePlatform() && type == MnType::Evo) {
// Platform fields are mandatory for EvoNodes
if (!proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
!proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
if (!net_info->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) || !net_info->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
}
}
return true;
}

static bool CheckNetInfo(const NetInfoInterface& net_info, TxValidationState& state)
{
switch (net_info.Validate()) {
case NetInfoStatus::BadAddress:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr");
case NetInfoStatus::BadPort:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-port");
case NetInfoStatus::BadType:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-type");
case NetInfoStatus::NotRoutable:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-unroutable");
case NetInfoStatus::Malformed:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
case NetInfoStatus::Success:
return true;
case NetInfoStatus::BadInput:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-entry");
case NetInfoStatus::Duplicate:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
case NetInfoStatus::MaxLimit:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-maxlimit");
}
assert(false);
}

bool CheckProviderNetworkFields(const std::shared_ptr<NetInfoInterface>& net_info, MnType type, uint16_t version,
const uint160* platform_node_id, uint16_t platform_p2p_port,
uint16_t platform_http_port, bool allow_empty, TxValidationState& state)
{
if (!net_info || net_info->CanStorePlatform() != (version >= ProTxVersion::ExtAddr)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
}
if (net_info->IsEmpty()) {
if (!allow_empty) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
}
} else {
if (!IsNetInfoTriviallyValid(net_info, type, state) || !CheckNetInfo(*net_info, state)) {
return false;
}
}

if (type != MnType::Evo) return true;
if (platform_node_id && platform_node_id->IsNull()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-nodeid");
}
if (version >= ProTxVersion::ExtAddr) {
if (platform_p2p_port != 0) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
}
if (platform_http_port != 0) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
}
return true;
}

if (::IsNodeOnMainnet()) {
if (platform_p2p_port != ::MainParams().GetDefaultPlatformP2PPort()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
}
if (platform_http_port != ::MainParams().GetDefaultPlatformHTTPPort()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
}
}
if (platform_p2p_port == ::MainParams().GetDefaultPort()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
}
if (platform_http_port == ::MainParams().GetDefaultPort()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
}

const uint16_t core_port{net_info->GetPrimary().GetPort()};
if (platform_p2p_port == platform_http_port || platform_p2p_port == core_port || platform_http_port == core_port) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports");
}
return true;
}

bool CProRegTx::IsTriviallyValid(TxValidationState& state) const
{
if (nVersion == 0 || nVersion > ProTxVersion::ExtAddr) {
Expand All @@ -129,7 +204,7 @@ bool CProRegTx::IsTriviallyValid(TxValidationState& state) const
if (netInfo->CanStorePlatform() != (nVersion >= ProTxVersion::ExtAddr)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
}
if (!netInfo->IsEmpty() && !IsNetInfoTriviallyValid(*this, state)) {
if (!netInfo->IsEmpty() && !IsNetInfoTriviallyValid(netInfo, nType, state)) {
// pass the state returned by the function above
return false;
}
Expand Down Expand Up @@ -199,7 +274,7 @@ bool CProUpServTx::IsTriviallyValid(TxValidationState& state) const
if (netInfo->IsEmpty()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
}
if (!IsNetInfoTriviallyValid(*this, state)) {
if (!IsNetInfoTriviallyValid(netInfo, nType, state)) {
// pass the state returned by the function above
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions src/evo/providertx.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ template<class T>
[[nodiscard]] std::string PayoutListToString(const MasternodePayoutShares& payouts);
[[nodiscard]] UniValue PayoutListToJson(const MasternodePayoutShares& payouts);

/** Validate all provider network fields using the same rules as special transaction validation.
* Pass nullptr for platform_node_id when validating endpoint input separately from the rest of a payload. */
[[nodiscard]] bool CheckProviderNetworkFields(const std::shared_ptr<NetInfoInterface>& net_info, MnType type,
uint16_t version, const uint160* platform_node_id, uint16_t platform_p2p_port,
uint16_t platform_http_port, bool allow_empty, TxValidationState& state);

class CProRegTx
{
public:
Expand Down
Loading
Loading