diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 91ed7da26..c9fe95e5e 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -348,6 +348,43 @@ func (bc *Blockchain) init() error { return fmt.Errorf("can't init cache for Management native contract: %w", err) } + // Check autogenerated native contracts' manifests and NEFs against the stored ones. + // Need to be done after native Management cache initialisation to be able to get + // contract state from DAO via high-level bc API. + for _, c := range bc.contracts.Contracts { + md := c.Metadata() + history := md.UpdateHistory + if len(history) == 0 || history[0] > bHeight { + continue + } + storedCS := bc.GetContractState(md.Hash) + if storedCS == nil { + return fmt.Errorf("native contract %s is not stored", md.Name) + } + w := io.NewBufBinWriter() + storedCS.EncodeBinary(w.BinWriter) + if w.Err != nil { + return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, w.Err) + } + buff := w.Bytes() + storedCSBytes := make([]byte, len(buff)) + copy(storedCSBytes, buff) + w.Reset() + autogenCS := &state.Contract{ + ContractBase: md.ContractBase, + UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value. + } + autogenCS.EncodeBinary(w.BinWriter) + if w.Err != nil { + return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, w.Err) + } + autogenCSBytes := w.Bytes() + if !bytes.Equal(storedCSBytes, autogenCSBytes) { + return fmt.Errorf("native %s: version mismatch (stored contract state differs from autogenerated one), "+ + "try to resynchronize the node from the genesis", md.Name) + } + } + return bc.updateExtensibleWhitelist(bHeight) }