mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
External (HTTP / JSON-RPC) is the only place JSON is legitimate. Every
existing encoding/json import in node/ moves to github.com/go-json-experiment/json
(v2 root, not the v1 sub-package). NewEncoder/NewDecoder rewrite to
MarshalWrite/UnmarshalRead. MarshalIndent rewrites to Marshal with
jsontext.WithIndent. json.RawMessage rewrites to jsontext.Value.
*json.SyntaxError rewrites to *jsontext.SyntacticError.
81 files migrated. LLM.md captures the rule + v1->v2 delta table.
Known v2 semantic deltas surfaced by existing tests (followups, not regressions):
- [N]byte fields with no MarshalJSON now marshal as base64 string (v1 marshalled
as JSON array of byte numbers). Affects vms/platformvm/txs/*_test.go fixtures
with embedded BLS proofOfPossession.
- time.Duration has no v2 default representation; configs that wire-format
Duration as nanoseconds (vms/{xvm,platformvm}/config, config/spec) need to
switch to string-form Duration or carry an explicit option. v2 root does not
re-export FormatDurationAsNano.
- v2 enforces strict UTF-8 (vms/chainadapter/messaging fixture has non-UTF-8).
- json.MarshalWrite does not append a trailing '\n' (v1 NewEncoder.Encode did);
service/auth/auth_test.go expectation updated.
- nil []byte round-trips to empty (not nil); config_test deep-equal fixtures
surface this.
All affected sites are at the API boundary; ZAP wire envelope already covers
the internal data paths (state, P2P, consensus, MPC, threshold). Internal
JSON sites that should move to ZAP next (separate work):
- vms/da/store.go (DA blob/cert storage as JSON)
- vms/platformvm/airdrop (airdrop claims as JSON in db)
- vms/chainadapter/appchain (SQLite materializer schema/data blobs)
- vms/chainadapter/messaging (conversation codec)
- staking/kms.go (KMS HTTP client — external technically, leave)
- utils/{bimap,ips} (small marshaler shims — low priority)
198 lines
6.0 KiB
Go
198 lines
6.0 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package xvm
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/go-json-experiment/json"
|
|
"github.com/luxfi/address"
|
|
"github.com/luxfi/formatting"
|
|
"github.com/luxfi/ids"
|
|
lux "github.com/luxfi/utxo"
|
|
"github.com/luxfi/node/vms/components/verify"
|
|
"github.com/luxfi/node/vms/xvm/fxs"
|
|
"github.com/luxfi/node/vms/xvm/txs"
|
|
"github.com/luxfi/utils"
|
|
"github.com/luxfi/utxo/nftfx"
|
|
"github.com/luxfi/utxo/propertyfx"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
|
|
avajson "github.com/luxfi/node/utils/json"
|
|
)
|
|
|
|
var (
|
|
errUnknownAssetType = errors.New("unknown asset type")
|
|
|
|
_ lux.TransferableIn = (*secp256k1fx.TransferInput)(nil)
|
|
_ verify.State = (*secp256k1fx.MintOutput)(nil)
|
|
_ lux.TransferableOut = (*secp256k1fx.TransferOutput)(nil)
|
|
_ fxs.FxOperation = (*secp256k1fx.MintOperation)(nil)
|
|
_ verify.Verifiable = (*secp256k1fx.Credential)(nil)
|
|
|
|
_ verify.State = (*nftfx.MintOutput)(nil)
|
|
_ verify.State = (*nftfx.TransferOutput)(nil)
|
|
_ fxs.FxOperation = (*nftfx.MintOperation)(nil)
|
|
_ fxs.FxOperation = (*nftfx.TransferOperation)(nil)
|
|
_ verify.Verifiable = (*nftfx.Credential)(nil)
|
|
|
|
_ verify.State = (*propertyfx.MintOutput)(nil)
|
|
_ verify.State = (*propertyfx.OwnedOutput)(nil)
|
|
_ fxs.FxOperation = (*propertyfx.MintOperation)(nil)
|
|
_ fxs.FxOperation = (*propertyfx.BurnOperation)(nil)
|
|
_ verify.Verifiable = (*propertyfx.Credential)(nil)
|
|
)
|
|
|
|
// StaticService defines the base service for the asset vm
|
|
type StaticService struct{}
|
|
|
|
func CreateStaticService() *StaticService {
|
|
return &StaticService{}
|
|
}
|
|
|
|
// BuildGenesisArgs are arguments for BuildGenesis
|
|
type BuildGenesisArgs struct {
|
|
NetworkID avajson.Uint32 `json:"networkID"`
|
|
GenesisData map[string]AssetDefinition `json:"genesisData"`
|
|
Encoding formatting.Encoding `json:"encoding"`
|
|
}
|
|
|
|
type AssetDefinition struct {
|
|
Name string `json:"name"`
|
|
Symbol string `json:"symbol"`
|
|
Denomination avajson.Uint8 `json:"denomination"`
|
|
InitialState map[string][]interface{} `json:"initialState"`
|
|
Memo string `json:"memo"`
|
|
}
|
|
|
|
// BuildGenesisReply is the reply from BuildGenesis
|
|
type BuildGenesisReply struct {
|
|
Bytes string `json:"bytes"`
|
|
Encoding formatting.Encoding `json:"encoding"`
|
|
}
|
|
|
|
// BuildGenesis returns the UTXOs such that at least one address in [args.Addresses] is
|
|
// referenced in the UTXO.
|
|
func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, reply *BuildGenesisReply) error {
|
|
parser, err := txs.NewParser(
|
|
[]fxs.Fx{
|
|
&secp256k1fx.Fx{},
|
|
&nftfx.Fx{},
|
|
&propertyfx.Fx{},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
g := Genesis{}
|
|
genesisCodec := parser.GenesisCodec()
|
|
for assetAlias, assetDefinition := range args.GenesisData {
|
|
assetMemo, err := formatting.Decode(args.Encoding, assetDefinition.Memo)
|
|
if err != nil {
|
|
return fmt.Errorf("problem formatting asset definition memo due to: %w", err)
|
|
}
|
|
asset := GenesisAsset{
|
|
Alias: assetAlias,
|
|
CreateAssetTx: txs.CreateAssetTx{
|
|
BaseTx: txs.BaseTx{BaseTx: lux.BaseTx{
|
|
NetworkID: uint32(args.NetworkID),
|
|
BlockchainID: ids.Empty,
|
|
Memo: assetMemo,
|
|
}},
|
|
Name: assetDefinition.Name,
|
|
Symbol: assetDefinition.Symbol,
|
|
Denomination: byte(assetDefinition.Denomination),
|
|
},
|
|
}
|
|
if len(assetDefinition.InitialState) > 0 {
|
|
initialState := &txs.InitialState{
|
|
FxIndex: 0, // Implementation note
|
|
}
|
|
for assetType, initialStates := range assetDefinition.InitialState {
|
|
switch assetType {
|
|
case "fixedCap":
|
|
for _, state := range initialStates {
|
|
b, err := json.Marshal(state)
|
|
if err != nil {
|
|
return fmt.Errorf("problem marshaling state: %w", err)
|
|
}
|
|
holder := Holder{}
|
|
if err := json.Unmarshal(b, &holder); err != nil {
|
|
return fmt.Errorf("problem unmarshaling holder: %w", err)
|
|
}
|
|
_, addrbuff, err := address.ParseBech32(holder.Address)
|
|
if err != nil {
|
|
return fmt.Errorf("problem parsing holder address: %w", err)
|
|
}
|
|
addr, err := ids.ToShortID(addrbuff)
|
|
if err != nil {
|
|
return fmt.Errorf("problem parsing holder address: %w", err)
|
|
}
|
|
initialState.Outs = append(initialState.Outs, &secp256k1fx.TransferOutput{
|
|
Amt: uint64(holder.Amount),
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
})
|
|
}
|
|
case "variableCap":
|
|
for _, state := range initialStates {
|
|
b, err := json.Marshal(state)
|
|
if err != nil {
|
|
return fmt.Errorf("problem marshaling state: %w", err)
|
|
}
|
|
owners := Owners{}
|
|
if err := json.Unmarshal(b, &owners); err != nil {
|
|
return fmt.Errorf("problem unmarshaling Owners: %w", err)
|
|
}
|
|
|
|
out := &secp256k1fx.MintOutput{
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
},
|
|
}
|
|
for _, addrStr := range owners.Minters {
|
|
_, addrBytes, err := address.ParseBech32(addrStr)
|
|
if err != nil {
|
|
return fmt.Errorf("problem parsing minters address: %w", err)
|
|
}
|
|
addr, err := ids.ToShortID(addrBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("problem parsing minters address: %w", err)
|
|
}
|
|
out.Addrs = append(out.Addrs, addr)
|
|
}
|
|
out.Sort()
|
|
|
|
initialState.Outs = append(initialState.Outs, out)
|
|
}
|
|
default:
|
|
return errUnknownAssetType
|
|
}
|
|
}
|
|
initialState.Sort(genesisCodec)
|
|
asset.States = append(asset.States, initialState)
|
|
}
|
|
utils.Sort(asset.States)
|
|
g.Txs = append(g.Txs, &asset)
|
|
}
|
|
utils.Sort(g.Txs)
|
|
|
|
b, err := genesisCodec.Marshal(txs.CodecVersion, &g)
|
|
if err != nil {
|
|
return fmt.Errorf("problem marshaling genesis: %w", err)
|
|
}
|
|
|
|
reply.Bytes, err = formatting.Encode(args.Encoding, b)
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't encode genesis as string: %w", err)
|
|
}
|
|
reply.Encoding = args.Encoding
|
|
return nil
|
|
}
|