mirror of
https://github.com/luxfi/sdk.git
synced 2026-10-30 15:19:23 +00:00
github.com/luxfi/codec is gone from this module — no import, no call, no require. Nothing here marshals or unmarshals a transaction any more. WHY IT BROKE. codec deleted its jsonrpc subpackage (1b67d6a, "delete dead subpackages") while nine files imported it. It looked dead because the PUBLISHED [email protected] still ships it, so only a workspace build — which resolves codec from the local checkout — ever saw the break. THE WIRE. A tx already carries its own ZAP encoding, so the codec was re-deriving what the tx could hand over: txs.Codec.Marshal(txs.CodecVersion, &tx.Unsigned) -> tx.Unsigned.Bytes() txs.UnsignedBytes(tx.Unsigned) txs.Codec.Marshal(txs.CodecVersion, tx) -> tx.Initialize() txs.Codec.Unmarshal(txBytes, &tx) -> txs.Parse(txBytes) codec.Unmarshal(utxoBytes, &utxo) -> utxo.ParseUTXO(...) These are the same calls the node makes in txs.Tx.Sign. AddAllUTXOs no longer takes a codec.Manager, and SortTransferableOutputs / state.Sort / SortOperations no longer get one — upstream dropped the parameter. PARALLEL IMPLEMENTATIONS, DELETED. wallet/chain/{p,x} carried a second copy of the builder and signer beside the subpackages that already held them — p/signer_visitor.go and p/signer/visitor.go were both 399 lines of the same thing, and each defect had to be fixed twice. The flat files are gone; the package keeps the facade it already had in wallet_exports.go, which re-exports the subpackage. -4787 lines. wallet/chain/c is deleted. Its whole reason to exist was a codec.Manager scaffold for C-chain atomic-tx wire that nothing runs: NewWallet was passed nil for it, and the builder was commented out. The two symbols anything used move to where they are read — Account, a balance and a nonce off an EVM client, and EVMKeychain, the geth-address half of a keychain. Wallet no longer advertises a C() that only ever returned nil. ALIGNED. XAssetID -> UTXOAssetID, matching runtime.Runtime and netrunner, which already expected it. Keychain GetEth -> GetByEVM, EthAddrs -> EVMAddrs. warp UnsignedMessage and Message collapsed into one flat Message, so a signature is an Envelope rather than a second message type. netrunner's HealthResponse is read as a field: the getter form is protobuf, behind //go:build grpc, and the default build is ZAP-native. VERIFIED on a machine with the native libraries: go build ./... exits 0 with no codec require in go.mod, and go test ./... is 23 ok. Three packages still fail to build their tests — profiles, wallet/keychain, wallet/primary — and all three fail identically on the tree before this change: Consensus, VerifyHybridSecp256k1MLDSA and isXChainNotEnabled are referenced by tests and defined nowhere. evm failed there too and now passes.
319 lines
14 KiB
Go
319 lines
14 KiB
Go
// Copyright (C) 2025, Lux Industries Inc All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package validatormanager
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/luxfi/constants"
|
|
warpPayload "github.com/luxfi/proto/p/warp/payload"
|
|
"github.com/luxfi/sdk/models"
|
|
|
|
"github.com/luxfi/geth/core/types"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/sdk/contract"
|
|
"github.com/luxfi/sdk/validator"
|
|
"github.com/luxfi/sdk/validatormanager/txs"
|
|
"github.com/luxfi/sdk/validatormanager/validatormanagertypes"
|
|
warpMessage "github.com/luxfi/sdk/validatormanager/warp"
|
|
"github.com/luxfi/warp"
|
|
|
|
"github.com/luxfi/crypto"
|
|
)
|
|
|
|
type ACP99ValidatorManagerSettings struct {
|
|
Admin crypto.Address
|
|
ChainID [32]byte
|
|
ChurnPeriodSeconds uint64
|
|
MaximumChurnPercentage uint8
|
|
}
|
|
|
|
type ValidatorManagerSettings struct {
|
|
ChainID [32]byte
|
|
ChurnPeriodSeconds uint64
|
|
MaximumChurnPercentage uint8
|
|
}
|
|
|
|
type NativeTokenValidatorManagerSettingsV1_0_0 struct {
|
|
BaseSettings ValidatorManagerSettings
|
|
MinimumStakeAmount *big.Int
|
|
MaximumStakeAmount *big.Int
|
|
MinimumStakeDuration uint64
|
|
MinimumDelegationFeeBips uint16
|
|
MaximumStakeMultiplier uint8
|
|
WeightToValueFactor *big.Int
|
|
RewardCalculator crypto.Address
|
|
UptimeBlockchainID [32]byte
|
|
}
|
|
|
|
type NativeTokenValidatorManagerSettingsV2_0_0 struct {
|
|
Manager crypto.Address
|
|
MinimumStakeAmount *big.Int
|
|
MaximumStakeAmount *big.Int
|
|
MinimumStakeDuration uint64
|
|
MinimumDelegationFeeBips uint16
|
|
MaximumStakeMultiplier uint8
|
|
WeightToValueFactor *big.Int
|
|
RewardCalculator crypto.Address
|
|
UptimeBlockchainID [32]byte
|
|
}
|
|
|
|
const (
|
|
ValidatorMessagesContractAddress = "0x9C00629cE712B0255b17A4a657171Acd15720B8C"
|
|
ValidatorContractAddress = "0x0C0DEBA5E0000000000000000000000000000000"
|
|
ValidatorProxyContractAddress = "0x0FEEDC0DE0000000000000000000000000000000"
|
|
ValidatorProxyAdminContractAddress = "0xA0AFFE1234567890aBcDEF1234567890AbCdEf34"
|
|
SpecializationProxyContractAddress = "0x100C0DE1C0FFEE00000000000000000000000000"
|
|
SpecializationProxyAdminContractAddress = "0x97A35a4A2A8a56256de7A32160819c7B3F4C9DA6"
|
|
RewardCalculatorAddress = "0x0DEADC0DE0000000000000000000000000000000"
|
|
|
|
DefaultPoSMinimumStakeAmount = 1
|
|
DefaultPoSMaximumStakeAmount = 1000
|
|
DefaultPoSMinimumStakeDuration = 100
|
|
DefaultPoSDMinimumDelegationFee = 1
|
|
DefaultPoSMaximumStakeMultiplier = 1
|
|
DefaultPoSWeightToValueFactor = 1
|
|
)
|
|
|
|
var (
|
|
ErrDelegatorIneligibleForRewards = fmt.Errorf("delegator ineligible for rewards")
|
|
ErrInvalidBLSPublicKey = fmt.Errorf("invalid BLS public key")
|
|
ErrAlreadyInitialized = fmt.Errorf("the contract is already initialized")
|
|
ErrInvalidMaximumChurnPercentage = fmt.Errorf("unvalid churn percentage")
|
|
ErrInvalidValidationID = fmt.Errorf("invalid validation id")
|
|
ErrInvalidValidatorStatus = fmt.Errorf("invalid validator status")
|
|
ErrMaxChurnRateExceeded = fmt.Errorf("max churn rate exceeded")
|
|
ErrInvalidInitializationStatus = fmt.Errorf("validators set already initialized")
|
|
ErrInvalidValidatorManagerBlockchainID = fmt.Errorf("invalid validator manager blockchain ID")
|
|
ErrInvalidValidatorManagerAddress = fmt.Errorf("invalid validator manager address")
|
|
ErrNodeAlreadyRegistered = fmt.Errorf("node already registered")
|
|
ErrInvalidChainConversionID = fmt.Errorf("invalid chain conversion id")
|
|
ErrInvalidRegistrationExpiry = fmt.Errorf("invalid registration expiry")
|
|
ErrInvalidBLSKeyLength = fmt.Errorf("invalid BLS key length")
|
|
ErrInvalidNodeID = fmt.Errorf("invalid node id")
|
|
ErrInvalidWarpMessage = fmt.Errorf("invalid warp message")
|
|
ErrInvalidWarpSourceChainID = fmt.Errorf("invalid wapr source chain ID")
|
|
ErrInvalidWarpOriginSenderAddress = fmt.Errorf("invalid warp origin sender address")
|
|
ErrInvalidCodecID = fmt.Errorf("invalid codec ID")
|
|
ErrInvalidConversionID = fmt.Errorf("invalid conversion ID")
|
|
ErrInvalidDelegationFee = fmt.Errorf("invalid delegation fee")
|
|
ErrInvalidDelegationID = fmt.Errorf("invalid delegation ID")
|
|
ErrInvalidDelegatorStatus = fmt.Errorf("invalid delegator status")
|
|
ErrInvalidMessageLength = fmt.Errorf("invalid message length")
|
|
ErrInvalidMessageType = fmt.Errorf("invalid message type")
|
|
ErrInvalidMinStakeDuration = fmt.Errorf("invalid min stake duration")
|
|
ErrInvalidNonce = fmt.Errorf("invalid nonce")
|
|
ErrInvalidPChainOwnerThreshold = fmt.Errorf("invalid pchain owner threshold")
|
|
ErrInvalidStakeAmount = fmt.Errorf("invalid stake amount")
|
|
ErrInvalidStakeMultiplier = fmt.Errorf("invalid stake multiplier")
|
|
ErrInvalidTokenAddress = fmt.Errorf("invalid token address")
|
|
ErrInvalidTotalWeight = fmt.Errorf("invalid total weight")
|
|
ErrMaxWeightExceeded = fmt.Errorf("max weight exceeded")
|
|
ErrMinStakeDurationNotPassed = fmt.Errorf("min stake duration not passed")
|
|
ErrPChainOwnerAddressesNotSorted = fmt.Errorf("pchain owner addresses not sorted")
|
|
ErrUnauthorizedOwner = fmt.Errorf("unauthorized owner")
|
|
ErrUnexpectedRegistrationStatus = fmt.Errorf("unexpected registration status")
|
|
ErrValidatorIneligibleForRewards = fmt.Errorf("validator ineligible for rewards")
|
|
ErrValidatorNotPoS = fmt.Errorf("validator not PoS")
|
|
ErrZeroWeightToValueFactor = fmt.Errorf("zero weight to value factor")
|
|
ErrInvalidOwner = fmt.Errorf("invalid proxy or validator owner")
|
|
ErrorSignatureToError = map[string]error{
|
|
"InvalidInitialization()": ErrAlreadyInitialized,
|
|
"InvalidMaximumChurnPercentage(uint8)": ErrInvalidMaximumChurnPercentage,
|
|
"InvalidValidationID(bytes32)": ErrInvalidValidationID,
|
|
"InvalidValidatorStatus(uint8)": ErrInvalidValidatorStatus,
|
|
"MaxChurnRateExceeded(uint64)": ErrMaxChurnRateExceeded,
|
|
"InvalidInitializationStatus()": ErrInvalidInitializationStatus,
|
|
"InvalidValidatorManagerBlockchainID(bytes32)": ErrInvalidValidatorManagerBlockchainID,
|
|
"InvalidValidatorManagerAddress(address)": ErrInvalidValidatorManagerAddress,
|
|
"NodeAlreadyRegistered(bytes)": ErrNodeAlreadyRegistered,
|
|
"InvalidChainConversionID(bytes32,bytes32)": ErrInvalidChainConversionID,
|
|
"InvalidRegistrationExpiry(uint64)": ErrInvalidRegistrationExpiry,
|
|
"InvalidBLSKeyLength(uint256)": ErrInvalidBLSKeyLength,
|
|
"InvalidNodeID(bytes)": ErrInvalidNodeID,
|
|
"InvalidWarpMessage()": ErrInvalidWarpMessage,
|
|
"InvalidWarpSourceChainID(bytes32)": ErrInvalidWarpSourceChainID,
|
|
"InvalidWarpOriginSenderAddress(address)": ErrInvalidWarpOriginSenderAddress,
|
|
"DelegatorIneligibleForRewards(bytes32)": ErrDelegatorIneligibleForRewards,
|
|
"InvalidBLSPublicKey()": ErrInvalidBLSPublicKey,
|
|
"InvalidCodecID(uint32)": ErrInvalidCodecID,
|
|
"InvalidConversionID(bytes32,bytes32)": ErrInvalidConversionID,
|
|
"InvalidDelegationFee(uint16)": ErrInvalidDelegationFee,
|
|
"InvalidDelegationID(bytes32)": ErrInvalidDelegationID,
|
|
"InvalidDelegatorStatus(DelegatorStatus)": ErrInvalidDelegatorStatus,
|
|
"InvalidMessageLength(uint32,uint32)": ErrInvalidMessageLength,
|
|
"InvalidMessageType()": ErrInvalidMessageType,
|
|
"InvalidMinStakeDuration(uint64)": ErrInvalidMinStakeDuration,
|
|
"InvalidNonce(uint64)": ErrInvalidNonce,
|
|
"InvalidPChainOwnerThreshold(uint256,uint256)": ErrInvalidPChainOwnerThreshold,
|
|
"InvalidStakeAmount(uint256)": ErrInvalidStakeAmount,
|
|
"InvalidStakeMultiplier(uint8)": ErrInvalidStakeMultiplier,
|
|
"InvalidTokenAddress(address)": ErrInvalidTokenAddress,
|
|
"InvalidTotalWeight(uint256)": ErrInvalidTotalWeight,
|
|
"MaxWeightExceeded(uint64)": ErrMaxWeightExceeded,
|
|
"MinStakeDurationNotPassed(uint64)": ErrMinStakeDurationNotPassed,
|
|
"PChainOwnerAddressesNotSorted()": ErrPChainOwnerAddressesNotSorted,
|
|
"UnauthorizedOwner(address)": ErrUnauthorizedOwner,
|
|
"UnexpectedRegistrationStatus(bool)": ErrUnexpectedRegistrationStatus,
|
|
"ValidatorIneligibleForRewards(bytes32)": ErrValidatorIneligibleForRewards,
|
|
"ValidatorNotPoS(bytes32)": ErrValidatorNotPoS,
|
|
"ZeroWeightToValueFactor()": ErrZeroWeightToValueFactor,
|
|
"OwnableInvalidOwner(address)": ErrInvalidOwner,
|
|
"OwnableUnauthorizedAccount(address)": ErrUnauthorizedOwner,
|
|
}
|
|
)
|
|
|
|
type PoSParams struct {
|
|
MinimumStakeAmount *big.Int
|
|
MaximumStakeAmount *big.Int
|
|
MinimumStakeDuration uint64
|
|
MinimumDelegationFee uint16
|
|
MaximumStakeMultiplier uint8
|
|
WeightToValueFactor *big.Int
|
|
RewardCalculatorAddress string
|
|
UptimeBlockchainID ids.ID
|
|
}
|
|
|
|
func (p PoSParams) Verify() error {
|
|
if p.MinimumStakeAmount.Cmp(big.NewInt(0)) < 0 {
|
|
return fmt.Errorf("minimum stake amount cannot be negative")
|
|
}
|
|
if p.MaximumStakeAmount.Cmp(big.NewInt(0)) < 0 {
|
|
return fmt.Errorf("maximum stake amount cannot be negative")
|
|
}
|
|
if p.MaximumStakeAmount.Cmp(p.MinimumStakeAmount) < 0 {
|
|
return fmt.Errorf("maximum stake amount cannot be less than minimum stake amount")
|
|
}
|
|
if p.WeightToValueFactor.Cmp(big.NewInt(0)) < 0 {
|
|
return fmt.Errorf("weight to value factor cannot be negative")
|
|
}
|
|
if p.RewardCalculatorAddress == "" {
|
|
return fmt.Errorf("reward calculator address cannot be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetPChainChainToL1ConversionUnsignedMessage(
|
|
net models.Network,
|
|
chainID ids.ID,
|
|
managerBlockchainID ids.ID,
|
|
managerAddress crypto.Address,
|
|
convertChainValidators []*txs.ConvertNetworkToL1Validator,
|
|
) (*warp.Message, error) {
|
|
validators := []warpMessage.ChainToL1ConversionValidatorData{}
|
|
for _, convertChainValidator := range convertChainValidators {
|
|
validators = append(validators, warpMessage.ChainToL1ConversionValidatorData{
|
|
NodeID: convertChainValidator.NodeID[:],
|
|
BLSPublicKey: convertChainValidator.Signer.PublicKey,
|
|
Weight: convertChainValidator.Weight,
|
|
})
|
|
}
|
|
chainConversionData := warpMessage.ChainToL1ConversionData{
|
|
ChainID: chainID,
|
|
ManagerChainID: managerBlockchainID,
|
|
ManagerAddress: managerAddress.Bytes(),
|
|
Validators: validators,
|
|
}
|
|
chainConversionID, err := warpMessage.ChainToL1ConversionID(chainConversionData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addressedCallPayload, err := warpMessage.NewChainToL1Conversion(chainConversionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
chainConversionAddressedCall, err := warpPayload.NewAddressedCall(
|
|
nil,
|
|
addressedCallPayload.Bytes(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
chainConversionUnsignedMessage, err := warp.NewMessage(
|
|
net.ID(),
|
|
constants.PlatformChainID,
|
|
chainConversionAddressedCall.Bytes(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return chainConversionUnsignedMessage, nil
|
|
}
|
|
|
|
// InitializeValidatorsSet calls poa manager validators set init method,
|
|
// passing to it the p-chain signed [chainConversionSignedMessage]
|
|
// to verify p-chain already processed the associated ConvertNetworkToL1Tx
|
|
func InitializeValidatorsSet(
|
|
rpcURL string,
|
|
managerAddress crypto.Address,
|
|
privateKey string,
|
|
chainID ids.ID,
|
|
managerBlockchainID ids.ID,
|
|
convertChainValidators []*txs.ConvertNetworkToL1Validator,
|
|
chainConversionSignedMessage *warp.Message,
|
|
) (*types.Transaction, *types.Receipt, error) {
|
|
type InitialValidator struct {
|
|
NodeID []byte
|
|
BlsPublicKey []byte
|
|
Weight uint64
|
|
}
|
|
type ChainConversionData struct {
|
|
ChainID [32]byte
|
|
ValidatorManagerBlockchainID [32]byte
|
|
ValidatorManagerAddress crypto.Address
|
|
InitialValidators []InitialValidator
|
|
}
|
|
validators := []InitialValidator{}
|
|
for _, convertChainValidator := range convertChainValidators {
|
|
validators = append(validators, InitialValidator{
|
|
NodeID: convertChainValidator.NodeID[:],
|
|
BlsPublicKey: convertChainValidator.Signer.PublicKey[:],
|
|
Weight: convertChainValidator.Weight,
|
|
})
|
|
}
|
|
chainConversionData := ChainConversionData{
|
|
ChainID: chainID,
|
|
ValidatorManagerBlockchainID: managerBlockchainID,
|
|
ValidatorManagerAddress: managerAddress,
|
|
InitialValidators: validators,
|
|
}
|
|
// No conversion needed - already using the right warp type
|
|
nodeWarpMsg := chainConversionSignedMessage
|
|
|
|
return contract.TxToMethodWithWarpMessage(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
managerAddress,
|
|
nodeWarpMsg,
|
|
big.NewInt(0),
|
|
"initialize validator set",
|
|
ErrorSignatureToError,
|
|
"initializeValidatorSet((bytes32,bytes32,address,[(bytes,bytes,uint64)]),uint32)",
|
|
chainConversionData,
|
|
uint32(0),
|
|
)
|
|
}
|
|
|
|
// GetValidatorManagerType returns validatormanagertypes.ProofOfAuthority if validator manager is verified to be Proof of Authority
|
|
// If validator manager is verified to be Proof of Stake, returns validatormanagertypes.ProofOfStake
|
|
// In other cases, returns validatormanagertypes.UndefinedValidatorManagement
|
|
func GetValidatorManagerType(
|
|
rpcURL string,
|
|
managerAddress crypto.Address,
|
|
) validatormanagertypes.ValidatorManagementType {
|
|
// verify it is PoS
|
|
if _, err := PoSWeightToValue(rpcURL, managerAddress, 0); err == nil {
|
|
return validatormanagertypes.ProofOfStake
|
|
}
|
|
// verify it is PoA
|
|
if _, err := validator.GetValidationID(rpcURL, managerAddress, ids.EmptyNodeID); err == nil {
|
|
return validatormanagertypes.ProofOfAuthority
|
|
}
|
|
return validatormanagertypes.UndefinedValidatorManagement
|
|
}
|