Merge pull request #1083 from nspcc-dev/fix/stateroot

Request verified state roots
This commit is contained in:
Roman Khimov 2020-06-22 16:42:41 +03:00 committed by GitHub
commit 3a6186af54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 12 deletions

View file

@ -1776,12 +1776,18 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction) bool {
}
// StateHeight returns height of the verified state root.
func (bc *Blockchain) StateHeight() uint32 {
h, _ := bc.dao.GetCurrentStateRootHeight()
return h
}
// AddStateRoot add new (possibly unverified) state root to the blockchain.
func (bc *Blockchain) AddStateRoot(r *state.MPTRoot) error {
our, err := bc.GetStateRoot(r.Index)
if err == nil {
if our.Flag == state.Verified {
return nil
return bc.updateStateHeight(r.Index)
} else if r.Witness == nil && our.Witness != nil {
r.Witness = our.Witness
}
@ -1803,10 +1809,24 @@ func (bc *Blockchain) AddStateRoot(r *state.MPTRoot) error {
}
flag = state.Verified
}
return bc.dao.PutStateRoot(&state.MPTRootState{
err = bc.dao.PutStateRoot(&state.MPTRootState{
MPTRoot: *r,
Flag: flag,
})
if err != nil {
return err
}
return bc.updateStateHeight(r.Index)
}
func (bc *Blockchain) updateStateHeight(newHeight uint32) error {
h, err := bc.dao.GetCurrentStateRootHeight()
if err != nil {
return errors.WithMessage(err, "can't get current state root height")
} else if newHeight == h+1 {
return bc.dao.PutCurrentStateRootHeight(h + 1)
}
return nil
}
// verifyStateRoot checks if state root is valid.

View file

@ -49,6 +49,7 @@ type Blockchainer interface {
References(t *transaction.Transaction) ([]transaction.InOut, error)
mempool.Feer // fee interface
PoolTx(*transaction.Transaction) error
StateHeight() uint32
SubscribeForBlocks(ch chan<- *block.Block)
SubscribeForExecutions(ch chan<- *state.AppExecResult)
SubscribeForNotifications(ch chan<- *state.NotificationEvent)

View file

@ -32,6 +32,7 @@ type DAO interface {
GetContractState(hash util.Uint160) (*state.Contract, error)
GetCurrentBlockHeight() (uint32, error)
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
GetCurrentStateRootHeight() (uint32, error)
GetHeaderHashes() ([]util.Uint256, error)
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
@ -434,6 +435,27 @@ func (dao *Simple) InitMPT(height uint32) error {
return nil
}
// GetCurrentStateRootHeight returns current state root height.
func (dao *Simple) GetCurrentStateRootHeight() (uint32, error) {
key := []byte{byte(storage.DataMPT)}
val, err := dao.Store.Get(key)
if err != nil {
if err == storage.ErrKeyNotFound {
err = nil
}
return 0, err
}
return binary.LittleEndian.Uint32(val), nil
}
// PutCurrentStateRootHeight updates current state root height.
func (dao *Simple) PutCurrentStateRootHeight(height uint32) error {
key := []byte{byte(storage.DataMPT)}
val := make([]byte, 4)
binary.LittleEndian.PutUint32(val, height)
return dao.Store.Put(key, val)
}
// GetStateRoot returns state root of a given height.
func (dao *Simple) GetStateRoot(height uint32) (*state.MPTRootState, error) {
r := new(state.MPTRootState)

View file

@ -154,7 +154,9 @@ func (chain testChain) IsLowPriority(util.Fixed8) bool {
func (chain testChain) PoolTx(*transaction.Transaction) error {
panic("TODO")
}
func (chain testChain) StateHeight() uint32 {
panic("TODO")
}
func (chain testChain) SubscribeForBlocks(ch chan<- *block.Block) {
panic("TODO")
}

View file

@ -620,11 +620,34 @@ func (s *Server) handleGetRootsCmd(p Peer, gr *payload.GetStateRoots) error {
}
// handleStateRootsCmd processees `roots` request.
func (s *Server) handleRootsCmd(rs *payload.StateRoots) error {
func (s *Server) handleRootsCmd(p Peer, rs *payload.StateRoots) error {
h := s.chain.StateHeight()
for i := range rs.Roots {
if rs.Roots[i].Index <= h {
continue
}
_ = s.chain.AddStateRoot(&rs.Roots[i])
}
return nil
// request more state roots from peer if needed
return s.requestStateRoot(p)
}
// requestStateRoot sends `getroots` message to get verified state roots.
func (s *Server) requestStateRoot(p Peer) error {
stateHeight := s.chain.StateHeight()
hdrHeight := s.chain.BlockHeight()
count := uint32(payload.MaxStateRootsAllowed)
if diff := hdrHeight - stateHeight; diff < count {
count = diff
}
if count == 0 {
return nil
}
gr := &payload.GetStateRoots{
Start: stateHeight + 1,
Count: count,
}
return p.EnqueueP2PMessage(s.MkMsg(CMDGetRoots, gr))
}
// handleStateRootCmd processees `stateroot` request.
@ -772,7 +795,7 @@ func (s *Server) handleMessage(peer Peer, msg *Message) error {
return s.handlePong(peer, pong)
case CMDRoots:
rs := msg.Payload.(*payload.StateRoots)
return s.handleRootsCmd(rs)
return s.handleRootsCmd(peer, rs)
case CMDStateRoot:
r := msg.Payload.(*state.MPTRoot)
return s.handleStateRootCmd(r)

View file

@ -251,6 +251,9 @@ func (p *TCPPeer) StartProtocol() {
if p.LastBlockIndex() > p.server.chain.BlockHeight() {
err = p.server.requestBlocks(p)
}
if err == nil {
err = p.server.requestStateRoot(p)
}
if err == nil {
timer.Reset(p.server.ProtoTickInterval)
}

View file

@ -745,10 +745,9 @@ func (s *Server) verifyProof(ps request.Params) (interface{}, *response.Error) {
}
func (s *Server) getStateHeight(_ request.Params) (interface{}, *response.Error) {
height := s.chain.BlockHeight()
return &result.StateHeight{
BlockHeight: height,
StateHeight: height,
BlockHeight: s.chain.BlockHeight(),
StateHeight: s.chain.StateHeight(),
}, nil
}

View file

@ -246,9 +246,8 @@ var rpcTestCases = map[string][]rpcTestCase{
sh, ok := res.(*result.StateHeight)
require.True(t, ok)
h := e.chain.BlockHeight()
require.Equal(t, h, sh.BlockHeight)
require.Equal(t, h, sh.StateHeight)
require.Equal(t, e.chain.BlockHeight(), sh.BlockHeight)
require.Equal(t, e.chain.StateHeight(), sh.StateHeight)
},
},
},