From f9f99d5e6cee338d1eba433dc6495dec966a8e87 Mon Sep 17 00:00:00 2001 From: evlekht <> Date: Thu, 9 Nov 2023 20:37:44 +0400 Subject: [PATCH 1/3] [PVM, DAC] Small clean-up --- .../dac/camino_add_member_proposal.go | 11 ++++------- ..._proposal.go => camino_base_fee_proposal.go} | 2 +- ...test.go => camino_base_fee_proposal_test.go} | 0 vms/platformvm/dac/camino_proposal.go | 17 ++++++++++++++--- .../txs/executor/camino_tx_executor.go | 6 ------ 5 files changed, 19 insertions(+), 17 deletions(-) rename vms/platformvm/dac/{camino_change_base_fee_proposal.go => camino_base_fee_proposal.go} (97%) rename vms/platformvm/dac/{camino_change_base_fee_proposal_test.go => camino_base_fee_proposal_test.go} (100%) diff --git a/vms/platformvm/dac/camino_add_member_proposal.go b/vms/platformvm/dac/camino_add_member_proposal.go index d0d1d8b2436d..d44747794ce1 100644 --- a/vms/platformvm/dac/camino_add_member_proposal.go +++ b/vms/platformvm/dac/camino_add_member_proposal.go @@ -12,10 +12,7 @@ import ( "golang.org/x/exp/slices" ) -const ( - addMemberProposalDuration = uint64(time.Hour * 24 * 30 * 2 / time.Second) // 2 month - addMemberProposalOptionsCount = 2 -) +const AddMemberProposalDuration = uint64(time.Hour * 24 * 30 * 2 / time.Second) // 2 month var ( _ Proposal = (*AddMemberProposal)(nil) @@ -44,8 +41,8 @@ func (p *AddMemberProposal) Verify() error { switch { case p.Start >= p.End: return errEndNotAfterStart - case p.End-p.Start != addMemberProposalDuration: - return fmt.Errorf("%w (expected: %d, actual: %d)", errWrongDuration, addMemberProposalDuration, p.End-p.Start) + case p.End-p.Start != AddMemberProposalDuration: + return fmt.Errorf("%w (expected: %d, actual: %d)", errWrongDuration, AddMemberProposalDuration, p.End-p.Start) } return nil } @@ -157,7 +154,7 @@ func (p *AddMemberProposalState) AddVote(voterAddress ids.ShortID, voteIntf Vote } // Will return modified proposal with added vote ignoring allowed voters, original proposal will not be modified! -func (p *AddMemberProposalState) ForceAddVote(voterAddress ids.ShortID, voteIntf Vote) (ProposalState, error) { //nolint:revive +func (p *AddMemberProposalState) ForceAddVote(voteIntf Vote) (ProposalState, error) { vote, ok := voteIntf.(*SimpleVote) if !ok { return nil, ErrWrongVote diff --git a/vms/platformvm/dac/camino_change_base_fee_proposal.go b/vms/platformvm/dac/camino_base_fee_proposal.go similarity index 97% rename from vms/platformvm/dac/camino_change_base_fee_proposal.go rename to vms/platformvm/dac/camino_base_fee_proposal.go index dba54fdbe0dc..4589be1a9349 100644 --- a/vms/platformvm/dac/camino_change_base_fee_proposal.go +++ b/vms/platformvm/dac/camino_base_fee_proposal.go @@ -164,7 +164,7 @@ func (p *BaseFeeProposalState) AddVote(voterAddress ids.ShortID, voteIntf Vote) } // Will return modified proposal with added vote ignoring allowed voters, original proposal will not be modified! -func (p *BaseFeeProposalState) ForceAddVote(voterAddress ids.ShortID, voteIntf Vote) (ProposalState, error) { //nolint:revive +func (p *BaseFeeProposalState) ForceAddVote(voteIntf Vote) (ProposalState, error) { vote, ok := voteIntf.(*SimpleVote) if !ok { return nil, ErrWrongVote diff --git a/vms/platformvm/dac/camino_change_base_fee_proposal_test.go b/vms/platformvm/dac/camino_base_fee_proposal_test.go similarity index 100% rename from vms/platformvm/dac/camino_change_base_fee_proposal_test.go rename to vms/platformvm/dac/camino_base_fee_proposal_test.go diff --git a/vms/platformvm/dac/camino_proposal.go b/vms/platformvm/dac/camino_proposal.go index 497787f448a1..2ab8f5e6ee82 100644 --- a/vms/platformvm/dac/camino_proposal.go +++ b/vms/platformvm/dac/camino_proposal.go @@ -33,9 +33,16 @@ type Proposal interface { StartTime() time.Time EndTime() time.Time - GetOptions() any CreateProposalState(allowedVoters []ids.ShortID) ProposalState Visit(VerifierVisitor) error + + // Returns proposal options. (used in magellan) + // + // We want to keep it in caminogo even if its used only in magellan, + // cause it could contain internal proposal logic. + // E.g. Add member proposal doesn't have options, but it hardcode-generates them. + // We don't want to care about that in magellan. + GetOptions() any } type ProposalState interface { @@ -51,7 +58,11 @@ type ProposalState interface { // Will return modified ProposalState with added vote, original ProposalState will not be modified! AddVote(voterAddress ids.ShortID, vote Vote) (ProposalState, error) - // Will return modified proposal with added vote ignoring allowed voters, original proposal will not be modified! + // Returns modified proposal with added vote ignoring allowed voters, original proposal will not be modified! // (used in magellan) - ForceAddVote(voterAddress ids.ShortID, voteIntf Vote) (ProposalState, error) + // + // We want to keep it in caminogo even if its used only in magellan, + // cause it contains internal proposal logic that affects success state. + // We don't want to care about that in magellan. + ForceAddVote(voteIntf Vote) (ProposalState, error) } diff --git a/vms/platformvm/txs/executor/camino_tx_executor.go b/vms/platformvm/txs/executor/camino_tx_executor.go index a4b091718daa..19ad15562677 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor.go +++ b/vms/platformvm/txs/executor/camino_tx_executor.go @@ -1940,8 +1940,6 @@ func (e *CaminoStandardTxExecutor) FinishProposalsTx(tx *txs.FinishProposalsTx) } isExpirationTime := expirationTime.Equal(chainTime) - // TODO@ if chainTime == expirationTime, then all expired proposals must be in tx - switch { case len(e.Tx.Creds) != 0: return errWrongCredentialsNumber @@ -1990,10 +1988,6 @@ func (e *CaminoStandardTxExecutor) FinishProposalsTx(tx *txs.FinishProposalsTx) // processing tx proposal IDs - // TODO@ what if early finished proposal expires at the finishTx block? - // TODO@ meaning that its id will be both in expired and early finished - // TODO@ prevent failing - for _, proposalID := range tx.EarlyFinishedSuccessfulProposalIDs { proposal, err := e.State.GetProposal(proposalID) if err != nil { From 58cbdfa629b2059df590a04436c4fd08bb965b88 Mon Sep 17 00:00:00 2001 From: evlekht <> Date: Tue, 14 Nov 2023 13:36:44 +0400 Subject: [PATCH 2/3] [PVM, DAC] Admin proposals --- genesis/camino_genesis.go | 7 +- vms/platformvm/addrstate/address_state.go | 63 ++++ vms/platformvm/camino_helpers_test.go | 4 +- vms/platformvm/camino_service.go | 9 +- vms/platformvm/camino_vm_test.go | 283 +++++++++++------ .../dac/camino_add_member_proposal.go | 14 + .../dac/camino_add_member_proposal_test.go | 89 ++++++ .../dac/camino_base_fee_proposal.go | 14 + .../dac/camino_base_fee_proposal_test.go | 85 ++++++ vms/platformvm/dac/camino_proposal.go | 12 + vms/platformvm/genesis/camino.go | 5 +- vms/platformvm/state/camino.go | 19 +- vms/platformvm/state/camino_address_state.go | 12 +- .../state/camino_address_state_test.go | 50 +-- vms/platformvm/state/camino_diff.go | 6 +- vms/platformvm/state/camino_diff_test.go | 26 +- vms/platformvm/state/camino_state.go | 6 +- vms/platformvm/state/camino_test.go | 7 +- vms/platformvm/state/mock_chain.go | 7 +- vms/platformvm/state/mock_diff.go | 7 +- vms/platformvm/state/mock_state.go | 7 +- vms/platformvm/txs/builder/camino_builder.go | 5 +- .../txs/builder/camino_builder_test.go | 13 +- vms/platformvm/txs/camino_address_state_tx.go | 62 +--- .../txs/camino_address_state_tx_test.go | 11 +- vms/platformvm/txs/codec.go | 1 + .../txs/executor/camino_advance_time_test.go | 3 +- vms/platformvm/txs/executor/camino_dac.go | 11 +- .../txs/executor/camino_dac_test.go | 17 +- .../txs/executor/camino_tx_executor.go | 111 ++++--- .../txs/executor/camino_tx_executor_test.go | 285 ++++++++++++------ 31 files changed, 861 insertions(+), 390 deletions(-) create mode 100644 vms/platformvm/addrstate/address_state.go diff --git a/genesis/camino_genesis.go b/genesis/camino_genesis.go index 5b72a1b826f4..01b1584e6b8a 100644 --- a/genesis/camino_genesis.go +++ b/genesis/camino_genesis.go @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/avalanchego/vms/avm" "github.com/ava-labs/avalanchego/vms/components/multisig" "github.com/ava-labs/avalanchego/vms/nftfx" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/api" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" @@ -364,12 +365,12 @@ func buildPGenesis(config *Config, hrp string, xGenesisBytes []byte, xGenesisDat // Getting args from allocations for _, allocation := range config.Camino.Allocations { - var addrState pchaintxs.AddressState + var addrState as.AddressState if allocation.AddressStates.ConsortiumMember { - addrState |= pchaintxs.AddressStateConsortiumMember + addrState |= as.AddressStateConsortiumMember } if allocation.AddressStates.KYCVerified { - addrState |= pchaintxs.AddressStateKYCVerified + addrState |= as.AddressStateKYCVerified } if addrState != 0 { platformvmArgs.Camino.AddressStates = append(platformvmArgs.Camino.AddressStates, genesis.AddressState{ diff --git a/vms/platformvm/addrstate/address_state.go b/vms/platformvm/addrstate/address_state.go new file mode 100644 index 000000000000..4f94b9d72700 --- /dev/null +++ b/vms/platformvm/addrstate/address_state.go @@ -0,0 +1,63 @@ +package addrstate + +type ( + AddressState uint64 + AddressStateBit uint8 +) + +// AddressState flags, max 63 +const ( + // Bits + + AddressStateBitRoleAdmin AddressStateBit = 0 // super role + + AddressStateBitRoleKYC 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 + + AddressStateBitKYCVerified AddressStateBit = 32 + AddressStateBitKYCExpired AddressStateBit = 33 + AddressStateBitConsortium AddressStateBit = 38 + AddressStateBitNodeDeferred AddressStateBit = 39 + AddressStateBitOffersCreator AddressStateBit = 50 + AddressStateBitCaminoProposer AddressStateBit = 51 + + AddressStateBitMax AddressStateBit = 63 + + // States + + 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 + + AddressStateKYCVerified AddressState = AddressState(1) << AddressStateBitKYCVerified // 0b0100000000000000000000000000000000 + AddressStateKYCExpired AddressState = AddressState(1) << AddressStateBitKYCExpired // 0b1000000000000000000000000000000000 + AddressStateKYCAll AddressState = AddressStateKYCVerified | AddressStateKYCExpired // 0b1100000000000000000000000000000000 + + AddressStateConsortiumMember AddressState = AddressState(1) << AddressStateBitConsortium // 0b0100000000000000000000000000000000000000 + AddressStateNodeDeferred AddressState = AddressState(1) << AddressStateBitNodeDeferred // 0b1000000000000000000000000000000000000000 + AddressStateVotableBits AddressState = AddressStateConsortiumMember | AddressStateNodeDeferred // 0b1100000000000000000000000000000000000000 + + AddressStateOffersCreator AddressState = AddressState(1) << AddressStateBitOffersCreator // 0b00100000000000000000000000000000000000000000000000000 + AddressStateCaminoProposer AddressState = AddressState(1) << AddressStateBitCaminoProposer // 0b01000000000000000000000000000000000000000000000000000 + + AddressStateAthensPhaseBits = AddressStateRoleOffersAdmin | AddressStateOffersCreator + AddressStateBerlinPhaseBits = AddressStateCaminoProposer | AddressStateRoleOffersAdmin + + AddressStateValidBits = AddressStateRoleAll | AddressStateKYCAll | AddressStateVotableBits | + AddressStateAthensPhaseBits | + AddressStateBerlinPhaseBits // 0b1100000000001100001100000000000000000000000000001111 +) + +func (as AddressState) Is(state AddressState) bool { + return as&state == state +} + +func (as AddressState) IsNot(state AddressState) bool { + return as&state != state +} diff --git a/vms/platformvm/camino_helpers_test.go b/vms/platformvm/camino_helpers_test.go index 82c7b08e2b05..15531b22970b 100644 --- a/vms/platformvm/camino_helpers_test.go +++ b/vms/platformvm/camino_helpers_test.go @@ -27,12 +27,12 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/version" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/api" "github.com/ava-labs/avalanchego/vms/platformvm/caminoconfig" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/genesis" "github.com/ava-labs/avalanchego/vms/platformvm/reward" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/stretchr/testify/require" ) @@ -189,7 +189,7 @@ func newCaminoGenesisWithUTXOs(caminoGenesisConfig api.Camino, genesisUTXOs []ap caminoGenesisConfig.ValidatorConsortiumMembers[i] = key.Address() caminoGenesisConfig.AddressStates = append(caminoGenesisConfig.AddressStates, genesis.AddressState{ Address: key.Address(), - State: txs.AddressStateConsortiumMember, + State: as.AddressStateConsortiumMember, }) } diff --git a/vms/platformvm/camino_service.go b/vms/platformvm/camino_service.go index b682d8ca1f39..9465d239af46 100644 --- a/vms/platformvm/camino_service.go +++ b/vms/platformvm/camino_service.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/keystore" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" "github.com/ava-labs/avalanchego/vms/platformvm/locked" "github.com/ava-labs/avalanchego/vms/platformvm/state" @@ -251,10 +252,10 @@ type SetAddressStateArgs struct { api.UserPass api.JSONFromAddrs - Change platformapi.Owner `json:"change"` - Address string `json:"address"` - State txs.AddressStateBit `json:"state"` - Remove bool `json:"remove"` + Change platformapi.Owner `json:"change"` + Address string `json:"address"` + State as.AddressStateBit `json:"state"` + Remove bool `json:"remove"` } // AddAdressState issues an AddAdressStateTx diff --git a/vms/platformvm/camino_vm_test.go b/vms/platformvm/camino_vm_test.go index e2bea9d718a6..7f109b4dea76 100644 --- a/vms/platformvm/camino_vm_test.go +++ b/vms/platformvm/camino_vm_test.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/utils/nodeid" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/avax" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/api" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/dac" @@ -82,22 +83,12 @@ func TestRemoveDeferredValidator(t *testing.T) { tx, err := vm.txBuilder.NewAddressStateTx( consortiumMemberKey.Address(), false, - txs.AddressStateBitConsortium, + as.AddressStateBitConsortium, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err := vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) - + _ = buildAndAcceptBlock(t, vm, tx) // Register node tx, err = vm.txBuilder.NewRegisterNodeTx( ids.EmptyNodeID, @@ -107,16 +98,7 @@ func TestRemoveDeferredValidator(t *testing.T) { outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Add the validator startTime := vm.clock.Time().Add(txexecutor.SyncBound).Add(1 * time.Second) @@ -154,21 +136,12 @@ func TestRemoveDeferredValidator(t *testing.T) { tx, err = vm.txBuilder.NewAddressStateTx( consortiumMemberKey.Address(), false, - txs.AddressStateBitNodeDeferred, + as.AddressStateBitNodeDeferred, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Verify that the validator is deferred (moved from current to deferred stakers set) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) @@ -178,11 +151,11 @@ 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, txs.AddressStateNodeDeferred|txs.AddressStateConsortiumMember) + require.Equal(ownerState, as.AddressStateNodeDeferred|as.AddressStateConsortiumMember) // Fast-forward clock to time for validator to be rewarded vm.clock.Set(endTime) - blk, err = vm.Builder.BuildBlock(context.Background()) + blk, err := vm.Builder.BuildBlock(context.Background()) require.NoError(err) err = blk.Verify(context.Background()) require.NoError(err) @@ -229,7 +202,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, txs.AddressStateConsortiumMember) + require.Equal(ownerState, as.AddressStateConsortiumMember) timestamp := vm.state.GetTimestamp() require.Equal(endTime.Unix(), timestamp.Unix()) @@ -280,21 +253,12 @@ func TestRemoveReactivatedValidator(t *testing.T) { tx, err := vm.txBuilder.NewAddressStateTx( consortiumMemberKey.Address(), false, - txs.AddressStateBitConsortium, + as.AddressStateBitConsortium, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err := vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Register node tx, err = vm.txBuilder.NewRegisterNodeTx( @@ -305,16 +269,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Add the validator vm.state.SetShortIDLink(ids.ShortID(nodeID), state.ShortLinkKeyRegisterNode, &addr) @@ -353,21 +308,12 @@ func TestRemoveReactivatedValidator(t *testing.T) { tx, err = vm.txBuilder.NewAddressStateTx( consortiumMemberKey.Address(), false, - txs.AddressStateBitNodeDeferred, + as.AddressStateBitNodeDeferred, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Verify that the validator is deferred (moved from current to deferred stakers set) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) @@ -379,21 +325,12 @@ func TestRemoveReactivatedValidator(t *testing.T) { tx, err = vm.txBuilder.NewAddressStateTx( consortiumMemberKey.Address(), true, - txs.AddressStateBitNodeDeferred, + as.AddressStateBitNodeDeferred, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, outputOwners, ) require.NoError(err) - err = vm.Builder.AddUnverifiedTx(tx) - require.NoError(err) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - err = blk.Verify(context.Background()) - require.NoError(err) - err = blk.Accept(context.Background()) - require.NoError(err) - err = vm.SetPreference(context.Background(), vm.manager.LastAccepted()) - require.NoError(err) + _ = buildAndAcceptBlock(t, vm, tx) // Verify that the validator is activated again (moved from deferred to current stakers set) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) @@ -403,7 +340,7 @@ func TestRemoveReactivatedValidator(t *testing.T) { // Fast-forward clock to time for validator to be rewarded vm.clock.Set(endTime) - blk, err = vm.Builder.BuildBlock(context.Background()) + blk, err := vm.Builder.BuildBlock(context.Background()) require.NoError(err) err = blk.Verify(context.Background()) require.NoError(err) @@ -631,7 +568,7 @@ func TestProposals(t *testing.T) { addrStateTx, err := vm.txBuilder.NewAddressStateTx( proposerAddr, false, - txs.AddressStateBitCaminoProposer, + as.AddressStateBitCaminoProposer, []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, nil, ) @@ -645,7 +582,9 @@ func TestProposals(t *testing.T) { proposalTx := buildBaseFeeProposalTx(t, vm, proposerKey, proposalBondAmount, fee, proposerKey, tt.feeOptions, chainTime.Add(100*time.Second), chainTime.Add(200*time.Second)) proposalState, nextProposalIDsToExpire, nexExpirationTime, proposalIDsToFinish := makeProposalWithTx(t, vm, proposalTx) - require.EqualValues(5, proposalState.TotalAllowedVoters) // all 5 validators must vote + baseFeeProposalState, ok := proposalState.(*dac.BaseFeeProposalState) + require.True(ok) + require.EqualValues(5, baseFeeProposalState.TotalAllowedVoters) // all 5 validators must vote require.Equal([]ids.ID{proposalTx.ID()}, nextProposalIDsToExpire) // we have only one proposal require.Equal(proposalState.EndTime(), nexExpirationTime) require.Empty(proposalIDsToFinish) // no early-finished proposals @@ -658,16 +597,18 @@ func TestProposals(t *testing.T) { // Fast-forward clock to time a bit forward, but still before proposals start // Try to vote on proposal, expect to fail - vm.clock.Set(proposalState.StartTime().Add(-time.Second)) + vm.clock.Set(baseFeeProposalState.StartTime().Add(-time.Second)) addVoteTx := buildSimpleVoteTx(t, vm, proposerKey, fee, proposalTx.ID(), caminoPreFundedKeys[0], 0) require.Error(vm.Builder.AddUnverifiedTx(addVoteTx)) - vm.clock.Set(proposalState.StartTime()) + vm.clock.Set(baseFeeProposalState.StartTime()) - optionWeights := make([]uint32, len(proposalState.Options)) + optionWeights := make([]uint32, len(baseFeeProposalState.Options)) for i, vote := range tt.votes { optionWeights[vote.option]++ voteTx := buildSimpleVoteTx(t, vm, proposerKey, fee, proposalTx.ID(), caminoPreFundedKeys[i], vote.option) - proposalIDsToFinish, proposalState = voteOnBaseFeeWithTx(t, vm, voteTx, proposalTx.ID(), optionWeights) + proposalState = voteWithTx(t, vm, voteTx, proposalTx.ID(), optionWeights) + proposalIDsToFinish, err = vm.state.GetProposalIDsToFinish() + require.NoError(err) if tt.earlyFinish && i == len(tt.votes)-1 { require.Equal([]ids.ID{proposalTx.ID()}, proposalIDsToFinish) // proposal has finished early } else { @@ -724,6 +665,102 @@ func TestProposals(t *testing.T) { } } +func TestAdminProposals(t *testing.T) { + require := require.New(t) + + proposerKey, proposerAddr, _ := generateKeyAndOwner(t) + proposerAddrStr, err := address.FormatBech32(constants.NetworkIDToHRP[testNetworkID], proposerAddr.Bytes()) + require.NoError(err) + caminoPreFundedKey0AddrStr, err := address.FormatBech32(constants.NetworkIDToHRP[testNetworkID], caminoPreFundedKeys[0].Address().Bytes()) + require.NoError(err) + + applicantAddr := proposerAddr + + defaultConfig := defaultCaminoConfig(true) + proposalBondAmount := defaultConfig.CaminoConfig.DACProposalBondAmount + balance := proposalBondAmount + defaultTxFee + + // Prepare vm + vm := newCaminoVM(api.Camino{ + VerifyNodeSignature: true, + LockModeBondDeposit: true, + InitialAdmin: caminoPreFundedKeys[0].Address(), + }, []api.UTXO{ + { + Amount: json.Uint64(balance), + Address: proposerAddrStr, + }, + { + Amount: json.Uint64(defaultTxFee), + Address: caminoPreFundedKey0AddrStr, + }, + }, &defaultConfig.BanffTime) + vm.ctx.Lock.Lock() + defer func() { require.NoError(vm.Shutdown(context.Background())) }() //nolint:lint + checkBalance(t, vm.state, proposerAddr, + balance, // total + 0, 0, 0, balance, // unlocked + ) + + fee := defaultTxFee + burnedAmt := uint64(0) + + // Give proposer address role to make admin proposals + addrStateTx, err := vm.txBuilder.NewAddressStateTx( + proposerAddr, + false, + as.AddressStateBitRoleConsortiumAdminProposer, + []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, + nil, + ) + require.NoError(err) + blk := buildAndAcceptBlock(t, vm, addrStateTx) + require.Len(blk.Txs(), 1) + checkTx(t, vm, blk.ID(), addrStateTx.ID()) + applicantAddrState, err := vm.state.GetAddressStates(applicantAddr) + require.NoError(err) + require.True(applicantAddrState.IsNot(as.AddressStateConsortiumMember)) + + // Add admin proposal + chainTime := vm.state.GetTimestamp() + proposalTx := buildAddMemberProposalTx(t, vm, proposerKey, proposalBondAmount, fee, + proposerKey, applicantAddr, chainTime.Add(100*time.Second), true) + proposalState, nextProposalIDsToExpire, nexExpirationTime, proposalIDsToFinish := makeProposalWithTx(t, vm, proposalTx) + addMemberProposalState, ok := proposalState.(*dac.AddMemberProposalState) + require.True(ok) + require.EqualValues(0, addMemberProposalState.TotalAllowedVoters) // its admin proposal + require.Equal([]ids.ID{proposalTx.ID()}, nextProposalIDsToExpire) // we have only one proposal + require.Equal(proposalState.EndTime(), nexExpirationTime) + require.Equal([]ids.ID{proposalTx.ID()}, proposalIDsToFinish) // admin proposal must be immediately finished + burnedAmt += fee + checkBalance(t, vm.state, proposerAddr, + balance-burnedAmt, // total + proposalBondAmount, // bonded + 0, 0, balance-proposalBondAmount-burnedAmt, // unlocked + ) + + // build block that will execute proposal + blk = buildAndAcceptBlock(t, vm, nil) + require.Len(blk.Txs(), 1) + checkTx(t, vm, blk.ID(), blk.Txs()[0].ID()) + _, err = vm.state.GetProposal(proposalTx.ID()) + require.ErrorIs(err, database.ErrNotFound) + _, _, err = vm.state.GetNextToExpireProposalIDsAndTime(nil) + require.ErrorIs(err, database.ErrNotFound) + proposalIDsToFinish, err = vm.state.GetProposalIDsToFinish() + require.NoError(err) + require.Empty(proposalIDsToFinish) + checkBalance(t, vm.state, proposerAddr, + balance-burnedAmt, // total + 0, 0, 0, balance-burnedAmt, // unlocked + ) + + // check that applicant became c-member + applicantAddrState, err = vm.state.GetAddressStates(applicantAddr) + require.NoError(err) + require.True(applicantAddrState.Is(as.AddressStateConsortiumMember)) +} + func buildAndAcceptBlock(t *testing.T, vm *VM, tx *txs.Tx) blocks.Block { t.Helper() if tx != nil { @@ -846,12 +883,59 @@ func buildBaseFeeProposalTx( return proposalTx } +func buildAddMemberProposalTx( + t *testing.T, + vm *VM, + fundsKey *secp256k1.PrivateKey, + amountToBond uint64, + amountToBurn uint64, + proposerKey *secp256k1.PrivateKey, + applicantAddress ids.ShortID, + startTime time.Time, + adminProposal bool, +) *txs.Tx { + t.Helper() + ins, outs, signers, _, err := vm.txBuilder.Lock( + vm.state, + []*secp256k1.PrivateKey{fundsKey}, + amountToBond, + amountToBurn, + locked.StateBonded, + nil, nil, 0, + ) + require.NoError(t, err) + var proposal dac.Proposal = &dac.AddMemberProposal{ + Start: uint64(startTime.Unix()), + End: uint64(startTime.Unix()) + dac.AddMemberProposalDuration, + ApplicantAddress: applicantAddress, + } + if adminProposal { + proposal = &dac.AdminProposal{Proposal: proposal} + } + wrapper := &txs.ProposalWrapper{Proposal: proposal} + proposalBytes, err := txs.Codec.Marshal(txs.Version, wrapper) + require.NoError(t, err) + proposalTx, err := txs.NewSigned(&txs.AddProposalTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: outs, + }}, + ProposalPayload: proposalBytes, + ProposerAddress: proposerKey.Address(), + ProposerAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, txs.Codec, append(signers, []*secp256k1.PrivateKey{proposerKey})) + require.NoError(t, err) + return proposalTx +} + func makeProposalWithTx( t *testing.T, vm *VM, tx *txs.Tx, ) ( - proposal *dac.BaseFeeProposalState, + proposal dac.ProposalState, nextProposalIDsToExpire []ids.ID, nexExpirationTime time.Time, proposalIDsToFinish []ids.ID, @@ -862,13 +946,11 @@ func makeProposalWithTx( checkTx(t, vm, blk.ID(), tx.ID()) proposalState, err := vm.state.GetProposal(tx.ID()) require.NoError(t, err) - baseFeeProposalState, ok := proposalState.(*dac.BaseFeeProposalState) - require.True(t, ok) nextProposalIDsToExpire, nexExpirationTime, err = vm.state.GetNextToExpireProposalIDsAndTime(nil) require.NoError(t, err) proposalIDsToFinish, err = vm.state.GetProposalIDsToFinish() require.NoError(t, err) - return baseFeeProposalState, nextProposalIDsToExpire, nexExpirationTime, proposalIDsToFinish + return proposalState, nextProposalIDsToExpire, nexExpirationTime, proposalIDsToFinish } func buildSimpleVoteTx( @@ -908,28 +990,29 @@ func buildSimpleVoteTx( return addVoteTx } -func voteOnBaseFeeWithTx( +func voteWithTx( t *testing.T, vm *VM, tx *txs.Tx, proposalID ids.ID, expectedVoteWeights []uint32, -) (proposalIDsToFinish []ids.ID, baseFeeProposalState *dac.BaseFeeProposalState) { +) (proposalState dac.ProposalState) { t.Helper() blk := buildAndAcceptBlock(t, vm, tx) require.Len(t, blk.Txs(), 1) checkTx(t, vm, blk.ID(), tx.ID()) proposalState, err := vm.state.GetProposal(proposalID) require.NoError(t, err) - baseFeeProposalState, ok := proposalState.(*dac.BaseFeeProposalState) - require.True(t, ok) - require.Len(t, baseFeeProposalState.Options, len(expectedVoteWeights)) - for i := range baseFeeProposalState.Options { - require.Equal(t, expectedVoteWeights[i], baseFeeProposalState.Options[i].Weight) + switch proposalState := proposalState.(type) { + case *dac.BaseFeeProposalState: + require.Len(t, proposalState.Options, len(expectedVoteWeights)) + for i := range proposalState.Options { + require.Equal(t, expectedVoteWeights[i], proposalState.Options[i].Weight) + } + default: + require.Fail(t, "unexpected proposalState type") } - proposalIDsToFinish, err = vm.state.GetProposalIDsToFinish() - require.NoError(t, err) - return proposalIDsToFinish, baseFeeProposalState + return proposalState } func buildAndAcceptBaseTx( diff --git a/vms/platformvm/dac/camino_add_member_proposal.go b/vms/platformvm/dac/camino_add_member_proposal.go index d44747794ce1..e18d44c4c48c 100644 --- a/vms/platformvm/dac/camino_add_member_proposal.go +++ b/vms/platformvm/dac/camino_add_member_proposal.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "golang.org/x/exp/slices" ) @@ -37,6 +38,10 @@ func (*AddMemberProposal) GetOptions() any { return []bool{true, false} } +func (*AddMemberProposal) AdminProposer() as.AddressState { + return as.AddressStateRoleConsortiumAdminProposer +} + func (p *AddMemberProposal) Verify() error { switch { case p.Start >= p.End: @@ -64,6 +69,15 @@ func (p *AddMemberProposal) CreateProposalState(allowedVoters []ids.ShortID) Pro return stateProposal } +func (p *AddMemberProposal) CreateFinishedProposalState(optionIndex uint32) (ProposalState, error) { + if optionIndex >= 2 { + return nil, fmt.Errorf("%w (expected: less than 2, actual: %d)", errWrongOptionIndex, optionIndex) + } + proposalState := p.CreateProposalState([]ids.ShortID{}).(*AddMemberProposalState) + proposalState.Options[optionIndex].Weight++ + return proposalState, nil +} + func (p *AddMemberProposal) Visit(visitor VerifierVisitor) error { return visitor.AddMemberProposal(p) } diff --git a/vms/platformvm/dac/camino_add_member_proposal_test.go b/vms/platformvm/dac/camino_add_member_proposal_test.go index c3fb59b0636d..a084da9548b4 100644 --- a/vms/platformvm/dac/camino_add_member_proposal_test.go +++ b/vms/platformvm/dac/camino_add_member_proposal_test.go @@ -306,3 +306,92 @@ func TestAddMemberProposalStateAddVote(t *testing.T) { }) } } + +func TestAddMemberProposalCreateFinishedProposalState(t *testing.T) { + applicantAddress := ids.ShortID{1} + + tests := map[string]struct { + proposal *AddMemberProposal + optionIndex uint32 + expectedProposalState ProposalState + expectedOriginalProposal *AddMemberProposal + expectedErr error + }{ + "Fail: option 2 out of bonds": { + proposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + optionIndex: 2, + expectedOriginalProposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + expectedErr: errWrongOptionIndex, + }, + "OK: option 0": { + proposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + optionIndex: 0, + expectedProposalState: &AddMemberProposalState{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + AllowedVoters: []ids.ShortID{}, + SimpleVoteOptions: SimpleVoteOptions[bool]{ + Options: []SimpleVoteOption[bool]{ + {Value: true, Weight: 1}, + {Value: false}, + }, + }, + }, + expectedOriginalProposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + }, + "OK: option 1": { + proposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + optionIndex: 1, + expectedProposalState: &AddMemberProposalState{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + AllowedVoters: []ids.ShortID{}, + SimpleVoteOptions: SimpleVoteOptions[bool]{ + Options: []SimpleVoteOption[bool]{ + {Value: true}, + {Value: false, Weight: 1}, + }, + }, + }, + expectedOriginalProposal: &AddMemberProposal{ + Start: 100, + End: 101, + ApplicantAddress: applicantAddress, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + proposalState, err := tt.proposal.CreateFinishedProposalState(tt.optionIndex) + require.ErrorIs(t, err, tt.expectedErr) + require.Equal(t, tt.expectedProposalState, proposalState) + require.Equal(t, tt.expectedOriginalProposal, tt.proposal) + if tt.expectedErr == nil { + require.True(t, proposalState.CanBeFinished()) + require.True(t, proposalState.IsSuccessful()) + } + }) + } +} diff --git a/vms/platformvm/dac/camino_base_fee_proposal.go b/vms/platformvm/dac/camino_base_fee_proposal.go index 4589be1a9349..11580e81fe1a 100644 --- a/vms/platformvm/dac/camino_base_fee_proposal.go +++ b/vms/platformvm/dac/camino_base_fee_proposal.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "golang.org/x/exp/slices" ) @@ -41,6 +42,10 @@ func (p *BaseFeeProposal) GetOptions() any { return p.Options } +func (*BaseFeeProposal) AdminProposer() as.AddressState { + return as.AddressStateEmpty // for now its forbidden, until we'll introduce dedicated role for it +} + func (p *BaseFeeProposal) Verify() error { switch { case len(p.Options) > baseFeeProposalMaxOptionsCount: @@ -72,6 +77,15 @@ func (p *BaseFeeProposal) CreateProposalState(allowedVoters []ids.ShortID) Propo return stateProposal } +func (p *BaseFeeProposal) CreateFinishedProposalState(optionIndex uint32) (ProposalState, error) { + if optionIndex >= uint32(len(p.Options)) { + return nil, fmt.Errorf("%w (expected: less than %d, actual: %d)", errWrongOptionIndex, len(p.Options), optionIndex) + } + proposalState := p.CreateProposalState([]ids.ShortID{}).(*BaseFeeProposalState) + proposalState.Options[optionIndex].Weight++ + return proposalState, nil +} + func (p *BaseFeeProposal) Visit(visitor VerifierVisitor) error { return visitor.BaseFeeProposal(p) } diff --git a/vms/platformvm/dac/camino_base_fee_proposal_test.go b/vms/platformvm/dac/camino_base_fee_proposal_test.go index a7a353a623bc..3655a0f915db 100644 --- a/vms/platformvm/dac/camino_base_fee_proposal_test.go +++ b/vms/platformvm/dac/camino_base_fee_proposal_test.go @@ -322,3 +322,88 @@ func TestBaseFeeProposalStateAddVote(t *testing.T) { }) } } + +func TestBaseFeeProposalCreateFinishedProposalState(t *testing.T) { + tests := map[string]struct { + proposal *BaseFeeProposal + optionIndex uint32 + expectedProposalState ProposalState + expectedOriginalProposal *BaseFeeProposal + expectedErr error + }{ + "Fail: option 2 out of bonds": { + proposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + optionIndex: 2, + expectedOriginalProposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + expectedErr: errWrongOptionIndex, + }, + "OK: option 0": { + proposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + optionIndex: 0, + expectedProposalState: &BaseFeeProposalState{ + Start: 100, + End: 101, + AllowedVoters: []ids.ShortID{}, + SimpleVoteOptions: SimpleVoteOptions[uint64]{ + Options: []SimpleVoteOption[uint64]{ + {Value: 10, Weight: 1}, + {Value: 20}, + }, + }, + }, + expectedOriginalProposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + }, + "OK: option 1": { + proposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + optionIndex: 1, + expectedProposalState: &BaseFeeProposalState{ + Start: 100, + End: 101, + AllowedVoters: []ids.ShortID{}, + SimpleVoteOptions: SimpleVoteOptions[uint64]{ + Options: []SimpleVoteOption[uint64]{ + {Value: 10}, + {Value: 20, Weight: 1}, + }, + }, + }, + expectedOriginalProposal: &BaseFeeProposal{ + Start: 100, + End: 101, + Options: []uint64{10, 20}, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + proposalState, err := tt.proposal.CreateFinishedProposalState(tt.optionIndex) + require.ErrorIs(t, err, tt.expectedErr) + require.Equal(t, tt.expectedProposalState, proposalState) + require.Equal(t, tt.expectedOriginalProposal, tt.proposal) + if tt.expectedErr == nil { + require.True(t, proposalState.CanBeFinished()) + require.True(t, proposalState.IsSuccessful()) + } + }) + } +} diff --git a/vms/platformvm/dac/camino_proposal.go b/vms/platformvm/dac/camino_proposal.go index 2ab8f5e6ee82..47523f839cfc 100644 --- a/vms/platformvm/dac/camino_proposal.go +++ b/vms/platformvm/dac/camino_proposal.go @@ -9,13 +9,17 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/verify" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" ) var ( + errWrongOptionIndex = errors.New("wrong option index") errEndNotAfterStart = errors.New("proposal end-time is not after start-time") errWrongDuration = errors.New("wrong proposal duration") ErrWrongVote = errors.New("this proposal can't be voted with this vote") ErrNotAllowedToVoteOnProposal = errors.New("this address has already voted or not allowed to vote on this proposal") + + _ Proposal = (*AdminProposal)(nil) ) type VerifierVisitor interface { @@ -33,7 +37,10 @@ type Proposal interface { StartTime() time.Time EndTime() time.Time + // AddressStateEmpty means that this proposal can't be used as admin proposal + AdminProposer() as.AddressState CreateProposalState(allowedVoters []ids.ShortID) ProposalState + CreateFinishedProposalState(optionIndex uint32) (ProposalState, error) Visit(VerifierVisitor) error // Returns proposal options. (used in magellan) @@ -66,3 +73,8 @@ type ProposalState interface { // We don't want to care about that in magellan. ForceAddVote(voteIntf Vote) (ProposalState, error) } + +type AdminProposal struct { + OptionIndex uint32 `serialize:"true"` + Proposal `serialize:"true"` +} diff --git a/vms/platformvm/genesis/camino.go b/vms/platformvm/genesis/camino.go index 1840f6925564..d94bcf738414 100644 --- a/vms/platformvm/genesis/camino.go +++ b/vms/platformvm/genesis/camino.go @@ -9,6 +9,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/vms/components/multisig" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) @@ -45,8 +46,8 @@ type ConsortiumMemberNodeID struct { } type AddressState struct { - Address ids.ShortID `serialize:"true"` - State txs.AddressState `serialize:"true"` + Address ids.ShortID `serialize:"true"` + State as.AddressState `serialize:"true"` } type Block struct { diff --git a/vms/platformvm/state/camino.go b/vms/platformvm/state/camino.go index 8c5227c0d15a..e2a30cb76fb1 100644 --- a/vms/platformvm/state/camino.go +++ b/vms/platformvm/state/camino.go @@ -21,6 +21,7 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/multisig" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/dac" @@ -80,8 +81,8 @@ type CaminoDiff interface { // Address State - SetAddressStates(ids.ShortID, txs.AddressState) - GetAddressStates(ids.ShortID) (txs.AddressState, error) + SetAddressStates(ids.ShortID, as.AddressState) + GetAddressStates(ids.ShortID) (as.AddressState, error) // Deposit offers @@ -169,7 +170,7 @@ type CaminoConfig struct { type caminoDiff struct { deferredStakerDiffs diffStakers - modifiedAddressStates map[ids.ShortID]txs.AddressState + modifiedAddressStates map[ids.ShortID]as.AddressState modifiedDepositOffers map[ids.ID]*deposit.Offer modifiedDeposits map[ids.ID]*depositDiff modifiedMultisigAliases map[ids.ShortID]*multisig.AliasWithNonce @@ -196,7 +197,7 @@ type caminoState struct { deferredValidatorList linkeddb.LinkedDB // Address State - addressStateCache cache.Cacher[ids.ShortID, txs.AddressState] + addressStateCache cache.Cacher[ids.ShortID, as.AddressState] addressStateDB database.Database // Deposit offers @@ -234,7 +235,7 @@ type caminoState struct { func newCaminoDiff() *caminoDiff { return &caminoDiff{ - modifiedAddressStates: make(map[ids.ShortID]txs.AddressState), + modifiedAddressStates: make(map[ids.ShortID]as.AddressState), modifiedDepositOffers: make(map[ids.ID]*deposit.Offer), modifiedDeposits: make(map[ids.ID]*depositDiff), modifiedMultisigAliases: make(map[ids.ShortID]*multisig.AliasWithNonce), @@ -246,10 +247,10 @@ func newCaminoDiff() *caminoDiff { } func newCaminoState(baseDB, validatorsDB database.Database, metricsReg prometheus.Registerer) (*caminoState, error) { - addressStateCache, err := metercacher.New[ids.ShortID, txs.AddressState]( + addressStateCache, err := metercacher.New[ids.ShortID, as.AddressState]( "address_state_cache", metricsReg, - &cache.LRU[ids.ShortID, txs.AddressState]{Size: addressStateCacheSize}, + &cache.LRU[ids.ShortID, as.AddressState]{Size: addressStateCacheSize}, ) if err != nil { return nil, err @@ -376,11 +377,11 @@ func (cs *caminoState) SyncGenesis(s *state, g *genesis.State) error { return err } cs.SetAddressStates(g.Camino.InitialAdmin, - initalAdminAddressState|txs.AddressStateRoleAdmin) + initalAdminAddressState|as.AddressStateRoleAdmin) addrStateTx, err := txs.NewSigned(&txs.AddressStateTx{ Address: g.Camino.InitialAdmin, - State: txs.AddressStateBitRoleAdmin, + State: as.AddressStateBitRoleAdmin, Remove: false, }, txs.Codec, nil) if err != nil { diff --git a/vms/platformvm/state/camino_address_state.go b/vms/platformvm/state/camino_address_state.go index d53e81242264..e407ac5a6eb2 100644 --- a/vms/platformvm/state/camino_address_state.go +++ b/vms/platformvm/state/camino_address_state.go @@ -8,17 +8,17 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" ) // Set a new state assigned to the address id -func (cs *caminoState) SetAddressStates(address ids.ShortID, states txs.AddressState) { +func (cs *caminoState) SetAddressStates(address ids.ShortID, states as.AddressState) { cs.modifiedAddressStates[address] = states cs.addressStateCache.Evict(address) } // Return the current state (if exists) for an address -func (cs *caminoState) GetAddressStates(address ids.ShortID) (txs.AddressState, error) { +func (cs *caminoState) GetAddressStates(address ids.ShortID) (as.AddressState, error) { // Try to get from modified state item, ok := cs.modifiedAddressStates[address] // Try to get from cache @@ -30,11 +30,11 @@ func (cs *caminoState) GetAddressStates(address ids.ShortID) (txs.AddressState, uintBytes, err := cs.addressStateDB.Get(address[:]) switch err { case nil: - item = txs.AddressState(binary.LittleEndian.Uint64(uintBytes)) + item = as.AddressState(binary.LittleEndian.Uint64(uintBytes)) case database.ErrNotFound: - item = txs.AddressStateEmpty + item = as.AddressStateEmpty default: - return txs.AddressStateEmpty, err + return as.AddressStateEmpty, err } cs.addressStateCache.Put(address, item) } diff --git a/vms/platformvm/state/camino_address_state_test.go b/vms/platformvm/state/camino_address_state_test.go index 70924cae1763..a0cdd4ceff3a 100644 --- a/vms/platformvm/state/camino_address_state_test.go +++ b/vms/platformvm/state/camino_address_state_test.go @@ -11,14 +11,14 @@ import ( "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" ) func TestGetAddressStates(t *testing.T) { address := ids.ShortID{1} - addressStates := txs.AddressState(12345) + addressStates := as.AddressState(12345) addressStatesBytes := make([]byte, 8) binary.LittleEndian.PutUint64(addressStatesBytes, uint64(addressStates)) testError := errors.New("test error") @@ -27,14 +27,14 @@ func TestGetAddressStates(t *testing.T) { caminoState func(*gomock.Controller) *caminoState address ids.ShortID expectedCaminoState func(*caminoState) *caminoState - expectedAddressStates txs.AddressState + expectedAddressStates as.AddressState expectedErr error }{ "OK: address states added or modified": { caminoState: func(c *gomock.Controller) *caminoState { return &caminoState{ caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{address: addressStates}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{address: addressStates}, }, } }, @@ -43,7 +43,7 @@ func TestGetAddressStates(t *testing.T) { return &caminoState{ addressStateCache: actualCaminoState.addressStateCache, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{address: addressStates}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{address: addressStates}, }, } }, @@ -51,7 +51,7 @@ func TestGetAddressStates(t *testing.T) { }, "OK: address states in cache": { caminoState: func(c *gomock.Controller) *caminoState { - cache := cache.NewMockCacher[ids.ShortID, txs.AddressState](c) + cache := cache.NewMockCacher[ids.ShortID, as.AddressState](c) cache.EXPECT().Get(address).Return(addressStates, true) return &caminoState{ addressStateCache: cache, @@ -69,8 +69,8 @@ func TestGetAddressStates(t *testing.T) { }, "OK: address states in db": { caminoState: func(c *gomock.Controller) *caminoState { - cache := cache.NewMockCacher[ids.ShortID, txs.AddressState](c) - cache.EXPECT().Get(address).Return(txs.AddressStateEmpty, false) + cache := cache.NewMockCacher[ids.ShortID, as.AddressState](c) + cache.EXPECT().Get(address).Return(as.AddressStateEmpty, false) cache.EXPECT().Put(address, addressStates) db := database.NewMockDatabase(c) db.EXPECT().Get(address[:]).Return(addressStatesBytes, nil) @@ -92,9 +92,9 @@ func TestGetAddressStates(t *testing.T) { }, "OK: not found in db": { caminoState: func(c *gomock.Controller) *caminoState { - cache := cache.NewMockCacher[ids.ShortID, txs.AddressState](c) - cache.EXPECT().Get(address).Return(txs.AddressStateEmpty, false) - cache.EXPECT().Put(address, txs.AddressStateEmpty) + cache := cache.NewMockCacher[ids.ShortID, as.AddressState](c) + cache.EXPECT().Get(address).Return(as.AddressStateEmpty, false) + cache.EXPECT().Put(address, as.AddressStateEmpty) db := database.NewMockDatabase(c) db.EXPECT().Get(address[:]).Return(nil, database.ErrNotFound) return &caminoState{ @@ -114,8 +114,8 @@ func TestGetAddressStates(t *testing.T) { }, "Fail: db error": { caminoState: func(c *gomock.Controller) *caminoState { - cache := cache.NewMockCacher[ids.ShortID, txs.AddressState](c) - cache.EXPECT().Get(address).Return(txs.AddressStateEmpty, false) + cache := cache.NewMockCacher[ids.ShortID, as.AddressState](c) + cache.EXPECT().Get(address).Return(as.AddressStateEmpty, false) db := database.NewMockDatabase(c) db.EXPECT().Get(address[:]).Return(nil, testError) return &caminoState{ @@ -150,21 +150,21 @@ func TestGetAddressStates(t *testing.T) { func TestSetAddressStates(t *testing.T) { address := ids.ShortID{1} - addressStates := txs.AddressState(12345) + addressStates := as.AddressState(12345) tests := map[string]struct { caminoState func(*gomock.Controller) *caminoState address ids.ShortID - addressStates txs.AddressState + addressStates as.AddressState expectedCaminoState func(*caminoState) *caminoState }{ "OK": { caminoState: func(c *gomock.Controller) *caminoState { - cache := cache.NewMockCacher[ids.ShortID, txs.AddressState](c) + cache := cache.NewMockCacher[ids.ShortID, as.AddressState](c) cache.EXPECT().Evict(address) return &caminoState{ addressStateCache: cache, - caminoDiff: &caminoDiff{modifiedAddressStates: map[ids.ShortID]txs.AddressState{}}, + caminoDiff: &caminoDiff{modifiedAddressStates: map[ids.ShortID]as.AddressState{}}, } }, address: address, @@ -173,7 +173,7 @@ func TestSetAddressStates(t *testing.T) { return &caminoState{ addressStateCache: actualCaminoState.addressStateCache, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{address: addressStates}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{address: addressStates}, }, } }, @@ -194,7 +194,7 @@ func TestWriteAddressStates(t *testing.T) { testError := errors.New("test error") address1 := ids.ShortID{1} address2 := ids.ShortID{2} - addressStates1 := txs.AddressState(12345) + addressStates1 := as.AddressState(12345) addressStatesBytes1 := make([]byte, 8) binary.LittleEndian.PutUint64(addressStatesBytes1, uint64(addressStates1)) @@ -210,7 +210,7 @@ func TestWriteAddressStates(t *testing.T) { return &caminoState{ addressStateDB: addressStateDB, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ address1: addressStates1, }, }, @@ -220,7 +220,7 @@ func TestWriteAddressStates(t *testing.T) { return &caminoState{ addressStateDB: actualState.addressStateDB, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{}, }, } }, @@ -232,7 +232,7 @@ func TestWriteAddressStates(t *testing.T) { addressStateDB.EXPECT().Delete(address1[:]).Return(testError) return &caminoState{ caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ address1: 0, }, }, @@ -242,7 +242,7 @@ func TestWriteAddressStates(t *testing.T) { expectedCaminoState: func(actualState *caminoState) *caminoState { return &caminoState{ caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{}, }, addressStateDB: actualState.addressStateDB, } @@ -257,7 +257,7 @@ func TestWriteAddressStates(t *testing.T) { return &caminoState{ addressStateDB: addressStateDB, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ address1: addressStates1, address2: 0, }, @@ -268,7 +268,7 @@ func TestWriteAddressStates(t *testing.T) { return &caminoState{ addressStateDB: actualState.addressStateDB, caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{}, }, } }, diff --git a/vms/platformvm/state/camino_diff.go b/vms/platformvm/state/camino_diff.go index 9b507820da2e..b404bb5ee3a1 100644 --- a/vms/platformvm/state/camino_diff.go +++ b/vms/platformvm/state/camino_diff.go @@ -14,11 +14,11 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/multisig" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/dac" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" "github.com/ava-labs/avalanchego/vms/platformvm/locked" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) func NewCaminoDiff( @@ -95,11 +95,11 @@ func (d *diff) CaminoConfig() (*CaminoConfig, error) { return parentState.CaminoConfig() } -func (d *diff) SetAddressStates(address ids.ShortID, states txs.AddressState) { +func (d *diff) SetAddressStates(address ids.ShortID, states as.AddressState) { d.caminoDiff.modifiedAddressStates[address] = states } -func (d *diff) GetAddressStates(address ids.ShortID) (txs.AddressState, error) { +func (d *diff) GetAddressStates(address ids.ShortID) (as.AddressState, error) { if states, ok := d.caminoDiff.modifiedAddressStates[address]; ok { return states, nil } diff --git a/vms/platformvm/state/camino_diff_test.go b/vms/platformvm/state/camino_diff_test.go index a85ddf3a7412..720b5888d76d 100644 --- a/vms/platformvm/state/camino_diff_test.go +++ b/vms/platformvm/state/camino_diff_test.go @@ -14,11 +14,11 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/multisig" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/dac" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" "github.com/ava-labs/avalanchego/vms/platformvm/locked" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -2024,15 +2024,15 @@ func TestDiffSetAddressStates(t *testing.T) { tests := map[string]struct { diff *diff address ids.ShortID - states txs.AddressState + states as.AddressState expectedDiff *diff }{ "OK": { - diff: &diff{caminoDiff: &caminoDiff{modifiedAddressStates: map[ids.ShortID]txs.AddressState{}}}, + diff: &diff{caminoDiff: &caminoDiff{modifiedAddressStates: map[ids.ShortID]as.AddressState{}}}, address: addr1, states: 111, expectedDiff: &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ addr1: 111, }, }}, @@ -2055,13 +2055,13 @@ func TestDiffGetAddressStates(t *testing.T) { diff func(*gomock.Controller) *diff address ids.ShortID expectedDiff func(actualDiff *diff) *diff - expectedAddresStates txs.AddressState + expectedAddresStates as.AddressState expectedErr error }{ "OK: modified": { diff: func(c *gomock.Controller) *diff { return &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ addr1: 111, }, }} @@ -2069,7 +2069,7 @@ func TestDiffGetAddressStates(t *testing.T) { address: addr1, expectedDiff: func(actualDiff *diff) *diff { return &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ addr1: 111, }, }} @@ -2079,7 +2079,7 @@ func TestDiffGetAddressStates(t *testing.T) { "OK: removed": { diff: func(c *gomock.Controller) *diff { return &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ addr1: 0, }, }} @@ -2087,7 +2087,7 @@ func TestDiffGetAddressStates(t *testing.T) { address: addr1, expectedDiff: func(actualDiff *diff) *diff { return &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ addr1: 0, }, }} @@ -2097,7 +2097,7 @@ func TestDiffGetAddressStates(t *testing.T) { "OK: in parent": { diff: func(c *gomock.Controller) *diff { parentState := NewMockChain(c) - parentState.EXPECT().GetAddressStates(addr1).Return(txs.AddressState(111), nil) + parentState.EXPECT().GetAddressStates(addr1).Return(as.AddressState(111), nil) return &diff{ stateVersions: newMockStateVersions(c, parentStateID, parentState), parentID: parentStateID, @@ -2117,7 +2117,7 @@ func TestDiffGetAddressStates(t *testing.T) { "Fail: parent errored": { diff: func(c *gomock.Controller) *diff { parentState := NewMockChain(c) - parentState.EXPECT().GetAddressStates(addr1).Return(txs.AddressStateEmpty, testErr) + parentState.EXPECT().GetAddressStates(addr1).Return(as.AddressStateEmpty, testErr) return &diff{ stateVersions: newMockStateVersions(c, parentStateID, parentState), parentID: parentStateID, @@ -5132,7 +5132,7 @@ func TestDiffApplyCaminoState(t *testing.T) { }{ "OK": { diff: &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ {1}: 101, {2}: 0, }, @@ -5243,7 +5243,7 @@ func TestDiffApplyCaminoState(t *testing.T) { return s }, expectedDiff: &diff{caminoDiff: &caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{ + modifiedAddressStates: map[ids.ShortID]as.AddressState{ {1}: 101, {2}: 0, }, diff --git a/vms/platformvm/state/camino_state.go b/vms/platformvm/state/camino_state.go index 41eac7751945..8a796a1b7508 100644 --- a/vms/platformvm/state/camino_state.go +++ b/vms/platformvm/state/camino_state.go @@ -11,11 +11,11 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/multisig" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/dac" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" "github.com/ava-labs/avalanchego/vms/platformvm/locked" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) func (s *state) LockedUTXOs(txIDs set.Set[ids.ID], addresses set.Set[ids.ShortID], lockState locked.State) ([]*avax.UTXO, error) { @@ -50,11 +50,11 @@ func (s *state) CaminoConfig() (*CaminoConfig, error) { return s.caminoState.CaminoConfig(), nil } -func (s *state) SetAddressStates(address ids.ShortID, states txs.AddressState) { +func (s *state) SetAddressStates(address ids.ShortID, states as.AddressState) { s.caminoState.SetAddressStates(address, states) } -func (s *state) GetAddressStates(address ids.ShortID) (txs.AddressState, error) { +func (s *state) GetAddressStates(address ids.ShortID) (as.AddressState, error) { return s.caminoState.GetAddressStates(address) } diff --git a/vms/platformvm/state/camino_test.go b/vms/platformvm/state/camino_test.go index ef58dd0e2112..f7f1029d739e 100644 --- a/vms/platformvm/state/camino_test.go +++ b/vms/platformvm/state/camino_test.go @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/deposit" pvm_genesis "github.com/ava-labs/avalanchego/vms/platformvm/genesis" "github.com/ava-labs/avalanchego/vms/platformvm/reward" @@ -248,17 +249,17 @@ func TestSyncGenesis(t *testing.T) { g: defaultGenesisState([]pvm_genesis.AddressState{ { Address: initialAdmin, - State: txs.AddressStateRoleAdmin, + State: as.AddressStateRoleAdmin, }, { Address: shortID, - State: txs.AddressStateRoleKYC, + State: as.AddressStateRoleKYC, }, }, depositTxs, initialAdmin), }, cs: *wrappers.IgnoreError(newCaminoState(baseDB, validatorsDB, prometheus.NewRegistry())).(*caminoState), want: caminoDiff{ - modifiedAddressStates: map[ids.ShortID]txs.AddressState{initialAdmin: txs.AddressStateRoleAdmin, shortID: txs.AddressStateRoleKYC}, + modifiedAddressStates: map[ids.ShortID]as.AddressState{initialAdmin: as.AddressStateRoleAdmin, shortID: as.AddressStateRoleKYC}, modifiedDepositOffers: map[ids.ID]*deposit.Offer{ depositOffers[0].ID: depositOffers[0], depositOffers[1].ID: depositOffers[1], diff --git a/vms/platformvm/state/mock_chain.go b/vms/platformvm/state/mock_chain.go index f65de5b52f32..e4e23abbc3f9 100644 --- a/vms/platformvm/state/mock_chain.go +++ b/vms/platformvm/state/mock_chain.go @@ -15,6 +15,7 @@ import ( set "github.com/ava-labs/avalanchego/utils/set" avax "github.com/ava-labs/avalanchego/vms/components/avax" multisig "github.com/ava-labs/avalanchego/vms/components/multisig" + addrstate "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" config "github.com/ava-labs/avalanchego/vms/platformvm/config" dac "github.com/ava-labs/avalanchego/vms/platformvm/dac" deposit "github.com/ava-labs/avalanchego/vms/platformvm/deposit" @@ -222,10 +223,10 @@ func (mr *MockChainMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { } // GetAddressStates mocks base method. -func (m *MockChain) GetAddressStates(arg0 ids.ShortID) (txs.AddressState, error) { +func (m *MockChain) GetAddressStates(arg0 ids.ShortID) (addrstate.AddressState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAddressStates", arg0) - ret0, _ := ret[0].(txs.AddressState) + ret0, _ := ret[0].(addrstate.AddressState) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -791,7 +792,7 @@ func (mr *MockChainMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.C } // SetAddressStates mocks base method. -func (m *MockChain) SetAddressStates(arg0 ids.ShortID, arg1 txs.AddressState) { +func (m *MockChain) SetAddressStates(arg0 ids.ShortID, arg1 addrstate.AddressState) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetAddressStates", arg0, arg1) } diff --git a/vms/platformvm/state/mock_diff.go b/vms/platformvm/state/mock_diff.go index 6e2bb2e9ceaf..fcde73116916 100644 --- a/vms/platformvm/state/mock_diff.go +++ b/vms/platformvm/state/mock_diff.go @@ -15,6 +15,7 @@ import ( set "github.com/ava-labs/avalanchego/utils/set" avax "github.com/ava-labs/avalanchego/vms/components/avax" multisig "github.com/ava-labs/avalanchego/vms/components/multisig" + addrstate "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" config "github.com/ava-labs/avalanchego/vms/platformvm/config" dac "github.com/ava-labs/avalanchego/vms/platformvm/dac" deposit "github.com/ava-labs/avalanchego/vms/platformvm/deposit" @@ -246,10 +247,10 @@ func (mr *MockDiffMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { } // GetAddressStates mocks base method. -func (m *MockDiff) GetAddressStates(arg0 ids.ShortID) (txs.AddressState, error) { +func (m *MockDiff) GetAddressStates(arg0 ids.ShortID) (addrstate.AddressState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAddressStates", arg0) - ret0, _ := ret[0].(txs.AddressState) + ret0, _ := ret[0].(addrstate.AddressState) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -815,7 +816,7 @@ func (mr *MockDiffMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.Ca } // SetAddressStates mocks base method. -func (m *MockDiff) SetAddressStates(arg0 ids.ShortID, arg1 txs.AddressState) { +func (m *MockDiff) SetAddressStates(arg0 ids.ShortID, arg1 addrstate.AddressState) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetAddressStates", arg0, arg1) } diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index 33a71d2fa8b8..085226fda2d5 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -19,6 +19,7 @@ import ( set "github.com/ava-labs/avalanchego/utils/set" avax "github.com/ava-labs/avalanchego/vms/components/avax" multisig "github.com/ava-labs/avalanchego/vms/components/multisig" + addrstate "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" blocks "github.com/ava-labs/avalanchego/vms/platformvm/blocks" config "github.com/ava-labs/avalanchego/vms/platformvm/config" dac "github.com/ava-labs/avalanchego/vms/platformvm/dac" @@ -294,10 +295,10 @@ func (mr *MockStateMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { } // GetAddressStates mocks base method. -func (m *MockState) GetAddressStates(arg0 ids.ShortID) (txs.AddressState, error) { +func (m *MockState) GetAddressStates(arg0 ids.ShortID) (addrstate.AddressState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAddressStates", arg0) - ret0, _ := ret[0].(txs.AddressState) + ret0, _ := ret[0].(addrstate.AddressState) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -956,7 +957,7 @@ func (mr *MockStateMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.C } // SetAddressStates mocks base method. -func (m *MockState) SetAddressStates(arg0 ids.ShortID, arg1 txs.AddressState) { +func (m *MockState) SetAddressStates(arg0 ids.ShortID, arg1 addrstate.AddressState) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetAddressStates", arg0, arg1) } diff --git a/vms/platformvm/txs/builder/camino_builder.go b/vms/platformvm/txs/builder/camino_builder.go index af5f146a8e41..8e765daf0f77 100644 --- a/vms/platformvm/txs/builder/camino_builder.go +++ b/vms/platformvm/txs/builder/camino_builder.go @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/avax" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/locked" @@ -60,7 +61,7 @@ type CaminoTxBuilder interface { NewAddressStateTx( address ids.ShortID, remove bool, - state txs.AddressStateBit, + state as.AddressStateBit, keys []*secp256k1.PrivateKey, change *secp256k1fx.OutputOwners, ) (*txs.Tx, error) @@ -300,7 +301,7 @@ func (b *caminoBuilder) NewRewardValidatorTx(txID ids.ID) (*txs.Tx, error) { func (b *caminoBuilder) NewAddressStateTx( address ids.ShortID, remove bool, - state txs.AddressStateBit, + state as.AddressStateBit, keys []*secp256k1.PrivateKey, change *secp256k1fx.OutputOwners, ) (*txs.Tx, error) { diff --git a/vms/platformvm/txs/builder/camino_builder_test.go b/vms/platformvm/txs/builder/camino_builder_test.go index 96c84737c9e1..1f840c4d6636 100644 --- a/vms/platformvm/txs/builder/camino_builder_test.go +++ b/vms/platformvm/txs/builder/camino_builder_test.go @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/nodeid" "github.com/ava-labs/avalanchego/vms/components/avax" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/api" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/treasury" @@ -55,37 +56,37 @@ func TestCaminoBuilderTxAddressState(t *testing.T) { tests := map[string]struct { remove bool - state txs.AddressStateBit + state as.AddressStateBit address ids.ShortID expectedErr error }{ "KYC Role: Add": { remove: false, - state: txs.AddressStateBitRoleKYC, + state: as.AddressStateBitRoleKYC, address: caminoPreFundedKeys[0].PublicKey().Address(), expectedErr: nil, }, "KYC Role: Remove": { remove: true, - state: txs.AddressStateBitRoleKYC, + state: as.AddressStateBitRoleKYC, address: caminoPreFundedKeys[0].PublicKey().Address(), expectedErr: nil, }, "Admin Role: Add": { remove: false, - state: txs.AddressStateBitRoleAdmin, + state: as.AddressStateBitRoleAdmin, address: caminoPreFundedKeys[0].PublicKey().Address(), expectedErr: nil, }, "Admin Role: Remove": { remove: true, - state: txs.AddressStateBitRoleAdmin, + state: as.AddressStateBitRoleAdmin, address: caminoPreFundedKeys[0].PublicKey().Address(), expectedErr: nil, }, "Empty Address": { remove: false, - state: txs.AddressStateBitRoleKYC, + state: as.AddressStateBitRoleKYC, address: ids.ShortEmpty, expectedErr: txs.ErrEmptyAddress, }, diff --git a/vms/platformvm/txs/camino_address_state_tx.go b/vms/platformvm/txs/camino_address_state_tx.go index 355b18f86e1f..108444bc7099 100644 --- a/vms/platformvm/txs/camino_address_state_tx.go +++ b/vms/platformvm/txs/camino_address_state_tx.go @@ -10,66 +10,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/components/verify" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/locked" ) -type ( - AddressState uint64 - AddressStateBit uint8 -) - -// AddressState flags, max 63 -const ( - // Bits - - AddressStateBitRoleAdmin AddressStateBit = 0 - AddressStateBitRoleKYC AddressStateBit = 1 - AddressStateBitRoleOffersAdmin AddressStateBit = 2 - - AddressStateBitKYCVerified AddressStateBit = 32 - AddressStateBitKYCExpired AddressStateBit = 33 - AddressStateBitConsortium AddressStateBit = 38 - AddressStateBitNodeDeferred AddressStateBit = 39 - AddressStateBitOffersCreator AddressStateBit = 50 - AddressStateBitCaminoProposer AddressStateBit = 51 - AddressStateBitMax AddressStateBit = 63 - - // States - - AddressStateEmpty AddressState = 0 - - AddressStateRoleAdmin AddressState = AddressState(1) << AddressStateBitRoleAdmin // 0b1 - AddressStateRoleKYC AddressState = AddressState(1) << AddressStateBitRoleKYC // 0b10 - AddressStateRoleOffersAdmin AddressState = AddressState(1) << AddressStateBitRoleOffersAdmin // 0b100 - AddressStateRoleAll AddressState = AddressStateRoleAdmin | AddressStateRoleKYC | AddressStateRoleOffersAdmin // 0b111 - - AddressStateKYCVerified AddressState = AddressState(1) << AddressStateBitKYCVerified // 0b0100000000000000000000000000000000 - AddressStateKYCExpired AddressState = AddressState(1) << AddressStateBitKYCExpired // 0b1000000000000000000000000000000000 - AddressStateKYCAll AddressState = AddressStateKYCVerified | AddressStateKYCExpired // 0b1100000000000000000000000000000000 - - AddressStateConsortiumMember AddressState = AddressState(1) << AddressStateBitConsortium // 0b0100000000000000000000000000000000000000 - AddressStateNodeDeferred AddressState = AddressState(1) << AddressStateBitNodeDeferred // 0b1000000000000000000000000000000000000000 - AddressStateVotableBits AddressState = AddressStateConsortiumMember | AddressStateNodeDeferred // 0b1100000000000000000000000000000000000000 - - AddressStateOffersCreator AddressState = AddressState(1) << AddressStateBitOffersCreator // 0b0100000000000000000000000000000000000000000000000000 - AddressStateCaminoProposer AddressState = AddressState(1) << AddressStateBitCaminoProposer // 0b1000000000000000000000000000000000000000000000000000 - - AddressStateAthensPhaseBits = AddressStateRoleOffersAdmin | AddressStateOffersCreator - AddressStateBerlinPhaseBits = AddressStateCaminoProposer - - AddressStateValidBits = AddressStateRoleAll | AddressStateKYCAll | AddressStateVotableBits | - AddressStateAthensPhaseBits | - AddressStateBerlinPhaseBits // 0b1100000000001100001100000000000000000000000000000111 -) - -func (as AddressState) Is(state AddressState) bool { - return as&state == state -} - -func (as AddressState) IsNot(state AddressState) bool { - return as&state != state -} - var ( _ UnsignedTx = (*AddressStateTx)(nil) @@ -86,7 +30,7 @@ type AddressStateTx struct { // The address to add / remove state Address ids.ShortID `serialize:"true" json:"address"` // The state to set / unset - State AddressStateBit `serialize:"true" json:"state"` + State 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) @@ -104,7 +48,7 @@ func (tx *AddressStateTx) SyntacticVerify(ctx *snow.Context) error { return nil case tx.Address == ids.ShortEmpty: return ErrEmptyAddress - case tx.State > AddressStateBitMax || AddressStateValidBits&AddressState(uint64(1)< as.AddressStateBitMax || as.AddressStateValidBits&as.AddressState(uint64(1)< 0 { @@ -2136,7 +2171,7 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error if err != nil { return err } - if tx.Remove && tx.State == txs.AddressStateBitRoleAdmin && tx.Address == tx.Executor { + if tx.Remove && tx.State == as.AddressStateBitRoleAdmin && tx.Address == tx.Executor { return errAdminCannotBeDeleted } } else { @@ -2150,7 +2185,7 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error } // Accumulate roles over all signers - checkSelfRemove := tx.Remove && tx.State == txs.AddressStateBitRoleAdmin + checkSelfRemove := tx.Remove && tx.State == as.AddressStateBitRoleAdmin for address := range addresses { states, err := e.State.GetAddressStates(address) if err != nil { @@ -2162,10 +2197,10 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error roles |= states } } - statesBit := txs.AddressState(1) << tx.State + statesBit := as.AddressState(1) << tx.State // Check for AthensPhase Bits if we time has not passed yet - if (statesBit&txs.AddressStateAthensPhaseBits) != 0 && + if (statesBit&as.AddressStateAthensPhaseBits) != 0 && !e.Config.IsAthensPhaseActivated(e.State.GetTimestamp()) { return errAddrStateNotPermitted } @@ -2209,7 +2244,7 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error txID := e.Tx.ID() - if tx.State == txs.AddressStateBitNodeDeferred { + if tx.State == 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) @@ -2247,11 +2282,11 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error } // [state] must have only one bit set -func verifyAccess(roles, state txs.AddressState) bool { +func verifyAccess(roles, state as.AddressState) bool { switch { - case roles.Is(txs.AddressStateRoleAdmin): // admin can do anything - case txs.AddressStateKYCAll&state != 0 && roles.Is(txs.AddressStateRoleKYC): // kyc role can change kyc status - case state == txs.AddressStateOffersCreator && roles.Is(txs.AddressStateRoleOffersAdmin): // offers admin can assign offers creator role + 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 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 1917454e76a1..f9d1e079302d 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor_test.go +++ b/vms/platformvm/txs/executor/camino_tx_executor_test.go @@ -25,6 +25,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/multisig" "github.com/ava-labs/avalanchego/vms/components/verify" + as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate" "github.com/ava-labs/avalanchego/vms/platformvm/api" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/dac" @@ -1635,10 +1636,10 @@ func TestAddAddressStateTxExecutor(t *testing.T) { UpgradeVersion uint16 stateAddress ids.ShortID targetAddress ids.ShortID - txFlag txs.AddressStateBit - existingState txs.AddressState + txFlag as.AddressStateBit + existingState as.AddressState expectedErrs []error - expectedState txs.AddressState + expectedState as.AddressState remove bool executor ids.ShortID executorAuth *secp256k1fx.Input @@ -1647,17 +1648,17 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: Admin, Flag: Admin role, Add, Same Address": { stateAddress: bob, targetAddress: bob, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateRoleAdmin, + 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: txs.AddressStateBitRoleKYC, - existingState: txs.AddressStateRoleKYC, + txFlag: as.AddressStateBitRoleKYC, + existingState: as.AddressStateRoleKYC, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, @@ -1665,8 +1666,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: KYC, Flag: Admin role, Add, Same Address": { stateAddress: bob, targetAddress: bob, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleKYC, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleKYC, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, @@ -1674,17 +1675,17 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: Admin, Flag: Admin role, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateRoleAdmin, + 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: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleAdmin, expectedState: 0, remove: true, }, @@ -1692,17 +1693,17 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: Admin, Flag: KYC role, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleKYC, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateRoleKYC, + 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: txs.AddressStateBitRoleKYC, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleKYC, + existingState: as.AddressStateRoleAdmin, expectedState: 0, remove: true, }, @@ -1710,8 +1711,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: Admin, Flag: Admin role, Remove, Same Address": { stateAddress: bob, targetAddress: bob, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleAdmin, expectedState: 0, expectedErrs: []error{errAdminCannotBeDeleted, errAdminCannotBeDeleted}, remove: true, @@ -1720,53 +1721,53 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: Admin, Flag: KYC Verified, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitKYCVerified, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateKYCVerified, + 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: txs.AddressStateBitKYCExpired, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateKYCExpired, + 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: txs.AddressStateBitConsortium, - existingState: txs.AddressStateRoleAdmin, - expectedState: txs.AddressStateConsortiumMember, + txFlag: as.AddressStateBitConsortium, + existingState: as.AddressStateRoleAdmin, + expectedState: as.AddressStateConsortiumMember, remove: false, }, // 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: txs.AddressStateBitKYCExpired, - existingState: txs.AddressStateRoleKYC, - expectedState: txs.AddressStateKYCExpired, + 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: txs.AddressStateBitKYCVerified, - existingState: txs.AddressStateRoleKYC, - expectedState: txs.AddressStateKYCVerified, + 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: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleAdmin, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, @@ -1774,8 +1775,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "Empty State Address": { stateAddress: ids.ShortEmpty, targetAddress: alice, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleAdmin, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, @@ -1783,8 +1784,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "Empty Target Address": { stateAddress: bob, targetAddress: ids.ShortEmpty, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateRoleAdmin, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateRoleAdmin, expectedErrs: []error{txs.ErrEmptyAddress, txs.ErrEmptyAddress}, remove: false, }, @@ -1792,8 +1793,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: Admin role, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateEmpty, remove: false, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1801,8 +1802,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: Admin role, Remove, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleAdmin, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitRoleAdmin, + existingState: as.AddressStateEmpty, remove: true, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1810,8 +1811,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: KYC role, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleKYC, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitRoleKYC, + existingState: as.AddressStateEmpty, remove: false, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1819,8 +1820,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: KYC role, Remove, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitRoleKYC, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitRoleKYC, + existingState: as.AddressStateEmpty, remove: true, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1828,8 +1829,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: KYC Verified, Add, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitKYCVerified, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitKYCVerified, + existingState: as.AddressStateEmpty, remove: false, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1837,8 +1838,8 @@ func TestAddAddressStateTxExecutor(t *testing.T) { "State: none, Flag: KYC Verified, Remove, Different Address": { stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitKYCVerified, - existingState: txs.AddressStateEmpty, + txFlag: as.AddressStateBitKYCVerified, + existingState: as.AddressStateEmpty, remove: true, expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, }, @@ -1847,9 +1848,9 @@ func TestAddAddressStateTxExecutor(t *testing.T) { UpgradeVersion: 1, stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitKYCExpired, - existingState: txs.AddressStateRoleKYC, - expectedState: txs.AddressStateKYCExpired, + txFlag: as.AddressStateBitKYCExpired, + existingState: as.AddressStateRoleKYC, + expectedState: as.AddressStateKYCExpired, expectedErrs: []error{errNotAthensPhase, nil}, remove: false, executor: bob, @@ -1860,9 +1861,9 @@ func TestAddAddressStateTxExecutor(t *testing.T) { UpgradeVersion: 1, stateAddress: bob, targetAddress: alice, - txFlag: txs.AddressStateBitKYCExpired, - existingState: txs.AddressStateRoleKYC, - expectedState: txs.AddressStateKYCExpired, + txFlag: as.AddressStateBitKYCExpired, + existingState: as.AddressStateRoleKYC, + expectedState: as.AddressStateKYCExpired, expectedErrs: []error{errNotAthensPhase, errSignatureMissing}, remove: false, executor: alice, @@ -4337,7 +4338,7 @@ func TestCaminoStandardTxExecutorRegisterNodeTx(t *testing.T) { "Not consortium member": { state: func(t *testing.T, c *gomock.Controller, utx *txs.RegisterNodeTx) *state.MockDiff { s := state.NewMockDiff(c) - s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(txs.AddressStateEmpty, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateEmpty, nil) return s }, utx: func() *txs.RegisterNodeTx { @@ -4357,7 +4358,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr2, nil) return s @@ -4379,7 +4380,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4405,7 +4406,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4433,7 +4434,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4463,7 +4464,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(nodeAddr1, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.NodeOwnerAddress}, nil) @@ -4509,7 +4510,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(ids.ShortEmpty, database.ErrNotFound) s.EXPECT().GetShortIDLink(ids.ShortID(utx.NewNodeID), state.ShortLinkKeyRegisterNode). @@ -4556,7 +4557,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.NodeOwnerAddress).Return(as.AddressStateConsortiumMember, nil) s.EXPECT().GetShortIDLink(utx.NodeOwnerAddress, state.ShortLinkKeyRegisterNode). Return(ids.ShortEmpty, database.ErrNotFound) s.EXPECT().GetShortIDLink(ids.ShortID(utx.NewNodeID), state.ShortLinkKeyRegisterNode). @@ -4974,7 +4975,7 @@ func TestCaminoStandardTxExecutorSuspendValidator(t *testing.T) { } }, preExecute: func(t *testing.T, tx *txs.Tx, state state.State) { - state.SetAddressStates(caminoPreFundedKeys[1].Address(), txs.AddressStateRoleKYC) + state.SetAddressStates(caminoPreFundedKeys[1].Address(), as.AddressStateRoleKYC) }, expectedErr: errAddrStateNotPermitted, }, @@ -5053,7 +5054,7 @@ func TestCaminoStandardTxExecutorSuspendValidator(t *testing.T) { tx, err := env.txBuilder.NewAddressStateTx( setAddressStateArgs.address, setAddressStateArgs.remove, - txs.AddressStateBitNodeDeferred, + as.AddressStateBitNodeDeferred, setAddressStateArgs.keys, setAddressStateArgs.changeAddr, ) @@ -5622,7 +5623,7 @@ func TestCaminoStandardTxExecutorAddDepositOfferTx(t *testing.T) { s.EXPECT().GetTimestamp().Return(time.Unix(100, 0)) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(txs.AddressStateEmpty, nil) + s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(as.AddressStateEmpty, nil) return s }, utx: func() *txs.AddDepositOfferTx { @@ -5644,7 +5645,7 @@ func TestCaminoStandardTxExecutorAddDepositOfferTx(t *testing.T) { s.EXPECT().GetTimestamp().Return(time.Unix(100, 0)) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(txs.AddressStateOffersCreator, nil) + s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(as.AddressStateOffersCreator, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.DepositOfferCreatorAddress}, nil) return s }, @@ -5669,7 +5670,7 @@ func TestCaminoStandardTxExecutorAddDepositOfferTx(t *testing.T) { s.EXPECT().GetTimestamp().Return(chainTime) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(txs.AddressStateOffersCreator, nil) + s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(as.AddressStateOffersCreator, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.DepositOfferCreatorAddress}, nil) s.EXPECT().GetCurrentSupply(constants.PrimaryNetworkID). Return(cfg.RewardConfig.SupplyCap-offer1.TotalMaxRewardAmount+1, nil) @@ -5740,7 +5741,7 @@ func TestCaminoStandardTxExecutorAddDepositOfferTx(t *testing.T) { s.EXPECT().GetTimestamp().Return(chainTime) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(txs.AddressStateOffersCreator, nil) + s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(as.AddressStateOffersCreator, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.DepositOfferCreatorAddress}, nil) s.EXPECT().GetCurrentSupply(constants.PrimaryNetworkID).Return(currentSupply, nil) s.EXPECT().GetAllDepositOffers().Return(existingOffers, nil) @@ -5766,7 +5767,7 @@ func TestCaminoStandardTxExecutorAddDepositOfferTx(t *testing.T) { s.EXPECT().GetTimestamp().Return(time.Time{}) s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) expectVerifyLock(t, s, utx.Ins, []*avax.UTXO{feeUTXO}, []ids.ShortID{feeOwnerAddr}, nil) - s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(txs.AddressStateOffersCreator, nil) + s.EXPECT().GetAddressStates(utx.DepositOfferCreatorAddress).Return(as.AddressStateOffersCreator, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.DepositOfferCreatorAddress}, nil) s.EXPECT().GetCurrentSupply(constants.PrimaryNetworkID). Return(cfg.RewardConfig.SupplyCap-offer1.TotalMaxRewardAmount, nil) @@ -5837,6 +5838,8 @@ func TestCaminoStandardTxExecutorAddProposalTx(t *testing.T) { feeUTXO := generateTestUTXO(ids.ID{1, 2, 3, 4, 5}, ctx.AVAXAssetID, defaultTxFee, feeOwner, ids.Empty, ids.Empty) bondUTXO := generateTestUTXO(ids.ID{1, 2, 3, 4, 6}, ctx.AVAXAssetID, proposalBondAmt, bondOwner, ids.Empty, ids.Empty) + applicantAddress := ids.ShortID{1, 1, 1} + proposalWrapper := &txs.ProposalWrapper{Proposal: &dac.BaseFeeProposal{ Start: 100, End: 101, Options: []uint64{1}, }} @@ -5995,6 +5998,61 @@ func TestCaminoStandardTxExecutorAddProposalTx(t *testing.T) { }, expectedErr: errProposalToFarInFuture, }, + "Wrong admin proposal type": { + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddProposalTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) + s.EXPECT().GetTimestamp().Return(cfg.BerlinPhaseTime) + return s + }, + utx: func(cfg *config.Config) *txs.AddProposalTx { + proposalWrapper := &txs.ProposalWrapper{Proposal: &dac.AdminProposal{ + Proposal: &dac.BaseFeeProposal{ + Start: 100, End: 100 + dac.AddMemberProposalDuration, Options: []uint64{1}, + }, + }} + proposalBytes, err := txs.Codec.Marshal(txs.Version, proposalWrapper) + require.NoError(t, err) + return &txs.AddProposalTx{ + BaseTx: *baseTxWithBondAmt(cfg.CaminoConfig.DACProposalBondAmount), + ProposalPayload: proposalBytes, + ProposerAddress: proposerAddr, + ProposerAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + } + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {bondOwnerKey}, {bondOwnerKey}, + }, + expectedErr: errWrongAdminProposal, + }, + "Admin proposal proposer doesn't have required address state": { + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddProposalTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + s := state.NewMockDiff(c) + s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) + s.EXPECT().GetTimestamp().Return(cfg.BerlinPhaseTime) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateEmpty, nil) + return s + }, + utx: func(cfg *config.Config) *txs.AddProposalTx { + proposalWrapper := &txs.ProposalWrapper{Proposal: &dac.AdminProposal{ + Proposal: &dac.AddMemberProposal{ + Start: 100, End: 100 + dac.AddMemberProposalDuration, ApplicantAddress: applicantAddress, + }, + }} + proposalBytes, err := txs.Codec.Marshal(txs.Version, proposalWrapper) + require.NoError(t, err) + return &txs.AddProposalTx{ + BaseTx: *baseTxWithBondAmt(cfg.CaminoConfig.DACProposalBondAmount), + ProposalPayload: proposalBytes, + ProposerAddress: proposerAddr, + ProposerAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + } + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {bondOwnerKey}, {bondOwnerKey}, + }, + expectedErr: errNotPermittedToCreateProposal, + }, "Wrong proposer credential": { state: func(t *testing.T, c *gomock.Controller, utx *txs.AddProposalTx, txID ids.ID, cfg *config.Config) *state.MockDiff { s := state.NewMockDiff(c) @@ -6023,7 +6081,7 @@ func TestCaminoStandardTxExecutorAddProposalTx(t *testing.T) { s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) s.EXPECT().GetTimestamp().Return(cfg.BerlinPhaseTime) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.ProposerAddress}, nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(txs.AddressStateEmpty, nil) // not AddressStateCaminoProposer + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateEmpty, nil) // not AddressStateCaminoProposer return s }, utx: func(cfg *config.Config) *txs.AddProposalTx { @@ -6069,7 +6127,7 @@ func TestCaminoStandardTxExecutorAddProposalTx(t *testing.T) { proposalsIterator.EXPECT().Release() proposalsIterator.EXPECT().Error().Return(nil) - s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(txs.AddressStateCaminoProposer, nil) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateCaminoProposer, nil) s.EXPECT().GetProposalIterator().Return(proposalsIterator, nil) // * @@ -6102,6 +6160,61 @@ func TestCaminoStandardTxExecutorAddProposalTx(t *testing.T) { {feeOwnerKey}, {bondOwnerKey}, {proposerKey}, }, }, + "OK: Admin proposal": { + state: func(t *testing.T, c *gomock.Controller, utx *txs.AddProposalTx, txID ids.ID, cfg *config.Config) *state.MockDiff { + proposal, err := utx.Proposal() + require.NoError(t, err) + proposalState, err := proposal.CreateFinishedProposalState(1) + require.NoError(t, err) + s := state.NewMockDiff(c) + s.EXPECT().CaminoConfig().Return(caminoStateConf, nil) + s.EXPECT().GetTimestamp().Return(cfg.BerlinPhaseTime) + s.EXPECT().GetAddressStates(utx.ProposerAddress).Return(as.AddressStateRoleConsortiumAdminProposer, nil) + expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.ProposerAddress}, nil) + + // * proposal verifier + proposalsIterator := state.NewMockProposalsIterator(c) + proposalsIterator.EXPECT().Next().Return(false) + proposalsIterator.EXPECT().Release() + proposalsIterator.EXPECT().Error().Return(nil) + + s.EXPECT().GetAddressStates(applicantAddress).Return(as.AddressStateEmpty, nil) + s.EXPECT().GetProposalIterator().Return(proposalsIterator, nil) + // * + + s.EXPECT().GetBaseFee().Return(defaultTxFee, nil) + expectVerifyLock(t, s, utx.Ins, + []*avax.UTXO{feeUTXO, bondUTXO}, + []ids.ShortID{ + feeOwnerAddr, bondOwnerAddr, // consumed + bondOwnerAddr, // produced + }, nil) + s.EXPECT().AddProposal(txID, proposalState) + s.EXPECT().AddProposalIDToFinish(txID) + expectConsumeUTXOs(t, s, utx.Ins) + expectProduceNewlyLockedUTXOs(t, s, utx.Outs, txID, 0, locked.StateBonded) + return s + }, + utx: func(cfg *config.Config) *txs.AddProposalTx { + proposalWrapper := &txs.ProposalWrapper{Proposal: &dac.AdminProposal{ + OptionIndex: 1, + Proposal: &dac.AddMemberProposal{ + Start: 100, End: 100 + dac.AddMemberProposalDuration, ApplicantAddress: applicantAddress, + }, + }} + proposalBytes, err := txs.Codec.Marshal(txs.Version, proposalWrapper) + require.NoError(t, err) + return &txs.AddProposalTx{ + BaseTx: *baseTxWithBondAmt(cfg.CaminoConfig.DACProposalBondAmount), + ProposalPayload: proposalBytes, + ProposerAddress: proposerAddr, + ProposerAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + } + }, + signers: [][]*secp256k1.PrivateKey{ + {feeOwnerKey}, {bondOwnerKey}, {proposerKey}, + }, + }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { @@ -6294,7 +6407,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(txs.AddressStateEmpty, nil) // not AddressStateConsortiumMember + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateEmpty, nil) // not AddressStateConsortiumMember return s }, utx: func(cfg *config.Config) *txs.AddVoteTx { @@ -6317,7 +6430,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, nil) expectVerifyMultisigPermission(t, s, []ids.ShortID{utx.VoterAddress}, nil) return s }, @@ -6341,7 +6454,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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) @@ -6370,7 +6483,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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) @@ -6399,7 +6512,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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) @@ -6425,7 +6538,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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) @@ -6458,7 +6571,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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 +6605,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(txs.AddressStateConsortiumMember, nil) + s.EXPECT().GetAddressStates(utx.VoterAddress).Return(as.AddressStateConsortiumMember, 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) From f3671829f882e74ce1b3e07d08a3b6e2e877c89f Mon Sep 17 00:00:00 2001 From: evlekht <> Date: Mon, 13 Nov 2023 17:21:34 +0400 Subject: [PATCH 3/3] [PVM] Forbid to change AddressStateBitConsortium with addressStateTx --- vms/platformvm/camino_vm_test.go | 43 ++++++++++++--- .../txs/executor/camino_tx_executor.go | 17 ++++-- .../txs/executor/camino_tx_executor_test.go | 54 ++++++++++++------- 3 files changed, 85 insertions(+), 29 deletions(-) diff --git a/vms/platformvm/camino_vm_test.go b/vms/platformvm/camino_vm_test.go index 7f109b4dea76..107a74909bcf 100644 --- a/vms/platformvm/camino_vm_test.go +++ b/vms/platformvm/camino_vm_test.go @@ -47,6 +47,8 @@ func TestRemoveDeferredValidator(t *testing.T) { nodeKey, nodeID := nodeid.GenerateCaminoNodeKeyAndID() + rootAdminKey := caminoPreFundedKeys[0] + adminProposerKey := caminoPreFundedKeys[0] consortiumMemberKey, err := testKeyFactory.NewPrivateKey() require.NoError(err) @@ -81,14 +83,28 @@ func TestRemoveDeferredValidator(t *testing.T) { // Set consortium member tx, err := vm.txBuilder.NewAddressStateTx( - consortiumMemberKey.Address(), + adminProposerKey.Address(), false, - as.AddressStateBitConsortium, - []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, + as.AddressStateBitRoleConsortiumAdminProposer, + []*secp256k1.PrivateKey{rootAdminKey}, outputOwners, ) require.NoError(err) _ = buildAndAcceptBlock(t, vm, tx) + proposalTx := buildAddMemberProposalTx( + t, + vm, + caminoPreFundedKeys[0], + vm.Config.CaminoConfig.DACProposalBondAmount, + defaultTxFee, + adminProposerKey, // AdminProposer + consortiumMemberKey.Address(), + vm.clock.Time(), + true, + ) + _, _, _, _ = makeProposalWithTx(t, vm, proposalTx) // add admin proposal + _ = buildAndAcceptBlock(t, vm, nil) // execute admin proposal + // Register node tx, err = vm.txBuilder.NewRegisterNodeTx( ids.EmptyNodeID, @@ -217,6 +233,8 @@ func TestRemoveReactivatedValidator(t *testing.T) { nodeKey, nodeID := nodeid.GenerateCaminoNodeKeyAndID() + rootAdminKey := caminoPreFundedKeys[0] + adminProposerKey := caminoPreFundedKeys[0] consortiumMemberKey, err := testKeyFactory.NewPrivateKey() require.NoError(err) @@ -251,14 +269,27 @@ func TestRemoveReactivatedValidator(t *testing.T) { // Set consortium member tx, err := vm.txBuilder.NewAddressStateTx( - consortiumMemberKey.Address(), + adminProposerKey.Address(), false, - as.AddressStateBitConsortium, - []*secp256k1.PrivateKey{caminoPreFundedKeys[0]}, + as.AddressStateBitRoleConsortiumAdminProposer, + []*secp256k1.PrivateKey{rootAdminKey}, outputOwners, ) require.NoError(err) _ = buildAndAcceptBlock(t, vm, tx) + proposalTx := buildAddMemberProposalTx( + t, + vm, + caminoPreFundedKeys[0], + vm.Config.CaminoConfig.DACProposalBondAmount, + defaultTxFee, + adminProposerKey, // AdminProposer + consortiumMemberKey.Address(), + vm.clock.Time(), + true, + ) + _, _, _, _ = makeProposalWithTx(t, vm, proposalTx) // add admin proposal + _ = buildAndAcceptBlock(t, vm, nil) // execute admin proposal // Register node tx, err = vm.txBuilder.NewRegisterNodeTx( diff --git a/vms/platformvm/txs/executor/camino_tx_executor.go b/vms/platformvm/txs/executor/camino_tx_executor.go index 9b09d4150438..5a18bfe88c5a 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor.go +++ b/vms/platformvm/txs/executor/camino_tx_executor.go @@ -2149,8 +2149,11 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error roles := as.AddressStateEmpty creds := e.Tx.Creds + chainTime := e.State.GetTimestamp() + isAthensPhase := e.Config.IsAthensPhaseActivated(chainTime) + if tx.UpgradeVersionID.Version() > 0 { - if !e.Config.IsAthensPhaseActivated(e.State.GetTimestamp()) { + if !isAthensPhase { return errNotAthensPhase } if err = e.Backend.Fx.VerifyMultisigPermission( @@ -2199,9 +2202,15 @@ func (e *CaminoStandardTxExecutor) AddressStateTx(tx *txs.AddressStateTx) error } statesBit := as.AddressState(1) << tx.State - // Check for AthensPhase Bits if we time has not passed yet - if (statesBit&as.AddressStateAthensPhaseBits) != 0 && - !e.Config.IsAthensPhaseActivated(e.State.GetTimestamp()) { + // 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 } diff --git a/vms/platformvm/txs/executor/camino_tx_executor_test.go b/vms/platformvm/txs/executor/camino_tx_executor_test.go index f9d1e079302d..3178784ec32b 100644 --- a/vms/platformvm/txs/executor/camino_tx_executor_test.go +++ b/vms/platformvm/txs/executor/camino_tx_executor_test.go @@ -1659,7 +1659,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { targetAddress: bob, txFlag: as.AddressStateBitRoleKYC, existingState: as.AddressStateRoleKYC, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, // Bob has KYC role, and he is trying to give himself Admin role @@ -1668,7 +1668,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { targetAddress: bob, txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateRoleKYC, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, // Bob has Admin role, and he is trying to give Alice Admin role @@ -1714,7 +1714,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateRoleAdmin, expectedState: 0, - expectedErrs: []error{errAdminCannotBeDeleted, errAdminCannotBeDeleted}, + expectedErrs: []error{errAdminCannotBeDeleted, errAdminCannotBeDeleted, errAdminCannotBeDeleted}, remove: true, }, // Bob has Admin role, and he is trying to give Alice the KYC Verified state @@ -1743,6 +1743,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { 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": { @@ -1768,7 +1769,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { targetAddress: alice, txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, // An Empty Address has Admin role, and he is trying to give Alice Admin role @@ -1777,7 +1778,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { targetAddress: alice, txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted, errAddrStateNotPermitted}, remove: false, }, // Bob has Admin role, and he is trying to give Admin role to an Empty Address @@ -1786,7 +1787,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { targetAddress: ids.ShortEmpty, txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateRoleAdmin, - expectedErrs: []error{txs.ErrEmptyAddress, txs.ErrEmptyAddress}, + expectedErrs: []error{txs.ErrEmptyAddress, txs.ErrEmptyAddress, txs.ErrEmptyAddress}, remove: false, }, // Bob has empty addr state, and he is trying to give Alice Admin role @@ -1796,7 +1797,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateEmpty, remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1805,7 +1806,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitRoleAdmin, existingState: as.AddressStateEmpty, remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1814,7 +1815,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitRoleKYC, existingState: as.AddressStateEmpty, remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1823,7 +1824,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitRoleKYC, existingState: as.AddressStateEmpty, remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1832,7 +1833,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitKYCVerified, existingState: as.AddressStateEmpty, remove: false, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1841,7 +1842,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitKYCVerified, existingState: as.AddressStateEmpty, remove: true, - expectedErrs: []error{errAddrStateNotPermitted, errAddrStateNotPermitted}, + 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": { @@ -1864,7 +1865,7 @@ func TestAddAddressStateTxExecutor(t *testing.T) { txFlag: as.AddressStateBitKYCExpired, existingState: as.AddressStateRoleKYC, expectedState: as.AddressStateKYCExpired, - expectedErrs: []error{errNotAthensPhase, errSignatureMissing}, + expectedErrs: []error{errNotAthensPhase, errSignatureMissing, errSignatureMissing}, remove: false, executor: alice, executorAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, @@ -1882,15 +1883,30 @@ func TestAddAddressStateTxExecutor(t *testing.T) { }, }} - athensPhaseTimes := []time.Time{ - env.state.GetTimestamp().Add(24 * time.Hour), // AthensPhase not yet active (> chainTime) - env.state.GetTimestamp(), // AthensPhase active (<= chainTime) + chainTime := env.state.GetTimestamp() + + 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" + }, } - for phase := 0; phase < 2; phase++ { - env.config.AthensPhaseTime = athensPhaseTimes[phase] + for phase := 0; phase < 3; phase++ { + phaseName := fnSetPhaseTimes[phase]() for name, tt := range tests { - t.Run(fmt.Sprintf("Phase %d; %s", phase, name), func(t *testing.T) { + t.Run(fmt.Sprintf("%s; %s", phaseName, name), func(t *testing.T) { addressStateTx := &txs.AddressStateTx{ UpgradeVersionID: codec.BuildUpgradeVersionID(tt.UpgradeVersion), BaseTx: baseTx,