chore: initial commit for v0.0.1
DChain single-node blockchain + React Native messenger client. Core: - PBFT consensus with multi-sig validator admission + equivocation slashing - BadgerDB + schema migration scaffold (CurrentSchemaVersion=0) - libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1) - Native Go contracts (username_registry) alongside WASM (wazero) - WebSocket gateway with topic-based fanout + Ed25519-nonce auth - Relay mailbox with NaCl envelope encryption (X25519 + Ed25519) - Prometheus /metrics, per-IP rate limit, body-size cap Deployment: - Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus - 3-node dev compose (docker-compose.yml) with mocked internet topology - 3-validator prod compose (deploy/prod/) for federation - Auto-update from Gitea via /api/update-check + systemd timer - Build-time version injection (ldflags → node --version) - UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER) Client (client-app/): - Expo / React Native / NativeWind - E2E NaCl encryption, typing indicator, contact requests - Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch Documentation: - README.md, CHANGELOG.md, CONTEXT.md - deploy/single/README.md with 6 operator scenarios - deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design - docs/contracts/*.md per contract
This commit is contained in:
509
blockchain/types.go
Normal file
509
blockchain/types.go
Normal file
@@ -0,0 +1,509 @@
|
||||
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:<pubkey>` 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:<contractID>.
|
||||
// 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:<contractID>:<blockHeight_20d>:<seq_05d>.
|
||||
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:<tokenID>.
|
||||
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:<nftID>.
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user