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

View file

@ -7,9 +7,9 @@ package chain
import ( import (
"context" "context"
"fmt"
"log" "log"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" 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) blockDiff, err := c.NextDifficulty(blockHeight, opts.Forks)
if err != nil { 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, if err := c.processBlockBlobs(entry.Block, entry.Txs,
blockHeight, blockDiff, opts); err != nil { 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 package chain
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/consensus" "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 { for i, gidx := range offsets {
txHash, outNo, err := c.GetOutput(amount, gidx) txHash, outNo, err := c.GetOutput(amount, gidx)
if err != nil { 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) tx, _, err := c.GetTransaction(txHash)
if err != nil { 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) { 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) { switch out := tx.Vout[outNo].(type) {
case types.TxOutputBare: case types.TxOutputBare:
toKey, ok := out.Target.(types.TxOutToKey) toKey, ok := out.Target.(types.TxOutToKey)
if !ok { 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 pubs[i] = toKey.Key
default: 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 return pubs, nil
@ -58,16 +57,16 @@ func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, er
for i, gidx := range offsets { for i, gidx := range offsets {
txHash, outNo, err := c.GetOutput(0, gidx) txHash, outNo, err := c.GetOutput(0, gidx)
if err != nil { 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) tx, _, err := c.GetTransaction(txHash)
if err != nil { 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) { 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) { switch out := tx.Vout[outNo].(type) {
@ -78,7 +77,7 @@ func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, er
BlindedAssetID: [32]byte(out.BlindedAssetID), BlindedAssetID: [32]byte(out.BlindedAssetID),
} }
default: 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 return members, nil

View file

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

View file

@ -9,12 +9,11 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt"
"log" "log"
"regexp" "regexp"
"strconv" "strconv"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "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) blocks, err := client.GetBlocksDetails(localHeight, batch)
if err != nil { 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 { 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 { for _, bd := range blocks {
if err := c.processBlock(bd, opts); err != nil { 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) txBlobBytes, err := hex.DecodeString(txInfo.Blob)
if err != nil { 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) 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) return coreerr.E("Chain.processBlock", "parse daemon block hash", err)
} }
if computedHash != daemonHash { 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) 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) return coreerr.E("Chain.processBlockBlobs", "parse genesis hash", err)
} }
if blockHash != genesisHash { 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)) txDec := wire.NewDecoder(bytes.NewReader(txBlobData))
tx := wire.DecodeTransaction(txDec) tx := wire.DecodeTransaction(txDec)
if err := txDec.Err(); err != nil { 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) txHash := wire.TransactionHash(&tx)
// Validate transaction semantics. // Validate transaction semantics.
if err := consensus.ValidateTransaction(&tx, txBlobData, opts.Forks, height); err != nil { 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. // Optionally verify signatures using the chain's output index.
if opts.VerifySignatures { if opts.VerifySignatures {
if err := consensus.VerifyTransactionSignatures(&tx, opts.Forks, height, c.GetRingOutputs, c.GetZCRingOutputs); err != nil { 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. // Index outputs.
gindexes, err := c.indexOutputs(txHash, &tx) gindexes, err := c.indexOutputs(txHash, &tx)
if err != nil { 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. // Mark key images as spent.
@ -240,11 +239,11 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
switch inp := vin.(type) { switch inp := vin.(type) {
case types.TxInputToKey: case types.TxInputToKey:
if err := c.MarkSpent(inp.KeyImage, height); err != nil { 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: case types.TxInputZC:
if err := c.MarkSpent(inp.KeyImage, height); err != nil { 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, KeeperBlock: height,
GlobalOutputIndexes: gindexes, GlobalOutputIndexes: gindexes,
}); err != nil { }); 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) return coreerr.E("resolveBlockBlobs", "fetch tx blobs", err)
} }
if len(missed) > 0 { 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) { 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. // Index fetched blobs by hash.
@ -363,16 +362,16 @@ func resolveBlockBlobs(blocks []rpc.BlockDetails, client *rpc.Client) error {
// Parse header from object_in_json. // Parse header from object_in_json.
hdr, err := parseBlockHeader(bd.ObjectInJSON) hdr, err := parseBlockHeader(bd.ObjectInJSON)
if err != nil { 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]. // Miner tx blob is transactions_details[0].
if len(bd.Transactions) == 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) minerTxBlob, err := hex.DecodeString(bd.Transactions[0].Blob)
if err != nil { 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. // Collect regular tx hashes.
@ -380,7 +379,7 @@ func resolveBlockBlobs(blocks []rpc.BlockDetails, client *rpc.Client) error {
for _, txInfo := range bd.Transactions[1:] { for _, txInfo := range bd.Transactions[1:] {
h, err := types.HashFromHex(txInfo.ID) h, err := types.HashFromHex(txInfo.ID)
if err != nil { 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) txHashes = append(txHashes, h)
} }
@ -414,8 +413,8 @@ func parseBlockHeader(objectInJSON string) (*types.BlockHeader, error) {
} }
var hj blockHeaderJSON var hj blockHeaderJSON
if err := json.Unmarshal([]byte("{"+m[1]+"}"), &hj); err != nil { if r := core.JSONUnmarshalString(core.Concat("{", m[1], "}"), &hj); !r.OK {
return nil, coreerr.E("parseBlockHeader", "unmarshal AGGREGATED", err) return nil, coreerr.E("parseBlockHeader", "unmarshal AGGREGATED", r.Value.(error))
} }
prevID, err := types.HashFromHex(hj.PrevID) prevID, err := types.HashFromHex(hj.PrevID)

View file

@ -7,8 +7,7 @@ package chain
import ( import (
"bytes" "bytes"
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/config"
@ -26,7 +25,7 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
// Height sequence check. // Height sequence check.
if expectedHeight != currentHeight { 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. // 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) return coreerr.E("Chain.ValidateHeader", "validate: get top block", err)
} }
if b.PrevID != topMeta.Hash { 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. // Block size check.
@ -51,7 +50,7 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
enc := wire.NewEncoder(&buf) enc := wire.NewEncoder(&buf)
wire.EncodeBlock(enc, b) wire.EncodeBlock(enc, b)
if enc.Err() == nil && uint64(buf.Len()) > config.MaxBlockSize { 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 return nil

View file

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

View file

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

View file

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

View file

@ -6,9 +6,9 @@
package consensus package consensus
import ( import (
"fmt"
"slices" "slices"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/config"
@ -30,7 +30,7 @@ func CheckTimestamp(blockTimestamp uint64, flags uint8, adjustedTime uint64, rec
limit = config.PosBlockFutureTimeLimit limit = config.PosBlockFutureTimeLimit
} }
if blockTimestamp > adjustedTime+limit { 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) blockTimestamp, adjustedTime, limit), ErrTimestampFuture)
} }
@ -41,7 +41,7 @@ func CheckTimestamp(blockTimestamp uint64, flags uint8, adjustedTime uint64, rec
median := medianTimestamp(recentTimestamps) median := medianTimestamp(recentTimestamps)
if blockTimestamp < median { if blockTimestamp < median {
return coreerr.E("CheckTimestamp", fmt.Sprintf("%d < median %d", return coreerr.E("CheckTimestamp", core.Sprintf("%d < median %d",
blockTimestamp, median), ErrTimestampOld) 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) return coreerr.E("ValidateMinerTx", "first input is not txin_gen", ErrMinerTxInputs)
} }
if gen.Height != height { 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. // 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. // Post-HF4: accept ZC inputs.
} }
} else { } 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 return nil
@ -121,7 +121,7 @@ func ValidateBlockReward(minerTx *types.Transaction, height, blockSize, medianSi
} }
if outputSum > expected { 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 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 { func checkBlockVersion(blk *types.Block, forks []config.HardFork, height uint64) error {
expected := expectedBlockMajorVersion(forks, height) expected := expectedBlockMajorVersion(forks, height)
if blk.MajorVersion != expected { 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) blk.MajorVersion, expected, height), ErrBlockMajorVersion)
} }
return nil 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 { func ValidateTransactionInBlock(tx *types.Transaction, txBlob []byte, forks []config.HardFork, height uint64) error {
// Pre-hardfork freeze: reject non-coinbase transactions in the freeze window. // Pre-hardfork freeze: reject non-coinbase transactions in the freeze window.
if !isCoinbase(tx) && IsPreHardforkFreeze(forks, config.HF5, height) { 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) return ValidateTransaction(tx, txBlob, forks, height)

View file

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

View file

@ -6,9 +6,9 @@
package consensus package consensus
import ( import (
"fmt"
"math" "math"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types" "dappco.re/go/core/blockchain/types"
@ -33,7 +33,7 @@ func TxFee(tx *types.Transaction) (uint64, error) {
} }
if outputSum > inputSum { 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 return inputSum - outputSum, nil

View file

@ -6,9 +6,9 @@
package consensus package consensus
import ( import (
"fmt"
"math/bits" "math/bits"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/config"
@ -44,7 +44,7 @@ func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) {
} }
if blockSize > 2*effectiveMedian { 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² // penalty = baseReward * (2*median - size) * size / median²

View file

@ -6,8 +6,7 @@
package consensus package consensus
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/config"
@ -26,7 +25,7 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
// 1. Blob size. // 1. Blob size.
if uint64(len(txBlob)) >= config.MaxTransactionBlobSize { 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. // 2. Input count.
@ -34,7 +33,7 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
return ErrNoInputs return ErrNoInputs
} }
if uint64(len(tx.Vin)) > config.TxMaxAllowedInputs { 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) 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 { if hf5Active && tx.Version < types.VersionPostHF5 {
return coreerr.E("checkTxVersion", 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) ErrTxVersionInvalid)
} }
if !hf5Active && tx.Version >= types.VersionPostHF5 { if !hf5Active && tx.Version >= types.VersionPostHF5 {
return coreerr.E("checkTxVersion", 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) ErrTxVersionInvalid)
} }
@ -105,12 +104,12 @@ func checkInputTypes(tx *types.Transaction, hf1Active, hf4Active bool) error {
case types.TxInputHTLC, types.TxInputMultisig: case types.TxInputHTLC, types.TxInputMultisig:
// HTLC and multisig inputs require at least HF1. // HTLC and multisig inputs require at least HF1.
if !hf1Active { 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: default:
// Future types (ZC) — accept if HF4+. // Future types (ZC) — accept if HF4+.
if !hf4Active { 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 { 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 { 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 { for i, vout := range tx.Vout {
switch o := vout.(type) { switch o := vout.(type) {
case types.TxOutputBare: case types.TxOutputBare:
if o.Amount == 0 { 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. // HTLC and Multisig output targets require at least HF1.
switch o.Target.(type) { switch o.Target.(type) {
case types.TxOutHTLC, types.TxOutMultisig: case types.TxOutHTLC, types.TxOutMultisig:
if !hf1Active { 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: case types.TxOutputZarcanum:

View file

@ -7,8 +7,7 @@ package consensus
import ( import (
"bytes" "bytes"
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types" "dappco.re/go/core/blockchain/types"
@ -47,7 +46,7 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
tag := dec.ReadUint8() tag := dec.ReadUint8()
if dec.Err() != nil { 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} entry := v2SigEntry{tag: tag}
@ -56,7 +55,7 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
case types.SigTypeZC: case types.SigTypeZC:
zc, err := parseZCSig(dec) zc, err := parseZCSig(dec)
if err != nil { 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 entry.zcSig = zc
@ -76,11 +75,11 @@ func parseV2Signatures(raw []byte) ([]v2SigEntry, error) {
skipZarcanumSig(dec) skipZarcanumSig(dec)
default: 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 { 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) entries = append(entries, entry)
} }
@ -119,7 +118,7 @@ func parseZCSig(dec *wire.Decoder) (*zcSigData, error) {
} }
if rgCount != rxCount { 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) zc.ringSize = int(rgCount)
@ -205,7 +204,7 @@ func parseV2Proofs(raw []byte) (*v2ProofData, error) {
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
tag := dec.ReadUint8() tag := dec.ReadUint8()
if dec.Err() != nil { 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 { switch tag {
@ -218,7 +217,7 @@ func parseV2Proofs(raw []byte) (*v2ProofData, error) {
for j := uint64(0); j < nBGE; j++ { for j := uint64(0); j < nBGE; j++ {
data.bgeProofs[j] = readBGEProofBytes(dec) data.bgeProofs[j] = readBGEProofBytes(dec)
if dec.Err() != nil { 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: 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 package consensus
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/config"
@ -71,7 +70,7 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
} }
if len(tx.Signatures) != ringInputCount { 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. // 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) ringKeys, err := getRingOutputs(amount, offsets)
if err != nil { 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] ringSigs := tx.Signatures[sigIdx]
if len(ringSigs) != len(ringKeys) { 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. // 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) { 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++ sigIdx++
@ -149,7 +148,7 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
// Match signatures to inputs: each input must have a corresponding signature. // Match signatures to inputs: each input must have a corresponding signature.
if len(sigEntries) != len(tx.Vin) { 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. // Validate that ZC inputs have ZC_sig and vice versa.
@ -157,11 +156,11 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
switch vin.(type) { switch vin.(type) {
case types.TxInputZC: case types.TxInputZC:
if sigEntries[i].tag != types.SigTypeZC { 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: case types.TxInputToKey:
if sigEntries[i].tag != types.SigTypeNLSAG && sigEntries[i].tag != types.SigTypeVoid { 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 zc := sigEntries[i].zcSig
if zc == nil { 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. // Extract absolute global indices from key offsets.
@ -196,11 +195,11 @@ func verifyV2Signatures(tx *types.Transaction, getZCRingOutputs ZCRingOutputsFn)
ringMembers, err := getZCRingOutputs(offsets) ringMembers, err := getZCRingOutputs(offsets)
if err != nil { 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 { 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. // 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), [32]byte(zcIn.KeyImage),
zc.clsagFlatSig, 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) { 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. // 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 { for i, p := range pseudoOutAssetIDs {
full, err := crypto.PointMul8(p) full, err := crypto.PointMul8(p)
if err != nil { 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 mul8PseudoOuts[i] = full
} }
@ -300,7 +299,7 @@ func verifyBGEProofs(tx *types.Transaction, sigEntries []v2SigEntry,
// mul8 the output's blinded asset ID. // mul8 the output's blinded asset ID.
mul8Out, err := crypto.PointMul8(outAssetID) mul8Out, err := crypto.PointMul8(outAssetID)
if err != nil { 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) // 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 { for i, mul8Pseudo := range mul8PseudoOuts {
diff, err := crypto.PointSub(mul8Pseudo, mul8Out) diff, err := crypto.PointSub(mul8Pseudo, mul8Out)
if err != nil { 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 ring[i] = diff
} }
if !crypto.VerifyBGE(context, ring, proofs.bgeProofs[j]) { 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 "C"
import ( import (
"fmt"
"unsafe" "unsafe"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" 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])), (*C.uint8_t)(unsafe.Pointer(&sec[0])),
) )
if rc != 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 return
} }
@ -34,7 +34,7 @@ func SecretToPublic(sec [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&pub[0])), (*C.uint8_t)(unsafe.Pointer(&pub[0])),
) )
if rc != 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 return pub, nil
} }

View file

@ -8,9 +8,9 @@ package crypto
// #include "bridge.h" // #include "bridge.h"
import "C" import "C"
import ( import (
"fmt"
"unsafe" "unsafe"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" 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])), (*C.uint8_t)(unsafe.Pointer(&output[0])),
) )
if ret != 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 return output, nil
} }

3
go.mod
View file

@ -3,6 +3,7 @@ module dappco.re/go/core/blockchain
go 1.26.0 go 1.26.0
require ( require (
dappco.re/go/core v0.8.0-alpha.1
dappco.re/go/core/cli v0.3.1 dappco.re/go/core/cli v0.3.1
dappco.re/go/core/io v0.2.0 dappco.re/go/core/io v0.2.0
dappco.re/go/core/log v0.1.0 dappco.re/go/core/log v0.1.0
@ -63,7 +64,7 @@ require (
) )
replace ( 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/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/crypt => forge.lthn.ai/core/go-crypt v0.1.7
dappco.re/go/core/i18n => forge.lthn.ai/core/go-i18n v0.1.4 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/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 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM=
forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc= 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 h1:jB7L/28S1NR+91u3GcOYuKfBLzPhhBUY1fRe6WkGVns=
forge.lthn.ai/core/go-crypt v0.1.6/go.mod h1:4VZAGqxlbadhSB66sJkdj54/HSJ+bSxVgwWK5kMMYDo= 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= forge.lthn.ai/core/go-i18n v0.1.4 h1:zOHUUJDgRo88/3tj++kN+VELg/buyZ4T2OSdG3HBbLQ=

View file

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

View file

@ -6,8 +6,7 @@
package rpc package rpc
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
) )
@ -21,7 +20,7 @@ func (c *Client) GetLastBlockHeader() (*BlockHeader, error) {
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp.BlockHeader, nil
} }
@ -39,7 +38,7 @@ func (c *Client) GetBlockHeaderByHeight(height uint64) (*BlockHeader, error) {
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp.BlockHeader, nil
} }
@ -57,7 +56,7 @@ func (c *Client) GetBlockHeaderByHash(hash string) (*BlockHeader, error) {
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp.BlockHeader, nil
} }
@ -77,7 +76,7 @@ func (c *Client) GetBlocksDetails(heightStart, count uint64) ([]BlockDetails, er
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return resp.Blocks, nil
} }

View file

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

View file

@ -8,13 +8,12 @@ package rpc
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
) )
@ -38,7 +37,7 @@ func NewClientWithHTTP(daemonURL string, httpClient *http.Client) *Client {
// Fall through with raw URL. // Fall through with raw URL.
return &Client{url: daemonURL + "/json_rpc", baseURL: daemonURL, httpClient: httpClient} 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 == "/" { if u.Path == "" || u.Path == "/" {
u.Path = "/json_rpc" u.Path = "/json_rpc"
} }
@ -56,7 +55,7 @@ type RPCError struct {
} }
func (e *RPCError) Error() string { 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. // JSON-RPC 2.0 envelope types.
@ -68,10 +67,10 @@ type jsonRPCRequest struct {
} }
type jsonRPCResponse struct { type jsonRPCResponse struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"` ID rawJSON `json:"id"`
Result json.RawMessage `json:"result"` Result rawJSON `json:"result"`
Error *jsonRPCError `json:"error,omitempty"` Error *jsonRPCError `json:"error,omitempty"`
} }
type jsonRPCError struct { type jsonRPCError struct {
@ -79,26 +78,37 @@ type jsonRPCError struct {
Message string `json:"message"` 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. // call makes a JSON-RPC 2.0 call to /json_rpc.
func (c *Client) call(method string, params any, result any) error { func (c *Client) call(method string, params any, result any) error {
reqBody, err := json.Marshal(jsonRPCRequest{ reqBody := core.JSONMarshalString(jsonRPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: "0", ID: "0",
Method: method, Method: method,
Params: params, 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 { 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() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { 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) body, err := io.ReadAll(resp.Body)
@ -107,8 +117,8 @@ func (c *Client) call(method string, params any, result any) error {
} }
var rpcResp jsonRPCResponse var rpcResp jsonRPCResponse
if err := json.Unmarshal(body, &rpcResp); err != nil { if r := core.JSONUnmarshalString(string(body), &rpcResp); !r.OK {
return coreerr.E("Client.call", "unmarshal response", err) return coreerr.E("Client.call", "unmarshal response", r.Value.(error))
} }
if rpcResp.Error != nil { 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 result != nil && len(rpcResp.Result) > 0 {
if err := json.Unmarshal(rpcResp.Result, result); err != nil { if r := core.JSONUnmarshalString(string(rpcResp.Result), result); !r.OK {
return coreerr.E("Client.call", "unmarshal result", err) return coreerr.E("Client.call", "unmarshal result", r.Value.(error))
} }
} }
return nil 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). // 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 { func (c *Client) legacyCall(path string, params any, result any) error {
reqBody, err := json.Marshal(params) reqBody := core.JSONMarshalString(params)
if err != nil {
return coreerr.E("Client.legacyCall", "marshal request", err)
}
url := c.baseURL + path 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 { 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() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { 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) 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 result != nil {
if err := json.Unmarshal(body, result); err != nil { if r := core.JSONUnmarshalString(string(body), result); !r.OK {
return coreerr.E("Client.legacyCall", "unmarshal response", err) return coreerr.E("Client.legacyCall", "unmarshal response", r.Value.(error))
} }
} }
return nil return nil

View file

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

View file

@ -6,8 +6,7 @@
package rpc package rpc
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
) )
@ -25,7 +24,7 @@ func (c *Client) GetInfo() (*DaemonInfo, error) {
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp.DaemonInfo, nil
} }
@ -41,7 +40,7 @@ func (c *Client) GetHeight() (uint64, error) {
return 0, err return 0, err
} }
if resp.Status != "OK" { 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 return resp.Height, nil
} }
@ -56,7 +55,7 @@ func (c *Client) GetBlockCount() (uint64, error) {
return 0, err return 0, err
} }
if resp.Status != "OK" { 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 return resp.Count, nil
} }

View file

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

View file

@ -6,8 +6,7 @@
package rpc package rpc
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
) )
@ -24,7 +23,7 @@ func (c *Client) SubmitBlock(hexBlob string) error {
return err return err
} }
if resp.Status != "OK" { 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 return nil
} }
@ -51,7 +50,7 @@ func (c *Client) GetBlockTemplate(walletAddr string) (*BlockTemplateResponse, er
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp, nil
} }

View file

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

View file

@ -6,8 +6,7 @@
package rpc package rpc
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
) )
@ -24,7 +23,7 @@ func (c *Client) GetTxDetails(txHash string) (*TxInfo, error) {
return nil, err return nil, err
} }
if resp.Status != "OK" { 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 return &resp.TxInfo, nil
} }
@ -45,7 +44,7 @@ func (c *Client) GetTransactions(hashes []string) (txsHex []string, missed []str
return nil, nil, err return nil, nil, err
} }
if resp.Status != "OK" { 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 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") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{ json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: json.RawMessage(`"0"`), ID: rawJSON(`"0"`),
Result: json.RawMessage(`{ Result: rawJSON(`{
"status": "OK", "status": "OK",
"tx_info": { "tx_info": {
"id": "a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8", "id": "a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8",
@ -55,7 +55,7 @@ func TestGetTxDetails_Bad_NotFound(t *testing.T) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(jsonRPCResponse{ json.NewEncoder(w).Encode(jsonRPCResponse{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: json.RawMessage(`"0"`), ID: rawJSON(`"0"`),
Error: &jsonRPCError{Code: -14, Message: "NOT_FOUND"}, Error: &jsonRPCError{Code: -14, Message: "NOT_FOUND"},
}) })
})) }))

View file

@ -6,10 +6,9 @@
package rpc package rpc
import ( import (
"encoding/hex" "dappco.re/go/core"
"fmt"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"encoding/hex"
) )
// RandomOutputEntry is a decoy output returned by getrandom_outs. // 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 return nil, err
} }
if resp.Status != "OK" { 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 return resp.Outs, nil
} }
@ -55,7 +54,7 @@ func (c *Client) SendRawTransaction(txBlob []byte) error {
return err return err
} }
if resp.Status != "OK" { 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 return nil
} }

View file

@ -9,11 +9,11 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"fmt"
"log" "log"
"net" "net"
"time" "time"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/chain" "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 { 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) conn, err := net.DialTimeout("tcp", seed, 10*time.Second)
if err != nil { 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() 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) return coreerr.E("syncOnce", "read handshake", err)
} }
if hdr.Command != uint32(p2p.CommandHandshake) { 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 var resp p2p.HandshakeResponse

View file

@ -6,10 +6,9 @@
package tui package tui
import ( import (
"fmt"
"strings"
"time" "time"
"dappco.re/go/core"
cli "dappco.re/go/core/cli/pkg/cli" cli "dappco.re/go/core/cli/pkg/cli"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
@ -215,10 +214,10 @@ func (m *ExplorerModel) viewBlockList() string {
return " no blocks \u2014 chain is empty" return " no blocks \u2014 chain is empty"
} }
var b strings.Builder b := core.NewBuilder()
// Header row. // Header row.
header := fmt.Sprintf(" %-8s %-18s %5s %12s %12s", header := core.Sprintf(" %-8s %-18s %5s %12s %12s",
"Height", "Hash", "Txs", "Difficulty", "Age") "Height", "Hash", "Txs", "Difficulty", "Age")
b.WriteString(header) b.WriteString(header)
b.WriteByte('\n') b.WriteByte('\n')
@ -240,10 +239,10 @@ func (m *ExplorerModel) viewBlockList() string {
prefix = "> " 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)) 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, prefix, row.Height, hashShort, row.TxCount,
formatDifficulty(row.Difficulty), age) formatDifficulty(row.Difficulty), age)
@ -264,17 +263,17 @@ func (m *ExplorerModel) viewBlockDetail() string {
return " no block selected" return " no block selected"
} }
var b strings.Builder b := core.NewBuilder()
meta := m.blockMeta meta := m.blockMeta
blk := m.block blk := m.block
b.WriteString(fmt.Sprintf(" Block %d\n", meta.Height)) b.WriteString(core.Sprintf(" Block %d\n", meta.Height))
b.WriteString(fmt.Sprintf(" Hash: %x\n", meta.Hash)) b.WriteString(core.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(core.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(core.Sprintf(" Difficulty: %s\n", formatDifficulty(meta.Difficulty)))
b.WriteString(fmt.Sprintf(" Version: %d.%d\n", blk.MajorVersion, blk.MinorVersion)) b.WriteString(core.Sprintf(" Version: %d.%d\n", blk.MajorVersion, blk.MinorVersion))
b.WriteString(fmt.Sprintf(" Nonce: %d\n", blk.Nonce)) b.WriteString(core.Sprintf(" Nonce: %d\n", blk.Nonce))
b.WriteString(fmt.Sprintf(" Txs: %d\n\n", len(blk.TxHashes))) b.WriteString(core.Sprintf(" Txs: %d\n\n", len(blk.TxHashes)))
if len(blk.TxHashes) == 0 { if len(blk.TxHashes) == 0 {
b.WriteString(" (coinbase only)") b.WriteString(" (coinbase only)")
@ -285,7 +284,7 @@ func (m *ExplorerModel) viewBlockDetail() string {
if i == m.txCursor { if i == m.txCursor {
prefix = "> " 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" return " no transaction selected"
} }
var b strings.Builder b := core.NewBuilder()
tx := m.tx tx := m.tx
b.WriteString(" Transaction\n") b.WriteString(" Transaction\n")
b.WriteString(fmt.Sprintf(" Hash: %x\n", m.txHash)) b.WriteString(core.Sprintf(" Hash: %x\n", m.txHash))
b.WriteString(fmt.Sprintf(" Version: %d\n", tx.Version)) b.WriteString(core.Sprintf(" Version: %d\n", tx.Version))
b.WriteString(fmt.Sprintf(" Inputs: %d\n", len(tx.Vin))) b.WriteString(core.Sprintf(" Inputs: %d\n", len(tx.Vin)))
b.WriteString(fmt.Sprintf(" Outputs: %d\n\n", len(tx.Vout))) b.WriteString(core.Sprintf(" Outputs: %d\n\n", len(tx.Vout)))
if len(tx.Vin) > 0 { if len(tx.Vin) > 0 {
b.WriteString(" Inputs:\n") b.WriteString(" Inputs:\n")
for i, in := range tx.Vin { for i, in := range tx.Vin {
switch v := in.(type) { switch v := in.(type) {
case types.TxInputGenesis: 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: 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: 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) { switch v := out.(type) {
case types.TxOutputBare: case types.TxOutputBare:
if toKey, ok := v.Target.(types.TxOutToKey); ok { 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 { } 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: 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: 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 package tui
import ( import (
"strings" "dappco.re/go/core"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
cli "dappco.re/go/core/cli/pkg/cli" 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. // View renders a single-line hint bar separated by vertical bars.
// The output is truncated to width if it would overflow. // The output is truncated to width if it would overflow.
func (m *KeyHintsModel) View(width, height int) string { 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 { if len(line) > width && width > 0 {
line = line[:width] line = line[:width]
} }

View file

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

View file

@ -10,9 +10,9 @@
package types package types
import ( import (
"fmt"
"math/big" "math/big"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"golang.org/x/crypto/sha3" "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. // After the prefix we need exactly 32+32+1+4 = 69 bytes.
remaining := raw[prefixLen:] remaining := raw[prefixLen:]
if len(remaining) != 69 { 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. // 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. // Validate that the last block size maps to a valid byte count.
if lastBlockChars > 0 && base58ReverseBlockSizes[lastBlockChars] < 0 { 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 var result []byte
@ -258,7 +258,7 @@ func decodeBlock(s string, byteCount int) ([]byte, error) {
for _, c := range []byte(s) { for _, c := range []byte(s) {
idx := base58CharIndex(c) idx := base58CharIndex(c)
if idx < 0 { 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.Mul(num, base)
num.Add(num, big.NewInt(int64(idx))) 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. // Convert to fixed-size byte array, big-endian.
raw := num.Bytes() raw := num.Bytes()
if len(raw) > byteCount { 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. // Pad with leading zeroes if necessary.

View file

@ -13,10 +13,9 @@
package types package types
import ( import (
"encoding/hex" "dappco.re/go/core"
"fmt"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"encoding/hex"
) )
// Hash is a 256-bit (32-byte) hash value, typically produced by Keccak-256. // 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) return h, coreerr.E("HashFromHex", "types: invalid hex for hash", err)
} }
if len(b) != 32 { 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) copy(h[:], b)
return h, nil 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) return pk, coreerr.E("PublicKeyFromHex", "types: invalid hex for public key", err)
} }
if len(b) != 32 { 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) copy(pk[:], b)
return pk, nil return pk, nil

View file

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

View file

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

View file

@ -10,10 +10,9 @@
package wallet package wallet
import ( import (
"encoding/binary" "dappco.re/go/core"
"fmt"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"encoding/binary"
"dappco.re/go/core/blockchain/types" "dappco.re/go/core/blockchain/types"
"dappco.re/go/core/blockchain/wire" "dappco.re/go/core/blockchain/wire"
@ -59,7 +58,7 @@ func ParseTxExtra(raw []byte) (*TxExtra, error) {
switch tag { switch tag {
case extraTagPublicKey: case extraTagPublicKey:
if pos+32 > len(raw) { 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]) copy(extra.TxPublicKey[:], raw[pos:pos+32])
pos += 32 pos += 32
@ -112,11 +111,11 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
// String types: varint(length) + length bytes. // String types: varint(length) + length bytes.
case 7, 9, 11, 19: case 7, 9, 11, 19:
if len(data) == 0 { 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) length, n, err := wire.DecodeVarint(data)
if err != nil { 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 return n + int(length), nil
@ -124,7 +123,7 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
case 14, 15, 16, 26, 27: case 14, 15, 16, 26, 27:
_, n, err := wire.DecodeVarint(data) _, n, err := wire.DecodeVarint(data)
if err != nil { 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 return n, nil
@ -141,6 +140,6 @@ func skipExtraElement(data []byte, tag uint8) (int, error) {
return 64, nil // signature return 64, nil // signature
default: 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 ( import (
"encoding/binary" "encoding/binary"
"fmt"
"hash/crc32" "hash/crc32"
"strings"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log" 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. // MnemonicEncode converts a 32-byte secret key to a 25-word mnemonic phrase.
func MnemonicEncode(key []byte) (string, error) { func MnemonicEncode(key []byte) (string, error) {
if len(key) != 32 { 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) words := make([]string, 0, 25)
@ -31,16 +30,16 @@ func MnemonicEncode(key []byte) (string, error) {
checkIdx := checksumIndex(words) checkIdx := checksumIndex(words)
words = append(words, words[checkIdx]) 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. // MnemonicDecode converts a 25-word mnemonic phrase to a 32-byte secret key.
func MnemonicDecode(phrase string) ([32]byte, error) { func MnemonicDecode(phrase string) ([32]byte, error) {
var key [32]byte var key [32]byte
words := strings.Fields(phrase) words := mnemonicWords(phrase)
if len(words) != 25 { 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]) expected := checksumIndex(words[:24])
@ -62,7 +61,7 @@ func MnemonicDecode(phrase string) ([32]byte, error) {
if !ok3 { if !ok3 {
word = words[i*3+2] 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) + val := uint32(w1) +
@ -74,6 +73,21 @@ func MnemonicDecode(phrase string) ([32]byte, error) {
return key, nil 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 { func checksumIndex(words []string) int {
var prefixes string var prefixes string
for _, w := range words { for _, w := range words {

View file

@ -10,8 +10,7 @@
package wallet package wallet
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/rpc" "dappco.re/go/core/blockchain/rpc"
@ -70,7 +69,7 @@ func (s *RPCRingSelector) SelectRing(amount uint64, realGlobalIndex uint64, ring
} }
if len(members) < ringSize { 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 return members, nil
} }

View file

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

View file

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

View file

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

View file

@ -6,8 +6,7 @@
package wire package wire
import ( import (
"fmt" "dappco.re/go/core"
coreerr "dappco.re/go/core/log" coreerr "dappco.re/go/core/log"
"dappco.re/go/core/blockchain/types" "dappco.re/go/core/blockchain/types"
@ -223,7 +222,7 @@ func decodeInputs(dec *Decoder) []types.TxInput {
in.EtcDetails = decodeRawVariantVector(dec) in.EtcDetails = decodeRawVariantVector(dec)
vin = append(vin, in) vin = append(vin, in)
default: 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 return vin
} }
} }
@ -261,7 +260,7 @@ func decodeKeyOffsets(dec *Decoder) []types.TxOutRef {
dec.ReadBlob32((*[32]byte)(&refs[i].TxID)) dec.ReadBlob32((*[32]byte)(&refs[i].TxID))
refs[i].N = dec.ReadVarint() refs[i].N = dec.ReadVarint()
default: 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 return refs
} }
} }
@ -340,7 +339,7 @@ func decodeOutputsV1(dec *Decoder) []types.TxOutput {
dec.ReadBlob32((*[32]byte)(&t.PKRefund)) dec.ReadBlob32((*[32]byte)(&t.PKRefund))
out.Target = t out.Target = t
default: 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 return vout
} }
vout = append(vout, out) vout = append(vout, out)
@ -427,7 +426,7 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
dec.ReadBlob32((*[32]byte)(&t.PKRefund)) dec.ReadBlob32((*[32]byte)(&t.PKRefund))
out.Target = t out.Target = t
default: 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 return vout
} }
vout = append(vout, out) vout = append(vout, out)
@ -441,7 +440,7 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
out.MixAttr = dec.ReadUint8() out.MixAttr = dec.ReadUint8()
vout = append(vout, out) vout = append(vout, out)
default: 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 return vout
} }
} }
@ -627,7 +626,7 @@ func readVariantElementData(dec *Decoder, tag uint8) []byte {
return dec.ReadBytes(96) return dec.ReadBytes(96)
default: 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 return nil
} }
} }

View file

@ -12,9 +12,7 @@
// to the C++ reference implementation. // to the C++ reference implementation.
package wire package wire
import ( import "dappco.re/go/core"
"errors"
)
// MaxVarintLen is the maximum number of bytes a CryptoNote varint can occupy. // 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, // 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 // ErrVarintOverflow is returned when a varint exceeds the maximum allowed
// length of 10 bytes. // 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 // ErrVarintEmpty is returned when attempting to decode a varint from an
// empty byte slice. // 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. // EncodeVarint encodes a uint64 value as a CryptoNote variable-length integer.
// //