Files
node/wallet/network/primary/wallet.go
T
zeekayandHanzo Dev c44cc2289f platformvm builds GREEN off the codec: executor fold + full consumer flip
vms/platformvm/... build + vet PASS with pcodecs/txs.Codec gone from the VM.

Executor semantics (standard_tx_executor):
- registerOwnSet(): shared primitive — per-validator state.L1Validator with
  native owner blobs (txs.MarshalOwner/UnmarshalOwner, no codec), active-set
  capacity check, EndAccumulatedFee=balance+accruedFees, SetNetToL1Conversion
  manager-authority recording (byte-for-byte legacy tail).
- ConvertNetworkTx: promote endomorphism (owner-authorized, folds old
  ConvertNetworkToL1Tx), gated by security.Mode.Manager.
- CreateNetworkTx: base (AddNet/SetNetOwner) + sovereign path registers own set.
- Deleted CreateSovereignL1Tx + ConvertNetworkToL1Tx.
- txs.UnmarshalOwner added: canonical owner marshal/unmarshal pair, no codec.

All ~13 txs.Visitor impls carry ConvertNetworkTx; gossip/block Parse(b) codec-free;
wallet/network/primary off txs.Codec.

KNOWN GAP (pending design decision, NOT silently green): CreateNetworkTx reads
tx.Chains() only to derive managerChainID — it does NOT create the genesis
chains (no AddChain). Atomic-spawn-with-chains vs decomplect-to-CreateChainTx is
the open call. 17 codec-era _test.go parked as .bak for follow-up rewrite.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-10 14:25:08 -07:00

192 lines
6.0 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package primary
import (
"context"
gethcommon "github.com/luxfi/geth/common"
"github.com/luxfi/constants"
"github.com/luxfi/ids"
"github.com/luxfi/keychain"
"github.com/luxfi/math/set"
"github.com/luxfi/node/vms/platformvm/txs"
"github.com/luxfi/node/wallet/chain/p"
"github.com/luxfi/node/wallet/chain/x"
"github.com/luxfi/node/wallet/network/primary/common"
"github.com/luxfi/utxo/secp256k1fx"
pbuilder "github.com/luxfi/node/wallet/chain/p/builder"
psigner "github.com/luxfi/node/wallet/chain/p/signer"
xbuilder "github.com/luxfi/node/wallet/chain/x/builder"
xsigner "github.com/luxfi/node/wallet/chain/x/signer"
)
var _ Wallet = (*wallet)(nil)
// EVMKeychain is the interface for keychains that expose 20-byte
// account addresses used by EVM-runtime chains (Lux C-Chain, Liquid
// EVM, Hanzo EVM, and every EVM-compatible chain).
//
// Naming: the value IS "EVM-runtime account address". The internal
// derivation uses Keccak256 of the secp256k1 pubkey — that's HOW it's
// computed. The data model that consumes it (EVM account) is WHAT it
// is. UTXO vs EVM is the relevant axis in a multi-chain keychain.
type EVMKeychain interface {
GetByEVM(addr gethcommon.Address) (keychain.Signer, bool)
EVMAddresses() set.Set[gethcommon.Address]
}
// KeychainAdapter adapts secp256k1fx.Keychain to BOTH
// wallet/keychain.Keychain (UTXO-side) and EVMKeychain (EVM-side).
type KeychainAdapter struct {
*secp256k1fx.Keychain
}
// Addresses implements wallet/keychain.Keychain (UTXO-side).
func (kc *KeychainAdapter) Addresses() set.Set[ids.ShortID] {
return kc.Keychain.Addrs
}
// Get implements keychain.Keychain (UTXO-side lookup by ShortID).
func (kc *KeychainAdapter) Get(addr ids.ShortID) (keychain.Signer, bool) {
return kc.Keychain.Get(addr)
}
// GetByEVM implements EVMKeychain (EVM-side lookup by 20-byte addr).
func (kc *KeychainAdapter) GetByEVM(addr gethcommon.Address) (keychain.Signer, bool) {
signer, ok := kc.Keychain.GetByEVM(addr)
if !ok {
return nil, false
}
// secp256k1fx.luxSigner already implements wallet/keychain.Signer
return signer.(keychain.Signer), true
}
// EVMAddresses implements EVMKeychain (EVM-side address set).
func (kc *KeychainAdapter) EVMAddresses() set.Set[gethcommon.Address] {
return kc.Keychain.EVMAddrs
}
// NewKeychainAdapter creates a KeychainAdapter from a secp256k1fx.Keychain
func NewKeychainAdapter(kc *secp256k1fx.Keychain) *KeychainAdapter {
return &KeychainAdapter{Keychain: kc}
}
// Wallet provides chain wallets for the primary network.
// NOTE: C-Chain wallet is disabled - use github.com/luxfi/evm/ethclient directly
type Wallet interface {
P() p.Wallet
X() x.Wallet
}
type wallet struct {
p p.Wallet
x x.Wallet
}
func (w *wallet) P() p.Wallet {
return w.p
}
func (w *wallet) X() x.Wallet {
return w.x
}
// Creates a new default wallet
func NewWallet(pWallet p.Wallet, xWallet x.Wallet) Wallet {
return &wallet{
p: pWallet,
x: xWallet,
}
}
// Creates a Wallet with the given set of options
func NewWalletWithOptions(w Wallet, options ...common.Option) Wallet {
return NewWallet(
p.NewWalletWithOptions(w.P(), options...),
x.NewWalletWithOptions(w.X(), options...),
)
}
type WalletConfig struct {
// Base URI to use for all node requests.
URI string // required
// Keys to use for signing all transactions.
LUXKeychain keychain.Keychain // required
EVMKeychain EVMKeychain // optional - for future C-Chain support
// Set of P-chain transactions that the wallet should know about to be able
// to generate transactions.
PChainTxs map[ids.ID]*txs.Tx // optional
// Set of P-chain transactions that the wallet should fetch to be able to
// generate transactions.
PChainTxsToFetch set.Set[ids.ID] // optional
}
// MakeWallet returns a wallet that supports issuing transactions to the chains
// living in the primary network.
//
// On creation, the wallet attaches to the provided uri and fetches all UTXOs
// that reference any of the provided keys. If the UTXOs are modified through an
// external issuance process, such as another instance of the wallet, the UTXOs
// may become out of sync. The wallet will also fetch all requested P-chain
// transactions.
//
// The wallet manages all state locally, and performs all tx signing locally.
func MakeWallet(ctx context.Context, config *WalletConfig) (Wallet, error) {
luxAddrs := config.LUXKeychain.Addresses()
luxState, err := FetchState(ctx, config.URI, luxAddrs)
if err != nil {
return nil, err
}
// evmAddrs := config.EVMKeychain.EVMAddresses()
// evmState, err := FetchEVMState(ctx, config.URI, evmAddrs)
// if err != nil {
// return nil, err
// }
pChainTxs := config.PChainTxs
if pChainTxs == nil {
pChainTxs = make(map[ids.ID]*txs.Tx)
}
for txID := range config.PChainTxsToFetch {
txBytes, err := luxState.PClient.GetTx(ctx, txID)
if err != nil {
return nil, err
}
tx, err := txs.Parse(txBytes)
if err != nil {
return nil, err
}
pChainTxs[txID] = tx
}
pUTXOs := common.NewChainUTXOs(constants.PlatformChainID, luxState.UTXOs)
pBackend := p.NewBackend(luxState.PCTX, pUTXOs, pChainTxs)
pBuilder := pbuilder.New(luxAddrs, luxState.PCTX, pBackend)
pSigner := psigner.New(config.LUXKeychain, pBackend)
xChainID := luxState.XCTX.BlockchainID
xUTXOs := common.NewChainUTXOs(xChainID, luxState.UTXOs)
xBackend := x.NewBackend(luxState.XCTX, xUTXOs)
xBuilder := xbuilder.New(luxAddrs, luxState.XCTX, xBackend)
xSigner := xsigner.New(config.LUXKeychain, xBackend)
// cChainID := luxState.CCTX.BlockchainID
// cUTXOs := common.NewChainUTXOs(cChainID, luxState.UTXOs)
// cBackend := c.NewBackend(cUTXOs, ethState.Accounts)
// cBuilder := c.NewBuilder(luxAddrs, evmAddrs, luxState.CCTX, cBackend)
// cSigner := c.NewSigner(config.LUXKeychain, config.EVMKeychain, cBackend)
pClient := p.NewClient(luxState.PClient, pBackend)
return NewWallet(
p.NewWallet(pClient, pBuilder, pSigner),
x.NewWallet(xBuilder, xSigner, xBackend),
), nil
}