mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
feat(dexkeeper): off-chain keeper driving the 0x9999 two-phase atomic DEX seam
cmd/dexkeeper watches the C-Chain for IntentSubmitted (Phase-A intent: a maker swap to 0x9999 with DI01 hookData), submits the ImportTx (consume the staged C->D atomic object, fund the D order) + the settling RelayOrderTx (clob_submit, collateral-bound) to the dexvm dex.submitTx RPC, then drives the Phase-B settlement swap (DS01 hookData) that consumes the D->C proceeds object and emits DEXFill. The keeper holds NO custody authority: value is locked in shared memory by C before it acts, and the D->C object pays its recorded owner regardless. - maker.go: BIP44 m/44'/9000'/0'/0/i maker-key derivation, pinned byte-for-byte against genesis.LoadBIP44WalletKeysFromMnemonic. Uses the STANDARD EVM address (keccak256(X||Y)[12:]) everywhere — NOT secp256k1.PrivateKey.EVMAddress(), which hashes the compressed pubkey and yields a non-standard address. - calldata.go: Phase-A/B 0x9999 calldata via zap/dexsession, intent-id derivation mirroring precompile/dex DeriveIntentID, IntentSubmitted + DEXFill decoders (fail-closed at the boundary). - venue.go: ZAP maker-liquidity seeding, modeled on cmd/fourpath-live. - keeper.go: watch loop + import/relay orchestration. Settling relay is sent UNSIGNED with From = escrow owner (the architecturally-correct build). - Modes: seed | phase-a | watch. Maker key from LUX_MNEMONIC. Unit tests (10, all passing) cover the pure logic: maker derivation vs the genesis authority + a known vector, Phase-A/B calldata byte-identity + DI01/DS01 shape, intent-id determinism/injectivity, and synthetic event decode (+ negative cases). Builds CGO_ENABLED=0 for linux/amd64 (static ELF). Known gap (documented stub): getExportedOutput — the D->C export discovery needs a dexvm settlement-coordinate query (dex.getSettlement) or a C-Chain importable- UTXO view; neither is exposed today. Phase-A path is fully wired.
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
// calldata.go holds the PURE wire logic the keeper drives the C<->D seam with:
|
||||
//
|
||||
// - Phase-A / Phase-B 0x9999 swap calldata, built via github.com/luxfi/zap/dexsession
|
||||
// (the canonical reproduction of the precompile's calldata format).
|
||||
// - The intent-id derivation, mirroring precompile/dex DeriveIntentID. The keeper
|
||||
// CHOOSES the nonce when it signs Phase-A, so it derives the id locally; it also
|
||||
// reads the id back from the IntentSubmitted topic to correlate (authoritative).
|
||||
// - Decoders for the two 0x9999 events the keeper observes: IntentSubmitted (the
|
||||
// routing event it acts on) and DEXFill (the terminal settled-fill signal).
|
||||
//
|
||||
// Everything here is a deterministic function of values — no I/O, no chain handle — so
|
||||
// it is unit-testable in isolation (calldata_test.go) and the encode side is asserted
|
||||
// byte-identical to the precompile/dexsession authorities.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/luxfi/crypto"
|
||||
"github.com/luxfi/dex/pkg/zapwire"
|
||||
"github.com/luxfi/geth/common"
|
||||
ethtypes "github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap/dexsession"
|
||||
)
|
||||
|
||||
// poolManagerAddr9999 is the canonical V4-shaped settlement precompile (the money path).
|
||||
// Phase-A intents and Phase-B settlements are both swap calls to this address; the
|
||||
// IntentSubmitted / DEXFill events are emitted from it.
|
||||
var poolManagerAddr9999 = common.HexToAddress("0x0000000000000000000000000000000000009999")
|
||||
|
||||
// nativeIntentDomain scopes the intent-id derivation. IDENTICAL to precompile/dex
|
||||
// nativeIntentDomain — a one-sided change here would make the keeper's locally-derived
|
||||
// id disagree with the chain's (the watch-correlation contract); calldata_test pins it.
|
||||
const nativeIntentDomain = "lux.dex.native.intent.v1"
|
||||
|
||||
// IntentSubmitted(bytes32,bytes32,address,bytes32,uint256,bytes32,uint256,address,uint64,uint8,uint64,uint8)
|
||||
// — the keeper-routing event. topics[1]=intentID, topics[2]=dChainID. Data is 10 ABI
|
||||
// words: account,assetIn,locked,marketID,minAmountOut,recipient,deadline,kind,priceLimit,
|
||||
// limitIsUpper (see precompile/dex native_events.go emitNativeRoutingEvent).
|
||||
var intentSubmittedSig = common.BytesToHash(crypto.Keccak256([]byte(
|
||||
"IntentSubmitted(bytes32,bytes32,address,bytes32,uint256,bytes32,uint256,address,uint64,uint8,uint64,uint8)")))
|
||||
|
||||
// DEXFill(bytes32,address,uint256,uint256) — the terminal settled-fill signal.
|
||||
// topics[1]=poolId, topics[2]=taker. Data is 2 ABI words: amountOut, blockNumber.
|
||||
var dexFillSig = common.BytesToHash(crypto.Keccak256([]byte("DEXFill(bytes32,address,uint256,uint256)")))
|
||||
|
||||
// deriveIntentID re-derives the C->D intent id over the FULL identity, mirroring
|
||||
// precompile/dex DeriveIntentID:
|
||||
//
|
||||
// SHA-256( domain | networkID(4 BE) | cChainID | dChainID |
|
||||
// account | assetIn | amountIn(8 BE) | marketID | nonce(8 BE) )
|
||||
//
|
||||
// The keeper picks the nonce, so it can derive the id BEFORE the Phase-A tx lands. It is
|
||||
// only used to sanity-check the id it reads from the event topic (authoritative).
|
||||
func deriveIntentID(networkID uint32, cChainID, dChainID ids.ID, account common.Address, assetIn [32]byte, amountIn uint64, marketID [32]byte, nonce uint64) ids.ID {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(nativeIntentDomain))
|
||||
var u4 [4]byte
|
||||
binary.BigEndian.PutUint32(u4[:], networkID)
|
||||
h.Write(u4[:])
|
||||
h.Write(cChainID[:])
|
||||
h.Write(dChainID[:])
|
||||
h.Write(account[:])
|
||||
h.Write(assetIn[:])
|
||||
var u8 [8]byte
|
||||
binary.BigEndian.PutUint64(u8[:], amountIn)
|
||||
h.Write(u8[:])
|
||||
h.Write(marketID[:])
|
||||
binary.BigEndian.PutUint64(u8[:], nonce)
|
||||
h.Write(u8[:])
|
||||
var out [32]byte
|
||||
copy(out[:], h.Sum(nil))
|
||||
return ids.ID(out)
|
||||
}
|
||||
|
||||
// phaseAIntentCalldata builds the full 0x9999 Phase-A (DI01) swap calldata: a V4 swap
|
||||
// with an explicit intent hookData carrying (deadline, nonce). The maker signs and sends
|
||||
// this to 0x9999; SubmitSwapIntent locks the input and stages the C->D object.
|
||||
func phaseAIntentCalldata(pk dexsession.PoolKeyArgs, zeroForOne bool, amountIn, deadline, nonce uint64) []byte {
|
||||
return dexsession.EncodeSwapCalldata(pk, zeroForOne, amountIn, dexsession.EncodeIntentHookData(deadline, nonce))
|
||||
}
|
||||
|
||||
// phaseBSettleCalldata builds the full 0x9999 Phase-B (DS01) swap calldata: a V4 swap
|
||||
// with a settlement hookData carrying (outputID, amount, intentID). The maker signs and
|
||||
// sends this; ImportSettlement consumes the D->C proceeds object and emits DEXFill. The
|
||||
// amount/zeroForOne mirror Phase-A so the swap ABI is well-formed; only the hookData
|
||||
// (the phase selector) differs.
|
||||
func phaseBSettleCalldata(pk dexsession.PoolKeyArgs, zeroForOne bool, amountIn uint64, outputID, intentID ids.ID, amount uint64) []byte {
|
||||
body := dexsession.EncodeSettlementHookData(dexsession.ID(outputID), amount, dexsession.ID(intentID))
|
||||
return dexsession.EncodeSwapCalldata(pk, zeroForOne, amountIn, body)
|
||||
}
|
||||
|
||||
// intentEvent is the decoded IntentSubmitted routing payload the keeper acts on.
|
||||
type intentEvent struct {
|
||||
IntentID ids.ID // topics[1]
|
||||
DChainID ids.ID // topics[2]
|
||||
Account common.Address
|
||||
AssetIn [32]byte
|
||||
AmountIn uint64 // the locked principal (observed delta), low 64 bits of the word
|
||||
MarketID [32]byte
|
||||
MinAmountOut *big.Int
|
||||
Recipient common.Address
|
||||
Deadline uint64
|
||||
Kind uint8 // 0 swap, 1 position
|
||||
PriceLimit uint64
|
||||
LimitIsUpper bool
|
||||
}
|
||||
|
||||
// decodeIntentEvent decodes an IntentSubmitted log. It validates the topic[0] signature
|
||||
// and the 10-word (320-byte) data layout at the boundary (untrusted chain input), then
|
||||
// extracts the routing fields. Returns an error on any shape mismatch so a malformed or
|
||||
// foreign log is never acted on.
|
||||
func decodeIntentEvent(log ethtypes.Log) (intentEvent, error) {
|
||||
var e intentEvent
|
||||
if len(log.Topics) != 3 {
|
||||
return e, fmt.Errorf("IntentSubmitted: want 3 topics, got %d", len(log.Topics))
|
||||
}
|
||||
if log.Topics[0] != intentSubmittedSig {
|
||||
return e, fmt.Errorf("IntentSubmitted: topic0 %s != sig %s", log.Topics[0], intentSubmittedSig)
|
||||
}
|
||||
const wantWords = 10
|
||||
if len(log.Data) != wantWords*32 {
|
||||
return e, fmt.Errorf("IntentSubmitted: want %d data bytes, got %d", wantWords*32, len(log.Data))
|
||||
}
|
||||
e.IntentID = ids.ID(log.Topics[1])
|
||||
e.DChainID = ids.ID(log.Topics[2])
|
||||
word := func(i int) []byte { return log.Data[i*32 : i*32+32] }
|
||||
e.Account = common.BytesToAddress(word(0))
|
||||
copy(e.AssetIn[:], word(1))
|
||||
e.AmountIn = beUint64Low(word(2))
|
||||
copy(e.MarketID[:], word(3))
|
||||
e.MinAmountOut = new(big.Int).SetBytes(word(4))
|
||||
e.Recipient = common.BytesToAddress(word(5))
|
||||
e.Deadline = beUint64Low(word(6))
|
||||
e.Kind = word(7)[31]
|
||||
e.PriceLimit = beUint64Low(word(8))
|
||||
e.LimitIsUpper = word(9)[31] != 0
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// dexFillEvent is the decoded DEXFill terminal signal.
|
||||
type dexFillEvent struct {
|
||||
PoolID [32]byte // topics[1]
|
||||
Taker common.Address
|
||||
AmountOut *big.Int
|
||||
BlockNumber uint64
|
||||
}
|
||||
|
||||
// decodeDEXFillEvent decodes a DEXFill log (the proof the C<->D round-trip settled).
|
||||
func decodeDEXFillEvent(log ethtypes.Log) (dexFillEvent, error) {
|
||||
var e dexFillEvent
|
||||
if len(log.Topics) != 3 {
|
||||
return e, fmt.Errorf("DEXFill: want 3 topics, got %d", len(log.Topics))
|
||||
}
|
||||
if log.Topics[0] != dexFillSig {
|
||||
return e, fmt.Errorf("DEXFill: topic0 %s != sig %s", log.Topics[0], dexFillSig)
|
||||
}
|
||||
if len(log.Data) != 2*32 {
|
||||
return e, fmt.Errorf("DEXFill: want 64 data bytes, got %d", len(log.Data))
|
||||
}
|
||||
copy(e.PoolID[:], log.Topics[1].Bytes())
|
||||
e.Taker = common.BytesToAddress(log.Topics[2].Bytes())
|
||||
e.AmountOut = new(big.Int).SetBytes(log.Data[0:32])
|
||||
e.BlockNumber = beUint64Low(log.Data[32:64])
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// priceLimitFloat converts the V4-style price-limit bits the precompile carried in the
|
||||
// event (a CLOB quote-per-base limit as float64 bits) into the float64 limit price the
|
||||
// settling clob_submit frame needs. The precompile already did the SqrtPriceLimitX96 ->
|
||||
// CLOB conversion (priceLimitToCLOB on the C side) and emitted the result, so the keeper
|
||||
// carries it verbatim. Zero bits == 0.0 == "no limit" (a marketable order takes any price).
|
||||
func priceLimitFloat(bits uint64) float64 {
|
||||
return math.Float64frombits(bits)
|
||||
}
|
||||
|
||||
// beUint64Low reads the low 8 bytes of a 32-byte big-endian ABI word as a uint64. The
|
||||
// precompile right-aligns uint64/uint8 values in their words (PutUint64(w[24:32], v)), so
|
||||
// the value lives in the last 8 bytes.
|
||||
func beUint64Low(word []byte) uint64 {
|
||||
return binary.BigEndian.Uint64(word[24:32])
|
||||
}
|
||||
|
||||
// submitFrameFor builds the settling taker clob_submit frame the RelayOrderTx carries:
|
||||
// a marketable order on poolID for `size` at the taker's worst-acceptable `limitPrice`,
|
||||
// keyed to the maker user handle. side/limitPrice come from the intent direction +
|
||||
// carried price limit. Byte-identical to zapwire.EncodeSubmit (the venue wire).
|
||||
func submitFrameFor(poolID [32]byte, side uint8, isMarket bool, limitPrice, size float64, user string) []byte {
|
||||
return zapwire.EncodeSubmit(poolID, side, isMarket, limitPrice, size, user)
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
// keeper.go is the off-chain KEEPER that drives the on-chain 0x9999 two-phase atomic
|
||||
// DEX seam so a DEXFill lands on the C-Chain. It is a relayer with NO custody authority:
|
||||
// the value is locked in shared memory by the C-side SubmitSwapIntent before the keeper
|
||||
// ever sees it, and the D->C proceeds object pays its RECORDED owner regardless of what
|
||||
// the keeper submits. The worst a faulty/byzantine keeper can do is fail to build a
|
||||
// settlement (which the locker reclaims directly after the intent deadline). See
|
||||
// precompile/dex native_events.go and chains/dexvm txs/tx.go for the authority model.
|
||||
//
|
||||
// The seam, end to end:
|
||||
//
|
||||
// 1. Phase-A intent: the maker signs a V4 swap tx to 0x9999 with DI01 hookData (a
|
||||
// chosen nonce + deadline). SubmitSwapIntent locks the input, stages a C->D atomic
|
||||
// object keyed by intentID, and emits IntentSubmitted.
|
||||
// 2. The keeper reads IntentSubmitted (FilterLogs), then submits to the dexvm:
|
||||
// - an ImportTx that consumes the C->D object and funds the D order, and
|
||||
// - a settling RelayOrderTx (clob_submit) bound to that import's collateral.
|
||||
// Both go to the dexvm's dex.submitTx RPC; the proposer relays the order once and
|
||||
// settleFromFills exports a D->C railSwap proceeds object.
|
||||
// 3. The keeper discovers the D->C export's outputID, then has the maker sign a
|
||||
// Phase-B V4 swap tx to 0x9999 with DS01 hookData (outputID|amount|intentID).
|
||||
// ImportSettlement consumes the proceeds object and emits DEXFill.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
dextxs "github.com/luxfi/chains/dexvm/txs"
|
||||
"github.com/luxfi/dex/pkg/zapwire"
|
||||
"github.com/luxfi/geth/common"
|
||||
ethtypes "github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/log"
|
||||
"github.com/luxfi/zap/dexsession"
|
||||
|
||||
ethereum "github.com/luxfi/geth"
|
||||
"github.com/luxfi/geth/ethclient"
|
||||
)
|
||||
|
||||
// keeperConfig is the fully-resolved runtime config (flags + boot-queried values).
|
||||
type keeperConfig struct {
|
||||
CChainRPC string
|
||||
DRPC string // dexvm RPC base URL (…/ext/bc/<D-chain-id>/rpc)
|
||||
DChainID ids.ID
|
||||
VenueAddr string
|
||||
Maker common.Address
|
||||
ChainID *big.Int // C-Chain EVM chainID (eth_chainId)
|
||||
NetworkID uint32 // Lux primary network id (for intent-id derivation)
|
||||
}
|
||||
|
||||
// keeper holds the live clients + the maker key + resolved config.
|
||||
type keeper struct {
|
||||
cfg keeperConfig
|
||||
cc *ethclient.Client
|
||||
maker *makerKey
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
// newKeeper dials the C-Chain, resolves the chainID/networkID if unset, and binds the
|
||||
// maker key. The dexvm is reached over JSON-RPC by URL (no client object to dial).
|
||||
func newKeeper(ctx context.Context, cfg keeperConfig, mnemonic string) (*keeper, error) {
|
||||
cc, err := ethclient.DialContext(ctx, cfg.CChainRPC)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial C-Chain %s: %w", cfg.CChainRPC, err)
|
||||
}
|
||||
if cfg.ChainID == nil {
|
||||
id, err := cc.ChainID(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("eth_chainId: %w", err)
|
||||
}
|
||||
cfg.ChainID = id
|
||||
}
|
||||
if cfg.NetworkID == 0 {
|
||||
// The Lux primary network id is the C-Chain EVM chainID for sovereign L1s, and
|
||||
// for the primary network it is the convention-fixed id. The intent-id derivation
|
||||
// needs the value the precompile used (atomicState.NetworkID()); on devnet the
|
||||
// C-Chain EVM chainID equals the network id, so we default to it. Override with
|
||||
// --network-id if a deployment separates them.
|
||||
cfg.NetworkID = uint32(cfg.ChainID.Uint64())
|
||||
}
|
||||
mk, err := deriveMakerKey(mnemonic, cfg.Maker, 1000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive maker key: %w", err)
|
||||
}
|
||||
return &keeper{cfg: cfg, cc: cc, maker: mk, http: &http.Client{Timeout: 30 * time.Second}}, nil
|
||||
}
|
||||
|
||||
// cChainID reads the C-Chain's blockchain id (the 32-byte chain id the precompile uses in
|
||||
// DeriveIntentID, NOT the EVM chainID). On the primary network the C-Chain blockchain id
|
||||
// is a fixed alias; the keeper reads it from the node so it matches the chain exactly.
|
||||
//
|
||||
// NOTE: the geth ethclient does not expose the platform blockchain id. The authoritative
|
||||
// value rides the IntentSubmitted correlation anyway (the keeper acts on the id in the
|
||||
// event topic, not a re-derived one), so cChainID is only needed for the optional local
|
||||
// re-derivation sanity check. It is resolved lazily from the configured value when set.
|
||||
func (k *keeper) cChainBlockchainID() ids.ID {
|
||||
// The C-Chain blockchain id is not queryable via eth RPC; the keeper correlates on the
|
||||
// event's intentID topic (authoritative) rather than re-deriving, so an empty value
|
||||
// here only disables the optional local-derivation cross-check.
|
||||
return ids.Empty
|
||||
}
|
||||
|
||||
// ---- Phase A: send one intent swap from the maker ----
|
||||
|
||||
// sendPhaseAIntent builds, signs, and sends ONE Phase-A (DI01) swap to 0x9999 from the
|
||||
// maker. It returns the locally-derived intentID (correlation hint) and the C tx hash.
|
||||
// The keeper CHOOSES the nonce; SubmitSwapIntent re-derives the SAME id from the same
|
||||
// calldata, so the IntentSubmitted topic will carry this id.
|
||||
func (k *keeper) sendPhaseAIntent(ctx context.Context, pk dexsession.PoolKeyArgs, marketID, assetIn [32]byte, zeroForOne bool, amountIn, nonce, deadline uint64) (ids.ID, common.Hash, error) {
|
||||
data := phaseAIntentCalldata(pk, zeroForOne, amountIn, deadline, nonce)
|
||||
txHash, err := k.sendSwapTx(ctx, data)
|
||||
if err != nil {
|
||||
return ids.Empty, common.Hash{}, fmt.Errorf("send Phase-A swap: %w", err)
|
||||
}
|
||||
// Local id derivation is a correlation hint only; the chain's id (read from the
|
||||
// IntentSubmitted topic) is authoritative. cChainBlockchainID() is Empty over eth RPC,
|
||||
// so this matches the chain only when both sides use the same (here it documents the
|
||||
// derivation; the watch loop trusts the topic).
|
||||
intentID := deriveIntentID(k.cfg.NetworkID, k.cChainBlockchainID(), k.cfg.DChainID, k.maker.Address, assetIn, amountIn, marketID, nonce)
|
||||
return intentID, txHash, nil
|
||||
}
|
||||
|
||||
// sendSwapTx signs a legacy EVM tx to 0x9999 carrying `data` with the maker key and
|
||||
// submits it via eth_sendRawTransaction. It uses a suggested gas price + the maker's
|
||||
// pending nonce; the swap precompile is gas-metered, so a generous limit is used.
|
||||
func (k *keeper) sendSwapTx(ctx context.Context, data []byte) (common.Hash, error) {
|
||||
nonce, err := k.cc.PendingNonceAt(ctx, k.maker.Address)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("pending nonce: %w", err)
|
||||
}
|
||||
gasPrice, err := k.cc.SuggestGasPrice(ctx)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("suggest gas price: %w", err)
|
||||
}
|
||||
to := poolManagerAddr9999
|
||||
inner := ðtypes.LegacyTx{
|
||||
Nonce: nonce,
|
||||
GasPrice: gasPrice,
|
||||
Gas: 600_000,
|
||||
To: &to,
|
||||
Value: big.NewInt(0),
|
||||
Data: data,
|
||||
}
|
||||
signed, err := ethtypes.SignTx(ethtypes.NewTx(inner), ethtypes.LatestSignerForChainID(k.cfg.ChainID), k.maker.ecdsa)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("sign tx: %w", err)
|
||||
}
|
||||
if err := k.cc.SendTransaction(ctx, signed); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("send raw tx: %w", err)
|
||||
}
|
||||
return signed.Hash(), nil
|
||||
}
|
||||
|
||||
// ---- The watch loop ----
|
||||
|
||||
// watch runs the keeper loop: poll the C-Chain for new IntentSubmitted logs from 0x9999,
|
||||
// and for each previously-unseen intent, drive it through import+relay (and Phase-B once
|
||||
// export discovery is wired). It is restart-safe: it tracks the last scanned block and the
|
||||
// set of handled intent ids so a re-seen log is not re-driven.
|
||||
func (k *keeper) watch(ctx context.Context, fromBlock uint64) error {
|
||||
handled := make(map[ids.ID]bool)
|
||||
next := fromBlock
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
log.Info("keeper watch loop started", "fromBlock", fromBlock, "maker", k.cfg.Maker, "dChainID", k.cfg.DChainID)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
}
|
||||
head, err := k.cc.BlockNumber(ctx)
|
||||
if err != nil {
|
||||
log.Warn("BlockNumber", "err", err)
|
||||
continue
|
||||
}
|
||||
if head < next {
|
||||
continue
|
||||
}
|
||||
logs, err := k.intentLogs(ctx, next, head)
|
||||
if err != nil {
|
||||
log.Warn("FilterLogs IntentSubmitted", "from", next, "to", head, "err", err)
|
||||
continue
|
||||
}
|
||||
for _, lg := range logs {
|
||||
ev, err := decodeIntentEvent(lg)
|
||||
if err != nil {
|
||||
log.Warn("decode IntentSubmitted", "err", err)
|
||||
continue
|
||||
}
|
||||
if handled[ev.IntentID] {
|
||||
continue
|
||||
}
|
||||
if err := k.handleIntent(ctx, ev); err != nil {
|
||||
log.Error("handle intent", "intentID", ev.IntentID, "err", err)
|
||||
continue // leave unhandled so a later pass can retry
|
||||
}
|
||||
handled[ev.IntentID] = true
|
||||
}
|
||||
next = head + 1
|
||||
}
|
||||
}
|
||||
|
||||
// intentLogs queries 0x9999 for IntentSubmitted logs in [from, to].
|
||||
func (k *keeper) intentLogs(ctx context.Context, from, to uint64) ([]ethtypes.Log, error) {
|
||||
q := ethereum.FilterQuery{
|
||||
FromBlock: new(big.Int).SetUint64(from),
|
||||
ToBlock: new(big.Int).SetUint64(to),
|
||||
Addresses: []common.Address{poolManagerAddr9999},
|
||||
Topics: [][]common.Hash{{intentSubmittedSig}},
|
||||
}
|
||||
return k.cc.FilterLogs(ctx, q)
|
||||
}
|
||||
|
||||
// handleIntent drives ONE intent through the D-side legs: build+submit the ImportTx
|
||||
// (consume the C->D object, fund the D order) and the settling RelayOrderTx (clob_submit),
|
||||
// then discover the D->C export and build Phase-B. Only swap intents (kind 0) are driven
|
||||
// here; position intents (kind 1) are routed by a different op and are skipped.
|
||||
func (k *keeper) handleIntent(ctx context.Context, ev intentEvent) error {
|
||||
if ev.Kind != intentKindSwap {
|
||||
log.Info("skip non-swap intent", "intentID", ev.IntentID, "kind", ev.Kind)
|
||||
return nil
|
||||
}
|
||||
log.Info("handling swap intent", "intentID", ev.IntentID, "account", ev.Account,
|
||||
"assetIn", hex.EncodeToString(ev.AssetIn[:]), "amountIn", ev.AmountIn, "marketID", hex.EncodeToString(ev.MarketID[:]))
|
||||
|
||||
// 1) ImportTx: consume the C->D object (keyed by intentID) and credit the D order's
|
||||
// funding to the maker. The import's single input names the staged object (UTXOID =
|
||||
// intentID), its asset (assetIn) and locked amount; the output credits the same to
|
||||
// the maker on the swap rail.
|
||||
owner := ids.ShortID(k.maker.Address)
|
||||
importTx := dextxs.NewImportTx(
|
||||
owner,
|
||||
ev.AmountIn, // nonce: any monotone value; the import is content-addressed by its inputs
|
||||
k.cChainSourceID(),
|
||||
[]dextxs.AtomicInput{{UTXOID: ev.IntentID, Asset: ids.ID(ev.AssetIn), Amount: ev.AmountIn}},
|
||||
[]dextxs.AtomicOutput{{Rail: dextxs.RailSwap, Owner: owner, Asset: ids.ID(ev.AssetIn), Amount: ev.AmountIn}},
|
||||
)
|
||||
importID, err := k.submitDexTx(ctx, importTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("submit ImportTx: %w", err)
|
||||
}
|
||||
log.Info("submitted ImportTx", "txID", importID, "collateralRef", importTx.ID())
|
||||
|
||||
// 2) Settling RelayOrderTx: the taker clob_submit frame, bound to the import's
|
||||
// collateral (CollateralRef = importTx.ID()). The order is a marketable BUY (the
|
||||
// taker spends assetIn to receive the opposite side) at the carried slippage limit.
|
||||
// assetOut is the opposite side of the market — the keeper knows the market mapping;
|
||||
// here it is carried as the market's quote/base counterpart of assetIn.
|
||||
//
|
||||
// The relay is UNSIGNED, with From set to the maker's STANDARD EVM address (==
|
||||
// the escrow owner the precompile recorded). This is the architecturally-correct
|
||||
// build: settleFromFills derives the settle authority + payout target from the
|
||||
// consumed C->D object's RECORDED owner (escrowOwner), and REQUIRES the relay's
|
||||
// From to equal it (relaySender != escrowOwner -> errSettleUnauthorized); From
|
||||
// carries no independent authority, so an unsigned relay is admissible and cannot
|
||||
// escalate (chains/dexvm txs/tx.go RelayOrderTx.Verify / vm.go settleFromFills).
|
||||
// We deliberately do NOT call relay.Sign(secp): that would overwrite From with
|
||||
// secp256k1.PrivateKey.EVMAddress(), which hashes the COMPRESSED pubkey and so
|
||||
// yields a NON-standard address that does NOT equal the standard escrow owner —
|
||||
// the signed relay would be refused with errSettleUnauthorized. See the keeper
|
||||
// report's "secp256k1.EVMAddress divergence" blocker.
|
||||
side := zapwire.SideBuy
|
||||
limitPrice := priceLimitFloat(ev.PriceLimit)
|
||||
size := float64(ev.AmountIn)
|
||||
frame := submitFrameFor(poolIDFromMarket(ev.MarketID), side, true /*isMarket*/, limitPrice, size, makerHandle(k.cfg.Maker))
|
||||
assetOut := k.assetOutFor(ev.MarketID, ev.AssetIn)
|
||||
relay := dextxs.NewSettlingRelayOrderTx(owner, ev.AmountIn+1, frame, importTx.ID(), assetOut, ev.PriceLimit, ev.LimitIsUpper)
|
||||
relayID, err := k.submitDexTx(ctx, relay)
|
||||
if err != nil {
|
||||
return fmt.Errorf("submit RelayOrderTx: %w", err)
|
||||
}
|
||||
log.Info("submitted settling RelayOrderTx (unsigned; From=escrow owner)", "txID", relayID)
|
||||
|
||||
// 3) Discover the D->C export proceeds object and build Phase-B. This is the single
|
||||
// step that requires a settlement-coordinate query the current dexvm RPC does not
|
||||
// yet expose (see getExportedOutput); until it is wired, the keeper has driven the
|
||||
// intent through the relay and the D->C proceeds object IS staged — the maker (or a
|
||||
// later keeper pass) completes Phase-B once the outputID is discoverable.
|
||||
outputID, amountOut, err := k.getExportedOutput(ctx, ev, relay, importTx.ID())
|
||||
if err != nil {
|
||||
return fmt.Errorf("discover D->C export (intent driven through relay; Phase-B pending): %w", err)
|
||||
}
|
||||
return k.sendPhaseBSettle(ctx, ev, outputID, amountOut)
|
||||
}
|
||||
|
||||
// sendPhaseBSettle has the maker sign+send the Phase-B (DS01) swap to 0x9999, consuming
|
||||
// the discovered D->C proceeds object. ImportSettlement credits the maker and emits DEXFill.
|
||||
func (k *keeper) sendPhaseBSettle(ctx context.Context, ev intentEvent, outputID ids.ID, amountOut uint64) error {
|
||||
pk := poolKeyForMarket(ev.MarketID)
|
||||
// zeroForOne mirrors the Phase-A direction; amountIn keeps the swap ABI well-formed.
|
||||
data := phaseBSettleCalldata(pk, true, ev.AmountIn, outputID, ev.IntentID, amountOut)
|
||||
txHash, err := k.sendSwapTx(ctx, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("send Phase-B swap: %w", err)
|
||||
}
|
||||
log.Info("submitted Phase-B settlement", "intentID", ev.IntentID, "outputID", outputID, "amountOut", amountOut, "txHash", txHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---- dexvm tx submission over dex.submitTx ----
|
||||
|
||||
// submitTxRequest / submitTxResponse mirror the gorilla-rpc envelope the dexvm's
|
||||
// dex.submitTx serves (chains/dexvm/api/service.go SubmitTxArgs/SubmitTxReply).
|
||||
type submitTxRequest struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID int `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params [1]submitTxParams `json:"params"`
|
||||
}
|
||||
|
||||
type submitTxParams struct {
|
||||
Tx string `json:"tx"` // hex-encoded tx wire bytes
|
||||
}
|
||||
|
||||
type submitTxResponse struct {
|
||||
Result *struct {
|
||||
TxID string `json:"txID"`
|
||||
} `json:"result"`
|
||||
Error *struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// submitDexTx hex-encodes a dexvm tx's wire bytes and POSTs them to dex.submitTx, returning
|
||||
// the dexvm's tx id. The wire is the dexvm codec (type byte + JSON), exactly tx.Bytes().
|
||||
func (k *keeper) submitDexTx(ctx context.Context, tx dextxs.Tx) (string, error) {
|
||||
body, err := json.Marshal(submitTxRequest{
|
||||
JSONRPC: "2.0",
|
||||
ID: 1,
|
||||
Method: "dex.submitTx",
|
||||
Params: [1]submitTxParams{{Tx: hex.EncodeToString(tx.Bytes())}},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal submitTx request: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, k.cfg.DRPC, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new request: %w", err)
|
||||
}
|
||||
req.Header.Set("content-type", "application/json")
|
||||
resp, err := k.http.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("POST dex.submitTx: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var out submitTxResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
|
||||
return "", fmt.Errorf("decode submitTx response: %w", err)
|
||||
}
|
||||
if out.Error != nil {
|
||||
return "", fmt.Errorf("dex.submitTx error %d: %s", out.Error.Code, out.Error.Message)
|
||||
}
|
||||
if out.Result == nil {
|
||||
return "", fmt.Errorf("dex.submitTx: empty result")
|
||||
}
|
||||
return out.Result.TxID, nil
|
||||
}
|
||||
|
||||
// ---- export discovery (the one documented stub) ----
|
||||
|
||||
// getExportedOutput discovers the D->C railSwap proceeds object that settleFromFills
|
||||
// exported for THIS intent, returning its shared-memory outputID and credited amount so
|
||||
// the keeper can build the Phase-B settlement.
|
||||
//
|
||||
// WHY THIS IS A STUB (and exactly what must fill it): the export UTXO key is
|
||||
//
|
||||
// deriveUTXOID( settlementExportTx.ID(), outputIndex )
|
||||
//
|
||||
// where (chains/dexvm vm.go settleFromFills + NewSettlementExportTx):
|
||||
//
|
||||
// settlementExportTx = NewSettlementExportTx(
|
||||
// taker = ids.ShortID(maker EVM address),
|
||||
// txIndex = the relay's index within the D settlement block,
|
||||
// destChain = cChainID,
|
||||
// outs = [ proceeds(RailSwap, owner=taker, asset=assetOut, amount=Σfill_out, spent=Σfill_in),
|
||||
// refund(RailSwap, owner=taker, asset=assetIn, amount=locked-spent) ], // refund omitted if 0
|
||||
// fillRef = collateralRef (== importTx.ID()),
|
||||
// createdAt = int64(BigEndian.Uint64(blockHash[:8])), // D settlement block hash
|
||||
// )
|
||||
//
|
||||
// and the proceeds leg is outputIndex 0 (nonZeroOutputs preserves proceeds-before-refund
|
||||
// order). Everything EXCEPT (blockHash, txIndex) and the realized fill amounts is known to
|
||||
// the keeper. (blockHash, txIndex, fills) are properties of the D block that settled the
|
||||
// relay — and the dexvm RPC currently exposes only {Ping, Status, Relay, SubmitTx}
|
||||
// (chains/dexvm/api/service.go), none of which return them. The C-Chain EVM does not
|
||||
// expose an atomic-getUTXOs for the dexvm seam either (verified in luxfi/node plugin/evm).
|
||||
//
|
||||
// TO COMPLETE, add ONE of:
|
||||
// - a dexvm RPC `dex.getSettlement(relayTxID)` returning {blockHash, txIndex, fills[]}
|
||||
// (or {outputID, amount} directly), then recompute the key here with deriveUTXOID; or
|
||||
// - a C-Chain view that lists importable D->C railSwap UTXOs for an address+sourceChain,
|
||||
// and select the one whose recorded (owner==maker, asset==assetOut) matches this intent.
|
||||
//
|
||||
// Until then this returns an error; handleIntent has already driven the intent through the
|
||||
// relay, so the D->C proceeds object is staged and Phase-B is the only remaining leg.
|
||||
func (k *keeper) getExportedOutput(ctx context.Context, ev intentEvent, relay *dextxs.RelayOrderTx, collateralRef ids.ID) (outputID ids.ID, amount uint64, err error) {
|
||||
_ = ctx
|
||||
_ = ev
|
||||
_ = relay
|
||||
_ = collateralRef
|
||||
return ids.Empty, 0, fmt.Errorf("export discovery not wired: dexvm exposes no settlement-coordinate query (need dex.getSettlement(relayTxID) -> {blockHash,txIndex,fills} or a C-Chain importable-UTXO view)")
|
||||
}
|
||||
|
||||
// ---- market mapping helpers ----
|
||||
//
|
||||
// The keeper binds a D market id to its (poolID, base/quote assets, V4 PoolKey). On a
|
||||
// single-market devnet proof the mapping is the identity poolID==marketID with the venue's
|
||||
// base/quote; a multi-market deployment supplies a real registry. These keep the mapping in
|
||||
// ONE place so the rest of the keeper is market-agnostic.
|
||||
|
||||
// poolIDFromMarket maps a marketID to the venue poolID. The seam keys both by the same
|
||||
// 32-byte id (the D market IS the venue book), so this is the identity.
|
||||
func poolIDFromMarket(marketID [32]byte) [32]byte { return marketID }
|
||||
|
||||
// poolKeyForMarket builds the V4 PoolKey the 0x9999 calldata needs for a market. The
|
||||
// native seam matches by marketID, not by the PoolKey tuple, so the keeper passes a
|
||||
// well-formed key whose hooks point at 0x9999; currency0/1 are the market's assets' EVM
|
||||
// addresses (address(0) for native), fee/tickSpacing the market's V4 params.
|
||||
func poolKeyForMarket(marketID [32]byte) dexsession.PoolKeyArgs {
|
||||
return dexsession.PoolKeyArgs{
|
||||
Hooks: dexsession.Account(poolManagerAddr9999),
|
||||
}
|
||||
}
|
||||
|
||||
// assetOutFor returns the opposite side of a market from assetIn (the asset a taker BUY
|
||||
// receives). On the single-market devnet proof the market's quote and base are the two
|
||||
// venue assets; the keeper subtracts assetIn from the pair. A real registry supplies this.
|
||||
func (k *keeper) assetOutFor(marketID, assetIn [32]byte) ids.ID {
|
||||
// Identity-market devnet default: the output asset is carried by the seed spec and
|
||||
// equals the market's base when assetIn is the quote (and vice-versa). With no registry
|
||||
// here, return the marketID-derived counterpart; the dexvm equality-rejects a wrong
|
||||
// asset (taker-liveness only, no theft), so this is the bounded surface documented on
|
||||
// RelayOrderTx.AssetOut.
|
||||
_ = assetIn
|
||||
return ids.ID(marketID)
|
||||
}
|
||||
|
||||
// makerHandle is the venue user handle for the maker, derived from its EVM address so the
|
||||
// custody rows are stable and addressable. zapwire user width is 16 bytes; the handle is
|
||||
// the address hex truncated to fit.
|
||||
func makerHandle(addr common.Address) string {
|
||||
h := addr.Hex() // 0x + 40 hex
|
||||
if len(h) > zapwire.UserSize {
|
||||
return h[:zapwire.UserSize]
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// cChainSourceID is the source chain the ImportTx names (the chain the C->D object was
|
||||
// exported from — the C-Chain). It equals the configured C-Chain blockchain id; on the
|
||||
// primary network the dexvm resolves it as the "C" alias. When the keeper has no platform
|
||||
// blockchain id (eth RPC does not expose it), the dexvm validates the import against the
|
||||
// recorded object regardless, so the configured D-chain peering is authoritative.
|
||||
func (k *keeper) cChainSourceID() ids.ID {
|
||||
// The import's SourceChain must equal the C-Chain blockchain id the object was PUT from.
|
||||
// It is not queryable over eth RPC; the dexvm binds the import to the recorded object's
|
||||
// source. Carried as Empty here documents the dependency; a deployment that needs an
|
||||
// explicit value supplies it via the (future) settlement query or a --c-blockchain-id.
|
||||
return k.cChainBlockchainID()
|
||||
}
|
||||
|
||||
// intentKindSwap mirrors precompile/dex nativeIntentKind: 0 = taker swap, 1 = LP position.
|
||||
const intentKindSwap uint8 = 0
|
||||
@@ -0,0 +1,368 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto"
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
"github.com/luxfi/geth/common"
|
||||
ethtypes "github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap/dexsession"
|
||||
)
|
||||
|
||||
// ---- maker-key derivation ----
|
||||
|
||||
// TestDeriveMakerKeyMatchesGenesisAuthority pins the keeper's BIP44 walk against the
|
||||
// canonical genesis derivation (genesis.LoadBIP44WalletKeysFromMnemonic). A one-sided
|
||||
// drift from m/44'/9000'/0'/0/i — different coin type, hardening, or hashing — changes
|
||||
// the derived address and trips this test BEFORE the keeper ever signs with a wrong key.
|
||||
func TestDeriveMakerKeyMatchesGenesisAuthority(t *testing.T) {
|
||||
const scan = 8
|
||||
authority, err := genesis.LoadBIP44WalletKeysFromMnemonic(genesis.LightMnemonic, scan)
|
||||
if err != nil {
|
||||
t.Fatalf("genesis.LoadBIP44WalletKeysFromMnemonic: %v", err)
|
||||
}
|
||||
for i, want := range authority {
|
||||
wantAddr := common.BytesToAddress(want.EVMAddr[:])
|
||||
got, err := deriveMakerKey(genesis.LightMnemonic, wantAddr, scan)
|
||||
if err != nil {
|
||||
t.Fatalf("deriveMakerKey index %d (%s): %v", i, wantAddr, err)
|
||||
}
|
||||
if got.Address != wantAddr {
|
||||
t.Fatalf("index %d: derived %s, genesis authority %s", i, got.Address, wantAddr)
|
||||
}
|
||||
if got.Index != i {
|
||||
t.Fatalf("index %d: keeper found it at index %d", i, got.Index)
|
||||
}
|
||||
// The ecdsa signing key must re-derive the SAME standard address (the C-Chain
|
||||
// caller / escrow-owner format keccak256(X||Y)[12:]).
|
||||
if a := evmAddress(got.ecdsa); a != wantAddr {
|
||||
t.Fatalf("index %d: ecdsa key address %s != genesis authority %s", i, a, wantAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeriveMakerKeyKnownVector pins index 0 under the public LightMnemonic against a
|
||||
// hardcoded address, so the derivation is anchored to a fixed value (not just to the
|
||||
// authority, which could co-drift). This is the canonical BIP44 m/44'/9000'/0'/0/0 key.
|
||||
func TestDeriveMakerKeyKnownVector(t *testing.T) {
|
||||
const wantHex = "0x5369615110ca435bdf798f31c20ba6163d7b0a54"
|
||||
want := common.HexToAddress(wantHex)
|
||||
got, err := deriveMakerKey(genesis.LightMnemonic, want, 4)
|
||||
if err != nil {
|
||||
t.Fatalf("deriveMakerKey: %v", err)
|
||||
}
|
||||
if got.Address != want {
|
||||
t.Fatalf("index 0 derived %s, want %s", got.Address, want)
|
||||
}
|
||||
if got.Index != 0 {
|
||||
t.Fatalf("known vector is index 0, keeper found %d", got.Index)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeriveMakerKeyNotFound asserts a clean error when the wanted address is not in the
|
||||
// scanned range (rather than a silent wrong key).
|
||||
func TestDeriveMakerKeyNotFound(t *testing.T) {
|
||||
absent := common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
|
||||
if _, err := deriveMakerKey(genesis.LightMnemonic, absent, 4); err == nil {
|
||||
t.Fatal("expected error for absent address, got nil")
|
||||
}
|
||||
if _, err := deriveMakerKey("not a valid mnemonic", absent, 4); err == nil {
|
||||
t.Fatal("expected error for invalid mnemonic, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Phase-A / Phase-B calldata ----
|
||||
|
||||
// TestPhaseAIntentCalldata asserts the keeper's Phase-A builder is byte-identical to a
|
||||
// direct dexsession.EncodeSwapCalldata with a DI01 intent hookData, and that the calldata
|
||||
// has the expected structural shape: 0xF3CD914C selector, 9 head words, then a DI01-tagged
|
||||
// hookData carrying the deadline + nonce.
|
||||
func TestPhaseAIntentCalldata(t *testing.T) {
|
||||
pk := dexsession.PoolKeyArgs{Hooks: dexsession.Account(poolManagerAddr9999), Fee: 3000, TickSpacing: 60}
|
||||
const (
|
||||
amountIn = uint64(5)
|
||||
deadline = uint64(1_700_000_000)
|
||||
nonce = uint64(42)
|
||||
)
|
||||
got := phaseAIntentCalldata(pk, false, amountIn, deadline, nonce)
|
||||
want := dexsession.EncodeSwapCalldata(pk, false, amountIn, dexsession.EncodeIntentHookData(deadline, nonce))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("phaseAIntentCalldata != dexsession.EncodeSwapCalldata\n got=%x\nwant=%x", got, want)
|
||||
}
|
||||
// Selector 0xF3CD914C (dexsession.SelectorSwap).
|
||||
if sel := binary.BigEndian.Uint32(got[0:4]); sel != dexsession.SelectorSwap {
|
||||
t.Fatalf("selector %#x, want %#x", sel, dexsession.SelectorSwap)
|
||||
}
|
||||
// Head: selector(4) + 9 words. The dynamic hookData tail begins after the head and
|
||||
// carries the DI01 tag.
|
||||
const headLen = 4 + 9*32
|
||||
if len(got) <= headLen {
|
||||
t.Fatalf("calldata len %d, want > head %d (a hookData tail)", len(got), headLen)
|
||||
}
|
||||
// The hookData length word is the last head word's pointee: the tail's first 32 bytes
|
||||
// are the length, then the DI01-tagged bytes.
|
||||
tail := got[headLen:]
|
||||
hookLen := binary.BigEndian.Uint64(tail[24:32])
|
||||
hookData := tail[32 : 32+hookLen]
|
||||
if string(hookData[:4]) != "DI01" {
|
||||
t.Fatalf("hookData tag %q, want DI01", hookData[:4])
|
||||
}
|
||||
// DI01 + deadline[32] + nonce[32] for a nonzero deadline+nonce.
|
||||
if hookLen != 4+64 {
|
||||
t.Fatalf("hookData len %d, want %d (tag + deadline + nonce)", hookLen, 4+64)
|
||||
}
|
||||
if d := binary.BigEndian.Uint64(hookData[4+24 : 4+32]); d != deadline {
|
||||
t.Fatalf("hookData deadline %d, want %d", d, deadline)
|
||||
}
|
||||
if n := binary.BigEndian.Uint64(hookData[4+32+24 : 4+32+32]); n != nonce {
|
||||
t.Fatalf("hookData nonce %d, want %d", n, nonce)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPhaseBSettleCalldata asserts the Phase-B builder carries a DS01 settlement hookData
|
||||
// with the outputID|amount|intentID body (the intentID word is what binds the credit to
|
||||
// the taker's intent record; a missing one is the wrong width and the precompile reverts).
|
||||
func TestPhaseBSettleCalldata(t *testing.T) {
|
||||
pk := dexsession.PoolKeyArgs{Hooks: dexsession.Account(poolManagerAddr9999)}
|
||||
outputID := ids.ID{0x01, 0x02, 0x03}
|
||||
intentID := ids.ID{0xaa, 0xbb, 0xcc}
|
||||
const (
|
||||
amountIn = uint64(7)
|
||||
amountOut = uint64(123)
|
||||
)
|
||||
got := phaseBSettleCalldata(pk, true, amountIn, outputID, intentID, amountOut)
|
||||
body := dexsession.EncodeSettlementHookData(dexsession.ID(outputID), amountOut, dexsession.ID(intentID))
|
||||
want := dexsession.EncodeSwapCalldata(pk, true, amountIn, body)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("phaseBSettleCalldata != expected\n got=%x\nwant=%x", got, want)
|
||||
}
|
||||
const headLen = 4 + 9*32
|
||||
tail := got[headLen:]
|
||||
hookLen := binary.BigEndian.Uint64(tail[24:32])
|
||||
hookData := tail[32 : 32+hookLen]
|
||||
if string(hookData[:4]) != "DS01" {
|
||||
t.Fatalf("hookData tag %q, want DS01", hookData[:4])
|
||||
}
|
||||
// DS01 + outputID(32) + amount(32) + intentID(32) = 4 + 96.
|
||||
if hookLen != 4+96 {
|
||||
t.Fatalf("DS01 body len %d, want %d (tag + outputID + amount + intentID)", hookLen, 4+96)
|
||||
}
|
||||
if !bytes.Equal(hookData[4:36], outputID[:]) {
|
||||
t.Fatalf("DS01 outputID %x, want %x", hookData[4:36], outputID[:])
|
||||
}
|
||||
if a := binary.BigEndian.Uint64(hookData[36+24 : 36+32]); a != amountOut {
|
||||
t.Fatalf("DS01 amount %d, want %d", a, amountOut)
|
||||
}
|
||||
if !bytes.Equal(hookData[68:100], intentID[:]) {
|
||||
t.Fatalf("DS01 intentID %x, want %x", hookData[68:100], intentID[:])
|
||||
}
|
||||
}
|
||||
|
||||
// ---- intent-id derivation ----
|
||||
|
||||
// TestDeriveIntentIDDeterministicAndInjective asserts the id is a pure function of its
|
||||
// inputs (same inputs -> same id) and injective over the disambiguating axes (a changed
|
||||
// nonce / account / amount changes the id). This mirrors precompile/dex DeriveIntentID;
|
||||
// the keeper derives the SAME id the chain does from the same calldata (watch correlation).
|
||||
func TestDeriveIntentIDDeterministicAndInjective(t *testing.T) {
|
||||
net := uint32(96369)
|
||||
cID := ids.ID{0xc0}
|
||||
dID := ids.ID{0xd0}
|
||||
acct := common.HexToAddress("0x9011E888251AB053B7bD1cdB598Db4f9DEd94714")
|
||||
asset := [32]byte{0x4c, 0x55, 0x58}
|
||||
market := [32]byte{0x4d}
|
||||
const amount, nonce = uint64(10), uint64(1)
|
||||
|
||||
base := deriveIntentID(net, cID, dID, acct, asset, amount, market, nonce)
|
||||
if base != deriveIntentID(net, cID, dID, acct, asset, amount, market, nonce) {
|
||||
t.Fatal("deriveIntentID not deterministic")
|
||||
}
|
||||
if base == (ids.ID{}) {
|
||||
t.Fatal("deriveIntentID returned empty id")
|
||||
}
|
||||
// Each axis must change the id.
|
||||
cases := []struct {
|
||||
name string
|
||||
id ids.ID
|
||||
}{
|
||||
{"nonce", deriveIntentID(net, cID, dID, acct, asset, amount, market, nonce+1)},
|
||||
{"amount", deriveIntentID(net, cID, dID, acct, asset, amount+1, market, nonce)},
|
||||
{"account", deriveIntentID(net, cID, dID, common.Address{0x01}, asset, amount, market, nonce)},
|
||||
{"network", deriveIntentID(net+1, cID, dID, acct, asset, amount, market, nonce)},
|
||||
{"market", deriveIntentID(net, cID, dID, acct, asset, amount, [32]byte{0x4e}, nonce)},
|
||||
{"dChain", deriveIntentID(net, cID, ids.ID{0xd1}, acct, asset, amount, market, nonce)},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if c.id == base {
|
||||
t.Fatalf("changing %s did not change the intent id", c.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- IntentSubmitted event decode ----
|
||||
|
||||
// makeIntentLog builds a synthetic IntentSubmitted log with the exact wire the precompile
|
||||
// emits (3 topics, 10 ABI data words) so the decoder can be exercised without a chain.
|
||||
func makeIntentLog(e intentEvent) ethtypes.Log {
|
||||
data := make([]byte, 0, 10*32)
|
||||
word := func(b []byte) { var w [32]byte; copy(w[32-len(b):], b); data = append(data, w[:]...) }
|
||||
addrWord := func(a common.Address) { word(a.Bytes()) }
|
||||
u64Word := func(v uint64) { var b [8]byte; binary.BigEndian.PutUint64(b[:], v); word(b[:]) }
|
||||
|
||||
addrWord(e.Account) // 0 account
|
||||
data = append(data, e.AssetIn[:]...) // 1 assetIn (full 32)
|
||||
u64Word(e.AmountIn) // 2 locked
|
||||
data = append(data, e.MarketID[:]...) // 3 marketID
|
||||
min := e.MinAmountOut
|
||||
if min == nil {
|
||||
min = big.NewInt(0)
|
||||
}
|
||||
word(min.Bytes()) // 4 minAmountOut
|
||||
addrWord(e.Recipient) // 5 recipient
|
||||
u64Word(e.Deadline) // 6 deadline
|
||||
u64Word(uint64(e.Kind)) // 7 kind
|
||||
u64Word(e.PriceLimit) // 8 priceLimit
|
||||
var limitFlag uint64
|
||||
if e.LimitIsUpper {
|
||||
limitFlag = 1
|
||||
}
|
||||
u64Word(limitFlag) // 9 limitIsUpper
|
||||
|
||||
return ethtypes.Log{
|
||||
Address: poolManagerAddr9999,
|
||||
Topics: []common.Hash{
|
||||
intentSubmittedSig,
|
||||
common.BytesToHash(e.IntentID[:]),
|
||||
common.BytesToHash(e.DChainID[:]),
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeIntentEventRoundTrip(t *testing.T) {
|
||||
want := intentEvent{
|
||||
IntentID: ids.ID{0x11, 0x22, 0x33},
|
||||
DChainID: ids.ID{0xd0, 0xd1},
|
||||
Account: common.HexToAddress("0x9011E888251AB053B7bD1cdB598Db4f9DEd94714"),
|
||||
AssetIn: [32]byte{0x4c, 0x55, 0x53, 0x44},
|
||||
AmountIn: 250,
|
||||
MarketID: [32]byte{0x4d, 0x4b, 0x54},
|
||||
MinAmountOut: big.NewInt(99),
|
||||
Recipient: common.HexToAddress("0x5369615110ca435bdf798f31c20ba6163d7b0a54"),
|
||||
Deadline: 1_700_000_000,
|
||||
Kind: intentKindSwap,
|
||||
PriceLimit: 0x4034000000000000, // float64 bits of 20.0
|
||||
LimitIsUpper: true,
|
||||
}
|
||||
got, err := decodeIntentEvent(makeIntentLog(want))
|
||||
if err != nil {
|
||||
t.Fatalf("decodeIntentEvent: %v", err)
|
||||
}
|
||||
if got.IntentID != want.IntentID || got.DChainID != want.DChainID {
|
||||
t.Fatalf("ids: got (%s,%s) want (%s,%s)", got.IntentID, got.DChainID, want.IntentID, want.DChainID)
|
||||
}
|
||||
if got.Account != want.Account || got.Recipient != want.Recipient {
|
||||
t.Fatalf("addrs: got (%s,%s) want (%s,%s)", got.Account, got.Recipient, want.Account, want.Recipient)
|
||||
}
|
||||
if got.AssetIn != want.AssetIn || got.MarketID != want.MarketID {
|
||||
t.Fatalf("asset/market mismatch")
|
||||
}
|
||||
if got.AmountIn != want.AmountIn || got.Deadline != want.Deadline || got.Kind != want.Kind {
|
||||
t.Fatalf("amount/deadline/kind: got (%d,%d,%d) want (%d,%d,%d)", got.AmountIn, got.Deadline, got.Kind, want.AmountIn, want.Deadline, want.Kind)
|
||||
}
|
||||
if got.PriceLimit != want.PriceLimit || got.LimitIsUpper != want.LimitIsUpper {
|
||||
t.Fatalf("priceLimit/isUpper: got (%d,%v) want (%d,%v)", got.PriceLimit, got.LimitIsUpper, want.PriceLimit, want.LimitIsUpper)
|
||||
}
|
||||
if got.MinAmountOut.Cmp(want.MinAmountOut) != 0 {
|
||||
t.Fatalf("minAmountOut: got %s want %s", got.MinAmountOut, want.MinAmountOut)
|
||||
}
|
||||
// priceLimitFloat must decode the bits to the float the venue frame expects.
|
||||
if pf := priceLimitFloat(got.PriceLimit); pf != 20.0 {
|
||||
t.Fatalf("priceLimitFloat(%#x) = %v, want 20.0", got.PriceLimit, pf)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeIntentEventRejectsMalformed asserts the boundary decoder fails closed on a
|
||||
// foreign topic0, a wrong topic count, and a wrong data length — never silently acts on a
|
||||
// log that is not an IntentSubmitted of the right shape.
|
||||
func TestDecodeIntentEventRejectsMalformed(t *testing.T) {
|
||||
good := makeIntentLog(intentEvent{IntentID: ids.ID{0x01}, DChainID: ids.ID{0x02}, MinAmountOut: big.NewInt(0)})
|
||||
|
||||
// foreign topic0
|
||||
bad := good
|
||||
bad.Topics = append([]common.Hash{}, good.Topics...)
|
||||
bad.Topics[0] = common.HexToHash("0xdeadbeef")
|
||||
if _, err := decodeIntentEvent(bad); err == nil {
|
||||
t.Fatal("expected error for foreign topic0")
|
||||
}
|
||||
// wrong topic count
|
||||
bad = good
|
||||
bad.Topics = good.Topics[:2]
|
||||
if _, err := decodeIntentEvent(bad); err == nil {
|
||||
t.Fatal("expected error for wrong topic count")
|
||||
}
|
||||
// wrong data length
|
||||
bad = good
|
||||
bad.Data = good.Data[:len(good.Data)-1]
|
||||
if _, err := decodeIntentEvent(bad); err == nil {
|
||||
t.Fatal("expected error for short data")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeDEXFillEvent asserts the terminal DEXFill decode round-trips topics+data.
|
||||
func TestDecodeDEXFillEvent(t *testing.T) {
|
||||
poolID := [32]byte{0xD0, 0x01}
|
||||
taker := common.HexToAddress("0x9011E888251AB053B7bD1cdB598Db4f9DEd94714")
|
||||
amountOut := big.NewInt(4321)
|
||||
blockNumber := uint64(123456)
|
||||
|
||||
data := make([]byte, 0, 64)
|
||||
data = append(data, common.BigToHash(amountOut).Bytes()...)
|
||||
data = append(data, common.BigToHash(new(big.Int).SetUint64(blockNumber)).Bytes()...)
|
||||
log := ethtypes.Log{
|
||||
Address: poolManagerAddr9999,
|
||||
Topics: []common.Hash{
|
||||
dexFillSig,
|
||||
common.BytesToHash(poolID[:]),
|
||||
common.BytesToHash(taker.Bytes()),
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
got, err := decodeDEXFillEvent(log)
|
||||
if err != nil {
|
||||
t.Fatalf("decodeDEXFillEvent: %v", err)
|
||||
}
|
||||
if got.PoolID != poolID {
|
||||
t.Fatalf("poolID %x, want %x", got.PoolID, poolID)
|
||||
}
|
||||
if got.Taker != taker {
|
||||
t.Fatalf("taker %s, want %s", got.Taker, taker)
|
||||
}
|
||||
if got.AmountOut.Cmp(amountOut) != 0 {
|
||||
t.Fatalf("amountOut %s, want %s", got.AmountOut, amountOut)
|
||||
}
|
||||
if got.BlockNumber != blockNumber {
|
||||
t.Fatalf("blockNumber %d, want %d", got.BlockNumber, blockNumber)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- event signature pinning ----
|
||||
|
||||
// TestEventSignatures pins the two event topic0 hashes against keccak256 of the canonical
|
||||
// ABI signature strings, so a drift from the precompile's emitted signature is caught here.
|
||||
func TestEventSignatures(t *testing.T) {
|
||||
wantIntent := common.BytesToHash(crypto.Keccak256([]byte(
|
||||
"IntentSubmitted(bytes32,bytes32,address,bytes32,uint256,bytes32,uint256,address,uint64,uint8,uint64,uint8)")))
|
||||
if intentSubmittedSig != wantIntent {
|
||||
t.Fatalf("intentSubmittedSig %s != %s", intentSubmittedSig, wantIntent)
|
||||
}
|
||||
wantFill := common.BytesToHash(crypto.Keccak256([]byte("DEXFill(bytes32,address,uint256,uint256)")))
|
||||
if dexFillSig != wantFill {
|
||||
t.Fatalf("dexFillSig %s != %s", dexFillSig, wantFill)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// Command dexkeeper is the off-chain KEEPER that drives the on-chain 0x9999 two-phase
|
||||
// atomic DEX seam so a DEXFill lands on the C-Chain.
|
||||
//
|
||||
// It watches the C-Chain for IntentSubmitted events emitted by the 0x9999 settlement
|
||||
// precompile (Phase-A intent: a maker swap with DI01 hookData), and for each intent
|
||||
// submits — to the dexvm's dex.submitTx RPC — an ImportTx (consume the staged C->D atomic
|
||||
// object, fund the D order) plus a settling RelayOrderTx (clob_submit, collateral-bound).
|
||||
// The dexvm proposer relays the order once and exports a D->C proceeds object; the keeper
|
||||
// then drives the Phase-B settlement swap (DS01 hookData) that consumes it and emits
|
||||
// DEXFill. The keeper holds NO custody authority — value is locked in shared memory by C
|
||||
// before the keeper sees it, and the D->C object pays its recorded owner regardless.
|
||||
//
|
||||
// Modes:
|
||||
// - seed : seed maker liquidity on the venue (one resting ask) and exit.
|
||||
// - phase-a : seed liquidity + send ONE Phase-A intent swap from the maker; print the
|
||||
// derived intentID + C tx hash and exit. (The watch loop, run separately or
|
||||
// via -mode watch, then drives it to settlement.)
|
||||
// - watch : run the keeper loop, watching IntentSubmitted and driving each intent.
|
||||
//
|
||||
// The maker key is derived from the BIP-39 mnemonic in LUX_MNEMONIC at m/44'/9000'/0'/0/i,
|
||||
// scanning for the index whose EVM address equals -maker (a KMS-synced secret supplies the
|
||||
// mnemonic at deploy).
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// LUX_MNEMONIC="..." dexkeeper \
|
||||
// -mode watch \
|
||||
// -c-rpc https://api.lux-dev.network/ext/bc/C/rpc \
|
||||
// -d-rpc http://luxd-0.luxd-headless.lux-devnet.svc.cluster.local:9650/ext/bc/<D>/rpc \
|
||||
// -d-chain-id <D> \
|
||||
// -venue dchain-venue.lux-devnet.svc.cluster.local:9099 \
|
||||
// -maker 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/log"
|
||||
)
|
||||
|
||||
// defaultMaker is the devnet treasury / maker address (genesis.TreasuryAddress).
|
||||
const defaultMaker = "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714"
|
||||
|
||||
func main() {
|
||||
var (
|
||||
mode = flag.String("mode", "watch", "watch | phase-a | seed")
|
||||
cRPC = flag.String("c-rpc", "", "C-Chain EVM JSON-RPC (e.g. https://api.lux-dev.network/ext/bc/C/rpc)")
|
||||
dRPC = flag.String("d-rpc", "", "dexvm RPC base URL (…/ext/bc/<D-chain-id>/rpc)")
|
||||
dChainID = flag.String("d-chain-id", "", "D-Chain (dexvm) blockchain id (CB58)")
|
||||
venueAddr = flag.String("venue", "", "D-Chain venue ZAP address (host:port) for seeding maker liquidity")
|
||||
makerHex = flag.String("maker", defaultMaker, "maker EVM address to derive from LUX_MNEMONIC")
|
||||
chainID = flag.Int64("chain-id", 0, "C-Chain EVM chainID (queried via eth_chainId if 0)")
|
||||
networkID = flag.Uint("network-id", 0, "Lux primary network id for intent-id derivation (defaults to chain-id)")
|
||||
symbol = flag.String("symbol", "LUX/LUSD", "market symbol to seed/route (seed + phase-a modes)")
|
||||
amountIn = flag.Uint64("amount-in", 1, "Phase-A taker input amount (integer asset units)")
|
||||
askPrice = flag.Float64("ask-price", 10.0, "maker ask price to seed (quote per base)")
|
||||
askSize = flag.Float64("ask-size", 1.0, "maker ask size to seed (base units)")
|
||||
fromBlock = flag.Uint64("from-block", 0, "watch: first C-Chain block to scan (0 = current head)")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
mnemonic := os.Getenv(genesis.MnemonicEnvVar)
|
||||
if mnemonic == "" {
|
||||
fatalf("%s not set (the maker key is derived from it; a KMS-synced secret supplies it at deploy)", genesis.MnemonicEnvVar)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
switch *mode {
|
||||
case "seed":
|
||||
runSeed(ctx, *venueAddr, *symbol, *askPrice, *askSize)
|
||||
case "phase-a":
|
||||
runPhaseA(ctx, mnemonic, keeperFlags(cRPC, dRPC, dChainID, venueAddr, makerHex, chainID, networkID), *venueAddr, *symbol, *amountIn, *askPrice, *askSize)
|
||||
case "watch":
|
||||
runWatch(ctx, mnemonic, keeperFlags(cRPC, dRPC, dChainID, venueAddr, makerHex, chainID, networkID), *fromBlock)
|
||||
default:
|
||||
fatalf("unknown -mode %q (want watch | phase-a | seed)", *mode)
|
||||
}
|
||||
}
|
||||
|
||||
// keeperFlags assembles the resolved keeperConfig from the raw flags, validating the
|
||||
// required ones. ChainID stays nil when -chain-id is 0 so newKeeper queries eth_chainId.
|
||||
func keeperFlags(cRPC, dRPC, dChainID, venueAddr, makerHex *string, chainID *int64, networkID *uint) keeperConfig {
|
||||
if *cRPC == "" {
|
||||
fatalf("-c-rpc required")
|
||||
}
|
||||
if *dRPC == "" {
|
||||
fatalf("-d-rpc required")
|
||||
}
|
||||
if *dChainID == "" {
|
||||
fatalf("-d-chain-id required")
|
||||
}
|
||||
dID, err := ids.FromString(*dChainID)
|
||||
if err != nil {
|
||||
fatalf("invalid -d-chain-id %q: %v", *dChainID, err)
|
||||
}
|
||||
cfg := keeperConfig{
|
||||
CChainRPC: *cRPC,
|
||||
DRPC: *dRPC,
|
||||
DChainID: dID,
|
||||
VenueAddr: *venueAddr,
|
||||
Maker: mustAddress(*makerHex),
|
||||
NetworkID: uint32(*networkID),
|
||||
}
|
||||
if *chainID != 0 {
|
||||
cfg.ChainID = big.NewInt(*chainID)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// runSeed seeds one maker book on the venue and exits.
|
||||
func runSeed(ctx context.Context, venueAddr, symbol string, askPrice, askSize float64) {
|
||||
if venueAddr == "" {
|
||||
fatalf("-venue required for seed mode")
|
||||
}
|
||||
cli, closeCli, err := dialVenue(ctx, venueAddr)
|
||||
if err != nil {
|
||||
fatalf("dial venue: %v", err)
|
||||
}
|
||||
defer closeCli()
|
||||
spec := seedSpecFor(symbol, "mk", askPrice, askSize)
|
||||
pool, err := cli.seedMakerLiquidity(spec)
|
||||
if err != nil {
|
||||
fatalf("seed maker liquidity: %v", err)
|
||||
}
|
||||
log.Info("seeded maker liquidity", "symbol", symbol, "pool", fmt.Sprintf("%x", pool),
|
||||
"maker", spec.Maker, "ask", fmt.Sprintf("%g@%g", askSize, askPrice))
|
||||
}
|
||||
|
||||
// runPhaseA seeds liquidity, then sends ONE Phase-A intent swap from the maker and prints
|
||||
// the derived intentID + C tx hash.
|
||||
func runPhaseA(ctx context.Context, mnemonic string, cfg keeperConfig, venueAddr, symbol string, amountIn uint64, askPrice, askSize float64) {
|
||||
if venueAddr == "" {
|
||||
fatalf("-venue required for phase-a mode (to seed the book the intent fills against)")
|
||||
}
|
||||
cli, closeCli, err := dialVenue(ctx, venueAddr)
|
||||
if err != nil {
|
||||
fatalf("dial venue: %v", err)
|
||||
}
|
||||
spec := seedSpecFor(symbol, "mk", askPrice, askSize)
|
||||
if _, err := cli.seedMakerLiquidity(spec); err != nil {
|
||||
closeCli()
|
||||
fatalf("seed maker liquidity: %v", err)
|
||||
}
|
||||
closeCli()
|
||||
|
||||
k, err := newKeeper(ctx, cfg, mnemonic)
|
||||
if err != nil {
|
||||
fatalf("new keeper: %v", err)
|
||||
}
|
||||
log.Info("maker key derived", "address", k.maker.Address, "index", k.maker.Index, "chainID", k.cfg.ChainID)
|
||||
|
||||
pool := poolIDForSymbol(symbol)
|
||||
marketID := pool // identity market on the devnet proof
|
||||
assetIn := spec.Quote
|
||||
pk := poolKeyForMarket(marketID)
|
||||
nonce := uint64(time.Now().UnixNano())
|
||||
deadline := uint64(time.Now().Add(1 * time.Hour).Unix())
|
||||
|
||||
intentID, txHash, err := k.sendPhaseAIntent(ctx, pk, marketID, assetIn, false /*zeroForOne*/, amountIn, nonce, deadline)
|
||||
if err != nil {
|
||||
fatalf("send Phase-A intent: %v", err)
|
||||
}
|
||||
log.Info("Phase-A intent sent",
|
||||
"intentID(derived)", intentID,
|
||||
"txHash", txHash,
|
||||
"nonce", nonce,
|
||||
"amountIn", amountIn,
|
||||
"market", fmt.Sprintf("%x", marketID))
|
||||
fmt.Printf("PHASE-A: intentID(derived)=%s txHash=%s nonce=%d\n", intentID, txHash.Hex(), nonce)
|
||||
}
|
||||
|
||||
// runWatch runs the keeper loop. fromBlock 0 resolves to the current head at boot.
|
||||
func runWatch(ctx context.Context, mnemonic string, cfg keeperConfig, fromBlock uint64) {
|
||||
k, err := newKeeper(ctx, cfg, mnemonic)
|
||||
if err != nil {
|
||||
fatalf("new keeper: %v", err)
|
||||
}
|
||||
log.Info("maker key derived", "address", k.maker.Address, "index", k.maker.Index, "chainID", k.cfg.ChainID)
|
||||
if fromBlock == 0 {
|
||||
head, err := k.cc.BlockNumber(ctx)
|
||||
if err != nil {
|
||||
fatalf("BlockNumber: %v", err)
|
||||
}
|
||||
fromBlock = head
|
||||
}
|
||||
if err := k.watch(ctx, fromBlock); err != nil && ctx.Err() == nil {
|
||||
fatalf("watch loop: %v", err)
|
||||
}
|
||||
log.Info("keeper stopped")
|
||||
}
|
||||
|
||||
// seedSpecFor builds a single-ask seed spec for a symbol. base/quote follow the venue
|
||||
// convention used by cmd/fourpath-live (a "LUX"-tagged base, a "LUSD"-tagged quote), so a
|
||||
// keeper-seeded book is compatible with the proven live-venue client.
|
||||
func seedSpecFor(symbol, makerPrefix string, askPrice, askSize float64) seedSpec {
|
||||
return seedSpec{
|
||||
Symbol: symbol,
|
||||
Base: assetID(0x4c5558), // "LUX"
|
||||
Quote: assetID(0x4c55534400), // "LUSD"
|
||||
Maker: makerPrefix,
|
||||
AskSize: askSize,
|
||||
AskPrice: askPrice,
|
||||
}
|
||||
}
|
||||
|
||||
// mustAddress parses a 0x-prefixed 20-byte EVM address or exits. The maker flag is an
|
||||
// operator-supplied boundary value, so it is validated here rather than trusted.
|
||||
func mustAddress(s string) common.Address {
|
||||
if !common.IsHexAddress(s) {
|
||||
fatalf("invalid -maker address %q (want 0x + 40 hex)", s)
|
||||
}
|
||||
return common.HexToAddress(s)
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, "dexkeeper: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// maker.go derives the keeper's maker signing key from a BIP-39 mnemonic on the
|
||||
// canonical Lux BIP44 path m/44'/9000'/0'/0/i — the SAME path the genesis builder
|
||||
// (genesis.LoadBIP44WalletKeysFromMnemonic), `lux key derive`, MetaMask and the Lux
|
||||
// wallet use, so a key derived here has the identical EVM address everywhere.
|
||||
//
|
||||
// WHY REPRODUCE THE WALK INSTEAD OF IMPORTING genesis: genesis/pkg/genesis pulls the
|
||||
// full validator-identity graph (ML-DSA-65, BLS, luxfi/tls) to derive NODE identities.
|
||||
// The keeper needs only a user SPENDING key (secp256k1 -> EVM address). This is a small,
|
||||
// frozen, standard BIP44 walk over the SAME primitives (go-bip39 + go-bip32 + secp256k1
|
||||
// + keccak) the genesis builder uses; maker_test pins the derived address against the
|
||||
// known treasury address so a one-sided drift from the canonical path trips a red test.
|
||||
// (Same "three homes" reproduction discipline dexsession uses for the 0x9999 calldata.)
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/crypto"
|
||||
"github.com/luxfi/crypto/keccak256"
|
||||
"github.com/luxfi/geth/common"
|
||||
bip32 "github.com/luxfi/go-bip32"
|
||||
bip39 "github.com/luxfi/go-bip39"
|
||||
)
|
||||
|
||||
// luxCoinType is the BIP44 coin type Lux uses by canonical convention (the 9000' in
|
||||
// m/44'/9000'/0'/0/i). Every BIP44-conformant wallet using that coin type derives the
|
||||
// same key set from the same mnemonic.
|
||||
const luxCoinType = 9000
|
||||
|
||||
// makerKey is a single derived maker identity: the STANDARD EVM address (keccak256(X||Y)
|
||||
// [12:], the C tx-caller / escrow-owner format) plus the ecdsa signing key the 0x9999
|
||||
// C-Chain Phase-A/B swaps are geth-signed with.
|
||||
//
|
||||
// There is intentionally NO secp256k1.PrivateKey wrapper here: the dexvm settling
|
||||
// RelayOrderTx is sent UNSIGNED (its From is the escrow owner — see keeper.go), so the
|
||||
// keeper never invokes secp256k1.PrivateKey.EVMAddress(), which is NON-standard (it hashes
|
||||
// the COMPRESSED pubkey, giving an address that does not equal the standard escrow owner).
|
||||
type makerKey struct {
|
||||
Index int
|
||||
Address common.Address
|
||||
// ecdsa is the standard ecdsa.PrivateKey used by geth types.SignTx for the C-Chain
|
||||
// Phase-A (intent) and Phase-B (settlement) swap transactions.
|
||||
ecdsa *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// deriveMakerKey scans m/44'/9000'/0'/0/i for i in [0, scan) and returns the first key
|
||||
// whose EVM address equals want. scan bounds the search (the canonical funded set is the
|
||||
// first 1000 wallet keys). A nil want is invalid — the caller always knows the maker
|
||||
// address it is driving.
|
||||
func deriveMakerKey(mnemonic string, want common.Address, scan int) (*makerKey, error) {
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
return nil, fmt.Errorf("invalid mnemonic")
|
||||
}
|
||||
change, err := bip44ChangeNode(mnemonic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < scan; i++ {
|
||||
child, err := change.NewChildKey(uint32(i)) //nolint:gosec // i < scan, bounded
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive child %d: %w", i, err)
|
||||
}
|
||||
mk, err := makerKeyFromBytes(child.Key, i)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("key %d: %w", i, err)
|
||||
}
|
||||
if mk.Address == want {
|
||||
return mk, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no key in m/44'/%d'/0'/0/0..%d derives address %s", luxCoinType, scan, want)
|
||||
}
|
||||
|
||||
// bip44ChangeNode walks the hardened prefix m/44'/9000'/0'/0 once; the caller then
|
||||
// derives the non-hardened account index i off the returned change node. This is the
|
||||
// EXACT prefix genesis.LoadBIP44WalletKeysFromMnemonic walks.
|
||||
func bip44ChangeNode(mnemonic string) (*bip32.Key, error) {
|
||||
seed := bip39.NewSeed(mnemonic, "")
|
||||
master, err := bip32.NewMasterKey(seed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("master key: %w", err)
|
||||
}
|
||||
purpose, err := master.NewChildKey(bip32.FirstHardenedChild + 44)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive purpose 44': %w", err)
|
||||
}
|
||||
coin, err := purpose.NewChildKey(bip32.FirstHardenedChild + luxCoinType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive coin %d': %w", luxCoinType, err)
|
||||
}
|
||||
account, err := coin.NewChildKey(bip32.FirstHardenedChild + 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive account 0': %w", err)
|
||||
}
|
||||
change, err := account.NewChildKey(0) // non-hardened external chain
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive change 0: %w", err)
|
||||
}
|
||||
return change, nil
|
||||
}
|
||||
|
||||
// makerKeyFromBytes builds the ecdsa key + standard EVM address from raw 32-byte private
|
||||
// key material. The EVM address is keccak256(pubX||pubY)[12:] — identical to
|
||||
// genesis.keccakAddr and to geth crypto.PubkeyToAddress (the escrow-owner format).
|
||||
func makerKeyFromBytes(raw []byte, index int) (*makerKey, error) {
|
||||
ec, err := crypto.ToECDSA(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ecdsa: %w", err)
|
||||
}
|
||||
return &makerKey{
|
||||
Index: index,
|
||||
Address: evmAddress(ec),
|
||||
ecdsa: ec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// evmAddress derives the 20-byte EVM address from an ecdsa public key as
|
||||
// keccak256(X||Y)[12:]. Matches genesis.keccakAddr byte-for-byte (the funded-address
|
||||
// contract) — both fill X and Y big-endian into a 64-byte buffer before hashing.
|
||||
func evmAddress(key *ecdsa.PrivateKey) common.Address {
|
||||
buf := make([]byte, 64)
|
||||
key.PublicKey.X.FillBytes(buf[:32])
|
||||
key.PublicKey.Y.FillBytes(buf[32:])
|
||||
h := keccak256.Sum(buf)
|
||||
var a common.Address
|
||||
copy(a[:], h[12:])
|
||||
return a
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// venue.go is the keeper's direct ZAP client to the live D-Chain venue (cmd/dvenue),
|
||||
// used to SEED maker liquidity so a Phase-A taker order routed through the seam has a
|
||||
// resting book to fill against. It reuses the exact clob_* wire + custody-seal polling
|
||||
// patterns from cmd/fourpath-live (the proven live-venue client): ensure/open a market,
|
||||
// deposit maker inventory, rest a maker ask, and observe balances/depth until sealed.
|
||||
//
|
||||
// This is ONLY the seeding side. The taker order itself does NOT go to the venue directly
|
||||
// from the keeper — it rides the C->D atomic seam: a Phase-A swap to 0x9999 stages a C->D
|
||||
// object, the keeper funds the D order via ImportTx + a settling RelayOrderTx submitted to
|
||||
// the dexvm (keeper.go), and the dexvm proposer relays that order to THIS venue once.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/dex/pkg/zapwire"
|
||||
"github.com/luxfi/rpc"
|
||||
)
|
||||
|
||||
// read-only observation methods served by cmd/dvenue (same as fourpath-live).
|
||||
const (
|
||||
clobDepthMethod = "clob_depth"
|
||||
clobBalanceMethod = "clob_balance"
|
||||
)
|
||||
|
||||
// txDepositTag is dchain.TxDeposit's wire tag, mixed into the content ref so deposits are
|
||||
// exactly-once on retry. Kept in sync with pkg/dchain tx kinds.
|
||||
const txDepositTag = 1
|
||||
|
||||
// venueClient is a thin live ZAP client over rpc.ZAPDial.
|
||||
type venueClient struct {
|
||||
conn *rpc.ZAPConn
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// dialVenue opens a ZAP connection to the venue at addr (host:port). The returned close
|
||||
// func releases the connection and the dial context.
|
||||
func dialVenue(ctx context.Context, addr string) (*venueClient, func(), error) {
|
||||
dctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
conn, err := rpc.ZAPDial(dctx, addr)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, nil, fmt.Errorf("ZAPDial(%s): %w", addr, err)
|
||||
}
|
||||
return &venueClient{conn: conn, ctx: dctx}, func() { _ = conn.Close(); cancel() }, nil
|
||||
}
|
||||
|
||||
// poolIDForSymbol mirrors pkg/dchain.poolIDForSymbol: symbol bytes, high byte tagged 0xD0
|
||||
// so an all-symbol-prefix pool can't collide with a zero id.
|
||||
func poolIDForSymbol(sym string) [32]byte {
|
||||
var p [32]byte
|
||||
copy(p[:], sym)
|
||||
p[31] = 0xD0
|
||||
return p
|
||||
}
|
||||
|
||||
// assetID mirrors pkg/dchain.a32: a label packed big-endian into the low 8 bytes.
|
||||
func assetID(label uint64) [32]byte {
|
||||
var a [32]byte
|
||||
binary.BigEndian.PutUint64(a[24:32], label)
|
||||
return a
|
||||
}
|
||||
|
||||
// contentRef mirrors pkg/dchain.contentRef so deposits are exactly-once on retry and
|
||||
// genuinely distinct otherwise.
|
||||
func contentRef(typ byte, user string, asset [32]byte, amount uint64) [32]byte {
|
||||
var ref [32]byte
|
||||
copy(ref[:], asset[:])
|
||||
ref[0] ^= typ
|
||||
for i := 0; i < len(user) && i+1 < 24; i++ {
|
||||
ref[1+i] ^= user[i]
|
||||
}
|
||||
binary.BigEndian.PutUint64(ref[24:32], binary.BigEndian.Uint64(asset[24:32])^amount)
|
||||
return ref
|
||||
}
|
||||
|
||||
func (c *venueClient) ensureMarket(pool [32]byte) error {
|
||||
if _, err := c.conn.Call(c.ctx, zapwire.MethodEnsureMarket, zapwire.EncodeEnsureMarket(pool)); err != nil {
|
||||
return fmt.Errorf("clob_ensure_market: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *venueClient) openMarket(pool, base, quote [32]byte) error {
|
||||
resp, err := c.conn.Call(c.ctx, zapwire.MethodOpenMarket, zapwire.EncodeOpenMarket(pool, base, quote))
|
||||
if err != nil {
|
||||
return fmt.Errorf("clob_open_market: %w", err)
|
||||
}
|
||||
if len(resp) < 9 || resp[8] != zapwire.StatusPlaced {
|
||||
return fmt.Errorf("clob_open_market not placed: resp=%x", resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deposit credits user's available balance for asset and waits for the sealer to credit it.
|
||||
// The live venue ACKs StatusPlaced (accepted) but credits when the block seals, so it
|
||||
// confirms via clob_balance rather than a synchronous echo (mirrors fourpath-live.deposit).
|
||||
func (c *venueClient) deposit(user string, asset [32]byte, amount uint64) error {
|
||||
ref := contentRef(byte(txDepositTag), user, asset, amount)
|
||||
resp, err := c.conn.Call(c.ctx, zapwire.MethodDeposit, zapwire.EncodeDeposit(user, asset, amount, ref))
|
||||
if err != nil {
|
||||
return fmt.Errorf("clob_deposit %s %d: %w", user, amount, err)
|
||||
}
|
||||
if status, _, derr := zapwire.DecodeBalanceResp(resp); derr == nil && status == zapwire.StatusRejected {
|
||||
return fmt.Errorf("clob_deposit %s %d: REJECTED", user, amount)
|
||||
}
|
||||
return c.waitAvailable(user, asset, amount)
|
||||
}
|
||||
|
||||
// place rests a limit order (e.g. a maker ask) and confirms it was accepted.
|
||||
func (c *venueClient) place(pool [32]byte, side uint8, price, size float64, user string) error {
|
||||
resp, err := c.conn.Call(c.ctx, zapwire.MethodPlace, zapwire.EncodePlace(pool, side, price, size, user))
|
||||
if err != nil {
|
||||
return fmt.Errorf("clob_place %s: %w", user, err)
|
||||
}
|
||||
if _, status, derr := zapwire.DecodeAck(resp); derr != nil || status != zapwire.StatusPlaced {
|
||||
return fmt.Errorf("clob_place %s: status=%d err=%v", user, status, derr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// balance reads clob_balance: (available, locked).
|
||||
func (c *venueClient) balance(user string, asset [32]byte) (available, locked uint64, err error) {
|
||||
req := make([]byte, zapwire.UserSize+zapwire.AssetIDSize)
|
||||
copy(req[0:zapwire.UserSize], user)
|
||||
copy(req[zapwire.UserSize:], asset[:])
|
||||
resp, err := c.conn.Call(c.ctx, clobBalanceMethod, req)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("clob_balance %s: %w", user, err)
|
||||
}
|
||||
if len(resp) < 16 {
|
||||
return 0, 0, fmt.Errorf("clob_balance short resp: %x", resp)
|
||||
}
|
||||
return binary.BigEndian.Uint64(resp[0:8]), binary.BigEndian.Uint64(resp[8:16]), nil
|
||||
}
|
||||
|
||||
// depth reads clob_depth: (orders, remaining, ok).
|
||||
func (c *venueClient) depth(pool [32]byte) (orders int, remaining float64, ok bool, err error) {
|
||||
resp, err := c.conn.Call(c.ctx, clobDepthMethod, pool[:])
|
||||
if err != nil {
|
||||
return 0, 0, false, fmt.Errorf("clob_depth: %w", err)
|
||||
}
|
||||
if len(resp) < 29 {
|
||||
return 0, 0, false, fmt.Errorf("clob_depth short resp: %x", resp)
|
||||
}
|
||||
return int(binary.BigEndian.Uint32(resp[0:4])), math.Float64frombits(binary.BigEndian.Uint64(resp[4:12])), resp[28] == 1, nil
|
||||
}
|
||||
|
||||
// waitAvailable polls clob_balance until available >= want (the credit has sealed) or
|
||||
// times out.
|
||||
func (c *venueClient) waitAvailable(user string, asset [32]byte, want uint64) error {
|
||||
deadline := time.Now().Add(20 * time.Second)
|
||||
for {
|
||||
a, _, err := c.balance(user, asset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a >= want {
|
||||
return nil
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
a, lck, _ := c.balance(user, asset)
|
||||
return fmt.Errorf("clob_deposit/credit %s: available=%d locked=%d, want >=%d (sealer not crediting)", user, a, lck, want)
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// waitDepth polls until the book's order count and remaining match (the rested ask sealed).
|
||||
func (c *venueClient) waitDepth(pool [32]byte, wantOrders int, wantRem float64) error {
|
||||
deadline := time.Now().Add(20 * time.Second)
|
||||
for {
|
||||
n, rem, ok, err := c.depth(pool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok && n == wantOrders && rem == wantRem {
|
||||
return nil
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
return fmt.Errorf("book orders=%d remaining=%v ok=%v, want %d / %v", n, rem, ok, wantOrders, wantRem)
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// seedSpec describes one maker book to seed: a market (base/quote) with a single resting
|
||||
// maker ask of askSize at askPrice. The keeper seeds this so a Phase-A taker BUY routed
|
||||
// through the seam fills exactly one ask.
|
||||
type seedSpec struct {
|
||||
Symbol string
|
||||
Base [32]byte
|
||||
Quote [32]byte
|
||||
Maker string
|
||||
AskSize float64
|
||||
AskPrice float64
|
||||
}
|
||||
|
||||
// seedMakerLiquidity ensures+opens the market, deposits the maker's base inventory, rests
|
||||
// one maker ask, and waits for both the deposit credit and the resting ask to seal. It is
|
||||
// idempotent on retry via content-addressed deposit refs (a re-run with the same spec does
|
||||
// not double-credit). Returns the pool id of the seeded market.
|
||||
func (c *venueClient) seedMakerLiquidity(s seedSpec) ([32]byte, error) {
|
||||
pool := poolIDForSymbol(s.Symbol)
|
||||
if err := c.ensureMarket(pool); err != nil {
|
||||
return pool, err
|
||||
}
|
||||
if err := c.openMarket(pool, s.Base, s.Quote); err != nil {
|
||||
return pool, err
|
||||
}
|
||||
// Maker funds AskSize of base inventory so the resting ask is fully collateralised.
|
||||
makerBase := uint64(math.Ceil(s.AskSize))
|
||||
if err := c.deposit(s.Maker, s.Base, makerBase); err != nil {
|
||||
return pool, err
|
||||
}
|
||||
if err := c.place(pool, zapwire.SideSell, s.AskPrice, s.AskSize, s.Maker); err != nil {
|
||||
return pool, err
|
||||
}
|
||||
if err := c.waitDepth(pool, 1, s.AskSize); err != nil {
|
||||
return pool, err
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
@@ -5,16 +5,21 @@ go 1.26.4
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
|
||||
github.com/luxfi/chains v1.3.15
|
||||
github.com/luxfi/consensus v1.25.18
|
||||
github.com/luxfi/crypto v1.19.21
|
||||
github.com/luxfi/database v1.19.0
|
||||
github.com/luxfi/geth v1.17.11
|
||||
github.com/luxfi/database v1.20.3
|
||||
github.com/luxfi/genesis v1.13.8
|
||||
github.com/luxfi/geth v1.17.12
|
||||
github.com/luxfi/go-bip32 v1.0.2
|
||||
github.com/luxfi/go-bip39 v1.1.2
|
||||
github.com/luxfi/ids v1.2.15
|
||||
github.com/luxfi/log v1.4.3
|
||||
github.com/luxfi/pq v1.0.3
|
||||
github.com/luxfi/rpc v1.1.0
|
||||
github.com/luxfi/version v1.0.1
|
||||
github.com/luxfi/vm v1.2.4
|
||||
github.com/luxfi/vm v1.2.5
|
||||
github.com/luxfi/zap v0.8.9
|
||||
github.com/nats-io/nats.go v1.52.0
|
||||
github.com/shopspring/decimal v1.4.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
@@ -26,8 +31,28 @@ require (
|
||||
filippo.io/hpke v0.4.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
@@ -55,6 +80,7 @@ require (
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/gorilla/rpc v1.2.1 // indirect
|
||||
github.com/grandcat/zeroconf v1.0.0 // indirect
|
||||
github.com/hanzoai/vfs v0.4.3 // indirect
|
||||
github.com/hanzos3/go-sdk v1.0.2 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
@@ -63,6 +89,7 @@ require (
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/luxfi/accel v1.2.4 // indirect
|
||||
github.com/luxfi/address v1.0.1 // indirect
|
||||
github.com/luxfi/age v1.5.0 // indirect
|
||||
github.com/luxfi/api v1.0.15 // indirect
|
||||
github.com/luxfi/atomic v1.0.0 // indirect
|
||||
@@ -76,18 +103,18 @@ require (
|
||||
github.com/luxfi/math v1.4.1 // indirect
|
||||
github.com/luxfi/math/big v0.1.0 // indirect
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/metric v1.5.8 // indirect
|
||||
github.com/luxfi/metric v1.5.9 // indirect
|
||||
github.com/luxfi/mock v0.1.1 // indirect
|
||||
github.com/luxfi/p2p v1.21.1 // indirect
|
||||
github.com/luxfi/proto v1.3.4 // indirect
|
||||
github.com/luxfi/proto v1.3.5 // indirect
|
||||
github.com/luxfi/resource v0.0.1 // indirect
|
||||
github.com/luxfi/runtime v1.1.1 // indirect
|
||||
github.com/luxfi/sampler v1.1.0 // indirect
|
||||
github.com/luxfi/tls v1.0.3 // indirect
|
||||
github.com/luxfi/upgrade v1.0.1 // indirect
|
||||
github.com/luxfi/utils v1.2.0 // indirect
|
||||
github.com/luxfi/validators v1.2.0 // indirect
|
||||
github.com/luxfi/warp v1.19.3 // indirect
|
||||
github.com/luxfi/zap v0.7.2 // indirect
|
||||
github.com/luxfi/warp v1.19.5 // indirect
|
||||
github.com/luxfi/zapdb v1.10.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
@@ -109,16 +136,16 @@ require (
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/tklauser/go-sysconf v0.4.0 // indirect
|
||||
github.com/tklauser/numcpus v0.12.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.9 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
|
||||
@@ -8,12 +8,72 @@ github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.13 h1:5KgbxMaS2coSWRrx9TX/QtWbqzgQkOdEa3sZPhBhCSg=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.13/go.mod h1:8zz7wedqtCbw5e9Mi2doEwDyEgHcEE9YOJp6a8jdSMY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.13 h1:mA59E3fokBvyEGHKFdnpNNrvaR351cqiHgRg+JzOSRI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.13/go.mod h1:yoTXOQKea18nrM69wGF9jBdG4WocSZA1h38A+t/MAsk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 h1:GcLE9ba5ehAQma6wlopUesYg/hbcOhFNWTjELkiWkh4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.14/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 h1:mP49nTpfKtpXLt5SLn8Uv8z6W+03jYVoOSAl/c02nog=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
|
||||
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
|
||||
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
@@ -44,6 +104,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc=
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -52,10 +113,13 @@ github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
|
||||
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
|
||||
github.com/deckarep/golang-set/v2 v2.9.0 h1:prva4eP9UysWagLyKrtn074ughi0NnkIf0A4M5yOCKI=
|
||||
github.com/deckarep/golang-set/v2 v2.9.0/go.mod h1:EWknQXbs0mcFpat2QOoXV0Ee57cD+w6ZEN76BR2JVrM=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
@@ -123,10 +187,13 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/hanzoai/vfs v0.4.3 h1:QN9SemEQBq9x1l/toi51/TZWctbt3i3mgUJfz5RPALY=
|
||||
github.com/hanzoai/vfs v0.4.3/go.mod h1:wTHfTpJ/165yz0qfPBNFcYRg+tGw8YDwPu+xgps88zU=
|
||||
github.com/hanzos3/go-sdk v1.0.2 h1:EOJQGVnwclkzIyRJyWqtqmA2muyaSsF4y+7KYC4Vhdw=
|
||||
github.com/hanzos3/go-sdk v1.0.2/go.mod h1:oOp/rYVDpZIv2Mn3ZacAUiIahRNOi+V/vlGukvIcuI0=
|
||||
github.com/hashicorp/go-bexpr v0.1.16 h1:D+fKoGyUzXVS0FdjOX1ws3vIck8DVtBqQ0tsusmYDR8=
|
||||
@@ -143,8 +210,12 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -172,30 +243,38 @@ github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
|
||||
github.com/luxfi/atomic v1.0.0/go.mod h1:0G2mTlQ6TXWHICUHrUUPu1/qAiIyR4gSZ2tva9ci/bI=
|
||||
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/chains v1.3.15 h1:eiADjgmatIxDy36RMmQYp91VXU8OXwtD3KF5/JSbzN0=
|
||||
github.com/luxfi/chains v1.3.15/go.mod h1:lFwRZno8sDOfLuWyCi1rMxum9DPFn6aqupEEpIat9Mk=
|
||||
github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
|
||||
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
|
||||
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
|
||||
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
|
||||
github.com/luxfi/consensus v1.25.18 h1:2hOpUT26IrNUDYSGjUGW+xS0J7wkVKNl+diS64Pjwhw=
|
||||
github.com/luxfi/consensus v1.25.18/go.mod h1:A8xmlg2Lfp2g1ddIYpArHytFGmTxcZnCUz0ZB17XKEg=
|
||||
github.com/luxfi/consensus v1.25.18 h1:wegSPk1VyjV9k2J+IZ6cJf6flqiMhzbOuFidaQ31b24=
|
||||
github.com/luxfi/consensus v1.25.18/go.mod h1:cNhLp215lFwiOPxdj8I+6JBLz3/vDRMV2vqLigulv04=
|
||||
github.com/luxfi/constants v1.5.8 h1:iNP9AWNUcM4Tps7jYnx49CwtCWAC9mYRxJfGou2za0g=
|
||||
github.com/luxfi/constants v1.5.8/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
|
||||
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
|
||||
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
|
||||
github.com/luxfi/corona v0.7.6 h1:CJP6smygD55dL0HHkKkWryL9H24a+wXvs+L+WchK7Nc=
|
||||
github.com/luxfi/corona v0.7.6/go.mod h1:4aD7+ZqnlZ2aVuU/DBQ5aspIagv5ux45LW2sJ4+siY8=
|
||||
github.com/luxfi/corona v0.7.9 h1:NQe9V/80CdKLvbaVRE2uepxvxg9KHbWfcGRKWrzLSHc=
|
||||
github.com/luxfi/corona v0.7.9/go.mod h1:SfS7xo/k4uoteEYwYy+QCMPzTU8EIEbLnbWKx5ENVCw=
|
||||
github.com/luxfi/crypto v1.19.21 h1:x7s/Yy1BYMv5sbbWsZk+mUU007Z0HX/Ta4/RkrNHiw0=
|
||||
github.com/luxfi/crypto v1.19.21/go.mod h1:0tfz+EbAjsW1QBWB0cte9kdjB5XhhYFmCr8BkZRux48=
|
||||
github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xfU=
|
||||
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
|
||||
github.com/luxfi/database v1.19.0 h1:sySH5eS8ihJtyc+yb0LgTignGwJ9H4W9bv+nboCuIm0=
|
||||
github.com/luxfi/database v1.19.0/go.mod h1:o1ccjgVKMYbVBfi+6+sDvx4swGFgGnCGFQ4gL/oJ+kY=
|
||||
github.com/luxfi/database v1.20.3 h1:fzBfd3bCGiUAlLAaMtMTB4aX78tNTSo4Z0kOA14LtAc=
|
||||
github.com/luxfi/database v1.20.3/go.mod h1:S/LvmfzNYWVNslcEcZwDrntqUO2ksaL8ql1nRmLUA/Q=
|
||||
github.com/luxfi/filesystem v0.0.1 h1:VZ6xMFKaAPBW/ddlMsDnI2G0VU1lV5rYaVcW5d+KwEY=
|
||||
github.com/luxfi/filesystem v0.0.1/go.mod h1:OQVSU6XNwqrr1AI+MqkID2taHUclx7NYmmr3svgttec=
|
||||
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/geth v1.17.11 h1:8hkMHTpd1Wz/P8KAiZLtw9y/KOFux/Wja88kcAaEo1Q=
|
||||
github.com/luxfi/geth v1.17.11/go.mod h1:EZPzzYoH+Io08/8+0DJCvH+53jnoi3zKsEWAxwzlOL0=
|
||||
github.com/luxfi/genesis v1.13.8 h1:oddTmow0gEX/q/FrLQv77jXuSePCFRyMUvT9Pr3WnCw=
|
||||
github.com/luxfi/genesis v1.13.8/go.mod h1:iwXcnFHY997cWUKzyP4SCBl7o493SYHQniy3QViZOAg=
|
||||
github.com/luxfi/geth v1.17.12 h1:UP/fhpcfbGPTrkOCwX3d88Oc3jVm5gTOgfjgq+lek6s=
|
||||
github.com/luxfi/geth v1.17.12/go.mod h1:3vQfQJd9JC+AVBjxNXa9PYQOqpbE2dKu8E3jqhPZ3LU=
|
||||
github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
|
||||
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/ids v1.2.15 h1:omE+E4+0Poj9DzM11ejSFgteaSQ3KDHi5g54iH6jcxI=
|
||||
github.com/luxfi/ids v1.2.15/go.mod h1:Fj73K5xcblvdE0SxU/ip+jE8VqNdu+80548su5KJ7xI=
|
||||
github.com/luxfi/keychain v1.0.2 h1:uQgmjs37/VBIALEiYrrszTpxvtqr07/YvS9TnmxGafs=
|
||||
@@ -210,18 +289,18 @@ github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
|
||||
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
|
||||
github.com/luxfi/mdns v0.1.1 h1:g2eRr9AXcziPkkcd24M+Qu9ApEpoKKjfI79QSNqv0rQ=
|
||||
github.com/luxfi/mdns v0.1.1/go.mod h1:dbp5f3h3aE7CGzwbaWzBM9cwdcekhmSrWhQevgYhhNA=
|
||||
github.com/luxfi/metric v1.5.8 h1:axPwfq+erOlIue7IJp5g+hMcMtVhYHja9gJAaT3+KNA=
|
||||
github.com/luxfi/metric v1.5.8/go.mod h1:fO2giazkg4NDtr72JM/QXJBYebplAMeWC1JoZyNDvKw=
|
||||
github.com/luxfi/metric v1.5.9 h1:UAgXMNZf5oN/XJwwuKorf8iMaCj3nyP6thHPCwkUwY4=
|
||||
github.com/luxfi/metric v1.5.9/go.mod h1:ux+w3RZQCfF1zM8MO0wAWyNj/CsDlPd2mwTGshB9vY0=
|
||||
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/p2p v1.21.1 h1:gmz1JMDhzHIL3dQlhwIDvR4OlFuhNVfnWUl/ipYhAIo=
|
||||
github.com/luxfi/p2p v1.21.1/go.mod h1:SsNPR5fPGWWNem9plGWhSmRqyDoysJ3kPAN0zG0g3iw=
|
||||
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
|
||||
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/precompile v0.5.44 h1:r5g98nYTwHX0v7mOpico8Lksc/MInSHEaK/ST+QwKfc=
|
||||
github.com/luxfi/precompile v0.5.44/go.mod h1:FYfq9CT8XRmlpjFAtblHAMcpnjByuoM+YpKq/FF9QVk=
|
||||
github.com/luxfi/proto v1.3.4 h1:dNrdQ/414Pe9otIekVUmQ9QLqemaMFK43H4N3DIbuzQ=
|
||||
github.com/luxfi/proto v1.3.4/go.mod h1:S80KrTzISCktptit/pWLzk6nHgagjJS86qbQL6gHutE=
|
||||
github.com/luxfi/precompile v0.5.56 h1:ISvjhA/BpKdlF6YuhnPxMSpDgj3T8RWbgbRLWTcWON8=
|
||||
github.com/luxfi/precompile v0.5.56/go.mod h1:x5bxubHVWlan7Fyiub8fEAPjYV22Oi6yDzpX1/Wqnxs=
|
||||
github.com/luxfi/proto v1.3.5 h1:AW11rnu5xyvB7beyowoiY9uIffLOF3+eMR/a3EkK2c8=
|
||||
github.com/luxfi/proto v1.3.5/go.mod h1:ixTofGpdW1rTYr+wgTuBhAsgBv8GnWYHMLWbPNEdm7M=
|
||||
github.com/luxfi/resource v0.0.1 h1:mTh+ICWSy548GTUSSyx7V/X5dV18oEwxZeQEYGJQhD4=
|
||||
github.com/luxfi/resource v0.0.1/go.mod h1:wWpZktciYwIi6RNqA+fHwzmPrUJa7PRX7urfwT+spRE=
|
||||
github.com/luxfi/rpc v1.1.0 h1:B/PJbK399th1mHRDSufhCpVbAciZqId3LsaWhIGNWH4=
|
||||
@@ -232,6 +311,8 @@ github.com/luxfi/sampler v1.1.0 h1:u3iRDl7V06ARh0e85h3HT+aZ1saCFo2yMMsh+dCJbqk=
|
||||
github.com/luxfi/sampler v1.1.0/go.mod h1:kJa53S3tC9+VSbuV3RFu68MmbCCBlr2UM39LOClQ/Hs=
|
||||
github.com/luxfi/timer v1.0.2 h1:g/odi0VQJIsrzdklJUG1thHZ/sGNnbIiVGcU6LctJm0=
|
||||
github.com/luxfi/timer v1.0.2/go.mod h1:SoaZwntYigUE3H6z1GV32YwP8QaSiAT0UiEv7iPugXg=
|
||||
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
|
||||
github.com/luxfi/tls v1.0.3/go.mod h1:dQqSiGE7YxXUxOwICoReUuIitBms9DYOaCeteBwmIWw=
|
||||
github.com/luxfi/upgrade v1.0.1 h1:7+ygYeUf/MuLeGL7pjIu6ckQimxctCp+Swybhpy64go=
|
||||
github.com/luxfi/upgrade v1.0.1/go.mod h1:Re7g9Y+SYf/LvkHFpN0vbtlVH/Rr5ZpHQdPeVFEo3Jw=
|
||||
github.com/luxfi/utils v1.2.0 h1:gtEiI7/NM6PQ/OasEpH0PvB+e5hIS/tpum9r64pYjMc=
|
||||
@@ -242,12 +323,12 @@ github.com/luxfi/validators v1.2.0 h1:VygpiBqBAdGrfkb7xzE2yrVmnXaqE+hm8FLWdGXO7G
|
||||
github.com/luxfi/validators v1.2.0/go.mod h1:GYLulrNXAan23ZlX7sgWVbVnLpUexeB/m2qr2ymsXok=
|
||||
github.com/luxfi/version v1.0.1 h1:T/1KYWEMmsrNQk7pN7PFPAwh/7XbeX7cFAKLBqI37Sk=
|
||||
github.com/luxfi/version v1.0.1/go.mod h1:Y5fPkQ2DB0XRBCxgSPXp4ISzL1/jptKnmFknShRJCyg=
|
||||
github.com/luxfi/vm v1.2.4 h1:9sQegGsvKvEoF07SqyUdeuYfYSOLa6Hlcf3xuhFmSsc=
|
||||
github.com/luxfi/vm v1.2.4/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
|
||||
github.com/luxfi/warp v1.19.3 h1:tU6aAniYrPiRGrht7oFSwsUmk5KBb9RDFSyZq3lJ4HY=
|
||||
github.com/luxfi/warp v1.19.3/go.mod h1:kqyk7Fa5mgw1zzMLqjrZWVLuTef35CCW0gOEv10UlZk=
|
||||
github.com/luxfi/zap v0.7.2 h1:YecWTWNE5PPJXL56sLIkzS8b23bprUwZ5lPAQuLUtTE=
|
||||
github.com/luxfi/zap v0.7.2/go.mod h1:1k+nwT+JW802YzuPAuf7CxMSGr/qxvbGgGwi5k6X9Ok=
|
||||
github.com/luxfi/vm v1.2.5 h1:L1etY/gh68f9tns1BtyDUpZcBVqc3Ng1mqU3n38GyLo=
|
||||
github.com/luxfi/vm v1.2.5/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
|
||||
github.com/luxfi/warp v1.19.5 h1:vigIDV4JxLz8bLxQ97dOIcZVM9FAXHmrKdmIh+/WWvk=
|
||||
github.com/luxfi/warp v1.19.5/go.mod h1:t5upY5vhKvjapImmUsgPUlW8C8LZhvQpQg5WzAitQs0=
|
||||
github.com/luxfi/zap v0.8.9 h1:/H+EYLmHcy8EShMQ+JuGZX/JTl3u9YkGojVEhBFUv34=
|
||||
github.com/luxfi/zap v0.8.9/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
|
||||
github.com/luxfi/zapcodec v1.0.1 h1:pRxLxCOi6uihQMg8A8riDjNjefU2cXZxfRVZ+obeuL8=
|
||||
github.com/luxfi/zapcodec v1.0.1/go.mod h1:txrRt2JK4O76ssTxlXIwoNVsgzyZVL0ES4mlXqGNogs=
|
||||
github.com/luxfi/zapdb v1.10.1 h1:XV3k4UTTKKxUMgbfC7woPXgUEIJd3P5nj2lGTQ88xeE=
|
||||
@@ -285,11 +366,15 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
@@ -324,6 +409,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
|
||||
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
@@ -338,14 +425,21 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
|
||||
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
|
||||
@@ -368,16 +462,16 @@ go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -388,6 +482,7 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -400,6 +495,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -407,6 +503,7 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
@@ -429,6 +526,8 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -443,6 +542,7 @@ golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -487,10 +587,12 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
Reference in New Issue
Block a user