Skip to content

Commit e4b7e82

Browse files
authored
Add SetSubnetOwner to Chain interface (#2031)
1 parent 0e86cf9 commit e4b7e82

File tree

12 files changed

+245
-20
lines changed

12 files changed

+245
-20
lines changed

vms/platformvm/config/execution_config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var DefaultExecutionConfig = ExecutionConfig{
1717
ChainCacheSize: 2048,
1818
ChainDBCacheSize: 2048,
1919
BlockIDCacheSize: 8192,
20+
FxOwnerCacheSize: 4 * units.MiB,
2021
ChecksumsEnabled: false,
2122
}
2223

@@ -29,6 +30,7 @@ type ExecutionConfig struct {
2930
ChainCacheSize int `json:"chain-cache-size"`
3031
ChainDBCacheSize int `json:"chain-db-cache-size"`
3132
BlockIDCacheSize int `json:"block-id-cache-size"`
33+
FxOwnerCacheSize int `json:"fx-owner-cache-size"`
3234
ChecksumsEnabled bool `json:"checksums-enabled"`
3335
}
3436

vms/platformvm/config/execution_config_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestExecutionConfigUnmarshal(t *testing.T) {
4646
"chain-cache-size": 6,
4747
"chain-db-cache-size": 7,
4848
"block-id-cache-size": 8,
49+
"fx-owner-cache-size": 9,
4950
"checksums-enabled": true
5051
}`)
5152
ec, err := GetExecutionConfig(b)
@@ -58,6 +59,7 @@ func TestExecutionConfigUnmarshal(t *testing.T) {
5859
ChainCacheSize: 6,
5960
ChainDBCacheSize: 7,
6061
BlockIDCacheSize: 8,
62+
FxOwnerCacheSize: 9,
6163
ChecksumsEnabled: true,
6264
}
6365
require.Equal(expected, ec)

vms/platformvm/state/diff.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func NewDiff(
7272
parentID: parentID,
7373
stateVersions: stateVersions,
7474
timestamp: parentState.GetTimestamp(),
75+
subnetOwners: make(map[ids.ID]fx.Owner),
7576
}, nil
7677
}
7778

@@ -293,16 +294,6 @@ func (d *diff) AddSubnet(createSubnetTx *txs.Tx) {
293294
if d.cachedSubnets != nil {
294295
d.cachedSubnets = append(d.cachedSubnets, createSubnetTx)
295296
}
296-
297-
castTx := createSubnetTx.Unsigned.(*txs.CreateSubnetTx)
298-
subnetID := createSubnetTx.ID()
299-
if d.subnetOwners == nil {
300-
d.subnetOwners = map[ids.ID]fx.Owner{
301-
subnetID: castTx.Owner,
302-
}
303-
} else {
304-
d.subnetOwners[subnetID] = castTx.Owner
305-
}
306297
}
307298

308299
func (d *diff) GetSubnetOwner(subnetID ids.ID) (fx.Owner, error) {
@@ -319,6 +310,10 @@ func (d *diff) GetSubnetOwner(subnetID ids.ID) (fx.Owner, error) {
319310
return parentState.GetSubnetOwner(subnetID)
320311
}
321312

313+
func (d *diff) SetSubnetOwner(subnetID ids.ID, owner fx.Owner) {
314+
d.subnetOwners[subnetID] = owner
315+
}
316+
322317
func (d *diff) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) {
323318
tx, exists := d.transformedSubnets[subnetID]
324319
if exists {
@@ -562,5 +557,8 @@ func (d *diff) Apply(baseState State) error {
562557
baseState.DeleteUTXO(utxoID)
563558
}
564559
}
560+
for subnetID, owner := range d.subnetOwners {
561+
baseState.SetSubnetOwner(subnetID, owner)
562+
}
565563
return nil
566564
}

vms/platformvm/state/diff_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,65 @@ func assertChainsEqual(t *testing.T, expected, actual Chain) {
516516
}
517517
}
518518
}
519+
520+
func TestDiffSubnetOwner(t *testing.T) {
521+
require := require.New(t)
522+
ctrl := gomock.NewController(t)
523+
524+
state, _ := newInitializedState(require)
525+
526+
states := NewMockVersions(ctrl)
527+
lastAcceptedID := ids.GenerateTestID()
528+
states.EXPECT().GetState(lastAcceptedID).Return(state, true).AnyTimes()
529+
530+
var (
531+
owner1 = fx.NewMockOwner(ctrl)
532+
owner2 = fx.NewMockOwner(ctrl)
533+
534+
createSubnetTx = &txs.Tx{
535+
Unsigned: &txs.CreateSubnetTx{
536+
BaseTx: txs.BaseTx{},
537+
Owner: owner1,
538+
},
539+
}
540+
541+
subnetID = createSubnetTx.ID()
542+
)
543+
544+
// Create subnet on base state
545+
owner, err := state.GetSubnetOwner(subnetID)
546+
require.ErrorIs(err, database.ErrNotFound)
547+
require.Nil(owner)
548+
549+
state.AddSubnet(createSubnetTx)
550+
state.SetSubnetOwner(subnetID, owner1)
551+
552+
owner, err = state.GetSubnetOwner(subnetID)
553+
require.NoError(err)
554+
require.Equal(owner1, owner)
555+
556+
// Create diff and verify that subnet owner returns correctly
557+
d, err := NewDiff(lastAcceptedID, states)
558+
require.NoError(err)
559+
560+
owner, err = d.GetSubnetOwner(subnetID)
561+
require.NoError(err)
562+
require.Equal(owner1, owner)
563+
564+
// Transferring subnet ownership on diff should be reflected on diff not state
565+
d.SetSubnetOwner(subnetID, owner2)
566+
owner, err = d.GetSubnetOwner(subnetID)
567+
require.NoError(err)
568+
require.Equal(owner2, owner)
569+
570+
owner, err = state.GetSubnetOwner(subnetID)
571+
require.NoError(err)
572+
require.Equal(owner1, owner)
573+
574+
// State should reflect new subnet owner after diff is applied.
575+
require.NoError(d.Apply(state))
576+
577+
owner, err = state.GetSubnetOwner(subnetID)
578+
require.NoError(err)
579+
require.Equal(owner2, owner)
580+
}

vms/platformvm/state/mock_chain.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vms/platformvm/state/mock_diff.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vms/platformvm/state/mock_state.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vms/platformvm/state/state.go

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ const (
5858
var (
5959
_ State = (*state)(nil)
6060

61-
ErrCantFindSubnet = errors.New("couldn't find subnet")
6261
errMissingValidatorSet = errors.New("missing validator set")
6362
errValidatorSetAlreadyPopulated = errors.New("validator set already populated")
6463
errDuplicateValidatorSet = errors.New("duplicate validator set")
@@ -81,6 +80,7 @@ var (
8180
rewardUTXOsPrefix = []byte("rewardUTXOs")
8281
utxoPrefix = []byte("utxo")
8382
subnetPrefix = []byte("subnet")
83+
subnetOwnerPrefix = []byte("subnetOwner")
8484
transformedSubnetPrefix = []byte("transformedSubnet")
8585
supplyPrefix = []byte("supply")
8686
chainPrefix = []byte("chain")
@@ -115,6 +115,7 @@ type Chain interface {
115115
AddSubnet(createSubnetTx *txs.Tx)
116116

117117
GetSubnetOwner(subnetID ids.ID) (fx.Owner, error)
118+
SetSubnetOwner(subnetID ids.ID, owner fx.Owner)
118119

119120
GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error)
120121
AddSubnetTransformation(transformSubnetTx *txs.Tx)
@@ -274,6 +275,8 @@ type stateBlk struct {
274275
* |-. subnets
275276
* | '-. list
276277
* | '-- txID -> nil
278+
* |-. subnetOwners
279+
* | '-. subnetID -> owner
277280
* |-. chains
278281
* | '-. subnetID
279282
* | '-. list
@@ -352,6 +355,11 @@ type state struct {
352355
subnetBaseDB database.Database
353356
subnetDB linkeddb.LinkedDB
354357

358+
// Subnet ID --> Owner of the subnet
359+
subnetOwners map[ids.ID]fx.Owner
360+
subnetOwnerCache cache.Cacher[ids.ID, fxOwnerAndSize] // cache of subnetID -> owner if the entry is nil, it is not in the database
361+
subnetOwnerDB database.Database
362+
355363
transformedSubnets map[ids.ID]*txs.Tx // map of subnetID -> transformSubnetTx
356364
transformedSubnetCache cache.Cacher[ids.ID, *txs.Tx] // cache of subnetID -> transformSubnetTx if the entry is nil, it is not in the database
357365
transformedSubnetDB database.Database
@@ -420,6 +428,11 @@ type txAndStatus struct {
420428
status status.Status
421429
}
422430

431+
type fxOwnerAndSize struct {
432+
owner fx.Owner
433+
size int
434+
}
435+
423436
func txSize(_ ids.ID, tx *txs.Tx) int {
424437
if tx == nil {
425438
return ids.IDLen + constants.PointerOverhead
@@ -573,6 +586,18 @@ func newState(
573586

574587
subnetBaseDB := prefixdb.New(subnetPrefix, baseDB)
575588

589+
subnetOwnerDB := prefixdb.New(subnetOwnerPrefix, baseDB)
590+
subnetOwnerCache, err := metercacher.New[ids.ID, fxOwnerAndSize](
591+
"subnet_owner_cache",
592+
metricsReg,
593+
cache.NewSizedLRU[ids.ID, fxOwnerAndSize](execCfg.FxOwnerCacheSize, func(_ ids.ID, f fxOwnerAndSize) int {
594+
return ids.IDLen + f.size
595+
}),
596+
)
597+
if err != nil {
598+
return nil, err
599+
}
600+
576601
transformedSubnetCache, err := metercacher.New(
577602
"transformed_subnet_cache",
578603
metricsReg,
@@ -669,6 +694,10 @@ func newState(
669694
subnetBaseDB: subnetBaseDB,
670695
subnetDB: linkeddb.NewDefault(subnetBaseDB),
671696

697+
subnetOwners: make(map[ids.ID]fx.Owner),
698+
subnetOwnerDB: subnetOwnerDB,
699+
subnetOwnerCache: subnetOwnerCache,
700+
672701
transformedSubnets: make(map[ids.ID]*txs.Tx),
673702
transformedSubnetCache: transformedSubnetCache,
674703
transformedSubnetDB: prefixdb.New(transformedSubnetPrefix, baseDB),
@@ -819,24 +848,54 @@ func (s *state) AddSubnet(createSubnetTx *txs.Tx) {
819848
}
820849

821850
func (s *state) GetSubnetOwner(subnetID ids.ID) (fx.Owner, error) {
851+
if owner, exists := s.subnetOwners[subnetID]; exists {
852+
return owner, nil
853+
}
854+
855+
if ownerAndSize, cached := s.subnetOwnerCache.Get(subnetID); cached {
856+
if ownerAndSize.owner == nil {
857+
return nil, database.ErrNotFound
858+
}
859+
return ownerAndSize.owner, nil
860+
}
861+
862+
ownerBytes, err := s.subnetOwnerDB.Get(subnetID[:])
863+
if err == nil {
864+
var owner fx.Owner
865+
if _, err := block.GenesisCodec.Unmarshal(ownerBytes, &owner); err != nil {
866+
return nil, err
867+
}
868+
s.subnetOwnerCache.Put(subnetID, fxOwnerAndSize{
869+
owner: owner,
870+
size: len(ownerBytes),
871+
})
872+
return owner, nil
873+
}
874+
if err != database.ErrNotFound {
875+
return nil, err
876+
}
877+
822878
subnetIntf, _, err := s.GetTx(subnetID)
823879
if err != nil {
824-
return nil, fmt.Errorf(
825-
"%w %q: %w",
826-
ErrCantFindSubnet,
827-
subnetID,
828-
err,
829-
)
880+
if err == database.ErrNotFound {
881+
s.subnetOwnerCache.Put(subnetID, fxOwnerAndSize{})
882+
}
883+
return nil, err
830884
}
831885

832886
subnet, ok := subnetIntf.Unsigned.(*txs.CreateSubnetTx)
833887
if !ok {
834888
return nil, fmt.Errorf("%q %w", subnetID, errIsNotSubnet)
835889
}
836890

891+
s.SetSubnetOwner(subnetID, subnet.Owner)
837892
return subnet.Owner, nil
838893
}
839894

895+
func (s *state) SetSubnetOwner(subnetID ids.ID, owner fx.Owner) {
896+
s.subnetOwners[subnetID] = owner
897+
}
898+
840899
func (s *state) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) {
841900
if tx, exists := s.transformedSubnets[subnetID]; exists {
842901
return tx, nil
@@ -1683,6 +1742,7 @@ func (s *state) write(updateValidators bool, height uint64) error {
16831742
s.writeRewardUTXOs(),
16841743
s.writeUTXOs(),
16851744
s.writeSubnets(),
1745+
s.writeSubnetOwners(),
16861746
s.writeTransformedSubnets(),
16871747
s.writeSubnetSupplies(),
16881748
s.writeChains(),
@@ -2273,6 +2333,29 @@ func (s *state) writeSubnets() error {
22732333
return nil
22742334
}
22752335

2336+
func (s *state) writeSubnetOwners() error {
2337+
for subnetID, owner := range s.subnetOwners {
2338+
subnetID := subnetID
2339+
owner := owner
2340+
delete(s.subnetOwners, subnetID)
2341+
2342+
ownerBytes, err := block.GenesisCodec.Marshal(block.Version, &owner)
2343+
if err != nil {
2344+
return fmt.Errorf("failed to marshal subnet owner: %w", err)
2345+
}
2346+
2347+
s.subnetOwnerCache.Put(subnetID, fxOwnerAndSize{
2348+
owner: owner,
2349+
size: len(ownerBytes),
2350+
})
2351+
2352+
if err := s.subnetOwnerDB.Put(subnetID[:], ownerBytes); err != nil {
2353+
return fmt.Errorf("failed to write subnet owner: %w", err)
2354+
}
2355+
}
2356+
return nil
2357+
}
2358+
22762359
func (s *state) writeTransformedSubnets() error {
22772360
for subnetID, tx := range s.transformedSubnets {
22782361
txID := tx.ID()

0 commit comments

Comments
 (0)