package blockchain import ( "crypto/sha256" "encoding/binary" "time" ) // EventType defines what kind of event a transaction represents. type EventType string const ( EventRegisterKey EventType = "REGISTER_KEY" EventCreateChannel EventType = "CREATE_CHANNEL" EventAddMember EventType = "ADD_MEMBER" EventOpenPayChan EventType = "OPEN_PAY_CHAN" EventClosePayChan EventType = "CLOSE_PAY_CHAN" EventTransfer EventType = "TRANSFER" EventRelayProof EventType = "RELAY_PROOF" EventRegisterRelay EventType = "REGISTER_RELAY" // node advertises relay service EventBindWallet EventType = "BIND_WALLET" // node binds a payout wallet address EventSlash EventType = "SLASH" // penalise a misbehaving validator EventHeartbeat EventType = "HEARTBEAT" // liveness ping from a node EventBlockReward EventType = "BLOCK_REWARD" // synthetic tx indexed on block commit EventContactRequest EventType = "CONTACT_REQUEST" // paid first-contact request (ICQ-style) EventAcceptContact EventType = "ACCEPT_CONTACT" // recipient accepts a pending request EventBlockContact EventType = "BLOCK_CONTACT" // recipient blocks a sender EventAddValidator EventType = "ADD_VALIDATOR" // existing validator adds a new one EventRemoveValidator EventType = "REMOVE_VALIDATOR" // existing validator removes one (or self-removal) EventDeployContract EventType = "DEPLOY_CONTRACT" // deploy a WASM smart contract EventCallContract EventType = "CALL_CONTRACT" // call a method on a deployed contract EventStake EventType = "STAKE" // lock tokens as validator stake EventUnstake EventType = "UNSTAKE" // release staked tokens back to balance EventIssueToken EventType = "ISSUE_TOKEN" // create a new fungible token EventTransferToken EventType = "TRANSFER_TOKEN" // transfer fungible tokens between addresses EventBurnToken EventType = "BURN_TOKEN" // destroy fungible tokens EventMintNFT EventType = "MINT_NFT" // mint a new non-fungible token EventTransferNFT EventType = "TRANSFER_NFT" // transfer NFT ownership EventBurnNFT EventType = "BURN_NFT" // burn (destroy) an NFT ) // Token amounts are stored in micro-tokens (µT). // 1 token = 1_000_000 µT const ( MicroToken uint64 = 1 Token uint64 = 1_000_000 // MinFee is the minimum transaction fee paid to the block validator. // Validators earn fees as their only income — no block reward minting. MinFee uint64 = 1_000 // 0.001 T per transaction // GenesisAllocation is a one-time mint at block 0 for the bootstrap validator. // All subsequent token supply comes only from re-distribution of existing balances. GenesisAllocation uint64 = 21_000_000 * Token // 21 million T, fixed supply // SlashAmount is the penalty deducted from a misbehaving validator's balance. SlashAmount uint64 = 50 * Token // RegistrationFee is the one-time fee to register an identity on-chain // (EventRegisterKey). Paid to the block validator. High enough to deter // Sybil attacks while remaining affordable. RegistrationFee uint64 = 1_000_000 // 1 T // MinContactFee is the minimum amount a sender must pay the recipient when // submitting an EventContactRequest (anti-spam; goes directly to recipient). MinContactFee uint64 = 5_000 // 0.005 T ) // Transaction is the atomic unit recorded in a block. // Bodies of messages are NEVER stored here — only identity/channel events. type Transaction struct { ID string `json:"id"` Type EventType `json:"type"` From string `json:"from"` // hex-encoded Ed25519 public key To string `json:"to"` // hex-encoded Ed25519 public key (if applicable) Amount uint64 `json:"amount"` // µT to transfer (for TRANSFER type) Fee uint64 `json:"fee"` // µT paid to the block validator Memo string `json:"memo,omitempty"` Payload []byte `json:"payload"` // JSON-encoded event-specific data Signature []byte `json:"signature"` // Ed25519 sig over canonical bytes Timestamp time.Time `json:"timestamp"` } // RegisterKeyPayload is embedded in EventRegisterKey transactions. type RegisterKeyPayload struct { PubKey string `json:"pub_key"` // hex-encoded Ed25519 public key Nickname string `json:"nickname"` // human-readable, non-unique PowNonce uint64 `json:"pow_nonce"` // proof-of-work nonce (Sybil barrier) PowTarget string `json:"pow_target"` X25519PubKey string `json:"x25519_pub_key,omitempty"` // hex Curve25519 key for E2E messaging } // CreateChannelPayload is embedded in EventCreateChannel transactions. type CreateChannelPayload struct { ChannelID string `json:"channel_id"` Title string `json:"title"` IsPublic bool `json:"is_public"` } // RegisterRelayPayload is embedded in EventRegisterRelay transactions. // A node publishes this to advertise itself as a relay service provider. // Clients look up relay nodes via GET /api/relays. type RegisterRelayPayload struct { // X25519PubKey is the hex-encoded Curve25519 public key for NaCl envelope encryption. // Senders use this key to seal messages addressed to this relay node. X25519PubKey string `json:"x25519_pub_key"` // FeePerMsgUT is the relay fee the node charges per delivered envelope (in µT). FeePerMsgUT uint64 `json:"fee_per_msg_ut"` // Multiaddr is the optional libp2p multiaddr string for direct connections. Multiaddr string `json:"multiaddr,omitempty"` } // RelayProofPayload proves that a relay/recipient node received an envelope. // The sender pre-authorises the fee by signing FeeAuthBytes(EnvelopeID, FeeUT). // On-chain the fee is pulled from the sender's balance and credited to the relay. type RelayProofPayload struct { // EnvelopeID is the stable identifier of the delivered envelope (hex). EnvelopeID string `json:"envelope_id"` // EnvelopeHash is SHA-256(nonce || ciphertext) — prevents double-claiming. EnvelopeHash []byte `json:"envelope_hash"` // SenderPubKey is the Ed25519 public key of the envelope sender (hex). SenderPubKey string `json:"sender_pub_key"` // FeeUT is the delivery fee the relay claims from the sender's balance. FeeUT uint64 `json:"fee_ut"` // FeeSig is the sender's Ed25519 signature over FeeAuthBytes(EnvelopeID, FeeUT). // This authorises the relay to pull FeeUT from the sender's on-chain balance. FeeSig []byte `json:"fee_sig"` // RelayPubKey is the Ed25519 public key of the relay claiming the fee (hex). RelayPubKey string `json:"relay_pub_key"` // DeliveredAt is the unix timestamp of delivery. DeliveredAt int64 `json:"delivered_at"` // RecipientSig is the recipient's optional Ed25519 sig over EnvelopeHash, // proving the message was successfully decrypted (not required for fee claim). RecipientSig []byte `json:"recipient_sig,omitempty"` } // FeeAuthBytes returns the canonical byte string that the sender must sign // to pre-authorise a relay fee pull. The relay includes this signature in // RelayProofPayload.FeeSig when submitting the proof on-chain. // // Format: SHA-256("relay-fee:" || envelopeID || uint64BE(feeUT)) func FeeAuthBytes(envelopeID string, feeUT uint64) []byte { h := sha256.New() h.Write([]byte("relay-fee:")) h.Write([]byte(envelopeID)) var b [8]byte binary.BigEndian.PutUint64(b[:], feeUT) h.Write(b[:]) return h.Sum(nil) } // TransferPayload carries an optional memo for token transfers. type TransferPayload struct { Memo string `json:"memo,omitempty"` } // BindWalletPayload links a node's signing key to a separate payout wallet. // After this tx is committed, block fees and relay fees are credited to // WalletPubKey instead of the node's own pub key. type BindWalletPayload struct { WalletPubKey string `json:"wallet_pub_key"` WalletAddr string `json:"wallet_addr"` } // SlashPayload is submitted by a validator to penalise a misbehaving peer. type SlashPayload struct { OffenderPubKey string `json:"offender_pub_key"` Reason string `json:"reason"` // "double_vote" | "downtime" | "equivocation" Evidence []byte `json:"evidence,omitempty"` } // HeartbeatPayload is a periodic liveness signal published by active nodes. // It carries the node's current chain height so peers can detect lagging nodes. // Heartbeats cost MinFee (paid to the block validator) and earn no reward — // they exist to build reputation and prove liveness. type HeartbeatPayload struct { PubKey string `json:"pub_key"` ChainHeight uint64 `json:"chain_height"` PeerCount int `json:"peer_count"` Version string `json:"version"` } // OpenPayChanPayload locks deposits from two parties into a payment channel. type OpenPayChanPayload struct { ChannelID string `json:"channel_id"` PartyA string `json:"party_a"` PartyB string `json:"party_b"` DepositA uint64 `json:"deposit_a_ut"` DepositB uint64 `json:"deposit_b_ut"` ExpiryBlock uint64 `json:"expiry_block"` SigB []byte `json:"sig_b"` // PartyB's Ed25519 sig over channel params } // ClosePayChanPayload settles a payment channel and distributes balances. type ClosePayChanPayload struct { ChannelID string `json:"channel_id"` BalanceA uint64 `json:"balance_a_ut"` BalanceB uint64 `json:"balance_b_ut"` Nonce uint64 `json:"nonce"` SigA []byte `json:"sig_a"` SigB []byte `json:"sig_b"` } // PayChanState is stored on-chain for each open payment channel. type PayChanState struct { ChannelID string `json:"channel_id"` PartyA string `json:"party_a"` PartyB string `json:"party_b"` DepositA uint64 `json:"deposit_a_ut"` DepositB uint64 `json:"deposit_b_ut"` ExpiryBlock uint64 `json:"expiry_block"` OpenedBlock uint64 `json:"opened_block"` Nonce uint64 `json:"nonce"` Closed bool `json:"closed"` } // BlockRewardPayload is attached to synthetic BLOCK_REWARD transactions. // These are index-only records so the explorer can show validator fee income. // There is no minting — the FeeReward comes from existing transaction fees. type BlockRewardPayload struct { ValidatorPubKey string `json:"validator_pub_key"` TargetPubKey string `json:"target_pub_key"` FeeReward uint64 `json:"fee_reward_ut"` TotalReward uint64 `json:"total_reward_ut"` } // ContactRequestPayload is embedded in EventContactRequest transactions. // The sender pays tx.Amount directly to the recipient (anti-spam fee). // A pending contact record is stored on-chain for the recipient to accept or block. type ContactRequestPayload struct { Intro string `json:"intro,omitempty"` // optional plaintext intro (≤ 280 chars) } // AcceptContactPayload is embedded in EventAcceptContact transactions. // tx.From accepts a pending request from tx.To. type AcceptContactPayload struct{} // BlockContactPayload is embedded in EventBlockContact transactions. // tx.From blocks tx.To; future contact requests from tx.To are rejected. type BlockContactPayload struct { Reason string `json:"reason,omitempty"` } // ChannelMember records a participant in a channel together with their // X25519 public key. The key is cached on-chain (written during ADD_MEMBER) // so channel senders don't have to fan out a separate /api/identity lookup // per recipient on every message — they GET /api/channels/:id/members // once and seal N envelopes in a loop. type ChannelMember struct { PubKey string `json:"pub_key"` // Ed25519 hex X25519PubKey string `json:"x25519_pub_key"` // optional; empty if member hasn't registered Address string `json:"address"` } // AddMemberPayload is embedded in EventAddMember transactions. // tx.From adds tx.To as a member of the specified channel. // If tx.To is empty, tx.From is added (self-join for public channels). type AddMemberPayload struct { ChannelID string `json:"channel_id"` } // AddValidatorPayload is embedded in EventAddValidator transactions. // tx.From must already be a validator; tx.To is the new validator's pub key. // // Admission is gated by two things: // 1. Stake: the candidate (tx.To) must have STAKE'd at least // MinValidatorStake beforehand. Prevents anyone spinning up a free // validator without economic buy-in. // 2. Multi-sig: at least ⌈2/3⌉ of the CURRENT validator set must approve. // The tx sender counts as one; remaining approvals go in CoSignatures. // For a 1-validator chain (fresh genesis / tests) sender alone is 2/3, // so CoSignatures can be empty — backward-compat is preserved. type AddValidatorPayload struct { Reason string `json:"reason,omitempty"` CoSignatures []ValidatorCoSig `json:"cosigs,omitempty"` } // ValidatorCoSig is an off-chain-assembled approval from one existing // validator for a specific candidate admission. The signature is over the // canonical digest returned by AdmitDigest(candidatePubKeyHex). type ValidatorCoSig struct { PubKey string `json:"pubkey"` // Ed25519 hex of a current validator Signature []byte `json:"signature"` // Ed25519 signature over AdmitDigest(candidate) } // AdmitDigest returns the canonical bytes a validator signs to approve // admitting `candidatePubHex` as a new validator. Stable across implementations // so co-sigs collected off-chain verify identically on-chain. func AdmitDigest(candidatePubHex string) []byte { h := sha256.New() h.Write([]byte("DCHAIN-ADD-VALIDATOR\x00")) h.Write([]byte(candidatePubHex)) return h.Sum(nil) } // MinValidatorStake is the minimum µT a candidate must have locked in // `stake:` before an ADD_VALIDATOR naming them is accepted. // 1 T = 1_000_000 µT — small enough that testnets can afford it easily, // large enough to deter "register 100 fake validators to 51%-attack". const MinValidatorStake uint64 = 1_000_000 // RemoveValidatorPayload is embedded in EventRemoveValidator transactions. // tx.From must be a validator; tx.To is the validator to remove. // // Two legitimate use cases: // 1. Self-removal (tx.From == tx.To): always allowed, no cosigs needed. // Lets a validator gracefully leave the set without requiring others. // 2. Forced removal (tx.From != tx.To): requires ⌈2/3⌉ cosigs of the // current validator set — same pattern as ADD_VALIDATOR. Stops a // single validator from unilaterally kicking peers. // // The signed payload is AdmitDigest(tx.To) but with the domain byte flipped // — see RemoveDigest below. This prevents a cosig collected for "admit X" // from being replayed as "remove X". type RemoveValidatorPayload struct { Reason string `json:"reason,omitempty"` CoSignatures []ValidatorCoSig `json:"cosigs,omitempty"` } // RemoveDigest is the canonical bytes a validator signs to approve removing // `targetPubHex` from the set. Distinct from AdmitDigest so signatures // can't be cross-replayed between add and remove operations. func RemoveDigest(targetPubHex string) []byte { h := sha256.New() h.Write([]byte("DCHAIN-REMOVE-VALIDATOR\x00")) h.Write([]byte(targetPubHex)) return h.Sum(nil) } // DeployContractPayload is embedded in EventDeployContract transactions. // WASMBase64 is the base64-encoded WASM binary. It is stored in the tx so that // nodes can replay the chain from genesis and re-derive contract state. type DeployContractPayload struct { WASMBase64 string `json:"wasm_b64"` ABIJson string `json:"abi_json"` InitArgs string `json:"init_args_json,omitempty"` } // CallContractPayload is embedded in EventCallContract transactions. type CallContractPayload struct { ContractID string `json:"contract_id"` Method string `json:"method"` ArgsJSON string `json:"args_json,omitempty"` GasLimit uint64 `json:"gas_limit"` } // ContractRecord is stored in BadgerDB at contract:. // WASMBytes is NOT in the block; it is derived from the deploy tx payload on replay. type ContractRecord struct { ContractID string `json:"contract_id"` WASMBytes []byte `json:"wasm_bytes"` ABIJson string `json:"abi_json"` DeployerPub string `json:"deployer_pub"` DeployedAt uint64 `json:"deployed_at"` // block height } // MinDeployFee is the minimum fee for a DEPLOY_CONTRACT transaction. // Covers storage costs for the WASM binary. const MinDeployFee uint64 = 10_000 // 0.01 T // MinCallFee is the minimum base fee for a CALL_CONTRACT transaction. // Gas costs are billed on top of this. const MinCallFee uint64 = MinFee // ContractLogEntry is one log message emitted by a contract via env.log(). // Stored in BadgerDB at clog:::. type ContractLogEntry struct { ContractID string `json:"contract_id"` BlockHeight uint64 `json:"block_height"` TxID string `json:"tx_id"` Seq int `json:"seq"` Message string `json:"message"` } // GasPrice is the cost in µT per 1 gas unit consumed during contract execution. const GasPrice uint64 = 1 // 1 µT per gas unit // MinStake is the minimum amount a validator must stake. const MinStake uint64 = 1_000 * Token // 1000 T // MinIssueTokenFee is the fee required to issue a new token. const MinIssueTokenFee uint64 = 100_000 // 0.1 T // StakePayload is embedded in EventStake transactions. // tx.Amount holds the amount to stake; tx.Fee is the transaction fee. type StakePayload struct{} // UnstakePayload is embedded in EventUnstake transactions. // The entire current stake is returned to the staker's balance. type UnstakePayload struct{} // IssueTokenPayload is embedded in EventIssueToken transactions. // The new token is credited to tx.From with TotalSupply units. type IssueTokenPayload struct { Name string `json:"name"` // human-readable token name, e.g. "My Token" Symbol string `json:"symbol"` // ticker symbol, e.g. "MTK" Decimals uint8 `json:"decimals"` // decimal places, e.g. 6 → 1 token = 1_000_000 base units TotalSupply uint64 `json:"total_supply"` // initial supply in base units } // TransferTokenPayload is embedded in EventTransferToken transactions. // tx.To is the recipient; tx.Amount is ignored (use payload Amount). type TransferTokenPayload struct { TokenID string `json:"token_id"` Amount uint64 `json:"amount"` // in base units } // BurnTokenPayload is embedded in EventBurnToken transactions. type BurnTokenPayload struct { TokenID string `json:"token_id"` Amount uint64 `json:"amount"` // in base units } // TokenRecord is stored in BadgerDB at token:. type TokenRecord struct { TokenID string `json:"token_id"` Name string `json:"name"` Symbol string `json:"symbol"` Decimals uint8 `json:"decimals"` TotalSupply uint64 `json:"total_supply"` // current (may decrease via burns) Issuer string `json:"issuer"` // creator pubkey IssuedAt uint64 `json:"issued_at"` // block height } // MinMintNFTFee is the fee required to mint a new NFT. const MinMintNFTFee uint64 = 10_000 // 0.01 T // MintNFTPayload is embedded in EventMintNFT transactions. type MintNFTPayload struct { Name string `json:"name"` // human-readable name Description string `json:"description,omitempty"` URI string `json:"uri,omitempty"` // off-chain metadata URI (IPFS, https, etc.) Attributes string `json:"attributes,omitempty"` // JSON string of trait attributes } // TransferNFTPayload is embedded in EventTransferNFT transactions. // tx.To is the new owner; tx.From must be current owner. type TransferNFTPayload struct { NFTID string `json:"nft_id"` } // BurnNFTPayload is embedded in EventBurnNFT transactions. type BurnNFTPayload struct { NFTID string `json:"nft_id"` } // NFTRecord is stored in BadgerDB at nft:. type NFTRecord struct { NFTID string `json:"nft_id"` Name string `json:"name"` Description string `json:"description,omitempty"` URI string `json:"uri,omitempty"` Attributes string `json:"attributes,omitempty"` Owner string `json:"owner"` // current owner pubkey Issuer string `json:"issuer"` // original minter pubkey MintedAt uint64 `json:"minted_at"` // block height Burned bool `json:"burned,omitempty"` } // ContactStatus is the state of a contact relationship. type ContactStatus string const ( ContactPending ContactStatus = "pending" ContactAccepted ContactStatus = "accepted" ContactBlocked ContactStatus = "blocked" ) // ContactInfo is returned by the contacts API. type ContactInfo struct { RequesterPub string `json:"requester_pub"` RequesterAddr string `json:"requester_addr"` Status ContactStatus `json:"status"` Intro string `json:"intro,omitempty"` FeeUT uint64 `json:"fee_ut"` TxID string `json:"tx_id"` CreatedAt int64 `json:"created_at"` } // IdentityInfo is returned by GET /api/identity/{pubkey}. type IdentityInfo struct { PubKey string `json:"pub_key"` Address string `json:"address"` X25519Pub string `json:"x25519_pub"` // hex Curve25519 key; empty if not published Nickname string `json:"nickname"` Registered bool `json:"registered"` // true if REGISTER_KEY tx was committed } // ConsensusMessage types used by the PBFT engine over the P2P layer. type MsgType string const ( MsgPrePrepare MsgType = "PRE_PREPARE" MsgPrepare MsgType = "PREPARE" MsgCommit MsgType = "COMMIT" MsgViewChange MsgType = "VIEW_CHANGE" MsgNewView MsgType = "NEW_VIEW" ) // ConsensusMsg is the envelope sent between validators. type ConsensusMsg struct { Type MsgType `json:"type"` View uint64 `json:"view"` SeqNum uint64 `json:"seq_num"` BlockHash []byte `json:"block_hash"` Block *Block `json:"block,omitempty"` From string `json:"from"` Signature []byte `json:"signature"` }