mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Pure zap tx: 18 type files converted (parallel) + verifyBaseTx reconciled
Batches 1-3 (proposal + spending + validator-family) landed pure: struct is the buffer, New*Tx builds, accessors read, SyntacticVerify via verifyBaseTx. Remaining package-internal: 5 complex types (Convert/CreateSovereign/CreateAsset/ Operation/Slash) + staker interface methods + consumer flip.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
//
|
||||
// File deprecation notice: this file defines AddChainValidatorTx, the
|
||||
@@ -6,14 +6,10 @@
|
||||
// (sovereign-L1), validators join a network — never a chain — via
|
||||
// AddValidatorTx. Chains live on networks (created via CreateChainTx);
|
||||
// the canonical model has validators validate networks, not chains. A
|
||||
// sovereign L1 IS a primary network at its own networkID; the Lux
|
||||
// primary networks live at 1/2/3/1337, and any downstream consumer
|
||||
// running its own primary picks any other uint32. AddValidatorTx is
|
||||
// the universal add-validator-to-a-network entry point for all of
|
||||
// them. New code must use AddValidatorTx. The wire codec entries,
|
||||
// Visitor method, and wallet IssueAddChainValidatorTx helper are kept
|
||||
// for one release cycle so existing pre-LP-018 P-chain history
|
||||
// continues to decode and replay.
|
||||
// sovereign L1 IS a primary network at its own networkID. AddValidatorTx
|
||||
// is the universal add-validator-to-a-network entry point. New code must
|
||||
// use AddValidatorTx. This type is retained for one release cycle so
|
||||
// existing pre-LP-018 P-chain history continues to decode and replay.
|
||||
|
||||
package txs
|
||||
|
||||
@@ -21,75 +17,93 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
_ StakerTx = (*AddChainValidatorTx)(nil)
|
||||
_ ScheduledStaker = (*AddChainValidatorTx)(nil)
|
||||
_ UnsignedTx = (*AddChainValidatorTx)(nil)
|
||||
|
||||
errAddPrimaryNetworkValidator = errors.New("can't add primary network validator with AddChainValidatorTx")
|
||||
)
|
||||
|
||||
// AddChainValidatorTx is the legacy per-chain (pre-LP-018: per-L1)
|
||||
// validator registration tx.
|
||||
// AddChainValidatorTx is the legacy per-chain validator registration tx.
|
||||
// The struct IS the wire: it holds the zap buffer and reads its fields by
|
||||
// offset. No codec, no marshal.
|
||||
//
|
||||
// Deprecated: Use AddValidatorTx. Under LP-018 sovereign-L1, validators
|
||||
// join a network (Lux primary 1/2/3/1337 or any sovereign L1's own
|
||||
// primary at its EVM chainID). Chains live on networks; validators no
|
||||
// longer register per-chain. This type is retained for one release
|
||||
// cycle for wire/codec compat with pre-LP-018 binaries. The Visitor
|
||||
// method, codec slots, and wallet IssueAddChainValidatorTx helper are
|
||||
// preserved so older P-chain history still decodes and replays.
|
||||
// Wire: zap header + object{ envelope@0..76, Validator@77, Chain@121,
|
||||
// ChainAuth@153 }.
|
||||
//
|
||||
// Deprecated: Use AddValidatorTx. Retained for one release cycle for
|
||||
// wire/codec compat with pre-LP-018 binaries.
|
||||
type AddChainValidatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// The validator
|
||||
ChainValidator `serialize:"true" json:"validator"`
|
||||
// Auth that will be allowing this validator into the network
|
||||
ChainAuth verify.Verifiable `serialize:"true" json:"chainAuthorization"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
func (tx *AddChainValidatorTx) NodeID() ids.NodeID {
|
||||
return tx.ChainValidator.NodeID
|
||||
const (
|
||||
offACVValidator = spendSize // 77: inline Validator (44B)
|
||||
offACVChain = offACVValidator + validatorSize // 121: chain id (32B)
|
||||
offACVChainAuth = offACVChain + 32 // 153: chain-auth list ptr (8B)
|
||||
addChainValidatorSize = offACVChainAuth + 8 // 161
|
||||
)
|
||||
|
||||
// NewAddChainValidatorTx builds the tx into a fresh zap buffer — the one
|
||||
// place a field becomes bytes (construction, not serialization).
|
||||
func NewAddChainValidatorTx(base *lux.BaseTx, validator Validator, chain ids.ID, chainAuth verify.Verifiable) (*AddChainValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 1024 + addChainValidatorSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authOff, authCount, err := writeAuth(b, chainAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(addChainValidatorSize)
|
||||
setEnvelope(ob, kindAddChainValidator, base, p)
|
||||
setValidator(ob, offACVValidator, validator)
|
||||
setID(ob, offACVChain, chain)
|
||||
ob.SetList(offACVChainAuth, authOff, authCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &AddChainValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
func (*AddChainValidatorTx) PublicKey() (*bls.PublicKey, bool, error) {
|
||||
return nil, false, nil
|
||||
// Validator is the inline validator descriptor (offset read).
|
||||
func (tx *AddChainValidatorTx) Validator() Validator {
|
||||
return readValidator(tx.root(), offACVValidator)
|
||||
}
|
||||
|
||||
func (*AddChainValidatorTx) PendingPriority() Priority {
|
||||
return ChainPermissionedValidatorPendingPriority
|
||||
// Chain is the network id this validator registers under (offset read).
|
||||
func (tx *AddChainValidatorTx) Chain() ids.ID { return readID(tx.root(), offACVChain) }
|
||||
|
||||
// ChainAuth proves the issuer may add this validator to the network.
|
||||
func (tx *AddChainValidatorTx) ChainAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offACVChainAuth)
|
||||
}
|
||||
|
||||
func (*AddChainValidatorTx) CurrentPriority() Priority {
|
||||
return ChainPermissionedValidatorCurrentPriority
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid
|
||||
// SyntacticVerify returns nil iff [tx] is valid.
|
||||
func (tx *AddChainValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case tx.Chain == constants.PrimaryNetworkID:
|
||||
}
|
||||
if tx.Chain() == constants.PrimaryNetworkID {
|
||||
return errAddPrimaryNetworkValidator
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := verify.All(&tx.Validator, tx.ChainAuth); err != nil {
|
||||
v := tx.Validator()
|
||||
if err := verify.All(&v, tx.ChainAuth()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cache that this is valid
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -97,8 +111,7 @@ func (tx *AddChainValidatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.AddChainValidatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
// Initialize is a no-op; Runtime is passed explicitly to InitRuntime.
|
||||
func (tx *AddChainValidatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -8,95 +8,98 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
safemath "github.com/luxfi/math"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
_ DelegatorTx = (*AddDelegatorTx)(nil)
|
||||
_ ScheduledStaker = (*AddDelegatorTx)(nil)
|
||||
_ UnsignedTx = (*AddDelegatorTx)(nil)
|
||||
|
||||
errDelegatorWeightMismatch = errors.New("delegator weight is not equal to total stake weight")
|
||||
errStakeMustBeLUX = errors.New("stake must be LUX")
|
||||
)
|
||||
|
||||
// AddDelegatorTx is an unsigned addDelegatorTx
|
||||
// AddDelegatorTx is an unsigned addDelegatorTx. The struct IS the wire: it
|
||||
// holds the zap buffer and reads its fields by offset. No codec, no marshal.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Validator@77, StakeOuts@121,
|
||||
// DelegationRewardsOwner@137 }.
|
||||
type AddDelegatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Describes the delegatee
|
||||
Validator `serialize:"true" json:"validator"`
|
||||
// Where to send staked tokens when done validating
|
||||
StakeOuts []*lux.TransferableOutput `serialize:"true" json:"stake"`
|
||||
// Where to send staking rewards when done validating
|
||||
DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [UnsignedAddDelegatorTx]. Also sets the [rt] to the given [vm.rt] so that
|
||||
// the addresses can be json marshalled into human readable format
|
||||
func (tx *AddDelegatorTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, out := range tx.StakeOuts {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
const (
|
||||
offADValidator = spendSize // 77: inline Validator (44B)
|
||||
offADStakeOuts = offADValidator + validatorSize // 121: stake-outs list ptr (8B)
|
||||
offADStakeAddrs = offADStakeOuts + 8 // 129: stake-outs owner-addr array ptr (8B)
|
||||
offADRewardsThreshold = offADStakeAddrs + 8 // 137: rewards owner threshold (u32)
|
||||
offADRewardsLocktime = offADRewardsThreshold + 4 // 141: rewards owner locktime (u64)
|
||||
offADRewardsAddrs = offADRewardsLocktime + 8 // 149: rewards owner addr array ptr (8B)
|
||||
addDelegatorSize = offADRewardsAddrs + 8 // 157
|
||||
)
|
||||
|
||||
// NewAddDelegatorTx builds the tx into a fresh zap buffer.
|
||||
func NewAddDelegatorTx(base *lux.BaseTx, validator Validator, stakeOuts []*lux.TransferableOutput, delegationRewardsOwner fx.Owner) (*AddDelegatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 1024 + addDelegatorSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Owner doesn't have InitRuntime method
|
||||
stakeListOff, stakeListCount, stakeAddrOff, stakeAddrCount, err := writeExtraOuts(b, stakeOuts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount, err := writeOwner(b, delegationRewardsOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(addDelegatorSize)
|
||||
setEnvelope(ob, kindAddDelegator, base, p)
|
||||
setValidator(ob, offADValidator, validator)
|
||||
ob.SetList(offADStakeOuts, stakeListOff, stakeListCount)
|
||||
ob.SetList(offADStakeAddrs, stakeAddrOff, stakeAddrCount)
|
||||
setOwner(ob, offADRewardsThreshold, offADRewardsLocktime, offADRewardsAddrs, ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &AddDelegatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
func (*AddDelegatorTx) ChainID() ids.ID {
|
||||
return constants.PrimaryNetworkID
|
||||
// Validator describes the delegatee (offset read).
|
||||
func (tx *AddDelegatorTx) Validator() Validator { return readValidator(tx.root(), offADValidator) }
|
||||
|
||||
// StakeOuts is where staked tokens go when done validating (offset read).
|
||||
func (tx *AddDelegatorTx) StakeOuts() []*lux.TransferableOutput {
|
||||
return readExtraOuts(tx.root(), offADStakeOuts, offADStakeAddrs)
|
||||
}
|
||||
|
||||
func (tx *AddDelegatorTx) NodeID() ids.NodeID {
|
||||
return tx.Validator.NodeID
|
||||
// DelegationRewardsOwner is where staking rewards go when done validating.
|
||||
func (tx *AddDelegatorTx) DelegationRewardsOwner() fx.Owner {
|
||||
return readOwner(tx.root(), offADRewardsThreshold, offADRewardsLocktime, offADRewardsAddrs)
|
||||
}
|
||||
|
||||
func (*AddDelegatorTx) PublicKey() (*bls.PublicKey, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (*AddDelegatorTx) PendingPriority() Priority {
|
||||
return PrimaryNetworkDelegatorLegacyPendingPriority
|
||||
}
|
||||
|
||||
func (*AddDelegatorTx) CurrentPriority() Priority {
|
||||
return PrimaryNetworkDelegatorCurrentPriority
|
||||
}
|
||||
|
||||
func (tx *AddDelegatorTx) Stake() []*lux.TransferableOutput {
|
||||
return tx.StakeOuts
|
||||
}
|
||||
|
||||
func (tx *AddDelegatorTx) RewardsOwner() fx.Owner {
|
||||
return tx.DelegationRewardsOwner
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid
|
||||
// SyntacticVerify returns nil iff [tx] is valid.
|
||||
func (tx *AddDelegatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil {
|
||||
v := tx.Validator()
|
||||
if err := verify.All(&v, tx.DelegationRewardsOwner()); err != nil {
|
||||
return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
|
||||
}
|
||||
|
||||
stakeOuts := tx.StakeOuts()
|
||||
totalStakeWeight := uint64(0)
|
||||
for _, out := range tx.StakeOuts {
|
||||
for _, out := range stakeOuts {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("output verification failed: %w", err)
|
||||
}
|
||||
@@ -114,18 +117,15 @@ func (tx *AddDelegatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
}
|
||||
|
||||
switch {
|
||||
case !lux.IsSortedTransferableOutputs(tx.StakeOuts):
|
||||
case !lux.IsSortedTransferableOutputs(stakeOuts):
|
||||
return errOutputsNotSorted
|
||||
case totalStakeWeight != tx.Wght:
|
||||
case totalStakeWeight != v.Wght:
|
||||
return fmt.Errorf("%w, delegator weight %d total stake weight %d",
|
||||
errDelegatorWeightMismatch,
|
||||
tx.Wght,
|
||||
v.Wght,
|
||||
totalStakeWeight,
|
||||
)
|
||||
}
|
||||
|
||||
// cache that this is valid
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -133,8 +133,7 @@ func (tx *AddDelegatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.AddDelegatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
// Initialize is a no-op; Runtime is passed explicitly to InitRuntime.
|
||||
func (tx *AddDelegatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -7,110 +7,113 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
safemath "github.com/luxfi/math"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
_ DelegatorTx = (*AddPermissionlessDelegatorTx)(nil)
|
||||
_ ScheduledStaker = (*AddPermissionlessDelegatorTx)(nil)
|
||||
)
|
||||
var _ UnsignedTx = (*AddPermissionlessDelegatorTx)(nil)
|
||||
|
||||
// AddPermissionlessDelegatorTx is an unsigned addPermissionlessDelegatorTx
|
||||
// AddPermissionlessDelegatorTx is an unsigned addPermissionlessDelegatorTx.
|
||||
// The struct IS the wire: it holds the zap buffer and reads its fields by
|
||||
// offset. No codec, no marshal.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Validator@77, Chain@121,
|
||||
// StakeOuts@153, DelegationRewardsOwner@169 }.
|
||||
type AddPermissionlessDelegatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Describes the validator
|
||||
Validator `serialize:"true" json:"validator"`
|
||||
// ID of the chain this validator is validating
|
||||
Chain ids.ID `serialize:"true" json:"chainID"`
|
||||
// Where to send staked tokens when done validating
|
||||
StakeOuts []*lux.TransferableOutput `serialize:"true" json:"stake"`
|
||||
// Where to send staking rewards when done validating
|
||||
DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [AddPermissionlessDelegatorTx]. Also sets the [rt] to the given [vm.rt] so
|
||||
// that the addresses can be json marshalled into human readable format
|
||||
func (tx *AddPermissionlessDelegatorTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, out := range tx.StakeOuts {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
const (
|
||||
offAPDValidator = spendSize // 77: inline Validator (44B)
|
||||
offAPDChain = offAPDValidator + validatorSize // 121: chain id (32B)
|
||||
offAPDStakeOuts = offAPDChain + 32 // 153: stake-outs list ptr (8B)
|
||||
offAPDStakeAddrs = offAPDStakeOuts + 8 // 161: stake-outs owner-addr array ptr (8B)
|
||||
offAPDRewardsThreshold = offAPDStakeAddrs + 8 // 169: rewards owner threshold (u32)
|
||||
offAPDRewardsLocktime = offAPDRewardsThreshold + 4 // 173: rewards owner locktime (u64)
|
||||
offAPDRewardsAddrs = offAPDRewardsLocktime + 8 // 181: rewards owner addr array ptr (8B)
|
||||
addPermissionlessDelegatorSize = offAPDRewardsAddrs + 8 // 189
|
||||
)
|
||||
|
||||
// NewAddPermissionlessDelegatorTx builds the tx into a fresh zap buffer.
|
||||
func NewAddPermissionlessDelegatorTx(base *lux.BaseTx, validator Validator, chain ids.ID, stakeOuts []*lux.TransferableOutput, delegationRewardsOwner fx.Owner) (*AddPermissionlessDelegatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 1024 + addPermissionlessDelegatorSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Owner doesn't have InitRuntime method
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) ChainID() ids.ID {
|
||||
return tx.Chain
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) NodeID() ids.NodeID {
|
||||
return tx.Validator.NodeID
|
||||
}
|
||||
|
||||
func (*AddPermissionlessDelegatorTx) PublicKey() (*bls.PublicKey, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) PendingPriority() Priority {
|
||||
if tx.Chain == constants.PrimaryNetworkID {
|
||||
return PrimaryNetworkDelegatorPermissionlessPendingPriority
|
||||
stakeListOff, stakeListCount, stakeAddrOff, stakeAddrCount, err := writeExtraOuts(b, stakeOuts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ChainPermissionlessDelegatorPendingPriority
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) CurrentPriority() Priority {
|
||||
if tx.Chain == constants.PrimaryNetworkID {
|
||||
return PrimaryNetworkDelegatorCurrentPriority
|
||||
ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount, err := writeOwner(b, delegationRewardsOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ChainPermissionlessDelegatorCurrentPriority
|
||||
|
||||
ob := b.StartObject(addPermissionlessDelegatorSize)
|
||||
setEnvelope(ob, kindAddPermissionlessDelegator, base, p)
|
||||
setValidator(ob, offAPDValidator, validator)
|
||||
setID(ob, offAPDChain, chain)
|
||||
ob.SetList(offAPDStakeOuts, stakeListOff, stakeListCount)
|
||||
ob.SetList(offAPDStakeAddrs, stakeAddrOff, stakeAddrCount)
|
||||
setOwner(ob, offAPDRewardsThreshold, offAPDRewardsLocktime, offAPDRewardsAddrs, ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &AddPermissionlessDelegatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) Stake() []*lux.TransferableOutput {
|
||||
return tx.StakeOuts
|
||||
// Validator describes the validator being delegated to (offset read).
|
||||
func (tx *AddPermissionlessDelegatorTx) Validator() Validator {
|
||||
return readValidator(tx.root(), offAPDValidator)
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessDelegatorTx) RewardsOwner() fx.Owner {
|
||||
return tx.DelegationRewardsOwner
|
||||
// Chain is the id of the chain this validator is validating (offset read).
|
||||
func (tx *AddPermissionlessDelegatorTx) Chain() ids.ID { return readID(tx.root(), offAPDChain) }
|
||||
|
||||
// StakeOuts is where staked tokens go when done validating (offset read).
|
||||
func (tx *AddPermissionlessDelegatorTx) StakeOuts() []*lux.TransferableOutput {
|
||||
return readExtraOuts(tx.root(), offAPDStakeOuts, offAPDStakeAddrs)
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid
|
||||
// DelegationRewardsOwner is where staking rewards go when done validating.
|
||||
func (tx *AddPermissionlessDelegatorTx) DelegationRewardsOwner() fx.Owner {
|
||||
return readOwner(tx.root(), offAPDRewardsThreshold, offAPDRewardsLocktime, offAPDRewardsAddrs)
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid.
|
||||
func (tx *AddPermissionlessDelegatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case len(tx.StakeOuts) == 0: // Ensure there is provided stake
|
||||
}
|
||||
stakeOuts := tx.StakeOuts()
|
||||
if len(stakeOuts) == 0 { // Ensure there is provided stake
|
||||
return errNoStake
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return fmt.Errorf("failed to verify BaseTx: %w", err)
|
||||
}
|
||||
if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil {
|
||||
v := tx.Validator()
|
||||
if err := verify.All(&v, tx.DelegationRewardsOwner()); err != nil {
|
||||
return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
|
||||
}
|
||||
|
||||
for _, out := range tx.StakeOuts {
|
||||
for _, out := range stakeOuts {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("failed to verify output: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
firstStakeOutput := tx.StakeOuts[0]
|
||||
firstStakeOutput := stakeOuts[0]
|
||||
stakedAssetID := firstStakeOutput.AssetID()
|
||||
totalStakeWeight := firstStakeOutput.Output().Amount()
|
||||
for _, out := range tx.StakeOuts[1:] {
|
||||
for _, out := range stakeOuts[1:] {
|
||||
newWeight, err := safemath.Add(totalStakeWeight, out.Output().Amount())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -124,18 +127,15 @@ func (tx *AddPermissionlessDelegatorTx) SyntacticVerify(rt *runtime.Runtime) err
|
||||
}
|
||||
|
||||
switch {
|
||||
case !lux.IsSortedTransferableOutputs(tx.StakeOuts):
|
||||
case !lux.IsSortedTransferableOutputs(stakeOuts):
|
||||
return errOutputsNotSorted
|
||||
case totalStakeWeight != tx.Wght:
|
||||
case totalStakeWeight != v.Wght:
|
||||
return fmt.Errorf("%w, delegator weight %d total stake weight %d",
|
||||
errDelegatorWeightMismatch,
|
||||
tx.Wght,
|
||||
v.Wght,
|
||||
totalStakeWeight,
|
||||
)
|
||||
}
|
||||
|
||||
// cache that this is valid
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -143,8 +143,7 @@ func (tx *AddPermissionlessDelegatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.AddPermissionlessDelegatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
// Initialize is a no-op; Runtime is passed explicitly to InitRuntime.
|
||||
func (tx *AddPermissionlessDelegatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -8,22 +8,20 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
safemath "github.com/luxfi/math"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/node/vms/platformvm/reward"
|
||||
"github.com/luxfi/node/vms/platformvm/signer"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ValidatorTx = (*AddPermissionlessValidatorTx)(nil)
|
||||
_ ScheduledStaker = (*AddPermissionlessDelegatorTx)(nil)
|
||||
_ UnsignedTx = (*AddPermissionlessValidatorTx)(nil)
|
||||
|
||||
errEmptyNodeID = errors.New("validator nodeID cannot be empty")
|
||||
errNoStake = errors.New("no stake")
|
||||
@@ -32,117 +30,131 @@ var (
|
||||
errValidatorWeightMismatch = errors.New("validator weight mismatch")
|
||||
)
|
||||
|
||||
// AddPermissionlessValidatorTx is an unsigned addPermissionlessValidatorTx
|
||||
// AddPermissionlessValidatorTx is an unsigned addPermissionlessValidatorTx.
|
||||
// The struct IS the wire: it holds the zap buffer and reads its fields by
|
||||
// offset. No codec, no marshal.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Validator@77, Chain@121,
|
||||
// Signer@153, StakeOuts@298, ValidatorRewardsOwner@314,
|
||||
// DelegatorRewardsOwner@334, DelegationShares@354 }.
|
||||
type AddPermissionlessValidatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Describes the validator
|
||||
Validator `serialize:"true" json:"validator"`
|
||||
// ID of the chain this validator is validating
|
||||
Chain ids.ID `serialize:"true" json:"chainID"`
|
||||
// If the [Chain] is the primary network, [Signer] is the BLS key for this
|
||||
// validator. If the [Chain] is not the primary network, this value is the
|
||||
// empty signer
|
||||
// Note: We do not enforce that the BLS key is unique across all validators.
|
||||
// This means that validators can share a key if they so choose.
|
||||
// However, a NodeID does uniquely map to a BLS key
|
||||
Signer signer.Signer `serialize:"true" json:"signer"`
|
||||
// Where to send staked tokens when done validating
|
||||
StakeOuts []*lux.TransferableOutput `serialize:"true" json:"stake"`
|
||||
// Where to send validation rewards when done validating
|
||||
ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"`
|
||||
// Where to send delegation rewards when done validating
|
||||
DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"`
|
||||
// Fee this validator charges delegators as a percentage, times 10,000
|
||||
// For example, if this validator has DelegationShares=300,000 then they
|
||||
// take 30% of rewards from delegators
|
||||
DelegationShares uint32 `serialize:"true" json:"shares"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [AddPermissionlessValidatorTx]. Also sets the [rt] to the given [vm.rt] so
|
||||
// that the addresses can be json marshalled into human readable format
|
||||
func (tx *AddPermissionlessValidatorTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, out := range tx.StakeOuts {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
const (
|
||||
offAPVValidator = spendSize // 77: inline Validator (44B)
|
||||
offAPVChain = offAPVValidator + validatorSize // 121: chain id (32B)
|
||||
offAPVSigner = offAPVChain + 32 // 153: inline Signer (145B)
|
||||
offAPVStakeOuts = offAPVSigner + signerSize // 298: stake-outs list ptr (8B)
|
||||
offAPVStakeAddrs = offAPVStakeOuts + 8 // 306: stake-outs owner-addr array ptr (8B)
|
||||
offAPVValRewardsThreshold = offAPVStakeAddrs + 8 // 314: validator rewards owner threshold (u32)
|
||||
offAPVValRewardsLocktime = offAPVValRewardsThreshold + 4 // 318: validator rewards owner locktime (u64)
|
||||
offAPVValRewardsAddrs = offAPVValRewardsLocktime + 8 // 326: validator rewards owner addr array ptr (8B)
|
||||
offAPVDelRewardsThreshold = offAPVValRewardsAddrs + 8 // 334: delegator rewards owner threshold (u32)
|
||||
offAPVDelRewardsLocktime = offAPVDelRewardsThreshold + 4 // 338: delegator rewards owner locktime (u64)
|
||||
offAPVDelRewardsAddrs = offAPVDelRewardsLocktime + 8 // 346: delegator rewards owner addr array ptr (8B)
|
||||
offAPVDelegationShares = offAPVDelRewardsAddrs + 8 // 354: delegation shares (u32)
|
||||
addPermissionlessValidatorSize = offAPVDelegationShares + 4 // 358
|
||||
)
|
||||
|
||||
// NewAddPermissionlessValidatorTx builds the tx into a fresh zap buffer.
|
||||
func NewAddPermissionlessValidatorTx(base *lux.BaseTx, validator Validator, chain ids.ID, sig signer.Signer, stakeOuts []*lux.TransferableOutput, validatorRewardsOwner fx.Owner, delegatorRewardsOwner fx.Owner, delegationShares uint32) (*AddPermissionlessValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 1024 + addPermissionlessValidatorSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Owner doesn't have InitRuntime method
|
||||
// tx.ValidatorRewardsOwner.InitRuntime(ctx)
|
||||
// tx.DelegatorRewardsOwner.InitRuntime(ctx)
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) ChainID() ids.ID {
|
||||
return tx.Chain
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) NodeID() ids.NodeID {
|
||||
return tx.Validator.NodeID
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) PublicKey() (*bls.PublicKey, bool, error) {
|
||||
if err := tx.Signer.Verify(); err != nil {
|
||||
return nil, false, err
|
||||
stakeListOff, stakeListCount, stakeAddrOff, stakeAddrCount, err := writeExtraOuts(b, stakeOuts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := tx.Signer.Key()
|
||||
return key, key != nil, nil
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) PendingPriority() Priority {
|
||||
if tx.Chain == constants.PrimaryNetworkID {
|
||||
return PrimaryNetworkValidatorPendingPriority
|
||||
valThreshold, valLocktime, valAddrOff, valAddrCount, err := writeOwner(b, validatorRewardsOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ChainPermissionlessValidatorPendingPriority
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) CurrentPriority() Priority {
|
||||
if tx.Chain == constants.PrimaryNetworkID {
|
||||
return PrimaryNetworkValidatorCurrentPriority
|
||||
delThreshold, delLocktime, delAddrOff, delAddrCount, err := writeOwner(b, delegatorRewardsOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ChainPermissionlessValidatorCurrentPriority
|
||||
|
||||
ob := b.StartObject(addPermissionlessValidatorSize)
|
||||
setEnvelope(ob, kindAddPermissionlessValidator, base, p)
|
||||
setValidator(ob, offAPVValidator, validator)
|
||||
setID(ob, offAPVChain, chain)
|
||||
if err := setSigner(ob, offAPVSigner, sig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob.SetList(offAPVStakeOuts, stakeListOff, stakeListCount)
|
||||
ob.SetList(offAPVStakeAddrs, stakeAddrOff, stakeAddrCount)
|
||||
setOwner(ob, offAPVValRewardsThreshold, offAPVValRewardsLocktime, offAPVValRewardsAddrs, valThreshold, valLocktime, valAddrOff, valAddrCount)
|
||||
setOwner(ob, offAPVDelRewardsThreshold, offAPVDelRewardsLocktime, offAPVDelRewardsAddrs, delThreshold, delLocktime, delAddrOff, delAddrCount)
|
||||
ob.SetUint32(offAPVDelegationShares, delegationShares)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &AddPermissionlessValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) Stake() []*lux.TransferableOutput {
|
||||
return tx.StakeOuts
|
||||
// Validator describes the validator (offset read).
|
||||
func (tx *AddPermissionlessValidatorTx) Validator() Validator {
|
||||
return readValidator(tx.root(), offAPVValidator)
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) ValidationRewardsOwner() fx.Owner {
|
||||
return tx.ValidatorRewardsOwner
|
||||
// Chain is the id of the chain this validator is validating (offset read).
|
||||
func (tx *AddPermissionlessValidatorTx) Chain() ids.ID { return readID(tx.root(), offAPVChain) }
|
||||
|
||||
// Signer is the BLS key for this validator (empty signer off the primary
|
||||
// network) (offset read).
|
||||
func (tx *AddPermissionlessValidatorTx) Signer() signer.Signer {
|
||||
return readSigner(tx.root(), offAPVSigner)
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) DelegationRewardsOwner() fx.Owner {
|
||||
return tx.DelegatorRewardsOwner
|
||||
// StakeOuts is where staked tokens go when done validating (offset read).
|
||||
func (tx *AddPermissionlessValidatorTx) StakeOuts() []*lux.TransferableOutput {
|
||||
return readExtraOuts(tx.root(), offAPVStakeOuts, offAPVStakeAddrs)
|
||||
}
|
||||
|
||||
func (tx *AddPermissionlessValidatorTx) Shares() uint32 {
|
||||
return tx.DelegationShares
|
||||
// ValidatorRewardsOwner is where validation rewards go when done validating.
|
||||
func (tx *AddPermissionlessValidatorTx) ValidatorRewardsOwner() fx.Owner {
|
||||
return readOwner(tx.root(), offAPVValRewardsThreshold, offAPVValRewardsLocktime, offAPVValRewardsAddrs)
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid
|
||||
// DelegatorRewardsOwner is where delegation rewards go when done validating.
|
||||
func (tx *AddPermissionlessValidatorTx) DelegatorRewardsOwner() fx.Owner {
|
||||
return readOwner(tx.root(), offAPVDelRewardsThreshold, offAPVDelRewardsLocktime, offAPVDelRewardsAddrs)
|
||||
}
|
||||
|
||||
// DelegationShares is the fee (times 10,000) charged to delegators.
|
||||
func (tx *AddPermissionlessValidatorTx) DelegationShares() uint32 {
|
||||
return tx.root().Uint32(offAPVDelegationShares)
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid.
|
||||
func (tx *AddPermissionlessValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case tx.Validator.NodeID == ids.EmptyNodeID:
|
||||
}
|
||||
v := tx.Validator()
|
||||
stakeOuts := tx.StakeOuts()
|
||||
switch {
|
||||
case v.NodeID == ids.EmptyNodeID:
|
||||
return errEmptyNodeID
|
||||
case len(tx.StakeOuts) == 0: // Ensure there is provided stake
|
||||
case len(stakeOuts) == 0: // Ensure there is provided stake
|
||||
return errNoStake
|
||||
case tx.DelegationShares > reward.PercentDenominator:
|
||||
case tx.DelegationShares() > reward.PercentDenominator:
|
||||
return errTooManyShares
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return fmt.Errorf("failed to verify BaseTx: %w", err)
|
||||
}
|
||||
if err := verify.All(&tx.Validator, tx.Signer, tx.ValidatorRewardsOwner, tx.DelegatorRewardsOwner); err != nil {
|
||||
sig := tx.Signer()
|
||||
if err := verify.All(&v, sig, tx.ValidatorRewardsOwner(), tx.DelegatorRewardsOwner()); err != nil {
|
||||
return fmt.Errorf("failed to verify validator, signer, or rewards owners: %w", err)
|
||||
}
|
||||
|
||||
hasKey := tx.Signer.Key() != nil
|
||||
isPrimaryNetwork := tx.Chain == constants.PrimaryNetworkID
|
||||
hasKey := sig.Key() != nil
|
||||
isPrimaryNetwork := tx.Chain() == constants.PrimaryNetworkID
|
||||
if hasKey != isPrimaryNetwork {
|
||||
return fmt.Errorf(
|
||||
"%w: hasKey=%v != isPrimaryNetwork=%v",
|
||||
@@ -152,16 +164,16 @@ func (tx *AddPermissionlessValidatorTx) SyntacticVerify(rt *runtime.Runtime) err
|
||||
)
|
||||
}
|
||||
|
||||
for _, out := range tx.StakeOuts {
|
||||
for _, out := range stakeOuts {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("failed to verify output: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
firstStakeOutput := tx.StakeOuts[0]
|
||||
firstStakeOutput := stakeOuts[0]
|
||||
stakedAssetID := firstStakeOutput.AssetID()
|
||||
totalStakeWeight := firstStakeOutput.Output().Amount()
|
||||
for _, out := range tx.StakeOuts[1:] {
|
||||
for _, out := range stakeOuts[1:] {
|
||||
newWeight, err := safemath.Add(totalStakeWeight, out.Output().Amount())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -175,14 +187,11 @@ func (tx *AddPermissionlessValidatorTx) SyntacticVerify(rt *runtime.Runtime) err
|
||||
}
|
||||
|
||||
switch {
|
||||
case !lux.IsSortedTransferableOutputs(tx.StakeOuts):
|
||||
case !lux.IsSortedTransferableOutputs(stakeOuts):
|
||||
return errOutputsNotSorted
|
||||
case totalStakeWeight != tx.Wght:
|
||||
return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Wght, totalStakeWeight)
|
||||
case totalStakeWeight != v.Wght:
|
||||
return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, v.Wght, totalStakeWeight)
|
||||
}
|
||||
|
||||
// cache that this is valid
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -190,8 +199,7 @@ func (tx *AddPermissionlessValidatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.AddPermissionlessValidatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
// Initialize is a no-op; Runtime is passed explicitly to InitRuntime.
|
||||
func (tx *AddPermissionlessValidatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -7,109 +7,106 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
safemath "github.com/luxfi/math"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/node/vms/platformvm/reward"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ValidatorTx = (*AddValidatorTx)(nil)
|
||||
_ ScheduledStaker = (*AddValidatorTx)(nil)
|
||||
_ UnsignedTx = (*AddValidatorTx)(nil)
|
||||
|
||||
errTooManyShares = fmt.Errorf("a staker can only require at most %d shares from delegators", reward.PercentDenominator)
|
||||
)
|
||||
|
||||
// AddValidatorTx is an unsigned addValidatorTx
|
||||
// AddValidatorTx is an unsigned addValidatorTx. The struct IS the wire: it
|
||||
// holds the zap buffer and reads its fields by offset. No codec, no marshal.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Validator@77, StakeOuts@121,
|
||||
// RewardsOwner@137, DelegationShares@157 }.
|
||||
type AddValidatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Describes the delegatee
|
||||
Validator `serialize:"true" json:"validator"`
|
||||
// Where to send staked tokens when done validating
|
||||
StakeOuts []*lux.TransferableOutput `serialize:"true" json:"stake"`
|
||||
// Where to send staking rewards when done validating
|
||||
RewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
|
||||
// Fee this validator charges delegators as a percentage, times 10,000
|
||||
// For example, if this validator has DelegationShares=300,000 then they
|
||||
// take 30% of rewards from delegators
|
||||
DelegationShares uint32 `serialize:"true" json:"shares"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [AddValidatorTx]. Also sets the [rt] to the given [vm.rt] so that
|
||||
// the addresses can be json marshalled into human readable format
|
||||
func (tx *AddValidatorTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, out := range tx.StakeOuts {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
const (
|
||||
offAVValidator = spendSize // 77: inline Validator (44B)
|
||||
offAVStakeOuts = offAVValidator + validatorSize // 121: stake-outs list ptr (8B)
|
||||
offAVStakeAddrs = offAVStakeOuts + 8 // 129: stake-outs owner-addr array ptr (8B)
|
||||
offAVRewardsThreshold = offAVStakeAddrs + 8 // 137: rewards owner threshold (u32)
|
||||
offAVRewardsLocktime = offAVRewardsThreshold + 4 // 141: rewards owner locktime (u64)
|
||||
offAVRewardsAddrs = offAVRewardsLocktime + 8 // 149: rewards owner addr array ptr (8B)
|
||||
offAVDelegationShares = offAVRewardsAddrs + 8 // 157: delegation shares (u32)
|
||||
addValidatorSize = offAVDelegationShares + 4 // 161
|
||||
)
|
||||
|
||||
// NewAddValidatorTx builds the tx into a fresh zap buffer.
|
||||
func NewAddValidatorTx(base *lux.BaseTx, validator Validator, stakeOuts []*lux.TransferableOutput, rewardsOwner fx.Owner, delegationShares uint32) (*AddValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 1024 + addValidatorSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Owner doesn't have InitRuntime method
|
||||
stakeListOff, stakeListCount, stakeAddrOff, stakeAddrCount, err := writeExtraOuts(b, stakeOuts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount, err := writeOwner(b, rewardsOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(addValidatorSize)
|
||||
setEnvelope(ob, kindAddValidator, base, p)
|
||||
setValidator(ob, offAVValidator, validator)
|
||||
ob.SetList(offAVStakeOuts, stakeListOff, stakeListCount)
|
||||
ob.SetList(offAVStakeAddrs, stakeAddrOff, stakeAddrCount)
|
||||
setOwner(ob, offAVRewardsThreshold, offAVRewardsLocktime, offAVRewardsAddrs, ownerThreshold, ownerLocktime, ownerAddrOff, ownerAddrCount)
|
||||
ob.SetUint32(offAVDelegationShares, delegationShares)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &AddValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
func (*AddValidatorTx) ChainID() ids.ID {
|
||||
return constants.PrimaryNetworkID
|
||||
// Validator describes the delegatee (offset read).
|
||||
func (tx *AddValidatorTx) Validator() Validator { return readValidator(tx.root(), offAVValidator) }
|
||||
|
||||
// StakeOuts is where staked tokens go when done validating (offset read).
|
||||
func (tx *AddValidatorTx) StakeOuts() []*lux.TransferableOutput {
|
||||
return readExtraOuts(tx.root(), offAVStakeOuts, offAVStakeAddrs)
|
||||
}
|
||||
|
||||
func (tx *AddValidatorTx) NodeID() ids.NodeID {
|
||||
return tx.Validator.NodeID
|
||||
// RewardsOwner is where staking rewards go when done validating.
|
||||
func (tx *AddValidatorTx) RewardsOwner() fx.Owner {
|
||||
return readOwner(tx.root(), offAVRewardsThreshold, offAVRewardsLocktime, offAVRewardsAddrs)
|
||||
}
|
||||
|
||||
func (*AddValidatorTx) PublicKey() (*bls.PublicKey, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
// DelegationShares is the fee (times 10,000) charged to delegators.
|
||||
func (tx *AddValidatorTx) DelegationShares() uint32 { return tx.root().Uint32(offAVDelegationShares) }
|
||||
|
||||
func (*AddValidatorTx) PendingPriority() Priority {
|
||||
return PrimaryNetworkValidatorPendingPriority
|
||||
}
|
||||
|
||||
func (*AddValidatorTx) CurrentPriority() Priority {
|
||||
return PrimaryNetworkValidatorCurrentPriority
|
||||
}
|
||||
|
||||
func (tx *AddValidatorTx) Stake() []*lux.TransferableOutput {
|
||||
return tx.StakeOuts
|
||||
}
|
||||
|
||||
func (tx *AddValidatorTx) ValidationRewardsOwner() fx.Owner {
|
||||
return tx.RewardsOwner
|
||||
}
|
||||
|
||||
func (tx *AddValidatorTx) DelegationRewardsOwner() fx.Owner {
|
||||
return tx.RewardsOwner
|
||||
}
|
||||
|
||||
func (tx *AddValidatorTx) Shares() uint32 {
|
||||
return tx.DelegationShares
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff [tx] is valid
|
||||
// SyntacticVerify returns nil iff [tx] is valid.
|
||||
func (tx *AddValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case tx.DelegationShares > reward.PercentDenominator: // Ensure delegators shares are in the allowed amount
|
||||
}
|
||||
if tx.DelegationShares() > reward.PercentDenominator { // shares in the allowed amount
|
||||
return errTooManyShares
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return fmt.Errorf("failed to verify BaseTx: %w", err)
|
||||
}
|
||||
if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
|
||||
v := tx.Validator()
|
||||
if err := verify.All(&v, tx.RewardsOwner()); err != nil {
|
||||
return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
|
||||
}
|
||||
|
||||
stakeOuts := tx.StakeOuts()
|
||||
totalStakeWeight := uint64(0)
|
||||
for _, out := range tx.StakeOuts {
|
||||
for _, out := range stakeOuts {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("failed to verify output: %w", err)
|
||||
}
|
||||
@@ -127,14 +124,11 @@ func (tx *AddValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
}
|
||||
|
||||
switch {
|
||||
case !lux.IsSortedTransferableOutputs(tx.StakeOuts):
|
||||
case !lux.IsSortedTransferableOutputs(stakeOuts):
|
||||
return errOutputsNotSorted
|
||||
case totalStakeWeight != tx.Wght:
|
||||
return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Wght, totalStakeWeight)
|
||||
case totalStakeWeight != v.Wght:
|
||||
return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, v.Wght, totalStakeWeight)
|
||||
}
|
||||
|
||||
// cache that this is valid
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -142,8 +136,7 @@ func (tx *AddValidatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.AddValidatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
// Initialize is a no-op; Runtime is passed explicitly to InitRuntime.
|
||||
func (tx *AddValidatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -9,11 +9,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/utils"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,86 +23,71 @@ var (
|
||||
errInputsNotSortedUnique = errors.New("inputs not sorted and unique")
|
||||
)
|
||||
|
||||
// BaseTx contains fields common to many transaction types. It should be
|
||||
// embedded in transaction implementations.
|
||||
// BaseTx is the bare spending envelope: kind + NetworkID/BlockchainID/Outs/
|
||||
// Ins/Memo, no delta fields. The struct IS the wire — it embeds spendingTx,
|
||||
// which holds the zap buffer and serves the whole envelope surface (Bytes/
|
||||
// NetworkID/Outputs/InputIDs/Memo/...). Delta-carrying tx types embed
|
||||
// spendingTx the same way and add their own field accessors.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76 } (kind=kindBase).
|
||||
type BaseTx struct {
|
||||
lux.BaseTx `serialize:"true"`
|
||||
|
||||
// true iff this transaction has already passed syntactic verification
|
||||
SyntacticallyVerified bool `json:"-"`
|
||||
|
||||
unsignedBytes []byte // Unsigned byte representation of this data
|
||||
spendingTx
|
||||
}
|
||||
|
||||
func (tx *BaseTx) SetBytes(unsignedBytes []byte) {
|
||||
tx.unsignedBytes = unsignedBytes
|
||||
}
|
||||
|
||||
func (tx *BaseTx) Bytes() []byte {
|
||||
return tx.unsignedBytes
|
||||
}
|
||||
|
||||
func (tx *BaseTx) InputIDs() set.Set[ids.ID] {
|
||||
inputIDs := set.NewSet[ids.ID](len(tx.Ins))
|
||||
for _, in := range tx.Ins {
|
||||
inputIDs.Add(in.InputID())
|
||||
// NewBaseTx builds a bare spending tx into a fresh zap buffer — the one place
|
||||
// the envelope fields become bytes (construction, not serialization).
|
||||
func NewBaseTx(base *lux.BaseTx) (*BaseTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + spendSize)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inputIDs
|
||||
}
|
||||
|
||||
func (tx *BaseTx) Outputs() []*lux.TransferableOutput {
|
||||
return tx.Outs
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this [BaseTx]. Also
|
||||
// sets the [rt] to the given [vm.rt] so that the addresses can be json
|
||||
// marshalled into human readable format
|
||||
func (tx *BaseTx) InitRuntime(rt *runtime.Runtime) {
|
||||
for _, in := range tx.BaseTx.Ins {
|
||||
in.FxID = secp256k1fx.ID
|
||||
}
|
||||
for _, out := range tx.BaseTx.Outs {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
ob := b.StartObject(spendSize)
|
||||
setEnvelope(ob, kindBase, base, p)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BaseTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// InitializeRuntime is a no-op. Runtime is passed explicitly.
|
||||
func (tx *BaseTx) Initialize(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyntacticVerify returns nil iff this tx is well formed
|
||||
// SyntacticVerify returns nil iff this tx is well formed.
|
||||
func (tx *BaseTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
if err := tx.BaseTx.Verify(rt); err != nil {
|
||||
return verifyBaseTx(tx.baseTx(), rt)
|
||||
}
|
||||
|
||||
func (tx *BaseTx) Visit(visitor Visitor) error { return visitor.BaseTx(tx) }
|
||||
|
||||
func (tx *BaseTx) Initialize(context.Context) error { return nil }
|
||||
|
||||
// verifyBaseTx runs the shared spending-envelope checks (metadata, per-output
|
||||
// and per-input verification, canonical ordering). Every spending tx type
|
||||
// composes this over its own delta-field checks — one envelope validator,
|
||||
// reused.
|
||||
func verifyBaseTx(base lux.BaseTx, rt *runtime.Runtime) error {
|
||||
if err := base.Verify(rt); err != nil {
|
||||
return fmt.Errorf("metadata failed verification: %w", err)
|
||||
}
|
||||
for _, out := range tx.Outs {
|
||||
for _, out := range base.Outs {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("output failed verification: %w", err)
|
||||
}
|
||||
}
|
||||
for _, in := range tx.Ins {
|
||||
for _, in := range base.Ins {
|
||||
if err := in.Verify(); err != nil {
|
||||
return fmt.Errorf("input failed verification: %w", err)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case !lux.IsSortedTransferableOutputs(tx.Outs):
|
||||
case !lux.IsSortedTransferableOutputs(base.Outs):
|
||||
return errOutputsNotSorted
|
||||
case !utils.IsSortedAndUnique(tx.Ins):
|
||||
case !utils.IsSortedAndUnique(base.Ins):
|
||||
return errInputsNotSortedUnique
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *BaseTx) Visit(visitor Visitor) error {
|
||||
return visitor.BaseTx(tx)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"errors"
|
||||
"unicode"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/utils"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,65 +34,114 @@ var (
|
||||
errIllegalNameCharacter = errors.New("illegal name character")
|
||||
)
|
||||
|
||||
// CreateChainTx is an unsigned createChainTx
|
||||
// CreateChainTx creates a blockchain on a chain. The struct IS the wire: it
|
||||
// embeds the spending envelope and reads its delta fields by offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// ChainID 32B @ 77 (id: chain that validates this blockchain)
|
||||
// VMID 32B @ 109 (id: VM running on the new blockchain)
|
||||
// BlockchainName 8B @ 141 (text ptr: human readable name)
|
||||
// FxIDs 8B @ 149 (id list ptr: feature extensions)
|
||||
// GenesisData 8B @ 157 (bytes ptr: genesis state)
|
||||
// ChainAuth 8B @ 165 (auth ptr: sig-index list)
|
||||
type CreateChainTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// ID of the Chain that validates this blockchain
|
||||
ChainID ids.ID `serialize:"true" json:"chainID"`
|
||||
// A human readable name for the blockchain; need not be unique
|
||||
BlockchainName string `serialize:"true" json:"blockchainName"`
|
||||
// ID of the VM running on the new blockchain
|
||||
VMID ids.ID `serialize:"true" json:"vmID"`
|
||||
// IDs of the feature extensions running on the new blockchain
|
||||
FxIDs []ids.ID `serialize:"true" json:"fxIDs"`
|
||||
// Byte representation of genesis state of the new blockchain
|
||||
GenesisData []byte `serialize:"true" json:"genesisData"`
|
||||
// Authorizes this blockchain to be added to this chain
|
||||
ChainAuth verify.Verifiable `serialize:"true" json:"chainAuthorization"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offCreateChainID = spendSize // 77
|
||||
offCreateChainVMID = 109
|
||||
offCreateChainName = 141
|
||||
offCreateChainFxIDs = 149
|
||||
offCreateChainGenesis = 157
|
||||
offCreateChainAuth = 165
|
||||
sizeCreateChain = 173
|
||||
)
|
||||
|
||||
// NewCreateChainTx builds the tx into a fresh zap buffer.
|
||||
func NewCreateChainTx(base *lux.BaseTx, chainID ids.ID, blockchainName string, vmID ids.ID, fxIDs []ids.ID, genesisData []byte, chainAuth verify.Verifiable) (*CreateChainTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeCreateChain)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fxOff, fxCount := writeIDList(b, fxIDs)
|
||||
authOff, authCount, err := writeAuth(b, chainAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeCreateChain)
|
||||
setEnvelope(ob, kindCreateChain, base, p)
|
||||
setID(ob, offCreateChainID, chainID)
|
||||
setID(ob, offCreateChainVMID, vmID)
|
||||
ob.SetText(offCreateChainName, blockchainName)
|
||||
ob.SetList(offCreateChainFxIDs, fxOff, fxCount)
|
||||
ob.SetBytes(offCreateChainGenesis, genesisData)
|
||||
ob.SetList(offCreateChainAuth, authOff, authCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &CreateChainTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// ChainID is the ID of the chain that validates this blockchain (offset read).
|
||||
func (tx *CreateChainTx) ChainID() ids.ID { return readID(tx.root(), offCreateChainID) }
|
||||
|
||||
// VMID is the ID of the VM running on the new blockchain.
|
||||
func (tx *CreateChainTx) VMID() ids.ID { return readID(tx.root(), offCreateChainVMID) }
|
||||
|
||||
// BlockchainName is a human-readable (non-unique) name for the blockchain.
|
||||
func (tx *CreateChainTx) BlockchainName() string { return tx.root().Text(offCreateChainName) }
|
||||
|
||||
// FxIDs are the IDs of the feature extensions running on the new blockchain.
|
||||
func (tx *CreateChainTx) FxIDs() []ids.ID { return readIDList(tx.root(), offCreateChainFxIDs) }
|
||||
|
||||
// GenesisData is the byte representation of the new blockchain's genesis state.
|
||||
func (tx *CreateChainTx) GenesisData() []byte {
|
||||
if g := tx.root().Bytes(offCreateChainGenesis); len(g) > 0 {
|
||||
return append([]byte(nil), g...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChainAuth authorizes this blockchain to be added to the chain.
|
||||
func (tx *CreateChainTx) ChainAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offCreateChainAuth)
|
||||
}
|
||||
|
||||
func (tx *CreateChainTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case tx.ChainID == constants.PrimaryNetworkID:
|
||||
case tx.ChainID() == constants.PrimaryNetworkID:
|
||||
return ErrCantValidatePrimaryNetwork
|
||||
case len(tx.BlockchainName) > MaxNameLen:
|
||||
case len(tx.BlockchainName()) > MaxNameLen:
|
||||
return errNameTooLong
|
||||
case tx.VMID == ids.Empty:
|
||||
case tx.VMID() == ids.Empty:
|
||||
return errInvalidVMID
|
||||
case !utils.IsSortedAndUnique(tx.FxIDs):
|
||||
case !utils.IsSortedAndUnique(tx.FxIDs()):
|
||||
return errFxIDsNotSortedAndUnique
|
||||
case len(tx.GenesisData) > MaxGenesisLen:
|
||||
case len(tx.GenesisData()) > MaxGenesisLen:
|
||||
return errGenesisTooLong
|
||||
}
|
||||
|
||||
for _, r := range tx.BlockchainName {
|
||||
for _, r := range tx.BlockchainName() {
|
||||
if r > unicode.MaxASCII || (!unicode.IsLetter(r) && !unicode.IsNumber(r) && r != ' ') {
|
||||
return errIllegalNameCharacter
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.ChainAuth.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return tx.ChainAuth().Verify()
|
||||
}
|
||||
|
||||
func (tx *CreateChainTx) Visit(visitor Visitor) error {
|
||||
return visitor.CreateChainTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *CreateChainTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
// Initialize is a no-op; the struct is already the wire.
|
||||
func (tx *CreateChainTx) Initialize(ctx context.Context) error { return nil }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -6,55 +6,71 @@ package txs
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var _ UnsignedTx = (*CreateNetworkTx)(nil)
|
||||
|
||||
// CreateNetworkTx is an unsigned proposal to create a new chain
|
||||
// CreateNetworkTx is an unsigned proposal to create a new network. The struct
|
||||
// IS the wire: it embeds spendingTx (the envelope) and adds the owner header
|
||||
// inline (threshold + locktime + addr-list ptr).
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Owner{ threshold:u32@77,
|
||||
// locktime:u64@81, addrs:listptr@89 } } (kind=kindCreateNetwork).
|
||||
type CreateNetworkTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Who is authorized to manage this chain
|
||||
Owner fx.Owner `serialize:"true" json:"owner"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [CreateNetworkTx]. Also sets the [rt] to the given [vm.rt] so that
|
||||
// the addresses can be json marshalled into human readable format
|
||||
func (tx *CreateNetworkTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
// Owner doesn't have InitRuntime method
|
||||
const (
|
||||
offCreateNetOwnerThreshold = spendSize // u32
|
||||
offCreateNetOwnerLocktime = 81 // u64
|
||||
offCreateNetOwnerAddrs = 89 // list ptr (8B)
|
||||
sizeCreateNet = 97
|
||||
)
|
||||
|
||||
// NewCreateNetworkTx builds the tx into a fresh zap buffer.
|
||||
func NewCreateNetworkTx(base *lux.BaseTx, owner fx.Owner) (*CreateNetworkTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + sizeCreateNet)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
threshold, locktime, addrOff, addrCount, err := writeOwner(b, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := b.StartObject(sizeCreateNet)
|
||||
setEnvelope(ob, kindCreateNetwork, base, p)
|
||||
setOwner(ob, offCreateNetOwnerThreshold, offCreateNetOwnerLocktime, offCreateNetOwnerAddrs, threshold, locktime, addrOff, addrCount)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CreateNetworkTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// SyntacticVerify verifies that this transaction is well-formed
|
||||
// Owner is who is authorized to manage this network (offset read).
|
||||
func (tx *CreateNetworkTx) Owner() fx.Owner {
|
||||
return readOwner(tx.root(), offCreateNetOwnerThreshold, offCreateNetOwnerLocktime, offCreateNetOwnerAddrs)
|
||||
}
|
||||
|
||||
// SyntacticVerify verifies that this transaction is well-formed.
|
||||
func (tx *CreateNetworkTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Owner.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return tx.Owner().Verify()
|
||||
}
|
||||
|
||||
func (tx *CreateNetworkTx) Visit(visitor Visitor) error {
|
||||
return visitor.CreateNetworkTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *CreateNetworkTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
func (tx *CreateNetworkTx) Initialize(context.Context) error { return nil }
|
||||
|
||||
@@ -1,44 +1,75 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var _ UnsignedTx = (*DisableL1ValidatorTx)(nil)
|
||||
|
||||
// DisableL1ValidatorTx disables an L1 validator, proving authorization with a
|
||||
// credential. The struct IS the wire: it embeds spendingTx (the envelope) and
|
||||
// adds the validation id plus an auth sig-index list.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, ValidationID:32B@77,
|
||||
// DisableAuth:listptr@109 } (kind=kindDisableL1Validator).
|
||||
type DisableL1ValidatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// ID corresponding to the validator
|
||||
ValidationID ids.ID `serialize:"true" json:"validationID"`
|
||||
// Authorizes this validator to be disabled
|
||||
DisableAuth verify.Verifiable `serialize:"true" json:"disableAuthorization"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offDisableValidationID = spendSize // 32B
|
||||
offDisableAuth = 109 // list ptr (8B)
|
||||
sizeDisable = 117
|
||||
)
|
||||
|
||||
// NewDisableL1ValidatorTx builds the tx into a fresh zap buffer.
|
||||
func NewDisableL1ValidatorTx(base *lux.BaseTx, validationID ids.ID, disableAuth verify.Verifiable) (*DisableL1ValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + sizeDisable)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authOff, authCount, err := writeAuth(b, disableAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := b.StartObject(sizeDisable)
|
||||
setEnvelope(ob, kindDisableL1Validator, base, p)
|
||||
setID(ob, offDisableValidationID, validationID)
|
||||
ob.SetList(offDisableAuth, authOff, authCount)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DisableL1ValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// ValidationID is the id of the validator being disabled (offset read).
|
||||
func (tx *DisableL1ValidatorTx) ValidationID() ids.ID {
|
||||
return readID(tx.root(), offDisableValidationID)
|
||||
}
|
||||
|
||||
// DisableAuth authorizes this validator to be disabled (offset read).
|
||||
func (tx *DisableL1ValidatorTx) DisableAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offDisableAuth)
|
||||
}
|
||||
|
||||
func (tx *DisableL1ValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.DisableAuth.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return tx.DisableAuth().Verify()
|
||||
}
|
||||
|
||||
func (tx *DisableL1ValidatorTx) Visit(visitor Visitor) error {
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/node/vms/platformvm/stakeable"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -24,44 +22,71 @@ var (
|
||||
errNoExportOutputs = errors.New("no export outputs")
|
||||
)
|
||||
|
||||
// ExportTx is an unsigned exportTx
|
||||
// ExportTx sends funds to another chain. The struct IS the wire: it embeds the
|
||||
// spending envelope and reads its delta fields by offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// DestinationChain 32B @ 77 (id: chain to send the funds to)
|
||||
// ExportedOutputs 8B @ 109 (output list ptr)
|
||||
// OwnerAddrs 8B @ 117 (shared owner-address array ptr)
|
||||
type ExportTx struct {
|
||||
BaseTx `serialize:"true"`
|
||||
|
||||
// Which chain to send the funds to
|
||||
DestinationChain ids.ID `serialize:"true" json:"destinationChain"`
|
||||
|
||||
// Outputs that are exported to the chain
|
||||
ExportedOutputs []*lux.TransferableOutput `serialize:"true" json:"exportedOutputs"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [UnsignedExportTx]. Also sets the [rt] to the given [vm.rt] so that
|
||||
// the addresses can be json marshalled into human readable format
|
||||
func (tx *ExportTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, out := range tx.ExportedOutputs {
|
||||
out.FxID = secp256k1fx.ID
|
||||
out.InitRuntime(rt)
|
||||
const (
|
||||
offExportDestChain = spendSize // 77
|
||||
offExportOutputs = 109
|
||||
offExportAddrs = 117
|
||||
sizeExport = 125
|
||||
)
|
||||
|
||||
// NewExportTx builds the tx into a fresh zap buffer.
|
||||
func NewExportTx(base *lux.BaseTx, destinationChain ids.ID, exportedOutputs []*lux.TransferableOutput) (*ExportTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeExport)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listOff, listCount, addrOff, addrCount, err := writeExtraOuts(b, exportedOutputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeExport)
|
||||
setEnvelope(ob, kindExport, base, p)
|
||||
setID(ob, offExportDestChain, destinationChain)
|
||||
ob.SetList(offExportOutputs, listOff, listCount)
|
||||
ob.SetList(offExportAddrs, addrOff, addrCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &ExportTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// SyntacticVerify this transaction is well-formed
|
||||
// DestinationChain is the chain the exported funds are sent to (offset read).
|
||||
func (tx *ExportTx) DestinationChain() ids.ID { return readID(tx.root(), offExportDestChain) }
|
||||
|
||||
// ExportedOutputs are the outputs exported to the destination chain.
|
||||
func (tx *ExportTx) ExportedOutputs() []*lux.TransferableOutput {
|
||||
return readExtraOuts(tx.root(), offExportOutputs, offExportAddrs)
|
||||
}
|
||||
|
||||
// SyntacticVerify this transaction is well-formed.
|
||||
func (tx *ExportTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case len(tx.ExportedOutputs) == 0:
|
||||
}
|
||||
outs := tx.ExportedOutputs()
|
||||
if len(outs) == 0 {
|
||||
return errNoExportOutputs
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, out := range tx.ExportedOutputs {
|
||||
for _, out := range outs {
|
||||
if err := out.Verify(); err != nil {
|
||||
return fmt.Errorf("output failed verification: %w", err)
|
||||
}
|
||||
@@ -69,11 +94,9 @@ func (tx *ExportTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
return ErrWrongLocktime
|
||||
}
|
||||
}
|
||||
if !lux.IsSortedTransferableOutputs(tx.ExportedOutputs) {
|
||||
if !lux.IsSortedTransferableOutputs(outs) {
|
||||
return errOutputsNotSorted
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -81,8 +104,5 @@ func (tx *ExportTx) Visit(visitor Visitor) error {
|
||||
return visitor.ExportTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *ExportTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
// Initialize is a no-op; the struct is already the wire.
|
||||
func (tx *ExportTx) Initialize(ctx context.Context) error { return nil }
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/utils"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -24,67 +22,93 @@ var (
|
||||
errNoImportInputs = errors.New("tx has no imported inputs")
|
||||
)
|
||||
|
||||
// ImportTx is an unsigned importTx
|
||||
// ImportTx consumes funds produced on another chain. The struct IS the wire:
|
||||
// it embeds the spending envelope and reads its delta fields by offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// SourceChain 32B @ 77 (id: chain to consume the funds from)
|
||||
// ImportedInputs 8B @ 109 (input list ptr)
|
||||
// SigIndices 8B @ 117 (shared input sig-index array ptr)
|
||||
type ImportTx struct {
|
||||
BaseTx `serialize:"true"`
|
||||
|
||||
// Which chain to consume the funds from
|
||||
SourceChain ids.ID `serialize:"true" json:"sourceChain"`
|
||||
|
||||
// Inputs that consume UTXOs produced on the chain
|
||||
ImportedInputs []*lux.TransferableInput `serialize:"true" json:"importedInputs"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [ImportTx]. Also sets the [rt] to the given [vm.rt] so that
|
||||
// the addresses can be json marshalled into human readable format
|
||||
func (tx *ImportTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
for _, in := range tx.ImportedInputs {
|
||||
in.FxID = secp256k1fx.ID
|
||||
const (
|
||||
offImportSourceChain = spendSize // 77
|
||||
offImportInputs = 109
|
||||
offImportSigIndices = 117
|
||||
sizeImport = 125
|
||||
)
|
||||
|
||||
// NewImportTx builds the tx into a fresh zap buffer.
|
||||
func NewImportTx(base *lux.BaseTx, sourceChain ids.ID, importedInputs []*lux.TransferableInput) (*ImportTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeImport)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listOff, listCount, sigOff, sigCount, err := writeExtraIns(b, importedInputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeImport)
|
||||
setEnvelope(ob, kindImport, base, p)
|
||||
setID(ob, offImportSourceChain, sourceChain)
|
||||
ob.SetList(offImportInputs, listOff, listCount)
|
||||
ob.SetList(offImportSigIndices, sigOff, sigCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &ImportTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// InputUTXOs returns the UTXOIDs of the imported funds
|
||||
// SourceChain is the chain the imported funds are consumed from (offset read).
|
||||
func (tx *ImportTx) SourceChain() ids.ID { return readID(tx.root(), offImportSourceChain) }
|
||||
|
||||
// ImportedInputs are the inputs consuming UTXOs produced on the source chain.
|
||||
func (tx *ImportTx) ImportedInputs() []*lux.TransferableInput {
|
||||
return readExtraIns(tx.root(), offImportInputs, offImportSigIndices)
|
||||
}
|
||||
|
||||
// InputUTXOs returns the UTXOIDs of the imported funds.
|
||||
func (tx *ImportTx) InputUTXOs() set.Set[ids.ID] {
|
||||
set := set.NewSet[ids.ID](len(tx.ImportedInputs))
|
||||
for _, in := range tx.ImportedInputs {
|
||||
set.Add(in.InputID())
|
||||
ins := tx.ImportedInputs()
|
||||
s := set.NewSet[ids.ID](len(ins))
|
||||
for _, in := range ins {
|
||||
s.Add(in.InputID())
|
||||
}
|
||||
return set
|
||||
return s
|
||||
}
|
||||
|
||||
func (tx *ImportTx) InputIDs() set.Set[ids.ID] {
|
||||
inputs := tx.BaseTx.InputIDs()
|
||||
atomicInputs := tx.InputUTXOs()
|
||||
return inputs.Union(atomicInputs)
|
||||
inputs := tx.spendingTx.InputIDs()
|
||||
return inputs.Union(tx.InputUTXOs())
|
||||
}
|
||||
|
||||
// SyntacticVerify this transaction is well-formed
|
||||
// SyntacticVerify this transaction is well-formed.
|
||||
func (tx *ImportTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case len(tx.ImportedInputs) == 0:
|
||||
}
|
||||
ins := tx.ImportedInputs()
|
||||
if len(ins) == 0 {
|
||||
return errNoImportInputs
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, in := range tx.ImportedInputs {
|
||||
for _, in := range ins {
|
||||
if err := in.Verify(); err != nil {
|
||||
return fmt.Errorf("input failed verification: %w", err)
|
||||
}
|
||||
}
|
||||
if !utils.IsSortedAndUnique(tx.ImportedInputs) {
|
||||
if !utils.IsSortedAndUnique(ins) {
|
||||
return errInputsNotSortedUnique
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,8 +116,5 @@ func (tx *ImportTx) Visit(visitor Visitor) error {
|
||||
return visitor.ImportTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *ImportTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
// Initialize is a no-op; the struct is already the wire.
|
||||
func (tx *ImportTx) Initialize(ctx context.Context) error { return nil }
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -17,32 +18,60 @@ var (
|
||||
ErrZeroBalance = errors.New("balance must be greater than 0")
|
||||
)
|
||||
|
||||
// IncreaseL1ValidatorBalanceTx tops up an L1 validator's continuous-fee
|
||||
// balance. The struct IS the wire: it embeds spendingTx (the envelope) and
|
||||
// adds two delta fields read by offset.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, ValidationID:32B@77,
|
||||
// Balance:u64@109 } (kind=kindIncreaseL1ValidatorBalance).
|
||||
type IncreaseL1ValidatorBalanceTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// ID corresponding to the validator
|
||||
ValidationID ids.ID `serialize:"true" json:"validationID"`
|
||||
// Balance <= sum($LUX inputs) - sum($LUX outputs) - TxFee
|
||||
Balance uint64 `serialize:"true" json:"balance"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offIncreaseValidationID = spendSize // 32B
|
||||
offIncreaseBalance = 109 // u64
|
||||
sizeIncrease = 117
|
||||
)
|
||||
|
||||
// NewIncreaseL1ValidatorBalanceTx builds the tx into a fresh zap buffer.
|
||||
func NewIncreaseL1ValidatorBalanceTx(base *lux.BaseTx, validationID ids.ID, balance uint64) (*IncreaseL1ValidatorBalanceTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + sizeIncrease)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := b.StartObject(sizeIncrease)
|
||||
setEnvelope(ob, kindIncreaseL1ValidatorBalance, base, p)
|
||||
setID(ob, offIncreaseValidationID, validationID)
|
||||
ob.SetUint64(offIncreaseBalance, balance)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IncreaseL1ValidatorBalanceTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// ValidationID is the id of the validator being topped up (offset read).
|
||||
func (tx *IncreaseL1ValidatorBalanceTx) ValidationID() ids.ID {
|
||||
return readID(tx.root(), offIncreaseValidationID)
|
||||
}
|
||||
|
||||
// Balance is the amount to add; Balance <= sum($LUX inputs) - sum($LUX outputs)
|
||||
// - TxFee (offset read).
|
||||
func (tx *IncreaseL1ValidatorBalanceTx) Balance() uint64 {
|
||||
return tx.root().Uint64(offIncreaseBalance)
|
||||
}
|
||||
|
||||
func (tx *IncreaseL1ValidatorBalanceTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
case tx.Balance == 0:
|
||||
case tx.Balance() == 0:
|
||||
return ErrZeroBalance
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return verifyBaseTx(tx.baseTx(), rt)
|
||||
}
|
||||
|
||||
func (tx *IncreaseL1ValidatorBalanceTx) Visit(visitor Visitor) error {
|
||||
|
||||
@@ -1,44 +1,80 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/vm/types"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var _ UnsignedTx = (*RegisterL1ValidatorTx)(nil)
|
||||
|
||||
// RegisterL1ValidatorTx registers an L1 validator. The struct IS the wire: it
|
||||
// embeds the spending envelope and reads its delta fields by offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// Balance u64 @ 77 (Balance <= sum($LUX inputs) - sum($LUX outputs) - TxFee)
|
||||
// ProofOfPossession 96B @ 85 (fixed, BLS PoP of the key in the Message)
|
||||
// Message 8B @ 181 (bytes ptr: signed Warp AddressedCall payload)
|
||||
type RegisterL1ValidatorTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Balance <= sum($LUX inputs) - sum($LUX outputs) - TxFee.
|
||||
Balance uint64 `serialize:"true" json:"balance"`
|
||||
// ProofOfPossession of the BLS key that is included in the Message.
|
||||
ProofOfPossession [bls.SignatureLen]byte `serialize:"true" json:"proofOfPossession"`
|
||||
// Message is expected to be a signed Warp message containing an
|
||||
// AddressedCall payload with the RegisterL1Validator message.
|
||||
Message types.JSONByteSlice `serialize:"true" json:"message"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offRegisterBalance = spendSize // 77
|
||||
offRegisterPoP = 85
|
||||
offRegisterMessage = 181
|
||||
sizeRegisterL1Validator = 189
|
||||
)
|
||||
|
||||
// NewRegisterL1ValidatorTx builds the tx into a fresh zap buffer.
|
||||
func NewRegisterL1ValidatorTx(base *lux.BaseTx, balance uint64, proofOfPossession [bls.SignatureLen]byte, message []byte) (*RegisterL1ValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeRegisterL1Validator)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeRegisterL1Validator)
|
||||
setEnvelope(ob, kindRegisterL1Validator, base, p)
|
||||
ob.SetUint64(offRegisterBalance, balance)
|
||||
ob.SetBytesFixed(offRegisterPoP, proofOfPossession[:])
|
||||
ob.SetBytes(offRegisterMessage, message)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &RegisterL1ValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// Balance is the initial balance funding this L1 validator (offset read).
|
||||
func (tx *RegisterL1ValidatorTx) Balance() uint64 { return tx.root().Uint64(offRegisterBalance) }
|
||||
|
||||
// ProofOfPossession is the BLS PoP of the key included in the Message.
|
||||
func (tx *RegisterL1ValidatorTx) ProofOfPossession() [bls.SignatureLen]byte {
|
||||
var pop [bls.SignatureLen]byte
|
||||
copy(pop[:], tx.root().BytesFixedSlice(offRegisterPoP, bls.SignatureLen))
|
||||
return pop
|
||||
}
|
||||
|
||||
// Message is the signed Warp message carrying the RegisterL1Validator payload.
|
||||
func (tx *RegisterL1ValidatorTx) Message() []byte {
|
||||
if m := tx.root().Bytes(offRegisterMessage); len(m) > 0 {
|
||||
return append([]byte(nil), m...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *RegisterL1ValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
base := BaseTx{BaseTx: tx.baseTx()}
|
||||
return base.SyntacticVerify(rt)
|
||||
}
|
||||
|
||||
func (tx *RegisterL1ValidatorTx) Visit(visitor Visitor) error {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -19,45 +21,78 @@ var (
|
||||
ErrRemovePrimaryNetworkValidator = errors.New("can't remove primary network validator with RemoveChainValidatorTx")
|
||||
)
|
||||
|
||||
// RemoveChainValidatorTx removes a validator from a chain.
|
||||
// RemoveChainValidatorTx removes a validator from a chain. The struct IS the
|
||||
// wire: it embeds spendingTx (the envelope) and adds the node id, chain id,
|
||||
// and an auth sig-index list.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, NodeID:20B@77, Chain:32B@97,
|
||||
// ChainAuth:listptr@129 } (kind=kindRemoveChainValidator).
|
||||
type RemoveChainValidatorTx struct {
|
||||
BaseTx `serialize:"true"`
|
||||
// The node to remove from the chain.
|
||||
NodeID ids.NodeID `serialize:"true" json:"nodeID"`
|
||||
// The chain to remove the node from.
|
||||
Chain ids.ID `serialize:"true" json:"chainID"`
|
||||
// Proves that the issuer has the right to remove the node from the chain.
|
||||
ChainAuth verify.Verifiable `serialize:"true" json:"chainAuthorization"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offRemoveNodeID = spendSize // 20B (ids.NodeIDLen)
|
||||
offRemoveChain = 97 // 32B
|
||||
offRemoveChainAuth = 129 // list ptr (8B)
|
||||
sizeRemove = 137
|
||||
)
|
||||
|
||||
// NewRemoveChainValidatorTx builds the tx into a fresh zap buffer.
|
||||
func NewRemoveChainValidatorTx(base *lux.BaseTx, nodeID ids.NodeID, chain ids.ID, chainAuth verify.Verifiable) (*RemoveChainValidatorTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + sizeRemove)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authOff, authCount, err := writeAuth(b, chainAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := b.StartObject(sizeRemove)
|
||||
setEnvelope(ob, kindRemoveChainValidator, base, p)
|
||||
setNodeID(ob, offRemoveNodeID, nodeID)
|
||||
setID(ob, offRemoveChain, chain)
|
||||
ob.SetList(offRemoveChainAuth, authOff, authCount)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RemoveChainValidatorTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// NodeID is the node to remove from the chain (offset read).
|
||||
func (tx *RemoveChainValidatorTx) NodeID() ids.NodeID {
|
||||
return readNodeID(tx.root(), offRemoveNodeID)
|
||||
}
|
||||
|
||||
// Chain is the chain to remove the node from (offset read).
|
||||
func (tx *RemoveChainValidatorTx) Chain() ids.ID {
|
||||
return readID(tx.root(), offRemoveChain)
|
||||
}
|
||||
|
||||
// ChainAuth proves the issuer's right to remove the node from the chain
|
||||
// (offset read).
|
||||
func (tx *RemoveChainValidatorTx) ChainAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offRemoveChainAuth)
|
||||
}
|
||||
|
||||
func (tx *RemoveChainValidatorTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
case tx.Chain == constants.PrimaryNetworkID:
|
||||
case tx.Chain() == constants.PrimaryNetworkID:
|
||||
return ErrRemovePrimaryNetworkValidator
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.ChainAuth.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return tx.ChainAuth().Verify()
|
||||
}
|
||||
|
||||
func (tx *RemoveChainValidatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.RemoveChainValidatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *RemoveChainValidatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
func (tx *RemoveChainValidatorTx) Initialize(context.Context) error { return nil }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -6,11 +6,11 @@ package txs
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var _ UnsignedTx = (*RewardValidatorTx)(nil)
|
||||
@@ -25,41 +25,44 @@ var _ UnsignedTx = (*RewardValidatorTx)(nil)
|
||||
// If this transaction is accepted and the next block accepted is an Abort
|
||||
// block, the validator is removed and the address that the validator specified
|
||||
// receives the staked LUX but no reward.
|
||||
//
|
||||
// The struct IS the wire: it holds the zap buffer and reads its field by
|
||||
// offset. No codec, no marshal.
|
||||
//
|
||||
// Wire: zap header + object{ kind:u8@0, TxID:32B@1 }.
|
||||
type RewardValidatorTx struct {
|
||||
// ID of the tx that created the delegator/validator being removed/rewarded
|
||||
TxID ids.ID `serialize:"true" json:"txID"`
|
||||
|
||||
unsignedBytes []byte // Unsigned byte representation of this data
|
||||
msg *zap.Message
|
||||
}
|
||||
|
||||
func (tx *RewardValidatorTx) SetBytes(unsignedBytes []byte) {
|
||||
tx.unsignedBytes = unsignedBytes
|
||||
// offRewardTxID is the wire position of the removed staker's tx id (after
|
||||
// kind@0).
|
||||
const offRewardTxID = 1 // 32B, after kind@0
|
||||
|
||||
// NewRewardValidatorTx builds the tx into a fresh zap buffer — the one place a
|
||||
// field becomes bytes (construction, not serialization).
|
||||
func NewRewardValidatorTx(txID ids.ID) *RewardValidatorTx {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 16 + 33)
|
||||
ob := b.StartObject(33)
|
||||
ob.SetUint8(offKind, uint8(kindRewardValidator))
|
||||
setID(ob, offRewardTxID, txID)
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &RewardValidatorTx{msg: msg}
|
||||
}
|
||||
|
||||
func (*RewardValidatorTx) InitRuntime(*runtime.Runtime) {}
|
||||
// TxID is the id of the tx that created the delegator/validator being
|
||||
// removed/rewarded (offset read).
|
||||
func (tx *RewardValidatorTx) TxID() ids.ID { return readID(tx.msg.Root(), offRewardTxID) }
|
||||
|
||||
func (tx *RewardValidatorTx) Bytes() []byte {
|
||||
return tx.unsignedBytes
|
||||
}
|
||||
// Bytes returns the underlying zap buffer literally — no encode.
|
||||
func (tx *RewardValidatorTx) Bytes() []byte { return tx.msg.Bytes() }
|
||||
|
||||
func (*RewardValidatorTx) InputIDs() set.Set[ids.ID] {
|
||||
return nil
|
||||
}
|
||||
// SetBytes wraps b as this tx's buffer (zero copy); the Parse dispatch uses it.
|
||||
func (tx *RewardValidatorTx) SetBytes(b []byte) { tx.msg, _ = zap.Parse(b) }
|
||||
|
||||
func (*RewardValidatorTx) Outputs() []*lux.TransferableOutput {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*RewardValidatorTx) SyntacticVerify(*runtime.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *RewardValidatorTx) Visit(visitor Visitor) error {
|
||||
return visitor.RewardValidatorTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *RewardValidatorTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
func (*RewardValidatorTx) InitRuntime(*runtime.Runtime) {}
|
||||
func (*RewardValidatorTx) InputIDs() set.Set[ids.ID] { return nil }
|
||||
func (*RewardValidatorTx) Outputs() []*lux.TransferableOutput { return nil }
|
||||
func (*RewardValidatorTx) SyntacticVerify(*runtime.Runtime) error { return nil }
|
||||
func (tx *RewardValidatorTx) Visit(visitor Visitor) error { return visitor.RewardValidatorTx(tx) }
|
||||
func (tx *RewardValidatorTx) Initialize(context.Context) error { return nil }
|
||||
|
||||
@@ -1,39 +1,64 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/vm/types"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var _ UnsignedTx = (*SetL1ValidatorWeightTx)(nil)
|
||||
|
||||
// SetL1ValidatorWeightTx carries a signed Warp message that sets an L1
|
||||
// validator's weight. The struct IS the wire: it embeds spendingTx (the
|
||||
// envelope) and adds the Warp Message as a variable-length bytes tail.
|
||||
//
|
||||
// Wire: zap header + object{ envelope@0..76, Message:bytesptr@77 }
|
||||
// (kind=kindSetL1ValidatorWeight).
|
||||
type SetL1ValidatorWeightTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// Message is expected to be a signed Warp message containing an
|
||||
// AddressedCall payload with the SetL1ValidatorWeight message.
|
||||
Message types.JSONByteSlice `serialize:"true" json:"message"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offSetWeightMessage = spendSize // bytes ptr (8B)
|
||||
sizeSetWeight = 85
|
||||
)
|
||||
|
||||
// NewSetL1ValidatorWeightTx builds the tx into a fresh zap buffer.
|
||||
func NewSetL1ValidatorWeightTx(base *lux.BaseTx, message []byte) (*SetL1ValidatorWeightTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 256 + sizeSetWeight)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := b.StartObject(sizeSetWeight)
|
||||
setEnvelope(ob, kindSetL1ValidatorWeight, base, p)
|
||||
ob.SetBytes(offSetWeightMessage, message)
|
||||
ob.FinishAsRoot()
|
||||
msg, err := zap.Parse(b.Finish())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SetL1ValidatorWeightTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// Message is the signed Warp message (an AddressedCall carrying the
|
||||
// SetL1ValidatorWeight payload). Returns a copy so callers can't mutate the
|
||||
// buffer.
|
||||
func (tx *SetL1ValidatorWeightTx) Message() []byte {
|
||||
if m := tx.root().Bytes(offSetWeightMessage); len(m) > 0 {
|
||||
return append([]byte(nil), m...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *SetL1ValidatorWeightTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
if tx == nil {
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return verifyBaseTx(tx.baseTx(), rt)
|
||||
}
|
||||
|
||||
func (tx *SetL1ValidatorWeightTx) Visit(visitor Visitor) error {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/fx"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,56 +22,86 @@ var (
|
||||
ErrTransferPermissionlessChain = errors.New("cannot transfer ownership of a permissionless chain")
|
||||
)
|
||||
|
||||
// TransferChainOwnershipTx re-owns a chain. The struct IS the wire: it embeds
|
||||
// the spending envelope and reads its delta fields by offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// Chain 32B @ 77 (id)
|
||||
// ChainAuth 8B @ 109 (auth ptr: sig-index list)
|
||||
// Owner.Threshold u32 @ 117
|
||||
// Owner.Locktime u64 @ 121
|
||||
// Owner.Addrs 8B @ 129 (addr list ptr)
|
||||
type TransferChainOwnershipTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// ID of the chain this tx is modifying
|
||||
Chain ids.ID `serialize:"true" json:"chainID"`
|
||||
// Proves that the issuer has the right to modify the chain.
|
||||
ChainAuth verify.Verifiable `serialize:"true" json:"chainAuthorization"`
|
||||
// Who is now authorized to manage this chain
|
||||
Owner fx.Owner `serialize:"true" json:"newOwner"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
// InitRuntime sets the FxID fields in the inputs and outputs of this
|
||||
// [TransferChainOwnershipTx]. Also sets the [rt] to the given [vm.rt] so
|
||||
// that the addresses can be json marshalled into human readable format
|
||||
func (tx *TransferChainOwnershipTx) InitRuntime(rt *runtime.Runtime) {
|
||||
tx.BaseTx.InitRuntime(rt)
|
||||
// Owner doesn't have InitRuntime method
|
||||
if owner, ok := tx.Owner.(*secp256k1fx.OutputOwners); ok {
|
||||
owner.InitRuntime(rt)
|
||||
const (
|
||||
offTransferChain = spendSize // 77
|
||||
offTransferChainAuth = 109
|
||||
offTransferOwnerThreshold = 117
|
||||
offTransferOwnerLocktime = 121
|
||||
offTransferOwnerAddrs = 129
|
||||
sizeTransferChainOwnership = 137
|
||||
)
|
||||
|
||||
// NewTransferChainOwnershipTx builds the tx into a fresh zap buffer.
|
||||
func NewTransferChainOwnershipTx(base *lux.BaseTx, chain ids.ID, chainAuth verify.Verifiable, owner fx.Owner) (*TransferChainOwnershipTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeTransferChainOwnership)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authOff, authCount, err := writeAuth(b, chainAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
threshold, locktime, addrOff, addrCount, err := writeOwner(b, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeTransferChainOwnership)
|
||||
setEnvelope(ob, kindTransferChainOwnership, base, p)
|
||||
setID(ob, offTransferChain, chain)
|
||||
ob.SetList(offTransferChainAuth, authOff, authCount)
|
||||
setOwner(ob, offTransferOwnerThreshold, offTransferOwnerLocktime, offTransferOwnerAddrs, threshold, locktime, addrOff, addrCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &TransferChainOwnershipTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// Chain is the ID of the chain this tx is modifying (offset read).
|
||||
func (tx *TransferChainOwnershipTx) Chain() ids.ID { return readID(tx.root(), offTransferChain) }
|
||||
|
||||
// ChainAuth proves the issuer has the right to modify the chain.
|
||||
func (tx *TransferChainOwnershipTx) ChainAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offTransferChainAuth)
|
||||
}
|
||||
|
||||
// Owner is who is now authorized to manage this chain.
|
||||
func (tx *TransferChainOwnershipTx) Owner() fx.Owner {
|
||||
return readOwner(tx.root(), offTransferOwnerThreshold, offTransferOwnerLocktime, offTransferOwnerAddrs)
|
||||
}
|
||||
|
||||
func (tx *TransferChainOwnershipTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified:
|
||||
// already passed syntactic verification
|
||||
return nil
|
||||
case tx.Chain == constants.PrimaryNetworkID:
|
||||
case tx.Chain() == constants.PrimaryNetworkID:
|
||||
return ErrTransferPermissionlessChain
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := verify.All(tx.ChainAuth, tx.Owner); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return verify.All(tx.ChainAuth(), tx.Owner())
|
||||
}
|
||||
|
||||
func (tx *TransferChainOwnershipTx) Visit(visitor Visitor) error {
|
||||
return visitor.TransferChainOwnershipTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *TransferChainOwnershipTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
// Initialize is a no-op; the struct is already the wire.
|
||||
func (tx *TransferChainOwnershipTx) Initialize(ctx context.Context) error { return nil }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/runtime"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/components/verify"
|
||||
"github.com/luxfi/node/vms/platformvm/reward"
|
||||
"github.com/luxfi/runtime"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,141 +39,230 @@ var (
|
||||
errUptimeRequirementTooLarge = fmt.Errorf("uptime requirement must be less than or equal to %d", reward.PercentDenominator)
|
||||
)
|
||||
|
||||
// TransformChainTx is an unsigned transformChainTx
|
||||
// TransformChainTx transforms a chain into a permissionless one. The struct IS
|
||||
// the wire: it embeds the spending envelope and reads its delta fields by
|
||||
// offset.
|
||||
//
|
||||
// Delta layout (fixed section, after the 77-byte spending envelope):
|
||||
//
|
||||
// Chain 32B @ 77
|
||||
// AssetID 32B @ 109
|
||||
// InitialSupply u64 @ 141
|
||||
// MaximumSupply u64 @ 149
|
||||
// MinConsumptionRate u64 @ 157
|
||||
// MaxConsumptionRate u64 @ 165
|
||||
// MinValidatorStake u64 @ 173
|
||||
// MaxValidatorStake u64 @ 181
|
||||
// MinStakeDuration u32 @ 189
|
||||
// MaxStakeDuration u32 @ 193
|
||||
// MinDelegationFee u32 @ 197
|
||||
// MinDelegatorStake u64 @ 201
|
||||
// MaxValidatorWeightFactor u8 @ 209
|
||||
// UptimeRequirement u32 @ 210
|
||||
// ChainAuth 8B @ 214 (auth ptr: sig-index list)
|
||||
type TransformChainTx struct {
|
||||
// Metadata, inputs and outputs
|
||||
BaseTx `serialize:"true"`
|
||||
// ID of the Chain to transform
|
||||
// Restrictions:
|
||||
// - Must not be the Primary Network ID
|
||||
Chain ids.ID `serialize:"true" json:"chainID"`
|
||||
// Asset to use when staking on the Net
|
||||
// Restrictions:
|
||||
// - Must not be the Empty ID
|
||||
// - Must not be the LUX ID
|
||||
AssetID ids.ID `serialize:"true" json:"assetID"`
|
||||
// Amount to initially specify as the current supply
|
||||
// Restrictions:
|
||||
// - Must be > 0
|
||||
InitialSupply uint64 `serialize:"true" json:"initialSupply"`
|
||||
// Amount to specify as the maximum token supply
|
||||
// Restrictions:
|
||||
// - Must be >= [InitialSupply]
|
||||
MaximumSupply uint64 `serialize:"true" json:"maximumSupply"`
|
||||
// MinConsumptionRate is the rate to allocate funds if the validator's stake
|
||||
// duration is 0
|
||||
MinConsumptionRate uint64 `serialize:"true" json:"minConsumptionRate"`
|
||||
// MaxConsumptionRate is the rate to allocate funds if the validator's stake
|
||||
// duration is equal to the minting period
|
||||
// Restrictions:
|
||||
// - Must be >= [MinConsumptionRate]
|
||||
// - Must be <= [reward.PercentDenominator]
|
||||
MaxConsumptionRate uint64 `serialize:"true" json:"maxConsumptionRate"`
|
||||
// MinValidatorStake is the minimum amount of funds required to become a
|
||||
// validator.
|
||||
// Restrictions:
|
||||
// - Must be > 0
|
||||
// - Must be <= [InitialSupply]
|
||||
MinValidatorStake uint64 `serialize:"true" json:"minValidatorStake"`
|
||||
// MaxValidatorStake is the maximum amount of funds a single validator can
|
||||
// be allocated, including delegated funds.
|
||||
// Restrictions:
|
||||
// - Must be >= [MinValidatorStake]
|
||||
// - Must be <= [MaximumSupply]
|
||||
MaxValidatorStake uint64 `serialize:"true" json:"maxValidatorStake"`
|
||||
// MinStakeDuration is the minimum number of seconds a staker can stake for.
|
||||
// Restrictions:
|
||||
// - Must be > 0
|
||||
MinStakeDuration uint32 `serialize:"true" json:"minStakeDuration"`
|
||||
// MaxStakeDuration is the maximum number of seconds a staker can stake for.
|
||||
// Restrictions:
|
||||
// - Must be >= [MinStakeDuration]
|
||||
// - Must be <= [GlobalMaxStakeDuration]
|
||||
MaxStakeDuration uint32 `serialize:"true" json:"maxStakeDuration"`
|
||||
// MinDelegationFee is the minimum percentage a validator must charge a
|
||||
// delegator for delegating.
|
||||
// Restrictions:
|
||||
// - Must be <= [reward.PercentDenominator]
|
||||
MinDelegationFee uint32 `serialize:"true" json:"minDelegationFee"`
|
||||
// MinDelegatorStake is the minimum amount of funds required to become a
|
||||
// delegator.
|
||||
// Restrictions:
|
||||
// - Must be > 0
|
||||
MinDelegatorStake uint64 `serialize:"true" json:"minDelegatorStake"`
|
||||
// MaxValidatorWeightFactor is the factor which calculates the maximum
|
||||
// amount of delegation a validator can receive.
|
||||
// Note: a value of 1 effectively disables delegation.
|
||||
// Restrictions:
|
||||
// - Must be > 0
|
||||
MaxValidatorWeightFactor byte `serialize:"true" json:"maxValidatorWeightFactor"`
|
||||
// UptimeRequirement is the minimum percentage a validator must be online
|
||||
// and responsive to receive a reward.
|
||||
// Restrictions:
|
||||
// - Must be <= [reward.PercentDenominator]
|
||||
UptimeRequirement uint32 `serialize:"true" json:"uptimeRequirement"`
|
||||
// Authorizes this transformation
|
||||
ChainAuth verify.Verifiable `serialize:"true" json:"chainAuthorization"`
|
||||
spendingTx
|
||||
}
|
||||
|
||||
const (
|
||||
offTransformChain = spendSize // 77
|
||||
offTransformAssetID = 109
|
||||
offTransformInitialSupply = 141
|
||||
offTransformMaximumSupply = 149
|
||||
offTransformMinConsumptionRate = 157
|
||||
offTransformMaxConsumptionRate = 165
|
||||
offTransformMinValidatorStake = 173
|
||||
offTransformMaxValidatorStake = 181
|
||||
offTransformMinStakeDuration = 189
|
||||
offTransformMaxStakeDuration = 193
|
||||
offTransformMinDelegationFee = 197
|
||||
offTransformMinDelegatorStake = 201
|
||||
offTransformMaxValidatorWeightFactor = 209
|
||||
offTransformUptimeRequirement = 210
|
||||
offTransformChainAuth = 214
|
||||
sizeTransformChain = 222
|
||||
)
|
||||
|
||||
// NewTransformChainTx builds the tx into a fresh zap buffer.
|
||||
func NewTransformChainTx(
|
||||
base *lux.BaseTx,
|
||||
chain ids.ID,
|
||||
assetID ids.ID,
|
||||
initialSupply uint64,
|
||||
maximumSupply uint64,
|
||||
minConsumptionRate uint64,
|
||||
maxConsumptionRate uint64,
|
||||
minValidatorStake uint64,
|
||||
maxValidatorStake uint64,
|
||||
minStakeDuration uint32,
|
||||
maxStakeDuration uint32,
|
||||
minDelegationFee uint32,
|
||||
minDelegatorStake uint64,
|
||||
maxValidatorWeightFactor byte,
|
||||
uptimeRequirement uint32,
|
||||
chainAuth verify.Verifiable,
|
||||
) (*TransformChainTx, error) {
|
||||
b := zap.NewBuilder(zap.HeaderSize + 512 + sizeTransformChain)
|
||||
p, err := writeSpending(b, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authOff, authCount, err := writeAuth(b, chainAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := b.StartObject(sizeTransformChain)
|
||||
setEnvelope(ob, kindTransformChain, base, p)
|
||||
setID(ob, offTransformChain, chain)
|
||||
setID(ob, offTransformAssetID, assetID)
|
||||
ob.SetUint64(offTransformInitialSupply, initialSupply)
|
||||
ob.SetUint64(offTransformMaximumSupply, maximumSupply)
|
||||
ob.SetUint64(offTransformMinConsumptionRate, minConsumptionRate)
|
||||
ob.SetUint64(offTransformMaxConsumptionRate, maxConsumptionRate)
|
||||
ob.SetUint64(offTransformMinValidatorStake, minValidatorStake)
|
||||
ob.SetUint64(offTransformMaxValidatorStake, maxValidatorStake)
|
||||
ob.SetUint32(offTransformMinStakeDuration, minStakeDuration)
|
||||
ob.SetUint32(offTransformMaxStakeDuration, maxStakeDuration)
|
||||
ob.SetUint32(offTransformMinDelegationFee, minDelegationFee)
|
||||
ob.SetUint64(offTransformMinDelegatorStake, minDelegatorStake)
|
||||
ob.SetUint8(offTransformMaxValidatorWeightFactor, maxValidatorWeightFactor)
|
||||
ob.SetUint32(offTransformUptimeRequirement, uptimeRequirement)
|
||||
ob.SetList(offTransformChainAuth, authOff, authCount)
|
||||
ob.FinishAsRoot()
|
||||
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return &TransformChainTx{spendingTx{msg}}, nil
|
||||
}
|
||||
|
||||
// Chain is the ID of the chain to transform (offset read).
|
||||
func (tx *TransformChainTx) Chain() ids.ID { return readID(tx.root(), offTransformChain) }
|
||||
|
||||
// AssetID is the asset to use when staking on the chain.
|
||||
func (tx *TransformChainTx) AssetID() ids.ID { return readID(tx.root(), offTransformAssetID) }
|
||||
|
||||
// InitialSupply is the amount to initially specify as the current supply.
|
||||
func (tx *TransformChainTx) InitialSupply() uint64 {
|
||||
return tx.root().Uint64(offTransformInitialSupply)
|
||||
}
|
||||
|
||||
// MaximumSupply is the amount to specify as the maximum token supply.
|
||||
func (tx *TransformChainTx) MaximumSupply() uint64 {
|
||||
return tx.root().Uint64(offTransformMaximumSupply)
|
||||
}
|
||||
|
||||
// MinConsumptionRate is the rate to allocate funds if the validator's stake
|
||||
// duration is 0.
|
||||
func (tx *TransformChainTx) MinConsumptionRate() uint64 {
|
||||
return tx.root().Uint64(offTransformMinConsumptionRate)
|
||||
}
|
||||
|
||||
// MaxConsumptionRate is the rate to allocate funds if the validator's stake
|
||||
// duration is equal to the minting period.
|
||||
func (tx *TransformChainTx) MaxConsumptionRate() uint64 {
|
||||
return tx.root().Uint64(offTransformMaxConsumptionRate)
|
||||
}
|
||||
|
||||
// MinValidatorStake is the minimum amount of funds required to become a
|
||||
// validator.
|
||||
func (tx *TransformChainTx) MinValidatorStake() uint64 {
|
||||
return tx.root().Uint64(offTransformMinValidatorStake)
|
||||
}
|
||||
|
||||
// MaxValidatorStake is the maximum amount of funds a single validator can be
|
||||
// allocated, including delegated funds.
|
||||
func (tx *TransformChainTx) MaxValidatorStake() uint64 {
|
||||
return tx.root().Uint64(offTransformMaxValidatorStake)
|
||||
}
|
||||
|
||||
// MinStakeDuration is the minimum number of seconds a staker can stake for.
|
||||
func (tx *TransformChainTx) MinStakeDuration() uint32 {
|
||||
return tx.root().Uint32(offTransformMinStakeDuration)
|
||||
}
|
||||
|
||||
// MaxStakeDuration is the maximum number of seconds a staker can stake for.
|
||||
func (tx *TransformChainTx) MaxStakeDuration() uint32 {
|
||||
return tx.root().Uint32(offTransformMaxStakeDuration)
|
||||
}
|
||||
|
||||
// MinDelegationFee is the minimum percentage a validator must charge a
|
||||
// delegator for delegating.
|
||||
func (tx *TransformChainTx) MinDelegationFee() uint32 {
|
||||
return tx.root().Uint32(offTransformMinDelegationFee)
|
||||
}
|
||||
|
||||
// MinDelegatorStake is the minimum amount of funds required to become a
|
||||
// delegator.
|
||||
func (tx *TransformChainTx) MinDelegatorStake() uint64 {
|
||||
return tx.root().Uint64(offTransformMinDelegatorStake)
|
||||
}
|
||||
|
||||
// MaxValidatorWeightFactor is the factor which calculates the maximum amount of
|
||||
// delegation a validator can receive.
|
||||
func (tx *TransformChainTx) MaxValidatorWeightFactor() byte {
|
||||
return tx.root().Uint8(offTransformMaxValidatorWeightFactor)
|
||||
}
|
||||
|
||||
// UptimeRequirement is the minimum percentage a validator must be online and
|
||||
// responsive to receive a reward.
|
||||
func (tx *TransformChainTx) UptimeRequirement() uint32 {
|
||||
return tx.root().Uint32(offTransformUptimeRequirement)
|
||||
}
|
||||
|
||||
// ChainAuth authorizes this transformation.
|
||||
func (tx *TransformChainTx) ChainAuth() verify.Verifiable {
|
||||
return readAuth(tx.root(), offTransformChainAuth)
|
||||
}
|
||||
|
||||
func (tx *TransformChainTx) SyntacticVerify(rt *runtime.Runtime) error {
|
||||
switch {
|
||||
case tx == nil:
|
||||
return ErrNilTx
|
||||
case tx.SyntacticallyVerified: // already passed syntactic verification
|
||||
return nil
|
||||
case tx.Chain == constants.PrimaryNetworkID:
|
||||
case tx.Chain() == constants.PrimaryNetworkID:
|
||||
return errCantTransformPrimaryNetwork
|
||||
case tx.AssetID == ids.Empty:
|
||||
case tx.AssetID() == ids.Empty:
|
||||
return errEmptyAssetID
|
||||
case tx.AssetID == rt.UTXOAssetID:
|
||||
case tx.AssetID() == rt.UTXOAssetID:
|
||||
return errAssetIDCantBeLUX
|
||||
case tx.InitialSupply == 0:
|
||||
case tx.InitialSupply() == 0:
|
||||
return errInitialSupplyZero
|
||||
case tx.InitialSupply > tx.MaximumSupply:
|
||||
case tx.InitialSupply() > tx.MaximumSupply():
|
||||
return errInitialSupplyGreaterThanMaxSupply
|
||||
case tx.MinConsumptionRate > tx.MaxConsumptionRate:
|
||||
case tx.MinConsumptionRate() > tx.MaxConsumptionRate():
|
||||
return errMinConsumptionRateTooLarge
|
||||
case tx.MaxConsumptionRate > reward.PercentDenominator:
|
||||
case tx.MaxConsumptionRate() > reward.PercentDenominator:
|
||||
return errMaxConsumptionRateTooLarge
|
||||
case tx.MinValidatorStake == 0:
|
||||
case tx.MinValidatorStake() == 0:
|
||||
return errMinValidatorStakeZero
|
||||
case tx.MinValidatorStake > tx.InitialSupply:
|
||||
case tx.MinValidatorStake() > tx.InitialSupply():
|
||||
return errMinValidatorStakeAboveSupply
|
||||
case tx.MinValidatorStake > tx.MaxValidatorStake:
|
||||
case tx.MinValidatorStake() > tx.MaxValidatorStake():
|
||||
return errMinValidatorStakeAboveMax
|
||||
case tx.MaxValidatorStake > tx.MaximumSupply:
|
||||
case tx.MaxValidatorStake() > tx.MaximumSupply():
|
||||
return errMaxValidatorStakeTooLarge
|
||||
case tx.MinStakeDuration == 0:
|
||||
case tx.MinStakeDuration() == 0:
|
||||
return errMinStakeDurationZero
|
||||
case tx.MinStakeDuration > tx.MaxStakeDuration:
|
||||
case tx.MinStakeDuration() > tx.MaxStakeDuration():
|
||||
return errMinStakeDurationTooLarge
|
||||
case tx.MinDelegationFee > reward.PercentDenominator:
|
||||
case tx.MinDelegationFee() > reward.PercentDenominator:
|
||||
return errMinDelegationFeeTooLarge
|
||||
case tx.MinDelegatorStake == 0:
|
||||
case tx.MinDelegatorStake() == 0:
|
||||
return errMinDelegatorStakeZero
|
||||
case tx.MaxValidatorWeightFactor == 0:
|
||||
case tx.MaxValidatorWeightFactor() == 0:
|
||||
return errMaxValidatorWeightFactorZero
|
||||
case tx.UptimeRequirement > reward.PercentDenominator:
|
||||
case tx.UptimeRequirement() > reward.PercentDenominator:
|
||||
return errUptimeRequirementTooLarge
|
||||
}
|
||||
|
||||
if err := tx.BaseTx.SyntacticVerify(rt); err != nil {
|
||||
if err := verifyBaseTx(tx.baseTx(), rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.ChainAuth.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SyntacticallyVerified = true
|
||||
return nil
|
||||
return tx.ChainAuth().Verify()
|
||||
}
|
||||
|
||||
func (tx *TransformChainTx) Visit(visitor Visitor) error {
|
||||
return visitor.TransformChainTx(tx)
|
||||
}
|
||||
|
||||
// InitializeWithRuntime initializes the transaction with Runtime
|
||||
func (tx *TransformChainTx) Initialize(ctx context.Context) error {
|
||||
// Initialize any context-dependent fields here
|
||||
return nil
|
||||
}
|
||||
// Initialize is a no-op; the struct is already the wire.
|
||||
func (tx *TransformChainTx) Initialize(ctx context.Context) error { return nil }
|
||||
|
||||
Reference in New Issue
Block a user