diff --git a/genesis/camino_genesis.go b/genesis/camino_genesis.go index 5bbcad462d21..3c75b0b868bf 100644 --- a/genesis/camino_genesis.go +++ b/genesis/camino_genesis.go @@ -370,7 +370,7 @@ func buildPGenesis(config *Config, hrp string, xGenesisBytes []byte, xGenesisDat for _, allocation := range config.Camino.Allocations { var addrState as.AddressState if allocation.AddressStates.ConsortiumMember { - addrState |= as.AddressStateConsortiumMember + addrState |= as.AddressStateConsortium } if allocation.AddressStates.KYCVerified { addrState |= as.AddressStateKYCVerified diff --git a/vms/platformvm/addrstate/camino_address_state.go b/vms/platformvm/addrstate/camino_address_state.go index 4f94b9d72700..d1e063eead66 100644 --- a/vms/platformvm/addrstate/camino_address_state.go +++ b/vms/platformvm/addrstate/camino_address_state.go @@ -11,7 +11,7 @@ const ( AddressStateBitRoleAdmin AddressStateBit = 0 // super role - AddressStateBitRoleKYC AddressStateBit = 1 // allows to set KYCVerified and KYCExpired + AddressStateBitRoleKYCAdmin AddressStateBit = 1 // allows to set KYCVerified and KYCExpired AddressStateBitRoleOffersAdmin AddressStateBit = 2 // allows to set OffersCreator AddressStateBitRoleConsortiumAdminProposer AddressStateBit = 3 // allows to create admin add/exclude member proposals @@ -28,30 +28,33 @@ const ( AddressStateEmpty AddressState = 0 - AddressStateRoleAdmin AddressState = AddressState(1) << AddressStateBitRoleAdmin // 0b1 - AddressStateRoleKYC AddressState = AddressState(1) << AddressStateBitRoleKYC // 0b10 - AddressStateRoleOffersAdmin AddressState = AddressState(1) << AddressStateBitRoleOffersAdmin // 0b100 - AddressStateRoleConsortiumAdminProposer AddressState = AddressState(1) << AddressStateBitRoleConsortiumAdminProposer // 0b1000 - AddressStateRoleAll AddressState = AddressStateRoleAdmin | AddressStateRoleKYC | // 0b1111 - AddressStateRoleOffersAdmin | AddressStateRoleConsortiumAdminProposer + AddressStateRoleAdmin = AddressState(1) << AddressStateBitRoleAdmin // 0b1 + AddressStateRoleKYCAdmin = AddressState(1) << AddressStateBitRoleKYCAdmin // 0b10 + AddressStateRoleOffersAdmin = AddressState(1) << AddressStateBitRoleOffersAdmin // 0b100 + AddressStateRoleConsortiumAdminProposer = AddressState(1) << AddressStateBitRoleConsortiumAdminProposer // 0b1000 - AddressStateKYCVerified AddressState = AddressState(1) << AddressStateBitKYCVerified // 0b0100000000000000000000000000000000 - AddressStateKYCExpired AddressState = AddressState(1) << AddressStateBitKYCExpired // 0b1000000000000000000000000000000000 - AddressStateKYCAll AddressState = AddressStateKYCVerified | AddressStateKYCExpired // 0b1100000000000000000000000000000000 + AddressStateKYCVerified = AddressState(1) << AddressStateBitKYCVerified // 0b0100000000000000000000000000000000 + AddressStateKYCExpired = AddressState(1) << AddressStateBitKYCExpired // 0b1000000000000000000000000000000000 - AddressStateConsortiumMember AddressState = AddressState(1) << AddressStateBitConsortium // 0b0100000000000000000000000000000000000000 - AddressStateNodeDeferred AddressState = AddressState(1) << AddressStateBitNodeDeferred // 0b1000000000000000000000000000000000000000 - AddressStateVotableBits AddressState = AddressStateConsortiumMember | AddressStateNodeDeferred // 0b1100000000000000000000000000000000000000 + AddressStateConsortium = AddressState(1) << AddressStateBitConsortium // 0b0100000000000000000000000000000000000000 + AddressStateNodeDeferred = AddressState(1) << AddressStateBitNodeDeferred // 0b1000000000000000000000000000000000000000 - AddressStateOffersCreator AddressState = AddressState(1) << AddressStateBitOffersCreator // 0b00100000000000000000000000000000000000000000000000000 - AddressStateCaminoProposer AddressState = AddressState(1) << AddressStateBitCaminoProposer // 0b01000000000000000000000000000000000000000000000000000 + AddressStateOffersCreator = AddressState(1) << AddressStateBitOffersCreator // 0b0100000000000000000000000000000000000000000000000000 + AddressStateCaminoProposer = AddressState(1) << AddressStateBitCaminoProposer // 0b1000000000000000000000000000000000000000000000000000 - AddressStateAthensPhaseBits = AddressStateRoleOffersAdmin | AddressStateOffersCreator - AddressStateBerlinPhaseBits = AddressStateCaminoProposer | AddressStateRoleOffersAdmin + // Bit groups (as AddressState) - AddressStateValidBits = AddressStateRoleAll | AddressStateKYCAll | AddressStateVotableBits | + AddressStateSunrisePhaseBits = AddressStateRoleAdmin | AddressStateRoleKYCAdmin | // 0b1100001100000000000000000000000000000011 + AddressStateKYCVerified | AddressStateKYCExpired | AddressStateConsortium | + AddressStateNodeDeferred + + AddressStateAthensPhaseBits = AddressStateRoleOffersAdmin | AddressStateOffersCreator // 0b0100000000000000000000000000000000000000000000000100 + + AddressStateBerlinPhaseBits = AddressStateCaminoProposer | AddressStateRoleConsortiumAdminProposer // 0b1000000000000000000000000000000000000000000000001000 + + AddressStateValidBits = AddressStateSunrisePhaseBits | // 0b1100000000001100001100000000000000000000000000001111 AddressStateAthensPhaseBits | - AddressStateBerlinPhaseBits // 0b1100000000001100001100000000000000000000000000001111 + AddressStateBerlinPhaseBits ) func (as AddressState) Is(state AddressState) bool { @@ -61,3 +64,7 @@ func (as AddressState) Is(state AddressState) bool { func (as AddressState) IsNot(state AddressState) bool { return as&state != state } + +func (asb AddressStateBit) ToAddressState() AddressState { + return AddressState(1) << asb +} diff --git a/vms/platformvm/camino_helpers_test.go b/vms/platformvm/camino_helpers_test.go index d5571b7245fe..46a2571ef4ed 100644 --- a/vms/platformvm/camino_helpers_test.go +++ b/vms/platformvm/camino_helpers_test.go @@ -210,7 +210,7 @@ func newCaminoGenesisWithUTXOs(t *testing.T, caminoGenesisConfig api.Camino, gen caminoGenesisConfig.ValidatorConsortiumMembers[i] = key.Address() caminoGenesisConfig.AddressStates = append(caminoGenesisConfig.AddressStates, genesis.AddressState{ Address: key.Address(), - State: as.AddressStateConsortiumMember, + State: as.AddressStateConsortium, }) } diff --git a/vms/platformvm/camino_service.go b/vms/platformvm/camino_service.go index d8bc61e1cb52..690201bb3ff3 100644 --- a/vms/platformvm/camino_service.go +++ b/vms/platformvm/camino_service.go @@ -290,10 +290,11 @@ func (s *CaminoService) SetAddressState(_ *http.Request, args *SetAddressStateAr // Create the transaction tx, err := s.vm.txBuilder.NewAddressStateTx( - targetAddr, // Address to change state - args.Remove, // Add or remove State - args.State, // The state to change - privKeys, // Keys providing the staked tokens + targetAddr, // Address to change state + args.Remove, // Add or remove State + args.State, // The state to change + ids.ShortEmpty, // executor address + privKeys, // Keys providing the staked tokens change, ) if err != nil { diff --git a/vms/platformvm/camino_vm_test.go b/vms/platformvm/camino_vm_test.go index 7d938bb8d821..33db9cc5a87e 100644 --- a/vms/platformvm/camino_vm_test.go +++ b/vms/platformvm/camino_vm_test.go @@ -81,6 +81,7 @@ func TestRemoveDeferredValidator(t *testing.T) { adminProposerKey.Address(), false, as.AddressStateBitRoleConsortiumAdminProposer, + rootAdminKey.Address(), []*secp256k1.PrivateKey{rootAdminKey}, outputOwners, ) @@ -90,6 +91,7 @@ func TestRemoveDeferredValidator(t *testing.T) { consortiumMemberKey.Address(), false, as.AddressStateBitKYCVerified, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -146,6 +148,7 @@ func TestRemoveDeferredValidator(t *testing.T) { consortiumMemberKey.Address(), false, as.AddressStateBitNodeDeferred, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) @@ -160,7 +163,7 @@ func TestRemoveDeferredValidator(t *testing.T) { // Verify that the validator's owner's deferred state and consortium member is true ownerState, _ := vm.state.GetAddressStates(consortiumMemberKey.Address()) - require.Equal(ownerState, as.AddressStateNodeDeferred|as.AddressStateConsortiumMember|as.AddressStateKYCVerified) + require.Equal(ownerState, as.AddressStateNodeDeferred|as.AddressStateConsortium|as.AddressStateKYCVerified) // Fast-forward clock to time for validator to be rewarded vm.clock.Set(endTime) @@ -211,7 +214,7 @@ func TestRemoveDeferredValidator(t *testing.T) { // Verify that the validator's owner's deferred state is false ownerState, _ = vm.state.GetAddressStates(consortiumMemberKey.Address()) - require.Equal(ownerState, as.AddressStateConsortiumMember|as.AddressStateKYCVerified) + require.Equal(ownerState, as.AddressStateConsortium|as.AddressStateKYCVerified) timestamp := vm.state.GetTimestamp() require.Equal(endTime.Unix(), timestamp.Unix()) @@ -260,6 +263,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { adminProposerKey.Address(), false, as.AddressStateBitRoleConsortiumAdminProposer, + rootAdminKey.Address(), []*secp256k1.PrivateKey{rootAdminKey}, outputOwners, ) @@ -269,6 +273,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { consortiumMemberKey.Address(), false, as.AddressStateBitKYCVerified, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -326,6 +331,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { consortiumMemberKey.Address(), false, as.AddressStateBitNodeDeferred, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) @@ -343,6 +349,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { consortiumMemberKey.Address(), true, as.AddressStateBitNodeDeferred, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) @@ -586,6 +593,7 @@ func TestProposals(t *testing.T) { proposerAddr, false, as.AddressStateBitCaminoProposer, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -728,6 +736,7 @@ func TestAdminProposals(t *testing.T) { proposerAddr, false, as.AddressStateBitRoleConsortiumAdminProposer, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -737,13 +746,14 @@ func TestAdminProposals(t *testing.T) { checkTx(t, vm, blk.ID(), addrStateTx.ID()) applicantAddrState, err := vm.state.GetAddressStates(applicantAddr) require.NoError(err) - require.True(applicantAddrState.IsNot(as.AddressStateConsortiumMember)) + require.True(applicantAddrState.IsNot(as.AddressStateConsortium)) // Make applicant (see admin proposal below) kyc-verified addrStateTx, err = vm.txBuilder.NewAddressStateTx( applicantAddr, false, as.AddressStateBitKYCVerified, + caminoPreFundedKeys[0].Address(), []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -792,7 +802,7 @@ func TestAdminProposals(t *testing.T) { // check that applicant became c-member applicantAddrState, err = vm.state.GetAddressStates(applicantAddr) require.NoError(err) - require.True(applicantAddrState.Is(as.AddressStateConsortiumMember)) + require.True(applicantAddrState.Is(as.AddressStateConsortium)) } func TestExcludeMemberProposals(t *testing.T) { @@ -920,6 +930,7 @@ func TestExcludeMemberProposals(t *testing.T) { consortiumAdminKey.Address(), false, as.AddressStateBitRoleConsortiumAdminProposer, + rootAdminKey.Address(), []*secp256k1.PrivateKey{rootAdminKey, fundsKey}, nil, ) @@ -946,6 +957,7 @@ func TestExcludeMemberProposals(t *testing.T) { memberToExcludeAddr, false, as.AddressStateBitKYCVerified, + rootAdminKey.Address(), []*secp256k1.PrivateKey{rootAdminKey}, nil, ) @@ -984,7 +996,7 @@ func TestExcludeMemberProposals(t *testing.T) { require.Equal(expectedHeight, height) memberAddrState, err = vm.state.GetAddressStates(memberToExcludeAddr) require.NoError(err) - require.Equal(as.AddressStateConsortiumMember|as.AddressStateKYCVerified, memberAddrState) + require.Equal(as.AddressStateConsortium|as.AddressStateKYCVerified, memberAddrState) bondedAmt -= proposalBondAmount checkBalance(t, vm.state, fundsAddr, balance-burnedAmt, // total @@ -1214,7 +1226,7 @@ func TestExcludeMemberProposals(t *testing.T) { 0, 0, balance-burnedAmt-bondedAmt, // unlocked ) } else { - require.Equal(as.AddressStateConsortiumMember|as.AddressStateKYCVerified, memberAddrState) + require.Equal(as.AddressStateConsortium|as.AddressStateKYCVerified, memberAddrState) if tt.pendingValidator { require.NoError(pendingValidatorErr) } else { diff --git a/vms/platformvm/state/camino.go b/vms/platformvm/state/camino.go index 34a86ce4bc09..d4a0e421ca8e 100644 --- a/vms/platformvm/state/camino.go +++ b/vms/platformvm/state/camino.go @@ -386,9 +386,9 @@ func (cs *caminoState) syncGenesis(s *state, g *genesis.Genesis) error { initalAdminAddressState|as.AddressStateRoleAdmin) addrStateTx, err := txs.NewSigned(&txs.AddressStateTx{ - Address: g.Camino.InitialAdmin, - State: as.AddressStateBitRoleAdmin, - Remove: false, + Address: g.Camino.InitialAdmin, + StateBit: as.AddressStateBitRoleAdmin, + Remove: false, }, txs.Codec, nil) if err != nil { return err diff --git a/vms/platformvm/state/camino_test.go b/vms/platformvm/state/camino_test.go index b361d3ed9ce9..295e7486cca6 100644 --- a/vms/platformvm/state/camino_test.go +++ b/vms/platformvm/state/camino_test.go @@ -247,13 +247,13 @@ func TestSyncGenesis(t *testing.T) { }, { Address: shortID, - State: as.AddressStateRoleKYC, + State: as.AddressStateRoleKYCAdmin, }, }, depositTxs, initialAdmin), }, cs: *wrappers.IgnoreError(newCaminoState(db, validatorsDB, prometheus.NewRegistry())).(*caminoState), want: caminoDiff{ - modifiedAddressStates: map[ids.ShortID]as.AddressState{initialAdmin: as.AddressStateRoleAdmin, shortID: as.AddressStateRoleKYC}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{initialAdmin: as.AddressStateRoleAdmin, shortID: as.AddressStateRoleKYCAdmin}, modifiedDepositOffers: map[ids.ID]*deposit.Offer{ depositOffers[0].ID: depositOffers[0], depositOffers[1].ID: depositOffers[1], diff --git a/vms/platformvm/txs/builder/camino_builder.go b/vms/platformvm/txs/builder/camino_builder.go index 5767f2ceca4b..14730b3f1c08 100644 --- a/vms/platformvm/txs/builder/camino_builder.go +++ b/vms/platformvm/txs/builder/camino_builder.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/utils" @@ -32,12 +33,14 @@ var ( fakeTreasuryKey = secp256k1.FakePrivateKey(treasury.Addr) fakeTreasuryKeychain = secp256k1fx.NewKeychain(fakeTreasuryKey) - errKeyMissing = errors.New("couldn't find key matching address") - errWrongNodeKeyType = errors.New("node key type isn't *secp256k1.PrivateKey") - errNotSECPOwner = errors.New("owner is not *secp256k1fx.OutputOwners") - errWrongLockMode = errors.New("this tx can't be used with this caminoGenesis.LockModeBondDeposit") - errNoUTXOsForImport = errors.New("no utxos for import") - errWrongOutType = errors.New("wrong output type") + errKeyMissing = errors.New("couldn't find key matching address") + errWrongNodeKeyType = errors.New("node key type isn't *secp256k1.PrivateKey") + errNotSECPOwner = errors.New("owner is not *secp256k1fx.OutputOwners") + errWrongLockMode = errors.New("this tx can't be used with this caminoGenesis.LockModeBondDeposit") + errNoUTXOsForImport = errors.New("no utxos for import") + errWrongOutType = errors.New("wrong output type") + errEmptyAddress = errors.New("address is empty") + errEmptyExecutorAddress = errors.New("executor address is empty") ) type CaminoBuilder interface { @@ -63,6 +66,7 @@ type CaminoTxBuilder interface { address ids.ShortID, remove bool, state as.AddressStateBit, + executor ids.ShortID, keys []*secp256k1.PrivateKey, change *secp256k1fx.OutputOwners, ) (*txs.Tx, error) @@ -295,6 +299,7 @@ func (b *caminoBuilder) NewAddressStateTx( address ids.ShortID, remove bool, state as.AddressStateBit, + executor ids.ShortID, keys []*secp256k1.PrivateKey, change *secp256k1fx.OutputOwners, ) (*txs.Tx, error) { @@ -303,18 +308,51 @@ func (b *caminoBuilder) NewAddressStateTx( return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err) } - // Create the tx - utx := &txs.AddressStateTx{ - BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ - NetworkID: b.ctx.NetworkID, - BlockchainID: b.ctx.ChainID, - Ins: ins, - Outs: outs, - }}, - Address: address, - Remove: remove, - State: state, + isBerlin := b.cfg.IsBerlinPhaseActivated(b.state.GetTimestamp()) + + switch { + case address == ids.ShortEmpty: + return nil, errEmptyAddress + case isBerlin && executor == ids.ShortEmpty: + return nil, errEmptyExecutorAddress + } + + executorSigner, err := getSigner(keys, executor) + if err != nil { + return nil, err + } + + var utx *txs.AddressStateTx + if isBerlin { + utx = &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.ctx.NetworkID, + BlockchainID: b.ctx.ChainID, + Ins: ins, + Outs: outs, + }}, + Address: address, + Remove: remove, + StateBit: state, + Executor: executor, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + } + signers = append(signers, executorSigner) + } else { + utx = &txs.AddressStateTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.ctx.NetworkID, + BlockchainID: b.ctx.ChainID, + Ins: ins, + Outs: outs, + }}, + Address: address, + Remove: remove, + StateBit: state, + } } + tx, err := txs.NewSigned(utx, txs.Codec, signers) if err != nil { return nil, err diff --git a/vms/platformvm/txs/builder/camino_builder_test.go b/vms/platformvm/txs/builder/camino_builder_test.go index 351d6caae43a..a6b26d364a37 100644 --- a/vms/platformvm/txs/builder/camino_builder_test.go +++ b/vms/platformvm/txs/builder/camino_builder_test.go @@ -40,6 +40,7 @@ func TestCaminoEnv(t *testing.T) { env.config.BanffTime = env.state.GetTimestamp() } +// only support upgr version 0 func TestCaminoBuilderTxAddressState(t *testing.T) { caminoConfig := api.Camino{ VerifyNodeSignature: true, @@ -54,39 +55,32 @@ func TestCaminoBuilderTxAddressState(t *testing.T) { tests := map[string]struct { remove bool - state as.AddressStateBit + stateBit as.AddressStateBit address ids.ShortID expectedErr error }{ "KYC Role: Add": { - remove: false, - state: as.AddressStateBitRoleKYC, - address: caminoPreFundedKeys[0].PublicKey().Address(), - expectedErr: nil, + stateBit: as.AddressStateBitRoleKYCAdmin, + address: caminoPreFundedKeys[0].Address(), }, "KYC Role: Remove": { - remove: true, - state: as.AddressStateBitRoleKYC, - address: caminoPreFundedKeys[0].PublicKey().Address(), - expectedErr: nil, + remove: true, + stateBit: as.AddressStateBitRoleKYCAdmin, + address: caminoPreFundedKeys[0].Address(), }, "Admin Role: Add": { - remove: false, - state: as.AddressStateBitRoleAdmin, - address: caminoPreFundedKeys[0].PublicKey().Address(), - expectedErr: nil, + stateBit: as.AddressStateBitRoleAdmin, + address: caminoPreFundedKeys[0].Address(), }, "Admin Role: Remove": { - remove: true, - state: as.AddressStateBitRoleAdmin, - address: caminoPreFundedKeys[0].PublicKey().Address(), - expectedErr: nil, + remove: true, + stateBit: as.AddressStateBitRoleAdmin, + address: caminoPreFundedKeys[0].Address(), }, - "Empty Address": { - remove: false, - state: as.AddressStateBitRoleKYC, + "Empty address": { + stateBit: as.AddressStateBitRoleKYCAdmin, address: ids.ShortEmpty, - expectedErr: txs.ErrEmptyAddress, + expectedErr: errEmptyAddress, }, } @@ -95,7 +89,8 @@ func TestCaminoBuilderTxAddressState(t *testing.T) { _, err := env.txBuilder.NewAddressStateTx( tt.address, tt.remove, - tt.state, + tt.stateBit, + caminoPreFundedKeys[0].Address(), caminoPreFundedKeys, nil, ) @@ -186,7 +181,7 @@ func TestUnlockDepositTx(t *testing.T) { outputOwners := secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testKey.PublicKey().Address()}, + Addrs: []ids.ShortID{testKey.Address()}, } depositTxID := ids.GenerateTestID() depositStartTime := time.Now() diff --git a/vms/platformvm/txs/camino_address_state_tx.go b/vms/platformvm/txs/camino_address_state_tx.go index 108444bc7099..50fd51686a26 100644 --- a/vms/platformvm/txs/camino_address_state_tx.go +++ b/vms/platformvm/txs/camino_address_state_tx.go @@ -5,6 +5,7 @@ package txs import ( "errors" + "fmt" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" @@ -17,8 +18,10 @@ import ( var ( _ UnsignedTx = (*AddressStateTx)(nil) - ErrEmptyAddress = errors.New("address is empty") - ErrInvalidState = errors.New("invalid state") + ErrEmptyExecutorAddress = errors.New("executor address is empty") + errEmptyAddress = errors.New("address is empty") + errBadExecutorAuth = errors.New("bad executor auth") + errInvalidAddrStateBit = errors.New("invalid address state bit") ) // AddressStateTx is an unsigned AddressStateTx @@ -30,7 +33,7 @@ type AddressStateTx struct { // The address to add / remove state Address ids.ShortID `serialize:"true" json:"address"` // The state to set / unset - State as.AddressStateBit `serialize:"true" json:"state"` + StateBit as.AddressStateBit `serialize:"true" json:"state"` // Remove or add the flag ? Remove bool `serialize:"true" json:"remove"` // The executor of this TX (needs access role) @@ -47,17 +50,17 @@ func (tx *AddressStateTx) SyntacticVerify(ctx *snow.Context) error { case tx.SyntacticallyVerified: // already passed syntactic verification return nil case tx.Address == ids.ShortEmpty: - return ErrEmptyAddress - case tx.State > as.AddressStateBitMax || as.AddressStateValidBits&as.AddressState(uint64(1)< as.AddressStateBitMax || tx.StateBit.ToAddressState()&as.AddressStateValidBits == 0: + return fmt.Errorf("%w: bit (%d)", errInvalidAddrStateBit, tx.StateBit) } if tx.UpgradeVersionID.Version() > 0 { if err := tx.ExecutorAuth.Verify(); err != nil { - return err + return fmt.Errorf("%w: %w", errBadExecutorAuth, err) } if tx.Executor == ids.ShortEmpty { - return ErrEmptyAddress + return ErrEmptyExecutorAddress } } diff --git a/vms/platformvm/txs/camino_address_state_tx_test.go b/vms/platformvm/txs/camino_address_state_tx_test.go index c2ac6850f8d2..560254739d49 100644 --- a/vms/platformvm/txs/camino_address_state_tx_test.go +++ b/vms/platformvm/txs/camino_address_state_tx_test.go @@ -4,192 +4,167 @@ package txs import ( + "fmt" "testing" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/vms/components/avax" as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/locked" - "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) func TestAddressStateTxSyntacticVerify(t *testing.T) { - require := require.New(t) ctx := defaultContext() - signers := [][]*secp256k1.PrivateKey{preFundedKeys} + addr1 := ids.ShortID{1} + lockTxID := ids.ID{2} + owner1 := secp256k1fx.OutputOwners{Threshold: 1, Addrs: []ids.ShortID{addr1}} - var ( - stx *Tx - addressStateTx *AddressStateTx - ) - - // Case : signed tx is nil - err := stx.SyntacticVerify(ctx) - require.ErrorIs(err, ErrNilSignedTx) + baseTx := BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + }} - // Case : unsigned tx is nil - err = addressStateTx.SyntacticVerify(ctx) - require.ErrorIs(err, ErrNilTx) + type testCase struct { + tx *AddressStateTx + expectedErr error + } - inputs := []*avax.TransferableInput{{ - UTXOID: avax.UTXOID{ - TxID: ids.ID{'t', 'x', 'I', 'D'}, - OutputIndex: 2, + tests := map[string]testCase{ + "Nil tx": { + expectedErr: ErrNilTx, }, - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 't'}}, - In: &secp256k1fx.TransferInput{ - Amt: uint64(5678), - Input: secp256k1fx.Input{SigIndices: []uint32{0}}, + "Empty target address": { + tx: &AddressStateTx{ + BaseTx: baseTx, + }, + expectedErr: errEmptyAddress, }, - }} - outputs := []*avax.TransferableOutput{{ - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 't'}}, - Out: &secp256k1fx.TransferOutput{ - Amt: uint64(1234), - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, + "Address state bit is greater than max possible bit": { + tx: &AddressStateTx{ + BaseTx: baseTx, + Address: addr1, + StateBit: as.AddressStateBitMax + 1, }, + expectedErr: errInvalidAddrStateBit, }, - }} - lockedOut := &avax.TransferableOutput{ - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 't'}}, - Out: &locked.Out{ - IDs: locked.IDsEmpty, - TransferableOut: &secp256k1fx.TransferOutput{ - Amt: uint64(1234), - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, - }, + "UpgradeVersion1, bad executor auth": { + tx: &AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: addr1, + ExecutorAuth: (*secp256k1fx.Input)(nil), }, + expectedErr: errBadExecutorAuth, }, - } - - stakedOut := &avax.TransferableOutput{ - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 't'}}, - Out: &stakeable.LockOut{ - TransferableOut: &secp256k1fx.TransferOutput{ - Amt: uint64(1234), - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, - }, + "UpgradeVersion1, empty executor address": { + tx: &AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: addr1, + ExecutorAuth: &secp256k1fx.Input{}, + }, + expectedErr: ErrEmptyExecutorAddress, + }, + "Stakeable base tx input": { + tx: &AddressStateTx{ + BaseTx: BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Ins: []*avax.TransferableInput{ + generateTestStakeableIn(ctx.AVAXAssetID, 1, 1, []uint32{0}), + }, + }}, + Address: addr1, + }, + expectedErr: locked.ErrWrongInType, + }, + "Stakeable base tx output": { + tx: &AddressStateTx{ + BaseTx: BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Outs: []*avax.TransferableOutput{ + generateTestStakeableOut(ctx.AVAXAssetID, 1, 1, owner1), + }, + }}, + Address: addr1, + }, + expectedErr: locked.ErrWrongOutType, + }, + "Locked base tx input": { + tx: &AddressStateTx{ + BaseTx: BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Ins: []*avax.TransferableInput{ + generateTestIn(ctx.AVAXAssetID, 1, lockTxID, ids.Empty, []uint32{0}), + }, + }}, + Address: addr1, + }, + expectedErr: locked.ErrWrongInType, + }, + "Locked base tx output": { + tx: &AddressStateTx{ + BaseTx: BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Outs: []*avax.TransferableOutput{ + generateTestOut(ctx.AVAXAssetID, 1, owner1, lockTxID, ids.Empty), + }, + }}, + Address: addr1, + }, + expectedErr: locked.ErrWrongOutType, + }, + "OK: UpgradeVersion0": { + tx: &AddressStateTx{ + BaseTx: baseTx, + Address: addr1, + }, + }, + "OK: UpgradeVersion1": { + tx: &AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: addr1, + Executor: addr1, + ExecutorAuth: &secp256k1fx.Input{}, }, }, } - addressStateTx = &AddressStateTx{ - BaseTx: BaseTx{BaseTx: avax.BaseTx{ - NetworkID: ctx.NetworkID, - BlockchainID: ctx.ChainID, - Ins: inputs, - Outs: outputs, - Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }}, - Address: preFundedKeys[0].PublicKey().Address(), - State: as.AddressStateBitRoleAdmin, - Remove: false, - } - - lockedOutputs := append([]*avax.TransferableOutput{}, outputs...) - lockedOutputs = append(lockedOutputs, lockedOut) - - addressStateTxLocked := &AddressStateTx{ - BaseTx: BaseTx{BaseTx: avax.BaseTx{ - NetworkID: ctx.NetworkID, - BlockchainID: ctx.ChainID, - Ins: inputs, - Outs: lockedOutputs, - Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }}, - Address: preFundedKeys[0].PublicKey().Address(), - State: as.AddressStateBitRoleAdmin, - Remove: false, - } - - lockedOutputs[1] = stakedOut - addressStateTxStaked := &AddressStateTx{ - BaseTx: BaseTx{BaseTx: avax.BaseTx{ - NetworkID: ctx.NetworkID, - BlockchainID: ctx.ChainID, - Ins: inputs, - Outs: lockedOutputs, - Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }}, - Address: preFundedKeys[0].PublicKey().Address(), - State: as.AddressStateBitRoleAdmin, - Remove: false, + // bit range test cases + for bit := as.AddressStateBit(0); bit <= as.AddressStateBitMax; bit++ { + tx := &AddressStateTx{ + BaseTx: baseTx, + Address: addr1, + StateBit: bit, + } + if bit.ToAddressState()&as.AddressStateValidBits == 0 { + testCaseName := fmt.Sprintf("BitValidity/%02d is invalid", bit) + require.NotContains(t, tests, testCaseName) + tests[testCaseName] = testCase{ + tx: tx, + expectedErr: errInvalidAddrStateBit, + } + } else { + testCaseName := fmt.Sprintf("BitValidity/OK: %02d", bit) + require.NotContains(t, tests, testCaseName) + tests[testCaseName] = testCase{ + tx: tx, + } + } } - executorAuth := secp256k1fx.Input{} - - addressStateTxUpgraded := &AddressStateTx{ - BaseTx: BaseTx{BaseTx: avax.BaseTx{ - NetworkID: ctx.NetworkID, - BlockchainID: ctx.ChainID, - Ins: inputs, - Outs: outputs, - Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }}, - UpgradeVersionID: codec.BuildUpgradeVersionID(1), - Address: preFundedKeys[0].PublicKey().Address(), - State: as.AddressStateBitRoleAdmin, - Remove: false, - Executor: ids.ShortEmpty, - ExecutorAuth: &executorAuth, + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + err := tt.tx.SyntacticVerify(ctx) + require.ErrorIs(t, err, tt.expectedErr) + }) } - - // Case: valid tx - stx, err = NewSigned(addressStateTx, Codec, signers) - require.NoError(err) - require.NoError(stx.SyntacticVerify(ctx)) - - // Case: Empty address - addressStateTx.SyntacticallyVerified = false - addressStateTx.Address = ids.ShortID{} - stx, err = NewSigned(addressStateTx, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.ErrorIs(err, ErrEmptyAddress) - addressStateTx.Address = preFundedKeys[0].PublicKey().Address() - - // Invalid mode - addressStateTx.SyntacticallyVerified = false - addressStateTx.State = 99 - stx, err = NewSigned(addressStateTx, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.ErrorIs(err, ErrInvalidState) - addressStateTx.State = as.AddressStateBitRoleAdmin - - // Locked out - stx, err = NewSigned(addressStateTxLocked, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.ErrorIs(err, locked.ErrWrongOutType) - - // Staked out - stx, err = NewSigned(addressStateTxStaked, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.ErrorIs(err, locked.ErrWrongOutType) - - // Upgraded / empty executor - stx, err = NewSigned(addressStateTxUpgraded, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.ErrorIs(err, ErrEmptyAddress) - - // Upgraded / Ok - addressStateTxUpgraded.Executor = ids.ShortID{'X'} - stx, err = NewSigned(addressStateTxUpgraded, Codec, signers) - require.NoError(err) - err = stx.SyntacticVerify(ctx) - require.NoError(err) } diff --git a/vms/platformvm/txs/executor/camino_advance_time_test.go b/vms/platformvm/txs/executor/camino_advance_time_test.go index c3d408a987d3..50e177d369f2 100644 --- a/vms/platformvm/txs/executor/camino_advance_time_test.go +++ b/vms/platformvm/txs/executor/camino_advance_time_test.go @@ -377,6 +377,7 @@ func deferValidator(env *caminoEnvironment, nodeOwnerAddress ids.ShortID, key *s nodeOwnerAddress, false, as.AddressStateBitNodeDeferred, + key.Address(), []*secp256k1.PrivateKey{key}, outputOwners, ) diff --git a/vms/platformvm/txs/executor/camino_helpers_test.go b/vms/platformvm/txs/executor/camino_helpers_test.go index 972d2c755b4c..cf98876734e0 100644 --- a/vms/platformvm/txs/executor/camino_helpers_test.go +++ b/vms/platformvm/txs/executor/camino_helpers_test.go @@ -14,7 +14,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/chains" "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/memdb" @@ -22,7 +21,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/uptime" - "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" @@ -107,7 +105,7 @@ func newCaminoEnvironment(postBanff, addSubnet bool, caminoGenesisConf api.Camin rewards := reward.NewCalculator(config.RewardConfig) - baseState := defaultCaminoState(&config, ctx, baseDB, rewards, caminoGenesisConf) + baseState := defaultCaminoState(config, ctx, baseDB, rewards, caminoGenesisConf) atomicUTXOs := avax.NewAtomicUTXOManager(ctx.SharedMemory, txs.Codec) uptimes := uptime.NewManager(baseState, clk) @@ -115,7 +113,7 @@ func newCaminoEnvironment(postBanff, addSubnet bool, caminoGenesisConf api.Camin txBuilder := builder.NewCamino( ctx, - &config, + config, clk, fx, baseState, @@ -124,7 +122,7 @@ func newCaminoEnvironment(postBanff, addSubnet bool, caminoGenesisConf api.Camin ) backend := Backend{ - Config: &config, + Config: config, Ctx: ctx, Clk: clk, Bootstrapped: &isBootstrapped, @@ -136,7 +134,7 @@ func newCaminoEnvironment(postBanff, addSubnet bool, caminoGenesisConf api.Camin env := &caminoEnvironment{ isBootstrapped: &isBootstrapped, - config: &config, + config: config, clk: clk, baseDB: baseDB, ctx: ctx, @@ -239,37 +237,15 @@ func defaultCaminoState( return state } -func defaultCaminoConfig(postBanff bool) config.Config { - banffTime := mockable.MaxTime - if postBanff { - banffTime = defaultValidateEndTime.Add(-2 * time.Second) - } - - return config.Config{ - Chains: chains.TestManager, - UptimeLockedCalculator: uptime.NewLockedCalculator(), - Validators: validators.NewManager(), - TxFee: defaultTxFee, - CreateSubnetTxFee: 100 * defaultTxFee, - CreateBlockchainTxFee: 100 * defaultTxFee, - MinValidatorStake: defaultCaminoValidatorWeight, - MaxValidatorStake: defaultCaminoValidatorWeight, - MinDelegatorStake: 1 * units.MilliAvax, - MinStakeDuration: defaultMinStakingDuration, - MaxStakeDuration: defaultMaxStakingDuration, - RewardConfig: reward.Config{ - MaxConsumptionRate: .12 * reward.PercentDenominator, - MinConsumptionRate: .10 * reward.PercentDenominator, - MintingPeriod: 365 * 24 * time.Hour, - SupplyCap: 720 * units.MegaAvax, - }, - ApricotPhase3Time: defaultValidateEndTime, - ApricotPhase5Time: defaultValidateEndTime, - BanffTime: banffTime, - CaminoConfig: caminoconfig.Config{ - DACProposalBondAmount: 100 * units.Avax, - }, +func defaultCaminoConfig(postBanff bool) *config.Config { + config := defaultConfig(postBanff, true) + config.MinValidatorStake = defaultCaminoValidatorWeight + config.MaxValidatorStake = defaultCaminoValidatorWeight + config.BerlinPhaseTime = defaultValidateStartTime.Add(-2 * time.Second) + config.CaminoConfig = caminoconfig.Config{ + DACProposalBondAmount: 100 * units.Avax, } + return &config } func buildCaminoGenesisTest(ctx *snow.Context, caminoGenesisConf api.Camino) []byte { @@ -628,7 +604,7 @@ func newCaminoEnvironmentWithMocks( rewards := reward.NewCalculator(vmConfig.RewardConfig) - defaultState := defaultCaminoState(&vmConfig, ctx, baseDB, rewards, caminoGenesisConf) + defaultState := defaultCaminoState(vmConfig, ctx, baseDB, rewards, caminoGenesisConf) if sharedMemory != nil { msm = &mutableSharedMemory{ @@ -644,7 +620,7 @@ func newCaminoEnvironmentWithMocks( ctx.Lock.Lock() return &caminoEnvironment{ isBootstrapped: &isBootstrapped, - config: &vmConfig, + config: vmConfig, clk: clk, baseDB: baseDB, ctx: ctx, @@ -657,7 +633,7 @@ func newCaminoEnvironmentWithMocks( utxosHandler: utxoHandler, txBuilder: builder.NewCamino( ctx, - &vmConfig, + vmConfig, clk, fx, defaultState, @@ -665,7 +641,7 @@ func newCaminoEnvironmentWithMocks( utxoHandler, ), backend: Backend{ - Config: &vmConfig, + Config: vmConfig, Ctx: ctx, Clk: clk, Bootstrapped: &isBootstrapped, @@ -805,3 +781,39 @@ func expectProduceNewlyLockedUTXOs(t *testing.T, s *state.MockDiff, outs []*avax }) } } + +type phase int + +const ( + sunrisePhase phase = 0 + athensPhase phase = 1 + berlinPhase phase = 2 + firstPhase phase = sunrisePhase + lastPhase phase = berlinPhase +) + +func phaseTime(t *testing.T, phase phase, cfg *config.Config) time.Time { + switch phase { + case sunrisePhase: // SunrisePhase + return cfg.AthensPhaseTime.Add(-time.Second) + case athensPhase: + return cfg.AthensPhaseTime + case berlinPhase: + return cfg.BerlinPhaseTime + } + require.FailNow(t, "unknown phase") + return time.Time{} +} + +func phaseName(t *testing.T, phase phase) string { + switch phase { + case sunrisePhase: + return "SunrisePhase" + case athensPhase: + return "AthensPhase" + case berlinPhase: + return "BerlinPhase" + } + require.FailNow(t, "unknown phase") + return "" +} diff --git a/vms/platformvm/txs/executor/camino_tx_executor.go b/vms/platformvm/txs/executor/camino_tx_executor.go index e46fdce3fb7b..ba9cf2532979 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor.go +++ b/vms/platformvm/txs/executor/camino_tx_executor.go @@ -80,6 +80,7 @@ var ( errAdminCannotBeDeleted = errors.New("admin cannot be deleted") errNotAthensPhase = errors.New("not allowed before AthensPhase") errNotBerlinPhase = errors.New("not allowed before BerlinPhase") + errBerlinPhase = errors.New("not allowed after BerlinPhase") errOfferCreatorCredentialMismatch = errors.New("offer creator credential isn't matching") errNotOfferCreator = errors.New("address isn't allowed to create deposit offers") errDepositCreatorCredentialMismatch = errors.New("deposit creator credential isn't matching") @@ -1172,7 +1173,7 @@ func (e *CaminoStandardTxExecutor) RegisterNodeTx(tx *txs.RegisterNodeTx) error return err } - if consortiumMemberAddressState.IsNot(as.AddressStateConsortiumMember) { + if consortiumMemberAddressState.IsNot(as.AddressStateConsortium) { return errNotConsortiumMember } @@ -1903,7 +1904,7 @@ func (e *CaminoStandardTxExecutor) AddVoteTx(tx *txs.AddVoteTx) error { return err } - if voterAddressState.IsNot(as.AddressStateConsortiumMember) { + if voterAddressState.IsNot(as.AddressStateConsortium) { return errNotConsortiumMember } @@ -2176,12 +2177,29 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error return err } - roles := as.AddressStateEmpty - creds := e.Tx.Creds - chainTime := e.State.GetTimestamp() + txAddressState := tx.StateBit.ToAddressState() + + // Check for bits that was affected or introduced in AthensPhase isAthensPhase := e.Config.IsAthensPhaseActivated(chainTime) + if !isAthensPhase && txAddressState&as.AddressStateAthensPhaseBits != 0 { + return fmt.Errorf("%w: can't modify bit (%d) before AthensPhase", errNotAthensPhase, tx.StateBit) + } + + // Check for bits that was affected or introduced in BerlinPhase + isBerlinPhase := e.Config.IsBerlinPhaseActivated(chainTime) + if !isBerlinPhase && txAddressState&as.AddressStateBerlinPhaseBits != 0 { + return fmt.Errorf("%w: can't modify bit (%d) before BerlinPhase", errNotBerlinPhase, tx.StateBit) + } + if isBerlinPhase && tx.StateBit == as.AddressStateBitConsortium { // Berlin phase moved consortium handling to admin proposals + return fmt.Errorf("%w: can't modify 'Consortium' bit (%d) after BerlinPhase", errBerlinPhase, tx.StateBit) + } + + creds := e.Tx.Creds + roles := as.AddressStateEmpty + isRemovingAdminRole := tx.Remove && tx.StateBit == as.AddressStateBitRoleAdmin + // Getting executor roles and verifying executor credential if tx.UpgradeVersionID.Version() > 0 { if !isAthensPhase { return errNotAthensPhase @@ -2196,70 +2214,43 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error }, e.State, ); err != nil { - return fmt.Errorf("%w: %w", errSignatureMissing, err) + return fmt.Errorf("%w: missing executor's signature: %w", errSignatureMissing, err) } creds = e.Tx.Creds[:len(e.Tx.Creds)-1] + if isRemovingAdminRole && tx.Address == tx.Executor { + return errAdminCannotBeDeleted + } + roles, err = e.State.GetAddressStates(tx.Executor) if err != nil { return err } - if tx.Remove && tx.State == as.AddressStateBitRoleAdmin && tx.Address == tx.Executor { - return errAdminCannotBeDeleted - } } else { + if isBerlinPhase { + return errBerlinPhase + } addresses, err := e.Fx.RecoverAddresses(tx.Bytes(), e.Tx.Creds) if err != nil { return fmt.Errorf("%w: %w", errRecoverAddresses, err) } - if len(addresses) == 0 { - return errWrongNumberOfCredentials - } - // Accumulate roles over all signers - checkSelfRemove := tx.Remove && tx.State == as.AddressStateBitRoleAdmin for address := range addresses { + if isRemovingAdminRole && address == tx.Address { + return errAdminCannotBeDeleted + } states, err := e.State.GetAddressStates(address) if err != nil { return err } - if checkSelfRemove && address == tx.Address { - return errAdminCannotBeDeleted - } roles |= states } } - statesBit := as.AddressState(1) << tx.State - - // Check for bits that was affected or introduced in AthensPhase - if !isAthensPhase && statesBit&as.AddressStateAthensPhaseBits != 0 { - return errAddrStateNotPermitted - } - - // Check for bits that was affected or introduced in BerlinPhase - isBerlinPhase := e.Config.IsBerlinPhaseActivated(chainTime) - if !isBerlinPhase && statesBit&as.AddressStateBerlinPhaseBits != 0 || - isBerlinPhase && tx.State == as.AddressStateBitConsortium { // Berlin phase moved consortium handling to admin proposals - return errAddrStateNotPermitted - } - - // Verify that roles are allowed to modify tx.State - if !verifyAccess(roles, statesBit) { - return fmt.Errorf("%w (addr: %s, bit: %b)", errAddrStateNotPermitted, tx.Address, tx.State) - } - // Get the current state - states, err := e.State.GetAddressStates(tx.Address) - if err != nil { - return err - } - // Calculate new states - newStates := states - if tx.Remove && (states&statesBit) != 0 { - newStates ^= statesBit - } else if !tx.Remove { - newStates |= statesBit + // Verify that executor roles are allowed to modify tx.State + if !isPermittedToModifyAddrStateBit(roles, txAddressState) { + return fmt.Errorf("%w (addr: %s, bit: %b)", errAddrStateNotPermitted, tx.Address, tx.StateBit) } // Verify the flowcheck @@ -2267,7 +2258,6 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error if err != nil { return err } - if err := e.FlowChecker.VerifySpend( tx, e.State, @@ -2281,9 +2271,9 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error return err } - txID := e.Tx.ID() - - if tx.State == as.AddressStateBitNodeDeferred { + // Special case: if addrStateBit is nodeDeferred, perform + // additional actions that will defer or resume validator + if tx.StateBit == as.AddressStateBitNodeDeferred { nodeShortID, err := e.State.GetShortIDLink(tx.Address, state.ShortLinkKeyRegisterNode) if err != nil { return fmt.Errorf("couldn't get consortium member registered nodeID: %w", err) @@ -2308,23 +2298,37 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error } } + // Get the current state + addrState, err := e.State.GetAddressStates(tx.Address) + if err != nil { + return err + } + // Calculate new state + newAddrState := addrState + if tx.Remove && (addrState&txAddressState) != 0 { + newAddrState ^= txAddressState + } else if !tx.Remove { + newAddrState |= txAddressState + } + // Set the new state if changed + if addrState != newAddrState { + e.State.SetAddressStates(tx.Address, newAddrState) + } + // Consume the UTXOS avax.Consume(e.State, tx.Ins) // Produce the UTXOS - avax.Produce(e.State, txID, tx.Outs) - // Set the new states if changed - if states != newStates { - e.State.SetAddressStates(tx.Address, newStates) - } + avax.Produce(e.State, e.Tx.ID(), tx.Outs) return nil } // [state] must have only one bit set -func verifyAccess(roles, state as.AddressState) bool { +func isPermittedToModifyAddrStateBit(roles, state as.AddressState) bool { + const addressStateKYCAll = as.AddressStateKYCVerified | as.AddressStateKYCExpired switch { case roles.Is(as.AddressStateRoleAdmin): // admin can do anything - case as.AddressStateKYCAll&state != 0 && roles.Is(as.AddressStateRoleKYC): // kyc role can change kyc status + case addressStateKYCAll&state != 0 && roles.Is(as.AddressStateRoleKYCAdmin): // kyc role can change kyc status case state == as.AddressStateOffersCreator && roles.Is(as.AddressStateRoleOffersAdmin): // offers admin can assign offers creator role default: return false diff --git a/vms/platformvm/txs/executor/camino_tx_executor_test.go b/vms/platformvm/txs/executor/camino_tx_executor_test.go index b9aab7945c1d..16f72c64c8f1 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor_test.go +++ b/vms/platformvm/txs/executor/camino_tx_executor_test.go @@ -961,9 +961,9 @@ func TestCaminoLockedInsOrLockedOuts(t *testing.T) { Ins: tt.ins, Outs: tt.outs, }}, - Address: caminoPreFundedKeys[0].PublicKey().Address(), - State: 0, - Remove: false, + Address: caminoPreFundedKeys[0].PublicKey().Address(), + StateBit: 0, + Remove: false, } executor := generateExecutor(addressStateTxLockedTx, env) @@ -1551,372 +1551,705 @@ func TestCaminoRewardValidatorTx(t *testing.T) { require.NoError(t, shutdownCaminoEnvironment(env)) } -// TODO @evlekht rewrite test, removal test cases not setting initial state for target, and by this not actually testing removal -func TestAddAddressStateTxExecutor(t *testing.T) { - var ( - bob = preFundedKeys[0].PublicKey().Address() - alice = preFundedKeys[1].PublicKey().Address() - ) +func TestAddressStateTxExecutor(t *testing.T) { + ctx, _ := defaultCtx(nil) + // ctx := snowtest.Context(t, snowtest.PChainID) + caminoGenesisConf := api.Camino{VerifyNodeSignature: true, LockModeBondDeposit: true} - caminoGenesisConf := api.Camino{ - VerifyNodeSignature: true, - LockModeBondDeposit: true, + otherAddr := ids.ShortID{1} + msigAliasAddr := ids.ShortID{2} + deferredNodeShortID := ids.ShortID{1, 1} + deferredNodeID := ids.NodeID(deferredNodeShortID) + deferredStaker := &state.Staker{TxID: ids.ID{1, 1, 1}} + msigAliasOwner := secp256k1fx.OutputOwners{Threshold: 1, Addrs: []ids.ShortID{msigAliasAddr}} + + feeOwnerKey, feeOwnerAddr, feeOwner := generateKeyAndOwner(t) + executorKey, executorAddr, executorOwner := generateKeyAndOwner(t) + + feeUTXO := generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, defaultTxFee, feeOwner, ids.Empty, ids.Empty) + halfFeeUTXO1 := generateTestUTXO(ids.ID{2}, ctx.AVAXAssetID, defaultTxFee/2, feeOwner, ids.Empty, ids.Empty) + halfFeeUTXO2 := generateTestUTXO(ids.ID{3}, ctx.AVAXAssetID, defaultTxFee-defaultTxFee/2, executorOwner, ids.Empty, ids.Empty) + msigFeeUTXO := generateTestUTXO(ids.ID{4}, ctx.AVAXAssetID, defaultTxFee, msigAliasOwner, ids.Empty, ids.Empty) + + baseTx := txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Ins: []*avax.TransferableInput{generateTestInFromUTXO(feeUTXO, []uint32{0})}, + }} + + type testData struct { + selfModify bool + txStateBit as.AddressStateBit + remove bool + currentTargetAddrState as.AddressState + executorAddrState as.AddressState } - env := newCaminoEnvironment( /*postBanff*/ true, false, caminoGenesisConf) - env.ctx.Lock.Lock() - defer func() { - require.NoError(t, shutdownCaminoEnvironment(env)) - }() + type testCase struct { + state func(*testing.T, *gomock.Controller, *txs.AddressStateTx, ids.ID, *config.Config) *state.MockDiff + utx *txs.AddressStateTx + signers [][]*secp256k1.PrivateKey + expectedErr error + } - utxos, err := avax.GetAllUTXOs(env.state, set.Set[ids.ShortID]{ - caminoPreFundedKeys[0].Address(): struct{}{}, - }) - require.NoError(t, err) + testCases := map[string]testCase{} - var unlockedUTXO *avax.UTXO - for _, utxo := range utxos { - if _, ok := utxo.Out.(*locked.Out); !ok { - unlockedUTXO = utxo - break + type testCaseFunc func(t *testing.T, tt testData, testCaseName string, phase phase) + type testCaseSimpleFunc func(t *testing.T, phase phase) + type testCaseFuncWithErr func(t *testing.T, tt testData, testCaseName string, expectedErr error, phase phase) + + var failCaseSimpleNoOp testCaseSimpleFunc = func(t *testing.T, phase phase) {} + + testCaseFailMultisigAlias := map[codec.UpgradeVersionID]testCaseSimpleFunc{} + testCaseFailMultisigAlias[codec.UpgradeVersion0] = func(t *testing.T, phase phase) { + testCaseName := fmt.Sprintf("%d_%s/Upgrade 0/Fail with multisig alias", phase, phaseName(t, phase)) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Zero(t, utx.UpgradeVersionID.Version()) + + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + + // not getting addr state for msigAlias addr + s.EXPECT().GetAddressStates(feeOwnerAddr).Return(as.AddressStateEmpty, nil) + s.EXPECT().GetAddressStates(executorAddr).Return(as.AddressStateEmpty, nil) + // fails to verify permission to change target + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Ins: []*avax.TransferableInput{generateTestInFromUTXO(msigFeeUTXO, []uint32{0})}, + }}, + Address: otherAddr, + StateBit: as.AddressStateBitKYCVerified, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey, executorKey}, + }, + expectedErr: errAddrStateNotPermitted, + } + } + testCaseFailMultisigAlias[codec.UpgradeVersion1] = failCaseSimpleNoOp + + testCaseFailUpgradeVersionForbidden := map[codec.UpgradeVersionID]testCaseSimpleFunc{} + testCaseFailUpgradeVersionForbidden[codec.UpgradeVersion0] = func(t *testing.T, phase phase) { + require.GreaterOrEqual(t, phase, berlinPhase) + testCaseName := fmt.Sprintf("%d_%s/Upgrade 0/Upgrade version is forbidden", phase, phaseName(t, phase)) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Zero(t, utx.UpgradeVersionID.Version()) + require.GreaterOrEqual(t, phase, berlinPhase) + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: baseTx, + Address: otherAddr, + StateBit: as.AddressStateBitKYCVerified, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, + }, + expectedErr: errBerlinPhase, + } + } + testCaseFailUpgradeVersionForbidden[codec.UpgradeVersion1] = func(t *testing.T, phase phase) { + require.Equal(t, sunrisePhase, phase) + testCaseName := fmt.Sprintf("%d_%s/Upgrade 1/Upgrade version is forbidden", phase, phaseName(t, phase)) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Greater(t, utx.UpgradeVersionID.Version(), uint16(0)) + require.Equal(t, phase, sunrisePhase) + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: otherAddr, + StateBit: as.AddressStateBitKYCVerified, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + expectedErr: errNotAthensPhase, } } - require.NotNil(t, unlockedUTXO) - out, ok := utxos[0].Out.(avax.TransferableOut) - require.True(t, ok) - unlockedUTXOAmount := out.Amount() + testCaseFailWrongExecutorCredential := map[codec.UpgradeVersionID]testCaseSimpleFunc{} + testCaseFailWrongExecutorCredential[codec.UpgradeVersion0] = failCaseSimpleNoOp + testCaseFailWrongExecutorCredential[codec.UpgradeVersion1] = func(t *testing.T, phase phase) { + if phase < athensPhase { + return + } + testCaseName := fmt.Sprintf("%d_%s/Upgrade 1/Wrong executor credential", phase, phaseName(t, phase)) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.Executor}, nil) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: otherAddr, + StateBit: as.AddressStateBitKYCVerified, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {feeOwnerKey}, + }, + expectedErr: errSignatureMissing, + } + } - signers := [][]*secp256k1.PrivateKey{ - {preFundedKeys[0]}, + testCaseFailNoPermission := map[codec.UpgradeVersionID]testCaseFunc{} + testCaseFailNoPermission[codec.UpgradeVersion0] = func( + t *testing.T, + tt testData, + testCaseName string, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 0/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + s.EXPECT().GetAddressStates(feeOwnerAddr).Return(tt.executorAddrState, nil) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: baseTx, + Address: otherAddr, + StateBit: tt.txStateBit, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, + }, + expectedErr: errAddrStateNotPermitted, + } + } + testCaseFailNoPermission[codec.UpgradeVersion1] = func( + t *testing.T, + tt testData, + testCaseName string, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 1/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.Executor}, nil) + s.EXPECT().GetAddressStates(executorAddr).Return(tt.executorAddrState, nil) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: otherAddr, + StateBit: tt.txStateBit, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + expectedErr: errAddrStateNotPermitted, + } } - outputOwners := secp256k1fx.OutputOwners{ - Locktime: 0, - Threshold: 1, - Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, + testCaseFailBitForbidden := map[codec.UpgradeVersionID]testCaseFuncWithErr{} + testCaseFailBitForbidden[codec.UpgradeVersion0] = func( + t *testing.T, + tt testData, + testCaseName string, + expectedErr error, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 0/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: baseTx, + Address: otherAddr, + StateBit: tt.txStateBit, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, + }, + expectedErr: expectedErr, + } + } + testCaseFailBitForbidden[codec.UpgradeVersion1] = func( + t *testing.T, + tt testData, + testCaseName string, + expectedErr error, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 1/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, fmt.Sprintf("duplicate test-case name: %s", testCaseName)) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: otherAddr, + StateBit: tt.txStateBit, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + expectedErr: expectedErr, + } } - sigIndices := []uint32{0} - tests := map[string]struct { - UpgradeVersion uint16 - stateAddress ids.ShortID - targetAddress ids.ShortID - txFlag as.AddressStateBit - existingState as.AddressState - expectedErrs []error - expectedState as.AddressState - remove bool - executor ids.ShortID - executorAuth *secp256k1fx.Input - }{ - // Bob has Admin role, and he is trying to give himself Admin role (again) - "State: Admin, Flag: Admin role, Add, Same Address": { - stateAddress: bob, - targetAddress: bob, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateRoleAdmin, - remove: false, - }, - // Bob has KYC role, and he is trying to give himself KYC role (again) - "State: KYC, Flag: KYC role, Add, Same Address": { - stateAddress: bob, - targetAddress: bob, - txFlag: as.AddressStateBitRoleKYC, - existingState: as.AddressStateRoleKYC, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - remove: false, - }, - // Bob has KYC role, and he is trying to give himself Admin role - "State: KYC, Flag: Admin role, Add, Same Address": { - stateAddress: bob, - targetAddress: bob, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleKYC, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - remove: false, - }, - // Bob has Admin role, and he is trying to give Alice Admin role - "State: Admin, Flag: Admin role, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateRoleAdmin, - remove: false, - }, - // Bob has Admin role, and he is trying to remove from Alice the Admin role - "State: Admin, Flag: Admin role, Remove, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedState: 0, - remove: true, - }, - // Bob has Admin role, and he is trying to give Alice KYC Role - "State: Admin, Flag: KYC role, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleKYC, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateRoleKYC, - remove: false, - }, - // Bob has Admin role, and he is trying to remove from Alice the KYC role - "State: Admin, Flag: KYC role, Remove, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleKYC, - existingState: as.AddressStateRoleAdmin, - expectedState: 0, - remove: true, - }, - // Bob has Admin role, and he is trying to remove it from himself - "State: Admin, Flag: Admin role, Remove, Same Address": { - stateAddress: bob, - targetAddress: bob, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedState: 0, - expectedErrs: []error{errAdminCannotBeDeleted, errAdminCannotBeDeleted, errAdminCannotBeDeleted}, - remove: true, - }, - // Bob has Admin role, and he is trying to give Alice the KYC Verified state - "State: Admin, Flag: KYC Verified, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCVerified, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateKYCVerified, - remove: false, - }, - // Bob has Admin role, and he is trying to give Alice the KYC Expired state - "State: Admin, Flag: KYC Expired, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCExpired, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateKYCExpired, - remove: false, - }, - // Bob has Admin role, and he is trying to give Alice the Consortium state - "State: Admin, Flag: Consortium, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitConsortium, - existingState: as.AddressStateRoleAdmin, - expectedState: as.AddressStateConsortiumMember, - remove: false, - expectedErrs: []error{nil, nil, errAddrStateNotPermitted}, - }, - // Bob has KYC role, and he is trying to give Alice KYC Expired state - "State: KYC, Flag: KYC Expired, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCExpired, - existingState: as.AddressStateRoleKYC, - expectedState: as.AddressStateKYCExpired, - remove: false, - }, - // Bob has KYC role, and he is trying to give Alice KYC Expired state - "State: KYC, Flag: KYC Verified, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCVerified, - existingState: as.AddressStateRoleKYC, - expectedState: as.AddressStateKYCVerified, - remove: false, - }, - // Some Address has Admin role, and he is trying to give Alice Admin role - "Wrong address": { - stateAddress: ids.GenerateTestShortID(), - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - remove: false, - }, - // An Empty Address has Admin role, and he is trying to give Alice Admin role - "Empty State Address": { - stateAddress: ids.ShortEmpty, - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - remove: false, - }, - // Bob has Admin role, and he is trying to give Admin role to an Empty Address - "Empty Target Address": { - stateAddress: bob, - targetAddress: ids.ShortEmpty, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{txs.ErrEmptyAddress, txs.ErrEmptyAddress, txs.ErrEmptyAddress}, - remove: false, - }, - // Bob has empty addr state, and he is trying to give Alice Admin role - "State: none, Flag: Admin role, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateEmpty, - remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has empty addr state, and he is trying to remove Admin role from Alice - "State: none, Flag: Admin role, Remove, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleAdmin, - existingState: as.AddressStateEmpty, - remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has empty addr state, and he is trying to give Alice KYC role - "State: none, Flag: KYC role, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleKYC, - existingState: as.AddressStateEmpty, - remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has empty addr state, and he is trying to remove KYC role from Alice - "State: none, Flag: KYC role, Remove, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitRoleKYC, - existingState: as.AddressStateEmpty, - remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has empty addr state, and he is trying to give Alice KYC Verified state - "State: none, Flag: KYC Verified, Add, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCVerified, - existingState: as.AddressStateEmpty, - remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has empty addr state, and he is trying to remove KYC Verified state from Alice - "State: none, Flag: KYC Verified, Remove, Different Address": { - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCVerified, - existingState: as.AddressStateEmpty, - remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, - }, - // Bob has KYC role, and he is trying to give Alice KYC Expired state - "Upgrade: 1, State: KYC, Flag: KYC Expired, Add, Different Address": { - UpgradeVersion: 1, - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCExpired, - existingState: as.AddressStateRoleKYC, - expectedState: as.AddressStateKYCExpired, - expectedErrs: []error{errNotAthensPhase, nil}, - remove: false, - executor: bob, - executorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, - }, - // Bob has KYC role, alice tries to executor her KYC Expired state - "Upgrade: 1, State: KYC, Flag: KYC Expired, wrong executor": { - UpgradeVersion: 1, - stateAddress: bob, - targetAddress: alice, - txFlag: as.AddressStateBitKYCExpired, - existingState: as.AddressStateRoleKYC, - expectedState: as.AddressStateKYCExpired, - expectedErrs: []error{errNotAthensPhase, errSignatureMissing, errSignatureMissing}, - remove: false, - executor: alice, - executorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, - }, + testCaseFailAdminSelfRemove := map[codec.UpgradeVersionID]testCaseSimpleFunc{} + testCaseFailAdminSelfRemove[codec.UpgradeVersion0] = func(t *testing.T, phase phase) { + testCaseName := fmt.Sprintf("%d_%s/Upgrade 0/AddressStateRoleAdmin (%d) self-remove", phase, phaseName(t, phase), as.AddressStateBitRoleAdmin) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: baseTx, + Address: feeOwnerAddr, + StateBit: as.AddressStateBitRoleAdmin, + Remove: true, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, + }, + expectedErr: errAdminCannotBeDeleted, + } + } + testCaseFailAdminSelfRemove[codec.UpgradeVersion1] = func(t *testing.T, phase phase) { + testCaseName := fmt.Sprintf("%d_%s/Upgrade 1/AddressStateRoleAdmin (%d) self-remove", phase, phaseName(t, phase), as.AddressStateBitRoleAdmin) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.Executor}, nil) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: executorAddr, + StateBit: as.AddressStateBitRoleAdmin, + Remove: true, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + expectedErr: errAdminCannotBeDeleted, + } } - baseTx := txs.BaseTx{BaseTx: avax.BaseTx{ - NetworkID: env.ctx.NetworkID, - BlockchainID: env.ctx.ChainID, - Ins: []*avax.TransferableInput{ - generateTestInFromUTXO(unlockedUTXO, sigIndices), - }, - Outs: []*avax.TransferableOutput{ - generateTestOut(avaxAssetID, unlockedUTXOAmount-defaultTxFee, outputOwners, ids.Empty, ids.Empty), - }, - }} + testCaseOK := map[codec.UpgradeVersionID]testCaseFunc{} + testCaseOK[codec.UpgradeVersion0] = func( + t *testing.T, + tt testData, + testCaseName string, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 0/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, testCaseName) + targetAddr := otherAddr + if tt.selfModify { + targetAddr = feeOwnerAddr + } + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Zero(t, utx.UpgradeVersionID.Version()) + if feeOwnerAddr == utx.Address { + require.Equal(t, tt.currentTargetAddrState, as.AddressStateRoleAdmin) + } + + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) - chainTime := env.state.GetTimestamp() + s.EXPECT().GetAddressStates(feeOwnerAddr).Return(tt.executorAddrState, nil) - fnSetPhaseTimes := []func() string{ - func() string { - env.config.AthensPhaseTime = chainTime.Add(1 * time.Second) // not yet active - env.config.BerlinPhaseTime = chainTime.Add(2 * time.Second) // not yet active - return "Sunrise" - }, - func() string { - env.config.AthensPhaseTime = chainTime // active - env.config.BerlinPhaseTime = chainTime.Add(1 * time.Second) // not yet active - return "Athens" - }, - func() string { - env.config.AthensPhaseTime = chainTime.Add(-1 * time.Second) // active - env.config.BerlinPhaseTime = chainTime // active - return "Berlin" - }, - } + s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) + expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - for phase := 0; phase < 3; phase++ { - phaseName := fnSetPhaseTimes[phase]() - for name, tt := range tests { - t.Run(fmt.Sprintf("%s; %s", phaseName, name), func(t *testing.T) { - addressStateTx := &txs.AddressStateTx{ - UpgradeVersionID: codec.BuildUpgradeVersionID(tt.UpgradeVersion), - BaseTx: baseTx, - Address: tt.targetAddress, - State: tt.txFlag, - Remove: tt.remove, - Executor: tt.executor, - ExecutorAuth: tt.executorAuth, + if utx.StateBit == as.AddressStateBitNodeDeferred { + s.EXPECT().GetShortIDLink(utx.Address, state.ShortLinkKeyRegisterNode).Return(deferredNodeShortID, nil) + if utx.Remove { + s.EXPECT().GetDeferredValidator(constants.PrimaryNetworkID, deferredNodeID).Return(deferredStaker, nil) + s.EXPECT().DeleteDeferredValidator(deferredStaker) + s.EXPECT().PutCurrentValidator(deferredStaker) + } else { + s.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, deferredNodeID).Return(deferredStaker, nil) + s.EXPECT().DeleteCurrentValidator(deferredStaker) + s.EXPECT().PutDeferredValidator(deferredStaker) + } } - tx, err := txs.NewSigned(addressStateTx, txs.Codec, signers) - require.NoError(t, err) + s.EXPECT().GetAddressStates(utx.Address).Return(tt.currentTargetAddrState, nil) + txAddrState := utx.StateBit.ToAddressState() + newTargetAddrState := tt.currentTargetAddrState + if utx.Remove && (tt.currentTargetAddrState&txAddrState) != 0 { + newTargetAddrState ^= txAddrState + } else if !utx.Remove { + newTargetAddrState |= txAddrState + } + if tt.currentTargetAddrState != newTargetAddrState { + s.EXPECT().SetAddressStates(utx.Address, newTargetAddrState) + } - if tt.UpgradeVersion > 0 { - tx.Creds = append(tx.Creds, tx.Creds[0]) + expectConsumeUTXOs(t, s, utx.Ins) + expectProduceUTXOs(t, s, utx.Outs, txID, 0) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: baseTx, + Address: targetAddr, + StateBit: tt.txStateBit, + Remove: tt.remove, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, + }, + } + } + testCaseOK[codec.UpgradeVersion1] = func( + t *testing.T, + tt testData, + testCaseName string, + phase phase, + ) { + testCaseName = fmt.Sprintf("%d_%s/Upgrade 1/%s", phase, phaseName(t, phase), testCaseName) + require.NotContains(t, testCases, testCaseName, testCaseName) + targetAddr := otherAddr + if tt.selfModify { + targetAddr = executorAddr + } + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Greater(t, utx.UpgradeVersionID.Version(), uint16(0)) + require.Greater(t, phase, 0) + if utx.Executor == utx.Address { + require.Equal(t, as.AddressStateRoleAdmin, tt.currentTargetAddrState) } - onAcceptState, err := state.NewCaminoDiff(lastAcceptedID, env) - require.NoError(t, err) + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) - executor := CaminoStandardTxExecutor{ - StandardTxExecutor{ - Backend: &env.backend, - State: onAcceptState, - Tx: tx, - }, - } + expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.Executor}, nil) + s.EXPECT().GetAddressStates(utx.Executor).Return(tt.executorAddrState, nil) - executor.State.SetAddressStates(tt.stateAddress, tt.existingState) + s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) + expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - err = addressStateTx.Visit(&executor) - if len(tt.expectedErrs) > phase { - require.ErrorIs(t, err, tt.expectedErrs[phase]) - } else { - require.NoError(t, err) + if utx.StateBit == as.AddressStateBitNodeDeferred { + s.EXPECT().GetShortIDLink(utx.Address, state.ShortLinkKeyRegisterNode).Return(deferredNodeShortID, nil) + if utx.Remove { + s.EXPECT().GetDeferredValidator(constants.PrimaryNetworkID, deferredNodeID).Return(deferredStaker, nil) + s.EXPECT().DeleteDeferredValidator(deferredStaker) + s.EXPECT().PutCurrentValidator(deferredStaker) + } else { + s.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, deferredNodeID).Return(deferredStaker, nil) + s.EXPECT().DeleteCurrentValidator(deferredStaker) + s.EXPECT().PutDeferredValidator(deferredStaker) + } } - if err == nil { - targetStates, _ := executor.State.GetAddressStates(tt.targetAddress) - require.Equal(t, targetStates, tt.expectedState) + s.EXPECT().GetAddressStates(utx.Address).Return(tt.currentTargetAddrState, nil) + txAddrState := utx.StateBit.ToAddressState() + newTargetAddrState := tt.currentTargetAddrState + if utx.Remove && (tt.currentTargetAddrState&txAddrState) != 0 { + newTargetAddrState ^= txAddrState + } else if !utx.Remove { + newTargetAddrState |= txAddrState } - }) + if tt.currentTargetAddrState != newTargetAddrState { + s.EXPECT().SetAddressStates(utx.Address, newTargetAddrState) + } + + expectConsumeUTXOs(t, s, utx.Ins) + expectProduceUTXOs(t, s, utx.Outs, txID, 0) + return s + }, + utx: &txs.AddressStateTx{ + UpgradeVersionID: codec.UpgradeVersion1, + BaseTx: baseTx, + Address: targetAddr, + StateBit: tt.txStateBit, + Remove: tt.remove, + Executor: executorAddr, + ExecutorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + } + } + + testCaseOKMultiInput := map[codec.UpgradeVersionID]testCaseSimpleFunc{} + testCaseOKMultiInput[codec.UpgradeVersion0] = func(t *testing.T, phase phase) { + testCaseName := fmt.Sprintf("%d_%s/Upgrade 0/OK: Second input owner has admin role", phase, phaseName(t, phase)) + require.NotContains(t, testCases, testCaseName, testCaseName) + testCases[testCaseName] = testCase{ + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddressStateTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + require.Zero(t, utx.UpgradeVersionID.Version()) + + s := state.NewMockDiff(c) + s.EXPECT().GetTimestamp().Return(phaseTime(t, phase, cfg)) + + // not getting addr state for msigAlias addr + s.EXPECT().GetAddressStates(feeOwnerAddr).Return(as.AddressStateEmpty, nil) + s.EXPECT().GetAddressStates(executorAddr).Return(as.AddressStateRoleAdmin, nil) + + s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) + expectVerifyLock(t, s, utx.Ins, + []*avax.UTXO{halfFeeUTXO1, halfFeeUTXO2}, + []ids.ShortID{feeOwnerAddr, executorAddr}, nil) + + s.EXPECT().GetAddressStates(utx.Address).Return(as.AddressStateEmpty, nil) + s.EXPECT().SetAddressStates(utx.Address, as.AddressStateKYCVerified) + + expectConsumeUTXOs(t, s, utx.Ins) + expectProduceUTXOs(t, s, utx.Outs, txID, 0) + return s + }, + utx: &txs.AddressStateTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: ctx.NetworkID, + BlockchainID: ctx.ChainID, + Ins: []*avax.TransferableInput{ + generateTestInFromUTXO(halfFeeUTXO1, []uint32{0}), + generateTestInFromUTXO(halfFeeUTXO2, []uint32{0}), + }, + }}, + Address: otherAddr, + StateBit: as.AddressStateBitKYCVerified, + ExecutorAuth: &secp256k1fx.Input{}, + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {executorKey}, + }, + } + } + testCaseOKMultiInput[codec.UpgradeVersion1] = failCaseSimpleNoOp + + simpleOKCases := map[string]testData{ + "OK: remove other admin": { + txStateBit: as.AddressStateBitRoleAdmin, + remove: true, + currentTargetAddrState: as.AddressStateRoleAdmin, + executorAddrState: as.AddressStateRoleAdmin, + }, + "OK: target state is not empty": { + txStateBit: as.AddressStateBitKYCVerified, + currentTargetAddrState: as.AddressStateConsortium, + executorAddrState: as.AddressStateRoleAdmin, + }, + "OK: not modified": { + txStateBit: as.AddressStateBitKYCVerified, + currentTargetAddrState: as.AddressStateKYCVerified, + executorAddrState: as.AddressStateRoleAdmin, + }, + "OK: modifying executors own address state": { + selfModify: true, + txStateBit: as.AddressStateBitKYCVerified, + currentTargetAddrState: as.AddressStateRoleAdmin, + executorAddrState: as.AddressStateRoleAdmin, + }, + "OK: removal": { + txStateBit: as.AddressStateBitKYCVerified, + remove: true, + currentTargetAddrState: as.AddressStateKYCVerified, + executorAddrState: as.AddressStateRoleAdmin, + }, + "OK: removal, not modified": { + txStateBit: as.AddressStateBitKYCVerified, + remove: true, + currentTargetAddrState: as.AddressStateConsortium, + executorAddrState: as.AddressStateRoleAdmin, + }, + } + + validBits := getBitsFromAddressState(as.AddressStateValidBits) + + // set role-bit permissions + permissionsMatrix := map[as.AddressStateBit]map[as.AddressStateBit]bool{} + for _, role := range validBits { + permissionsMatrix[role] = map[as.AddressStateBit]bool{} + for _, bit := range validBits { + permissionsMatrix[role][bit] = false + } + } + for _, bit := range validBits { + permissionsMatrix[as.AddressStateBitRoleAdmin][bit] = true + } + permissionsMatrix[as.AddressStateBitRoleKYCAdmin][as.AddressStateBitKYCVerified] = true + permissionsMatrix[as.AddressStateBitRoleKYCAdmin][as.AddressStateBitKYCExpired] = true + permissionsMatrix[as.AddressStateBitRoleOffersAdmin][as.AddressStateBitOffersCreator] = true + + // set phase-bit restrictions + bitsPhaseMatrix := map[as.AddressStateBit]map[phase]error{} + for _, bit := range validBits { + bitsPhaseMatrix[bit] = map[phase]error{} + for phase := firstPhase; phase <= lastPhase; phase++ { + bitsPhaseMatrix[bit][phase] = nil + } + } + // sunriseBits := getBitsFromAddressState(as.AddressStateSunrisePhaseBits) + athensBits := getBitsFromAddressState(as.AddressStateAthensPhaseBits) + berlinBits := getBitsFromAddressState(as.AddressStateBerlinPhaseBits) + for _, bit := range athensBits { + for phase := firstPhase; phase < athensPhase; phase++ { + bitsPhaseMatrix[bit][phase] = errNotAthensPhase + } + } + for _, bit := range berlinBits { + for phase := firstPhase; phase < berlinPhase; phase++ { + bitsPhaseMatrix[bit][phase] = errNotBerlinPhase + } + } + bitsPhaseMatrix[as.AddressStateBitConsortium][berlinPhase] = errBerlinPhase + + // set phase-txUpgrade restrictions := getBitsFromAddressState(as.AddressStateSunrisePhaseBits) + txUpgradeMatrix := map[phase][]codec.UpgradeVersionID{} + for phase := firstPhase; phase < berlinPhase; phase++ { + txUpgradeMatrix[phase] = append(txUpgradeMatrix[phase], codec.UpgradeVersion0) + } + for phase := athensPhase; phase <= lastPhase; phase++ { + txUpgradeMatrix[phase] = append(txUpgradeMatrix[phase], codec.UpgradeVersion1) + } + + for role, permissions := range permissionsMatrix { + for bit, allowed := range permissions { + if allowed { + for phase := firstPhase; phase <= lastPhase; phase++ { + if bitsPhaseMatrix[bit][phase] != nil { + continue + } + txUpgrades := txUpgradeMatrix[phase] + for _, txUpgrade := range txUpgrades { + if bit == as.AddressStateBitConsortium { + // resume node, defer is tested below + testCaseOK[txUpgrade](t, testData{ + txStateBit: bit, + executorAddrState: role.ToAddressState(), + remove: true, + }, fmt.Sprintf("OK: (%0d) modifies (%0d), resume node", role, bit), phase) + } + testCaseOK[txUpgrade](t, testData{ + txStateBit: bit, + executorAddrState: role.ToAddressState(), + }, fmt.Sprintf("OK: (%0d) modifies (%0d)", role, bit), phase) + } + } + } else { + for phase := firstPhase; phase <= lastPhase; phase++ { + if bitsPhaseMatrix[bit][phase] != nil { + continue + } + txUpgrades := txUpgradeMatrix[phase] + for _, txUpgrade := range txUpgrades { + testCaseFailNoPermission[txUpgrade](t, testData{ + txStateBit: bit, + executorAddrState: role.ToAddressState(), + }, fmt.Sprintf("Fail: (%0d) modifies (%0d)", role, bit), phase) + } + } + } + } + } + + for _, bit := range validBits { + for phase := firstPhase; phase <= lastPhase; phase++ { + expectedErr := bitsPhaseMatrix[bit][phase] + if expectedErr == nil { + continue + } + txUpgrades := txUpgradeMatrix[phase] + for _, txUpgrade := range txUpgrades { + testCaseFailBitForbidden[txUpgrade](t, testData{txStateBit: bit}, + fmt.Sprintf("Forbid bit %d", bit), expectedErr, phase) + } + } + } + + for phase := firstPhase; phase <= lastPhase; phase++ { + txUpgrades := txUpgradeMatrix[phase] + for _, txUpgrade := range txUpgrades { + for name, tt := range simpleOKCases { + testCaseOK[txUpgrade](t, tt, name, phase) + } + testCaseOKMultiInput[txUpgrade](t, phase) // noop for upgr1 + testCaseFailAdminSelfRemove[txUpgrade](t, phase) + testCaseFailWrongExecutorCredential[txUpgrade](t, phase) // noop for upgr0 or sunrise phase + testCaseFailMultisigAlias[txUpgrade](t, phase) // noop for upgr1 } } + + testCaseFailUpgradeVersionForbidden[codec.UpgradeVersion1](t, sunrisePhase) + for phase := berlinPhase; phase <= lastPhase; phase++ { + testCaseFailUpgradeVersionForbidden[codec.UpgradeVersion0](t, phase) + } + + for name, tt := range testCases { + t.Run(name, func(t *testing.T) { + env := newCaminoEnvironmentWithMocks(caminoGenesisConf, nil) + defer func() { require.NoError(t, shutdownCaminoEnvironment(env)) }() + + avax.SortTransferableInputsWithSigners(tt.utx.Ins, tt.signers) + avax.SortTransferableOutputs(tt.utx.Outs, txs.Codec) + tx, err := txs.NewSigned(tt.utx, txs.Codec, tt.signers) + require.NoError(t, err) + + err = tx.Unsigned.Visit(&CaminoStandardTxExecutor{ + StandardTxExecutor{ + Backend: &env.backend, + State: tt.state(t, gomock.NewController(t), tt.utx, tx.ID(), env.config), + Tx: tx, + }, + }) + require.ErrorIs(t, err, tt.expectedErr) + }) + } } func TestCaminoStandardTxExecutorDepositTx(t *testing.T) { @@ -4334,7 +4667,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "Consortium member has already registered node": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr2, nil) return s @@ -4356,7 +4689,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "Old node is in current validator's set": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4382,7 +4715,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "Old node is in pending validator's set": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4410,7 +4743,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "Old node is in deferred validator's set": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4440,7 +4773,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "OK: change registered node": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4486,7 +4819,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "OK: consortium member is msig alias": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(ids.ShortEmpty, database.ErrNotFound) s.EXPECT().GetShortIDLink(ids.ShortID(utx.NewNodeID), state.ShortLinkKeyRegisterNode). @@ -4533,7 +4866,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "OK": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(ids.ShortEmpty, database.ErrNotFound) s.EXPECT().GetShortIDLink(ids.ShortID(utx.NewNodeID), state.ShortLinkKeyRegisterNode). @@ -4895,177 +5228,6 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) { } } -func TestCaminoStandardTxExecutorAddressStateTxSuspendValidator(t *testing.T) { - // finding first staker to remove - env := newCaminoEnvironment( /*postBanff*/ true, false, api.Camino{LockModeBondDeposit: true}) - env.ctx.Lock.Lock() - stakerIterator, err := env.state.GetCurrentStakerIterator() - require.NoError(t, err) - require.True(t, stakerIterator.Next()) - stakerToRemove := stakerIterator.Value() - stakerIterator.Release() - nodeID := stakerToRemove.NodeID - consortiumMemberAddress, err := env.state.GetShortIDLink(ids.ShortID(nodeID), state.ShortLinkKeyRegisterNode) - require.NoError(t, err) - require.NoError(t, shutdownCaminoEnvironment(env)) - - initAdmin := caminoPreFundedKeys[0] - // other common data - - outputOwners := &secp256k1fx.OutputOwners{ - Locktime: 0, - Threshold: 1, - Addrs: []ids.ShortID{consortiumMemberAddress}, - } - - type args struct { - address ids.ShortID - remove bool - keys []*secp256k1.PrivateKey - changeAddr *secp256k1fx.OutputOwners - } - tests := map[string]struct { - generateArgs func() args - preExecute func(*testing.T, *txs.Tx, state.State) - expectedErr error - assert func(*testing.T) - }{ - "Set state to deferred from address with no roles -> ErrAddrStateNotPermitted": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - keys: []*secp256k1.PrivateKey{caminoPreFundedKeys[1]}, // non-admin address - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) {}, - expectedErr: errAddrStateNotPermitted, - }, - "Set state to deferred from kyc address -> ErrAddrStateNotPermitted": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - keys: []*secp256k1.PrivateKey{caminoPreFundedKeys[1]}, - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) { - state.SetAddressStates(caminoPreFundedKeys[1].Address(), as.AddressStateRoleKYC) - }, - expectedErr: errAddrStateNotPermitted, - }, - "Happy path set state to deferred": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - keys: []*secp256k1.PrivateKey{initAdmin}, - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) {}, - expectedErr: nil, - }, - "Remove deferred state from non-admin address -> ErrAddrStateNotPermitted": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - remove: true, - keys: []*secp256k1.PrivateKey{caminoPreFundedKeys[1]}, // non-admin address - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) {}, - expectedErr: errAddrStateNotPermitted, - }, - "Happy path set state to active": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - remove: true, - keys: []*secp256k1.PrivateKey{initAdmin}, - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) { - stakerToTransfer, err := state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) - require.NoError(t, err) - state.DeleteCurrentValidator(stakerToTransfer) - state.PutDeferredValidator(stakerToTransfer) - }, - expectedErr: nil, - }, - "Remove deferred state of an active validator": { - generateArgs: func() args { - return args{ - address: consortiumMemberAddress, - remove: true, - keys: []*secp256k1.PrivateKey{initAdmin}, - changeAddr: outputOwners, - } - }, - preExecute: func(t *testing.T, tx *txs.Tx, state state.State) {}, - expectedErr: errValidatorNotFound, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - caminoGenesisConf := api.Camino{ - VerifyNodeSignature: true, - LockModeBondDeposit: true, - InitialAdmin: initAdmin.Address(), - } - env := newCaminoEnvironment( /*postBanff*/ true, false, caminoGenesisConf) - env.ctx.Lock.Lock() - defer func() { - require.NoError(t, shutdownCaminoEnvironment(env)) - }() - - env.config.BanffTime = env.state.GetTimestamp() - require.NoError(t, env.state.Commit()) - - setAddressStateArgs := tt.generateArgs() - tx, err := env.txBuilder.NewAddressStateTx( - setAddressStateArgs.address, - setAddressStateArgs.remove, - as.AddressStateBitNodeDeferred, - setAddressStateArgs.keys, - setAddressStateArgs.changeAddr, - ) - require.NoError(t, err) - - tt.preExecute(t, tx, env.state) - onAcceptState, err := state.NewDiff(lastAcceptedID, env) - require.NoError(t, err) - - executor := CaminoStandardTxExecutor{ - StandardTxExecutor{ - Backend: &env.backend, - State: onAcceptState, - Tx: tx, - }, - } - - err = tx.Unsigned.Visit(&executor) - require.ErrorIs(t, err, tt.expectedErr) - if tt.expectedErr != nil { - return - } - var stakerIterator state.StakerIterator - if setAddressStateArgs.remove { - stakerIterator, err = onAcceptState.GetCurrentStakerIterator() - require.NoError(t, err) - } else { - stakerIterator, err = onAcceptState.GetDeferredStakerIterator() - require.NoError(t, err) - } - require.True(t, stakerIterator.Next()) - stakerToRemove := stakerIterator.Value() - stakerIterator.Release() - require.Equal(t, stakerToRemove.NodeID, nodeID) - }) - } -} - func TestCaminoStandardTxExecutorExportTxMultisig(t *testing.T) { fakeMSigAlias := preFundedKeys[0].Address() sourceKey := preFundedKeys[1] @@ -6410,7 +6572,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) return s }, @@ -6434,7 +6596,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -6463,7 +6625,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -6492,7 +6654,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -6518,7 +6680,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -6551,7 +6713,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -6585,7 +6747,7 @@ func TestCaminoStandardTxExecutorAddVoteTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(proposal.StartTime()) s.EXPECT().GetProposal(utx.ProposalID).Return(proposal, nil) - s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortium, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) @@ -7594,7 +7756,7 @@ func TestCaminoStandardTxExecutorFinishProposalsTx(t *testing.T) { // * proposalExecutor s.EXPECT().GetAddressStates(earlySuccessfulProposalWithBond.MemberAddress). - Return(as.AddressStateConsortiumMember, nil) + Return(as.AddressStateConsortium, nil) s.EXPECT().SetAddressStates(earlySuccessfulProposalWithBond.MemberAddress, as.AddressStateEmpty) s.EXPECT().GetShortIDLink(earlySuccessfulProposalWithBond.MemberAddress, state.ShortLinkKeyRegisterNode). Return(memberNodeShortID1, nil) @@ -7607,7 +7769,7 @@ func TestCaminoStandardTxExecutorFinishProposalsTx(t *testing.T) { s.EXPECT().DeletePendingValidator(pendingValidator1) s.EXPECT().GetAddressStates(expiredSuccessfulProposalWithBond.MemberAddress). - Return(as.AddressStateConsortiumMember, nil) + Return(as.AddressStateConsortium, nil) s.EXPECT().SetAddressStates(expiredSuccessfulProposalWithBond.MemberAddress, as.AddressStateEmpty) s.EXPECT().GetShortIDLink(expiredSuccessfulProposalWithBond.MemberAddress, state.ShortLinkKeyRegisterNode). Return(memberNodeShortID2, nil) @@ -7673,3 +7835,35 @@ func TestCaminoStandardTxExecutorFinishProposalsTx(t *testing.T) { }) } } + +func getBitsFromAddressState(addrState as.AddressState) []as.AddressStateBit { + bit := as.AddressStateBit(0) + var addrStateBits []as.AddressStateBit + for addrState > 0 { + if addrState&1 == 1 { + addrStateBits = append(addrStateBits, bit) + } + addrState >>= 1 + bit++ + } + return addrStateBits +} + +func TestGetBitsFromAddressState(t *testing.T) { + tests := []struct { + addressState as.AddressState + expectedAddressStateBits []as.AddressStateBit + }{ + {0b00100101, []as.AddressStateBit{0, 2, 5}}, + {0b00000000, nil}, + {0b00000001, []as.AddressStateBit{0}}, + {0b11111111, []as.AddressStateBit{0, 1, 2, 3, 4, 5, 6, 7}}, + {0b10000000, []as.AddressStateBit{7}}, + {0b1111111111, []as.AddressStateBit{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}, + } + + for _, tt := range tests { + addressStateBits := getBitsFromAddressState(tt.addressState) + require.Equal(t, tt.expectedAddressStateBits, addressStateBits) + } +} diff --git a/vms/platformvm/txs/executor/dac/camino_dac.go b/vms/platformvm/txs/executor/dac/camino_dac.go index 525599739f4d..2bbc5049600a 100644 --- a/vms/platformvm/txs/executor/dac/camino_dac.go +++ b/vms/platformvm/txs/executor/dac/camino_dac.go @@ -146,7 +146,7 @@ func (e *proposalVerifier) AddMemberProposal(proposal *dac.AddMemberProposal) er switch { case err != nil: return err - case applicantAddress.Is(as.AddressStateConsortiumMember): + case applicantAddress.Is(as.AddressStateConsortium): return fmt.Errorf("%w (applicant)", errConsortiumMember) case applicantAddress.IsNot(as.AddressStateKYCVerified): return fmt.Errorf("%w (applicant)", errNotKYCVerified) @@ -185,7 +185,7 @@ func (e *proposalExecutor) AddMemberProposal(proposal *dac.AddMemberProposalStat if err != nil { return err } - newAddrState := addrState | as.AddressStateConsortiumMember + newAddrState := addrState | as.AddressStateConsortium if newAddrState == addrState { // c-member was somehow already added // this should never happen, cause adding only done via addMemberProposal @@ -208,7 +208,7 @@ func (e *proposalVerifier) ExcludeMemberProposal(proposal *dac.ExcludeMemberProp switch { case err != nil: return err - case memberAddressState.IsNot(as.AddressStateConsortiumMember): + case memberAddressState.IsNot(as.AddressStateConsortium): return fmt.Errorf("%w (member)", errNotConsortiumMember) } @@ -218,7 +218,7 @@ func (e *proposalVerifier) ExcludeMemberProposal(proposal *dac.ExcludeMemberProp switch { case err != nil: return err - case proposerAddressState.IsNot(as.AddressStateConsortiumMember): + case proposerAddressState.IsNot(as.AddressStateConsortium): return fmt.Errorf("%w (proposer)", errNotConsortiumMember) } @@ -262,7 +262,7 @@ func (e *proposalExecutor) ExcludeMemberProposal(proposal *dac.ExcludeMemberProp if err != nil { return err } - newAddrState := addrState ^ as.AddressStateConsortiumMember + newAddrState := addrState ^ as.AddressStateConsortium if newAddrState == addrState { // c-member was somehow already excluded // this should never happen, cause excluding only done via excludeMemberProposal @@ -341,7 +341,7 @@ func (e *proposalVerifier) GeneralProposal(*dac.GeneralProposal) error { switch { case err != nil: return err - case proposerAddressState.IsNot(as.AddressStateConsortiumMember): + case proposerAddressState.IsNot(as.AddressStateConsortium): return fmt.Errorf("%w (proposer)", errNotConsortiumMember) } diff --git a/vms/platformvm/txs/executor/dac/camino_dac_test.go b/vms/platformvm/txs/executor/dac/camino_dac_test.go index e559522b176c..160bcc1fadae 100644 --- a/vms/platformvm/txs/executor/dac/camino_dac_test.go +++ b/vms/platformvm/txs/executor/dac/camino_dac_test.go @@ -211,7 +211,7 @@ func TestProposalVerifierAddMemberProposal(t *testing.T) { "Applicant address is consortium member": { state: func(c *gomock.Controller, utx *txs.AddProposalTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(applicantAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(applicantAddress).Return(as.AddressStateConsortium, nil) return s }, utx: func() *txs.AddProposalTx { @@ -327,7 +327,7 @@ func TestProposalExecutorAddMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(applicantAddress).Return(applicantAddressState, nil) - s.EXPECT().SetAddressStates(applicantAddress, applicantAddressState|as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(applicantAddress, applicantAddressState|as.AddressStateConsortium) return s }, proposal: &dac.AddMemberProposalState{ @@ -408,7 +408,7 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { "Proposer is not consortium member": { state: func(c *gomock.Controller, utx *txs.AddProposalTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateEmpty, nil) return s }, @@ -428,8 +428,8 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { "Proposer doesn't have registered node": { state: func(c *gomock.Controller, utx *txs.AddProposalTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.ProposerAddress, state.ShortLinkKeyRegisterNode).Return(ids.ShortEmpty, database.ErrNotFound) return s }, @@ -449,8 +449,8 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { "Proposer doesn't have active validator": { state: func(c *gomock.Controller, utx *txs.AddProposalTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.ProposerAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, memberNodeID).Return(nil, database.ErrNotFound) return s @@ -476,8 +476,8 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { proposalsIterator.EXPECT().Value().Return(&dac.ExcludeMemberProposalState{MemberAddress: memberAddress}, nil) proposalsIterator.EXPECT().Release() - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.ProposerAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, memberNodeID).Return(memberValidator, nil) s.EXPECT().GetProposalIterator().Return(proposalsIterator, nil) @@ -504,7 +504,7 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { proposalsIterator.EXPECT().Release() proposalsIterator.EXPECT().Error().Return(nil) - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetProposalIterator().Return(proposalsIterator, nil) return s }, @@ -529,8 +529,8 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { proposalsIterator.EXPECT().Release() proposalsIterator.EXPECT().Error().Return(nil) - s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortiumMember, nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(memberAddress).Return(as.AddressStateConsortium, nil) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateConsortium, nil) s.EXPECT().GetShortIDLink(utx.ProposerAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, memberNodeID).Return(memberValidator, nil) s.EXPECT().GetProposalIterator().Return(proposalsIterator, nil) @@ -569,7 +569,7 @@ func TestProposalVerifierExcludeMemberProposal(t *testing.T) { func TestProposalExecutorExcludeMemberProposal(t *testing.T) { memberAddress := ids.ShortID{1} - memberAddressState := as.AddressStateCaminoProposer | as.AddressStateConsortiumMember // just not only c-member + memberAddressState := as.AddressStateCaminoProposer | as.AddressStateConsortium // just not only c-member memberNodeShortID := ids.ShortID{2} memberNodeID := ids.NodeID(memberNodeShortID) memberValidator := &state.Staker{TxID: ids.ID{3}} @@ -583,7 +583,7 @@ func TestProposalExecutorExcludeMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(memberAddress).Return(memberAddressState, nil) - s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortium) s.EXPECT().GetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().SetShortIDLink(memberNodeShortID, state.ShortLinkKeyRegisterNode, nil) s.EXPECT().SetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode, nil) @@ -605,7 +605,7 @@ func TestProposalExecutorExcludeMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(memberAddress).Return(memberAddressState, nil) - s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortium) s.EXPECT().GetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().SetShortIDLink(memberNodeShortID, state.ShortLinkKeyRegisterNode, nil) s.EXPECT().SetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode, nil) @@ -626,7 +626,7 @@ func TestProposalExecutorExcludeMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(memberAddress).Return(memberAddressState, nil) - s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortium) s.EXPECT().GetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().SetShortIDLink(memberNodeShortID, state.ShortLinkKeyRegisterNode, nil) s.EXPECT().SetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode, nil) @@ -646,7 +646,7 @@ func TestProposalExecutorExcludeMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(memberAddress).Return(memberAddressState, nil) - s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortium) s.EXPECT().GetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode).Return(memberNodeShortID, nil) s.EXPECT().SetShortIDLink(memberNodeShortID, state.ShortLinkKeyRegisterNode, nil) s.EXPECT().SetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode, nil) @@ -665,7 +665,7 @@ func TestProposalExecutorExcludeMemberProposal(t *testing.T) { state: func(c *gomock.Controller) *state.MockDiff { s := state.NewMockDiff(c) s.EXPECT().GetAddressStates(memberAddress).Return(memberAddressState, nil) - s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortiumMember) + s.EXPECT().SetAddressStates(memberAddress, memberAddressState^as.AddressStateConsortium) s.EXPECT().GetShortIDLink(memberAddress, state.ShortLinkKeyRegisterNode).Return(ids.ShortEmpty, database.ErrNotFound) return s },