From 20f7007bec0e54804ba6a349911d0dd8e53a68fb Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 20 Nov 2023 10:58:31 -0500 Subject: [PATCH 01/16] add newView to merkleState; refactor logMerkleRoot to not return error --- vms/platformvm/state/merkle_state.go | 49 +++++++------------ vms/platformvm/state/merkle_state_load_ops.go | 6 +-- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/vms/platformvm/state/merkle_state.go b/vms/platformvm/state/merkle_state.go index c718003be4e6..8100b61d2623 100644 --- a/vms/platformvm/state/merkle_state.go +++ b/vms/platformvm/state/merkle_state.go @@ -1307,9 +1307,9 @@ func (ms *merkleState) processPendingStakers() (map[ids.ID]*stakersData, error) return output, nil } -func (ms *merkleState) writeMerkleState(currentData, pendingData map[ids.ID]*stakersData) error { +func (ms *merkleState) newView(currentData, pendingData map[ids.ID]*stakersData) (merkledb.TrieView, error) { batchOps := make([]database.BatchOp, 0) - err := utils.Err( + if err := utils.Err( ms.writeMetadata(&batchOps), ms.writePermissionedSubnets(&batchOps), ms.writeSubnetOwners(&batchOps), @@ -1319,24 +1319,23 @@ func (ms *merkleState) writeMerkleState(currentData, pendingData map[ids.ID]*sta ms.writePendingStakers(&batchOps, pendingData), ms.writeDelegateeRewards(&batchOps), ms.writeUTXOs(&batchOps), - ) - if err != nil { - return err + ); err != nil { + return nil, err } - if len(batchOps) == 0 { - // nothing to commit - return nil - } + return ms.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{BatchOps: batchOps}) +} - view, err := ms.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{BatchOps: batchOps}) +func (ms *merkleState) writeMerkleState(currentData, pendingData map[ids.ID]*stakersData) error { + view, err := ms.newView(currentData, pendingData) if err != nil { - return fmt.Errorf("failed creating merkleDB view: %w", err) + return err } if err := view.CommitToDB(context.TODO()); err != nil { return fmt.Errorf("failed committing merkleDB view: %w", err) } - return ms.logMerkleRoot(len(batchOps) != 0) + ms.logMerkleRoot() + return nil } func (ms *merkleState) writeMetadata(batchOps *[]database.BatchOp) error { @@ -1759,36 +1758,24 @@ func (ms *merkleState) updateValidatorSet( return nil } -func (ms *merkleState) logMerkleRoot(hasChanges bool) error { +func (ms *merkleState) logMerkleRoot() { // get current Height blk, err := ms.GetStatelessBlock(ms.GetLastAccepted()) if err != nil { // may happen in tests. Let's just skip - return nil + ms.ctx.Log.Error("failed to get last accepted block", zap.Error(err)) + return } - if !hasChanges { - ms.ctx.Log.Info("merkle root", - zap.Uint64("height", blk.Height()), - zap.Stringer("blkID", blk.ID()), - zap.String("merkle root", "no changes to merkle state"), - ) - return nil - } - - view, err := ms.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{}) - if err != nil { - return fmt.Errorf("failed creating merkleDB view: %w", err) - } - root, err := view.GetMerkleRoot(context.TODO()) + rootID, err := ms.merkleDB.GetMerkleRoot(context.Background()) if err != nil { - return fmt.Errorf("failed pulling merkle root: %w", err) + ms.ctx.Log.Error("failed to get merkle root", zap.Error(err)) + return } ms.ctx.Log.Info("merkle root", zap.Uint64("height", blk.Height()), zap.Stringer("blkID", blk.ID()), - zap.String("merkle root", root.String()), + zap.Stringer("merkle root", rootID), ) - return nil } diff --git a/vms/platformvm/state/merkle_state_load_ops.go b/vms/platformvm/state/merkle_state_load_ops.go index c9c17a8ddbec..a63fb1d7b578 100644 --- a/vms/platformvm/state/merkle_state_load_ops.go +++ b/vms/platformvm/state/merkle_state_load_ops.go @@ -154,14 +154,14 @@ func (ms *merkleState) syncGenesis(genesisBlk block.Block, genesis *genesis.Gene // Load pulls data previously stored on disk that is expected to be in memory. func (ms *merkleState) load(hasSynced bool) error { - return utils.Err( + err := utils.Err( ms.loadMerkleMetadata(), ms.loadCurrentStakers(), ms.loadPendingStakers(), ms.initValidatorSets(), - - ms.logMerkleRoot(!hasSynced), // we already logged if sync has happened ) + ms.logMerkleRoot() // we already logged if sync has happened + return err } // Loads the chain time and last accepted block ID from disk From 91d9cdad8c086a5bcac684b53a55328d45a274fa Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 20 Nov 2023 13:33:20 -0500 Subject: [PATCH 02/16] add NewView to State interface --- vms/platformvm/state/merkle_state.go | 13 +++++++++++++ vms/platformvm/state/mock_state.go | 16 ++++++++++++++++ vms/platformvm/state/state.go | 7 +++++++ 3 files changed, 36 insertions(+) diff --git a/vms/platformvm/state/merkle_state.go b/vms/platformvm/state/merkle_state.go index 8100b61d2623..258e38a49014 100644 --- a/vms/platformvm/state/merkle_state.go +++ b/vms/platformvm/state/merkle_state.go @@ -1307,6 +1307,19 @@ func (ms *merkleState) processPendingStakers() (map[ids.ID]*stakersData, error) return output, nil } +func (ms *merkleState) NewView() (merkledb.TrieView, error) { + // TODO reduce unneeded vars here. + currentData, _, _, _, err := ms.processCurrentStakers() + if err != nil { + return nil, err + } + pendingData, err := ms.processPendingStakers() + if err != nil { + return nil, err + } + return ms.newView(currentData, pendingData) +} + func (ms *merkleState) newView(currentData, pendingData map[ids.ID]*stakersData) (merkledb.TrieView, error) { batchOps := make([]database.BatchOp, 0) if err := utils.Err( diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index 41ce946a12e0..03f54b74f06d 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -22,6 +22,7 @@ import ( fx "github.com/ava-labs/avalanchego/vms/platformvm/fx" status "github.com/ava-labs/avalanchego/vms/platformvm/status" txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + merkledb "github.com/ava-labs/avalanchego/x/merkledb" gomock "go.uber.org/mock/gomock" ) @@ -604,6 +605,21 @@ func (mr *MockStateMockRecorder) GetUptime(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUptime", reflect.TypeOf((*MockState)(nil).GetUptime), arg0, arg1) } +// NewView mocks base method. +func (m *MockState) NewView() (merkledb.TrieView, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewView") + ret0, _ := ret[0].(merkledb.TrieView) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewView indicates an expected call of NewView. +func (mr *MockStateMockRecorder) NewView() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockState)(nil).NewView)) +} + // PruneAndIndex mocks base method. func (m *MockState) PruneAndIndex(arg0 sync.Locker, arg1 logging.Logger) error { m.ctrl.T.Helper() diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 199b245008f7..951e6a6a9533 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -44,6 +44,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/reward" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/x/merkledb" safemath "github.com/ava-labs/avalanchego/utils/math" ) @@ -130,6 +131,8 @@ type State interface { uptime.State avax.UTXOReader + NewView() (merkledb.TrieView, error) + GetLastAccepted() ids.ID SetLastAccepted(blkID ids.ID) @@ -704,6 +707,10 @@ func newState( }, nil } +func (s *state) NewView() (merkledb.TrieView, error) { + return nil, errors.New("unimplemented") +} + func (s *state) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { return s.currentStakers.GetValidator(subnetID, nodeID) } From 8785c554aa31a528031e5768d878d7e0b8e2f313 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 21 Nov 2023 14:03:20 -0500 Subject: [PATCH 03/16] wip add method GetMerkleChanges to diff --- vms/platformvm/state/diff.go | 156 ++++++++++++++++++ vms/platformvm/state/merkle_state.go | 35 ++-- vms/platformvm/state/merkle_state_load_ops.go | 4 +- vms/platformvm/state/mock_chain.go | 17 ++ vms/platformvm/state/mock_diff.go | 17 ++ vms/platformvm/state/mock_state.go | 8 +- vms/platformvm/state/state.go | 6 +- 7 files changed, 214 insertions(+), 29 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 64c681535cdc..2fd17bf7a8bf 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -11,9 +11,11 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/block" "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/x/merkledb" ) var ( @@ -76,6 +78,10 @@ func NewDiff( }, nil } +func (*diff) NewView([]database.BatchOp) (merkledb.TrieView, error) { + return nil, errors.New("TODO") +} + func (d *diff) GetTimestamp() time.Time { return d.timestamp } @@ -476,6 +482,155 @@ func (d *diff) DeleteUTXO(utxoID ids.ID) { } } +func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { + batchOps := []database.BatchOp{} + + // writeMetadata + encodedChainTime, err := d.timestamp.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("failed to encoding chainTime: %w", err) + } + batchOps = append(batchOps, database.BatchOp{ + Key: merkleChainTimeKey, + Value: encodedChainTime, + }) + + // writePermissionedSubnets + for _, subnet := range d.addedSubnets { + key := merklePermissionedSubnetKey(subnet.ID()) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: subnet.Bytes(), + }) + } + + // writeSubnetOwners + for subnetID, owner := range d.subnetOwners { + owner := owner + + ownerBytes, err := block.GenesisCodec.Marshal(block.Version, &owner) + if err != nil { + return nil, fmt.Errorf("failed to marshal subnet owner: %w", err) + } + + key := merkleSubnetOwnersKey(subnetID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: ownerBytes, + }) + } + + // writeElasticSubnets + for _, tx := range d.transformedSubnets { + transformSubnetTx := tx.Unsigned.(*txs.TransformSubnetTx) + key := merkleElasticSubnetKey(transformSubnetTx.Subnet) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: transformSubnetTx.Bytes(), + }) + } + + // writeChains + for _, chains := range d.addedChains { + for _, chain := range chains { + createChainTx := chain.Unsigned.(*txs.CreateChainTx) + subnetID := createChainTx.SubnetID + key := merkleChainKey(subnetID, chain.ID()) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: chain.Bytes(), + }) + } + } + + // writeCurrentStakers + // for stakerTxID, data := range currentData { + // key := merkleCurrentStakersKey(stakerTxID) + + // if data.TxBytes == nil { + // batchOps = append(batchOps, database.BatchOp{ + // Key: key, + // Delete: true, + // }) + // continue + // } + + // dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, data) + // if err != nil { + // return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", stakerTxID, err) + // } + // batchOps = append(batchOps, database.BatchOp{ + // Key: key, + // Value: dataBytes, + // }) + // } + + // writePendingStakers + // for stakerTxID, data := range pendingData { + // key := merklePendingStakersKey(stakerTxID) + + // if data.TxBytes == nil { + // batchOps = append(batchOps, database.BatchOp{ + // Key: key, + // Delete: true, + // }) + // continue + // } + + // dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, data) + // if err != nil { + // return nil, fmt.Errorf("failed to serialize pending stakers data, stakerTxID %v: %w", stakerTxID, err) + // } + // batchOps = append(batchOps, database.BatchOp{ + // Key: key, + // Value: dataBytes, + // }) + // } + + // writeDelegateeRewards + for subnetID, nodes := range d.modifiedDelegateeRewards { + for nodeID, amount := range nodes { + key := merkleDelegateeRewardsKey(nodeID, subnetID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: database.PackUInt64(amount), + }) + } + } + + // writeUTXOs + for utxoID, utxo := range d.modifiedUTXOs { + key := merkleUtxoIDKey(utxoID) + + if utxo != nil { + // Inserting a UTXO + utxoBytes, err := txs.GenesisCodec.Marshal(txs.Version, utxo) + if err != nil { + return nil, err + } + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: utxoBytes, + }) + continue + } + + // Deleting a UTXO + switch _, err := d.GetUTXO(utxoID); err { + case nil: + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Delete: true, + }) + case database.ErrNotFound: + default: + return nil, err + } + } + + return batchOps, nil +} + func (d *diff) Apply(baseState State) error { baseState.SetTimestamp(d.timestamp) for subnetID, supply := range d.currentSupply { @@ -557,5 +712,6 @@ func (d *diff) Apply(baseState State) error { for subnetID, owner := range d.subnetOwners { baseState.SetSubnetOwner(subnetID, owner) } + return nil } diff --git a/vms/platformvm/state/merkle_state.go b/vms/platformvm/state/merkle_state.go index 258e38a49014..1847c702062b 100644 --- a/vms/platformvm/state/merkle_state.go +++ b/vms/platformvm/state/merkle_state.go @@ -1307,22 +1307,15 @@ func (ms *merkleState) processPendingStakers() (map[ids.ID]*stakersData, error) return output, nil } -func (ms *merkleState) NewView() (merkledb.TrieView, error) { - // TODO reduce unneeded vars here. - currentData, _, _, _, err := ms.processCurrentStakers() - if err != nil { - return nil, err - } - pendingData, err := ms.processPendingStakers() - if err != nil { - return nil, err - } - return ms.newView(currentData, pendingData) +func (ms *merkleState) NewView(ops []database.BatchOp) (merkledb.TrieView, error) { + return ms.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{ + BatchOps: ops, + }) } -func (ms *merkleState) newView(currentData, pendingData map[ids.ID]*stakersData) (merkledb.TrieView, error) { +func (ms *merkleState) getMerkleChanges(currentData, pendingData map[ids.ID]*stakersData) ([]database.BatchOp, error) { batchOps := make([]database.BatchOp, 0) - if err := utils.Err( + err := utils.Err( ms.writeMetadata(&batchOps), ms.writePermissionedSubnets(&batchOps), ms.writeSubnetOwners(&batchOps), @@ -1332,20 +1325,22 @@ func (ms *merkleState) newView(currentData, pendingData map[ids.ID]*stakersData) ms.writePendingStakers(&batchOps, pendingData), ms.writeDelegateeRewards(&batchOps), ms.writeUTXOs(&batchOps), - ); err != nil { - return nil, err - } + ) - return ms.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{BatchOps: batchOps}) + return batchOps, err } func (ms *merkleState) writeMerkleState(currentData, pendingData map[ids.ID]*stakersData) error { - view, err := ms.newView(currentData, pendingData) + changes, err := ms.getMerkleChanges(currentData, pendingData) if err != nil { return err } - if err := view.CommitToDB(context.TODO()); err != nil { - return fmt.Errorf("failed committing merkleDB view: %w", err) + view, err := ms.NewView(changes) + if err != nil { + return err + } + if err := view.CommitToDB(context.Background()); err != nil { + return err } ms.logMerkleRoot() return nil diff --git a/vms/platformvm/state/merkle_state_load_ops.go b/vms/platformvm/state/merkle_state_load_ops.go index a63fb1d7b578..06768abaa83c 100644 --- a/vms/platformvm/state/merkle_state_load_ops.go +++ b/vms/platformvm/state/merkle_state_load_ops.go @@ -45,7 +45,7 @@ func (ms *merkleState) sync(genesis []byte) error { } } - return ms.load(shouldInit) + return ms.load() } func (ms *merkleState) shouldInit() (bool, error) { @@ -153,7 +153,7 @@ func (ms *merkleState) syncGenesis(genesisBlk block.Block, genesis *genesis.Gene } // Load pulls data previously stored on disk that is expected to be in memory. -func (ms *merkleState) load(hasSynced bool) error { +func (ms *merkleState) load() error { err := utils.Err( ms.loadMerkleMetadata(), ms.loadCurrentStakers(), diff --git a/vms/platformvm/state/mock_chain.go b/vms/platformvm/state/mock_chain.go index c82ceb3af831..5f8ae5cd757c 100644 --- a/vms/platformvm/state/mock_chain.go +++ b/vms/platformvm/state/mock_chain.go @@ -11,11 +11,13 @@ import ( reflect "reflect" time "time" + database "github.com/ava-labs/avalanchego/database" ids "github.com/ava-labs/avalanchego/ids" avax "github.com/ava-labs/avalanchego/vms/components/avax" fx "github.com/ava-labs/avalanchego/vms/platformvm/fx" status "github.com/ava-labs/avalanchego/vms/platformvm/status" txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + merkledb "github.com/ava-labs/avalanchego/x/merkledb" gomock "go.uber.org/mock/gomock" ) @@ -414,6 +416,21 @@ func (mr *MockChainMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockChain)(nil).GetUTXO), arg0) } +// NewView mocks base method. +func (m *MockChain) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewView", arg0) + ret0, _ := ret[0].(merkledb.TrieView) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewView indicates an expected call of NewView. +func (mr *MockChainMockRecorder) NewView(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockChain)(nil).NewView), arg0) +} + // PutCurrentDelegator mocks base method. func (m *MockChain) PutCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() diff --git a/vms/platformvm/state/mock_diff.go b/vms/platformvm/state/mock_diff.go index 49bab7897009..8fa7c07e10c1 100644 --- a/vms/platformvm/state/mock_diff.go +++ b/vms/platformvm/state/mock_diff.go @@ -11,11 +11,13 @@ import ( reflect "reflect" time "time" + database "github.com/ava-labs/avalanchego/database" ids "github.com/ava-labs/avalanchego/ids" avax "github.com/ava-labs/avalanchego/vms/components/avax" fx "github.com/ava-labs/avalanchego/vms/platformvm/fx" status "github.com/ava-labs/avalanchego/vms/platformvm/status" txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + merkledb "github.com/ava-labs/avalanchego/x/merkledb" gomock "go.uber.org/mock/gomock" ) @@ -428,6 +430,21 @@ func (mr *MockDiffMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockDiff)(nil).GetUTXO), arg0) } +// NewView mocks base method. +func (m *MockDiff) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewView", arg0) + ret0, _ := ret[0].(merkledb.TrieView) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewView indicates an expected call of NewView. +func (mr *MockDiffMockRecorder) NewView(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockDiff)(nil).NewView), arg0) +} + // PutCurrentDelegator mocks base method. func (m *MockDiff) PutCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index 03f54b74f06d..135b72f7c9d4 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -606,18 +606,18 @@ func (mr *MockStateMockRecorder) GetUptime(arg0, arg1 interface{}) *gomock.Call } // NewView mocks base method. -func (m *MockState) NewView() (merkledb.TrieView, error) { +func (m *MockState) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewView") + ret := m.ctrl.Call(m, "NewView", arg0) ret0, _ := ret[0].(merkledb.TrieView) ret1, _ := ret[1].(error) return ret0, ret1 } // NewView indicates an expected call of NewView. -func (mr *MockStateMockRecorder) NewView() *gomock.Call { +func (mr *MockStateMockRecorder) NewView(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockState)(nil).NewView)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockState)(nil).NewView), arg0) } // PruneAndIndex mocks base method. diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 951e6a6a9533..4780d8daeaea 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -101,6 +101,8 @@ type Chain interface { avax.UTXOGetter avax.UTXODeleter + NewView(ops []database.BatchOp) (merkledb.TrieView, error) + GetTimestamp() time.Time SetTimestamp(tm time.Time) @@ -131,8 +133,6 @@ type State interface { uptime.State avax.UTXOReader - NewView() (merkledb.TrieView, error) - GetLastAccepted() ids.ID SetLastAccepted(blkID ids.ID) @@ -707,7 +707,7 @@ func newState( }, nil } -func (s *state) NewView() (merkledb.TrieView, error) { +func (*state) NewView([]database.BatchOp) (merkledb.TrieView, error) { return nil, errors.New("unimplemented") } From 28384fb99e0cdabeed258adacdbdf70560a4770b Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Wed, 22 Nov 2023 15:27:17 -0500 Subject: [PATCH 04/16] WIP add replacement for writeCurrentStakers --- vms/platformvm/state/diff.go | 77 +++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 2fd17bf7a8bf..7cabebfe4be1 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -544,26 +544,67 @@ func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { } // writeCurrentStakers - // for stakerTxID, data := range currentData { - // key := merkleCurrentStakersKey(stakerTxID) + // TODO refactor + for _, nodeIDToValidatorDiff := range d.currentStakerDiffs.validatorDiffs { + for _, validatorDiff := range nodeIDToValidatorDiff { + switch validatorDiff.validatorStatus { + case deleted: + key := merkleCurrentStakersKey(validatorDiff.validator.TxID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Delete: true, + }) + case added: + key := merkleCurrentStakersKey(validatorDiff.validator.TxID) + tx, _, err := d.GetTx(validatorDiff.validator.TxID) + if err != nil { + return nil, fmt.Errorf("failed to get tx %s: %w", validatorDiff.validator.TxID, err) + } + + stakersDataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ + TxBytes: tx.Bytes(), + PotentialReward: validatorDiff.validator.PotentialReward, + }) + if err != nil { + return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", validatorDiff.validator.TxID, err) + } + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: stakersDataBytes, + }) + } - // if data.TxBytes == nil { - // batchOps = append(batchOps, database.BatchOp{ - // Key: key, - // Delete: true, - // }) - // continue - // } + addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) + defer addedDelegatorIterator.Release() + for addedDelegatorIterator.Next() { + staker := addedDelegatorIterator.Value() + tx, _, err := d.GetTx(staker.TxID) + if err != nil { + return nil, fmt.Errorf("failed loading current delegator tx, %w", err) + } + stakersDataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ + TxBytes: tx.Bytes(), + PotentialReward: staker.PotentialReward, + }) + if err != nil { + return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", staker.TxID, err) + } + key := merkleCurrentStakersKey(staker.TxID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: stakersDataBytes, + }) + } - // dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, data) - // if err != nil { - // return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", stakerTxID, err) - // } - // batchOps = append(batchOps, database.BatchOp{ - // Key: key, - // Value: dataBytes, - // }) - // } + for _, staker := range validatorDiff.deletedDelegators { + key := merkleCurrentStakersKey(staker.TxID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Delete: true, + }) + } + } + } // writePendingStakers // for stakerTxID, data := range pendingData { From 0a92fc280b5c59e6e50b2185da3b22059395e6b4 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 27 Nov 2023 12:06:26 -0500 Subject: [PATCH 05/16] refactor writeCurrentStakers block --- vms/platformvm/state/diff.go | 65 +++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 7cabebfe4be1..6085689b68cb 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -18,6 +18,8 @@ import ( "github.com/ava-labs/avalanchego/x/merkledb" ) +const initialTxSliceSize = 8 + var ( _ Diff = (*diff)(nil) @@ -543,34 +545,24 @@ func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { } } + type txIDAndReward struct { + txID ids.ID + reward uint64 + } + // writeCurrentStakers - // TODO refactor for _, nodeIDToValidatorDiff := range d.currentStakerDiffs.validatorDiffs { + toDeleteTxIDs := make([]ids.ID, 0, initialTxSliceSize) + toAddTxIDAndRewards := make([]txIDAndReward, 0, initialTxSliceSize) + for _, validatorDiff := range nodeIDToValidatorDiff { switch validatorDiff.validatorStatus { case deleted: - key := merkleCurrentStakersKey(validatorDiff.validator.TxID) - batchOps = append(batchOps, database.BatchOp{ - Key: key, - Delete: true, - }) + toDeleteTxIDs = append(toDeleteTxIDs, validatorDiff.validator.TxID) case added: - key := merkleCurrentStakersKey(validatorDiff.validator.TxID) - tx, _, err := d.GetTx(validatorDiff.validator.TxID) - if err != nil { - return nil, fmt.Errorf("failed to get tx %s: %w", validatorDiff.validator.TxID, err) - } - - stakersDataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ - TxBytes: tx.Bytes(), - PotentialReward: validatorDiff.validator.PotentialReward, - }) - if err != nil { - return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", validatorDiff.validator.TxID, err) - } - batchOps = append(batchOps, database.BatchOp{ - Key: key, - Value: stakersDataBytes, + toAddTxIDAndRewards = append(toAddTxIDAndRewards, txIDAndReward{ + txID: validatorDiff.validator.TxID, + reward: validatorDiff.validator.PotentialReward, }) } @@ -578,28 +570,39 @@ func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { defer addedDelegatorIterator.Release() for addedDelegatorIterator.Next() { staker := addedDelegatorIterator.Value() - tx, _, err := d.GetTx(staker.TxID) + toAddTxIDAndRewards = append(toAddTxIDAndRewards, txIDAndReward{ + txID: staker.TxID, + reward: staker.PotentialReward, + }) + } + + for _, staker := range validatorDiff.deletedDelegators { + toDeleteTxIDs = append(toDeleteTxIDs, staker.TxID) + } + + for _, txIDAndReward := range toAddTxIDAndRewards { + tx, _, err := d.GetTx(txIDAndReward.txID) if err != nil { - return nil, fmt.Errorf("failed loading current delegator tx, %w", err) + return nil, err } + stakersDataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ TxBytes: tx.Bytes(), - PotentialReward: staker.PotentialReward, + PotentialReward: txIDAndReward.reward, }) if err != nil { - return nil, fmt.Errorf("failed to serialize current stakers data, stakerTxID %v: %w", staker.TxID, err) + return nil, err } - key := merkleCurrentStakersKey(staker.TxID) + batchOps = append(batchOps, database.BatchOp{ - Key: key, + Key: merkleCurrentStakersKey(txIDAndReward.txID), Value: stakersDataBytes, }) } - for _, staker := range validatorDiff.deletedDelegators { - key := merkleCurrentStakersKey(staker.TxID) + for _, txID := range toDeleteTxIDs { batchOps = append(batchOps, database.BatchOp{ - Key: key, + Key: merkleCurrentStakersKey(txID), Delete: true, }) } From a3c82585f78eb6aab0aec5c8b6f741b284ba597f Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 27 Nov 2023 13:56:20 -0500 Subject: [PATCH 06/16] WIP add replacement for writePendingStakers --- vms/platformvm/state/diff.go | 78 +++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 6085689b68cb..8cd1ba0253a6 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -610,26 +610,64 @@ func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { } // writePendingStakers - // for stakerTxID, data := range pendingData { - // key := merklePendingStakersKey(stakerTxID) - - // if data.TxBytes == nil { - // batchOps = append(batchOps, database.BatchOp{ - // Key: key, - // Delete: true, - // }) - // continue - // } - - // dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, data) - // if err != nil { - // return nil, fmt.Errorf("failed to serialize pending stakers data, stakerTxID %v: %w", stakerTxID, err) - // } - // batchOps = append(batchOps, database.BatchOp{ - // Key: key, - // Value: dataBytes, - // }) - // } + for _, subnetValidatorDiffs := range d.pendingStakerDiffs.validatorDiffs { + for _, validatorDiff := range subnetValidatorDiffs { + // validatorDiff.validator is not guaranteed to be non-nil here. + // Access it only if validatorDiff.validatorStatus is added or deleted + switch validatorDiff.validatorStatus { + case added: + txID := validatorDiff.validator.TxID + tx, _, err := d.GetTx(txID) + if err != nil { + return nil, err + } + dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ + TxBytes: tx.Bytes(), + PotentialReward: 0, + }) + if err != nil { + return nil, err + } + batchOps = append(batchOps, database.BatchOp{ + Key: merklePendingStakersKey(txID), + Value: dataBytes, + }) + case deleted: + batchOps = append(batchOps, database.BatchOp{ + Key: merklePendingStakersKey(validatorDiff.validator.TxID), + Delete: true, + }) + } + + addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) + defer addedDelegatorIterator.Release() + for addedDelegatorIterator.Next() { + staker := addedDelegatorIterator.Value() + tx, _, err := d.GetTx(staker.TxID) + if err != nil { + return nil, fmt.Errorf("failed loading pending delegator tx, %w", err) + } + dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ + TxBytes: tx.Bytes(), + PotentialReward: 0, + }) + if err != nil { + return nil, err + } + batchOps = append(batchOps, database.BatchOp{ + Key: merklePendingStakersKey(staker.TxID), + Value: dataBytes, + }) + } + + for _, staker := range validatorDiff.deletedDelegators { + batchOps = append(batchOps, database.BatchOp{ + Key: merklePendingStakersKey(staker.TxID), + Delete: true, + }) + } + } + } // writeDelegateeRewards for subnetID, nodes := range d.modifiedDelegateeRewards { From be4aec8716aa55d578e9084f74286b4ce7e03918 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 27 Nov 2023 14:13:13 -0500 Subject: [PATCH 07/16] refactor writePendingStakers block --- vms/platformvm/state/diff.go | 63 ++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 8cd1ba0253a6..e9b0f8f5fc09 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -611,59 +611,60 @@ func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { // writePendingStakers for _, subnetValidatorDiffs := range d.pendingStakerDiffs.validatorDiffs { + toDeleteTxIDs := make([]ids.ID, 0, initialTxSliceSize) + toAddTxIDAndRewards := make([]txIDAndReward, 0, initialTxSliceSize) + for _, validatorDiff := range subnetValidatorDiffs { // validatorDiff.validator is not guaranteed to be non-nil here. // Access it only if validatorDiff.validatorStatus is added or deleted switch validatorDiff.validatorStatus { case added: - txID := validatorDiff.validator.TxID - tx, _, err := d.GetTx(txID) - if err != nil { - return nil, err - } - dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ - TxBytes: tx.Bytes(), - PotentialReward: 0, - }) - if err != nil { - return nil, err - } - batchOps = append(batchOps, database.BatchOp{ - Key: merklePendingStakersKey(txID), - Value: dataBytes, + toAddTxIDAndRewards = append(toAddTxIDAndRewards, txIDAndReward{ + txID: validatorDiff.validator.TxID, + reward: 0, }) case deleted: - batchOps = append(batchOps, database.BatchOp{ - Key: merklePendingStakersKey(validatorDiff.validator.TxID), - Delete: true, - }) + toDeleteTxIDs = append(toDeleteTxIDs, validatorDiff.validator.TxID) } addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) defer addedDelegatorIterator.Release() for addedDelegatorIterator.Next() { staker := addedDelegatorIterator.Value() - tx, _, err := d.GetTx(staker.TxID) + toAddTxIDAndRewards = append(toAddTxIDAndRewards, txIDAndReward{ + txID: staker.TxID, + reward: 0, + }) + } + + for _, staker := range validatorDiff.deletedDelegators { + toDeleteTxIDs = append(toDeleteTxIDs, staker.TxID) + } + + for _, txID := range toDeleteTxIDs { + batchOps = append(batchOps, database.BatchOp{ + Key: merklePendingStakersKey(txID), + Delete: true, + }) + } + + for _, txIDAndReward := range toAddTxIDAndRewards { + tx, _, err := d.GetTx(txIDAndReward.txID) if err != nil { - return nil, fmt.Errorf("failed loading pending delegator tx, %w", err) + return nil, err } - dataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ + + stakersDataBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stakersData{ TxBytes: tx.Bytes(), - PotentialReward: 0, + PotentialReward: txIDAndReward.reward, }) if err != nil { return nil, err } - batchOps = append(batchOps, database.BatchOp{ - Key: merklePendingStakersKey(staker.TxID), - Value: dataBytes, - }) - } - for _, staker := range validatorDiff.deletedDelegators { batchOps = append(batchOps, database.BatchOp{ - Key: merklePendingStakersKey(staker.TxID), - Delete: true, + Key: merklePendingStakersKey(txIDAndReward.txID), + Value: stakersDataBytes, }) } } From a7b758bd9b52732fa2a6e670e78d8c529491042c Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 27 Nov 2023 17:14:16 -0500 Subject: [PATCH 08/16] implement NewView on diff --- vms/platformvm/state/diff.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index e9b0f8f5fc09..2166d06828fe 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -80,8 +80,20 @@ func NewDiff( }, nil } -func (*diff) NewView([]database.BatchOp) (merkledb.TrieView, error) { - return nil, errors.New("TODO") +func (d *diff) NewView(ops []database.BatchOp) (merkledb.TrieView, error) { + parentState, ok := d.stateVersions.GetState(d.parentID) + if !ok { + return nil, fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID) + } + + diffOps, err := d.GetMerkleChanges() + if err != nil { + return nil, err + } + + ops = append(diffOps, ops...) + + return parentState.NewView(ops) } func (d *diff) GetTimestamp() time.Time { From 192072c9bacbb7ce675d490cdbc5d9853674d990 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 28 Nov 2023 17:02:07 -0500 Subject: [PATCH 09/16] update NewView interface --- scripts/mocks.mockgen.txt | 33 ++++++++++++++++++++++++++++++ vms/platformvm/state/diff.go | 15 ++++++++++---- vms/platformvm/state/mock_state.go | 24 +++++++++++----------- vms/platformvm/state/state.go | 14 +++++++------ 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/scripts/mocks.mockgen.txt b/scripts/mocks.mockgen.txt index 9ca9a72535a6..76add90f3f7f 100644 --- a/scripts/mocks.mockgen.txt +++ b/scripts/mocks.mockgen.txt @@ -1,3 +1,36 @@ +github.com/ava-labs/avalanchego/api/server=Server=api/server/mock_server.go +github.com/ava-labs/avalanchego/chains/atomic=SharedMemory=chains/atomic/mock_shared_memory.go +github.com/ava-labs/avalanchego/codec=Manager=codec/mock_manager.go +github.com/ava-labs/avalanchego/database=Batch=database/mock_batch.go +github.com/ava-labs/avalanchego/database=Iterator=database/mock_iterator.go +github.com/ava-labs/avalanchego/message=OutboundMessage=message/mock_message.go +github.com/ava-labs/avalanchego/message=OutboundMsgBuilder=message/mock_outbound_message_builder.go +github.com/ava-labs/avalanchego/network/peer=GossipTracker=network/peer/mock_gossip_tracker.go +github.com/ava-labs/avalanchego/network/p2p=Handler=network/p2p/mocks/mock_handler.go +github.com/ava-labs/avalanchego/snow/consensus/snowman=Block=snow/consensus/snowman/mock_block.go +github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex=LinearizableVM=snow/engine/avalanche/vertex/mock_vm.go +github.com/ava-labs/avalanchego/snow/engine/snowman/block=BuildBlockWithContextChainVM=snow/engine/snowman/block/mocks/build_block_with_context_vm.go +github.com/ava-labs/avalanchego/snow/engine/snowman/block=ChainVM=snow/engine/snowman/block/mocks/chain_vm.go +github.com/ava-labs/avalanchego/snow/engine/snowman/block=StateSyncableVM=snow/engine/snowman/block/mocks/state_syncable_vm.go +github.com/ava-labs/avalanchego/snow/engine/snowman/block=WithVerifyContext=snow/engine/snowman/block/mocks/with_verify_context.go +github.com/ava-labs/avalanchego/snow/networking/handler=Handler=snow/networking/handler/mock_handler.go +github.com/ava-labs/avalanchego/snow/networking/timeout=Manager=snow/networking/timeout/mock_manager.go +github.com/ava-labs/avalanchego/snow/networking/tracker=Targeter=snow/networking/tracker/mock_targeter.go +github.com/ava-labs/avalanchego/snow/networking/tracker=Tracker=snow/networking/tracker/mock_resource_tracker.go +github.com/ava-labs/avalanchego/snow/uptime=Calculator=snow/uptime/mock_calculator.go +github.com/ava-labs/avalanchego/snow/validators=State=snow/validators/mock_state.go +github.com/ava-labs/avalanchego/snow/validators=SubnetConnector=snow/validators/mock_subnet_connector.go +github.com/ava-labs/avalanchego/utils/crypto/keychain=Ledger=utils/crypto/keychain/mock_ledger.go +github.com/ava-labs/avalanchego/utils/filesystem=Reader=utils/filesystem/mock_io.go +github.com/ava-labs/avalanchego/utils/hashing=Hasher=utils/hashing/mock_hasher.go +github.com/ava-labs/avalanchego/utils/logging=Logger=utils/logging/mock_logger.go +github.com/ava-labs/avalanchego/utils/resource=User=utils/resource/mock_user.go +github.com/ava-labs/avalanchego/vms/avm/block=Block=vms/avm/block/mock_block.go +github.com/ava-labs/avalanchego/vms/avm/metrics=Metrics=vms/avm/metrics/mock_metrics.go +github.com/ava-labs/avalanchego/vms/avm/state=Chain,State,Diff=vms/avm/state/mock_state.go +github.com/ava-labs/avalanchego/vms/avm/txs/mempool=Mempool=vms/avm/txs/mempool/mock_mempool.go +github.com/ava-labs/avalanchego/vms/components/avax=TransferableIn=vms/components/avax/mock_transferable_in.go +github.com/ava-labs/avalanchego/vms/components/verify=Verifiable=vms/components/verify/mock_verifiable.go github.com/ava-labs/avalanchego/vms/platformvm/block/executor=Manager=vms/platformvm/block/executor/mock_manager.go github.com/ava-labs/avalanchego/vms/platformvm/block=Block=vms/platformvm/block/mock_block.go github.com/ava-labs/avalanchego/vms/platformvm/state=Chain,Diff,State,Versions=vms/platformvm/state/mock_state.go diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 9d91274a89f9..ee809029a53a 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -4,6 +4,7 @@ package state import ( + "context" "errors" "fmt" "time" @@ -80,20 +81,26 @@ func NewDiff( }, nil } -func (d *diff) NewView(ops []database.BatchOp) (merkledb.TrieView, error) { +// Returns a view that contains the merkle state of this diff. +func (d *diff) NewView() (merkledb.TrieView, error) { parentState, ok := d.stateVersions.GetState(d.parentID) if !ok { return nil, fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID) } - diffOps, err := d.GetMerkleChanges() + ops, err := d.GetMerkleChanges() if err != nil { return nil, err } - ops = append(diffOps, ops...) + parentView, err := parentState.NewView() + if err != nil { + return nil, err + } - return parentState.NewView(ops) + return parentView.NewView(context.Background(), merkledb.ViewChanges{ + BatchOps: ops, + }) } func (d *diff) GetTimestamp() time.Time { diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index b6064816559f..7ba679214fda 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -420,18 +420,18 @@ func (mr *MockChainMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { } // NewView mocks base method. -func (m *MockChain) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { +func (m *MockChain) NewView() (merkledb.TrieView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewView", arg0) + ret := m.ctrl.Call(m, "NewView") ret0, _ := ret[0].(merkledb.TrieView) ret1, _ := ret[1].(error) return ret0, ret1 } // NewView indicates an expected call of NewView. -func (mr *MockChainMockRecorder) NewView(arg0 interface{}) *gomock.Call { +func (mr *MockChainMockRecorder) NewView() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockChain)(nil).NewView), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockChain)(nil).NewView)) } // PutCurrentDelegator mocks base method. @@ -942,18 +942,18 @@ func (mr *MockDiffMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { } // NewView mocks base method. -func (m *MockDiff) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { +func (m *MockDiff) NewView() (merkledb.TrieView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewView", arg0) + ret := m.ctrl.Call(m, "NewView") ret0, _ := ret[0].(merkledb.TrieView) ret1, _ := ret[1].(error) return ret0, ret1 } // NewView indicates an expected call of NewView. -func (mr *MockDiffMockRecorder) NewView(arg0 interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) NewView() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockDiff)(nil).NewView), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockDiff)(nil).NewView)) } // PutCurrentDelegator mocks base method. @@ -1634,18 +1634,18 @@ func (mr *MockStateMockRecorder) GetUptime(arg0, arg1 interface{}) *gomock.Call } // NewView mocks base method. -func (m *MockState) NewView(arg0 []database.BatchOp) (merkledb.TrieView, error) { +func (m *MockState) NewView() (merkledb.TrieView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewView", arg0) + ret := m.ctrl.Call(m, "NewView") ret0, _ := ret[0].(merkledb.TrieView) ret1, _ := ret[1].(error) return ret0, ret1 } // NewView indicates an expected call of NewView. -func (mr *MockStateMockRecorder) NewView(arg0 interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) NewView() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockState)(nil).NewView), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewView", reflect.TypeOf((*MockState)(nil).NewView)) } // PutCurrentDelegator mocks base method. diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index ed9c6156621b..543f3cf3f766 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -98,7 +98,7 @@ type Chain interface { avax.UTXOGetter avax.UTXODeleter - NewView(ops []database.BatchOp) (merkledb.TrieView, error) + NewView() (merkledb.TrieView, error) GetTimestamp() time.Time SetTimestamp(tm time.Time) @@ -1507,10 +1507,8 @@ func (s *state) processPendingStakers() (map[ids.ID]*stakersData, error) { return output, nil } -func (s *state) NewView(ops []database.BatchOp) (merkledb.TrieView, error) { - return s.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{ - BatchOps: ops, - }) +func (s *state) NewView() (merkledb.TrieView, error) { + return s.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{}) } func (s *state) getMerkleChanges(currentData, pendingData map[ids.ID]*stakersData) ([]database.BatchOp, error) { @@ -1535,10 +1533,14 @@ func (s *state) writeMerkleState(currentData, pendingData map[ids.ID]*stakersDat if err != nil { return err } - view, err := s.NewView(changes) + + view, err := s.merkleDB.NewView(context.TODO(), merkledb.ViewChanges{ + BatchOps: changes, + }) if err != nil { return err } + if err := view.CommitToDB(context.Background()); err != nil { return err } From 256b278686341d0ecb04bcb94f07522768950fa2 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 28 Nov 2023 17:04:37 -0500 Subject: [PATCH 10/16] comment --- vms/platformvm/state/diff.go | 2 -- vms/platformvm/state/state.go | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index ee809029a53a..5f5d37231ef0 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -81,7 +81,6 @@ func NewDiff( }, nil } -// Returns a view that contains the merkle state of this diff. func (d *diff) NewView() (merkledb.TrieView, error) { parentState, ok := d.stateVersions.GetState(d.parentID) if !ok { @@ -814,6 +813,5 @@ func (d *diff) Apply(baseState Chain) error { for subnetID, owner := range d.subnetOwners { baseState.SetSubnetOwner(subnetID, owner) } - return nil } diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 543f3cf3f766..7017dedeeb61 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -98,6 +98,7 @@ type Chain interface { avax.UTXOGetter avax.UTXODeleter + // Returns a view that contains the merkleized portion of the state. NewView() (merkledb.TrieView, error) GetTimestamp() time.Time From 6f832cc8b5aad5b87ff8402a8aad89703c97fae0 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Wed, 29 Nov 2023 10:52:03 -0500 Subject: [PATCH 11/16] unexport function --- vms/platformvm/state/diff.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 5f5d37231ef0..49672397ecf5 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -87,7 +87,7 @@ func (d *diff) NewView() (merkledb.TrieView, error) { return nil, fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID) } - ops, err := d.GetMerkleChanges() + ops, err := d.getMerkleChanges() if err != nil { return nil, err } @@ -502,7 +502,7 @@ func (d *diff) DeleteUTXO(utxoID ids.ID) { } } -func (d *diff) GetMerkleChanges() ([]database.BatchOp, error) { +func (d *diff) getMerkleChanges() ([]database.BatchOp, error) { batchOps := []database.BatchOp{} // writeMetadata From cadce708561b2aa4014506c4934670cc7519531a Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Wed, 29 Nov 2023 16:01:43 -0500 Subject: [PATCH 12/16] update writeMetadata portion of getMerkleChanges to include last accepted block ID and supplies --- vms/platformvm/state/diff.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 9d7435a99b9c..0afefd8b158f 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -517,6 +517,17 @@ func (d *diff) getMerkleChanges() ([]database.BatchOp, error) { Key: merkleChainTimeKey, Value: encodedChainTime, }) + batchOps = append(batchOps, database.BatchOp{ + Key: merkleLastAcceptedBlkIDKey, + Value: nil, // TODO write this block's ID + }) + for subnetID, supply := range d.currentSupply { + key := merkleSuppliesKey(subnetID) + batchOps = append(batchOps, database.BatchOp{ + Key: key, + Value: database.PackUInt64(supply), + }) + } // writePermissionedSubnets for _, subnet := range d.addedSubnets { From d98b52bd0aaa28d5810d02ff0c51210bcf80fb02 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Wed, 29 Nov 2023 17:06:44 -0500 Subject: [PATCH 13/16] include txs in merkleized state --- vms/platformvm/state/state.go | 22 ++++++++++------------ vms/platformvm/state/state_helpers.go | 7 +++++++ vms/platformvm/state/state_helpers_test.go | 10 ++++++++++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 50fb9b693422..4bdf35658545 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -69,12 +69,11 @@ var ( merkleSingletonPrefix = []byte{0x01} merkleBlockPrefix = []byte{0x02} merkleBlockIDsPrefix = []byte{0x03} - merkleTxPrefix = []byte{0x04} - merkleIndexUTXOsPrefix = []byte{0x05} // to serve UTXOIDs(addr) - merkleUptimesPrefix = []byte{0x06} // locally measured uptimes - merkleWeightDiffPrefix = []byte{0x07} // non-merkleized validators weight diff. TODO: should we merkleize them? - merkleBlsKeyDiffPrefix = []byte{0x08} - merkleRewardUtxosPrefix = []byte{0x09} + merkleIndexUTXOsPrefix = []byte{0x04} // to serve UTXOIDs(addr) + merkleUptimesPrefix = []byte{0x05} // locally measured uptimes + merkleWeightDiffPrefix = []byte{0x06} // non-merkleized validators weight diff. TODO: should we merkleize them? + merkleBlsKeyDiffPrefix = []byte{0x07} + merkleRewardUtxosPrefix = []byte{0x08} initializedKey = []byte("initialized") @@ -92,6 +91,7 @@ var ( pendingStakersSectionPrefix = []byte{0x06} delegateeRewardsPrefix = []byte{0x07} subnetOwnersPrefix = []byte{0x08} + txsSectionPrefix = []byte{0x09} ) // Chain collects all methods to manage the state of the chain for block @@ -287,7 +287,6 @@ type state struct { // a limited windows to support APIs addedTxs map[ids.ID]*txAndStatus // map of txID -> {*txs.Tx, Status} txCache cache.Cacher[ids.ID, *txAndStatus] // txID -> {*txs.Tx, Status}. If the entry is nil, it isn't in the database - txDB database.Database indexedUTXOsDB database.Database @@ -408,7 +407,6 @@ func newState( singletonDB = prefixdb.New(merkleSingletonPrefix, baseDB) blockDB = prefixdb.New(merkleBlockPrefix, baseDB) blockIDsDB = prefixdb.New(merkleBlockIDsPrefix, baseDB) - txDB = prefixdb.New(merkleTxPrefix, baseDB) indexedUTXOsDB = prefixdb.New(merkleIndexUTXOsPrefix, baseDB) localUptimesDB = prefixdb.New(merkleUptimesPrefix, baseDB) flatValidatorWeightDiffsDB = prefixdb.New(merkleWeightDiffPrefix, baseDB) @@ -551,7 +549,6 @@ func newState( addedTxs: make(map[ids.ID]*txAndStatus), txCache: txCache, - txDB: txDB, indexedUTXOsDB: indexedUTXOsDB, @@ -808,7 +805,8 @@ func (s *state) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { return tx.tx, tx.status, nil } - txBytes, err := s.txDB.Get(txID[:]) + key := merkleTxKey(txID) + txBytes, err := s.merkleDB.Get(key) switch err { case nil: stx := txBytesAndStatus{} @@ -1394,7 +1392,6 @@ func (s *state) Close() error { s.flatValidatorPublicKeyDiffsDB.Close(), s.localUptimesDB.Close(), s.indexedUTXOsDB.Close(), - s.txDB.Close(), s.blockDB.Close(), s.blockIDDB.Close(), s.merkleDB.Close(), @@ -1896,7 +1893,8 @@ func (s *state) writeTxs() error { // referencing additional data (because of shared byte slices) that // would not be properly accounted for in the cache sizing. s.txCache.Evict(txID) - if err := s.txDB.Put(txID[:], txBytes); err != nil { + key := merkleTxKey(txID) + if err := s.merkleDB.Put(key[:], txBytes); err != nil { return fmt.Errorf("failed to add tx: %w", err) } } diff --git a/vms/platformvm/state/state_helpers.go b/vms/platformvm/state/state_helpers.go index cf8b165e2698..5c922d2dd6e7 100644 --- a/vms/platformvm/state/state_helpers.go +++ b/vms/platformvm/state/state_helpers.go @@ -128,3 +128,10 @@ func merkleSubnetOwnersKey(subnetID ids.ID) []byte { copy(key[len(delegateeRewardsPrefix):], subnetID[:]) return key } + +func merkleTxKey(txID ids.ID) []byte { + key := make([]byte, len(txsSectionPrefix)+ids.IDLen) + copy(key, txsSectionPrefix) + copy(key[len(txsSectionPrefix):], txID[:]) + return key +} diff --git a/vms/platformvm/state/state_helpers_test.go b/vms/platformvm/state/state_helpers_test.go index 00547c9c8d93..fb96bd780bf2 100644 --- a/vms/platformvm/state/state_helpers_test.go +++ b/vms/platformvm/state/state_helpers_test.go @@ -140,3 +140,13 @@ func TestDelegateeRewardsKey(t *testing.T) { require.Equal(nodeID[:], key[len(prefix):len(prefix)+len(nodeID[:])]) require.Equal(subnetID[:], key[len(prefix)+len(nodeID[:]):]) } + +func TestMerkleTxKey(t *testing.T) { + require := require.New(t) + txID := ids.GenerateTestID() + + key := merkleTxKey(txID) + require.Len(key, len(txsSectionPrefix)+len(txID[:])) + require.Equal(txsSectionPrefix, key[:len(txsSectionPrefix)]) + require.Equal(txID[:], key[len(txsSectionPrefix):]) +} From 1c501d62bacd75d5b4c8400110ec2f6dee9b0b2f Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Thu, 30 Nov 2023 13:21:35 -0500 Subject: [PATCH 14/16] appease linter --- vms/platformvm/state/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 691d15551e53..77310af0b2ba 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -1906,7 +1906,7 @@ func (s *state) writeTxs() error { // would not be properly accounted for in the cache sizing. s.txCache.Evict(txID) key := merkleTxKey(txID) - if err := s.merkleDB.Put(key[:], txBytes); err != nil { + if err := s.merkleDB.Put(key, txBytes); err != nil { return fmt.Errorf("failed to add tx: %w", err) } } From 3442bcadd72255cf7f32795cddbe794149ef77e5 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 5 Dec 2023 10:01:53 -0500 Subject: [PATCH 15/16] revert unneeded change --- vms/platformvm/state/state.go | 14 +++++++------- vms/platformvm/state/state_helpers.go | 6 +++--- vms/platformvm/state/state_helpers_test.go | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index e6d0a6739da6..c7e1d3370a7d 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -63,11 +63,12 @@ var ( merkleSingletonPrefix = []byte{0x01} merkleBlockPrefix = []byte{0x02} merkleBlockIDsPrefix = []byte{0x03} - merkleIndexUTXOsPrefix = []byte{0x04} // to serve UTXOIDs(addr) - merkleUptimesPrefix = []byte{0x05} // locally measured uptimes - merkleWeightDiffPrefix = []byte{0x06} // non-merkleized validators weight diff. TODO: should we merkleize them? - merkleBlsKeyDiffPrefix = []byte{0x07} - merkleRewardUtxosPrefix = []byte{0x08} + merkleTxsPrefix = []byte{0x04} + merkleIndexUTXOsPrefix = []byte{0x05} // to serve UTXOIDs(addr) + merkleUptimesPrefix = []byte{0x06} // locally measured uptimes + merkleWeightDiffPrefix = []byte{0x07} // non-merkleized validators weight diff. TODO: should we merkleize them? + merkleBlsKeyDiffPrefix = []byte{0x08} + merkleRewardUtxosPrefix = []byte{0x09} initializedKey = []byte{0x00} lastAcceptedBlockIDKey = []byte{0x01} @@ -85,7 +86,6 @@ var ( pendingStakersSectionPrefix = []byte{0x06} delegateeRewardsPrefix = []byte{0x07} subnetOwnersPrefix = []byte{0x08} - txsSectionPrefix = []byte{0x09} ) // Chain collects all methods to manage the state of the chain for block @@ -370,7 +370,7 @@ func newState( flatValidatorWeightDiffsDB = prefixdb.New(merkleWeightDiffPrefix, baseDB) flatValidatorPublicKeyDiffsDB = prefixdb.New(merkleBlsKeyDiffPrefix, baseDB) rewardUTXOsDB = prefixdb.New(merkleRewardUtxosPrefix, baseDB) - txDB = prefixdb.New(txsSectionPrefix, baseDB) + txDB = prefixdb.New(merkleTxsPrefix, baseDB) ) noOpTracer, err := trace.New(trace.Config{Enabled: false}) diff --git a/vms/platformvm/state/state_helpers.go b/vms/platformvm/state/state_helpers.go index 5c922d2dd6e7..91532169c663 100644 --- a/vms/platformvm/state/state_helpers.go +++ b/vms/platformvm/state/state_helpers.go @@ -130,8 +130,8 @@ func merkleSubnetOwnersKey(subnetID ids.ID) []byte { } func merkleTxKey(txID ids.ID) []byte { - key := make([]byte, len(txsSectionPrefix)+ids.IDLen) - copy(key, txsSectionPrefix) - copy(key[len(txsSectionPrefix):], txID[:]) + key := make([]byte, len(merkleTxsPrefix)+ids.IDLen) + copy(key, merkleTxsPrefix) + copy(key[len(merkleTxsPrefix):], txID[:]) return key } diff --git a/vms/platformvm/state/state_helpers_test.go b/vms/platformvm/state/state_helpers_test.go index fb96bd780bf2..1905c8f782f9 100644 --- a/vms/platformvm/state/state_helpers_test.go +++ b/vms/platformvm/state/state_helpers_test.go @@ -146,7 +146,7 @@ func TestMerkleTxKey(t *testing.T) { txID := ids.GenerateTestID() key := merkleTxKey(txID) - require.Len(key, len(txsSectionPrefix)+len(txID[:])) - require.Equal(txsSectionPrefix, key[:len(txsSectionPrefix)]) - require.Equal(txID[:], key[len(txsSectionPrefix):]) + require.Len(key, len(merkleTxsPrefix)+len(txID[:])) + require.Equal(merkleTxsPrefix, key[:len(merkleTxsPrefix)]) + require.Equal(txID[:], key[len(merkleTxsPrefix):]) } From db27c1731f6abc790037aca59fff9f08ead2160d Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 5 Dec 2023 10:06:20 -0500 Subject: [PATCH 16/16] remove txDB --- vms/platformvm/state/state.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index c7e1d3370a7d..ed2598ce974a 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -277,7 +277,6 @@ type state struct { // FIND a way to reduce use of these. No use in verification of addedTxs // a limited windows to support APIs addedTxs map[ids.ID]*txAndStatus // map of txID -> {*txs.Tx, Status} - txDB database.Database indexedUTXOsDB database.Database @@ -370,7 +369,6 @@ func newState( flatValidatorWeightDiffsDB = prefixdb.New(merkleWeightDiffPrefix, baseDB) flatValidatorPublicKeyDiffsDB = prefixdb.New(merkleBlsKeyDiffPrefix, baseDB) rewardUTXOsDB = prefixdb.New(merkleRewardUtxosPrefix, baseDB) - txDB = prefixdb.New(merkleTxsPrefix, baseDB) ) noOpTracer, err := trace.New(trace.Config{Enabled: false}) @@ -425,7 +423,6 @@ func newState( blockIDDB: blockIDsDB, addedTxs: make(map[ids.ID]*txAndStatus), - txDB: txDB, indexedUTXOsDB: indexedUTXOsDB, @@ -640,7 +637,8 @@ func (s *state) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { return tx.tx, tx.status, nil } - txBytes, err := s.txDB.Get(txID[:]) + key := merkleTxKey(txID) + txBytes, err := s.merkleDB.Get(key) if err != nil { return nil, status.Unknown, err } @@ -1634,7 +1632,8 @@ func (s *state) writeTxs() error { // Note: Evict is used rather than Put here because stx may end up // referencing additional data (because of shared byte slices) that // would not be properly accounted for in the cache sizing. - if err := s.txDB.Put(txID[:], txBytes); err != nil { + key := merkleTxKey(txID) + if err := s.merkleDB.Put(key, txBytes); err != nil { return fmt.Errorf("failed to add tx: %w", err) } }