Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cli/util/upload_state.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package util

import (
"encoding/hex"
"fmt"
"strconv"
"time"
Expand Down Expand Up @@ -138,11 +139,14 @@ loop:
attrs = []object.Attribute{
object.NewAttribute(attr, strconv.Itoa(int(height))),
object.NewAttribute("Timestamp", strconv.FormatInt(time.Now().Unix(), 10)),
object.NewAttribute("StateRoot", stateRoot.Root.StringLE()),
object.NewAttribute(neofs.DefaultStateRootAttribute, stateRoot.Root.StringLE()),
object.NewAttribute("StateSyncInterval", strconv.Itoa(syncInterval)),
object.NewAttribute("BlockTime", strconv.FormatUint(h.Timestamp, 10)),
}
)
if len(stateRoot.Witness) > 0 {
attrs = append(attrs, object.NewAttribute(neofs.DefaultWitnessAttribute, hex.EncodeToString(stateRoot.Witness[0].Bytes())))
}
hdr.SetContainerID(containerID)
hdr.SetOwner(signer.UserID())
hdr.SetAttributes(attrs...)
Expand Down
2 changes: 2 additions & 0 deletions docs/neofs-blockstorage.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ in a binary form as a separate object with a unique OID and a set of attributes:
- state root hash in the LE form (`StateRoot:58a5157b7e99eeabf631291f1747ec8eb12ab89461cda888492b17a301de81e8`)
- millisecond-precision block creation timestamp (`BlockTime:1468595301000`)
- second-precision contract state object uploading timestamp (`Timestamp:1742957073`)
- hex-encoded binary representation of state root witness (for validated
stateroots only) (`Witness:c60c408b7c6f48320eb201ed20cce53188a4c6eb989cc71fa49991380d1c53f6527f4b1e014c2f77d7893f372a2f8e1fdf49f4513720a25256196d6531b6c18e4f5d440c4096e6bdeed500011a20f784444d757fe2b3a372d2c63369d0c759dfc5e852b2ae63b41d721aae44eb22284db1e16a032107f1c36986c94e41696f2bcd659827210c40f6d84e945e1f632a2e43b730c728c5f18741868c19f41aa46a151f8f94ea5c880d67f681bd14baa45cfcaa8b1d2c7553f493299e34cd941ba3d6e93044d2392493130c210345e2bbda8d3d9e24d1e9ee61df15d4f435f69a44fe012d86e9cf9377baaa42cd0c210353663d8da8d6c344aade0168c1cfb651db859175a60c48c6bd4000c9e682d0f50c210392fbd1d809a3c62f7dcde8f25454a1570830a21e4b014b3f362a79baf413e1150c21039b45040cc529966165ef5dff3d046a4960520ce616ae170e265d669e0e2de7f414419ed0dc3a`)

The binary form of the contract state object includes serialized sequence of
contract storage item key-value pairs for every deployed contract including
Expand Down
12 changes: 11 additions & 1 deletion internal/fakechain/fakechain.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,12 @@ func (s *FakeStateSync) AddMPTNodes(nodes [][]byte) error {
}

// AddContractStorageItems implements the StateSync interface.
func (s *FakeStateSync) AddContractStorageItems(kv []storage.KeyValue, syncHeight uint32, expectedRoot util.Uint256) error {
func (s *FakeStateSync) AddContractStorageItems(kv []storage.KeyValue) error {
panic("TODO")
}

// InitContractStorageSync implements the StateSync interface.
func (s *FakeStateSync) InitContractStorageSync(r state.MPTRoot) error {
panic("TODO")
}

Expand Down Expand Up @@ -527,3 +532,8 @@ func (s *FakeStateSync) GetStateSyncPoint() uint32 {
func (s *FakeStateSync) GetLastStoredKey() []byte {
panic("TODO")
}

// VerifyWitness implements the StateSync interface.
func (s *FakeStateSync) VerifyWitness(h util.Uint160, c hash.Hashable, w *transaction.Witness, gas int64) (int64, error) {
panic("TODO")
}
10 changes: 7 additions & 3 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -860,22 +860,25 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
if err != nil {
return fmt.Errorf("failed to get checkpoint metadata: %w", err)
}
root = ckpt.MPTRoot
root = ckpt.IntermediateRoot
} else {
blk, err := bc.dao.GetBlock(bc.GetHeaderHash(p + 1))
if err != nil {
return fmt.Errorf("failed to get block to init MPT: %w", err)
}
root = blk.PrevStateRoot
}
bc.stateRoot.JumpToState(&state.MPTRoot{
err := bc.stateRoot.JumpToState(&state.MPTRoot{
Index: p,
Root: root,
})
if err != nil {
return fmt.Errorf("failed to update stateroot module state: %w", err)
}

bc.dao.Store.Delete(jumpStageKey)
bc.dao.Store.Delete([]byte{byte(storage.SYSStateSyncCheckpoint)})
_, err := bc.dao.Store.Persist()
_, err = bc.dao.Store.Persist()
if err != nil {
return fmt.Errorf("failed to persist %d stage of state jump: %w", staleBlocksRemoved, err)
}
Expand Down Expand Up @@ -912,6 +915,7 @@ func (bc *Blockchain) resetRAMState(height uint32, resetHeaders bool) error {
if err != nil {
return fmt.Errorf("failed to initialize natives cache: %w", err)
}
bc.designate.NotifyServices(bc.dao)

if err := bc.updateExtensibleWhitelist(height); err != nil {
return fmt.Errorf("failed to update extensible whitelist: %w", err)
Expand Down
25 changes: 17 additions & 8 deletions pkg/core/dao/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,26 +580,35 @@ func (dao *Simple) GetStateSyncPoint() (uint32, error) {
// StateSyncCheckpoint stores the state of an interrupted contract storage state sync in
// ContractStorageBased mode.
type StateSyncCheckpoint struct {
// MPTRoot is a computed intermediate MPT root at the StateSyncCheckpoint.
MPTRoot util.Uint256
// IsMPTSynced indicates whether sync process is completed.
IsMPTSynced bool
// IntermediateRoot is a computed intermediate root of non-complete MPT
// at the StateSyncCheckpoint.
IntermediateRoot util.Uint256
// TODO: @roman-khimov, technically this and previous commit include a DB schema change since StateSyncCheckpoint is stored in the DB.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a note to release, that's it. State sync should be finished with the old node if it's in process.

// Practically, hardly ever anyone used this feature and StateSyncCheckpoint is removed right after sync.
// So can we avoid DB schema version upgrade?
// Root is the actual state root at the StateSyncPoint.
Root util.Uint256
// Witness is the actual witness of state root at the StateSyncPoint, it
// may be empty if the state object doesn't contain it.
Witness transaction.Witness
// LastStoredKey is the last processed storage key.
LastStoredKey []byte
}

// EncodeBinary encodes StateSyncCheckpoint to binary format.
func (s *StateSyncCheckpoint) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(s.MPTRoot[:])
w.WriteBool(s.IsMPTSynced)
w.WriteBytes(s.IntermediateRoot[:])
w.WriteBytes(s.Root[:])
w.WriteVarBytes(s.LastStoredKey)
s.Witness.EncodeBinary(w)
}

// DecodeBinary decodes StateSyncCheckpoint from binary format.
func (s *StateSyncCheckpoint) DecodeBinary(br *io.BinReader) {
br.ReadBytes(s.MPTRoot[:])
s.IsMPTSynced = br.ReadBool()
br.ReadBytes(s.IntermediateRoot[:])
br.ReadBytes(s.Root[:])
s.LastStoredKey = br.ReadVarBytes()
s.Witness.DecodeBinary(br)
}

// GetStateSyncCheckpoint returns the current StateSyncCheckpoint.
Expand Down
10 changes: 7 additions & 3 deletions pkg/core/dao/dao_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,9 +447,13 @@ func TestPutGetStateSyncCheckPoint(t *testing.T) {

// non-empty store
expected := StateSyncCheckpoint{
MPTRoot: util.Uint256{1, 2, 3},
IsMPTSynced: true,
LastStoredKey: []byte{1, 2, 3},
IntermediateRoot: util.Uint256{1, 2, 3},
Root: util.Uint256{4, 5, 6},
LastStoredKey: []byte{1, 2, 3},
Witness: transaction.Witness{
InvocationScript: []byte{1, 2, 3},
VerificationScript: []byte{1, 2, 3},
},
}
dao.PutStateSyncCheckpoint(expected)
actual, err := dao.GetStateSyncCheckpoint()
Expand Down
1 change: 1 addition & 0 deletions pkg/core/native/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type (
SetOracleService(o OracleService)
SetNotaryService(n NotaryService)
SetStateRootService(s StateRootService)
NotifyServices(dao *dao.Simple)
}

// INotary is an interface required from native Notary contract for
Expand Down
22 changes: 18 additions & 4 deletions pkg/core/native/designate.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,24 @@ func (s *Designate) OnPersist(ic *interop.Context) error {

// PostPersist implements the Contract interface.
func (s *Designate) PostPersist(ic *interop.Context) error {
cache := ic.DAO.GetRWCache(s.ID).(*DesignationCache)
if !cache.rolesChangedFlag {
return nil
s.notifyServicesInternal(ic.DAO, false)
return nil
}

// NotifyServices notifies dependent services about changes in the node roles.
// It does not check whether roles were updated in the last block. It modifies
// Designate cache accordingly.
func (s *Designate) NotifyServices(dao *dao.Simple) {
s.notifyServicesInternal(dao, true)
}

// notifyServicesInternal is the internal implementation of NotifyServices. It
// accepts an additional force flag to force services notification even if no
// roles were updated in the last block.
func (s *Designate) notifyServicesInternal(dao *dao.Simple, force bool) {
cache := dao.GetRWCache(s.ID).(*DesignationCache)
if !cache.rolesChangedFlag && !force {
return
}

s.notifyRoleChanged(&cache.oracles, noderoles.Oracle)
Expand All @@ -195,7 +210,6 @@ func (s *Designate) PostPersist(ic *interop.Context) error {
s.notifyRoleChanged(&cache.notaries, noderoles.P2PNotary)

cache.rolesChangedFlag = false
return nil
}

// Metadata returns contract metadata.
Expand Down
10 changes: 7 additions & 3 deletions pkg/core/stateroot/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func (s *Module) CleanStorage() error {
}

// JumpToState performs jump to the state specified by given stateroot index.
func (s *Module) JumpToState(sr *state.MPTRoot) {
func (s *Module) JumpToState(sr *state.MPTRoot) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always returns nil (at least as of the commit where it's added).

s.addLocalStateRoot(s.Store, sr)

data := make([]byte, 4)
Expand All @@ -234,6 +234,8 @@ func (s *Module) JumpToState(sr *state.MPTRoot) {
s.currentLocal.Store(sr.Root)
s.localHeight.Store(sr.Index)
s.mpt = mpt.NewTrie(mpt.NewHashNode(sr.Root), s.mode, s.Store)

return nil
}

// ResetState resets MPT state to the given height.
Expand Down Expand Up @@ -371,13 +373,15 @@ func (s *Module) VerifyStateRoot(r *state.MPTRoot) error {
return s.verifyWitness(r)
}

const maxVerificationGAS = 2_00000000
// MaxVerificationGAS is the maximum amount of GAS that can be spent for stateroot
// witness verification.
const MaxVerificationGAS = 2_00000000

// verifyWitness verifies state root witness.
func (s *Module) verifyWitness(r *state.MPTRoot) error {
s.mtx.Lock()
h := s.getKeyCacheForHeight(r.Index).validatorsHash
s.mtx.Unlock()
_, err := s.verifier(h, r, &r.Witness[0], maxVerificationGAS)
_, err := s.verifier(h, r, &r.Witness[0], MaxVerificationGAS)
return err
}
Loading
Loading