Blockchains

Decred

secp256k1 with BLAKE-256 in every hash. Addresses start with Ds. No mnemonic derivation and that's on purpose.
IDdecred07 / 24SLIP-44 42'
Chain

Decred

secp256k1

  • legacy ECDSA P2PKH
Formats
1
Coin type
42'
Key 1
DsmcYVbP1N…A7nLDx

Access

Loadblockchains.decred()()
Import@agntn/keys/blockchains/decred
Walkkeyspace in the browser

Load it

js
import { useBlockchain, blockchains } from "@agntn/keys";

const decredChain = useBlockchain(await blockchains.decred()());
const testnet = useBlockchain(await blockchains.decred({ network: "testnet" })());

Mainnet and testnet3 only, anything else throws in the constructor.

The address

Decred looks like Bitcoin from a distance and swaps the hash the moment you get close. RIPEMD160(BLAKE256(pubkey)), a two byte prefix in front, then base58check with a double BLAKE-256 checksum instead of double SHA-256.

NetworkPrefixStarts with
mainnet07 3fDs
testnet0f 21Ts
js
const privateKey = "0000000000000000000000000000000000000000000000000000000000000001";
const publicKey = decredChain.getKeyPublic(privateKey);

decredChain.getAddress(publicKey); // DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx
testnet.getAddress(publicKey); // TsmfmUitQApgnNxQypdGd2x36djCCpDpERU

That's the only format. Ask for "segwit" or anything else and getAddress throws a RangeError instead of quietly handing you a legacy address. Schnorr and Ed25519 keys, which dcrd also knows, aren't implemented.

Validation

js
decredChain.validateAddress("DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx"); // true
decredChain.validateAddress("TsmfmUitQApgnNxQypdGd2x36djCCpDpERU"); // false, testnet on a mainnet driver

Base58 decode with the BLAKE-256 checksum, 22 bytes of payload, and both prefix bytes have to match the driver's network. A Bitcoin address fails at the checksum before the prefix even gets a look.

Mnemonics

deriveHDWallet throws. dcrd's HD derivation strips leading zero bytes from keys where BIP32 keeps them, so a plain BIP32 walk lands on the wrong key for some paths and looks perfectly fine doing it. A driver that's silently wrong is worse than one that refuses, so it refuses. deriveWallet with a private key works as usual, and so does WIF:

js
import { encode, decode } from "@agntn/keys/wif";

encode(privateKey, { chain: "decred" }); // PmQdGRXNZdAgEqwDZMLAF2XSQRLFeSFKi4HLPbdW3kC66HegjYtxq
decode(wif, { chain: "decred" }).privateKey;

Decred WIF has its own layout: two prefix bytes, a scheme byte that's 0x00 for ECDSA, the key, and a single BLAKE-256 checksum. Only the compressed form exists, compressed: false throws.

Signing

"Decred Signed Message:\n" with compact size lengths in front of the preamble and the message, one round of BLAKE-256, then secp256k1. 64 bytes of r||s in hex, verified with verifyMessage on the same driver. { recovered: true } gives dcrd's base64 with a header byte, and both of dcrd's verifymessage vectors come out byte for byte. dcrd stops at header 34, so recoverMessageSigner throws on the BIP137 headers above it.

Where it lives

src/blockchains/decred.ts, all of it. It extends AbstractBlockchain directly, not the Bitcoin base, because nothing except the curve is shared.