mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
port/xvm-cpp, port/xvm-rust, port/platformvm-rust and port/platformvm-cpp land on main; the only overlaps were documents, taken from the ports. The xvm C++ port kept its Go golden-vector generator in the directory with the C++ tests it feeds, which Go refuses to build as a package. It lives in test/golden/ now; the comments and the recipe that name it follow it. The sums the ports' Go test helpers need were never tidied in; they are. Co-authored-by: Hanzo Dev <[email protected]>
317 lines
9.3 KiB
Go
317 lines
9.3 KiB
Go
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
//
|
|
// Reference-vector generator: builds X-Chain values with the GO implementation
|
|
// (github.com/luxfi/node/vms/xvm) and prints their canonical wire bytes as a C++
|
|
// header. The C++ port's wire test asserts against these bytes verbatim, so the
|
|
// two implementations cannot drift without a test failing.
|
|
//
|
|
// Run from the node checkout: GOWORK=off go run <this file> > golden.hpp
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/vms/components/verify"
|
|
"github.com/luxfi/node/vms/xvm"
|
|
"github.com/luxfi/node/vms/xvm/block"
|
|
"github.com/luxfi/node/vms/xvm/fxs"
|
|
"github.com/luxfi/node/vms/xvm/txs"
|
|
lux "github.com/luxfi/utxo"
|
|
"github.com/luxfi/utxo/nftfx"
|
|
"github.com/luxfi/utxo/propertyfx"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
"github.com/luxfi/utxo/wire"
|
|
)
|
|
|
|
func emit(name string, b []byte) {
|
|
fmt.Printf("inline constexpr const char* %s =\n \"%x\";\n", name, b)
|
|
}
|
|
|
|
func addr(n byte) ids.ShortID {
|
|
var a ids.ShortID
|
|
for i := range a {
|
|
a[i] = n + byte(i)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func id(n byte) ids.ID {
|
|
var a ids.ID
|
|
for i := range a {
|
|
a[i] = n + byte(i)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func sig(n byte) [65]byte {
|
|
var s [65]byte
|
|
for i := range s {
|
|
s[i] = n + byte(i)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func main() {
|
|
fmt.Println("// Copyright (C) 2026, Lux Industries Inc. All rights reserved.")
|
|
fmt.Println("// SPDX-License-Identifier: BSD-3-Clause-Eco")
|
|
fmt.Println("//")
|
|
fmt.Println("// GENERATED by chains/cpp/xvm/test/golden_gen.go — do not edit.")
|
|
fmt.Println("//")
|
|
fmt.Println("// Every constant is the hex of a canonical wire buffer produced by the GO")
|
|
fmt.Println("// X-Chain (github.com/luxfi/node/vms/xvm). They are the port's contract: the")
|
|
fmt.Println("// C++ implementation must emit these bytes, or the two are different chains.")
|
|
fmt.Println("#pragma once")
|
|
fmt.Println("namespace lux::xvm::golden {")
|
|
|
|
// ---- fx primitives ----
|
|
owners := secp256k1fx.OutputOwners{Locktime: 12345, Threshold: 1, Addrs: []ids.ShortID{addr(1)}}
|
|
emit("kOutputOwners", owners.Bytes())
|
|
|
|
twoOwners := secp256k1fx.OutputOwners{Locktime: 0, Threshold: 2, Addrs: []ids.ShortID{addr(1), addr(40)}}
|
|
emit("kOutputOwnersTwo", twoOwners.Bytes())
|
|
|
|
transferOut := &secp256k1fx.TransferOutput{Amt: 54321, OutputOwners: owners}
|
|
emit("kTransferOutput", transferOut.Bytes())
|
|
|
|
transferIn := &secp256k1fx.TransferInput{Amt: 54321, Input: secp256k1fx.Input{SigIndices: []uint32{0, 3}}}
|
|
emit("kTransferInput", transferIn.Bytes())
|
|
|
|
mintOut := &secp256k1fx.MintOutput{OutputOwners: owners}
|
|
emit("kMintOutput", mintOut.Bytes())
|
|
|
|
mintOp := &secp256k1fx.MintOperation{
|
|
MintInput: secp256k1fx.Input{SigIndices: []uint32{0}},
|
|
MintOutput: *mintOut,
|
|
TransferOutput: *transferOut,
|
|
}
|
|
emit("kMintOperation", mintOp.Bytes())
|
|
|
|
cred := &secp256k1fx.Credential{Sigs: [][65]byte{sig(7), sig(9)}}
|
|
emit("kCredential", cred.Bytes())
|
|
|
|
nftMintOut := &nftfx.MintOutput{GroupID: 7, OutputOwners: owners}
|
|
emit("kNFTMintOutput", nftMintOut.Bytes())
|
|
|
|
nftTransferOut := &nftfx.TransferOutput{GroupID: 7, Payload: []byte("hello nft"), OutputOwners: owners}
|
|
emit("kNFTTransferOutput", nftTransferOut.Bytes())
|
|
|
|
nftMintOp := &nftfx.MintOperation{
|
|
MintInput: secp256k1fx.Input{SigIndices: []uint32{0}},
|
|
GroupID: 7,
|
|
Payload: []byte("hello nft"),
|
|
Outputs: []*secp256k1fx.OutputOwners{&owners, &twoOwners},
|
|
}
|
|
emit("kNFTMintOperation", nftMintOp.Bytes())
|
|
|
|
nftTransferOp := &nftfx.TransferOperation{
|
|
Input: secp256k1fx.Input{SigIndices: []uint32{0, 1}},
|
|
Output: *nftTransferOut,
|
|
}
|
|
emit("kNFTTransferOperation", nftTransferOp.Bytes())
|
|
|
|
nftCred := &nftfx.Credential{Credential: secp256k1fx.Credential{Sigs: [][65]byte{sig(3)}}}
|
|
emit("kNFTCredential", nftCred.Bytes())
|
|
|
|
propMintOut := &propertyfx.MintOutput{OutputOwners: owners}
|
|
emit("kPropertyMintOutput", propMintOut.Bytes())
|
|
|
|
propOwnedOut := &propertyfx.OwnedOutput{OutputOwners: twoOwners}
|
|
emit("kPropertyOwnedOutput", propOwnedOut.Bytes())
|
|
|
|
propMintOp := &propertyfx.MintOperation{
|
|
MintInput: secp256k1fx.Input{SigIndices: []uint32{1}},
|
|
MintOutput: *propMintOut,
|
|
OwnedOutput: *propOwnedOut,
|
|
}
|
|
emit("kPropertyMintOperation", propMintOp.Bytes())
|
|
|
|
propBurnOp := &propertyfx.BurnOperation{Input: secp256k1fx.Input{SigIndices: []uint32{0}}}
|
|
emit("kPropertyBurnOperation", propBurnOp.Bytes())
|
|
|
|
propCred := &propertyfx.Credential{Credential: secp256k1fx.Credential{Sigs: [][65]byte{sig(11)}}}
|
|
emit("kPropertyCredential", propCred.Bytes())
|
|
|
|
// ---- UTXO ----
|
|
utxo := &lux.UTXO{
|
|
UTXOID: lux.UTXOID{TxID: id(100), OutputIndex: 3},
|
|
Asset: lux.Asset{ID: id(50)},
|
|
Out: transferOut,
|
|
}
|
|
utxoBytes, err := utxo.WireBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kUTXO", utxoBytes)
|
|
emit("kUTXOInputID", func() []byte { i := utxo.InputID(); return i[:] }())
|
|
|
|
// ---- the five transactions ----
|
|
networkID := uint32(10)
|
|
chainID := id(200)
|
|
|
|
baseFields := lux.BaseTx{
|
|
NetworkID: networkID,
|
|
BlockchainID: chainID,
|
|
Outs: []*lux.TransferableOutput{{
|
|
Asset: lux.Asset{ID: id(50)},
|
|
Out: transferOut,
|
|
}},
|
|
Ins: []*lux.TransferableInput{{
|
|
UTXOID: lux.UTXOID{TxID: id(100), OutputIndex: 3},
|
|
Asset: lux.Asset{ID: id(50)},
|
|
In: transferIn,
|
|
}},
|
|
Memo: []byte("memo bytes"),
|
|
}
|
|
|
|
baseTx := &txs.BaseTx{BaseTx: baseFields}
|
|
baseUnsigned, err := txs.UnsignedBytes(baseTx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kBaseTxUnsigned", baseUnsigned)
|
|
|
|
signedBase := &txs.Tx{Unsigned: baseTx, Creds: []*fxs.FxCredential{{Credential: cred}}}
|
|
if err := signedBase.Initialize(); err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kBaseTxSigned", signedBase.Bytes())
|
|
emit("kBaseTxID", func() []byte { i := signedBase.ID(); return i[:] }())
|
|
|
|
createAssetTx := &txs.CreateAssetTx{
|
|
BaseTx: txs.BaseTx{BaseTx: baseFields},
|
|
Name: "Lux Test Asset",
|
|
Symbol: "LTA",
|
|
Denomination: 9,
|
|
States: []*txs.InitialState{{
|
|
FxIndex: 0,
|
|
Outs: []verify.State{mintOut, transferOut},
|
|
}},
|
|
}
|
|
// The genesis outputs must be in canonical wire order.
|
|
createAssetTx.States[0].Sort()
|
|
caUnsigned, err := txs.UnsignedBytes(createAssetTx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kCreateAssetTxUnsigned", caUnsigned)
|
|
stateBytes, err := createAssetTx.States[0].Bytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kInitialState", stateBytes)
|
|
|
|
op := &txs.Operation{
|
|
Asset: lux.Asset{ID: id(50)},
|
|
UTXOIDs: []*lux.UTXOID{{TxID: id(100), OutputIndex: 1}, {TxID: id(100), OutputIndex: 4}},
|
|
Op: mintOp,
|
|
}
|
|
opBytes, err := op.Bytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kOperation", opBytes)
|
|
|
|
opTx := &txs.OperationTx{
|
|
BaseTx: txs.BaseTx{BaseTx: baseFields},
|
|
Ops: []*txs.Operation{op},
|
|
}
|
|
opUnsigned, err := txs.UnsignedBytes(opTx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kOperationTxUnsigned", opUnsigned)
|
|
|
|
importTx := &txs.ImportTx{
|
|
BaseTx: txs.BaseTx{BaseTx: baseFields},
|
|
SourceChain: id(77),
|
|
ImportedIns: []*lux.TransferableInput{{
|
|
UTXOID: lux.UTXOID{TxID: id(150), OutputIndex: 0},
|
|
Asset: lux.Asset{ID: id(50)},
|
|
In: transferIn,
|
|
}},
|
|
}
|
|
importUnsigned, err := txs.UnsignedBytes(importTx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kImportTxUnsigned", importUnsigned)
|
|
|
|
exportTx := &txs.ExportTx{
|
|
BaseTx: txs.BaseTx{BaseTx: baseFields},
|
|
DestinationChain: id(88),
|
|
ExportedOuts: []*lux.TransferableOutput{{
|
|
Asset: lux.Asset{ID: id(50)},
|
|
Out: transferOut,
|
|
}},
|
|
}
|
|
exportUnsigned, err := txs.UnsignedBytes(exportTx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kExportTxUnsigned", exportUnsigned)
|
|
|
|
// ---- a block carrying that signed BaseTx ----
|
|
blk, err := block.NewStandardBlock(id(210), 7, time.Unix(1730000000, 0), []*txs.Tx{signedBase})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kStandardBlock", blk.Bytes())
|
|
emit("kStandardBlockID", func() []byte { i := blk.ID(); return i[:] }())
|
|
|
|
blkRoot, err := block.NewStandardBlockWithRoot(id(210), 7, time.Unix(1730000000, 0), id(220), []*txs.Tx{signedBase})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kStandardBlockWithRoot", blkRoot.Bytes())
|
|
|
|
// ---- the XVMBaseTx envelope alone, and the SignedTx envelope alone ----
|
|
emit("kXVMBaseTxEnvelope", wire.NewXVMBaseTx(wire.XVMBaseTxInput{
|
|
NetworkID: networkID,
|
|
BlockchainID: [32]byte(chainID),
|
|
Outs: []wire.XVMTransferOut{{AssetID: id(50), Output: transferOut.Bytes()}},
|
|
Ins: []wire.XVMTransferIn{{
|
|
TxID: id(100), OutputIndex: 3, AssetID: id(50), Input: transferIn.Bytes(),
|
|
}},
|
|
Memo: []byte("memo bytes"),
|
|
}))
|
|
|
|
// ---- the genesis buffer a host hands the chain ----
|
|
//
|
|
// Two assets, so the alias/tx length lists carry more than one entry and a
|
|
// C++ reader that only ever reads element zero would be caught.
|
|
secondAsset := &txs.CreateAssetTx{
|
|
BaseTx: txs.BaseTx{BaseTx: lux.BaseTx{NetworkID: networkID, BlockchainID: ids.Empty}},
|
|
Name: "Second Asset",
|
|
Symbol: "SEC",
|
|
Denomination: 0,
|
|
States: []*txs.InitialState{{
|
|
FxIndex: 0,
|
|
Outs: []verify.State{mintOut},
|
|
}},
|
|
}
|
|
secondAsset.States[0].Sort()
|
|
|
|
gen := &xvm.Genesis{Txs: []*xvm.GenesisAsset{
|
|
{Alias: "LTA", CreateAssetTx: *createAssetTx},
|
|
{Alias: "SEC", CreateAssetTx: *secondAsset},
|
|
}}
|
|
genBytes, err := gen.Bytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kGenesis", genBytes)
|
|
|
|
firstAssetID, err := xvm.AssetIDFromGenesisBytes(genBytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
emit("kGenesisFeeAssetID", firstAssetID[:])
|
|
|
|
fmt.Println("} // namespace lux::xvm::golden")
|
|
_ = os.Stdout
|
|
}
|