mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
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>
755 lines
19 KiB
Go
755 lines
19 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package builder
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/runtime"
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
"github.com/luxfi/ids"
|
|
lux "github.com/luxfi/utxo"
|
|
"github.com/luxfi/node/vms/platformvm/config"
|
|
"github.com/luxfi/node/vms/platformvm/security"
|
|
"github.com/luxfi/node/vms/platformvm/state"
|
|
"github.com/luxfi/node/vms/platformvm/txs"
|
|
"github.com/luxfi/node/vms/platformvm/utxo"
|
|
"github.com/luxfi/timer/mockable"
|
|
"github.com/luxfi/utils"
|
|
"github.com/luxfi/math"
|
|
"github.com/luxfi/node/vms/platformvm/fx"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
)
|
|
|
|
// Max number of items allowed in a page
|
|
const MaxPageSize = 1024
|
|
|
|
var (
|
|
_ Builder = (*builder)(nil)
|
|
|
|
ErrNoFunds = errors.New("no spendable funds were found")
|
|
)
|
|
|
|
type Builder interface {
|
|
AtomicTxBuilder
|
|
DecisionTxBuilder
|
|
ProposalTxBuilder
|
|
}
|
|
|
|
type AtomicTxBuilder interface {
|
|
// chainID: chain to import UTXOs from
|
|
// to: address of recipient
|
|
// keys: keys to import the funds
|
|
// changeAddr: address to send change to, if there is any
|
|
NewImportTx(
|
|
chainID ids.ID,
|
|
to ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// amount: amount of tokens to export
|
|
// chainID: chain to send the UTXOs to
|
|
// to: address of recipient
|
|
// keys: keys to pay the fee and provide the tokens
|
|
// changeAddr: address to send change to, if there is any
|
|
NewExportTx(
|
|
amount uint64,
|
|
chainID ids.ID,
|
|
to ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
}
|
|
|
|
type DecisionTxBuilder interface {
|
|
// netID: ID of the net that validates the new chain
|
|
// genesisData: byte repr. of genesis state of the new chain
|
|
// vmID: ID of VM this chain runs
|
|
// fxIDs: ids of features extensions this chain supports
|
|
// chainName: name of the chain
|
|
// keys: keys to sign the tx
|
|
// changeAddr: address to send change to, if there is any
|
|
NewCreateChainTx(
|
|
netID ids.ID,
|
|
genesisData []byte,
|
|
vmID ids.ID,
|
|
fxIDs []ids.ID,
|
|
chainName string,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// threshold: [threshold] of [ownerAddrs] needed to manage this network
|
|
// ownerAddrs: control addresses for the new network
|
|
// keys: keys to pay the fee
|
|
// changeAddr: address to send change to, if there is any
|
|
NewCreateNetworkTx(
|
|
threshold uint32,
|
|
ownerAddrs []ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// amount: amount the sender is sending
|
|
// owner: recipient of the funds
|
|
// keys: keys to sign the tx and pay the amount
|
|
// changeAddr: address to send change to, if there is any
|
|
NewBaseTx(
|
|
amount uint64,
|
|
owner secp256k1fx.OutputOwners,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
}
|
|
|
|
type ProposalTxBuilder interface {
|
|
// stakeAmount: amount the validator stakes
|
|
// startTime: unix time they start validating
|
|
// endTime: unix time they stop validating
|
|
// nodeID: ID of the node we want to validate with
|
|
// rewardAddress: address to send reward to, if applicable
|
|
// shares: 10,000 times percentage of reward taken from delegators
|
|
// keys: Keys providing the staked tokens
|
|
// changeAddr: Address to send change to, if there is any
|
|
NewAddValidatorTx(
|
|
stakeAmount,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
rewardAddress ids.ShortID,
|
|
shares uint32,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// stakeAmount: amount the delegator stakes
|
|
// startTime: unix time they start delegating
|
|
// endTime: unix time they stop delegating
|
|
// nodeID: ID of the node we are delegating to
|
|
// rewardAddress: address to send reward to, if applicable
|
|
// keys: keys providing the staked tokens
|
|
// changeAddr: address to send change to, if there is any
|
|
NewAddDelegatorTx(
|
|
stakeAmount,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
rewardAddress ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// weight: sampling weight of the new validator
|
|
// startTime: unix time they start delegating
|
|
// endTime: unix time they top delegating
|
|
// nodeID: ID of the node validating
|
|
// netID: ID of the net the validator will validate
|
|
// keys: keys to use for adding the validator
|
|
// changeAddr: address to send change to, if there is any
|
|
NewAddChainValidatorTx(
|
|
weight,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
netID ids.ID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// Creates a transaction that removes [nodeID]
|
|
// as a validator from [netID]
|
|
// keys: keys to use for removing the validator
|
|
// changeAddr: address to send change to, if there is any
|
|
NewRemoveChainValidatorTx(
|
|
nodeID ids.NodeID,
|
|
netID ids.ID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// Creates a transaction that transfers ownership of [netID]
|
|
// threshold: [threshold] of [ownerAddrs] needed to manage this chain
|
|
// ownerAddrs: control addresses for the new chain
|
|
// keys: keys to use for modifying the chain
|
|
// changeAddr: address to send change to, if there is any
|
|
NewTransferChainOwnershipTx(
|
|
netID ids.ID,
|
|
threshold uint32,
|
|
ownerAddrs []ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error)
|
|
|
|
// newAdvanceTimeTx creates a new tx that, if it is accepted and followed by a
|
|
// Commit block, will set the chain's timestamp to [timestamp].
|
|
NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error)
|
|
|
|
// RewardStakerTx creates a new transaction that proposes to remove the staker
|
|
// [validatorID] from the default validator set.
|
|
NewRewardValidatorTx(txID ids.ID) (*txs.Tx, error)
|
|
}
|
|
|
|
func New(
|
|
rt *runtime.Runtime,
|
|
cfg *config.Config,
|
|
clk *mockable.Clock,
|
|
fx fx.Fx,
|
|
state state.State,
|
|
atomicUTXOManager lux.AtomicUTXOManager,
|
|
utxoSpender utxo.Spender,
|
|
) Builder {
|
|
return &builder{
|
|
AtomicUTXOManager: atomicUTXOManager,
|
|
Spender: utxoSpender,
|
|
state: state,
|
|
cfg: cfg,
|
|
rt: rt,
|
|
NetworkID: rt.NetworkID,
|
|
ChainID: rt.ChainID,
|
|
UTXOAssetID: rt.UTXOAssetID,
|
|
clk: clk,
|
|
fx: fx,
|
|
}
|
|
}
|
|
|
|
type builder struct {
|
|
lux.AtomicUTXOManager
|
|
utxo.Spender
|
|
state state.State
|
|
|
|
cfg *config.Config
|
|
rt *runtime.Runtime
|
|
NetworkID uint32
|
|
ChainID ids.ID
|
|
UTXOAssetID ids.ID
|
|
clk *mockable.Clock
|
|
fx fx.Fx
|
|
}
|
|
|
|
func (b *builder) NewImportTx(
|
|
from ids.ID,
|
|
to ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
kc := secp256k1fx.NewKeychain(keys...)
|
|
|
|
addrs := kc.Addresses()
|
|
atomicUTXOs, _, _, err := b.GetAtomicUTXOs(from, addrs, ids.ShortEmpty, ids.Empty, MaxPageSize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("problem retrieving atomic UTXOs: %w", err)
|
|
}
|
|
|
|
importedInputs := []*lux.TransferableInput{}
|
|
signers := [][]*secp256k1.PrivateKey{}
|
|
|
|
importedAmounts := make(map[ids.ID]uint64)
|
|
now := b.clk.Unix()
|
|
for _, utxo := range atomicUTXOs {
|
|
inputIntf, utxoSigners, err := kc.Spend(utxo.Out, now)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
input, ok := inputIntf.(lux.TransferableIn)
|
|
if !ok {
|
|
continue
|
|
}
|
|
assetID := utxo.AssetID()
|
|
importedAmounts[assetID], err = math.Add(importedAmounts[assetID], input.Amount())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
importedInputs = append(importedInputs, &lux.TransferableInput{
|
|
UTXOID: utxo.UTXOID,
|
|
Asset: utxo.Asset,
|
|
In: input,
|
|
})
|
|
signers = append(signers, utxoSigners)
|
|
}
|
|
lux.SortTransferableInputsWithSigners(importedInputs, signers)
|
|
|
|
if len(importedAmounts) == 0 {
|
|
return nil, ErrNoFunds // No imported UTXOs were spendable
|
|
}
|
|
|
|
importedLUX := importedAmounts[b.UTXOAssetID]
|
|
|
|
ins := []*lux.TransferableInput{}
|
|
outs := []*lux.TransferableOutput{}
|
|
switch {
|
|
case importedLUX < b.cfg.TxFee: // imported amount goes toward paying tx fee
|
|
var baseSigners [][]*secp256k1.PrivateKey
|
|
ins, outs, _, baseSigners, err = b.Spend(b.state, keys, 0, b.cfg.TxFee-importedLUX, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
signers = append(baseSigners, signers...)
|
|
delete(importedAmounts, b.UTXOAssetID)
|
|
case importedLUX == b.cfg.TxFee:
|
|
delete(importedAmounts, b.UTXOAssetID)
|
|
default:
|
|
importedAmounts[b.UTXOAssetID] -= b.cfg.TxFee
|
|
}
|
|
|
|
for assetID, amount := range importedAmounts {
|
|
outs = append(outs, &lux.TransferableOutput{
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: amount,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Locktime: 0,
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{to},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
lux.SortTransferableOutputs(outs) // sort imported outputs
|
|
|
|
// Create the transaction
|
|
utx, err := txs.NewImportTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Outs: outs,
|
|
Ins: ins,
|
|
},
|
|
from,
|
|
importedInputs,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewExportTx(
|
|
amount uint64,
|
|
chainID ids.ID,
|
|
to ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
toBurn, err := math.Add(amount, b.cfg.TxFee)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("amount (%d) + tx fee(%d) overflows", amount, b.cfg.TxFee)
|
|
}
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, toBurn, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
// Create the transaction
|
|
utx, err := txs.NewExportTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs, // Non-exported outputs
|
|
},
|
|
chainID,
|
|
[]*lux.TransferableOutput{{ // Exported to X-Chain
|
|
Asset: lux.Asset{ID: b.UTXOAssetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: amount,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Locktime: 0,
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{to},
|
|
},
|
|
},
|
|
}},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewCreateChainTx(
|
|
netID ids.ID,
|
|
genesisData []byte,
|
|
vmID ids.ID,
|
|
fxIDs []ids.ID,
|
|
chainName string,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
createBlockchainTxFee := b.cfg.CreateChainTxFee
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, createBlockchainTxFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
chainAuth, chainSigners, err := b.Authorize(b.state, netID, keys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't authorize tx's net restrictions: %w", err)
|
|
}
|
|
signers = append(signers, chainSigners)
|
|
|
|
// Sort the provided fxIDs
|
|
utils.Sort(fxIDs)
|
|
|
|
// Create the tx
|
|
utx, err := txs.NewCreateChainTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
},
|
|
netID,
|
|
chainName,
|
|
vmID,
|
|
fxIDs,
|
|
genesisData,
|
|
chainAuth,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewCreateNetworkTx(
|
|
threshold uint32,
|
|
ownerAddrs []ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
createNetworkTxFee := b.cfg.CreateNetworkTxFee
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, createNetworkTxFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
// Sort control addresses
|
|
utils.Sort(ownerAddrs)
|
|
|
|
// Create the tx — a classic permissioned network: it restakes its parent
|
|
// (the primary network) and runs no own validator set. Members are added
|
|
// later via AddChainValidatorTx.
|
|
utx, err := txs.NewCreateNetworkTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
},
|
|
constants.PrimaryNetworkID,
|
|
&secp256k1fx.OutputOwners{
|
|
Threshold: threshold,
|
|
Addrs: ownerAddrs,
|
|
},
|
|
security.Mode{RestakeParent: true, Admission: security.NoOwnSet, Manager: security.PChain},
|
|
nil, // validators (none: no own set)
|
|
nil, // chains (none at genesis)
|
|
0, // managerChainIdx (unused without a contract-governed own set)
|
|
nil, // managerAddress
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewAddValidatorTx(
|
|
stakeAmount,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
rewardAddress ids.ShortID,
|
|
shares uint32,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
ins, unstakedOuts, stakedOuts, signers, err := b.Spend(b.state, keys, stakeAmount, b.cfg.AddNetworkValidatorFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
// Create the tx
|
|
utx, err := txs.NewAddValidatorTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: unstakedOuts,
|
|
},
|
|
txs.Validator{
|
|
NodeID: nodeID,
|
|
Start: startTime,
|
|
End: endTime,
|
|
Wght: stakeAmount,
|
|
},
|
|
stakedOuts,
|
|
&secp256k1fx.OutputOwners{
|
|
Locktime: 0,
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{rewardAddress},
|
|
},
|
|
shares,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewAddDelegatorTx(
|
|
stakeAmount,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
rewardAddress ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
ins, unlockedOuts, lockedOuts, signers, err := b.Spend(b.state, keys, stakeAmount, b.cfg.AddNetworkDelegatorFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
// Create the tx
|
|
utx, err := txs.NewAddDelegatorTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: unlockedOuts,
|
|
},
|
|
txs.Validator{
|
|
NodeID: nodeID,
|
|
Start: startTime,
|
|
End: endTime,
|
|
Wght: stakeAmount,
|
|
},
|
|
lockedOuts,
|
|
&secp256k1fx.OutputOwners{
|
|
Locktime: 0,
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{rewardAddress},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewAddChainValidatorTx(
|
|
weight,
|
|
startTime,
|
|
endTime uint64,
|
|
nodeID ids.NodeID,
|
|
netID ids.ID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, b.cfg.TxFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
chainAuth, chainSigners, err := b.Authorize(b.state, netID, keys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't authorize tx's net restrictions: %w", err)
|
|
}
|
|
signers = append(signers, chainSigners)
|
|
|
|
// Create the tx
|
|
utx, err := txs.NewAddChainValidatorTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
},
|
|
txs.Validator{
|
|
NodeID: nodeID,
|
|
Start: startTime,
|
|
End: endTime,
|
|
Wght: weight,
|
|
},
|
|
netID,
|
|
chainAuth,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewRemoveChainValidatorTx(
|
|
nodeID ids.NodeID,
|
|
netID ids.ID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, b.cfg.TxFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
chainAuth, chainSigners, err := b.Authorize(b.state, netID, keys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't authorize tx's net restrictions: %w", err)
|
|
}
|
|
signers = append(signers, chainSigners)
|
|
|
|
// Create the tx
|
|
utx, err := txs.NewRemoveChainValidatorTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
},
|
|
nodeID,
|
|
netID,
|
|
chainAuth,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error) {
|
|
utx := txs.NewAdvanceTimeTx(uint64(timestamp.Unix()))
|
|
tx, err := txs.NewSigned(utx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewRewardValidatorTx(txID ids.ID) (*txs.Tx, error) {
|
|
utx := txs.NewRewardValidatorTx(txID)
|
|
tx, err := txs.NewSigned(utx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewTransferChainOwnershipTx(
|
|
netID ids.ID,
|
|
threshold uint32,
|
|
ownerAddrs []ids.ShortID,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, b.cfg.TxFee, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
chainAuth, chainSigners, err := b.Authorize(b.state, netID, keys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't authorize tx's net restrictions: %w", err)
|
|
}
|
|
signers = append(signers, chainSigners)
|
|
|
|
utx, err := txs.NewTransferChainOwnershipTx(
|
|
&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
},
|
|
netID,
|
|
chainAuth,
|
|
&secp256k1fx.OutputOwners{
|
|
Threshold: threshold,
|
|
Addrs: ownerAddrs,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|
|
|
|
func (b *builder) NewBaseTx(
|
|
amount uint64,
|
|
owner secp256k1fx.OutputOwners,
|
|
keys []*secp256k1.PrivateKey,
|
|
changeAddr ids.ShortID,
|
|
) (*txs.Tx, error) {
|
|
toBurn, err := math.Add(amount, b.cfg.TxFee)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("amount (%d) + tx fee(%d) overflows", amount, b.cfg.TxFee)
|
|
}
|
|
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, toBurn, changeAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
|
|
}
|
|
|
|
outs = append(outs, &lux.TransferableOutput{
|
|
Asset: lux.Asset{ID: b.UTXOAssetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: amount,
|
|
OutputOwners: owner,
|
|
},
|
|
})
|
|
|
|
lux.SortTransferableOutputs(outs)
|
|
|
|
utx, err := txs.NewBaseTx(&lux.BaseTx{
|
|
NetworkID: b.NetworkID,
|
|
BlockchainID: b.ChainID,
|
|
Ins: ins,
|
|
Outs: outs,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx, err := txs.NewSigned(utx, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tx, tx.SyntacticVerify(b.rt)
|
|
}
|