refactor(types,consensus,chain): apply AX design principles across public API
Migrate types/asset.go from fmt.Errorf to coreerr.E() for consistency with the rest of the types package. Add usage-example comments (AX-2) to all key exported functions in consensus/, chain/, and types/ so agents can see concrete calling patterns without reading implementation details. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
123047bebd
commit
caf83faf39
11 changed files with 72 additions and 19 deletions
|
|
@ -19,6 +19,9 @@ type Chain struct {
|
|||
}
|
||||
|
||||
// New creates a Chain backed by the given store.
|
||||
//
|
||||
// s, _ := store.New("~/.lethean/chain/chain.db")
|
||||
// blockchain := chain.New(s)
|
||||
func New(s *store.Store) *Chain {
|
||||
return &Chain{store: s}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,12 +76,16 @@ func (c *Chain) nextDifficultyWith(height uint64, forks []config.HardFork, baseT
|
|||
|
||||
// NextDifficulty computes the expected PoW difficulty for the block at the
|
||||
// given height. Pre-HF6 the target is 120s; post-HF6 it doubles to 240s.
|
||||
//
|
||||
// diff, err := blockchain.NextDifficulty(nextHeight, config.MainnetForks)
|
||||
func (c *Chain) NextDifficulty(height uint64, forks []config.HardFork) (uint64, error) {
|
||||
return c.nextDifficultyWith(height, forks, config.DifficultyPowTarget, config.DifficultyPowTargetHF6)
|
||||
}
|
||||
|
||||
// NextPoSDifficulty computes the expected PoS difficulty for the block at the
|
||||
// given height. Pre-HF6 the target is 120s; post-HF6 it doubles to 240s.
|
||||
//
|
||||
// diff, err := blockchain.NextPoSDifficulty(nextHeight, config.MainnetForks)
|
||||
func (c *Chain) NextPoSDifficulty(height uint64, forks []config.HardFork) (uint64, error) {
|
||||
return c.nextDifficultyWith(height, forks, config.DifficultyPosTarget, config.DifficultyPosTargetHF6)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
// GetRingOutputs fetches the public keys for the given global output indices
|
||||
// at the specified spending height and amount. This implements the
|
||||
// consensus.RingOutputsFn signature for use during signature verification.
|
||||
//
|
||||
// keys, err := blockchain.GetRingOutputs(blockHeight, inputAmount, []uint64{0, 5, 12, 30})
|
||||
func (c *Chain) GetRingOutputs(height, amount uint64, offsets []uint64) ([]types.PublicKey, error) {
|
||||
publicKeys := make([]types.PublicKey, len(offsets))
|
||||
for i, gidx := range offsets {
|
||||
|
|
@ -81,6 +83,8 @@ func ringOutputSpendKey(height uint64, target types.TxOutTarget) (types.PublicKe
|
|||
// consensus.ZCRingOutputsFn signature for post-HF4 CLSAG GGX verification.
|
||||
//
|
||||
// ZC outputs are indexed at amount=0 (confidential amounts).
|
||||
//
|
||||
// members, err := blockchain.GetZCRingOutputs([]uint64{100, 200, 300})
|
||||
func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, error) {
|
||||
members := make([]consensus.ZCRingMember, len(offsets))
|
||||
for i, gidx := range offsets {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import "dappco.re/go/core/blockchain/crypto"
|
|||
// The caller is responsible for constructing the balance context point(s)
|
||||
// from transaction inputs, outputs, fees, and any asset-operation terms.
|
||||
// This helper only performs the cryptographic check.
|
||||
//
|
||||
// ok := consensus.VerifyBalanceProof(txHash, false, pointA, pointB, proofBytes)
|
||||
func VerifyBalanceProof(hash [32]byte, aIsX bool, a [32]byte, b [32]byte, proof []byte) bool {
|
||||
return crypto.VerifyDoubleSchnorr(hash, aIsX, a, b, proof)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ func IsPoS(flags uint8) bool {
|
|||
|
||||
// CheckTimestamp validates a block's timestamp against future limits and
|
||||
// the median of recent timestamps.
|
||||
//
|
||||
// consensus.CheckTimestamp(blk.Timestamp, blk.Flags, uint64(time.Now().Unix()), recentTimestamps)
|
||||
func CheckTimestamp(blockTimestamp uint64, flags uint8, adjustedTime uint64, recentTimestamps []uint64) error {
|
||||
// Future time limit.
|
||||
limit := config.BlockFutureTimeLimit
|
||||
|
|
@ -77,6 +79,8 @@ func expectedMinerTxVersion(forks []config.HardFork, height uint64) uint64 {
|
|||
// ValidateMinerTx checks the structure of a coinbase (miner) transaction.
|
||||
// For PoW blocks: exactly 1 input (TxInputGenesis). For PoS blocks: exactly
|
||||
// 2 inputs (TxInputGenesis + stake input).
|
||||
//
|
||||
// consensus.ValidateMinerTx(&blk.MinerTx, height, config.MainnetForks)
|
||||
func ValidateMinerTx(tx *types.Transaction, height uint64, forks []config.HardFork) error {
|
||||
expectedVersion := expectedMinerTxVersion(forks, height)
|
||||
if tx.Version != expectedVersion {
|
||||
|
|
@ -132,6 +136,8 @@ func ValidateMinerTx(tx *types.Transaction, height uint64, forks []config.HardFo
|
|||
//
|
||||
// Post-HF4 miner transactions may use Zarcanum outputs, so the validator
|
||||
// sums both transparent amounts and the encoded Zarcanum amount field.
|
||||
//
|
||||
// consensus.ValidateBlockReward(&blk.MinerTx, height, blockSize, medianSize, totalFees, config.MainnetForks)
|
||||
func ValidateBlockReward(minerTx *types.Transaction, height, blockSize, medianSize, totalFees uint64, forks []config.HardFork) error {
|
||||
base := BaseReward(height)
|
||||
reward, err := BlockReward(base, blockSize, medianSize)
|
||||
|
|
@ -193,6 +199,8 @@ func checkBlockVersion(majorVersion uint8, forks []config.HardFork, height uint6
|
|||
|
||||
// CheckBlockVersion validates that the block's major version matches the
|
||||
// expected version for the supplied height and fork schedule.
|
||||
//
|
||||
// consensus.CheckBlockVersion(blk.MajorVersion, config.MainnetForks, height)
|
||||
func CheckBlockVersion(majorVersion uint8, forks []config.HardFork, height uint64) error {
|
||||
return checkBlockVersion(majorVersion, forks, height)
|
||||
}
|
||||
|
|
@ -201,6 +209,8 @@ func CheckBlockVersion(majorVersion uint8, forks []config.HardFork, height uint6
|
|||
// the block version, timestamp, miner transaction structure, and reward.
|
||||
// Transaction semantic validation for regular transactions should be done
|
||||
// separately via ValidateTransaction for each tx in the block.
|
||||
//
|
||||
// consensus.ValidateBlock(&blk, height, blockSize, medianSize, totalFees, adjustedTime, recentTimestamps, config.MainnetForks)
|
||||
func ValidateBlock(blk *types.Block, height, blockSize, medianSize, totalFees, adjustedTime uint64,
|
||||
recentTimestamps []uint64, forks []config.HardFork) error {
|
||||
|
||||
|
|
@ -238,6 +248,8 @@ func ValidateBlock(blk *types.Block, height, blockSize, medianSize, totalFees, a
|
|||
//
|
||||
// Returns false if the fork version is not found or if the activation height
|
||||
// is too low for a meaningful freeze window.
|
||||
//
|
||||
// if consensus.IsPreHardforkFreeze(config.TestnetForks, config.HF5, height) { /* reject non-coinbase txs */ }
|
||||
func IsPreHardforkFreeze(forks []config.HardFork, version uint8, height uint64) bool {
|
||||
activationHeight, ok := config.HardforkActivationHeight(forks, version)
|
||||
if !ok {
|
||||
|
|
@ -262,6 +274,8 @@ func IsPreHardforkFreeze(forks []config.HardFork, version uint8, height uint64)
|
|||
// pre-hardfork freeze check. This wraps ValidateTransaction with an
|
||||
// additional check: during the freeze window before HF5, non-coinbase
|
||||
// transactions are rejected.
|
||||
//
|
||||
// consensus.ValidateTransactionInBlock(&tx, txBlob, config.MainnetForks, blockHeight)
|
||||
func ValidateTransactionInBlock(tx *types.Transaction, txBlob []byte, forks []config.HardFork, height uint64) error {
|
||||
// Pre-hardfork freeze: reject non-coinbase transactions in the freeze window.
|
||||
if !isCoinbase(tx) && IsPreHardforkFreeze(forks, config.HF5, height) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
// TxFee calculates the transaction fee for pre-HF4 (v0/v1) transactions.
|
||||
// Coinbase transactions return 0. For standard transactions, fee equals
|
||||
// the difference between total input amounts and total output amounts.
|
||||
//
|
||||
// fee, err := consensus.TxFee(&tx)
|
||||
func TxFee(tx *types.Transaction) (uint64, error) {
|
||||
if isCoinbase(tx) {
|
||||
return 0, nil
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ var maxTarget = new(big.Int).Lsh(big.NewInt(1), 256)
|
|||
// CheckDifficulty returns true if hash meets the given difficulty target.
|
||||
// The hash (interpreted as a 256-bit little-endian number) must be less
|
||||
// than maxTarget / difficulty.
|
||||
//
|
||||
// if consensus.CheckDifficulty(powHash, currentDifficulty) { /* valid PoW solution */ }
|
||||
func CheckDifficulty(hash types.Hash, difficulty uint64) bool {
|
||||
if difficulty == 0 {
|
||||
return true
|
||||
|
|
@ -39,6 +41,8 @@ func CheckDifficulty(hash types.Hash, difficulty uint64) bool {
|
|||
|
||||
// CheckPoWHash computes the RandomX hash of a block header hash + nonce
|
||||
// and checks it against the difficulty target.
|
||||
//
|
||||
// valid, err := consensus.CheckPoWHash(headerHash, nonce, difficulty)
|
||||
func CheckPoWHash(headerHash types.Hash, nonce, difficulty uint64) (bool, error) {
|
||||
// Build input: header_hash (32 bytes) || nonce (8 bytes LE).
|
||||
var input [40]byte
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
// BaseReward returns the base block reward at the given height.
|
||||
// Height 0 (genesis) returns the premine amount. All other heights
|
||||
// return the fixed block reward (1 LTHN).
|
||||
//
|
||||
// reward := consensus.BaseReward(15000) // 1_000_000_000_000 (1 LTHN)
|
||||
func BaseReward(height uint64) uint64 {
|
||||
if height == 0 {
|
||||
return config.Premine
|
||||
|
|
@ -33,6 +35,8 @@ func BaseReward(height uint64) uint64 {
|
|||
// reward = baseReward * (2*median - size) * size / median²
|
||||
//
|
||||
// Uses math/bits.Mul64 for 128-bit intermediate products to avoid overflow.
|
||||
//
|
||||
// reward, err := consensus.BlockReward(consensus.BaseReward(height), blockSize, medianSize)
|
||||
func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) {
|
||||
effectiveMedian := medianSize
|
||||
if effectiveMedian < config.BlockGrantedFullRewardZone {
|
||||
|
|
@ -72,6 +76,9 @@ func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) {
|
|||
// MinerReward calculates the total miner payout. Pre-HF4, transaction
|
||||
// fees are added to the base reward. Post-HF4 (postHF4=true), fees are
|
||||
// burned and the miner receives only the base reward.
|
||||
//
|
||||
// payout := consensus.MinerReward(reward, totalFees, false) // pre-HF4: reward + fees
|
||||
// payout := consensus.MinerReward(reward, totalFees, true) // post-HF4: reward only (fees burned)
|
||||
func MinerReward(baseReward, totalFees uint64, postHF4 bool) uint64 {
|
||||
if postHF4 {
|
||||
return baseReward
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ func newTransactionForkState(forks []config.HardFork, height uint64) transaction
|
|||
|
||||
// ValidateTransaction performs semantic validation on a regular (non-coinbase)
|
||||
// transaction. Checks are ordered to match the C++ validate_tx_semantic().
|
||||
//
|
||||
// consensus.ValidateTransaction(&tx, txBlob, config.MainnetForks, blockHeight)
|
||||
func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.HardFork, height uint64) error {
|
||||
state := newTransactionForkState(forks, height)
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ type ZCRingOutputsFn func(offsets []uint64) ([]ZCRingMember, error)
|
|||
// getRingOutputs is used for pre-HF4 (V1) signature verification.
|
||||
// getZCRingOutputs is used for post-HF4 (V2) CLSAG GGX verification.
|
||||
// Either may be nil for structural-only checks.
|
||||
//
|
||||
// consensus.VerifyTransactionSignatures(&tx, config.MainnetForks, height, chain.GetRingOutputs, chain.GetZCRingOutputs)
|
||||
// consensus.VerifyTransactionSignatures(&tx, config.MainnetForks, height, nil, nil) // structural only
|
||||
func VerifyTransactionSignatures(tx *types.Transaction, forks []config.HardFork,
|
||||
height uint64, getRingOutputs RingOutputsFn, getZCRingOutputs ZCRingOutputsFn) error {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ package types
|
|||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
coreerr "dappco.re/go/core/log"
|
||||
)
|
||||
|
||||
// AssetDescriptorOperationTag is the wire tag for asset_descriptor_operation
|
||||
|
|
@ -37,24 +39,27 @@ type AssetDescriptorBase struct {
|
|||
}
|
||||
|
||||
// Validate checks that the base asset metadata is structurally valid.
|
||||
//
|
||||
// base := types.AssetDescriptorBase{Ticker: "LTHN", FullName: "Lethean", TotalMaxSupply: 1_000_000, OwnerKey: ownerPub}
|
||||
// if err := base.Validate(); err != nil { ... }
|
||||
func (base AssetDescriptorBase) Validate() error {
|
||||
tickerLen := utf8.RuneCountInString(base.Ticker)
|
||||
fullNameLen := utf8.RuneCountInString(base.FullName)
|
||||
|
||||
if base.TotalMaxSupply == 0 {
|
||||
return fmt.Errorf("types: total max supply must be non-zero")
|
||||
return coreerr.E("AssetDescriptorBase.Validate", "total max supply must be non-zero", nil)
|
||||
}
|
||||
if base.CurrentSupply > base.TotalMaxSupply {
|
||||
return fmt.Errorf("types: current supply %d exceeds max supply %d", base.CurrentSupply, base.TotalMaxSupply)
|
||||
return coreerr.E("AssetDescriptorBase.Validate", fmt.Sprintf("current supply %d exceeds max supply %d", base.CurrentSupply, base.TotalMaxSupply), nil)
|
||||
}
|
||||
if tickerLen == 0 || tickerLen > 6 {
|
||||
return fmt.Errorf("types: ticker length %d out of range", tickerLen)
|
||||
return coreerr.E("AssetDescriptorBase.Validate", fmt.Sprintf("ticker length %d out of range [1,6]", tickerLen), nil)
|
||||
}
|
||||
if fullNameLen == 0 || fullNameLen > 64 {
|
||||
return fmt.Errorf("types: full name length %d out of range", fullNameLen)
|
||||
return coreerr.E("AssetDescriptorBase.Validate", fmt.Sprintf("full name length %d out of range [1,64]", fullNameLen), nil)
|
||||
}
|
||||
if base.OwnerKey.IsZero() {
|
||||
return fmt.Errorf("types: owner key must be non-zero")
|
||||
return coreerr.E("AssetDescriptorBase.Validate", "owner key must be non-zero", nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -73,62 +78,65 @@ type AssetDescriptorOperation struct {
|
|||
}
|
||||
|
||||
// Validate checks that the operation is structurally valid for HF5 parsing.
|
||||
//
|
||||
// op := types.AssetDescriptorOperation{Version: 1, OperationType: types.AssetOpRegister, Descriptor: &base}
|
||||
// if err := op.Validate(); err != nil { ... }
|
||||
func (op AssetDescriptorOperation) Validate() error {
|
||||
switch op.Version {
|
||||
case 0, 1:
|
||||
default:
|
||||
return fmt.Errorf("types: unsupported version %d", op.Version)
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", fmt.Sprintf("unsupported version %d", op.Version), nil)
|
||||
}
|
||||
|
||||
switch op.OperationType {
|
||||
case AssetOpRegister:
|
||||
if !op.AssetID.IsZero() {
|
||||
return fmt.Errorf("types: register operation must not carry asset id")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "register operation must not carry asset id", nil)
|
||||
}
|
||||
if op.Descriptor == nil {
|
||||
return fmt.Errorf("types: register operation missing descriptor")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "register operation missing descriptor", nil)
|
||||
}
|
||||
if err := op.Descriptor.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if op.AmountToEmit != 0 || op.AmountToBurn != 0 {
|
||||
return fmt.Errorf("types: register operation must not include emission or burn amounts")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "register operation must not include emission or burn amounts", nil)
|
||||
}
|
||||
case AssetOpEmit:
|
||||
if op.AssetID.IsZero() {
|
||||
return fmt.Errorf("types: emit operation must carry asset id")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "emit operation must carry asset id", nil)
|
||||
}
|
||||
if op.AmountToEmit == 0 {
|
||||
return fmt.Errorf("types: emit operation has zero amount")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "emit operation has zero amount", nil)
|
||||
}
|
||||
if op.Descriptor != nil {
|
||||
return fmt.Errorf("types: emit operation must not carry descriptor")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "emit operation must not carry descriptor", nil)
|
||||
}
|
||||
case AssetOpUpdate:
|
||||
if op.AssetID.IsZero() {
|
||||
return fmt.Errorf("types: update operation must carry asset id")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "update operation must carry asset id", nil)
|
||||
}
|
||||
if op.Descriptor == nil {
|
||||
return fmt.Errorf("types: update operation missing descriptor")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "update operation missing descriptor", nil)
|
||||
}
|
||||
if err := op.Descriptor.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if op.AmountToEmit != 0 || op.AmountToBurn != 0 {
|
||||
return fmt.Errorf("types: update operation must not include emission or burn amounts")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "update operation must not include emission or burn amounts", nil)
|
||||
}
|
||||
case AssetOpBurn, AssetOpPublicBurn:
|
||||
if op.AssetID.IsZero() {
|
||||
return fmt.Errorf("types: burn operation must carry asset id")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "burn operation must carry asset id", nil)
|
||||
}
|
||||
if op.AmountToBurn == 0 {
|
||||
return fmt.Errorf("types: burn operation has zero amount")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "burn operation has zero amount", nil)
|
||||
}
|
||||
if op.Descriptor != nil {
|
||||
return fmt.Errorf("types: burn operation must not carry descriptor")
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", "burn operation must not carry descriptor", nil)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("types: unsupported operation type %d", op.OperationType)
|
||||
return coreerr.E("AssetDescriptorOperation.Validate", fmt.Sprintf("unsupported operation type %d", op.OperationType), nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue