From eccf6398030d1ae0a75924dc4a26057d4fa62ed1 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 2 Oct 2020 14:44:42 +0300 Subject: [PATCH] core: add InitializeCache method to NEO native contracts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There might be a case when cached contract values store nil (e.g. after restoring chain from dump). We should always initialize cached values irrespective to the (NEO).Initialize method. This commit fixes a bug introduced in 83e94d3 when 4-nodes privnet is failing after restoring from dump: ``` $ docker logs neo_go_node_one => Try to restore blocks before running node 2020-09-30T11:55:49.122Z INFO no storage version found! creating genesis block 2020-09-30T11:55:49.124Z INFO service hasn't started since it's disabled {"service": "Pprof"} 2020-09-30T11:55:49.124Z INFO service hasn't started since it's disabled {"service": "Prometheus"} 2020-09-30T11:55:49.124Z INFO skipped genesis block {"hash": "3792eaa22c196399a114666fd491c4b9ac52491d9abb1f633a8036a8ac81e4db"} 2020-09-30T11:55:49.141Z INFO shutting down service {"service": "Pprof", "endpoint": ":30001"} 2020-09-30T11:55:49.141Z INFO shutting down service {"service": "Prometheus", "endpoint": ":40001"} 2020-09-30T11:55:49.141Z INFO blockchain persist completed {"persistedBlocks": 3, "persistedKeys": 146, "headerHeight": 3, "blockHeight": 3, "took": "324.27µs"} 2020-09-30T11:55:49.150Z INFO restoring blockchain {"version": "0.1.0"} 2020-09-30T11:55:49.150Z INFO service hasn't started since it's disabled {"service": "Prometheus"} 2020-09-30T11:55:49.151Z INFO service hasn't started since it's disabled {"service": "Pprof"} 2020-09-30T11:55:49.443Z INFO starting rpc-server {"endpoint": ":30333"} 2020-09-30T11:55:49.443Z INFO node started {"blockHeight": 3, "headerHeight": 3} _ ____________ __________ / | / / ____/ __ \ / ____/ __ \ / |/ / __/ / / / /_____/ / __/ / / / / /| / /___/ /_/ /_____/ /_/ / /_/ / /_/ |_/_____/\____/ \____/\____/ /NEO-GO:/ 2020-09-30T11:55:49.444Z INFO new peer connected {"addr": "172.23.0.5:39638", "peerCount": 1} 2020-09-30T11:55:49.444Z INFO new peer connected {"addr": "172.23.0.5:20333", "peerCount": 2} 2020-09-30T11:55:49.444Z WARN peer disconnected {"addr": "172.23.0.5:20333", "reason": "identical node id", "peerCount": 1} 2020-09-30T11:55:49.445Z WARN peer disconnected {"addr": "172.23.0.5:39638", "reason": "identical node id", "peerCount": 0} 2020-09-30T11:55:49.445Z INFO new peer connected {"addr": "172.23.0.3:20335", "peerCount": 1} 2020-09-30T11:55:49.445Z INFO new peer connected {"addr": "172.23.0.2:20334", "peerCount": 2} 2020-09-30T11:55:49.445Z INFO started protocol {"addr": "172.23.0.3:20335", "userAgent": "/NEO-GO:/", "startHeight": 3, "id": 1339919829} 2020-09-30T11:55:49.445Z INFO new peer connected {"addr": "172.23.0.4:20336", "peerCount": 3} 2020-09-30T11:55:49.445Z INFO started protocol {"addr": "172.23.0.4:20336", "userAgent": "/NEO-GO:/", "startHeight": 3, "id": 4036722359} 2020-09-30T11:55:49.445Z INFO node reached synchronized state, starting consensus 2020-09-30T11:55:49.445Z INFO started protocol {"addr": "172.23.0.2:20334", "userAgent": "/NEO-GO:/", "startHeight": 3, "id": 1557367037} panic: runtime error: integer divide by zero goroutine 132 [running]: github.com/nspcc-dev/dbft.(*Context).GetPrimaryIndex(...) github.com/nspcc-dev/dbft@v0.0.0-20200925163137-8f3b9ab3b720/context.go:83 github.com/nspcc-dev/dbft.(*Context).reset(0xc0000e0780, 0x0) github.com/nspcc-dev/dbft@v0.0.0-20200925163137-8f3b9ab3b720/context.go:208 +0x64b github.com/nspcc-dev/dbft.(*DBFT).InitializeConsensus(0xc0000e0780, 0x964800) github.com/nspcc-dev/dbft@v0.0.0-20200925163137-8f3b9ab3b720/dbft.go:87 +0x51 github.com/nspcc-dev/dbft.(*DBFT).Start(0xc0000e0780) github.com/nspcc-dev/dbft@v0.0.0-20200925163137-8f3b9ab3b720/dbft.go:81 +0x4b github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start(0xc0001a2160) github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:206 +0x56 github.com/nspcc-dev/neo-go/pkg/network.(*Server).tryStartConsensus(0xc0000ec500) github.com/nspcc-dev/neo-go/pkg/network/server.go:311 +0xda github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ec500, 0x104d800, 0xc000222090, 0xc0000a6f10, 0x0, 0x0) github.com/nspcc-dev/neo-go/pkg/network/server.go:781 +0xa7a github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc000222090) github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:162 +0x2e7 created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:40 +0x1ac ``` --- pkg/core/blockchain.go | 5 ++++ pkg/core/native/native_neo.go | 43 +++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 20d3446df..d5c84c7bd 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -253,6 +253,11 @@ func (bc *Blockchain) init() error { } } + err = bc.contracts.NEO.InitializeCache(bc, bc.dao) + if err != nil { + return fmt.Errorf("can't init cache for NEO native contract: %w", err) + } + return nil } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index dc48a8d5b..43830e1cc 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -204,6 +204,30 @@ func (n *NEO) Initialize(ic *interop.Context) error { return nil } +// InitializeCache initializes all NEO cache with the proper values from storage. +// Cache initialisation should be done apart from Initialize because Initialize is +// called only when deploying native contracts. +func (n *NEO) InitializeCache(bc blockchainer.Blockchainer, d dao.DAO) error { + committee := keys.PublicKeys{} + si := d.GetStorageItem(n.ContractID, prefixCommittee) + if err := committee.DecodeBytes(si.Value); err != nil { + return err + } + if err := n.updateCache(committee, bc); err != nil { + return err + } + + var gr state.GASRecord + si = d.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock}) + if err := gr.FromBytes(si.Value); err != nil { + return err + } + n.gasPerBlock.Store(gr) + n.gasPerBlockChanged.Store(false) + + return nil +} + func (n *NEO) updateCache(committee keys.PublicKeys, bc blockchainer.Blockchainer) error { n.committee.Store(committee) script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy()) @@ -247,25 +271,6 @@ func shouldUpdateCommittee(h uint32, bc blockchainer.Blockchainer) bool { // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { - gpb := n.gasPerBlockChanged.Load() - if gpb == nil { - committee := keys.PublicKeys{} - si := ic.DAO.GetStorageItem(n.ContractID, prefixCommittee) - if err := committee.DecodeBytes(si.Value); err != nil { - return err - } - if err := n.updateCache(committee, ic.Chain); err != nil { - return err - } - - var gr state.GASRecord - si = ic.DAO.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock}) - if err := gr.FromBytes(si.Value); err != nil { - return err - } - n.gasPerBlock.Store(gr) - n.gasPerBlockChanged.Store(false) - } if shouldUpdateCommittee(ic.Block.Index, ic.Chain) { if err := n.updateCommittee(ic); err != nil { return err