Skip to content

key-wallet: platform node id uses HASH160 but the canonical value is the Tenderdash node ID (SHA256[0..20]) — platform-node matching can never hit on-chain registrations #883

Description

@QuantumExplorer

Summary

key-wallet computes the masternode platform node id as HASH160(ed25519_pubkey) (RIPEMD160∘SHA256), but the canonical value — what Tenderdash announces on the platform P2P network and what ProRegTx builders write on-chain — is the Tenderdash/CometBFT node ID: the first 20 bytes of a single SHA256 of the Ed25519 public key. The result: key-wallet's derived node ids never match real on-chain platform_node_id values, so provider-key matching (#863/#864) and any wallet UI can never associate a wallet's platform-node key with its own evonode registration.

Follow-up in the #878/#879/#881 series: with #879/#881 in place, operator BLS keys now match DashSync end-to-end (user-verified with a shared mnemonic); the platform node id is the remaining divergence.

Evidence

DashSync builds ProRegTx with truncated SHA256dashsync-iOS, DSLocalMasternode.m:458-459:

UInt256 platformNodeHash = [DSKeyManager publicKeyData:platformNodeKey].SHA256;  // single SHA-256
UInt160 platformNodeID   = *(UInt160 *)&platformNodeHash;                        // first 20 bytes

(-SHA256 is a single SHA-256, NSData+Dash.m:1115-1120; contrast the ECDSA owner key at :471 which uses .hash160.) This matches the CometBFT/Tenderdash node-ID convention — node ID = SHA256(pubkey)[0..20] — which is what dashmate derives from node_key.json and what the evonode actually advertises, i.e. the value the on-chain field must carry for platform P2P to work.

rust-dashcore uses HASH160 instead:

  • key-wallet/src/derivation_slip10.rs:184-191 (identifier()): hash160::Hash::hash(&pubkey_bytes)
  • key-wallet/src/account/eddsa_account.rs:350-356 (derive_address_at): P2PKH pseudo-address from hash160
  • key-wallet/src/transaction_checking/account_checker.rs:1034-1048: ProRegTx matching compares reg.platform_node_id against the hash160-derived pool entries — so a real registration (SHA256[:20]) can never match
  • dash/src/blockdata/transaction/special_transaction/provider_registration.rs:111: the field is typed platform_node_id: Option<PubkeyHash> — wire-compatible (20 bytes) but semantically it is a Tenderdash node id, not a public key hash

SLIP-0010 derivation itself appears aligned: both sides use m/9'/coin'/3'/4'/index' (all hardened) with standard SLIP-0010; BLS parity post-#879 confirms the seed/path plumbing. Caveat: DashSharedCore ships as a compiled binary locally, and the wallet UIs display keys in different encodings (base64 vs hex), so raw-pubkey equality is assumed rather than byte-verified — if a residual key-level divergence exists it would be inside DashSharedCore's SLIP-0010 primitive, but the hashing divergence documented above is real and sufficient to break matching regardless.

Proposed fix

  1. Compute the platform node id as SHA256(ed25519_pubkey)[0..20] wherever key-wallet derives or matches it (identifier() for the platform-node context, derive_address_at pseudo-addressing for the ProviderPlatformKeys pool, and the account_checker ProRegTx comparison).
  2. Consider a dedicated PlatformNodeId newtype instead of PubkeyHash on ProviderRegistrationPayload to prevent the conventions from being conflated again (wire format unchanged).
  3. Pin with a test vector: known seed → ed25519 pubkey → node id, cross-checked against a dashmate/Tenderdash-generated node ID for the same key.

Related: #878, #879, #881, dashpay/platform#4120.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions