fix(blockchain): upgrade to core v0.8.0-alpha.1 + replace banned imports

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-26 14:10:18 +00:00
parent d004158022
commit 96d60484b6
48 changed files with 365 additions and 391 deletions

View file

@ -6,11 +6,9 @@
package chain
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types"
@ -20,7 +18,7 @@ import (
// MarkSpent records a key image as spent at the given block height.
func (c *Chain) MarkSpent(ki types.KeyImage, height uint64) error {
if err := c.store.Set(groupSpentKeys, ki.String(), strconv.FormatUint(height, 10)); err != nil {
return coreerr.E("Chain.MarkSpent", fmt.Sprintf("chain: mark spent %s", ki), err)
return coreerr.E("Chain.MarkSpent", core.Sprintf("chain: mark spent %s", ki), err)
}
return nil
}
@ -28,11 +26,11 @@ func (c *Chain) MarkSpent(ki types.KeyImage, height uint64) error {
// IsSpent checks whether a key image has been spent.
func (c *Chain) IsSpent(ki types.KeyImage) (bool, error) {
_, err := c.store.Get(groupSpentKeys, ki.String())
if errors.Is(err, store.ErrNotFound) {
if core.Is(err, store.ErrNotFound) {
return false, nil
}
if err != nil {
return false, coreerr.E("Chain.IsSpent", fmt.Sprintf("chain: check spent %s", ki), err)
return false, coreerr.E("Chain.IsSpent", core.Sprintf("chain: check spent %s", ki), err)
}
return true, nil
}
@ -56,13 +54,10 @@ func (c *Chain) PutOutput(amount uint64, txID types.Hash, outNo uint32) (uint64,
TxID: txID.String(),
OutNo: outNo,
}
val, err := json.Marshal(entry)
if err != nil {
return 0, coreerr.E("Chain.PutOutput", "chain: marshal output", err)
}
val := core.JSONMarshalString(entry)
key := strconv.FormatUint(gindex, 10)
if err := c.store.Set(grp, key, string(val)); err != nil {
if err := c.store.Set(grp, key, val); err != nil {
return 0, coreerr.E("Chain.PutOutput", "chain: store output", err)
}
return gindex, nil
@ -74,15 +69,15 @@ func (c *Chain) GetOutput(amount uint64, gindex uint64) (types.Hash, uint32, err
key := strconv.FormatUint(gindex, 10)
val, err := c.store.Get(grp, key)
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return types.Hash{}, 0, coreerr.E("Chain.GetOutput", fmt.Sprintf("chain: output %d:%d not found", amount, gindex), nil)
if core.Is(err, store.ErrNotFound) {
return types.Hash{}, 0, coreerr.E("Chain.GetOutput", core.Sprintf("chain: output %d:%d not found", amount, gindex), nil)
}
return types.Hash{}, 0, coreerr.E("Chain.GetOutput", "chain: get output", err)
}
var entry outputEntry
if err := json.Unmarshal([]byte(val), &entry); err != nil {
return types.Hash{}, 0, coreerr.E("Chain.GetOutput", "chain: unmarshal output", err)
if r := core.JSONUnmarshalString(val, &entry); !r.OK {
return types.Hash{}, 0, coreerr.E("Chain.GetOutput", "chain: unmarshal output", r.Value.(error))
}
hash, err := types.HashFromHex(entry.TxID)
if err != nil {

View file

@ -7,9 +7,9 @@ package chain
import (
"context"
"fmt"
"log"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -120,12 +120,12 @@ func (c *Chain) P2PSync(ctx context.Context, conn P2PConnection, opts SyncOption
blockDiff, err := c.NextDifficulty(blockHeight, opts.Forks)
if err != nil {
return coreerr.E("Chain.P2PSync", fmt.Sprintf("p2p sync: compute difficulty for block %d", blockHeight), err)
return coreerr.E("Chain.P2PSync", core.Sprintf("p2p sync: compute difficulty for block %d", blockHeight), err)
}
if err := c.processBlockBlobs(entry.Block, entry.Txs,
blockHeight, blockDiff, opts); err != nil {
return coreerr.E("Chain.P2PSync", fmt.Sprintf("p2p sync: process block %d", blockHeight), err)
return coreerr.E("Chain.P2PSync", core.Sprintf("p2p sync: process block %d", blockHeight), err)
}
}
}

View file

@ -6,8 +6,7 @@
package chain
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/consensus"
@ -22,27 +21,27 @@ func (c *Chain) GetRingOutputs(amount uint64, offsets []uint64) ([]types.PublicK
for i, gidx := range offsets {
txHash, outNo, err := c.GetOutput(amount, gidx)
if err != nil {
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d (amount=%d, gidx=%d)", i, amount, gidx), err)
return nil, coreerr.E("Chain.GetRingOutputs", core.Sprintf("ring output %d (amount=%d, gidx=%d)", i, amount, gidx), err)
}
tx, _, err := c.GetTransaction(txHash)
if err != nil {
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: tx %s", i, txHash), err)
return nil, coreerr.E("Chain.GetRingOutputs", core.Sprintf("ring output %d: tx %s", i, txHash), err)
}
if int(outNo) >= len(tx.Vout) {
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
return nil, coreerr.E("Chain.GetRingOutputs", core.Sprintf("ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
}
switch out := tx.Vout[outNo].(type) {
case types.TxOutputBare:
toKey, ok := out.Target.(types.TxOutToKey)
if !ok {
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: unsupported target type %T", i, out.Target), nil)
return nil, coreerr.E("Chain.GetRingOutputs", core.Sprintf("ring output %d: unsupported target type %T", i, out.Target), nil)
}
pubs[i] = toKey.Key
default:
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: unsupported output type %T", i, out), nil)
return nil, coreerr.E("Chain.GetRingOutputs", core.Sprintf("ring output %d: unsupported output type %T", i, out), nil)
}
}
return pubs, nil
@ -58,16 +57,16 @@ func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, er
for i, gidx := range offsets {
txHash, outNo, err := c.GetOutput(0, gidx)
if err != nil {
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d (gidx=%d)", i, gidx), err)
return nil, coreerr.E("Chain.GetZCRingOutputs", core.Sprintf("ZC ring output %d (gidx=%d)", i, gidx), err)
}
tx, _, err := c.GetTransaction(txHash)
if err != nil {
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: tx %s", i, txHash), err)
return nil, coreerr.E("Chain.GetZCRingOutputs", core.Sprintf("ZC ring output %d: tx %s", i, txHash), err)
}
if int(outNo) >= len(tx.Vout) {
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
return nil, coreerr.E("Chain.GetZCRingOutputs", core.Sprintf("ZC ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
}
switch out := tx.Vout[outNo].(type) {
@ -78,7 +77,7 @@ func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, er
BlindedAssetID: [32]byte(out.BlindedAssetID),
}
default:
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: expected TxOutputZarcanum, got %T", i, out), nil)
return nil, coreerr.E("Chain.GetZCRingOutputs", core.Sprintf("ZC ring output %d: expected TxOutputZarcanum, got %T", i, out), nil)
}
}
return members, nil

View file

@ -8,11 +8,9 @@ package chain
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"strconv"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types"
@ -31,7 +29,7 @@ const (
// heightKey returns a zero-padded 10-digit decimal key for the given height.
func heightKey(h uint64) string {
return fmt.Sprintf("%010d", h)
return core.Sprintf("%010d", h)
}
// blockRecord is the JSON value stored in the blocks group.
@ -46,26 +44,23 @@ func (c *Chain) PutBlock(b *types.Block, meta *BlockMeta) error {
enc := wire.NewEncoder(&buf)
wire.EncodeBlock(enc, b)
if err := enc.Err(); err != nil {
return coreerr.E("Chain.PutBlock", fmt.Sprintf("chain: encode block %d", meta.Height), err)
return coreerr.E("Chain.PutBlock", core.Sprintf("chain: encode block %d", meta.Height), err)
}
rec := blockRecord{
Meta: *meta,
Blob: hex.EncodeToString(buf.Bytes()),
}
val, err := json.Marshal(rec)
if err != nil {
return coreerr.E("Chain.PutBlock", fmt.Sprintf("chain: marshal block %d", meta.Height), err)
}
val := core.JSONMarshalString(rec)
if err := c.store.Set(groupBlocks, heightKey(meta.Height), string(val)); err != nil {
return coreerr.E("Chain.PutBlock", fmt.Sprintf("chain: store block %d", meta.Height), err)
if err := c.store.Set(groupBlocks, heightKey(meta.Height), val); err != nil {
return coreerr.E("Chain.PutBlock", core.Sprintf("chain: store block %d", meta.Height), err)
}
// Update hash -> height index.
hashHex := meta.Hash.String()
if err := c.store.Set(groupBlockIndex, hashHex, strconv.FormatUint(meta.Height, 10)); err != nil {
return coreerr.E("Chain.PutBlock", fmt.Sprintf("chain: index block %d", meta.Height), err)
return coreerr.E("Chain.PutBlock", core.Sprintf("chain: index block %d", meta.Height), err)
}
return nil
@ -75,10 +70,10 @@ func (c *Chain) PutBlock(b *types.Block, meta *BlockMeta) error {
func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, *BlockMeta, error) {
val, err := c.store.Get(groupBlocks, heightKey(height))
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetBlockByHeight", fmt.Sprintf("chain: block %d not found", height), nil)
if core.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetBlockByHeight", core.Sprintf("chain: block %d not found", height), nil)
}
return nil, nil, coreerr.E("Chain.GetBlockByHeight", fmt.Sprintf("chain: get block %d", height), err)
return nil, nil, coreerr.E("Chain.GetBlockByHeight", core.Sprintf("chain: get block %d", height), err)
}
return decodeBlockRecord(val)
}
@ -87,14 +82,14 @@ func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, *BlockMeta, error
func (c *Chain) GetBlockByHash(hash types.Hash) (*types.Block, *BlockMeta, error) {
heightStr, err := c.store.Get(groupBlockIndex, hash.String())
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetBlockByHash", fmt.Sprintf("chain: block %s not found", hash), nil)
if core.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetBlockByHash", core.Sprintf("chain: block %s not found", hash), nil)
}
return nil, nil, coreerr.E("Chain.GetBlockByHash", fmt.Sprintf("chain: get block index %s", hash), err)
return nil, nil, coreerr.E("Chain.GetBlockByHash", core.Sprintf("chain: get block index %s", hash), err)
}
height, err := strconv.ParseUint(heightStr, 10, 64)
if err != nil {
return nil, nil, coreerr.E("Chain.GetBlockByHash", fmt.Sprintf("chain: parse height %q", heightStr), err)
return nil, nil, coreerr.E("Chain.GetBlockByHash", core.Sprintf("chain: parse height %q", heightStr), err)
}
return c.GetBlockByHeight(height)
}
@ -111,20 +106,17 @@ func (c *Chain) PutTransaction(hash types.Hash, tx *types.Transaction, meta *TxM
enc := wire.NewEncoder(&buf)
wire.EncodeTransaction(enc, tx)
if err := enc.Err(); err != nil {
return coreerr.E("Chain.PutTransaction", fmt.Sprintf("chain: encode tx %s", hash), err)
return coreerr.E("Chain.PutTransaction", core.Sprintf("chain: encode tx %s", hash), err)
}
rec := txRecord{
Meta: *meta,
Blob: hex.EncodeToString(buf.Bytes()),
}
val, err := json.Marshal(rec)
if err != nil {
return coreerr.E("Chain.PutTransaction", fmt.Sprintf("chain: marshal tx %s", hash), err)
}
val := core.JSONMarshalString(rec)
if err := c.store.Set(groupTx, hash.String(), string(val)); err != nil {
return coreerr.E("Chain.PutTransaction", fmt.Sprintf("chain: store tx %s", hash), err)
if err := c.store.Set(groupTx, hash.String(), val); err != nil {
return coreerr.E("Chain.PutTransaction", core.Sprintf("chain: store tx %s", hash), err)
}
return nil
}
@ -133,15 +125,15 @@ func (c *Chain) PutTransaction(hash types.Hash, tx *types.Transaction, meta *TxM
func (c *Chain) GetTransaction(hash types.Hash) (*types.Transaction, *TxMeta, error) {
val, err := c.store.Get(groupTx, hash.String())
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetTransaction", fmt.Sprintf("chain: tx %s not found", hash), nil)
if core.Is(err, store.ErrNotFound) {
return nil, nil, coreerr.E("Chain.GetTransaction", core.Sprintf("chain: tx %s not found", hash), nil)
}
return nil, nil, coreerr.E("Chain.GetTransaction", fmt.Sprintf("chain: get tx %s", hash), err)
return nil, nil, coreerr.E("Chain.GetTransaction", core.Sprintf("chain: get tx %s", hash), err)
}
var rec txRecord
if err := json.Unmarshal([]byte(val), &rec); err != nil {
return nil, nil, coreerr.E("Chain.GetTransaction", "chain: unmarshal tx", err)
if r := core.JSONUnmarshalString(val, &rec); !r.OK {
return nil, nil, coreerr.E("Chain.GetTransaction", "chain: unmarshal tx", r.Value.(error))
}
blob, err := hex.DecodeString(rec.Blob)
if err != nil {
@ -166,19 +158,19 @@ func (c *Chain) HasTransaction(hash types.Hash) bool {
func (c *Chain) getBlockMeta(height uint64) (*BlockMeta, error) {
val, err := c.store.Get(groupBlocks, heightKey(height))
if err != nil {
return nil, coreerr.E("Chain.getBlockMeta", fmt.Sprintf("chain: block meta %d", height), err)
return nil, coreerr.E("Chain.getBlockMeta", core.Sprintf("chain: block meta %d", height), err)
}
var rec blockRecord
if err := json.Unmarshal([]byte(val), &rec); err != nil {
return nil, coreerr.E("Chain.getBlockMeta", fmt.Sprintf("chain: unmarshal block meta %d", height), err)
if r := core.JSONUnmarshalString(val, &rec); !r.OK {
return nil, coreerr.E("Chain.getBlockMeta", core.Sprintf("chain: unmarshal block meta %d", height), r.Value.(error))
}
return &rec.Meta, nil
}
func decodeBlockRecord(val string) (*types.Block, *BlockMeta, error) {
var rec blockRecord
if err := json.Unmarshal([]byte(val), &rec); err != nil {
return nil, nil, coreerr.E("decodeBlockRecord", "chain: unmarshal block", err)
if r := core.JSONUnmarshalString(val, &rec); !r.OK {
return nil, nil, coreerr.E("decodeBlockRecord", "chain: unmarshal block", r.Value.(error))
}
blob, err := hex.DecodeString(rec.Blob)
if err != nil {

View file

@ -9,12 +9,11 @@ import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"regexp"
"strconv"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -72,16 +71,16 @@ func (c *Chain) Sync(ctx context.Context, client *rpc.Client, opts SyncOptions)
blocks, err := client.GetBlocksDetails(localHeight, batch)
if err != nil {
return coreerr.E("Chain.Sync", fmt.Sprintf("sync: fetch blocks at %d", localHeight), err)
return coreerr.E("Chain.Sync", core.Sprintf("sync: fetch blocks at %d", localHeight), err)
}
if err := resolveBlockBlobs(blocks, client); err != nil {
return coreerr.E("Chain.Sync", fmt.Sprintf("sync: resolve blobs at %d", localHeight), err)
return coreerr.E("Chain.Sync", core.Sprintf("sync: resolve blobs at %d", localHeight), err)
}
for _, bd := range blocks {
if err := c.processBlock(bd, opts); err != nil {
return coreerr.E("Chain.Sync", fmt.Sprintf("sync: process block %d", bd.Height), err)
return coreerr.E("Chain.Sync", core.Sprintf("sync: process block %d", bd.Height), err)
}
}
@ -126,7 +125,7 @@ func (c *Chain) processBlock(bd rpc.BlockDetails, opts SyncOptions) error {
}
txBlobBytes, err := hex.DecodeString(txInfo.Blob)
if err != nil {
return coreerr.E("Chain.processBlock", fmt.Sprintf("decode tx hex %s", txInfo.ID), err)
return coreerr.E("Chain.processBlock", core.Sprintf("decode tx hex %s", txInfo.ID), err)
}
txBlobs = append(txBlobs, txBlobBytes)
}
@ -140,7 +139,7 @@ func (c *Chain) processBlock(bd rpc.BlockDetails, opts SyncOptions) error {
return coreerr.E("Chain.processBlock", "parse daemon block hash", err)
}
if computedHash != daemonHash {
return coreerr.E("Chain.processBlock", fmt.Sprintf("block hash mismatch: computed %s, daemon says %s", computedHash, daemonHash), nil)
return coreerr.E("Chain.processBlock", core.Sprintf("block hash mismatch: computed %s, daemon says %s", computedHash, daemonHash), nil)
}
return c.processBlockBlobs(blockBlob, txBlobs, bd.Height, diff, opts)
@ -168,7 +167,7 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
return coreerr.E("Chain.processBlockBlobs", "parse genesis hash", err)
}
if blockHash != genesisHash {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("genesis hash %s does not match expected %s", blockHash, GenesisHash), nil)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("genesis hash %s does not match expected %s", blockHash, GenesisHash), nil)
}
}
@ -212,27 +211,27 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
txDec := wire.NewDecoder(bytes.NewReader(txBlobData))
tx := wire.DecodeTransaction(txDec)
if err := txDec.Err(); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("decode tx wire [%d]", i), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("decode tx wire [%d]", i), err)
}
txHash := wire.TransactionHash(&tx)
// Validate transaction semantics.
if err := consensus.ValidateTransaction(&tx, txBlobData, opts.Forks, height); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("validate tx %s", txHash), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("validate tx %s", txHash), err)
}
// Optionally verify signatures using the chain's output index.
if opts.VerifySignatures {
if err := consensus.VerifyTransactionSignatures(&tx, opts.Forks, height, c.GetRingOutputs, c.GetZCRingOutputs); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("verify tx signatures %s", txHash), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("verify tx signatures %s", txHash), err)
}
}
// Index outputs.
gindexes, err := c.indexOutputs(txHash, &tx)
if err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("index tx outputs %s", txHash), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("index tx outputs %s", txHash), err)
}
// Mark key images as spent.
@ -240,11 +239,11 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
switch inp := vin.(type) {
case types.TxInputToKey:
if err := c.MarkSpent(inp.KeyImage, height); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("mark spent %s", inp.KeyImage), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("mark spent %s", inp.KeyImage), err)
}
case types.TxInputZC:
if err := c.MarkSpent(inp.KeyImage, height); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("mark spent %s", inp.KeyImage), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("mark spent %s", inp.KeyImage), err)
}
}
}
@ -254,7 +253,7 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
KeeperBlock: height,
GlobalOutputIndexes: gindexes,
}); err != nil {
return coreerr.E("Chain.processBlockBlobs", fmt.Sprintf("store tx %s", txHash), err)
return coreerr.E("Chain.processBlockBlobs", core.Sprintf("store tx %s", txHash), err)
}
}
@ -332,10 +331,10 @@ func resolveBlockBlobs(blocks []rpc.BlockDetails, client *rpc.Client) error {
return coreerr.E("resolveBlockBlobs", "fetch tx blobs", err)
}
if len(missed) > 0 {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("daemon missed %d tx(es): %v", len(missed), missed), nil)
return coreerr.E("resolveBlockBlobs", core.Sprintf("daemon missed %d tx(es): %v", len(missed), missed), nil)
}
if len(txHexes) != len(allHashes) {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("expected %d tx blobs, got %d", len(allHashes), len(txHexes)), nil)
return coreerr.E("resolveBlockBlobs", core.Sprintf("expected %d tx blobs, got %d", len(allHashes), len(txHexes)), nil)
}
// Index fetched blobs by hash.
@ -363,16 +362,16 @@ func resolveBlockBlobs(blocks []rpc.BlockDetails, client *rpc.Client) error {
// Parse header from object_in_json.
hdr, err := parseBlockHeader(bd.ObjectInJSON)
if err != nil {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("block %d: parse header", bd.Height), err)
return coreerr.E("resolveBlockBlobs", core.Sprintf("block %d: parse header", bd.Height), err)
}
// Miner tx blob is transactions_details[0].
if len(bd.Transactions) == 0 {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("block %d has no transactions_details", bd.Height), nil)
return coreerr.E("resolveBlockBlobs", core.Sprintf("block %d has no transactions_details", bd.Height), nil)
}
minerTxBlob, err := hex.DecodeString(bd.Transactions[0].Blob)
if err != nil {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("block %d: decode miner tx hex", bd.Height), err)
return coreerr.E("resolveBlockBlobs", core.Sprintf("block %d: decode miner tx hex", bd.Height), err)
}
// Collect regular tx hashes.
@ -380,7 +379,7 @@ func resolveBlockBlobs(blocks []rpc.BlockDetails, client *rpc.Client) error {
for _, txInfo := range bd.Transactions[1:] {
h, err := types.HashFromHex(txInfo.ID)
if err != nil {
return coreerr.E("resolveBlockBlobs", fmt.Sprintf("block %d: parse tx hash %s", bd.Height, txInfo.ID), err)
return coreerr.E("resolveBlockBlobs", core.Sprintf("block %d: parse tx hash %s", bd.Height, txInfo.ID), err)
}
txHashes = append(txHashes, h)
}
@ -414,8 +413,8 @@ func parseBlockHeader(objectInJSON string) (*types.BlockHeader, error) {
}
var hj blockHeaderJSON
if err := json.Unmarshal([]byte("{"+m[1]+"}"), &hj); err != nil {
return nil, coreerr.E("parseBlockHeader", "unmarshal AGGREGATED", err)
if r := core.JSONUnmarshalString(core.Concat("{", m[1], "}"), &hj); !r.OK {
return nil, coreerr.E("parseBlockHeader", "unmarshal AGGREGATED", r.Value.(error))
}
prevID, err := types.HashFromHex(hj.PrevID)

View file

@ -7,8 +7,7 @@ package chain
import (
"bytes"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -26,7 +25,7 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
// Height sequence check.
if expectedHeight != currentHeight {
return coreerr.E("Chain.ValidateHeader", fmt.Sprintf("validate: expected height %d but chain is at %d", expectedHeight, currentHeight), nil)
return coreerr.E("Chain.ValidateHeader", core.Sprintf("validate: expected height %d but chain is at %d", expectedHeight, currentHeight), nil)
}
// Genesis block: prev_id must be zero.
@ -43,7 +42,7 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
return coreerr.E("Chain.ValidateHeader", "validate: get top block", err)
}
if b.PrevID != topMeta.Hash {
return coreerr.E("Chain.ValidateHeader", fmt.Sprintf("validate: prev_id %s does not match top block %s", b.PrevID, topMeta.Hash), nil)
return coreerr.E("Chain.ValidateHeader", core.Sprintf("validate: prev_id %s does not match top block %s", b.PrevID, topMeta.Hash), nil)
}
// Block size check.
@ -51,7 +50,7 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
enc := wire.NewEncoder(&buf)
wire.EncodeBlock(enc, b)
if enc.Err() == nil && uint64(buf.Len()) > config.MaxBlockSize {
return coreerr.E("Chain.ValidateHeader", fmt.Sprintf("validate: block size %d exceeds max %d", buf.Len(), config.MaxBlockSize), nil)
return coreerr.E("Chain.ValidateHeader", core.Sprintf("validate: block size %d exceeds max %d", buf.Len(), config.MaxBlockSize), nil)
}
return nil

View file

@ -7,11 +7,11 @@ package blockchain
import (
"context"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
cli "dappco.re/go/core/cli/pkg/cli"
@ -38,7 +38,7 @@ func runExplorer(dataDir, seed string, testnet bool) error {
return err
}
dbPath := filepath.Join(dataDir, "chain.db")
dbPath := core.JoinPath(dataDir, "chain.db")
s, err := store.New(dbPath)
if err != nil {
return coreerr.E("runExplorer", "open store", err)
@ -48,7 +48,7 @@ func runExplorer(dataDir, seed string, testnet bool) error {
c := chain.New(s)
cfg, forks := resolveConfig(testnet, &seed)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer cancel()
var wg sync.WaitGroup

View file

@ -7,14 +7,12 @@ package blockchain
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/chain"
@ -55,7 +53,7 @@ func runSyncForeground(dataDir, seed string, testnet bool) error {
return err
}
dbPath := filepath.Join(dataDir, "chain.db")
dbPath := core.JoinPath(dataDir, "chain.db")
s, err := store.New(dbPath)
if err != nil {
return coreerr.E("runSyncForeground", "open store", err)
@ -65,7 +63,7 @@ func runSyncForeground(dataDir, seed string, testnet bool) error {
c := chain.New(s)
cfg, forks := resolveConfig(testnet, &seed)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
log.Println("Starting headless P2P sync...")
@ -79,7 +77,7 @@ func runSyncDaemon(dataDir, seed string, testnet bool) error {
return err
}
pidFile := filepath.Join(dataDir, "sync.pid")
pidFile := core.JoinPath(dataDir, "sync.pid")
d := process.NewDaemon(process.DaemonOptions{
PIDFile: pidFile,
@ -94,7 +92,7 @@ func runSyncDaemon(dataDir, seed string, testnet bool) error {
return coreerr.E("runSyncDaemon", "daemon start", err)
}
dbPath := filepath.Join(dataDir, "chain.db")
dbPath := core.JoinPath(dataDir, "chain.db")
s, err := store.New(dbPath)
if err != nil {
_ = d.Stop()
@ -105,7 +103,7 @@ func runSyncDaemon(dataDir, seed string, testnet bool) error {
c := chain.New(s)
cfg, forks := resolveConfig(testnet, &seed)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
d.SetReady(true)
@ -124,19 +122,14 @@ func runSyncDaemon(dataDir, seed string, testnet bool) error {
}
func stopSyncDaemon(dataDir string) error {
pidFile := filepath.Join(dataDir, "sync.pid")
pidFile := core.JoinPath(dataDir, "sync.pid")
pid, running := process.ReadPID(pidFile)
if pid == 0 || !running {
return coreerr.E("stopSyncDaemon", "no running sync daemon found", nil)
}
proc, err := os.FindProcess(pid)
if err != nil {
return coreerr.E("stopSyncDaemon", fmt.Sprintf("find process %d", pid), err)
}
if err := proc.Signal(syscall.SIGTERM); err != nil {
return coreerr.E("stopSyncDaemon", fmt.Sprintf("signal process %d", pid), err)
if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
return coreerr.E("stopSyncDaemon", core.Sprintf("signal process %d", pid), err)
}
log.Printf("Sent SIGTERM to sync daemon (PID %d)", pid)

View file

@ -6,9 +6,7 @@
package blockchain
import (
"os"
"path/filepath"
"dappco.re/go/core"
coreio "dappco.re/go/core/io"
coreerr "dappco.re/go/core/log"
@ -54,11 +52,11 @@ func resolveConfig(testnet bool, seed *string) (config.ChainConfig, []config.Har
}
func defaultDataDir() string {
home, err := os.UserHomeDir()
if err != nil {
home := core.Env("DIR_HOME")
if home == "" {
return ".lethean"
}
return filepath.Join(home, ".lethean", "chain")
return core.JoinPath(home, ".lethean", "chain")
}
func ensureDataDir(dataDir string) error {

View file

@ -6,9 +6,9 @@
package consensus
import (
"fmt"
"slices"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -30,7 +30,7 @@ func CheckTimestamp(blockTimestamp uint64, flags uint8, adjustedTime uint64, rec
limit = config.PosBlockFutureTimeLimit
}
if blockTimestamp > adjustedTime+limit {
return coreerr.E("CheckTimestamp", fmt.Sprintf("%d > %d + %d",
return coreerr.E("CheckTimestamp", core.Sprintf("%d > %d + %d",
blockTimestamp, adjustedTime, limit), ErrTimestampFuture)
}
@ -41,7 +41,7 @@ func CheckTimestamp(blockTimestamp uint64, flags uint8, adjustedTime uint64, rec
median := medianTimestamp(recentTimestamps)
if blockTimestamp < median {
return coreerr.E("CheckTimestamp", fmt.Sprintf("%d < median %d",
return coreerr.E("CheckTimestamp", core.Sprintf("%d < median %d",
blockTimestamp, median), ErrTimestampOld)
}
@ -75,7 +75,7 @@ func ValidateMinerTx(tx *types.Transaction, height uint64, forks []config.HardFo
return coreerr.E("ValidateMinerTx", "first input is not txin_gen", ErrMinerTxInputs)
}
if gen.Height != height {
return coreerr.E("ValidateMinerTx", fmt.Sprintf("got %d, expected %d", gen.Height, height), ErrMinerTxHeight)
return coreerr.E("ValidateMinerTx", core.Sprintf("got %d, expected %d", gen.Height, height), ErrMinerTxHeight)
}
// PoW blocks: exactly 1 input. PoS: exactly 2.
@ -94,7 +94,7 @@ func ValidateMinerTx(tx *types.Transaction, height uint64, forks []config.HardFo
// Post-HF4: accept ZC inputs.
}
} else {
return coreerr.E("ValidateMinerTx", fmt.Sprintf("%d inputs (expected 1 or 2)", len(tx.Vin)), ErrMinerTxInputs)
return coreerr.E("ValidateMinerTx", core.Sprintf("%d inputs (expected 1 or 2)", len(tx.Vin)), ErrMinerTxInputs)
}
return nil
@ -121,7 +121,7 @@ func ValidateBlockReward(minerTx *types.Transaction, height, blockSize, medianSi
}
if outputSum > expected {
return coreerr.E("ValidateBlockReward", fmt.Sprintf("outputs %d > expected %d", outputSum, expected), ErrRewardMismatch)
return coreerr.E("ValidateBlockReward", core.Sprintf("outputs %d > expected %d", outputSum, expected), ErrRewardMismatch)
}
return nil
@ -152,7 +152,7 @@ func expectedBlockMajorVersion(forks []config.HardFork, height uint64) uint8 {
func checkBlockVersion(blk *types.Block, forks []config.HardFork, height uint64) error {
expected := expectedBlockMajorVersion(forks, height)
if blk.MajorVersion != expected {
return coreerr.E("checkBlockVersion", fmt.Sprintf("got %d, want %d at height %d",
return coreerr.E("checkBlockVersion", core.Sprintf("got %d, want %d at height %d",
blk.MajorVersion, expected, height), ErrBlockMajorVersion)
}
return nil
@ -226,7 +226,7 @@ func IsPreHardforkFreeze(forks []config.HardFork, version uint8, height uint64)
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) {
return coreerr.E("ValidateTransactionInBlock", fmt.Sprintf("height %d is within HF5 freeze window", height), ErrPreHardforkFreeze)
return coreerr.E("ValidateTransactionInBlock", core.Sprintf("height %d is within HF5 freeze window", height), ErrPreHardforkFreeze)
}
return ValidateTransaction(tx, txBlob, forks, height)

View file

@ -5,39 +5,39 @@
package consensus
import "errors"
import "dappco.re/go/core"
// Sentinel errors for consensus validation failures.
var (
// Transaction structural errors.
ErrTxTooLarge = errors.New("consensus: transaction too large")
ErrNoInputs = errors.New("consensus: transaction has no inputs")
ErrTooManyInputs = errors.New("consensus: transaction exceeds max inputs")
ErrInvalidInputType = errors.New("consensus: unsupported input type")
ErrNoOutputs = errors.New("consensus: transaction has no outputs")
ErrTooFewOutputs = errors.New("consensus: transaction below min outputs")
ErrTooManyOutputs = errors.New("consensus: transaction exceeds max outputs")
ErrInvalidOutput = errors.New("consensus: invalid output")
ErrDuplicateKeyImage = errors.New("consensus: duplicate key image in transaction")
ErrInvalidExtra = errors.New("consensus: invalid extra field")
ErrTxVersionInvalid = errors.New("consensus: invalid transaction version for current hardfork")
ErrPreHardforkFreeze = errors.New("consensus: non-coinbase transaction rejected during pre-hardfork freeze")
ErrTxTooLarge = core.E("", "consensus: transaction too large", nil)
ErrNoInputs = core.E("", "consensus: transaction has no inputs", nil)
ErrTooManyInputs = core.E("", "consensus: transaction exceeds max inputs", nil)
ErrInvalidInputType = core.E("", "consensus: unsupported input type", nil)
ErrNoOutputs = core.E("", "consensus: transaction has no outputs", nil)
ErrTooFewOutputs = core.E("", "consensus: transaction below min outputs", nil)
ErrTooManyOutputs = core.E("", "consensus: transaction exceeds max outputs", nil)
ErrInvalidOutput = core.E("", "consensus: invalid output", nil)
ErrDuplicateKeyImage = core.E("", "consensus: duplicate key image in transaction", nil)
ErrInvalidExtra = core.E("", "consensus: invalid extra field", nil)
ErrTxVersionInvalid = core.E("", "consensus: invalid transaction version for current hardfork", nil)
ErrPreHardforkFreeze = core.E("", "consensus: non-coinbase transaction rejected during pre-hardfork freeze", nil)
// Transaction economic errors.
ErrInputOverflow = errors.New("consensus: input amount overflow")
ErrOutputOverflow = errors.New("consensus: output amount overflow")
ErrNegativeFee = errors.New("consensus: outputs exceed inputs")
ErrInputOverflow = core.E("", "consensus: input amount overflow", nil)
ErrOutputOverflow = core.E("", "consensus: output amount overflow", nil)
ErrNegativeFee = core.E("", "consensus: outputs exceed inputs", nil)
// Block errors.
ErrBlockTooLarge = errors.New("consensus: block exceeds max size")
ErrBlockMajorVersion = errors.New("consensus: invalid block major version for height")
ErrTimestampFuture = errors.New("consensus: block timestamp too far in future")
ErrTimestampOld = errors.New("consensus: block timestamp below median")
ErrMinerTxInputs = errors.New("consensus: invalid miner transaction inputs")
ErrMinerTxHeight = errors.New("consensus: miner transaction height mismatch")
ErrMinerTxUnlock = errors.New("consensus: miner transaction unlock time invalid")
ErrRewardMismatch = errors.New("consensus: block reward mismatch")
ErrMinerTxProofs = errors.New("consensus: miner transaction proof count invalid")
ErrBlockTooLarge = core.E("", "consensus: block exceeds max size", nil)
ErrBlockMajorVersion = core.E("", "consensus: invalid block major version for height", nil)
ErrTimestampFuture = core.E("", "consensus: block timestamp too far in future", nil)
ErrTimestampOld = core.E("", "consensus: block timestamp below median", nil)
ErrMinerTxInputs = core.E("", "consensus: invalid miner transaction inputs", nil)
ErrMinerTxHeight = core.E("", "consensus: miner transaction height mismatch", nil)
ErrMinerTxUnlock = core.E("", "consensus: miner transaction unlock time invalid", nil)
ErrRewardMismatch = core.E("", "consensus: block reward mismatch", nil)
ErrMinerTxProofs = core.E("", "consensus: miner transaction proof count invalid", nil)
// ErrBlockVersion is an alias for ErrBlockMajorVersion, used by
// checkBlockVersion when the block major version does not match

View file

@ -6,9 +6,9 @@
package consensus
import (
"fmt"
"math"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types"
@ -33,7 +33,7 @@ func TxFee(tx *types.Transaction) (uint64, error) {
}
if outputSum > inputSum {
return 0, coreerr.E("TxFee", fmt.Sprintf("inputs=%d, outputs=%d", inputSum, outputSum), ErrNegativeFee)
return 0, coreerr.E("TxFee", core.Sprintf("inputs=%d, outputs=%d", inputSum, outputSum), ErrNegativeFee)
}
return inputSum - outputSum, nil

View file

@ -6,9 +6,9 @@
package consensus
import (
"fmt"
"math/bits"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -44,7 +44,7 @@ func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) {
}
if blockSize > 2*effectiveMedian {
return 0, coreerr.E("BlockReward", fmt.Sprintf("consensus: block size %d too large for median %d", blockSize, effectiveMedian), nil)
return 0, coreerr.E("BlockReward", core.Sprintf("consensus: block size %d too large for median %d", blockSize, effectiveMedian), nil)
}
// penalty = baseReward * (2*median - size) * size / median²

View file

@ -6,8 +6,7 @@
package consensus
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -26,7 +25,7 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
// 1. Blob size.
if uint64(len(txBlob)) >= config.MaxTransactionBlobSize {
return coreerr.E("ValidateTransaction", fmt.Sprintf("%d bytes", len(txBlob)), ErrTxTooLarge)
return coreerr.E("ValidateTransaction", core.Sprintf("%d bytes", len(txBlob)), ErrTxTooLarge)
}
// 2. Input count.
@ -34,7 +33,7 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
return ErrNoInputs
}
if uint64(len(tx.Vin)) > config.TxMaxAllowedInputs {
return coreerr.E("ValidateTransaction", fmt.Sprintf("%d", len(tx.Vin)), ErrTooManyInputs)
return coreerr.E("ValidateTransaction", core.Sprintf("%d", len(tx.Vin)), ErrTooManyInputs)
}
hf1Active := config.IsHardForkActive(forks, config.HF1, height)
@ -82,13 +81,13 @@ func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint6
if hf5Active && tx.Version < types.VersionPostHF5 {
return coreerr.E("checkTxVersion",
fmt.Sprintf("version %d too low after HF5 at height %d", tx.Version, height),
core.Sprintf("version %d too low after HF5 at height %d", tx.Version, height),
ErrTxVersionInvalid)
}
if !hf5Active && tx.Version >= types.VersionPostHF5 {
return coreerr.E("checkTxVersion",
fmt.Sprintf("version %d not allowed before HF5 at height %d", tx.Version, height),
core.Sprintf("version %d not allowed before HF5 at height %d", tx.Version, height),
ErrTxVersionInvalid)
}
@ -105,12 +104,12 @@ func checkInputTypes(tx *types.Transaction, hf1Active, hf4Active bool) error {
case types.TxInputHTLC, types.TxInputMultisig:
// HTLC and multisig inputs require at least HF1.
if !hf1Active {
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF1", vin.InputType()), ErrInvalidInputType)
return coreerr.E("checkInputTypes", core.Sprintf("tag %d pre-HF1", vin.InputType()), ErrInvalidInputType)
}
default:
// Future types (ZC) — accept if HF4+.
if !hf4Active {
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF4", vin.InputType()), ErrInvalidInputType)
return coreerr.E("checkInputTypes", core.Sprintf("tag %d pre-HF4", vin.InputType()), ErrInvalidInputType)
}
}
}
@ -123,24 +122,24 @@ func checkOutputs(tx *types.Transaction, hf1Active, hf4Active bool) error {
}
if hf4Active && uint64(len(tx.Vout)) < config.TxMinAllowedOutputs {
return coreerr.E("checkOutputs", fmt.Sprintf("%d (min %d)", len(tx.Vout), config.TxMinAllowedOutputs), ErrTooFewOutputs)
return coreerr.E("checkOutputs", core.Sprintf("%d (min %d)", len(tx.Vout), config.TxMinAllowedOutputs), ErrTooFewOutputs)
}
if uint64(len(tx.Vout)) > config.TxMaxAllowedOutputs {
return coreerr.E("checkOutputs", fmt.Sprintf("%d", len(tx.Vout)), ErrTooManyOutputs)
return coreerr.E("checkOutputs", core.Sprintf("%d", len(tx.Vout)), ErrTooManyOutputs)
}
for i, vout := range tx.Vout {
switch o := vout.(type) {
case types.TxOutputBare:
if o.Amount == 0 {
return coreerr.E("checkOutputs", fmt.Sprintf("output %d has zero amount", i), ErrInvalidOutput)
return coreerr.E("checkOutputs", core.Sprintf("output %d has zero amount", i), ErrInvalidOutput)
}
// HTLC and Multisig output targets require at least HF1.
switch o.Target.(type) {
case types.TxOutHTLC, types.TxOutMultisig:
if !hf1Active {
return coreerr.E("checkOutputs", fmt.Sprintf("output %d: HTLC/multisig target pre-HF1", i), ErrInvalidOutput)
return coreerr.E("checkOutputs", core.Sprintf("output %d: HTLC/multisig target pre-HF1", i), ErrInvalidOutput)
}
}
case types.TxOutputZarcanum:

View file

@ -7,8 +7,7 @@ package consensus
import (
"bytes"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types"
@ -47,7 +46,7 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
for i := uint64(0); i < count; i++ {
tag := dec.ReadUint8()
if dec.Err() != nil {
return nil, coreerr.E("parseV2Signatures", fmt.Sprintf("read sig tag %d", i), dec.Err())
return nil, coreerr.E("parseV2Signatures", core.Sprintf("read sig tag %d", i), dec.Err())
}
entry := v2SigEntry{tag: tag}
@ -56,7 +55,7 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
case types.SigTypeZC:
zc, err := parseZCSig(dec)
if err != nil {
return nil, coreerr.E("parseV2Signatures", fmt.Sprintf("parse ZC_sig %d", i), err)
return nil, coreerr.E("parseV2Signatures", core.Sprintf("parse ZC_sig %d", i), err)
}
entry.zcSig = zc
@ -76,11 +75,11 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
skipZarcanumSig(dec)
default:
return nil, coreerr.E("parseV2Signatures", fmt.Sprintf("unsupported sig tag 0x%02x", tag), nil)
return nil, coreerr.E("parseV2Signatures", core.Sprintf("unsupported sig tag 0x%02x", tag), nil)
}
if dec.Err() != nil {
return nil, coreerr.E("parseV2Signatures", fmt.Sprintf("parse sig %d (tag 0x%02x)", i, tag), dec.Err())
return nil, coreerr.E("parseV2Signatures", core.Sprintf("parse sig %d (tag 0x%02x)", i, tag), dec.Err())
}
entries = append(entries, entry)
}
@ -119,7 +118,7 @@ func parseZCSig(dec *wire.Decoder) (*zcSigData, error) {
}
if rgCount != rxCount {
return nil, coreerr.E("parseZCSig", fmt.Sprintf("CLSAG r_g count %d != r_x count %d", rgCount, rxCount), nil)
return nil, coreerr.E("parseZCSig", core.Sprintf("CLSAG r_g count %d != r_x count %d", rgCount, rxCount), nil)
}
zc.ringSize = int(rgCount)
@ -205,7 +204,7 @@ func parseV2Proofs(raw []byte) (*v2ProofData, error) {
for i := uint64(0); i < count; i++ {
tag := dec.ReadUint8()
if dec.Err() != nil {
return nil, coreerr.E("parseV2Proofs", fmt.Sprintf("read proof tag %d", i), dec.Err())
return nil, coreerr.E("parseV2Proofs", core.Sprintf("read proof tag %d", i), dec.Err())
}
switch tag {
@ -218,7 +217,7 @@ func parseV2Proofs(raw []byte) (*v2ProofData, error) {
for j := uint64(0); j < nBGE; j++ {
data.bgeProofs[j] = readBGEProofBytes(dec)
if dec.Err() != nil {
return nil, coreerr.E("parseV2Proofs", fmt.Sprintf("parse BGE proof %d", j), dec.Err())
return nil, coreerr.E("parseV2Proofs", core.Sprintf("parse BGE proof %d", j), dec.Err())
}
}
@ -239,7 +238,7 @@ func parseV2Proofs(raw []byte) (*v2ProofData, error) {
}
default:
return nil, coreerr.E("parseV2Proofs", fmt.Sprintf("unsupported proof tag 0x%02x", tag), nil)
return nil, coreerr.E("parseV2Proofs", core.Sprintf("unsupported proof tag 0x%02x", tag), nil)
}
}

View file

@ -6,8 +6,7 @@
package consensus
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -71,7 +70,7 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
}
if len(tx.Signatures) != ringInputCount {
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: signature count %d != input count %d", len(tx.Signatures), ringInputCount), nil)
return coreerr.E("verifyV1Signatures", core.Sprintf("consensus: signature count %d != input count %d", len(tx.Signatures), ringInputCount), nil)
}
// Actual NLSAG verification requires the crypto bridge and ring outputs.
@ -110,12 +109,12 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
ringKeys, err := getRingOutputs(amount, offsets)
if err != nil {
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: failed to fetch ring outputs for input %d", sigIdx), err)
return coreerr.E("verifyV1Signatures", core.Sprintf("consensus: failed to fetch ring outputs for input %d", sigIdx), err)
}
ringSigs := tx.Signatures[sigIdx]
if len(ringSigs) != len(ringKeys) {
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: input %d has %d signatures but ring size %d", sigIdx, len(ringSigs), len(ringKeys)), nil)
return coreerr.E("verifyV1Signatures", core.Sprintf("consensus: input %d has %d signatures but ring size %d", sigIdx, len(ringSigs), len(ringKeys)), nil)
}
// Convert typed slices to raw byte arrays for the crypto bridge.
@ -130,7 +129,7 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
}
if !crypto.CheckRingSignature([32]byte(prefixHash), [32]byte(keyImage), pubs, sigs) {
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: ring signature verification failed for input %d", sigIdx), nil)
return coreerr.E("verifyV1Signatures", core.Sprintf("consensus: ring signature verification failed for input %d", sigIdx), nil)
}
sigIdx++
@ -149,7 +148,7 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
// Match signatures to inputs: each input must have a corresponding signature.
if len(sigEntries) != len(tx.Vin) {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: V2 signature count %d != input count %d", len(sigEntries), len(tx.Vin)), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: V2 signature count %d != input count %d", len(sigEntries), len(tx.Vin)), nil)
}
// Validate that ZC inputs have ZC_sig and vice versa.
@ -157,11 +156,11 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
switch vin.(type) {
case types.TxInputZC:
if sigEntries[i].tag != types.SigTypeZC {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: input %d is ZC but signature tag is 0x%02x", i, sigEntries[i].tag), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: input %d is ZC but signature tag is 0x%02x", i, sigEntries[i].tag), nil)
}
case types.TxInputToKey:
if sigEntries[i].tag != types.SigTypeNLSAG && sigEntries[i].tag != types.SigTypeVoid {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: input %d is to_key but signature tag is 0x%02x", i, sigEntries[i].tag), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: input %d is to_key but signature tag is 0x%02x", i, sigEntries[i].tag), nil)
}
}
}
@ -185,7 +184,7 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
zc := sigEntries[i].zcSig
if zc == nil {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: input %d: missing ZC_sig data", i), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: input %d: missing ZC_sig data", i), nil)
}
// Extract absolute global indices from key offsets.
@ -196,11 +195,11 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
ringMembers, err := getZCRingOutputs(offsets)
if err != nil {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: failed to fetch ZC ring outputs for input %d", i), err)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: failed to fetch ZC ring outputs for input %d", i), err)
}
if len(ringMembers) != zc.ringSize {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: input %d: ring size %d from chain != %d from sig", i, len(ringMembers), zc.ringSize), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: input %d: ring size %d from chain != %d from sig", i, len(ringMembers), zc.ringSize), nil)
}
// Build flat ring: [stealth(32) | commitment(32) | blinded_asset_id(32)] per entry.
@ -219,7 +218,7 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
[32]byte(zcIn.KeyImage),
zc.clsagFlatSig,
) {
return coreerr.E("verifyV2Signatures", fmt.Sprintf("consensus: CLSAG GGX verification failed for input %d", i), nil)
return coreerr.E("verifyV2Signatures", core.Sprintf("consensus: CLSAG GGX verification failed for input %d", i), nil)
}
}
@ -273,7 +272,7 @@ func verifyBGEProofs(tx *types.Transaction, sigEntries []v2SigEntry,
}
if len(proofs.bgeProofs) != len(outputAssetIDs) {
return coreerr.E("verifyBGEProofs", fmt.Sprintf("consensus: BGE proof count %d != Zarcanum output count %d", len(proofs.bgeProofs), len(outputAssetIDs)), nil)
return coreerr.E("verifyBGEProofs", core.Sprintf("consensus: BGE proof count %d != Zarcanum output count %d", len(proofs.bgeProofs), len(outputAssetIDs)), nil)
}
// Collect pseudo-out asset IDs from ZC signatures and expand to full points.
@ -289,7 +288,7 @@ func verifyBGEProofs(tx *types.Transaction, sigEntries []v2SigEntry,
for i, p := range pseudoOutAssetIDs {
full, err := crypto.PointMul8(p)
if err != nil {
return coreerr.E("verifyBGEProofs", fmt.Sprintf("consensus: mul8 pseudo-out asset ID %d", i), err)
return coreerr.E("verifyBGEProofs", core.Sprintf("consensus: mul8 pseudo-out asset ID %d", i), err)
}
mul8PseudoOuts[i] = full
}
@ -300,7 +299,7 @@ func verifyBGEProofs(tx *types.Transaction, sigEntries []v2SigEntry,
// mul8 the output's blinded asset ID.
mul8Out, err := crypto.PointMul8(outAssetID)
if err != nil {
return coreerr.E("verifyBGEProofs", fmt.Sprintf("consensus: mul8 output asset ID %d", j), err)
return coreerr.E("verifyBGEProofs", core.Sprintf("consensus: mul8 output asset ID %d", j), err)
}
// ring[i] = mul8(pseudo_out_i) - mul8(output_j)
@ -308,13 +307,13 @@ func verifyBGEProofs(tx *types.Transaction, sigEntries []v2SigEntry,
for i, mul8Pseudo := range mul8PseudoOuts {
diff, err := crypto.PointSub(mul8Pseudo, mul8Out)
if err != nil {
return coreerr.E("verifyBGEProofs", fmt.Sprintf("consensus: BGE ring[%d][%d] sub", j, i), err)
return coreerr.E("verifyBGEProofs", core.Sprintf("consensus: BGE ring[%d][%d] sub", j, i), err)
}
ring[i] = diff
}
if !crypto.VerifyBGE(context, ring, proofs.bgeProofs[j]) {
return coreerr.E("verifyBGEProofs", fmt.Sprintf("consensus: BGE proof verification failed for output %d", j), nil)
return coreerr.E("verifyBGEProofs", core.Sprintf("consensus: BGE proof verification failed for output %d", j), nil)
}
}

View file

@ -8,9 +8,9 @@ package crypto
import "C"
import (
"fmt"
"unsafe"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -21,7 +21,7 @@ func GenerateKeys() (pub [32]byte, sec [32]byte, err error) {
(*C.uint8_t)(unsafe.Pointer(&sec[0])),
)
if rc != 0 {
err = coreerr.E("GenerateKeys", fmt.Sprintf("generate_keys failed (rc=%d)", rc), nil)
err = coreerr.E("GenerateKeys", core.Sprintf("generate_keys failed (rc=%d)", rc), nil)
}
return
}
@ -34,7 +34,7 @@ func SecretToPublic(sec [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&pub[0])),
)
if rc != 0 {
return pub, coreerr.E("SecretToPublic", fmt.Sprintf("secret_to_public failed (rc=%d)", rc), nil)
return pub, coreerr.E("SecretToPublic", core.Sprintf("secret_to_public failed (rc=%d)", rc), nil)
}
return pub, nil
}

View file

@ -8,9 +8,9 @@ package crypto
// #include "bridge.h"
import "C"
import (
"fmt"
"unsafe"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -25,7 +25,7 @@ func RandomXHash(key, input []byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&output[0])),
)
if ret != 0 {
return output, coreerr.E("RandomXHash", fmt.Sprintf("RandomX hash failed with code %d", ret), nil)
return output, coreerr.E("RandomXHash", core.Sprintf("RandomX hash failed with code %d", ret), nil)
}
return output, nil
}

3
go.mod
View file

@ -3,6 +3,7 @@ module dappco.re/go/core/blockchain
go 1.26.0
require (
dappco.re/go/core v0.8.0-alpha.1
dappco.re/go/core/cli v0.3.1
dappco.re/go/core/io v0.2.0
dappco.re/go/core/log v0.1.0
@ -63,7 +64,7 @@ require (
)
replace (
dappco.re/go/core => forge.lthn.ai/core/go v0.5.0
dappco.re/go/core => forge.lthn.ai/core/go v0.8.0-alpha.1
dappco.re/go/core/cli => forge.lthn.ai/core/cli v0.3.1
dappco.re/go/core/crypt => forge.lthn.ai/core/go-crypt v0.1.7
dappco.re/go/core/i18n => forge.lthn.ai/core/go-i18n v0.1.4

2
go.sum
View file

@ -2,6 +2,8 @@ forge.lthn.ai/core/cli v0.3.1 h1:ZpHhaDrdbaV98JDxj/f0E5nytYk9tTMRu3qohGyK4M0=
forge.lthn.ai/core/cli v0.3.1/go.mod h1:28cOl9eK0H033Otkjrv9f/QCmtHcJl+IIx4om8JskOg=
forge.lthn.ai/core/go v0.3.1 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM=
forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc=
forge.lthn.ai/core/go v0.8.0-alpha.1 h1:dybLUTR9HMqtT5lxolAUaF1c+j/vGPIDMyiMygyGlq8=
forge.lthn.ai/core/go v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
forge.lthn.ai/core/go-crypt v0.1.6 h1:jB7L/28S1NR+91u3GcOYuKfBLzPhhBUY1fRe6WkGVns=
forge.lthn.ai/core/go-crypt v0.1.6/go.mod h1:4VZAGqxlbadhSB66sJkdj54/HSJ+bSxVgwWK5kMMYDo=
forge.lthn.ai/core/go-i18n v0.1.4 h1:zOHUUJDgRo88/3tj++kN+VELg/buyZ4T2OSdG3HBbLQ=

View file

@ -10,11 +10,11 @@ import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"strconv"
"sync/atomic"
"time"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/consensus"
@ -141,7 +141,7 @@ func (m *Miner) Start(ctx context.Context) error {
// Parse difficulty.
diff, err := strconv.ParseUint(tmpl.Difficulty, 10, 64)
if err != nil {
return coreerr.E("Miner.Start", fmt.Sprintf("mining: invalid difficulty %q", tmpl.Difficulty), err)
return coreerr.E("Miner.Start", core.Sprintf("mining: invalid difficulty %q", tmpl.Difficulty), err)
}
// Decode the block template blob.

View file

@ -6,8 +6,7 @@
package rpc
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -21,7 +20,7 @@ func (c *Client) GetLastBlockHeader() (*BlockHeader, error) {
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetLastBlockHeader", fmt.Sprintf("getlastblockheader: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetLastBlockHeader", core.Sprintf("getlastblockheader: status %q", resp.Status), nil)
}
return &resp.BlockHeader, nil
}
@ -39,7 +38,7 @@ func (c *Client) GetBlockHeaderByHeight(height uint64) (*BlockHeader, error) {
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetBlockHeaderByHeight", fmt.Sprintf("getblockheaderbyheight: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetBlockHeaderByHeight", core.Sprintf("getblockheaderbyheight: status %q", resp.Status), nil)
}
return &resp.BlockHeader, nil
}
@ -57,7 +56,7 @@ func (c *Client) GetBlockHeaderByHash(hash string) (*BlockHeader, error) {
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetBlockHeaderByHash", fmt.Sprintf("getblockheaderbyhash: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetBlockHeaderByHash", core.Sprintf("getblockheaderbyhash: status %q", resp.Status), nil)
}
return &resp.BlockHeader, nil
}
@ -77,7 +76,7 @@ func (c *Client) GetBlocksDetails(heightStart, count uint64) ([]BlockDetails, er
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetBlocksDetails", fmt.Sprintf("get_blocks_details: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetBlocksDetails", core.Sprintf("get_blocks_details: status %q", resp.Status), nil)
}
return resp.Blocks, nil
}

View file

@ -29,8 +29,8 @@ var testBlockHeaderJSON = `{
func blockHeaderResponse() jsonRPCResponse {
return jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{"block_header":` + testBlockHeaderJSON + `,"status":"OK"}`),
ID: rawJSON(`"0"`),
Result: rawJSON(`{"block_header":` + testBlockHeaderJSON + `,"status":"OK"}`),
}
}
@ -96,8 +96,8 @@ func TestGetBlocksDetails_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{
ID: rawJSON(`"0"`),
Result: rawJSON(`{
"blocks": [{
"height": 0,
"timestamp": 1770897600,
@ -134,7 +134,7 @@ func TestGetBlockHeaderByHeight_Bad_TooBig(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
ID: rawJSON(`"0"`),
Error: &jsonRPCError{Code: -2, Message: "TOO_BIG_HEIGHT"},
})
}))

View file

@ -8,13 +8,12 @@ package rpc
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -38,7 +37,7 @@ func NewClientWithHTTP(daemonURL string, httpClient *http.Client) *Client {
// Fall through with raw URL.
return &Client{url: daemonURL + "/json_rpc", baseURL: daemonURL, httpClient: httpClient}
}
baseURL := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
baseURL := core.Sprintf("%s://%s", u.Scheme, u.Host)
if u.Path == "" || u.Path == "/" {
u.Path = "/json_rpc"
}
@ -56,7 +55,7 @@ type RPCError struct {
}
func (e *RPCError) Error() string {
return fmt.Sprintf("rpc error %d: %s", e.Code, e.Message)
return core.Sprintf("rpc error %d: %s", e.Code, e.Message)
}
// JSON-RPC 2.0 envelope types.
@ -68,10 +67,10 @@ type jsonRPCRequest struct {
}
type jsonRPCResponse struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"`
Result json.RawMessage `json:"result"`
Error *jsonRPCError `json:"error,omitempty"`
JSONRPC string `json:"jsonrpc"`
ID rawJSON `json:"id"`
Result rawJSON `json:"result"`
Error *jsonRPCError `json:"error,omitempty"`
}
type jsonRPCError struct {
@ -79,26 +78,37 @@ type jsonRPCError struct {
Message string `json:"message"`
}
type rawJSON []byte
func (r *rawJSON) UnmarshalJSON(data []byte) error {
*r = append((*r)[:0], data...)
return nil
}
func (r rawJSON) MarshalJSON() ([]byte, error) {
if r == nil {
return []byte("null"), nil
}
return []byte(r), nil
}
// call makes a JSON-RPC 2.0 call to /json_rpc.
func (c *Client) call(method string, params any, result any) error {
reqBody, err := json.Marshal(jsonRPCRequest{
reqBody := core.JSONMarshalString(jsonRPCRequest{
JSONRPC: "2.0",
ID: "0",
Method: method,
Params: params,
})
if err != nil {
return coreerr.E("Client.call", "marshal request", err)
}
resp, err := c.httpClient.Post(c.url, "application/json", bytes.NewReader(reqBody))
resp, err := c.httpClient.Post(c.url, "application/json", bytes.NewReader([]byte(reqBody)))
if err != nil {
return coreerr.E("Client.call", fmt.Sprintf("post %s", method), err)
return coreerr.E("Client.call", core.Sprintf("post %s", method), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return coreerr.E("Client.call", fmt.Sprintf("http %d from %s", resp.StatusCode, method), nil)
return coreerr.E("Client.call", core.Sprintf("http %d from %s", resp.StatusCode, method), nil)
}
body, err := io.ReadAll(resp.Body)
@ -107,8 +117,8 @@ func (c *Client) call(method string, params any, result any) error {
}
var rpcResp jsonRPCResponse
if err := json.Unmarshal(body, &rpcResp); err != nil {
return coreerr.E("Client.call", "unmarshal response", err)
if r := core.JSONUnmarshalString(string(body), &rpcResp); !r.OK {
return coreerr.E("Client.call", "unmarshal response", r.Value.(error))
}
if rpcResp.Error != nil {
@ -116,8 +126,8 @@ func (c *Client) call(method string, params any, result any) error {
}
if result != nil && len(rpcResp.Result) > 0 {
if err := json.Unmarshal(rpcResp.Result, result); err != nil {
return coreerr.E("Client.call", "unmarshal result", err)
if r := core.JSONUnmarshalString(string(rpcResp.Result), result); !r.OK {
return coreerr.E("Client.call", "unmarshal result", r.Value.(error))
}
}
return nil
@ -125,20 +135,17 @@ func (c *Client) call(method string, params any, result any) error {
// legacyCall makes a plain JSON POST to a legacy URI path (e.g. /getheight).
func (c *Client) legacyCall(path string, params any, result any) error {
reqBody, err := json.Marshal(params)
if err != nil {
return coreerr.E("Client.legacyCall", "marshal request", err)
}
reqBody := core.JSONMarshalString(params)
url := c.baseURL + path
resp, err := c.httpClient.Post(url, "application/json", bytes.NewReader(reqBody))
resp, err := c.httpClient.Post(url, "application/json", bytes.NewReader([]byte(reqBody)))
if err != nil {
return coreerr.E("Client.legacyCall", fmt.Sprintf("post %s", path), err)
return coreerr.E("Client.legacyCall", core.Sprintf("post %s", path), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return coreerr.E("Client.legacyCall", fmt.Sprintf("http %d from %s", resp.StatusCode, path), nil)
return coreerr.E("Client.legacyCall", core.Sprintf("http %d from %s", resp.StatusCode, path), nil)
}
body, err := io.ReadAll(resp.Body)
@ -147,8 +154,8 @@ func (c *Client) legacyCall(path string, params any, result any) error {
}
if result != nil {
if err := json.Unmarshal(body, result); err != nil {
return coreerr.E("Client.legacyCall", "unmarshal response", err)
if r := core.JSONUnmarshalString(string(body), result); !r.OK {
return coreerr.E("Client.legacyCall", "unmarshal response", r.Value.(error))
}
}
return nil

View file

@ -36,8 +36,8 @@ func TestClient_Good_JSONRPCCall(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{"count":6300,"status":"OK"}`),
ID: rawJSON(`"0"`),
Result: rawJSON(`{"count":6300,"status":"OK"}`),
})
}))
defer srv.Close()
@ -88,7 +88,7 @@ func TestClient_Bad_RPCError(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
ID: rawJSON(`"0"`),
Error: &jsonRPCError{
Code: -2,
Message: "TOO_BIG_HEIGHT",

View file

@ -6,8 +6,7 @@
package rpc
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -25,7 +24,7 @@ func (c *Client) GetInfo() (*DaemonInfo, error) {
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetInfo", fmt.Sprintf("getinfo: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetInfo", core.Sprintf("getinfo: status %q", resp.Status), nil)
}
return &resp.DaemonInfo, nil
}
@ -41,7 +40,7 @@ func (c *Client) GetHeight() (uint64, error) {
return 0, err
}
if resp.Status != "OK" {
return 0, coreerr.E("Client.GetHeight", fmt.Sprintf("getheight: status %q", resp.Status), nil)
return 0, coreerr.E("Client.GetHeight", core.Sprintf("getheight: status %q", resp.Status), nil)
}
return resp.Height, nil
}
@ -56,7 +55,7 @@ func (c *Client) GetBlockCount() (uint64, error) {
return 0, err
}
if resp.Status != "OK" {
return 0, coreerr.E("Client.GetBlockCount", fmt.Sprintf("getblockcount: status %q", resp.Status), nil)
return 0, coreerr.E("Client.GetBlockCount", core.Sprintf("getblockcount: status %q", resp.Status), nil)
}
return resp.Count, nil
}

View file

@ -17,8 +17,8 @@ func TestGetInfo_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{
ID: rawJSON(`"0"`),
Result: rawJSON(`{
"height": 6300,
"tx_count": 12345,
"tx_pool_size": 3,
@ -83,8 +83,8 @@ func TestGetBlockCount_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{"count":6301,"status":"OK"}`),
ID: rawJSON(`"0"`),
Result: rawJSON(`{"count":6301,"status":"OK"}`),
})
}))
defer srv.Close()

View file

@ -6,8 +6,7 @@
package rpc
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -24,7 +23,7 @@ func (c *Client) SubmitBlock(hexBlob string) error {
return err
}
if resp.Status != "OK" {
return coreerr.E("Client.SubmitBlock", fmt.Sprintf("submitblock: status %q", resp.Status), nil)
return coreerr.E("Client.SubmitBlock", core.Sprintf("submitblock: status %q", resp.Status), nil)
}
return nil
}
@ -51,7 +50,7 @@ func (c *Client) GetBlockTemplate(walletAddr string) (*BlockTemplateResponse, er
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetBlockTemplate", fmt.Sprintf("getblocktemplate: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetBlockTemplate", core.Sprintf("getblocktemplate: status %q", resp.Status), nil)
}
return &resp, nil
}

View file

@ -31,8 +31,8 @@ func TestSubmitBlock_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{"status":"OK"}`),
ID: rawJSON(`"0"`),
Result: rawJSON(`{"status":"OK"}`),
})
}))
defer srv.Close()
@ -49,7 +49,7 @@ func TestSubmitBlock_Bad_Rejected(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
ID: rawJSON(`"0"`),
Error: &jsonRPCError{Code: -7, Message: "BLOCK_NOT_ACCEPTED"},
})
}))
@ -86,8 +86,8 @@ func TestGetBlockTemplate_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{
ID: rawJSON(`"0"`),
Result: rawJSON(`{
"difficulty": "42",
"height": 100,
"blocktemplate_blob": "0100000000000000000000000000",
@ -122,8 +122,8 @@ func TestGetBlockTemplate_Bad_Status(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{"status":"BUSY"}`),
ID: rawJSON(`"0"`),
Result: rawJSON(`{"status":"BUSY"}`),
})
}))
defer srv.Close()

View file

@ -6,8 +6,7 @@
package rpc
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -24,7 +23,7 @@ func (c *Client) GetTxDetails(txHash string) (*TxInfo, error) {
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetTxDetails", fmt.Sprintf("get_tx_details: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetTxDetails", core.Sprintf("get_tx_details: status %q", resp.Status), nil)
}
return &resp.TxInfo, nil
}
@ -45,7 +44,7 @@ func (c *Client) GetTransactions(hashes []string) (txsHex []string, missed []str
return nil, nil, err
}
if resp.Status != "OK" {
return nil, nil, coreerr.E("Client.GetTransactions", fmt.Sprintf("gettransactions: status %q", resp.Status), nil)
return nil, nil, coreerr.E("Client.GetTransactions", core.Sprintf("gettransactions: status %q", resp.Status), nil)
}
return resp.TxsAsHex, resp.MissedTx, nil
}

View file

@ -17,8 +17,8 @@ func TestGetTxDetails_Good(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
Result: json.RawMessage(`{
ID: rawJSON(`"0"`),
Result: rawJSON(`{
"status": "OK",
"tx_info": {
"id": "a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8",
@ -55,7 +55,7 @@ func TestGetTxDetails_Bad_NotFound(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0",
ID: json.RawMessage(`"0"`),
ID: rawJSON(`"0"`),
Error: &jsonRPCError{Code: -14, Message: "NOT_FOUND"},
})
}))

View file

@ -6,10 +6,9 @@
package rpc
import (
"encoding/hex"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"encoding/hex"
)
// RandomOutputEntry is a decoy output returned by getrandom_outs.
@ -35,7 +34,7 @@ func (c *Client) GetRandomOutputs(amount uint64, count int) ([]RandomOutputEntry
return nil, err
}
if resp.Status != "OK" {
return nil, coreerr.E("Client.GetRandomOutputs", fmt.Sprintf("getrandom_outs: status %q", resp.Status), nil)
return nil, coreerr.E("Client.GetRandomOutputs", core.Sprintf("getrandom_outs: status %q", resp.Status), nil)
}
return resp.Outs, nil
}
@ -55,7 +54,7 @@ func (c *Client) SendRawTransaction(txBlob []byte) error {
return err
}
if resp.Status != "OK" {
return coreerr.E("Client.SendRawTransaction", fmt.Sprintf("sendrawtransaction: status %q", resp.Status), nil)
return coreerr.E("Client.SendRawTransaction", core.Sprintf("sendrawtransaction: status %q", resp.Status), nil)
}
return nil
}

View file

@ -9,11 +9,11 @@ import (
"context"
"crypto/rand"
"encoding/binary"
"fmt"
"log"
"net"
"time"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/chain"
@ -56,7 +56,7 @@ func syncLoop(ctx context.Context, c *chain.Chain, cfg *config.ChainConfig, fork
func syncOnce(ctx context.Context, c *chain.Chain, cfg *config.ChainConfig, opts chain.SyncOptions, seed string) error {
conn, err := net.DialTimeout("tcp", seed, 10*time.Second)
if err != nil {
return coreerr.E("syncOnce", fmt.Sprintf("dial %s", seed), err)
return coreerr.E("syncOnce", core.Sprintf("dial %s", seed), err)
}
defer conn.Close()
@ -94,7 +94,7 @@ func syncOnce(ctx context.Context, c *chain.Chain, cfg *config.ChainConfig, opts
return coreerr.E("syncOnce", "read handshake", err)
}
if hdr.Command != uint32(p2p.CommandHandshake) {
return coreerr.E("syncOnce", fmt.Sprintf("unexpected command %d", hdr.Command), nil)
return coreerr.E("syncOnce", core.Sprintf("unexpected command %d", hdr.Command), nil)
}
var resp p2p.HandshakeResponse

View file

@ -6,10 +6,9 @@
package tui
import (
"fmt"
"strings"
"time"
"dappco.re/go/core"
cli "dappco.re/go/core/cli/pkg/cli"
tea "github.com/charmbracelet/bubbletea"
@ -215,10 +214,10 @@ func (m *ExplorerModel) viewBlockList() string {
return " no blocks \u2014 chain is empty"
}
var b strings.Builder
b := core.NewBuilder()
// Header row.
header := fmt.Sprintf(" %-8s %-18s %5s %12s %12s",
header := core.Sprintf(" %-8s %-18s %5s %12s %12s",
"Height", "Hash", "Txs", "Difficulty", "Age")
b.WriteString(header)
b.WriteByte('\n')
@ -240,10 +239,10 @@ func (m *ExplorerModel) viewBlockList() string {
prefix = "> "
}
hashShort := fmt.Sprintf("%x", row.Hash[:4]) + "..."
hashShort := core.Concat(core.Sprintf("%x", row.Hash[:4]), "...")
age := formatAge(time.Unix(int64(row.Timestamp), 0))
line := fmt.Sprintf("%s%-8d %-18s %5d %12s %12s",
line := core.Sprintf("%s%-8d %-18s %5d %12s %12s",
prefix, row.Height, hashShort, row.TxCount,
formatDifficulty(row.Difficulty), age)
@ -264,17 +263,17 @@ func (m *ExplorerModel) viewBlockDetail() string {
return " no block selected"
}
var b strings.Builder
b := core.NewBuilder()
meta := m.blockMeta
blk := m.block
b.WriteString(fmt.Sprintf(" Block %d\n", meta.Height))
b.WriteString(fmt.Sprintf(" Hash: %x\n", meta.Hash))
b.WriteString(fmt.Sprintf(" Timestamp: %s\n", time.Unix(int64(meta.Timestamp), 0).UTC().Format(time.RFC3339)))
b.WriteString(fmt.Sprintf(" Difficulty: %s\n", formatDifficulty(meta.Difficulty)))
b.WriteString(fmt.Sprintf(" Version: %d.%d\n", blk.MajorVersion, blk.MinorVersion))
b.WriteString(fmt.Sprintf(" Nonce: %d\n", blk.Nonce))
b.WriteString(fmt.Sprintf(" Txs: %d\n\n", len(blk.TxHashes)))
b.WriteString(core.Sprintf(" Block %d\n", meta.Height))
b.WriteString(core.Sprintf(" Hash: %x\n", meta.Hash))
b.WriteString(core.Sprintf(" Timestamp: %s\n", time.Unix(int64(meta.Timestamp), 0).UTC().Format(time.RFC3339)))
b.WriteString(core.Sprintf(" Difficulty: %s\n", formatDifficulty(meta.Difficulty)))
b.WriteString(core.Sprintf(" Version: %d.%d\n", blk.MajorVersion, blk.MinorVersion))
b.WriteString(core.Sprintf(" Nonce: %d\n", blk.Nonce))
b.WriteString(core.Sprintf(" Txs: %d\n\n", len(blk.TxHashes)))
if len(blk.TxHashes) == 0 {
b.WriteString(" (coinbase only)")
@ -285,7 +284,7 @@ func (m *ExplorerModel) viewBlockDetail() string {
if i == m.txCursor {
prefix = "> "
}
b.WriteString(fmt.Sprintf(" %s%x\n", prefix, txHash[:8]))
b.WriteString(core.Sprintf(" %s%x\n", prefix, txHash[:8]))
}
}
@ -297,25 +296,25 @@ func (m *ExplorerModel) viewTxDetail() string {
return " no transaction selected"
}
var b strings.Builder
b := core.NewBuilder()
tx := m.tx
b.WriteString(" Transaction\n")
b.WriteString(fmt.Sprintf(" Hash: %x\n", m.txHash))
b.WriteString(fmt.Sprintf(" Version: %d\n", tx.Version))
b.WriteString(fmt.Sprintf(" Inputs: %d\n", len(tx.Vin)))
b.WriteString(fmt.Sprintf(" Outputs: %d\n\n", len(tx.Vout)))
b.WriteString(core.Sprintf(" Hash: %x\n", m.txHash))
b.WriteString(core.Sprintf(" Version: %d\n", tx.Version))
b.WriteString(core.Sprintf(" Inputs: %d\n", len(tx.Vin)))
b.WriteString(core.Sprintf(" Outputs: %d\n\n", len(tx.Vout)))
if len(tx.Vin) > 0 {
b.WriteString(" Inputs:\n")
for i, in := range tx.Vin {
switch v := in.(type) {
case types.TxInputGenesis:
b.WriteString(fmt.Sprintf(" [%d] coinbase height=%d\n", i, v.Height))
b.WriteString(core.Sprintf(" [%d] coinbase height=%d\n", i, v.Height))
case types.TxInputToKey:
b.WriteString(fmt.Sprintf(" [%d] to_key amount=%d key_image=%x\n", i, v.Amount, v.KeyImage[:4]))
b.WriteString(core.Sprintf(" [%d] to_key amount=%d key_image=%x\n", i, v.Amount, v.KeyImage[:4]))
default:
b.WriteString(fmt.Sprintf(" [%d] %T\n", i, v))
b.WriteString(core.Sprintf(" [%d] %T\n", i, v))
}
}
}
@ -326,14 +325,14 @@ func (m *ExplorerModel) viewTxDetail() string {
switch v := out.(type) {
case types.TxOutputBare:
if toKey, ok := v.Target.(types.TxOutToKey); ok {
b.WriteString(fmt.Sprintf(" [%d] bare amount=%d key=%x\n", i, v.Amount, toKey.Key[:4]))
b.WriteString(core.Sprintf(" [%d] bare amount=%d key=%x\n", i, v.Amount, toKey.Key[:4]))
} else {
b.WriteString(fmt.Sprintf(" [%d] bare amount=%d target=%T\n", i, v.Amount, v.Target))
b.WriteString(core.Sprintf(" [%d] bare amount=%d target=%T\n", i, v.Amount, v.Target))
}
case types.TxOutputZarcanum:
b.WriteString(fmt.Sprintf(" [%d] zarcanum stealth=%x\n", i, v.StealthAddress[:4]))
b.WriteString(core.Sprintf(" [%d] zarcanum stealth=%x\n", i, v.StealthAddress[:4]))
default:
b.WriteString(fmt.Sprintf(" [%d] %T\n", i, v))
b.WriteString(core.Sprintf(" [%d] %T\n", i, v))
}
}
}

View file

@ -6,8 +6,7 @@
package tui
import (
"strings"
"dappco.re/go/core"
tea "github.com/charmbracelet/bubbletea"
cli "dappco.re/go/core/cli/pkg/cli"
@ -44,7 +43,7 @@ func (m *KeyHintsModel) Update(msg tea.Msg) (cli.FrameModel, tea.Cmd) {
// View renders a single-line hint bar separated by vertical bars.
// The output is truncated to width if it would overflow.
func (m *KeyHintsModel) View(width, height int) string {
line := " " + strings.Join(m.hints, " \u2502 ")
line := " " + core.Join(" \u2502 ", m.hints...)
if len(line) > width && width > 0 {
line = line[:width]
}

View file

@ -6,9 +6,9 @@
package tui
import (
"fmt"
"time"
"dappco.re/go/core"
tea "github.com/charmbracelet/bubbletea"
cli "dappco.re/go/core/cli/pkg/cli"
@ -53,7 +53,7 @@ func (m *StatusModel) View(width, height int) string {
line = " height 0 | syncing..."
} else {
s := m.status
line = fmt.Sprintf(" height %d | sync %.1f%% | diff %s | %d peers | tip %s",
line = core.Sprintf(" height %d | sync %.1f%% | diff %s | %d peers | tip %s",
s.Height, s.SyncPct, formatDifficulty(s.Difficulty), s.PeerCount, formatAge(s.TipTime))
}
if len(line) > width && width > 0 {
@ -70,13 +70,13 @@ func formatAge(t time.Time) string {
d := time.Since(t)
switch {
case d < time.Minute:
return fmt.Sprintf("%ds ago", int(d.Seconds()))
return core.Sprintf("%ds ago", int(d.Seconds()))
case d < time.Hour:
return fmt.Sprintf("%dm ago", int(d.Minutes()))
return core.Sprintf("%dm ago", int(d.Minutes()))
case d < 24*time.Hour:
return fmt.Sprintf("%dh ago", int(d.Hours()))
return core.Sprintf("%dh ago", int(d.Hours()))
default:
return fmt.Sprintf("%dd ago", int(d.Hours()/24))
return core.Sprintf("%dd ago", int(d.Hours()/24))
}
}
@ -84,12 +84,12 @@ func formatAge(t time.Time) string {
func formatDifficulty(d uint64) string {
switch {
case d >= 1_000_000_000:
return fmt.Sprintf("%.1fG", float64(d)/1_000_000_000)
return core.Sprintf("%.1fG", float64(d)/1_000_000_000)
case d >= 1_000_000:
return fmt.Sprintf("%.1fM", float64(d)/1_000_000)
return core.Sprintf("%.1fM", float64(d)/1_000_000)
case d >= 1_000:
return fmt.Sprintf("%.1fK", float64(d)/1_000)
return core.Sprintf("%.1fK", float64(d)/1_000)
default:
return fmt.Sprintf("%d", d)
return core.Sprintf("%d", d)
}
}

View file

@ -10,9 +10,9 @@
package types
import (
"fmt"
"math/big"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"golang.org/x/crypto/sha3"
@ -99,7 +99,7 @@ func DecodeAddress(s string) (*Address, uint64, error) {
// After the prefix we need exactly 32+32+1+4 = 69 bytes.
remaining := raw[prefixLen:]
if len(remaining) != 69 {
return nil, 0, coreerr.E("DecodeAddress", fmt.Sprintf("types: unexpected address data length: want 69 bytes after prefix, got %d", len(remaining)), nil)
return nil, 0, coreerr.E("DecodeAddress", core.Sprintf("types: unexpected address data length: want 69 bytes after prefix, got %d", len(remaining)), nil)
}
// Validate checksum: Keccak-256 of everything except the last 4 bytes.
@ -223,7 +223,7 @@ func base58Decode(s string) ([]byte, error) {
// Validate that the last block size maps to a valid byte count.
if lastBlockChars > 0 && base58ReverseBlockSizes[lastBlockChars] < 0 {
return nil, coreerr.E("base58Decode", fmt.Sprintf("types: invalid base58 string length %d", len(s)), nil)
return nil, coreerr.E("base58Decode", core.Sprintf("types: invalid base58 string length %d", len(s)), nil)
}
var result []byte
@ -258,7 +258,7 @@ func decodeBlock(s string, byteCount int) ([]byte, error) {
for _, c := range []byte(s) {
idx := base58CharIndex(c)
if idx < 0 {
return nil, coreerr.E("decodeBlock", fmt.Sprintf("types: invalid base58 character %q", c), nil)
return nil, coreerr.E("decodeBlock", core.Sprintf("types: invalid base58 character %q", c), nil)
}
num.Mul(num, base)
num.Add(num, big.NewInt(int64(idx)))
@ -267,7 +267,7 @@ func decodeBlock(s string, byteCount int) ([]byte, error) {
// Convert to fixed-size byte array, big-endian.
raw := num.Bytes()
if len(raw) > byteCount {
return nil, coreerr.E("decodeBlock", fmt.Sprintf("types: base58 block overflow: decoded %d bytes, expected %d", len(raw), byteCount), nil)
return nil, coreerr.E("decodeBlock", core.Sprintf("types: base58 block overflow: decoded %d bytes, expected %d", len(raw), byteCount), nil)
}
// Pad with leading zeroes if necessary.

View file

@ -13,10 +13,9 @@
package types
import (
"encoding/hex"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"encoding/hex"
)
// Hash is a 256-bit (32-byte) hash value, typically produced by Keccak-256.
@ -45,7 +44,7 @@ func HashFromHex(s string) (Hash, error) {
return h, coreerr.E("HashFromHex", "types: invalid hex for hash", err)
}
if len(b) != 32 {
return h, coreerr.E("HashFromHex", fmt.Sprintf("types: hash hex must be 64 characters, got %d", len(s)), nil)
return h, coreerr.E("HashFromHex", core.Sprintf("types: hash hex must be 64 characters, got %d", len(s)), nil)
}
copy(h[:], b)
return h, nil
@ -70,7 +69,7 @@ func PublicKeyFromHex(s string) (PublicKey, error) {
return pk, coreerr.E("PublicKeyFromHex", "types: invalid hex for public key", err)
}
if len(b) != 32 {
return pk, coreerr.E("PublicKeyFromHex", fmt.Sprintf("types: public key hex must be 64 characters, got %d", len(s)), nil)
return pk, coreerr.E("PublicKeyFromHex", core.Sprintf("types: public key hex must be 64 characters, got %d", len(s)), nil)
}
copy(pk[:], b)
return pk, nil

View file

@ -14,9 +14,9 @@ import (
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"encoding/json"
"io"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"golang.org/x/crypto/argon2"
@ -113,10 +113,7 @@ func (a *Account) Address() types.Address {
// Save encrypts the account with Argon2id + AES-256-GCM and persists it to
// the given store. The stored blob layout is: salt (16) | nonce (12) | ciphertext.
func (a *Account) Save(s *store.Store, password string) error {
plaintext, err := json.Marshal(a)
if err != nil {
return coreerr.E("Account.Save", "wallet: marshal account", err)
}
plaintext := []byte(core.JSONMarshalString(a))
salt := make([]byte, saltLen)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
@ -187,8 +184,8 @@ func LoadAccount(s *store.Store, password string) (*Account, error) {
}
var acc Account
if err := json.Unmarshal(plaintext, &acc); err != nil {
return nil, coreerr.E("LoadAccount", "wallet: unmarshal account", err)
if r := core.JSONUnmarshalString(string(plaintext), &acc); !r.OK {
return nil, coreerr.E("LoadAccount", "wallet: unmarshal account", r.Value.(error))
}
return &acc, nil
}

View file

@ -12,9 +12,9 @@ package wallet
import (
"bytes"
"cmp"
"fmt"
"slices"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config"
@ -83,7 +83,7 @@ func (b *V1Builder) Build(req *BuildRequest) (*types.Transaction, error) {
destTotal += dst.Amount
}
if sourceTotal < destTotal+req.Fee {
return nil, coreerr.E("V1Builder.Build", fmt.Sprintf("wallet: insufficient funds: have %d, need %d", sourceTotal, destTotal+req.Fee), nil)
return nil, coreerr.E("V1Builder.Build", core.Sprintf("wallet: insufficient funds: have %d, need %d", sourceTotal, destTotal+req.Fee), nil)
}
change := sourceTotal - destTotal - req.Fee
@ -101,7 +101,7 @@ func (b *V1Builder) Build(req *BuildRequest) (*types.Transaction, error) {
for i, src := range req.Sources {
input, meta, buildErr := b.buildInput(&src)
if buildErr != nil {
return nil, coreerr.E("V1Builder.Build", fmt.Sprintf("wallet: input %d", i), buildErr)
return nil, coreerr.E("V1Builder.Build", core.Sprintf("wallet: input %d", i), buildErr)
}
tx.Vin = append(tx.Vin, input)
metas[i] = meta
@ -112,7 +112,7 @@ func (b *V1Builder) Build(req *BuildRequest) (*types.Transaction, error) {
for _, dst := range req.Destinations {
out, outErr := deriveOutput(txSec, dst.Address, outputIdx, dst.Amount)
if outErr != nil {
return nil, coreerr.E("V1Builder.Build", fmt.Sprintf("wallet: output %d", outputIdx), outErr)
return nil, coreerr.E("V1Builder.Build", core.Sprintf("wallet: output %d", outputIdx), outErr)
}
tx.Vout = append(tx.Vout, out)
outputIdx++
@ -136,7 +136,7 @@ func (b *V1Builder) Build(req *BuildRequest) (*types.Transaction, error) {
for i, meta := range metas {
sigs, signErr := b.signer.SignInput(prefixHash, meta.ephemeral, meta.ring, meta.realIndex)
if signErr != nil {
return nil, coreerr.E("V1Builder.Build", fmt.Sprintf("wallet: sign input %d", i), signErr)
return nil, coreerr.E("V1Builder.Build", core.Sprintf("wallet: sign input %d", i), signErr)
}
tx.Signatures = append(tx.Signatures, sigs)
}

View file

@ -10,10 +10,9 @@
package wallet
import (
"encoding/binary"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"encoding/binary"
"dappco.re/go/core/blockchain/types"
"dappco.re/go/core/blockchain/wire"
@ -59,7 +58,7 @@ func ParseTxExtra(raw []byte) (*TxExtra, error) {
switch tag {
case extraTagPublicKey:
if pos+32 > len(raw) {
return extra, coreerr.E("ParseTxExtra", fmt.Sprintf("wallet: extra: truncated public key at offset %d", pos), nil)
return extra, coreerr.E("ParseTxExtra", core.Sprintf("wallet: extra: truncated public key at offset %d", pos), nil)
}
copy(extra.TxPublicKey[:], raw[pos:pos+32])
pos += 32
@ -112,11 +111,11 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
// String types: varint(length) + length bytes.
case 7, 9, 11, 19:
if len(data) == 0 {
return 0, coreerr.E("skipExtraElement", fmt.Sprintf("wallet: extra: no data for string tag %d", tag), nil)
return 0, coreerr.E("skipExtraElement", core.Sprintf("wallet: extra: no data for string tag %d", tag), nil)
}
length, n, err := wire.DecodeVarint(data)
if err != nil {
return 0, coreerr.E("skipExtraElement", fmt.Sprintf("wallet: extra: invalid string length for tag %d", tag), err)
return 0, coreerr.E("skipExtraElement", core.Sprintf("wallet: extra: invalid string length for tag %d", tag), err)
}
return n + int(length), nil
@ -124,7 +123,7 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
case 14, 15, 16, 26, 27:
_, n, err := wire.DecodeVarint(data)
if err != nil {
return 0, coreerr.E("skipExtraElement", fmt.Sprintf("wallet: extra: invalid varint for tag %d", tag), err)
return 0, coreerr.E("skipExtraElement", core.Sprintf("wallet: extra: invalid varint for tag %d", tag), err)
}
return n, nil
@ -141,6 +140,6 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
return 64, nil // signature
default:
return 0, coreerr.E("skipExtraElement", fmt.Sprintf("wallet: extra: unknown tag %d", tag), nil)
return 0, coreerr.E("skipExtraElement", core.Sprintf("wallet: extra: unknown tag %d", tag), nil)
}
}

View file

@ -2,10 +2,9 @@ package wallet
import (
"encoding/binary"
"fmt"
"hash/crc32"
"strings"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -14,7 +13,7 @@ const numWords = 1626
// MnemonicEncode converts a 32-byte secret key to a 25-word mnemonic phrase.
func MnemonicEncode(key []byte) (string, error) {
if len(key) != 32 {
return "", coreerr.E("MnemonicEncode", fmt.Sprintf("wallet: mnemonic encode requires 32 bytes, got %d", len(key)), nil)
return "", coreerr.E("MnemonicEncode", core.Sprintf("wallet: mnemonic encode requires 32 bytes, got %d", len(key)), nil)
}
words := make([]string, 0, 25)
@ -31,16 +30,16 @@ func MnemonicEncode(key []byte) (string, error) {
checkIdx := checksumIndex(words)
words = append(words, words[checkIdx])
return strings.Join(words, " "), nil
return core.Join(" ", words...), nil
}
// MnemonicDecode converts a 25-word mnemonic phrase to a 32-byte secret key.
func MnemonicDecode(phrase string) ([32]byte, error) {
var key [32]byte
words := strings.Fields(phrase)
words := mnemonicWords(phrase)
if len(words) != 25 {
return key, coreerr.E("MnemonicDecode", fmt.Sprintf("wallet: mnemonic requires 25 words, got %d", len(words)), nil)
return key, coreerr.E("MnemonicDecode", core.Sprintf("wallet: mnemonic requires 25 words, got %d", len(words)), nil)
}
expected := checksumIndex(words[:24])
@ -62,7 +61,7 @@ func MnemonicDecode(phrase string) ([32]byte, error) {
if !ok3 {
word = words[i*3+2]
}
return key, coreerr.E("MnemonicDecode", fmt.Sprintf("wallet: unknown mnemonic word %q", word), nil)
return key, coreerr.E("MnemonicDecode", core.Sprintf("wallet: unknown mnemonic word %q", word), nil)
}
val := uint32(w1) +
@ -74,6 +73,21 @@ func MnemonicDecode(phrase string) ([32]byte, error) {
return key, nil
}
func mnemonicWords(phrase string) []string {
normalised := core.Trim(phrase)
for _, ws := range []string{"\n", "\r", "\t"} {
normalised = core.Replace(normalised, ws, " ")
}
parts := core.Split(normalised, " ")
words := make([]string, 0, len(parts))
for _, part := range parts {
if part != "" {
words = append(words, part)
}
}
return words
}
func checksumIndex(words []string) int {
var prefixes string
for _, w := range words {

View file

@ -10,8 +10,7 @@
package wallet
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/rpc"
@ -70,7 +69,7 @@ func (s *RPCRingSelector) SelectRing(amount uint64, realGlobalIndex uint64, ring
}
if len(members) < ringSize {
return nil, coreerr.E("RPCRingSelector.SelectRing", fmt.Sprintf("wallet: insufficient decoys: got %d, need %d", len(members), ringSize), nil)
return nil, coreerr.E("RPCRingSelector.SelectRing", core.Sprintf("wallet: insufficient decoys: got %d, need %d", len(members), ringSize), nil)
}
return members, nil
}

View file

@ -10,9 +10,7 @@
package wallet
import (
"encoding/json"
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
store "dappco.re/go/core/store"
@ -66,22 +64,18 @@ func (t *Transfer) IsSpendable(chainHeight uint64, _ bool) bool {
// putTransfer serialises a transfer as JSON and stores it in the given store,
// keyed by the transfer's key image hex string.
func putTransfer(s *store.Store, tr *Transfer) error {
val, err := json.Marshal(tr)
if err != nil {
return coreerr.E("putTransfer", "wallet: marshal transfer", err)
}
return s.Set(groupTransfers, tr.KeyImage.String(), string(val))
return s.Set(groupTransfers, tr.KeyImage.String(), core.JSONMarshalString(tr))
}
// getTransfer retrieves and deserialises a transfer by its key image.
func getTransfer(s *store.Store, ki types.KeyImage) (*Transfer, error) {
val, err := s.Get(groupTransfers, ki.String())
if err != nil {
return nil, coreerr.E("getTransfer", fmt.Sprintf("wallet: get transfer %s", ki), err)
return nil, coreerr.E("getTransfer", core.Sprintf("wallet: get transfer %s", ki), err)
}
var tr Transfer
if err := json.Unmarshal([]byte(val), &tr); err != nil {
return nil, coreerr.E("getTransfer", "wallet: unmarshal transfer", err)
if r := core.JSONUnmarshalString(val, &tr); !r.OK {
return nil, coreerr.E("getTransfer", "wallet: unmarshal transfer", r.Value.(error))
}
return &tr, nil
}
@ -107,7 +101,7 @@ func listTransfers(s *store.Store) ([]Transfer, error) {
transfers := make([]Transfer, 0, len(pairs))
for _, val := range pairs {
var tr Transfer
if err := json.Unmarshal([]byte(val), &tr); err != nil {
if r := core.JSONUnmarshalString(val, &tr); !r.OK {
continue
}
transfers = append(transfers, tr)

View file

@ -11,10 +11,10 @@ package wallet
import (
"cmp"
"fmt"
"slices"
"strconv"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/chain"
@ -77,7 +77,7 @@ func (w *Wallet) Sync() error {
for h := lastScanned; h < chainHeight; h++ {
blk, _, err := w.chain.GetBlockByHeight(h)
if err != nil {
return coreerr.E("Wallet.Sync", fmt.Sprintf("wallet: get block %d", h), err)
return coreerr.E("Wallet.Sync", core.Sprintf("wallet: get block %d", h), err)
}
// Scan miner tx.
@ -209,7 +209,7 @@ func (w *Wallet) Send(destinations []Destination, fee uint64) (*types.Transactio
}
}
if selectedSum < needed {
return nil, coreerr.E("Wallet.Send", fmt.Sprintf("wallet: insufficient balance: have %d, need %d", selectedSum, needed), nil)
return nil, coreerr.E("Wallet.Send", core.Sprintf("wallet: insufficient balance: have %d, need %d", selectedSum, needed), nil)
}
req := &BuildRequest{

View file

@ -7,9 +7,9 @@ package wire
import (
"encoding/binary"
"fmt"
"io"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -82,7 +82,7 @@ func (d *Decoder) ReadBytes(n int) []byte {
return nil
}
if n < 0 || n > MaxBlobSize {
d.err = coreerr.E("Decoder.ReadBytes", fmt.Sprintf("wire: blob size %d exceeds maximum %d", n, MaxBlobSize), nil)
d.err = coreerr.E("Decoder.ReadBytes", core.Sprintf("wire: blob size %d exceeds maximum %d", n, MaxBlobSize), nil)
return nil
}
buf := make([]byte, n)

View file

@ -6,8 +6,7 @@
package wire
import (
"fmt"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types"
@ -223,7 +222,7 @@ func decodeInputs(dec *Decoder) []types.TxInput {
in.EtcDetails = decodeRawVariantVector(dec)
vin = append(vin, in)
default:
dec.err = coreerr.E("decodeInputs", fmt.Sprintf("wire: unsupported input tag 0x%02x", tag), nil)
dec.err = coreerr.E("decodeInputs", core.Sprintf("wire: unsupported input tag 0x%02x", tag), nil)
return vin
}
}
@ -261,7 +260,7 @@ func decodeKeyOffsets(dec *Decoder) []types.TxOutRef {
dec.ReadBlob32((*[32]byte)(&refs[i].TxID))
refs[i].N = dec.ReadVarint()
default:
dec.err = coreerr.E("decodeKeyOffsets", fmt.Sprintf("wire: unsupported ref tag 0x%02x", refs[i].Tag), nil)
dec.err = coreerr.E("decodeKeyOffsets", core.Sprintf("wire: unsupported ref tag 0x%02x", refs[i].Tag), nil)
return refs
}
}
@ -340,7 +339,7 @@ func decodeOutputsV1(dec *Decoder) []types.TxOutput {
dec.ReadBlob32((*[32]byte)(&t.PKRefund))
out.Target = t
default:
dec.err = coreerr.E("decodeOutputsV1", fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), nil)
dec.err = coreerr.E("decodeOutputsV1", core.Sprintf("wire: unsupported target tag 0x%02x", tag), nil)
return vout
}
vout = append(vout, out)
@ -427,7 +426,7 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
dec.ReadBlob32((*[32]byte)(&t.PKRefund))
out.Target = t
default:
dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported target tag 0x%02x", targetTag), nil)
dec.err = coreerr.E("decodeOutputsV2", core.Sprintf("wire: unsupported target tag 0x%02x", targetTag), nil)
return vout
}
vout = append(vout, out)
@ -441,7 +440,7 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
out.MixAttr = dec.ReadUint8()
vout = append(vout, out)
default:
dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported output tag 0x%02x", tag), nil)
dec.err = coreerr.E("decodeOutputsV2", core.Sprintf("wire: unsupported output tag 0x%02x", tag), nil)
return vout
}
}
@ -627,7 +626,7 @@ func readVariantElementData(dec *Decoder, tag uint8) []byte {
return dec.ReadBytes(96)
default:
dec.err = coreerr.E("readVariantElementData", fmt.Sprintf("wire: unsupported variant tag 0x%02x (%d)", tag, tag), nil)
dec.err = coreerr.E("readVariantElementData", core.Sprintf("wire: unsupported variant tag 0x%02x (%d)", tag, tag), nil)
return nil
}
}

View file

@ -12,9 +12,7 @@
// to the C++ reference implementation.
package wire
import (
"errors"
)
import "dappco.re/go/core"
// MaxVarintLen is the maximum number of bytes a CryptoNote varint can occupy.
// A uint64 requires at most 10 bytes of 7-bit encoding (64 bits / 7 = ~9.14,
@ -23,11 +21,11 @@ const MaxVarintLen = 10
// ErrVarintOverflow is returned when a varint exceeds the maximum allowed
// length of 10 bytes.
var ErrVarintOverflow = errors.New("wire: varint overflow (exceeds 10 bytes)")
var ErrVarintOverflow = core.E("", "wire: varint overflow (exceeds 10 bytes)", nil)
// ErrVarintEmpty is returned when attempting to decode a varint from an
// empty byte slice.
var ErrVarintEmpty = errors.New("wire: cannot decode varint from empty data")
var ErrVarintEmpty = core.E("", "wire: cannot decode varint from empty data", nil)
// EncodeVarint encodes a uint64 value as a CryptoNote variable-length integer.
//