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 SHA256 — dashsync-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
- 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).
- Consider a dedicated
PlatformNodeId newtype instead of PubkeyHash on ProviderRegistrationPayload to prevent the conventions from being conflated again (wire format unchanged).
- 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.
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-chainplatform_node_idvalues, 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 SHA256 —
dashsync-iOS,DSLocalMasternode.m:458-459:(
-SHA256is a single SHA-256,NSData+Dash.m:1115-1120; contrast the ECDSA owner key at:471which uses.hash160.) This matches the CometBFT/Tenderdash node-ID convention — node ID =SHA256(pubkey)[0..20]— which is what dashmate derives fromnode_key.jsonand 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 hash160key-wallet/src/transaction_checking/account_checker.rs:1034-1048: ProRegTx matching comparesreg.platform_node_idagainst the hash160-derived pool entries — so a real registration (SHA256[:20]) can never matchdash/src/blockdata/transaction/special_transaction/provider_registration.rs:111: the field is typedplatform_node_id: Option<PubkeyHash>— wire-compatible (20 bytes) but semantically it is a Tenderdash node id, not a public key hashSLIP-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
SHA256(ed25519_pubkey)[0..20]wherever key-wallet derives or matches it (identifier()for the platform-node context,derive_address_atpseudo-addressing for the ProviderPlatformKeys pool, and theaccount_checkerProRegTx comparison).PlatformNodeIdnewtype instead ofPubkeyHashonProviderRegistrationPayloadto prevent the conventions from being conflated again (wire format unchanged).Related: #878, #879, #881, dashpay/platform#4120.