diff --git a/config/protocol.privnet.docker.single.yml b/config/protocol.privnet.docker.single.yml index cbc7bbd350..a3c03f90ec 100644 --- a/config/protocol.privnet.docker.single.yml +++ b/config/protocol.privnet.docker.single.yml @@ -10,6 +10,12 @@ ProtocolConfiguration: - node_single:20333 VerifyTransactions: true P2PSigExtensions: false + Hardforks: + Aspidochelone: 5 + Basilisk: 10 + Cockatrice: 15 + Domovoi: 20 + Echidna: 50 ApplicationConfiguration: SkipBlockVerification: false diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index da46e78604..7bd6b61f5a 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -373,7 +373,7 @@ func TestAppCall(t *testing.T) { } fc := fakechain.NewFakeChain() - ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false), + ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore()), interop.DefaultBaseExecFee, native.DefaultStoragePrice, contractGetter, nil, nil, nil, nil, zaptest.NewLogger(t)) t.Run("valid script", func(t *testing.T) { diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 6472d46daa..d8a881f731 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -753,17 +753,23 @@ func convertKeys(validators []dbft.PublicKey) (pubs []*keys.PublicKey) { } func (s *service) newBlockFromContext(ctx *dbft.Context[util.Uint256]) dbft.Block[util.Uint256] { - block := &neoBlock{network: s.ProtocolConfiguration.Magic} + var ( + blockVersion = uint32(coreb.VersionInitial) + block = &neoBlock{network: s.ProtocolConfiguration.Magic} + ) + hfe, ok := s.ProtocolConfiguration.Hardforks[config.HFEchidna.String()] + if ok && hfe <= ctx.BlockIndex { + blockVersion = coreb.VersionEchidna + } block.Block.Timestamp = ctx.Timestamp / nsInMs block.Block.Nonce = ctx.Nonce block.Block.Index = ctx.BlockIndex - if s.ProtocolConfiguration.StateRootInHeader { + if blockVersion > coreb.VersionInitial { sr, err := s.Chain.GetStateRoot(ctx.BlockIndex - 1) if err != nil { s.log.Fatal(fmt.Sprintf("failed to get state root: %s", err.Error())) } - block.StateRootEnabled = true block.PrevStateRoot = sr.Root } @@ -784,7 +790,7 @@ func (s *service) newBlockFromContext(ctx *dbft.Context[util.Uint256]) dbft.Bloc } block.Block.NextConsensus = hash.Hash160(script) block.Block.PrevHash = ctx.PrevHash - block.Block.Version = coreb.VersionInitial + block.Block.Version = blockVersion primaryIndex := byte(ctx.PrimaryIndex) block.Block.PrimaryIndex = primaryIndex diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go index 3e451258f9..c147e79ebb 100644 --- a/pkg/core/block/block.go +++ b/pkg/core/block/block.go @@ -68,11 +68,8 @@ func (b *Block) RebuildMerkleRoot() { // This is commonly used to create a block from stored data. // Blocks created from trimmed data will have their Trimmed field // set to true. -func NewTrimmedFromReader(stateRootEnabled bool, br *io.BinReader) (*Block, error) { +func NewTrimmedFromReader(br *io.BinReader) (*Block, error) { block := &Block{ - Header: Header{ - StateRootEnabled: stateRootEnabled, - }, Trimmed: true, } @@ -93,11 +90,11 @@ func NewTrimmedFromReader(stateRootEnabled bool, br *io.BinReader) (*Block, erro return block, br.Err } -// New creates a new blank block with proper state root setting. -func New(stateRootEnabled bool) *Block { +// New creates a new blank block of appropriate version. +func New(version uint32) *Block { return &Block{ Header: Header{ - StateRootEnabled: stateRootEnabled, + Version: version, }, } } @@ -213,7 +210,7 @@ func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int { size := expectedHeaderSizeWithEmptyWitness - 1 - 1 + // 1 is for the zero-length (new(Header)).Script.Invocation/Verification io.GetVarSize(&b.Script) + io.GetVarSize(txCount) - if b.StateRootEnabled { + if b.Version > 0 { size += util.Uint256Size } return size @@ -233,7 +230,7 @@ func (b *Block) ToStackItem() stackitem.Item { stackitem.NewByteArray(b.NextConsensus.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))), } - if b.StateRootEnabled { + if b.Version > 0 { items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE())) } diff --git a/pkg/core/block/block_test.go b/pkg/core/block/block_test.go index f0e708f229..ea464b3702 100644 --- a/pkg/core/block/block_test.go +++ b/pkg/core/block/block_test.go @@ -31,7 +31,7 @@ func TestDecodeBlock1(t *testing.T) { b, err := hex.DecodeString(data["raw"].(string)) require.NoError(t, err) - block := New(false) + block := &Block{} assert.NoError(t, testserdes.DecodeBinary(b, block)) assert.Equal(t, uint32(data["index"].(float64)), block.Index) @@ -60,7 +60,7 @@ func TestTrimmedBlock(t *testing.T) { require.NoError(t, buf.Err) r := io.NewBinReaderFromBuf(buf.Bytes()) - trimmedBlock, err := NewTrimmedFromReader(false, r) + trimmedBlock, err := NewTrimmedFromReader(r) require.NoError(t, err) assert.True(t, trimmedBlock.Trimmed) @@ -110,7 +110,7 @@ func TestBinBlockDecodeEncode(t *testing.T) { rawblock := "AAAAAAwIVa2D6Yha3tArd5XnwkAf7deJBsdyyvpYb2xMZGBbkOUNHAsfre0rKA/F+Ox05/bQSXmcRZnzK3M6Z+/TxJUh0MNFeAEAAAAAAAAAAAAAAQAAAADe7nnBifMAmLC6ai65CzqSWKbH/wHGDEDgwCcXkcaFw5MGOp1cpkgApzDTX2/RxKlmPeXTgWYtfEA8g9svUSbZA4TeoGyWvX8LiN0tJKrzajdMGvTVGqVmDEDp6PBmZmRx9CxswtLht6oWa2Uq4rl5diPsLtqXZeZepMlxUSbaCdlFTB7iWQG9yKXWR5hc0sScevvuVwwsUYdlDEDwlhwZrP07E5fEQKttVMYAiL7edd/eW2yoMGZe6Q95g7yXQ69edVHfQb61fBw3DjCpMDZ5lsxp3BgzXglJwMSKkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQIAWNC7C8DYpwAAAAAAIKpEAAAAAADoAwAAAd7uecGJ8wCYsLpqLrkLOpJYpsf/AQBbCwIA4fUFDBSAzse29bVvUFePc38WLTqxTUZlDQwU3u55wYnzAJiwumouuQs6klimx/8UwB8MCHRyYW5zZmVyDBT1Y+pAvCg9TQ4FxI6jBbPyoHNA70FifVtSOQHGDEC4UIzT61GYPx0LdksrF6C2ioYai6fbwpjv3BGAqiyagxiomYGZRLeXZyD67O5FJ86pXRFtSbVYu2YDG+T5ICIgDEDzm/wl+BnHvQXaHQ1rGLtdUMc41wN6I48kPPM7F23gL9sVxGziQIMRLnpTbWHrnzaU9Sy0fXkvIrdJy1KABkSQDEDBwuBuVK+nsZvn1oAscPj6d3FJiUGK9xiHpX9Ipp/5jTnXRBAyzyGc8IZMBVql4WS8kwFe6ojA/9BvFb5eWXnEkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQDYJLwZwNinAAAAAAAgqkQAAAAAAOgDAAAB3u55wYnzAJiwumouuQs6klimx/8BAF8LAwBA2d2ITQoADBSAzse29bVvUFePc38WLTqxTUZlDQwU3u55wYnzAJiwumouuQs6klimx/8UwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSOQHGDEDWn0D7z2ELqpN8ghcM/PtfFwo56/BfEasfHuSKECJMYxvU47r2ZtSihg59lGxSZzHsvxTy6nsyvJ22ycNhINdJDECl61cg937N/HujKsLMu2wJMS7C54bzJ3q22Czqllvw3Yp809USgKDs+W+3QD7rI+SFs0OhIn0gooCUU6f/13WjDEDr9XdeT5CGTO8CL0JigzcTcucs0GBcqHs8fToO6zPuuCfS7Wh6dyxSCijT4A4S+7BUdW3dsO7828ke1fj8oNxmkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQ==" rawblockBytes, _ := base64.StdEncoding.DecodeString(rawblock) - b := New(false) + b := &Block{} assert.NoError(t, testserdes.DecodeBinary(rawblockBytes, b)) expected := map[string]bool{ // 1 trans @@ -144,12 +144,12 @@ func TestBinBlockDecodeEncode(t *testing.T) { assert.NoError(t, err) assert.Equal(t, rawblock, base64.StdEncoding.EncodeToString(data)) - testserdes.MarshalUnmarshalJSON(t, b, New(false)) + testserdes.MarshalUnmarshalJSON(t, b, &Block{}) } func TestJSONEmptyTx(t *testing.T) { jblock := `{"hash":"0x5f3fbb43d1e516fe07771e2e873ebc9e2810662401acf603a775aace486220bd","version":0,"previousblockhash":"0x1f4d1defa46faa5e7b9b8d3f79a06bec777d7c26c4aa5f6f5899a291daa87c15","merkleroot":"0x0000000000000000000000000000000000000000000000000000000000000000","time":1627894840919,"nonce":"BA8E021F1AEEA3F6","index":1,"nextconsensus":"NVg7LjGcUSrgxgjX3zEgqaksfMaiS8Z6e1","primary":0,"witnesses":[{"invocation":"DEAq7W/jUhpMon1t9muqXKfBvNyGwLfFFM1vAxrMKvUl6MqK+LL/lJAJP9uAk/cberIWWhSsdcxUtltkBLemg/VuDECQZGuvP93JlZga2ml8cnbe5cNiGgO0EMrbGYyzvgr8calP5SwMNPSYms10gIHxlsuXDU++EQpZu/vKxfHoxdC5DEDgsA3POVZdfN+i5+ekvtsaIvif42n0GC+dZi3Rp37ETmt4NtkoK2I2UXi+WIjm5yXLJsPhAvEV6cJSrvqBdsQBDEDTS6NU+kB+tgeBe9lWv+6y0L2qcUBIaUxiTCaNWZtLPghQICBvjDz1/9ttJRXG3I5N9CFDjjLKCpdIY842HW4/DEC+wlWjkCzVqzKslvpCKZbEPUGIf87CFAD88xqzl26m/TpTUcT0+D5oI2bVzAk0mcdBTPnyjcNbv17BFmr63+09","verification":"FQwhAkhv0VcCxEkKJnAxEqXMHQkj/Wl6M0Br1aHADgATsJpwDCECTHt/tsMQ/M8bozsIJRnYKWTqk4aNZ2Zi1KWa1UjfDn0MIQKq7DhHD2qtAELG6HfP2Ah9Jnaw9Rb93TYoAbm9OTY5ngwhA7IJ/U9TpxcOpERODLCmu2pTwr0BaSaYnPhfmw+6F6cMDCEDuNnVdx2PUTqghpucyNUJhkA7eMbaNokGOMPUalrc4EoMIQLKDidpe5wkj28W4IX9AGHib0TahbWO6DXBEMql7DulVAwhAt9I9g6PPgHEj/QLm38TENeosqGTGIvv4cLj33QOiVCTF0Ge0Nw6"}],"tx":[]}` - b := New(false) + b := &Block{} require.NoError(t, json.Unmarshal([]byte(jblock), b)) s, err := json.Marshal(b) require.NoError(t, err) @@ -166,7 +166,7 @@ func TestBlockSizeCalculation(t *testing.T) { rawBlock := "AAAAAAwIVa2D6Yha3tArd5XnwkAf7deJBsdyyvpYb2xMZGBbkOUNHAsfre0rKA/F+Ox05/bQSXmcRZnzK3M6Z+/TxJUh0MNFeAEAAAAAAAAAAAAAAQAAAADe7nnBifMAmLC6ai65CzqSWKbH/wHGDEDgwCcXkcaFw5MGOp1cpkgApzDTX2/RxKlmPeXTgWYtfEA8g9svUSbZA4TeoGyWvX8LiN0tJKrzajdMGvTVGqVmDEDp6PBmZmRx9CxswtLht6oWa2Uq4rl5diPsLtqXZeZepMlxUSbaCdlFTB7iWQG9yKXWR5hc0sScevvuVwwsUYdlDEDwlhwZrP07E5fEQKttVMYAiL7edd/eW2yoMGZe6Q95g7yXQ69edVHfQb61fBw3DjCpMDZ5lsxp3BgzXglJwMSKkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQIAWNC7C8DYpwAAAAAAIKpEAAAAAADoAwAAAd7uecGJ8wCYsLpqLrkLOpJYpsf/AQBbCwIA4fUFDBSAzse29bVvUFePc38WLTqxTUZlDQwU3u55wYnzAJiwumouuQs6klimx/8UwB8MCHRyYW5zZmVyDBT1Y+pAvCg9TQ4FxI6jBbPyoHNA70FifVtSOQHGDEC4UIzT61GYPx0LdksrF6C2ioYai6fbwpjv3BGAqiyagxiomYGZRLeXZyD67O5FJ86pXRFtSbVYu2YDG+T5ICIgDEDzm/wl+BnHvQXaHQ1rGLtdUMc41wN6I48kPPM7F23gL9sVxGziQIMRLnpTbWHrnzaU9Sy0fXkvIrdJy1KABkSQDEDBwuBuVK+nsZvn1oAscPj6d3FJiUGK9xiHpX9Ipp/5jTnXRBAyzyGc8IZMBVql4WS8kwFe6ojA/9BvFb5eWXnEkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQDYJLwZwNinAAAAAAAgqkQAAAAAAOgDAAAB3u55wYnzAJiwumouuQs6klimx/8BAF8LAwBA2d2ITQoADBSAzse29bVvUFePc38WLTqxTUZlDQwU3u55wYnzAJiwumouuQs6klimx/8UwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSOQHGDEDWn0D7z2ELqpN8ghcM/PtfFwo56/BfEasfHuSKECJMYxvU47r2ZtSihg59lGxSZzHsvxTy6nsyvJ22ycNhINdJDECl61cg937N/HujKsLMu2wJMS7C54bzJ3q22Czqllvw3Yp809USgKDs+W+3QD7rI+SFs0OhIn0gooCUU6f/13WjDEDr9XdeT5CGTO8CL0JigzcTcucs0GBcqHs8fToO6zPuuCfS7Wh6dyxSCijT4A4S+7BUdW3dsO7828ke1fj8oNxmkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBe85spQ==" rawBlockBytes, _ := base64.StdEncoding.DecodeString(rawBlock) - b := New(false) + b := &Block{} assert.NoError(t, testserdes.DecodeBinary(rawBlockBytes, b)) expected := []struct { @@ -243,21 +243,27 @@ func TestGetExpectedBlockSize(t *testing.T) { check := func(t *testing.T, stateRootEnabled bool) { t.Run("without transactions", func(t *testing.T) { b := newDumbBlock() - b.StateRootEnabled = stateRootEnabled + if stateRootEnabled { + b.Version = VersionEchidna + } b.Transactions = []*transaction.Transaction{} require.Equal(t, io.GetVarSize(b), b.GetExpectedBlockSize()) require.Equal(t, io.GetVarSize(b), b.GetExpectedBlockSizeWithoutTransactions(0)) }) t.Run("with one transaction", func(t *testing.T) { b := newDumbBlock() - b.StateRootEnabled = stateRootEnabled + if stateRootEnabled { + b.Version = VersionEchidna + } expected := io.GetVarSize(b) require.Equal(t, expected, b.GetExpectedBlockSize()) require.Equal(t, expected-b.Transactions[0].Size(), b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions))) }) t.Run("with multiple transactions", func(t *testing.T) { b := newDumbBlock() - b.StateRootEnabled = stateRootEnabled + if stateRootEnabled { + b.Version = VersionEchidna + } b.Transactions = make([]*transaction.Transaction, 123) for i := range b.Transactions { tx := transaction.New([]byte{byte(opcode.RET)}, int64(i)) diff --git a/pkg/core/block/header.go b/pkg/core/block/header.go index b56e1cbbae..152d831632 100644 --- a/pkg/core/block/header.go +++ b/pkg/core/block/header.go @@ -13,8 +13,12 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" ) -// VersionInitial is the default Neo block version. -const VersionInitial uint32 = 0 +const ( + // VersionInitial is the initial Neo block version. + VersionInitial = iota + // VersionEchidna is StateRoot-enabled Neo block version + VersionEchidna +) // Header holds the base info of a block. type Header struct { @@ -45,8 +49,6 @@ type Header struct { // Script used to validate the block Script transaction.Witness - // StateRootEnabled specifies if the header contains state root. - StateRootEnabled bool // PrevStateRoot is the state root of the previous block. PrevStateRoot util.Uint256 // PrimaryIndex is the index of the primary consensus node for this block. @@ -126,7 +128,7 @@ func (b *Header) encodeHashableFields(bw *io.BinWriter) { bw.WriteU32LE(b.Index) bw.WriteB(b.PrimaryIndex) bw.WriteBytes(b.NextConsensus[:]) - if b.StateRootEnabled { + if b.Version > 0 { bw.WriteBytes(b.PrevStateRoot[:]) } } @@ -142,7 +144,7 @@ func (b *Header) decodeHashableFields(br *io.BinReader) { b.Index = br.ReadU32LE() b.PrimaryIndex = br.ReadB() br.ReadBytes(b.NextConsensus[:]) - if b.StateRootEnabled { + if b.Version > 0 { br.ReadBytes(b.PrevStateRoot[:]) } @@ -167,7 +169,7 @@ func (b Header) MarshalJSON() ([]byte, error) { NextConsensus: address.Uint160ToString(b.NextConsensus), Witnesses: []transaction.Witness{b.Script}, } - if b.StateRootEnabled { + if b.Version > 0 { aux.PrevStateRoot = &b.PrevStateRoot } return json.Marshal(aux) @@ -206,7 +208,7 @@ func (b *Header) UnmarshalJSON(data []byte) error { b.PrimaryIndex = aux.PrimaryIndex b.NextConsensus = nextC b.Script = aux.Witnesses[0] - if b.StateRootEnabled { + if b.Version > 0 { if aux.PrevStateRoot == nil { return errors.New("'previousstateroot' is empty") } diff --git a/pkg/core/block/header_test.go b/pkg/core/block/header_test.go index 8a1d508221..965f8e2a30 100644 --- a/pkg/core/block/header_test.go +++ b/pkg/core/block/header_test.go @@ -26,12 +26,12 @@ func testHeaderEncodeDecode(t *testing.T, stateRootEnabled bool) { }, } if stateRootEnabled { - header.StateRootEnabled = stateRootEnabled + header.Version = VersionEchidna header.PrevStateRoot = random.Uint256() } _ = header.Hash() - headerDecode := &Header{StateRootEnabled: stateRootEnabled} + headerDecode := &Header{} testserdes.EncodeDecodeBinary(t, &header, headerDecode) assert.Equal(t, header.Version, headerDecode.Version, "expected both versions to be equal") diff --git a/pkg/core/block/helper_test.go b/pkg/core/block/helper_test.go index db61d299fd..d64f987ca7 100644 --- a/pkg/core/block/helper_test.go +++ b/pkg/core/block/helper_test.go @@ -18,7 +18,7 @@ func getDecodedBlock(t *testing.T, i int) *Block { b, err := hex.DecodeString(data["raw"].(string)) require.NoError(t, err) - block := New(false) + block := &Block{} require.NoError(t, testserdes.DecodeBinary(b, block)) return block diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index f8c591f6b0..b14a01fa55 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -271,9 +271,6 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl zap.Uint32("MaxValidUntilBlockIncrement", cfg.MaxValidUntilBlockIncrement)) } if cfg.P2PStateExchangeExtensions { - if !cfg.StateRootInHeader { - return nil, errors.New("P2PStatesExchangeExtensions are enabled, but StateRootInHeader is off") - } if cfg.KeepOnlyLatestState && !cfg.RemoveUntraceableBlocks { return nil, errors.New("P2PStateExchangeExtensions can be enabled either on MPT-complete node (KeepOnlyLatestState=false) or on light GC-enabled node (RemoveUntraceableBlocks=true)") } @@ -308,8 +305,8 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl } bc := &Blockchain{ config: cfg, - dao: dao.NewSimple(s, cfg.StateRootInHeader), - persistent: dao.NewSimple(s, cfg.StateRootInHeader), + dao: dao.NewSimple(s), + persistent: dao.NewSimple(s), store: s, stopCh: make(chan struct{}), runToExitCh: make(chan struct{}), @@ -411,7 +408,6 @@ func (bc *Blockchain) init() error { bc.log.Info("no storage version found! creating genesis block") ver = dao.Version{ StoragePrefix: storage.STStorage, - StateRootInHeader: bc.config.StateRootInHeader, P2PSigExtensions: bc.config.P2PSigExtensions, P2PStateExchangeExtensions: bc.config.P2PStateExchangeExtensions, KeepOnlyLatestState: bc.config.Ledger.KeepOnlyLatestState, @@ -434,10 +430,6 @@ func (bc *Blockchain) init() error { if ver.Value != version { return fmt.Errorf("storage version mismatch (expected=%s, actual=%s)", version, ver.Value) } - if ver.StateRootInHeader != bc.config.StateRootInHeader { - return fmt.Errorf("StateRootInHeader setting mismatch (config=%t, db=%t)", - bc.config.StateRootInHeader, ver.StateRootInHeader) - } if ver.P2PSigExtensions != bc.config.P2PSigExtensions { return fmt.Errorf("P2PSigExtensions setting mismatch (old=%t, new=%t)", ver.P2PSigExtensions, bc.config.P2PSigExtensions) @@ -1485,10 +1477,6 @@ func (bc *Blockchain) AddBlock(block *block.Block) error { if expectedHeight != block.Index { return fmt.Errorf("expected %d, got %d: %w", expectedHeight, block.Index, ErrInvalidBlockIndex) } - if bc.config.StateRootInHeader != block.StateRootEnabled { - return fmt.Errorf("%w: %v != %v", - ErrHdrStateRootSetting, bc.config.StateRootInHeader, block.StateRootEnabled) - } if block.Index == bc.HeaderHeight()+1 { err := bc.addHeaders(!bc.config.SkipBlockVerification, &block.Header) @@ -1749,7 +1737,8 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error // because changes applied are the ones from HALTed transactions. return fmt.Errorf("error while trying to apply MPT changes: %w", err) } - if bc.config.StateRootInHeader && bc.HeaderHeight() > sr.Index { + var hfe = config.HFEchidna + if bc.isHardforkEnabled(&hfe, sr.Index) && bc.HeaderHeight() > sr.Index { h, err := bc.GetHeader(bc.GetHeaderHash(sr.Index + 1)) if err != nil { err = fmt.Errorf("failed to get next header: %w", err) @@ -2509,12 +2498,10 @@ var ( ) func (bc *Blockchain) verifyHeader(currHeader, prevHeader *block.Header) error { - if bc.config.StateRootInHeader { - if bc.stateRoot.CurrentLocalHeight() == prevHeader.Index { - if sr := bc.stateRoot.CurrentLocalStateRoot(); currHeader.PrevStateRoot != sr { - return fmt.Errorf("%w: %s != %s", - ErrHdrInvalidStateRoot, currHeader.PrevStateRoot.StringLE(), sr.StringLE()) - } + if currHeader.Version > block.VersionInitial { + if sr := bc.stateRoot.CurrentLocalStateRoot(); currHeader.PrevStateRoot != sr { + return fmt.Errorf("%w: %s != %s", + ErrHdrInvalidStateRoot, currHeader.PrevStateRoot.StringLE(), sr.StringLE()) } } if prevHeader.Hash() != currHeader.PrevHash { @@ -2861,7 +2848,7 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact return nil, fmt.Errorf("failed to retrieve stateroot for height %d: %w", b.Index, err) } s := mpt.NewTrieStore(sr.Root, mode, storage.NewPrivateMemCachedStore(bc.dao.Store)) - dTrie := dao.NewSimple(s, bc.config.StateRootInHeader) + dTrie := dao.NewSimple(s) dTrie.Version = bc.dao.Version // Initialize native cache before passing DAO to interop context constructor, because // the constructor will call BaseExecFee/StoragePrice policy methods on the passed DAO. @@ -2876,7 +2863,7 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact // getFakeNextBlock returns fake block with the specified index and pre-filled Timestamp field. func (bc *Blockchain) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) { - b := block.New(bc.config.StateRootInHeader) + b := &block.Block{} b.Index = nextBlockHeight hdr, err := bc.GetHeader(bc.GetHeaderHash(nextBlockHeight - 1)) if err != nil { diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 10966375c1..6dba951faa 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -98,7 +98,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) { t.Run("mismatch storage version", func(t *testing.T) { ps = newPS(t) cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. - d := dao.NewSimple(cache, bc.GetConfig().StateRootInHeader) + d := dao.NewSimple(cache) d.PutVersion(dao.Version{ Value: "0.0.0", }) @@ -108,15 +108,6 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) { require.Error(t, err) require.True(t, strings.Contains(err.Error(), "storage version mismatch"), err) }) - t.Run("mismatch StateRootInHeader", func(t *testing.T) { - ps = newPS(t) - _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { - customConfig(c) - c.StateRootInHeader = false - }, ps) - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "StateRootInHeader setting mismatch"), err) - }) t.Run("mismatch P2PSigExtensions", func(t *testing.T) { ps = newPS(t) _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { @@ -528,16 +519,10 @@ func TestBlockchain_AddBlockStateRoot(t *testing.T) { sr, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight()) require.NoError(t, err) - b := e.NewUnsignedBlock(t) - b.StateRootEnabled = false - b.PrevStateRoot = util.Uint256{} - e.SignBlock(b) - err = bc.AddBlock(b) - require.ErrorIs(t, err, core.ErrHdrStateRootSetting) - u := sr.Root u[0] ^= 0xFF - b = e.NewUnsignedBlock(t) + b := e.NewUnsignedBlock(t) + b.Version = block.VersionEchidna b.PrevStateRoot = u e.SignBlock(b) err = bc.AddBlock(b) diff --git a/pkg/core/chaindump/dump.go b/pkg/core/chaindump/dump.go index ee28448338..bcecbf2928 100644 --- a/pkg/core/chaindump/dump.go +++ b/pkg/core/chaindump/dump.go @@ -64,14 +64,12 @@ func Restore(bc DumperRestorer, r *io.BinReader, skip, count uint32, f func(b *b } } - stateRootInHeader := bc.GetConfig().StateRootInHeader - for ; i < skip+count; i++ { buf, err := readBlock(r) if err != nil { return err } - b := block.New(stateRootInHeader) + b := &block.Block{} r := io.NewBinReaderFromBuf(buf) b.DecodeBinary(r) if r.Err != nil { diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 579d355482..d1449e7c9f 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -68,16 +68,15 @@ type NativeContractCache interface { } // NewSimple creates a new simple dao using the provided backend store. -func NewSimple(backend storage.Store, stateRootInHeader bool) *Simple { +func NewSimple(backend storage.Store) *Simple { st := storage.NewMemCachedStore(backend) - return newSimple(st, stateRootInHeader) + return newSimple(st) } -func newSimple(st *storage.MemCachedStore, stateRootInHeader bool) *Simple { +func newSimple(st *storage.MemCachedStore) *Simple { return &Simple{ Version: Version{ - StoragePrefix: storage.STStorage, - StateRootInHeader: stateRootInHeader, + StoragePrefix: storage.STStorage, }, Store: st, nativeCache: make(map[int32]NativeContractCache), @@ -92,7 +91,7 @@ func (dao *Simple) GetBatch() *storage.MemBatch { // GetWrapped returns a new DAO instance with another layer of wrapped // MemCachedStore around the current DAO Store. func (dao *Simple) GetWrapped() *Simple { - d := NewSimple(dao.Store, dao.Version.StateRootInHeader) + d := NewSimple(dao.Store) d.Version = dao.Version d.nativeCachePS = dao return d @@ -275,7 +274,7 @@ func (dao *Simple) GetAppExecResults(hash util.Uint256, trig trigger.Type) ([]st case storage.ExecBlock: r := io.NewBinReaderFromBuf(bs) _ = r.ReadB() - _, err = block.NewTrimmedFromReader(dao.Version.StateRootInHeader, r) + _, err = block.NewTrimmedFromReader(r) if err != nil { return nil, err } @@ -432,7 +431,7 @@ func (dao *Simple) getBlock(key []byte) (*block.Block, error) { // It may be a transaction. return nil, storage.ErrKeyNotFound } - block, err := block.NewTrimmedFromReader(dao.Version.StateRootInHeader, r) + block, err := block.NewTrimmedFromReader(r) if err != nil { return nil, err } @@ -442,7 +441,6 @@ func (dao *Simple) getBlock(key []byte) (*block.Block, error) { // Version represents the current dao version. type Version struct { StoragePrefix storage.KeyPrefix - StateRootInHeader bool P2PSigExtensions bool P2PStateExchangeExtensions bool KeepOnlyLatestState bool @@ -451,7 +449,7 @@ type Version struct { } const ( - stateRootInHeaderBit = 1 << iota + unusedStateRootInHeaderBit = 1 << iota p2pSigExtensionsBit p2pStateExchangeExtensionsBit keepOnlyLatestStateBit @@ -478,7 +476,6 @@ func (v *Version) FromBytes(data []byte) error { v.Value = string(data[:i]) v.StoragePrefix = storage.KeyPrefix(data[i+1]) - v.StateRootInHeader = data[i+2]&stateRootInHeaderBit != 0 v.P2PSigExtensions = data[i+2]&p2pSigExtensionsBit != 0 v.P2PStateExchangeExtensions = data[i+2]&p2pStateExchangeExtensionsBit != 0 v.KeepOnlyLatestState = data[i+2]&keepOnlyLatestStateBit != 0 @@ -493,9 +490,6 @@ func (v *Version) FromBytes(data []byte) error { // Bytes encodes v to a byte-slice. func (v *Version) Bytes() []byte { var mask byte - if v.StateRootInHeader { - mask |= stateRootInHeaderBit - } if v.P2PSigExtensions { mask |= p2pSigExtensionsBit } diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 9db364118d..897ed177b2 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -18,7 +18,7 @@ import ( ) func TestPutGetAndDecode(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) serializable := &TestSerializable{field: random.String(4)} hash := []byte{1} require.NoError(t, dao.putWithBuffer(serializable, hash, io.NewBufBinWriter())) @@ -42,7 +42,7 @@ func (t *TestSerializable) DecodeBinary(reader *io.BinReader) { } func TestPutGetStorageItem(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) id := int32(random.Int(0, 1024)) key := []byte{0} storageItem := state.StorageItem{} @@ -52,7 +52,7 @@ func TestPutGetStorageItem(t *testing.T) { } func TestDeleteStorageItem(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) id := int32(random.Int(0, 1024)) key := []byte{0} storageItem := state.StorageItem{} @@ -63,7 +63,7 @@ func TestDeleteStorageItem(t *testing.T) { } func TestGetBlock_NotExists(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) hash := random.Uint256() block, err := dao.GetBlock(hash) require.Error(t, err) @@ -71,7 +71,7 @@ func TestGetBlock_NotExists(t *testing.T) { } func TestPutGetBlock(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) b := &block.Block{ Header: block.Header{ Script: transaction.Witness{ @@ -110,19 +110,18 @@ func TestPutGetBlock(t *testing.T) { } func TestGetVersion_NoVersion(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) version, err := dao.GetVersion() require.Error(t, err) require.Equal(t, "", version.Value) } func TestGetVersion(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) expected := Version{ - StoragePrefix: 0x42, - P2PSigExtensions: true, - StateRootInHeader: true, - Value: "testVersion", + StoragePrefix: 0x42, + P2PSigExtensions: true, + Value: "testVersion", } dao.PutVersion(expected) actual, err := dao.GetVersion() @@ -130,14 +129,14 @@ func TestGetVersion(t *testing.T) { require.Equal(t, expected, actual) t.Run("invalid", func(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) dao.Store.Put([]byte{byte(storage.SYSVersion)}, []byte("0.1.2\x00x")) _, err := dao.GetVersion() require.Error(t, err) }) t.Run("old format", func(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) dao.Store.Put([]byte{byte(storage.SYSVersion)}, []byte("0.1.2")) version, err := dao.GetVersion() @@ -147,14 +146,14 @@ func TestGetVersion(t *testing.T) { } func TestGetCurrentHeaderHeight_NoHeader(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) height, err := dao.GetCurrentBlockHeight() require.Error(t, err) require.Equal(t, uint32(0), height) } func TestGetCurrentHeaderHeight_Store(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) b := &block.Block{ Header: block.Header{ Script: transaction.Witness{ @@ -171,7 +170,7 @@ func TestGetCurrentHeaderHeight_Store(t *testing.T) { func TestStoreAsTransaction(t *testing.T) { t.Run("no conflicts", func(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1) tx.Signers = append(tx.Signers, transaction.Signer{}) tx.Scripts = append(tx.Scripts, transaction.Witness{}) @@ -195,7 +194,7 @@ func TestStoreAsTransaction(t *testing.T) { }) t.Run("with conflicts", func(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) conflictsH := util.Uint256{1, 2, 3} signer1 := util.Uint160{1, 2, 3} signer2 := util.Uint160{4, 5, 6} @@ -354,7 +353,7 @@ func TestStoreAsTransaction(t *testing.T) { } func BenchmarkStoreAsTransaction(b *testing.B) { - dao := NewSimple(storage.NewMemoryStore(), false) + dao := NewSimple(storage.NewMemoryStore()) tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1) tx.Attributes = []transaction.Attribute{ { @@ -399,7 +398,7 @@ func BenchmarkStoreAsTransaction(b *testing.B) { func TestMakeStorageItemKey(t *testing.T) { var id int32 = 5 - dao := NewSimple(storage.NewMemoryStore(), true) + dao := NewSimple(storage.NewMemoryStore()) expected := []byte{byte(storage.STStorage), 0, 0, 0, 0, 1, 2, 3} binary.LittleEndian.PutUint32(expected[1:5], uint32(id)) @@ -418,7 +417,7 @@ func TestMakeStorageItemKey(t *testing.T) { } func TestPutGetStateSyncPoint(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), true) + dao := NewSimple(storage.NewMemoryStore()) // empty store _, err := dao.GetStateSyncPoint() @@ -433,7 +432,7 @@ func TestPutGetStateSyncPoint(t *testing.T) { } func TestPutGetStateSyncCurrentBlockHeight(t *testing.T) { - dao := NewSimple(storage.NewMemoryStore(), true) + dao := NewSimple(storage.NewMemoryStore()) // empty store _, err := dao.GetStateSyncCurrentBlockHeight() diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index a2466d6e59..331085464e 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -107,7 +107,7 @@ func newBlockWithState(cfg config.ProtocolConfiguration, index uint32, prev util b.Index = index if prevState != nil { - b.StateRootEnabled = true + b.Version = block.VersionEchidna b.PrevStateRoot = *prevState } }, txs...) diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index d789636b78..b9c774a92b 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -72,7 +72,7 @@ func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM { ic := interop.NewContext( trigger.Verification, fakechain.NewFakeChain(), - dao.NewSimple(storage.NewMemoryStore(), false), + dao.NewSimple(storage.NewMemoryStore()), interop.DefaultBaseExecFee, native.DefaultStoragePrice, nil, nil, nil, nil, container, nil) @@ -178,7 +178,7 @@ func TestCheckSig(t *testing.T) { require.NoError(t, err) verifyFunc := ECDSASecp256r1CheckSig - d := dao.NewSimple(storage.NewMemoryStore(), false) + d := dao.NewSimple(storage.NewMemoryStore()) ic := &interop.Context{Network: uint32(netmode.UnitTestNet), DAO: d} runCase := func(t *testing.T, isErr bool, result any, args ...any) { ic.SpawnVM() diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index f5a5fe79c9..f52127808f 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -45,7 +45,7 @@ func TestPlatform(t *testing.T) { } func TestGetTime(t *testing.T) { - b := block.New(false) + b := &block.Block{} b.Timestamp = rand.Uint64() ic := &interop.Context{VM: vm.New(), Block: b} require.NoError(t, GetTime(ic)) @@ -133,7 +133,7 @@ func TestLog(t *testing.T) { func TestCurrentSigners(t *testing.T) { t.Run("container is block", func(t *testing.T) { - b := block.New(false) + b := &block.Block{} ic := &interop.Context{VM: vm.New(), Container: b} require.NoError(t, CurrentSigners(ic)) checkStack(t, ic.VM, stackitem.Null{}) diff --git a/pkg/core/native/management_test.go b/pkg/core/native/management_test.go index 73859507b1..013a1eaaa4 100644 --- a/pkg/core/native/management_test.go +++ b/pkg/core/native/management_test.go @@ -18,7 +18,7 @@ import ( func TestDeployGetUpdateDestroyContract(t *testing.T) { mgmt := newManagement() mgmt.Policy = newPolicy(false) - d := dao.NewSimple(storage.NewMemoryStore(), false) + d := dao.NewSimple(storage.NewMemoryStore()) ic := &interop.Context{DAO: d} err := mgmt.Initialize(ic, nil, nil) require.NoError(t, err) @@ -81,12 +81,12 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) { func TestManagement_Initialize(t *testing.T) { t.Run("good", func(t *testing.T) { - d := dao.NewSimple(storage.NewMemoryStore(), false) + d := dao.NewSimple(storage.NewMemoryStore()) mgmt := newManagement() require.NoError(t, mgmt.InitializeCache(0, d)) }) t.Run("invalid contract state", func(t *testing.T) { - d := dao.NewSimple(storage.NewMemoryStore(), false) + d := dao.NewSimple(storage.NewMemoryStore()) mgmt := newManagement() d.PutStorageItem(mgmt.ID, []byte{PrefixContract}, state.StorageItem{0xFF}) require.Error(t, mgmt.InitializeCache(0, d)) @@ -96,7 +96,7 @@ func TestManagement_Initialize(t *testing.T) { func TestManagement_GetNEP17Contracts(t *testing.T) { mgmt := newManagement() mgmt.Policy = newPolicy(false) - d := dao.NewSimple(storage.NewMemoryStore(), false) + d := dao.NewSimple(storage.NewMemoryStore()) err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil) require.NoError(t, err) require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil)) diff --git a/pkg/core/native_designate_test.go b/pkg/core/native_designate_test.go index f2c4048e5a..1897d05386 100644 --- a/pkg/core/native_designate_test.go +++ b/pkg/core/native_designate_test.go @@ -22,7 +22,7 @@ func TestDesignate_DesignateAsRole(t *testing.T) { des := bc.contracts.Designate tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) - bl := block.New(bc.config.StateRootInHeader) + bl := &block.Block{} bl.Index = bc.BlockHeight() + 1 ic := bc.newInteropContext(trigger.Application, bc.dao, bl, tx) ic.SpawnVM() diff --git a/pkg/core/statesync/module.go b/pkg/core/statesync/module.go index 4915a279fc..228fa127c4 100644 --- a/pkg/core/statesync/module.go +++ b/pkg/core/statesync/module.go @@ -320,9 +320,6 @@ func (s *Module) AddBlock(block *block.Block) error { if expectedHeight != block.Index { return fmt.Errorf("expected %d, got %d: invalid block index", expectedHeight, block.Index) } - if s.bc.GetConfig().StateRootInHeader != block.StateRootEnabled { - return fmt.Errorf("stateroot setting mismatch: %v != %v", s.bc.GetConfig().StateRootInHeader, block.StateRootEnabled) - } if !s.bc.GetConfig().SkipBlockVerification { merkle := block.ComputeMerkleRoot() if !block.MerkleRoot.Equals(merkle) { diff --git a/pkg/core/statesync/module_test.go b/pkg/core/statesync/module_test.go index 3ead29e43b..4db25030d5 100644 --- a/pkg/core/statesync/module_test.go +++ b/pkg/core/statesync/module_test.go @@ -54,7 +54,7 @@ func TestModule_PR2019_discussion_r689629704(t *testing.T) { syncPoint: 1000500, syncStage: headersSynced, syncInterval: 100500, - dao: dao.NewSimple(actualStorage, true), + dao: dao.NewSimple(actualStorage), mptpool: NewPool(), } stateSync.billet = mpt.NewBillet(sr, mpt.ModeLatest, diff --git a/pkg/core/statesync/neotest_test.go b/pkg/core/statesync/neotest_test.go index d69325a5ce..2f0357cdc9 100644 --- a/pkg/core/statesync/neotest_test.go +++ b/pkg/core/statesync/neotest_test.go @@ -351,21 +351,12 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) { require.NoError(t, err) require.Error(t, module.AddBlock(b)) }) - t.Run("error: missing state root in block header", func(t *testing.T) { - b := &block.Block{ - Header: block.Header{ - Index: uint32(stateSyncPoint) - maxTraceable + 1, - StateRootEnabled: false, - }, - } - require.Error(t, module.AddBlock(b)) - }) t.Run("error: invalid block merkle root", func(t *testing.T) { b := &block.Block{ Header: block.Header{ - Index: uint32(stateSyncPoint) - maxTraceable + 1, - StateRootEnabled: true, - MerkleRoot: util.Uint256{1, 2, 3}, + Version: block.VersionEchidna, + Index: uint32(stateSyncPoint) - maxTraceable + 1, + MerkleRoot: util.Uint256{1, 2, 3}, }, } require.Error(t, module.AddBlock(b)) diff --git a/pkg/core/util.go b/pkg/core/util.go index 196a2c2f4f..0332935a09 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -69,9 +69,17 @@ func CreateGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) }) } + var genesisVersion = uint32(block.VersionInitial) + + height, ok := cfg.Hardforks[config.HFEchidna.String()] + if ok && height == 0 { + genesisVersion = block.VersionEchidna + } + base := block.Header{ - Version: 0, + Version: genesisVersion, PrevHash: util.Uint256{}, + PrevStateRoot: util.Uint256{}, Timestamp: uint64(time.Date(2016, 7, 15, 15, 8, 21, 0, time.UTC).Unix()) * 1000, // Milliseconds. Nonce: 2083236893, Index: 0, @@ -80,7 +88,6 @@ func CreateGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) InvocationScript: []byte{}, VerificationScript: []byte{byte(opcode.PUSH1)}, }, - StateRootEnabled: cfg.StateRootInHeader, } b := &block.Block{ diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go index 1540af8624..b352cda73a 100644 --- a/pkg/neotest/basic.go +++ b/pkg/neotest/basic.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -347,6 +348,7 @@ func (e *Executor) NewUnsignedBlock(t testing.TB, txs ...*transaction.Transactio lastBlock := e.TopBlock(t) b := &block.Block{ Header: block.Header{ + Index: e.Chain.BlockHeight() + 1, NextConsensus: e.Validator.ScriptHash(), Script: transaction.Witness{ VerificationScript: e.Validator.Script(), @@ -355,12 +357,12 @@ func (e *Executor) NewUnsignedBlock(t testing.TB, txs ...*transaction.Transactio }, Transactions: txs, } - if e.Chain.GetConfig().StateRootInHeader { - b.StateRootEnabled = true + hfe, ok := e.Chain.GetConfig().Hardforks[config.HFEchidna.String()] + if ok && hfe <= b.Index { + b.Version = block.VersionEchidna b.PrevStateRoot = e.Chain.GetStateModule().CurrentLocalStateRoot() } b.PrevHash = lastBlock.Hash() - b.Index = e.Chain.BlockHeight() + 1 b.RebuildMerkleRoot() return b } diff --git a/pkg/network/message.go b/pkg/network/message.go index 2142d6a413..f2c39896e4 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -28,10 +28,6 @@ type Message struct { // Compressed message payload. compressedPayload []byte - - // StateRootInHeader specifies if the state root is included in the block header. - // This is needed for correct decoding. - StateRootInHeader bool } // MessageFlag represents compression level of a message payload. @@ -145,7 +141,7 @@ func (m *Message) decodePayload() error { case CMDAddr: p = &payload.AddressList{} case CMDBlock: - p = block.New(m.StateRootInHeader) + p = &block.Block{} case CMDExtensible: p = payload.NewExtensible() case CMDP2PNotaryRequest: @@ -157,7 +153,7 @@ func (m *Message) decodePayload() error { case CMDGetBlockByIndex: p = &payload.GetBlockByIndex{} case CMDHeaders: - p = &payload.Headers{StateRootInHeader: m.StateRootInHeader} + p = &payload.Headers{} case CMDTX: p, err := transaction.NewTransactionFromBytes(buf) if err != nil { diff --git a/pkg/network/message_test.go b/pkg/network/message_test.go index 57cd82d75a..69459d9951 100644 --- a/pkg/network/message_test.go +++ b/pkg/network/message_test.go @@ -163,12 +163,6 @@ func TestEncodeDecodeBlock(t *testing.T) { t.Run("good", func(t *testing.T) { testEncodeDecode(t, CMDBlock, newDummyBlock(12, 1)) }) - t.Run("invalid state root enabled setting", func(t *testing.T) { - expected := NewMessage(CMDBlock, newDummyBlock(31, 1)) - data, err := testserdes.Encode(expected) - require.NoError(t, err) - require.Error(t, testserdes.Decode(data, &Message{StateRootInHeader: true})) - }) } func TestEncodeDecodeGetBlock(t *testing.T) { @@ -334,7 +328,7 @@ func (f failSer) EncodeBinary(r *io.BinWriter) { func (failSer) DecodeBinary(w *io.BinReader) {} func newDummyBlock(height uint32, txCount int) *block.Block { - b := block.New(false) + b := &block.Block{} b.Index = height b.PrevHash = random.Uint256() b.Timestamp = rand.Uint64() diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go index 5d75279e0e..a7f746df7b 100644 --- a/pkg/network/payload/headers.go +++ b/pkg/network/payload/headers.go @@ -11,8 +11,6 @@ import ( // Headers payload. type Headers struct { Hdrs []*block.Header - // StateRootInHeader specifies whether the header contains a state root. - StateRootInHeader bool } // Users can at most request 2k headers. @@ -46,7 +44,6 @@ func (p *Headers) DecodeBinary(br *io.BinReader) { for i := 0; i < int(lenHeaders); i++ { header := &block.Header{} - header.StateRootEnabled = p.StateRootInHeader header.DecodeBinary(br) p.Hdrs[i] = header } diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 9d2c6f20bf..09c0d5ffc7 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -420,7 +420,7 @@ func TestBlock(t *testing.T) { s.chain.(*fakechain.FakeChain).Blockheight.Store(12344) require.Equal(t, uint32(12344), s.chain.BlockHeight()) - b := block.New(false) + b := &block.Block{} b.Index = 12345 s.testHandleMessage(t, nil, CMDBlock, b) require.Eventually(t, func() bool { return s.chain.BlockHeight() == 12345 }, 2*time.Second, time.Millisecond*500) diff --git a/pkg/network/tcp_peer.go b/pkg/network/tcp_peer.go index 496935d94d..63f148ab91 100644 --- a/pkg/network/tcp_peer.go +++ b/pkg/network/tcp_peer.go @@ -166,7 +166,7 @@ func (p *TCPPeer) handleConn() { r := io.NewBinReaderFromIO(p.conn) loop: for { - msg := &Message{StateRootInHeader: p.server.config.StateRootInHeader} + msg := &Message{} err = msg.Decode(r) if errors.Is(err, payload.ErrTooManyHeaders) { diff --git a/pkg/rpcclient/rpc.go b/pkg/rpcclient/rpc.go index 466d21d1db..e7eb3d00da 100644 --- a/pkg/rpcclient/rpc.go +++ b/pkg/rpcclient/rpc.go @@ -95,11 +95,7 @@ func (c *Client) getBlock(param any) (*block.Block, error) { return nil, err } r := io.NewBinReaderFromBuf(resp) - sr, err := c.stateRootInHeader() - if err != nil { - return nil, err - } - b = block.New(sr) + b = &block.Block{} b.DecodeBinary(r) if r.Err != nil { return nil, r.Err @@ -128,11 +124,6 @@ func (c *Client) getBlockVerbose(param any) (*result.Block, error) { resp = &result.Block{} err error ) - sr, err := c.stateRootInHeader() - if err != nil { - return nil, err - } - resp.Header.StateRootEnabled = sr if err = c.performRequest("getblock", params, resp); err != nil { return nil, err } @@ -163,13 +154,8 @@ func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) { if err := c.performRequest("getblockheader", params, &resp); err != nil { return nil, err } - sr, err := c.stateRootInHeader() - if err != nil { - return nil, err - } r := io.NewBinReaderFromBuf(resp) h = new(block.Header) - h.StateRootEnabled = sr h.DecodeBinary(r) if r.Err != nil { return nil, r.Err @@ -874,18 +860,6 @@ func (c *Client) ValidateAddress(address string) error { return nil } -// stateRootInHeader returns true if the state root is contained in the block header. -// Requires Init() before use. -func (c *Client) stateRootInHeader() (bool, error) { - c.cacheLock.RLock() - defer c.cacheLock.RUnlock() - - if !c.cache.initDone { - return false, errNetworkNotInitialized - } - return c.cache.stateRootInHeader, nil -} - // TraverseIterator returns a set of iterator values (maxItemsCount at max) for // the specified iterator and session. If result contains no elements, then either // Iterator has no elements or session was expired and terminated by the server. diff --git a/pkg/rpcclient/rpc_test.go b/pkg/rpcclient/rpc_test.go index 26fb68ad33..69e18cf0f2 100644 --- a/pkg/rpcclient/rpc_test.go +++ b/pkg/rpcclient/rpc_test.go @@ -67,7 +67,7 @@ func getResultBlock1() *result.Block { if err != nil { panic(err) } - b := block.New(false) + b := &block.Block{} err = testserdes.DecodeBinary(binB, b) if err != nil { panic(err) diff --git a/pkg/rpcclient/wsclient.go b/pkg/rpcclient/wsclient.go index 069ebd0b26..1e65d9e746 100644 --- a/pkg/rpcclient/wsclient.go +++ b/pkg/rpcclient/wsclient.go @@ -551,13 +551,7 @@ readloop: ntf := Notification{Type: event} switch event { case neorpc.BlockEventID: - sr, err := c.stateRootInHeader() - if err != nil { - // Client is not initialized. - connCloseErr = fmt.Errorf("failed to fetch StateRootInHeader: %w", err) - break readloop - } - ntf.Value = block.New(sr) + ntf.Value = &block.Block{} case neorpc.TransactionEventID: ntf.Value = &transaction.Transaction{} case neorpc.NotificationEventID: @@ -567,13 +561,7 @@ readloop: case neorpc.NotaryRequestEventID: ntf.Value = new(result.NotaryRequestEvent) case neorpc.HeaderOfAddedBlockEventID: - sr, err := c.stateRootInHeader() - if err != nil { - // Client is not initialized. - connCloseErr = fmt.Errorf("failed to fetch StateRootInHeader: %w", err) - break readloop - } - ntf.Value = &block.New(sr).Header + ntf.Value = &block.Header{} case neorpc.MissedEventID: // No value. default: diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go index 24fcf52017..4d6d197de9 100644 --- a/pkg/services/rpcsrv/server.go +++ b/pkg/services/rpcsrv/server.go @@ -2620,7 +2620,7 @@ func (s *Server) submitBlock(reqParams params.Params) (any, *neorpc.Error) { if err != nil { return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("missing parameter or not a base64: %s", err)) } - b := block.New(s.stateRootEnabled) + b := &block.Block{} r := io.NewBinReaderFromBuf(blockBytes) b.DecodeBinary(r) if r.Err != nil { diff --git a/pkg/services/rpcsrv/server_helper_test.go b/pkg/services/rpcsrv/server_helper_test.go index 3595bacd60..e6beaba9a6 100644 --- a/pkg/services/rpcsrv/server_helper_test.go +++ b/pkg/services/rpcsrv/server_helper_test.go @@ -103,7 +103,7 @@ func getTestBlocks(t *testing.T) []*block.Block { blocks := make([]*block.Block, 0, int(nBlocks)) for i := 0; i < int(nBlocks); i++ { _ = br.ReadU32LE() - b := block.New(false) + b := &block.Block{} b.DecodeBinary(br) require.Nil(t, br.Err) blocks = append(blocks, b) diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index f211593836..08f614ab35 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -170,12 +170,15 @@ func newBlock(bc *core.Blockchain, lastBlock *block.Block, script []byte, txs .. }, Transactions: txs, } - if bc.GetConfig().StateRootInHeader { + hfe, ok := bc.GetConfig().Hardforks[config.HFEchidna.String()] + if ok && hfe <= b.Index { + b.Version = block.VersionEchidna + } + if b.Version > block.VersionInitial { sr, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight()) if err != nil { return nil, err } - b.StateRootEnabled = true b.PrevStateRoot = sr.Root } b.RebuildMerkleRoot()