diff --git a/vms/platformvm/client.go b/vms/platformvm/client.go index 54554f37d92b..505c545bdded 100644 --- a/vms/platformvm/client.go +++ b/vms/platformvm/client.go @@ -235,7 +235,8 @@ type GetSubnetClientResponse struct { Locktime uint64 // subnet transformation tx ID for a permissionless subnet SubnetTransformationTxID ids.ID - // subnet manager information for a permissionless L1 + // subnet conversion information for an L1 + ConversionID ids.ID ManagerChainID ids.ID ManagerAddress []byte } @@ -259,6 +260,7 @@ func (c *client) GetSubnet(ctx context.Context, subnetID ids.ID, options ...rpc. Threshold: uint32(res.Threshold), Locktime: uint64(res.Locktime), SubnetTransformationTxID: res.SubnetTransformationTxID, + ConversionID: res.ConversionID, ManagerChainID: res.ManagerChainID, ManagerAddress: res.ManagerAddress, }, nil diff --git a/vms/platformvm/config/execution_config.go b/vms/platformvm/config/execution_config.go index fafdaf9b99d2..e5bef1637d05 100644 --- a/vms/platformvm/config/execution_config.go +++ b/vms/platformvm/config/execution_config.go @@ -21,7 +21,7 @@ var DefaultExecutionConfig = ExecutionConfig{ ChainDBCacheSize: 2048, BlockIDCacheSize: 8192, FxOwnerCacheSize: 4 * units.MiB, - SubnetManagerCacheSize: 4 * units.MiB, + SubnetConversionCacheSize: 4 * units.MiB, ChecksumsEnabled: false, MempoolPruneFrequency: 30 * time.Minute, } @@ -37,7 +37,7 @@ type ExecutionConfig struct { ChainDBCacheSize int `json:"chain-db-cache-size"` BlockIDCacheSize int `json:"block-id-cache-size"` FxOwnerCacheSize int `json:"fx-owner-cache-size"` - SubnetManagerCacheSize int `json:"subnet-manager-cache-size"` + SubnetConversionCacheSize int `json:"subnet-conversion-cache-size"` ChecksumsEnabled bool `json:"checksums-enabled"` MempoolPruneFrequency time.Duration `json:"mempool-prune-frequency"` } diff --git a/vms/platformvm/config/execution_config_test.go b/vms/platformvm/config/execution_config_test.go index 5929a75f9063..c938c177add3 100644 --- a/vms/platformvm/config/execution_config_test.go +++ b/vms/platformvm/config/execution_config_test.go @@ -89,7 +89,7 @@ func TestExecutionConfigUnmarshal(t *testing.T) { ChainDBCacheSize: 7, BlockIDCacheSize: 8, FxOwnerCacheSize: 9, - SubnetManagerCacheSize: 10, + SubnetConversionCacheSize: 10, ChecksumsEnabled: true, MempoolPruneFrequency: time.Minute, } diff --git a/vms/platformvm/service.go b/vms/platformvm/service.go index 91b7810d30df..22153a97ece6 100644 --- a/vms/platformvm/service.go +++ b/vms/platformvm/service.go @@ -439,7 +439,8 @@ type GetSubnetResponse struct { Locktime avajson.Uint64 `json:"locktime"` // subnet transformation tx ID for an elastic subnet SubnetTransformationTxID ids.ID `json:"subnetTransformationTxID"` - // subnet manager information for a permissionless L1 + // subnet conversion information for an L1 + ConversionID ids.ID `json:"conversionID"` ManagerChainID ids.ID `json:"managerChainID"` ManagerAddress types.JSONByteSlice `json:"managerAddress"` } @@ -490,12 +491,14 @@ func (s *Service) GetSubnet(_ *http.Request, args *GetSubnetArgs, response *GetS return err } - switch chainID, addr, err := s.vm.state.GetSubnetManager(args.SubnetID); err { + switch c, err := s.vm.state.GetSubnetConversion(args.SubnetID); err { case nil: response.IsPermissioned = false - response.ManagerChainID = chainID - response.ManagerAddress = addr + response.ConversionID = c.ConversionID + response.ManagerChainID = c.ChainID + response.ManagerAddress = c.Addr case database.ErrNotFound: + response.ConversionID = ids.Empty response.ManagerChainID = ids.Empty response.ManagerAddress = []byte(nil) default: diff --git a/vms/platformvm/service.md b/vms/platformvm/service.md index f4cd28cd35f1..dbb12907694b 100644 --- a/vms/platformvm/service.md +++ b/vms/platformvm/service.md @@ -1204,7 +1204,7 @@ Testnet: U8iRqJoiJm8xZHAacmvYyZVwqQx6uDNtQeP3CQ6fcgQk3JqnK ### `platform.getSubnet` -Get owners and elastic info about the Subnet. +Get owners and info about the Subnet or L1. **Signature:** @@ -1217,7 +1217,10 @@ platform.getSubnet({ controlKeys: []string, threshold: string, locktime: string, - subnetTransformationTxID: string + subnetTransformationTxID: string, + conversionID: string, + managerChainID: string, + managerAddress: string } ``` @@ -1226,8 +1229,10 @@ platform.getSubnet({ a permissioned subnet. If the Subnet is a PoS Subnet, then `threshold` will be `0` and `controlKeys` will be empty. - changes can not be made into the subnet until `locktime` is in the past. -- `subnetTransformationTxID` is the ID of the transaction that changed the subnet into a elastic one, - for when this change was performed. +- `subnetTransformationTxID` is the ID of the transaction that changed the subnet into an elastic one, if it exists. +- `conversionID` is the ID of the conversion from a permissioned Subnet into an L1, if it exists. +- `managerChainID` is the ChainID that has the ability to modify this L1s validator set, if it exists. +- `managerAddress` is the address that has the ability to modify this L1s validator set, if it exists. **Example Call:** @@ -1250,7 +1255,10 @@ curl -X POST --data '{ "controlKeys": ["P-fuji1ztvstx6naeg6aarfd047fzppdt8v4gsah88e0c","P-fuji193kvt4grqewv6ce2x59wnhydr88xwdgfcedyr3"], "threshold": "1", "locktime": "0", - "subnetTransformationTxID": "11111111111111111111111111111111LpoYY" + "subnetTransformationTxID": "11111111111111111111111111111111LpoYY", + "conversionID": "11111111111111111111111111111111LpoYY", + "managerChainID": "11111111111111111111111111111111LpoYY", + "managerAddress": null }, "id": 1 } diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 24bdabfa96da..9fe6a62363c0 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -52,8 +52,8 @@ type diff struct { addedSubnetIDs []ids.ID // Subnet ID --> Owner of the subnet subnetOwners map[ids.ID]fx.Owner - // Subnet ID --> Manager of the subnet - subnetManagers map[ids.ID]chainIDAndAddr + // Subnet ID --> Conversion of the subnet + subnetConversions map[ids.ID]SubnetConversion // Subnet ID --> Tx that transforms the subnet transformedSubnets map[ids.ID]*txs.Tx @@ -76,14 +76,14 @@ func NewDiff( return nil, fmt.Errorf("%w: %s", ErrMissingParentState, parentID) } return &diff{ - parentID: parentID, - stateVersions: stateVersions, - timestamp: parentState.GetTimestamp(), - feeState: parentState.GetFeeState(), - accruedFees: parentState.GetAccruedFees(), - expiryDiff: newExpiryDiff(), - subnetOwners: make(map[ids.ID]fx.Owner), - subnetManagers: make(map[ids.ID]chainIDAndAddr), + parentID: parentID, + stateVersions: stateVersions, + timestamp: parentState.GetTimestamp(), + feeState: parentState.GetFeeState(), + accruedFees: parentState.GetAccruedFees(), + expiryDiff: newExpiryDiff(), + subnetOwners: make(map[ids.ID]fx.Owner), + subnetConversions: make(map[ids.ID]SubnetConversion), }, nil } @@ -357,24 +357,21 @@ func (d *diff) SetSubnetOwner(subnetID ids.ID, owner fx.Owner) { d.subnetOwners[subnetID] = owner } -func (d *diff) GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) { - if manager, exists := d.subnetManagers[subnetID]; exists { - return manager.ChainID, manager.Addr, nil +func (d *diff) GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) { + if c, ok := d.subnetConversions[subnetID]; ok { + return c, nil } - // If the subnet manager was not assigned in this diff, ask the parent state. + // If the subnet conversion was not assigned in this diff, ask the parent state. parentState, ok := d.stateVersions.GetState(d.parentID) if !ok { - return ids.Empty, nil, ErrMissingParentState + return SubnetConversion{}, ErrMissingParentState } - return parentState.GetSubnetManager(subnetID) + return parentState.GetSubnetConversion(subnetID) } -func (d *diff) SetSubnetManager(subnetID ids.ID, chainID ids.ID, addr []byte) { - d.subnetManagers[subnetID] = chainIDAndAddr{ - ChainID: chainID, - Addr: addr, - } +func (d *diff) SetSubnetConversion(subnetID ids.ID, c SubnetConversion) { + d.subnetConversions[subnetID] = c } func (d *diff) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) { @@ -576,8 +573,8 @@ func (d *diff) Apply(baseState Chain) error { for subnetID, owner := range d.subnetOwners { baseState.SetSubnetOwner(subnetID, owner) } - for subnetID, manager := range d.subnetManagers { - baseState.SetSubnetManager(subnetID, manager.ChainID, manager.Addr) + for subnetID, c := range d.subnetConversions { + baseState.SetSubnetConversion(subnetID, c) } return nil } diff --git a/vms/platformvm/state/diff_test.go b/vms/platformvm/state/diff_test.go index 3b091cd67a69..013a918b60fa 100644 --- a/vms/platformvm/state/diff_test.go +++ b/vms/platformvm/state/diff_test.go @@ -772,46 +772,44 @@ func TestDiffSubnetOwner(t *testing.T) { require.Equal(owner2, owner) } -func TestDiffSubnetManager(t *testing.T) { +func TestDiffSubnetConversion(t *testing.T) { var ( - require = require.New(t) - state = newTestState(t, memdb.New()) - newManager = chainIDAndAddr{ids.GenerateTestID(), []byte{1, 2, 3, 4}} - subnetID = ids.GenerateTestID() + require = require.New(t) + state = newTestState(t, memdb.New()) + subnetID = ids.GenerateTestID() + expectedConversion = SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: []byte{1, 2, 3, 4}, + } ) - chainID, addr, err := state.GetSubnetManager(subnetID) + actualConversion, err := state.GetSubnetConversion(subnetID) require.ErrorIs(err, database.ErrNotFound) - require.Equal(ids.Empty, chainID) - require.Nil(addr) + require.Zero(actualConversion) d, err := NewDiffOn(state) require.NoError(err) - chainID, addr, err = d.GetSubnetManager(subnetID) + actualConversion, err = d.GetSubnetConversion(subnetID) require.ErrorIs(err, database.ErrNotFound) - require.Equal(ids.Empty, chainID) - require.Nil(addr) + require.Zero(actualConversion) - // Setting a subnet manager should be reflected on diff not state - d.SetSubnetManager(subnetID, newManager.ChainID, newManager.Addr) - chainID, addr, err = d.GetSubnetManager(subnetID) + // Setting a subnet conversion should be reflected on diff not state + d.SetSubnetConversion(subnetID, expectedConversion) + actualConversion, err = d.GetSubnetConversion(subnetID) require.NoError(err) - require.Equal(newManager.ChainID, chainID) - require.Equal(newManager.Addr, addr) + require.Equal(expectedConversion, actualConversion) - chainID, addr, err = state.GetSubnetManager(subnetID) + actualConversion, err = state.GetSubnetConversion(subnetID) require.ErrorIs(err, database.ErrNotFound) - require.Equal(ids.Empty, chainID) - require.Nil(addr) + require.Zero(actualConversion) - // State should reflect new subnet manager after diff is applied + // State should reflect new subnet conversion after diff is applied require.NoError(d.Apply(state)) - - chainID, addr, err = state.GetSubnetManager(subnetID) + actualConversion, err = state.GetSubnetConversion(subnetID) require.NoError(err) - require.Equal(newManager.ChainID, chainID) - require.Equal(newManager.Addr, addr) + require.Equal(expectedConversion, actualConversion) } func TestDiffStacking(t *testing.T) { diff --git a/vms/platformvm/state/mock_chain.go b/vms/platformvm/state/mock_chain.go index 3b380a87a8b8..27daeae3a101 100644 --- a/vms/platformvm/state/mock_chain.go +++ b/vms/platformvm/state/mock_chain.go @@ -353,20 +353,19 @@ func (mr *MockChainMockRecorder) GetPendingValidator(subnetID, nodeID any) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockChain)(nil).GetPendingValidator), subnetID, nodeID) } -// GetSubnetManager mocks base method. -func (m *MockChain) GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) { +// GetSubnetConversion mocks base method. +func (m *MockChain) GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSubnetManager", subnetID) - ret0, _ := ret[0].(ids.ID) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "GetSubnetConversion", subnetID) + ret0, _ := ret[0].(SubnetConversion) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// GetSubnetManager indicates an expected call of GetSubnetManager. -func (mr *MockChainMockRecorder) GetSubnetManager(subnetID any) *gomock.Call { +// GetSubnetConversion indicates an expected call of GetSubnetConversion. +func (mr *MockChainMockRecorder) GetSubnetConversion(subnetID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetManager", reflect.TypeOf((*MockChain)(nil).GetSubnetManager), subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetConversion", reflect.TypeOf((*MockChain)(nil).GetSubnetConversion), subnetID) } // GetSubnetOwner mocks base method. @@ -573,16 +572,16 @@ func (mr *MockChainMockRecorder) SetFeeState(f any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeeState", reflect.TypeOf((*MockChain)(nil).SetFeeState), f) } -// SetSubnetManager mocks base method. -func (m *MockChain) SetSubnetManager(subnetID, chainID ids.ID, addr []byte) { +// SetSubnetConversion mocks base method. +func (m *MockChain) SetSubnetConversion(subnetID ids.ID, c SubnetConversion) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetSubnetManager", subnetID, chainID, addr) + m.ctrl.Call(m, "SetSubnetConversion", subnetID, c) } -// SetSubnetManager indicates an expected call of SetSubnetManager. -func (mr *MockChainMockRecorder) SetSubnetManager(subnetID, chainID, addr any) *gomock.Call { +// SetSubnetConversion indicates an expected call of SetSubnetConversion. +func (mr *MockChainMockRecorder) SetSubnetConversion(subnetID, c any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetManager", reflect.TypeOf((*MockChain)(nil).SetSubnetManager), subnetID, chainID, addr) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetConversion", reflect.TypeOf((*MockChain)(nil).SetSubnetConversion), subnetID, c) } // SetSubnetOwner mocks base method. diff --git a/vms/platformvm/state/mock_diff.go b/vms/platformvm/state/mock_diff.go index 77edfde92aaf..8732fc49b406 100644 --- a/vms/platformvm/state/mock_diff.go +++ b/vms/platformvm/state/mock_diff.go @@ -367,20 +367,19 @@ func (mr *MockDiffMockRecorder) GetPendingValidator(subnetID, nodeID any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockDiff)(nil).GetPendingValidator), subnetID, nodeID) } -// GetSubnetManager mocks base method. -func (m *MockDiff) GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) { +// GetSubnetConversion mocks base method. +func (m *MockDiff) GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSubnetManager", subnetID) - ret0, _ := ret[0].(ids.ID) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "GetSubnetConversion", subnetID) + ret0, _ := ret[0].(SubnetConversion) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// GetSubnetManager indicates an expected call of GetSubnetManager. -func (mr *MockDiffMockRecorder) GetSubnetManager(subnetID any) *gomock.Call { +// GetSubnetConversion indicates an expected call of GetSubnetConversion. +func (mr *MockDiffMockRecorder) GetSubnetConversion(subnetID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetManager", reflect.TypeOf((*MockDiff)(nil).GetSubnetManager), subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetConversion", reflect.TypeOf((*MockDiff)(nil).GetSubnetConversion), subnetID) } // GetSubnetOwner mocks base method. @@ -587,16 +586,16 @@ func (mr *MockDiffMockRecorder) SetFeeState(f any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeeState", reflect.TypeOf((*MockDiff)(nil).SetFeeState), f) } -// SetSubnetManager mocks base method. -func (m *MockDiff) SetSubnetManager(subnetID, chainID ids.ID, addr []byte) { +// SetSubnetConversion mocks base method. +func (m *MockDiff) SetSubnetConversion(subnetID ids.ID, c SubnetConversion) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetSubnetManager", subnetID, chainID, addr) + m.ctrl.Call(m, "SetSubnetConversion", subnetID, c) } -// SetSubnetManager indicates an expected call of SetSubnetManager. -func (mr *MockDiffMockRecorder) SetSubnetManager(subnetID, chainID, addr any) *gomock.Call { +// SetSubnetConversion indicates an expected call of SetSubnetConversion. +func (mr *MockDiffMockRecorder) SetSubnetConversion(subnetID, c any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetManager", reflect.TypeOf((*MockDiff)(nil).SetSubnetManager), subnetID, chainID, addr) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetConversion", reflect.TypeOf((*MockDiff)(nil).SetSubnetConversion), subnetID, c) } // SetSubnetOwner mocks base method. diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index f602345688b1..a17593982572 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -557,6 +557,21 @@ func (mr *MockStateMockRecorder) GetStatelessBlock(blockID any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessBlock", reflect.TypeOf((*MockState)(nil).GetStatelessBlock), blockID) } +// GetSubnetConversion mocks base method. +func (m *MockState) GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetConversion", subnetID) + ret0, _ := ret[0].(SubnetConversion) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetConversion indicates an expected call of GetSubnetConversion. +func (mr *MockStateMockRecorder) GetSubnetConversion(subnetID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetConversion", reflect.TypeOf((*MockState)(nil).GetSubnetConversion), subnetID) +} + // GetSubnetIDs mocks base method. func (m *MockState) GetSubnetIDs() ([]ids.ID, error) { m.ctrl.T.Helper() @@ -572,22 +587,6 @@ func (mr *MockStateMockRecorder) GetSubnetIDs() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetIDs", reflect.TypeOf((*MockState)(nil).GetSubnetIDs)) } -// GetSubnetManager mocks base method. -func (m *MockState) GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSubnetManager", subnetID) - ret0, _ := ret[0].(ids.ID) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetSubnetManager indicates an expected call of GetSubnetManager. -func (mr *MockStateMockRecorder) GetSubnetManager(subnetID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetManager", reflect.TypeOf((*MockState)(nil).GetSubnetManager), subnetID) -} - // GetSubnetOwner mocks base method. func (m *MockState) GetSubnetOwner(subnetID ids.ID) (fx.Owner, error) { m.ctrl.T.Helper() @@ -846,16 +845,16 @@ func (mr *MockStateMockRecorder) SetLastAccepted(blkID any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastAccepted", reflect.TypeOf((*MockState)(nil).SetLastAccepted), blkID) } -// SetSubnetManager mocks base method. -func (m *MockState) SetSubnetManager(subnetID, chainID ids.ID, addr []byte) { +// SetSubnetConversion mocks base method. +func (m *MockState) SetSubnetConversion(subnetID ids.ID, c SubnetConversion) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetSubnetManager", subnetID, chainID, addr) + m.ctrl.Call(m, "SetSubnetConversion", subnetID, c) } -// SetSubnetManager indicates an expected call of SetSubnetManager. -func (mr *MockStateMockRecorder) SetSubnetManager(subnetID, chainID, addr any) *gomock.Call { +// SetSubnetConversion indicates an expected call of SetSubnetConversion. +func (mr *MockStateMockRecorder) SetSubnetConversion(subnetID, c any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetManager", reflect.TypeOf((*MockState)(nil).SetSubnetManager), subnetID, chainID, addr) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubnetConversion", reflect.TypeOf((*MockState)(nil).SetSubnetConversion), subnetID, c) } // SetSubnetOwner mocks base method. diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index f351bf5940b2..58c2056570bd 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -78,7 +78,7 @@ var ( UTXOPrefix = []byte("utxo") SubnetPrefix = []byte("subnet") SubnetOwnerPrefix = []byte("subnetOwner") - SubnetManagerPrefix = []byte("subnetManager") + SubnetConversionPrefix = []byte("subnetConversion") TransformedSubnetPrefix = []byte("transformedSubnet") SupplyPrefix = []byte("supply") ChainPrefix = []byte("chain") @@ -123,8 +123,8 @@ type Chain interface { GetSubnetOwner(subnetID ids.ID) (fx.Owner, error) SetSubnetOwner(subnetID ids.ID, owner fx.Owner) - GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) - SetSubnetManager(subnetID ids.ID, chainID ids.ID, addr []byte) + GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) + SetSubnetConversion(subnetID ids.ID, c SubnetConversion) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) AddSubnetTransformation(transformSubnetTx *txs.Tx) @@ -275,7 +275,9 @@ type stateBlk struct { * | '-. list * | '-- txID -> nil * |-. subnetOwners - * | '-. subnetID -> owner + * | '-- subnetID -> owner + * |-. subnetConversions + * | '-- subnetID -> conversionID + chainID + addr * |-. chains * | '-. subnetID * | '-. list @@ -364,9 +366,9 @@ type state struct { subnetOwnerCache cache.Cacher[ids.ID, fxOwnerAndSize] // cache of subnetID -> owner; if the entry is nil, it is not in the database subnetOwnerDB database.Database - subnetManagers map[ids.ID]chainIDAndAddr // map of subnetID -> manager of the subnet - subnetManagerCache cache.Cacher[ids.ID, chainIDAndAddr] // cache of subnetID -> manager - subnetManagerDB database.Database + subnetConversions map[ids.ID]SubnetConversion // map of subnetID -> conversion of the subnet + subnetConversionCache cache.Cacher[ids.ID, SubnetConversion] // cache of subnetID -> conversion + subnetConversionDB database.Database transformedSubnets map[ids.ID]*txs.Tx // map of subnetID -> transformSubnetTx transformedSubnetCache cache.Cacher[ids.ID, *txs.Tx] // cache of subnetID -> transformSubnetTx; if the entry is nil, it is not in the database @@ -439,9 +441,10 @@ type fxOwnerAndSize struct { size int } -type chainIDAndAddr struct { - ChainID ids.ID `serialize:"true"` - Addr []byte `serialize:"true"` +type SubnetConversion struct { + ConversionID ids.ID `serialize:"true"` + ChainID ids.ID `serialize:"true"` + Addr []byte `serialize:"true"` } func txSize(_ ids.ID, tx *txs.Tx) int { @@ -552,12 +555,12 @@ func New( return nil, err } - subnetManagerDB := prefixdb.New(SubnetManagerPrefix, baseDB) - subnetManagerCache, err := metercacher.New[ids.ID, chainIDAndAddr]( - "subnet_manager_cache", + subnetConversionDB := prefixdb.New(SubnetConversionPrefix, baseDB) + subnetConversionCache, err := metercacher.New[ids.ID, SubnetConversion]( + "subnet_conversion_cache", metricsReg, - cache.NewSizedLRU[ids.ID, chainIDAndAddr](execCfg.SubnetManagerCacheSize, func(_ ids.ID, f chainIDAndAddr) int { - return 2*ids.IDLen + len(f.Addr) + cache.NewSizedLRU[ids.ID, SubnetConversion](execCfg.SubnetConversionCacheSize, func(_ ids.ID, c SubnetConversion) int { + return 3*ids.IDLen + len(c.Addr) }), ) if err != nil { @@ -666,9 +669,9 @@ func New( subnetOwnerDB: subnetOwnerDB, subnetOwnerCache: subnetOwnerCache, - subnetManagers: make(map[ids.ID]chainIDAndAddr), - subnetManagerDB: subnetManagerDB, - subnetManagerCache: subnetManagerCache, + subnetConversions: make(map[ids.ID]SubnetConversion), + subnetConversionDB: subnetConversionDB, + subnetConversionCache: subnetConversionCache, transformedSubnets: make(map[ids.ID]*txs.Tx), transformedSubnetCache: transformedSubnetCache, @@ -856,33 +859,30 @@ func (s *state) SetSubnetOwner(subnetID ids.ID, owner fx.Owner) { s.subnetOwners[subnetID] = owner } -func (s *state) GetSubnetManager(subnetID ids.ID) (ids.ID, []byte, error) { - if chainIDAndAddr, exists := s.subnetManagers[subnetID]; exists { - return chainIDAndAddr.ChainID, chainIDAndAddr.Addr, nil +func (s *state) GetSubnetConversion(subnetID ids.ID) (SubnetConversion, error) { + if c, ok := s.subnetConversions[subnetID]; ok { + return c, nil } - if chainIDAndAddr, cached := s.subnetManagerCache.Get(subnetID); cached { - return chainIDAndAddr.ChainID, chainIDAndAddr.Addr, nil + if c, ok := s.subnetConversionCache.Get(subnetID); ok { + return c, nil } - chainIDAndAddrBytes, err := s.subnetManagerDB.Get(subnetID[:]) + bytes, err := s.subnetConversionDB.Get(subnetID[:]) if err != nil { - return ids.Empty, nil, err + return SubnetConversion{}, err } - var manager chainIDAndAddr - if _, err := block.GenesisCodec.Unmarshal(chainIDAndAddrBytes, &manager); err != nil { - return ids.Empty, nil, err + var c SubnetConversion + if _, err := block.GenesisCodec.Unmarshal(bytes, &c); err != nil { + return SubnetConversion{}, err } - s.subnetManagerCache.Put(subnetID, manager) - return manager.ChainID, manager.Addr, nil + s.subnetConversionCache.Put(subnetID, c) + return c, nil } -func (s *state) SetSubnetManager(subnetID ids.ID, chainID ids.ID, addr []byte) { - s.subnetManagers[subnetID] = chainIDAndAddr{ - ChainID: chainID, - Addr: addr, - } +func (s *state) SetSubnetConversion(subnetID ids.ID, c SubnetConversion) { + s.subnetConversions[subnetID] = c } func (s *state) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) { @@ -1765,7 +1765,7 @@ func (s *state) write(updateValidators bool, height uint64) error { s.writeUTXOs(), s.writeSubnets(), s.writeSubnetOwners(), - s.writeSubnetManagers(), + s.writeSubnetConversions(), s.writeTransformedSubnets(), s.writeSubnetSupplies(), s.writeChains(), @@ -1791,6 +1791,7 @@ func (s *state) Close() error { s.rewardUTXODB.Close(), s.utxoDB.Close(), s.subnetBaseDB.Close(), + s.subnetConversionDB.Close(), s.transformedSubnetDB.Close(), s.supplyDB.Close(), s.chainDB.Close(), @@ -2364,21 +2365,19 @@ func (s *state) writeSubnetOwners() error { return nil } -func (s *state) writeSubnetManagers() error { - for subnetID, manager := range s.subnetManagers { - subnetID := subnetID - manager := manager - delete(s.subnetManagers, subnetID) +func (s *state) writeSubnetConversions() error { + for subnetID, c := range s.subnetConversions { + delete(s.subnetConversions, subnetID) - managerBytes, err := block.GenesisCodec.Marshal(block.CodecVersion, &manager) + bytes, err := block.GenesisCodec.Marshal(block.CodecVersion, &c) if err != nil { - return fmt.Errorf("failed to marshal subnet manager: %w", err) + return fmt.Errorf("failed to marshal subnet conversion: %w", err) } - s.subnetManagerCache.Put(subnetID, manager) + s.subnetConversionCache.Put(subnetID, c) - if err := s.subnetManagerDB.Put(subnetID[:], managerBytes); err != nil { - return fmt.Errorf("failed to write subnet manager: %w", err) + if err := s.subnetConversionDB.Put(subnetID[:], bytes); err != nil { + return fmt.Errorf("failed to write subnet conversion: %w", err) } } return nil diff --git a/vms/platformvm/state/state_test.go b/vms/platformvm/state/state_test.go index 0f23a3ebc833..d29129500435 100644 --- a/vms/platformvm/state/state_test.go +++ b/vms/platformvm/state/state_test.go @@ -1323,52 +1323,46 @@ func TestStateSubnetOwner(t *testing.T) { require.Equal(owner2, owner) } -func TestStateSubnetManager(t *testing.T) { +func TestStateSubnetConversion(t *testing.T) { tests := []struct { name string - setup func(t *testing.T, s State, subnetID ids.ID, chainID ids.ID, addr []byte) + setup func(s *state, subnetID ids.ID, c SubnetConversion) }{ { name: "in-memory", - setup: func(_ *testing.T, s State, subnetID ids.ID, chainID ids.ID, addr []byte) { - s.SetSubnetManager(subnetID, chainID, addr) + setup: func(s *state, subnetID ids.ID, c SubnetConversion) { + s.SetSubnetConversion(subnetID, c) }, }, { name: "cache", - setup: func(t *testing.T, s State, subnetID ids.ID, chainID ids.ID, addr []byte) { - subnetManagerCache := s.(*state).subnetManagerCache - - require.Zero(t, subnetManagerCache.Len()) - subnetManagerCache.Put(subnetID, chainIDAndAddr{ - ChainID: chainID, - Addr: addr, - }) - require.Equal(t, 1, subnetManagerCache.Len()) + setup: func(s *state, subnetID ids.ID, c SubnetConversion) { + s.subnetConversionCache.Put(subnetID, c) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - require := require.New(t) - - initializedState := newTestState(t, memdb.New()) + var ( + require = require.New(t) + state = newTestState(t, memdb.New()) + subnetID = ids.GenerateTestID() + expectedConversion = SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: []byte{'a', 'd', 'd', 'r'}, + } + ) - subnetID := ids.GenerateTestID() - chainID, addr, err := initializedState.GetSubnetManager(subnetID) + actualConversion, err := state.GetSubnetConversion(subnetID) require.ErrorIs(err, database.ErrNotFound) - require.Equal(ids.Empty, chainID) - require.Nil(addr) - - expectedChainID := ids.GenerateTestID() - expectedAddr := []byte{'a', 'd', 'd', 'r'} + require.Zero(actualConversion) - test.setup(t, initializedState, subnetID, expectedChainID, expectedAddr) + test.setup(state, subnetID, expectedConversion) - chainID, addr, err = initializedState.GetSubnetManager(subnetID) + actualConversion, err = state.GetSubnetConversion(subnetID) require.NoError(err) - require.Equal(expectedChainID, chainID) - require.Equal(expectedAddr, addr) + require.Equal(expectedConversion, actualConversion) }) } } diff --git a/vms/platformvm/txs/executor/create_chain_test.go b/vms/platformvm/txs/executor/create_chain_test.go index 61fead2677a7..7f9919e4a8f5 100644 --- a/vms/platformvm/txs/executor/create_chain_test.go +++ b/vms/platformvm/txs/executor/create_chain_test.go @@ -286,7 +286,14 @@ func TestEtnaCreateChainTxInvalidWithManagedSubnet(t *testing.T) { builderDiff, err := state.NewDiffOn(stateDiff) require.NoError(err) - stateDiff.SetSubnetManager(subnetID, ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'}) + stateDiff.SetSubnetConversion( + subnetID, + state.SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: []byte("address"), + }, + ) feeCalculator := state.PickFeeCalculator(env.config, builderDiff) executor := StandardTxExecutor{ diff --git a/vms/platformvm/txs/executor/staker_tx_verification.go b/vms/platformvm/txs/executor/staker_tx_verification.go index d458c01ab259..72ef8561ad43 100644 --- a/vms/platformvm/txs/executor/staker_tx_verification.go +++ b/vms/platformvm/txs/executor/staker_tx_verification.go @@ -308,7 +308,7 @@ func verifyRemoveSubnetValidatorTx( } if backend.Config.UpgradeConfig.IsEtnaActivated(currentTimestamp) { - _, _, err := chainState.GetSubnetManager(tx.Subnet) + _, err := chainState.GetSubnetConversion(tx.Subnet) if err == nil { return nil, false, fmt.Errorf("%w: %q", ErrRemoveValidatorManagedSubnet, tx.Subnet) } diff --git a/vms/platformvm/txs/executor/standard_tx_executor.go b/vms/platformvm/txs/executor/standard_tx_executor.go index d0a37f5ba82c..1c08880a94e0 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor.go +++ b/vms/platformvm/txs/executor/standard_tx_executor.go @@ -541,8 +541,16 @@ func (e *StandardTxExecutor) ConvertSubnetTx(tx *txs.ConvertSubnetTx) error { avax.Consume(e.State, tx.Ins) // Produce the UTXOS avax.Produce(e.State, txID, tx.Outs) - // Set the new Subnet manager in the database - e.State.SetSubnetManager(tx.Subnet, tx.ChainID, tx.Address) + // Track the subnet conversion in the database + e.State.SetSubnetConversion( + tx.Subnet, + state.SubnetConversion{ + // TODO: Populate the conversionID + ConversionID: ids.Empty, + ChainID: tx.ChainID, + Addr: tx.Address, + }, + ) return nil } diff --git a/vms/platformvm/txs/executor/standard_tx_executor_test.go b/vms/platformvm/txs/executor/standard_tx_executor_test.go index e11ad73ac5d9..0301748101bc 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor_test.go +++ b/vms/platformvm/txs/executor/standard_tx_executor_test.go @@ -913,7 +913,14 @@ func TestEtnaStandardTxExecutorAddSubnetValidator(t *testing.T) { onAcceptState, err := state.NewDiff(lastAcceptedID, env) require.NoError(err) - onAcceptState.SetSubnetManager(subnetID, ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'}) + onAcceptState.SetSubnetConversion( + subnetID, + state.SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: []byte("address"), + }, + ) executor := StandardTxExecutor{ Backend: &env.backend, @@ -1991,10 +1998,17 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { expectedErr: ErrFlowCheckFailed, }, { - name: "attempted to remove subnet validator after subnet manager is set", + name: "attempted to remove subnet validator after subnet conversion has occurred", newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) - env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'}, nil).AnyTimes() + env.state.EXPECT().GetSubnetConversion(env.unsignedTx.Subnet).Return( + state.SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: []byte("address"), + }, + nil, + ).AnyTimes() env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes() cfg := &config.Config{ @@ -2245,7 +2259,10 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { subnetOwner := fxmock.NewOwner(ctrl) env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes() env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) - env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.Empty, nil, database.ErrNotFound).Times(1) + env.state.EXPECT().GetSubnetConversion(env.unsignedTx.Subnet).Return( + state.SubnetConversion{}, + database.ErrNotFound, + ).Times(1) env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil) env.flowChecker.EXPECT().VerifySpend( @@ -2276,7 +2293,7 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { err: ErrFlowCheckFailed, }, { - name: "invalid if subnet manager is set", + name: "invalid after subnet conversion", newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { env := newValidTransformSubnetTxVerifyEnv(t, ctrl) @@ -2284,7 +2301,14 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { subnetOwner := fxmock.NewOwner(ctrl) env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes() env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1) - env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.GenerateTestID(), make([]byte, 20), nil) + env.state.EXPECT().GetSubnetConversion(env.unsignedTx.Subnet).Return( + state.SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: make([]byte, 20), + }, + nil, + ) env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) @@ -2319,7 +2343,10 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { subnetOwner := fxmock.NewOwner(ctrl) env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes() env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1) - env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.Empty, nil, database.ErrNotFound).Times(1) + env.state.EXPECT().GetSubnetConversion(env.unsignedTx.Subnet).Return( + state.SubnetConversion{}, + database.ErrNotFound, + ).Times(1) env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) env.flowChecker.EXPECT().VerifySpend( @@ -2478,7 +2505,14 @@ func TestStandardExecutorConvertSubnetTx(t *testing.T) { { name: "invalid if subnet is converted", updateExecutor: func(e *StandardTxExecutor) { - e.State.SetSubnetManager(subnetID, ids.GenerateTestID(), nil) + e.State.SetSubnetConversion( + subnetID, + state.SubnetConversion{ + ConversionID: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Addr: utils.RandomBytes(32), + }, + ) }, expectedErr: errIsImmutable, }, @@ -2566,10 +2600,17 @@ func TestStandardExecutorConvertSubnetTx(t *testing.T) { require.Equal(expectedUTXO, utxo) } - stateChainID, stateAddress, err := diff.GetSubnetManager(subnetID) + stateConversion, err := diff.GetSubnetConversion(subnetID) require.NoError(err) - require.Equal(chainID, stateChainID) - require.Equal(address, stateAddress) + require.Equal( + state.SubnetConversion{ + // TODO: Specify the correct conversionID + ConversionID: ids.Empty, + ChainID: chainID, + Addr: address, + }, + stateConversion, + ) }) } } diff --git a/vms/platformvm/txs/executor/subnet_tx_verification.go b/vms/platformvm/txs/executor/subnet_tx_verification.go index 6e5ecb9a34f5..59acbe650491 100644 --- a/vms/platformvm/txs/executor/subnet_tx_verification.go +++ b/vms/platformvm/txs/executor/subnet_tx_verification.go @@ -43,7 +43,7 @@ func verifyPoASubnetAuthorization( return nil, err } - _, _, err = chainState.GetSubnetManager(subnetID) + _, err = chainState.GetSubnetConversion(subnetID) if err == nil { return nil, fmt.Errorf("%q %w", subnetID, errIsImmutable) }