github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/blockchain_neotest_test.go (about) 1 package core_test 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "math/big" 8 "path/filepath" 9 "sort" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/nspcc-dev/neo-go/internal/basicchain" 15 "github.com/nspcc-dev/neo-go/internal/contracts" 16 "github.com/nspcc-dev/neo-go/internal/random" 17 "github.com/nspcc-dev/neo-go/internal/testchain" 18 "github.com/nspcc-dev/neo-go/pkg/compiler" 19 "github.com/nspcc-dev/neo-go/pkg/config" 20 "github.com/nspcc-dev/neo-go/pkg/config/netmode" 21 "github.com/nspcc-dev/neo-go/pkg/core" 22 "github.com/nspcc-dev/neo-go/pkg/core/block" 23 "github.com/nspcc-dev/neo-go/pkg/core/dao" 24 "github.com/nspcc-dev/neo-go/pkg/core/fee" 25 "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" 26 "github.com/nspcc-dev/neo-go/pkg/core/mempool" 27 "github.com/nspcc-dev/neo-go/pkg/core/native" 28 "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" 29 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 30 "github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices" 31 "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" 32 "github.com/nspcc-dev/neo-go/pkg/core/state" 33 "github.com/nspcc-dev/neo-go/pkg/core/storage" 34 "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" 35 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 36 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 37 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 38 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 39 "github.com/nspcc-dev/neo-go/pkg/io" 40 "github.com/nspcc-dev/neo-go/pkg/neotest" 41 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 42 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 43 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 44 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 45 "github.com/nspcc-dev/neo-go/pkg/util" 46 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 47 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 48 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 49 "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" 50 "github.com/nspcc-dev/neo-go/pkg/wallet" 51 "github.com/stretchr/testify/assert" 52 "github.com/stretchr/testify/require" 53 ) 54 55 func newLevelDBForTestingWithPath(t testing.TB, dbPath string) (storage.Store, string) { 56 if dbPath == "" { 57 dbPath = t.TempDir() 58 } 59 dbOptions := dbconfig.LevelDBOptions{ 60 DataDirectoryPath: dbPath, 61 } 62 newLevelStore, err := storage.NewLevelDBStore(dbOptions) 63 require.Nil(t, err, "NewLevelDBStore error") 64 return newLevelStore, dbPath 65 } 66 67 func TestBlockchain_StartFromExistingDB(t *testing.T) { 68 ps, path := newLevelDBForTestingWithPath(t, "") 69 customConfig := func(c *config.Blockchain) { 70 c.StateRootInHeader = true // Need for P2PStateExchangeExtensions check. 71 c.P2PSigExtensions = true // Need for basic chain initializer. 72 } 73 bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, ps) 74 require.NoError(t, err) 75 go bc.Run() 76 e := neotest.NewExecutor(t, bc, validators, committee) 77 basicchain.Init(t, "../../", e) 78 require.True(t, bc.BlockHeight() > 5, "ensure that basic chain is correctly initialised") 79 80 // Information for further tests. 81 h := bc.BlockHeight() 82 cryptoLibHash, err := bc.GetNativeContractScriptHash(nativenames.CryptoLib) 83 require.NoError(t, err) 84 cryptoLibState := bc.GetContractState(cryptoLibHash) 85 require.NotNil(t, cryptoLibState) 86 var ( 87 managementID = -1 88 managementContractPrefix = 8 89 ) 90 91 bc.Close() // Ensure persist is done and persistent store is properly closed. 92 93 newPS := func(t *testing.T) storage.Store { 94 ps, _ = newLevelDBForTestingWithPath(t, path) 95 t.Cleanup(func() { require.NoError(t, ps.Close()) }) 96 return ps 97 } 98 t.Run("mismatch storage version", func(t *testing.T) { 99 ps = newPS(t) 100 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 101 d := dao.NewSimple(cache, bc.GetConfig().StateRootInHeader) 102 d.PutVersion(dao.Version{ 103 Value: "0.0.0", 104 }) 105 _, err := d.Persist() // Persist to `cache` wrapper. 106 require.NoError(t, err) 107 _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 108 require.Error(t, err) 109 require.True(t, strings.Contains(err.Error(), "storage version mismatch"), err) 110 }) 111 t.Run("mismatch StateRootInHeader", func(t *testing.T) { 112 ps = newPS(t) 113 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { 114 customConfig(c) 115 c.StateRootInHeader = false 116 }, ps) 117 require.Error(t, err) 118 require.True(t, strings.Contains(err.Error(), "StateRootInHeader setting mismatch"), err) 119 }) 120 t.Run("mismatch P2PSigExtensions", func(t *testing.T) { 121 ps = newPS(t) 122 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { 123 customConfig(c) 124 c.P2PSigExtensions = false 125 }, ps) 126 require.Error(t, err) 127 require.True(t, strings.Contains(err.Error(), "P2PSigExtensions setting mismatch"), err) 128 }) 129 t.Run("mismatch P2PStateExchangeExtensions", func(t *testing.T) { 130 ps = newPS(t) 131 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { 132 customConfig(c) 133 c.StateRootInHeader = true 134 c.P2PStateExchangeExtensions = true 135 }, ps) 136 require.Error(t, err) 137 require.True(t, strings.Contains(err.Error(), "P2PStateExchangeExtensions setting mismatch"), err) 138 }) 139 t.Run("mismatch KeepOnlyLatestState", func(t *testing.T) { 140 ps = newPS(t) 141 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { 142 customConfig(c) 143 c.Ledger.KeepOnlyLatestState = true 144 }, ps) 145 require.Error(t, err) 146 require.True(t, strings.Contains(err.Error(), "KeepOnlyLatestState setting mismatch"), err) 147 }) 148 t.Run("Magic mismatch", func(t *testing.T) { 149 ps = newPS(t) 150 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) { 151 customConfig(c) 152 c.Magic = 100500 153 }, ps) 154 require.Error(t, err) 155 require.True(t, strings.Contains(err.Error(), "protocol configuration Magic mismatch"), err) 156 }) 157 t.Run("corrupted headers", func(t *testing.T) { 158 ps = newPS(t) 159 160 // Corrupt headers hashes batch. 161 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 162 // Make the chain think we're at 2000+ which will trigger page 0 read. 163 buf := io.NewBufBinWriter() 164 buf.WriteBytes(util.Uint256{}.BytesLE()) 165 buf.WriteU32LE(2000) 166 cache.Put([]byte{byte(storage.SYSCurrentHeader)}, buf.Bytes()) 167 168 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 169 require.Error(t, err) 170 require.True(t, strings.Contains(err.Error(), "failed to retrieve header hash page"), err) 171 }) 172 t.Run("corrupted current header height", func(t *testing.T) { 173 ps = newPS(t) 174 175 // Remove current header. 176 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 177 cache.Delete([]byte{byte(storage.SYSCurrentHeader)}) 178 179 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 180 require.Error(t, err) 181 require.True(t, strings.Contains(err.Error(), "failed to retrieve current header"), err) 182 }) 183 t.Run("missing last batch of 2000 headers and missing last header", func(t *testing.T) { 184 ps = newPS(t) 185 186 // Remove latest headers hashes batch and current header. 187 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 188 cache.Delete([]byte{byte(storage.IXHeaderHashList)}) 189 currHeaderInfo, err := cache.Get([]byte{byte(storage.SYSCurrentHeader)}) 190 require.NoError(t, err) 191 currHeaderHash, err := util.Uint256DecodeBytesLE(currHeaderInfo[:32]) 192 require.NoError(t, err) 193 cache.Delete(append([]byte{byte(storage.DataExecutable)}, currHeaderHash.BytesBE()...)) 194 195 _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 196 require.Error(t, err) 197 require.True(t, strings.Contains(err.Error(), "could not get header"), err) 198 }) 199 t.Run("missing last block", func(t *testing.T) { 200 ps = newPS(t) 201 202 // Remove current block from storage. 203 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 204 cache.Delete([]byte{byte(storage.SYSCurrentBlock)}) 205 206 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 207 require.Error(t, err) 208 require.True(t, strings.Contains(err.Error(), "failed to retrieve current block height"), err) 209 }) 210 t.Run("missing last stateroot", func(t *testing.T) { 211 ps = newPS(t) 212 213 // Remove latest stateroot from storage. 214 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 215 key := make([]byte, 5) 216 key[0] = byte(storage.DataMPTAux) 217 binary.BigEndian.PutUint32(key[1:], h) 218 cache.Delete(key) 219 220 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 221 require.Error(t, err) 222 require.True(t, strings.Contains(err.Error(), "can't init MPT at height"), err) 223 }) 224 t.Run("failed native Management initialisation", func(t *testing.T) { 225 ps = newPS(t) 226 227 // Corrupt serialised CryptoLib state. 228 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 229 key := make([]byte, 1+4+1+20) 230 key[0] = byte(storage.STStorage) 231 binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) 232 key[5] = byte(managementContractPrefix) 233 copy(key[6:], cryptoLibHash.BytesBE()) 234 cache.Put(key, []byte{1, 2, 3}) 235 236 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 237 require.Error(t, err) 238 require.True(t, strings.Contains(err.Error(), "can't init natives cache: failed to initialize cache for ContractManagement"), err) 239 }) 240 t.Run("invalid native contract activation", func(t *testing.T) { 241 ps = newPS(t) 242 243 // Remove CryptoLib from storage. 244 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 245 key := make([]byte, 1+4+1+20) 246 key[0] = byte(storage.STStorage) 247 binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) 248 key[5] = byte(managementContractPrefix) 249 copy(key[6:], cryptoLibHash.BytesBE()) 250 cache.Delete(key) 251 252 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 253 require.Error(t, err) 254 require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s is not stored, but should be active at height %d according to config", nativenames.CryptoLib, h)), err) 255 }) 256 t.Run("stored and autogenerated native contract's states mismatch", func(t *testing.T) { 257 ps = newPS(t) 258 259 // Change stored CryptoLib state. 260 cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. 261 key := make([]byte, 1+4+1+20) 262 key[0] = byte(storage.STStorage) 263 binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) 264 key[5] = byte(managementContractPrefix) 265 copy(key[6:], cryptoLibHash.BytesBE()) 266 cs := *cryptoLibState 267 cs.ID = -123 268 csBytes, err := stackitem.SerializeConvertible(&cs) 269 require.NoError(t, err) 270 cache.Put(key, csBytes) 271 272 _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) 273 require.Error(t, err) 274 require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Cockatrice (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err) 275 }) 276 277 t.Run("good", func(t *testing.T) { 278 ps = newPS(t) 279 _, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, ps) 280 require.NoError(t, err) 281 }) 282 } 283 284 // TestBlockchain_InitializeNeoCache_Bug3181 is aimed to reproduce and check situation 285 // when panic occures on native Neo cache initialization due to access to native Policy 286 // cache when it's not yet initialized to recalculate candidates. 287 func TestBlockchain_InitializeNeoCache_Bug3181(t *testing.T) { 288 ps, path := newLevelDBForTestingWithPath(t, "") 289 bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps) 290 require.NoError(t, err) 291 go bc.Run() 292 e := neotest.NewExecutor(t, bc, validators, committee) 293 294 // Add at least one registered candidate to enable candidates Policy check. 295 acc := e.NewAccount(t, 10000_0000_0000) // block #1 296 neo := e.NewInvoker(e.NativeHash(t, nativenames.Neo), acc) 297 neo.Invoke(t, true, "registerCandidate", acc.(neotest.SingleSigner).Account().PublicKey().Bytes()) // block #2 298 299 // Put some empty blocks to reach N-1 block height, so that newEpoch cached 300 // values of native Neo contract require an update on the subsequent cache 301 // initialization. 302 for i := 0; i < len(bc.GetConfig().StandbyCommittee)-1-2; i++ { 303 e.AddNewBlock(t) 304 } 305 bc.Close() // Ensure persist is done and persistent store is properly closed. 306 307 ps, _ = newLevelDBForTestingWithPath(t, path) 308 t.Cleanup(func() { require.NoError(t, ps.Close()) }) 309 310 // Start chain from the existing database that should trigger an update of native 311 // Neo newEpoch* cached values during initializaition. This update requires candidates 312 // list recalculation and policies checks, thus, access to native Policy cache 313 // that is not yet initialized by that moment. 314 require.NotPanics(t, func() { 315 _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps) 316 require.NoError(t, err) 317 }) 318 } 319 320 // TestBlockchain_InitializeNeoCache_Bug3424 ensures that Neo cache (new epoch 321 // committee and stand by validators) is properly initialized after node restart 322 // at the dBFT epoch boundary. 323 func TestBlockchain_InitializeNeoCache_Bug3424(t *testing.T) { 324 ps, path := newLevelDBForTestingWithPath(t, "") 325 bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps) 326 require.NoError(t, err) 327 go bc.Run() 328 e := neotest.NewExecutor(t, bc, validators, committee) 329 cfg := e.Chain.GetConfig() 330 committeeSize := cfg.GetCommitteeSize(0) 331 validatorsCount := cfg.GetNumOfCNs(0) 332 333 // Stand by committee drives the chain. 334 standBySorted, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) 335 require.NoError(t, err) 336 standBySorted = standBySorted[:validatorsCount] 337 sort.Sort(standBySorted) 338 pubs := e.Chain.ComputeNextBlockValidators() 339 require.Equal(t, standBySorted, keys.PublicKeys(pubs)) 340 341 // Move from stand by committee to the elected nodes. 342 e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 100_0000_0000, nil) 343 neoCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo)) 344 neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) 345 policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy)) 346 347 advanceChain := func(t *testing.T) { 348 for int(e.Chain.BlockHeight())%committeeSize != 0 { 349 neoCommitteeInvoker.AddNewBlock(t) 350 } 351 } 352 advanceChainToEpochBoundary := func(t *testing.T) { 353 for int(e.Chain.BlockHeight()+1)%committeeSize != 0 { 354 neoCommitteeInvoker.AddNewBlock(t) 355 } 356 } 357 358 // voters vote for candidates. 359 voters := make([]neotest.Signer, committeeSize+1) 360 candidates := make([]neotest.Signer, committeeSize+1) 361 for i := 0; i < committeeSize+1; i++ { 362 voters[i] = e.NewAccount(t, 10_0000_0000) 363 candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration 364 } 365 txes := make([]*transaction.Transaction, 0, committeeSize*3) 366 for i := 0; i < committeeSize+1; i++ { 367 transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil) 368 txes = append(txes, transferTx) 369 registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 370 txes = append(txes, registerTx) 371 voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 372 txes = append(txes, voteTx) 373 } 374 txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().ScriptHash())) 375 neoValidatorsInvoker.AddNewBlock(t, txes...) 376 for _, tx := range txes { 377 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values 378 } 379 380 // Ensure validators are properly updated. 381 advanceChain(t) 382 pubs = e.Chain.ComputeNextBlockValidators() 383 sortedCandidates := make(keys.PublicKeys, validatorsCount) 384 for i := range candidates[:validatorsCount] { 385 sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PublicKey() 386 } 387 sort.Sort(sortedCandidates) 388 require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs)) 389 390 // Move to the last block in the epoch and restart the node. 391 advanceChainToEpochBoundary(t) 392 bc.Close() // Ensure persist is done and persistent store is properly closed. 393 ps, _ = newLevelDBForTestingWithPath(t, path) 394 t.Cleanup(func() { require.NoError(t, ps.Close()) }) 395 396 // Start chain from the existing database that should trigger an update of native 397 // Neo newEpoch* cached values during initializaition. This update requires candidates 398 // list recalculation and caldidates policies checks. 399 bc, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps) 400 require.NoError(t, err) 401 402 pubs = bc.ComputeNextBlockValidators() 403 require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs)) 404 } 405 406 // This test enables Notary native contract at non-zero height and checks that no 407 // Notary cache initialization is performed before that height on node restart. 408 /* 409 func TestBlockchain_InitializeNativeCacheWrtNativeActivations(t *testing.T) { 410 const notaryEnabledHeight = 3 411 ps, path := newLevelDBForTestingWithPath(t, "") 412 customConfig := func(c *config.Blockchain) { 413 c.P2PSigExtensions = true 414 c.NativeUpdateHistories = make(map[string][]uint32) 415 for _, n := range []string{ 416 nativenames.Neo, 417 nativenames.Gas, 418 nativenames.Designation, 419 nativenames.Management, 420 nativenames.CryptoLib, 421 nativenames.Ledger, 422 nativenames.Management, 423 nativenames.Oracle, 424 nativenames.Policy, 425 nativenames.StdLib, 426 nativenames.Notary, 427 } { 428 if n == nativenames.Notary { 429 c.NativeUpdateHistories[n] = []uint32{notaryEnabledHeight} 430 } else { 431 c.NativeUpdateHistories[n] = []uint32{0} 432 } 433 } 434 } 435 bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, ps) 436 require.NoError(t, err) 437 go bc.Run() 438 e := neotest.NewExecutor(t, bc, validators, committee) 439 e.AddNewBlock(t) 440 bc.Close() // Ensure persist is done and persistent store is properly closed. 441 442 ps, _ = newLevelDBForTestingWithPath(t, path) 443 444 // If NativeActivations are not taken into account during native cache initialization, 445 // bs.init() will panic on Notary cache initialization as it's not deployed yet. 446 require.NotPanics(t, func() { 447 bc, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, ps) 448 require.NoError(t, err) 449 }) 450 go bc.Run() 451 defer bc.Close() 452 e = neotest.NewExecutor(t, bc, validators, committee) 453 h := e.Chain.BlockHeight() 454 455 // Notary isn't initialized yet, so accessing Notary cache should return error. 456 _, err = e.Chain.GetMaxNotValidBeforeDelta() 457 require.Error(t, err) 458 459 // Ensure Notary will be properly initialized and accessing Notary cache works 460 // as expected. 461 for i := 0; i < notaryEnabledHeight; i++ { 462 require.NotPanics(t, func() { 463 e.AddNewBlock(t) 464 }, h+uint32(i)+1) 465 } 466 _, err = e.Chain.GetMaxNotValidBeforeDelta() 467 require.NoError(t, err) 468 } 469 */ 470 471 func TestBlockchain_AddHeaders(t *testing.T) { 472 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 473 c.StateRootInHeader = true 474 }) 475 e := neotest.NewExecutor(t, bc, acc, acc) 476 477 newHeader := func(t *testing.T, index uint32, prevHash util.Uint256, timestamp uint64) *block.Header { 478 b := e.NewUnsignedBlock(t) 479 b.Index = index 480 b.PrevHash = prevHash 481 b.PrevStateRoot = util.Uint256{} 482 b.Timestamp = timestamp 483 e.SignBlock(b) 484 return &b.Header 485 } 486 b1 := e.NewUnsignedBlock(t) 487 h1 := &e.SignBlock(b1).Header 488 h2 := newHeader(t, h1.Index+1, h1.Hash(), h1.Timestamp+1) 489 h3 := newHeader(t, h2.Index+1, h2.Hash(), h2.Timestamp+1) 490 491 require.NoError(t, bc.AddHeaders()) 492 require.NoError(t, bc.AddHeaders(h1, h2)) 493 require.NoError(t, bc.AddHeaders(h2, h3)) 494 495 assert.Equal(t, h3.Index, bc.HeaderHeight()) 496 assert.Equal(t, uint32(0), bc.BlockHeight()) 497 assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash()) 498 499 // Add them again, they should not be added. 500 require.NoError(t, bc.AddHeaders(h3, h2, h1)) 501 502 assert.Equal(t, h3.Index, bc.HeaderHeight()) 503 assert.Equal(t, uint32(0), bc.BlockHeight()) 504 assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash()) 505 506 h4Bad := newHeader(t, h3.Index+1, h3.Hash().Reverse(), h3.Timestamp+1) 507 h5Bad := newHeader(t, h4Bad.Index+1, h4Bad.Hash(), h4Bad.Timestamp+1) 508 509 assert.Error(t, bc.AddHeaders(h4Bad, h5Bad)) 510 assert.Equal(t, h3.Index, bc.HeaderHeight()) 511 assert.Equal(t, uint32(0), bc.BlockHeight()) 512 assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash()) 513 514 h4Bad2 := newHeader(t, h3.Index+1, h3.Hash().Reverse(), h3.Timestamp+1) 515 h4Bad2.Script.InvocationScript = []byte{} 516 assert.Error(t, bc.AddHeaders(h4Bad2)) 517 assert.Equal(t, h3.Index, bc.HeaderHeight()) 518 assert.Equal(t, uint32(0), bc.BlockHeight()) 519 assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash()) 520 } 521 522 func TestBlockchain_AddBlockStateRoot(t *testing.T) { 523 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 524 c.StateRootInHeader = true 525 }) 526 e := neotest.NewExecutor(t, bc, acc, acc) 527 528 sr, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight()) 529 require.NoError(t, err) 530 531 b := e.NewUnsignedBlock(t) 532 b.StateRootEnabled = false 533 b.PrevStateRoot = util.Uint256{} 534 e.SignBlock(b) 535 err = bc.AddBlock(b) 536 require.ErrorIs(t, err, core.ErrHdrStateRootSetting) 537 538 u := sr.Root 539 u[0] ^= 0xFF 540 b = e.NewUnsignedBlock(t) 541 b.PrevStateRoot = u 542 e.SignBlock(b) 543 err = bc.AddBlock(b) 544 require.ErrorIs(t, err, core.ErrHdrInvalidStateRoot) 545 546 b = e.NewUnsignedBlock(t) 547 e.SignBlock(b) 548 require.NoError(t, bc.AddBlock(b)) 549 } 550 551 func TestBlockchain_AddHeadersStateRoot(t *testing.T) { 552 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 553 c.StateRootInHeader = true 554 }) 555 e := neotest.NewExecutor(t, bc, acc, acc) 556 557 b := e.NewUnsignedBlock(t) 558 e.SignBlock(b) 559 h1 := b.Header 560 r := bc.GetStateModule().CurrentLocalStateRoot() 561 562 // invalid stateroot 563 h1.PrevStateRoot[0] ^= 0xFF 564 require.ErrorIs(t, bc.AddHeaders(&h1), core.ErrHdrInvalidStateRoot) 565 566 // valid stateroot 567 h1.PrevStateRoot = r 568 require.NoError(t, bc.AddHeaders(&h1)) 569 570 // unable to verify stateroot (stateroot is computed for block #0 only => can 571 // verify stateroot of header #1 only) => just store the header 572 b = e.NewUnsignedBlock(t) 573 b.PrevHash = h1.Hash() 574 b.Timestamp = h1.Timestamp + 1 575 b.PrevStateRoot = util.Uint256{} 576 b.Index = h1.Index + 1 577 e.SignBlock(b) 578 require.NoError(t, bc.AddHeaders(&b.Header)) 579 } 580 581 func TestBlockchain_AddBadBlock(t *testing.T) { 582 check := func(t *testing.T, b *block.Block, cfg func(c *config.Blockchain)) { 583 bc, _ := chain.NewSingleWithCustomConfig(t, cfg) 584 err := bc.AddBlock(b) 585 if cfg == nil { 586 require.Error(t, err) 587 } else { 588 require.NoError(t, err) 589 } 590 } 591 bc, acc := chain.NewSingle(t) 592 e := neotest.NewExecutor(t, bc, acc, acc) 593 neoHash := e.NativeHash(t, nativenames.Neo) 594 595 tx := e.NewUnsignedTx(t, neoHash, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 596 tx.ValidUntilBlock = 0 // Intentionally make the transaction invalid. 597 e.SignTx(t, tx, -1, acc) 598 b := e.NewUnsignedBlock(t, tx) 599 e.SignBlock(b) 600 check(t, b, nil) 601 check(t, b, func(c *config.Blockchain) { 602 c.SkipBlockVerification = true 603 }) 604 605 b = e.NewUnsignedBlock(t) 606 b.PrevHash = util.Uint256{} // Intentionally make block invalid. 607 e.SignBlock(b) 608 check(t, b, nil) 609 check(t, b, func(c *config.Blockchain) { 610 c.SkipBlockVerification = true 611 }) 612 613 tx = e.NewUnsignedTx(t, neoHash, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) // Check the good tx. 614 e.SignTx(t, tx, -1, acc) 615 b = e.NewUnsignedBlock(t, tx) 616 e.SignBlock(b) 617 check(t, b, func(c *config.Blockchain) { 618 c.VerifyTransactions = true 619 c.SkipBlockVerification = false 620 }) 621 } 622 623 func TestBlockchain_GetHeader(t *testing.T) { 624 bc, acc := chain.NewSingle(t) 625 e := neotest.NewExecutor(t, bc, acc, acc) 626 627 block := e.AddNewBlock(t) 628 hash := block.Hash() 629 header, err := bc.GetHeader(hash) 630 require.NoError(t, err) 631 assert.Equal(t, &block.Header, header) 632 633 b2 := e.NewUnsignedBlock(t) 634 _, err = bc.GetHeader(b2.Hash()) 635 assert.Error(t, err) 636 } 637 638 func TestBlockchain_GetBlock(t *testing.T) { 639 bc, acc := chain.NewSingle(t) 640 e := neotest.NewExecutor(t, bc, acc, acc) 641 blocks := e.GenerateNewBlocks(t, 10) 642 neoValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Neo)) 643 644 for i := 0; i < len(blocks); i++ { 645 block, err := bc.GetBlock(blocks[i].Hash()) 646 require.NoErrorf(t, err, "can't get block %d: %s", i, err) 647 assert.Equal(t, blocks[i].Index, block.Index) 648 assert.Equal(t, blocks[i].Hash(), block.Hash()) 649 } 650 651 t.Run("store only header", func(t *testing.T) { 652 t.Run("non-empty block", func(t *testing.T) { 653 tx := neoValidatorInvoker.PrepareInvoke(t, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 654 b := e.NewUnsignedBlock(t, tx) 655 e.SignBlock(b) 656 require.NoError(t, bc.AddHeaders(&b.Header)) 657 658 _, err := bc.GetBlock(b.Hash()) 659 require.Error(t, err) 660 661 _, err = bc.GetHeader(b.Hash()) 662 require.NoError(t, err) 663 664 require.NoError(t, bc.AddBlock(b)) 665 666 _, err = bc.GetBlock(b.Hash()) 667 require.NoError(t, err) 668 }) 669 t.Run("empty block", func(t *testing.T) { 670 b := e.NewUnsignedBlock(t) 671 e.SignBlock(b) 672 673 require.NoError(t, bc.AddHeaders(&b.Header)) 674 675 _, err := bc.GetBlock(b.Hash()) 676 require.NoError(t, err) 677 }) 678 }) 679 } 680 681 func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { 682 bc, acc := chain.NewSingle(t) 683 e := neotest.NewExecutor(t, bc, acc, acc) 684 685 cs, csInvalid := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash()) 686 c1 := &neotest.Contract{ 687 Hash: cs.Hash, 688 NEF: &cs.NEF, 689 Manifest: &cs.Manifest, 690 } 691 c2 := &neotest.Contract{ 692 Hash: csInvalid.Hash, 693 NEF: &csInvalid.NEF, 694 Manifest: &csInvalid.Manifest, 695 } 696 e.DeployContract(t, c1, nil) 697 e.DeployContract(t, c2, nil) 698 699 gas := bc.GetMaxVerificationGAS() 700 t.Run("Contract", func(t *testing.T) { 701 t.Run("Missing", func(t *testing.T) { 702 newH := cs.Hash 703 newH[0] = ^newH[0] 704 w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} 705 _, err := bc.VerifyWitness(newH, nil, w, gas) 706 require.ErrorIs(t, err, core.ErrUnknownVerificationContract) 707 }) 708 t.Run("Invalid", func(t *testing.T) { 709 w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} 710 _, err := bc.VerifyWitness(csInvalid.Hash, nil, w, gas) 711 require.ErrorIs(t, err, core.ErrInvalidVerificationContract) 712 }) 713 t.Run("ValidSignature", func(t *testing.T) { 714 w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} 715 _, err := bc.VerifyWitness(cs.Hash, nil, w, gas) 716 require.NoError(t, err) 717 }) 718 t.Run("InvalidSignature", func(t *testing.T) { 719 w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}} 720 _, err := bc.VerifyWitness(cs.Hash, nil, w, gas) 721 require.ErrorIs(t, err, core.ErrVerificationFailed) 722 }) 723 }) 724 t.Run("NotEnoughGas", func(t *testing.T) { 725 verif := []byte{byte(opcode.PUSH1)} 726 w := &transaction.Witness{ 727 InvocationScript: []byte{byte(opcode.NOP)}, 728 VerificationScript: verif, 729 } 730 _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, 1) 731 require.ErrorIs(t, err, core.ErrVerificationFailed) 732 }) 733 t.Run("NoResult", func(t *testing.T) { 734 verif := []byte{byte(opcode.DROP)} 735 w := &transaction.Witness{ 736 InvocationScript: []byte{byte(opcode.PUSH1)}, 737 VerificationScript: verif, 738 } 739 _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) 740 require.ErrorIs(t, err, core.ErrVerificationFailed) 741 }) 742 t.Run("BadResult", func(t *testing.T) { 743 verif := make([]byte, keys.SignatureLen+2) 744 verif[0] = byte(opcode.PUSHDATA1) 745 verif[1] = keys.SignatureLen 746 w := &transaction.Witness{ 747 InvocationScript: []byte{byte(opcode.NOP)}, 748 VerificationScript: verif, 749 } 750 _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) 751 require.ErrorIs(t, err, core.ErrVerificationFailed) 752 }) 753 t.Run("TooManyResults", func(t *testing.T) { 754 verif := []byte{byte(opcode.NOP)} 755 w := &transaction.Witness{ 756 InvocationScript: []byte{byte(opcode.PUSH1), byte(opcode.PUSH1)}, 757 VerificationScript: verif, 758 } 759 _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) 760 require.ErrorIs(t, err, core.ErrVerificationFailed) 761 }) 762 } 763 764 func TestBlockchain_IsTxStillRelevant(t *testing.T) { 765 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 766 c.P2PSigExtensions = true 767 }) 768 e := neotest.NewExecutor(t, bc, acc, acc) 769 770 mp := bc.GetMemPool() 771 772 t.Run("small ValidUntilBlock", func(t *testing.T) { 773 tx := e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}, bc.BlockHeight()+1) 774 775 require.True(t, bc.IsTxStillRelevant(tx, nil, false)) 776 e.AddNewBlock(t) 777 require.False(t, bc.IsTxStillRelevant(tx, nil, false)) 778 }) 779 780 t.Run("tx is already persisted", func(t *testing.T) { 781 tx := e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}, bc.BlockHeight()+2) 782 783 require.True(t, bc.IsTxStillRelevant(tx, nil, false)) 784 e.AddNewBlock(t, tx) 785 require.False(t, bc.IsTxStillRelevant(tx, nil, false)) 786 }) 787 788 t.Run("tx with Conflicts attribute", func(t *testing.T) { 789 tx1 := e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}, bc.BlockHeight()+5) 790 791 tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 792 tx2.Nonce = neotest.Nonce() 793 tx2.ValidUntilBlock = e.Chain.BlockHeight() + 5 794 tx2.Attributes = []transaction.Attribute{{ 795 Type: transaction.ConflictsT, 796 Value: &transaction.Conflicts{Hash: tx1.Hash()}, 797 }} 798 e.SignTx(t, tx2, -1, acc) 799 800 require.True(t, bc.IsTxStillRelevant(tx1, mp, false)) 801 require.NoError(t, bc.PoolTx(tx2)) 802 require.False(t, bc.IsTxStillRelevant(tx1, mp, false)) 803 }) 804 t.Run("NotValidBefore", func(t *testing.T) { 805 tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 806 tx3.Nonce = neotest.Nonce() 807 tx3.Attributes = []transaction.Attribute{{ 808 Type: transaction.NotValidBeforeT, 809 Value: &transaction.NotValidBefore{Height: bc.BlockHeight() + 1}, 810 }} 811 tx3.ValidUntilBlock = bc.BlockHeight() + 2 812 e.SignTx(t, tx3, -1, acc) 813 814 require.False(t, bc.IsTxStillRelevant(tx3, nil, false)) 815 e.AddNewBlock(t) 816 require.True(t, bc.IsTxStillRelevant(tx3, nil, false)) 817 }) 818 t.Run("contract witness check fails", func(t *testing.T) { 819 src := fmt.Sprintf(`package verify 820 import ( 821 "github.com/nspcc-dev/neo-go/pkg/interop/contract" 822 "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" 823 ) 824 func Verify() bool { 825 addr := address.ToHash160("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`") 826 currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates) 827 return currentHeight.(int) < %d 828 }`, bc.BlockHeight()+2) // deploy + next block 829 c := neotest.CompileSource(t, acc.ScriptHash(), strings.NewReader(src), &compiler.Options{ 830 Name: "verification_contract", 831 }) 832 e.DeployContract(t, c, nil) 833 834 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 835 tx.Nonce = neotest.Nonce() 836 tx.ValidUntilBlock = bc.BlockHeight() + 2 837 tx.Signers = []transaction.Signer{ 838 { 839 Account: c.Hash, 840 Scopes: transaction.None, 841 }, 842 } 843 tx.NetworkFee += 10_000_000 844 tx.Scripts = []transaction.Witness{{}} 845 846 require.True(t, bc.IsTxStillRelevant(tx, mp, false)) 847 e.AddNewBlock(t) 848 require.False(t, bc.IsTxStillRelevant(tx, mp, false)) 849 }) 850 } 851 852 func TestBlockchain_MemPoolRemoval(t *testing.T) { 853 const added = 16 854 const notAdded = 32 855 bc, acc := chain.NewSingle(t) 856 e := neotest.NewExecutor(t, bc, acc, acc) 857 858 addedTxes := make([]*transaction.Transaction, added) 859 notAddedTxes := make([]*transaction.Transaction, notAdded) 860 for i := range addedTxes { 861 addedTxes[i] = e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}, 100) 862 require.NoError(t, bc.PoolTx(addedTxes[i])) 863 } 864 for i := range notAddedTxes { 865 notAddedTxes[i] = e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}, 100) 866 require.NoError(t, bc.PoolTx(notAddedTxes[i])) 867 } 868 mempool := bc.GetMemPool() 869 e.AddNewBlock(t, addedTxes...) 870 for _, tx := range addedTxes { 871 require.False(t, mempool.ContainsKey(tx.Hash())) 872 } 873 for _, tx := range notAddedTxes { 874 require.True(t, mempool.ContainsKey(tx.Hash())) 875 } 876 } 877 878 func TestBlockchain_HasBlock(t *testing.T) { 879 bc, acc := chain.NewSingle(t) 880 e := neotest.NewExecutor(t, bc, acc, acc) 881 882 blocks := e.GenerateNewBlocks(t, 10) 883 884 for i := 0; i < len(blocks); i++ { 885 assert.True(t, bc.HasBlock(blocks[i].Hash())) 886 } 887 newBlock := e.NewUnsignedBlock(t) 888 assert.False(t, bc.HasBlock(newBlock.Hash())) 889 } 890 891 func TestBlockchain_GetTransaction(t *testing.T) { 892 bc, acc := chain.NewSingle(t) 893 e := neotest.NewExecutor(t, bc, acc, acc) 894 895 tx1 := e.PrepareInvocation(t, []byte{byte(opcode.PUSH1)}, []neotest.Signer{acc}) 896 e.AddNewBlock(t, tx1) 897 898 tx2 := e.PrepareInvocation(t, []byte{byte(opcode.PUSH2)}, []neotest.Signer{acc}) 899 tx2Size := io.GetVarSize(tx2) 900 b := e.AddNewBlock(t, tx2) 901 902 tx, height, err := bc.GetTransaction(tx2.Hash()) 903 require.Nil(t, err) 904 assert.Equal(t, b.Index, height) 905 assert.Equal(t, tx2Size, tx.Size()) 906 assert.Equal(t, b.Transactions[0], tx) 907 } 908 909 func TestBlockchain_GetClaimable(t *testing.T) { 910 bc, acc := chain.NewSingle(t) 911 912 t.Run("first generation period", func(t *testing.T) { 913 amount, err := bc.CalculateClaimable(acc.ScriptHash(), 1) 914 require.NoError(t, err) 915 require.EqualValues(t, big.NewInt(5*native.GASFactor/10), amount) 916 }) 917 } 918 919 func TestBlockchain_Close(t *testing.T) { 920 st := storage.NewMemoryStore() 921 bc, acc := chain.NewSingleWithCustomConfigAndStore(t, nil, st, false) 922 e := neotest.NewExecutor(t, bc, acc, acc) 923 go bc.Run() 924 e.GenerateNewBlocks(t, 10) 925 bc.Close() 926 // It's a hack, but we use internal knowledge of MemoryStore 927 // implementation which makes it completely unusable (up to panicing) 928 // after Close(). 929 require.Panics(t, func() { 930 _ = st.PutChangeSet(map[string][]byte{"0": {1}}, nil) 931 }) 932 } 933 934 func TestBlockchain_Subscriptions(t *testing.T) { 935 // We use buffering here as a substitute for reader goroutines, events 936 // get queued up and we read them one by one here. 937 const chBufSize = 16 938 blockCh := make(chan *block.Block, chBufSize) 939 txCh := make(chan *transaction.Transaction, chBufSize) 940 notificationCh := make(chan *state.ContainedNotificationEvent, chBufSize) 941 executionCh := make(chan *state.AppExecResult, chBufSize) 942 943 bc, acc := chain.NewSingle(t) 944 e := neotest.NewExecutor(t, bc, acc, acc) 945 nativeGASHash := e.NativeHash(t, nativenames.Gas) 946 bc.SubscribeForBlocks(blockCh) 947 bc.SubscribeForTransactions(txCh) 948 bc.SubscribeForNotifications(notificationCh) 949 bc.SubscribeForExecutions(executionCh) 950 951 assert.Empty(t, notificationCh) 952 assert.Empty(t, executionCh) 953 assert.Empty(t, blockCh) 954 assert.Empty(t, txCh) 955 956 generatedB := e.AddNewBlock(t) 957 require.Eventually(t, func() bool { return len(blockCh) != 0 }, time.Second, 10*time.Millisecond) 958 assert.Len(t, notificationCh, 1) // validator bounty 959 assert.Len(t, executionCh, 2) 960 assert.Empty(t, txCh) 961 962 b := <-blockCh 963 assert.Equal(t, generatedB, b) 964 assert.Empty(t, blockCh) 965 966 aer := <-executionCh 967 assert.Equal(t, b.Hash(), aer.Container) 968 aer = <-executionCh 969 assert.Equal(t, b.Hash(), aer.Container) 970 971 notif := <-notificationCh 972 require.Equal(t, bc.UtilityTokenHash(), notif.ScriptHash) 973 974 script := io.NewBufBinWriter() 975 emit.Bytes(script.BinWriter, []byte("yay!")) 976 emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify) 977 require.NoError(t, script.Err) 978 txGood1 := e.PrepareInvocation(t, script.Bytes(), []neotest.Signer{acc}) 979 980 // Reset() reuses the script buffer and we need to keep scripts. 981 script = io.NewBufBinWriter() 982 emit.Bytes(script.BinWriter, []byte("nay!")) 983 emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify) 984 emit.Opcodes(script.BinWriter, opcode.THROW) 985 require.NoError(t, script.Err) 986 txBad := e.PrepareInvocation(t, script.Bytes(), []neotest.Signer{acc}) 987 988 script = io.NewBufBinWriter() 989 emit.Bytes(script.BinWriter, []byte("yay! yay! yay!")) 990 emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify) 991 require.NoError(t, script.Err) 992 txGood2 := e.PrepareInvocation(t, script.Bytes(), []neotest.Signer{acc}) 993 994 invBlock := e.AddNewBlock(t, txGood1, txBad, txGood2) 995 996 require.Eventually(t, func() bool { 997 return len(blockCh) != 0 && len(txCh) != 0 && 998 len(notificationCh) != 0 && len(executionCh) != 0 999 }, time.Second, 10*time.Millisecond) 1000 1001 b = <-blockCh 1002 require.Equal(t, invBlock, b) 1003 assert.Empty(t, blockCh) 1004 1005 exec := <-executionCh 1006 require.Equal(t, b.Hash(), exec.Container) 1007 require.Equal(t, exec.VMState, vmstate.Halt) 1008 1009 // 3 burn events for every tx and 1 mint for primary node 1010 require.True(t, len(notificationCh) >= 4) 1011 for i := 0; i < 4; i++ { 1012 notif := <-notificationCh 1013 require.Equal(t, nativeGASHash, notif.ScriptHash) 1014 } 1015 1016 // Follow in-block transaction order. 1017 for _, txExpected := range invBlock.Transactions { 1018 tx := <-txCh 1019 require.Equal(t, txExpected, tx) 1020 exec := <-executionCh 1021 require.Equal(t, tx.Hash(), exec.Container) 1022 if exec.VMState == vmstate.Halt { 1023 notif := <-notificationCh 1024 require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash) 1025 } 1026 } 1027 assert.Empty(t, txCh) 1028 assert.Len(t, notificationCh, 1) 1029 assert.Len(t, executionCh, 1) 1030 1031 notif = <-notificationCh 1032 require.Equal(t, bc.UtilityTokenHash(), notif.ScriptHash) 1033 1034 exec = <-executionCh 1035 require.Equal(t, b.Hash(), exec.Container) 1036 require.Equal(t, exec.VMState, vmstate.Halt) 1037 1038 bc.UnsubscribeFromBlocks(blockCh) 1039 bc.UnsubscribeFromTransactions(txCh) 1040 bc.UnsubscribeFromNotifications(notificationCh) 1041 bc.UnsubscribeFromExecutions(executionCh) 1042 1043 // Ensure that new blocks are processed correctly after unsubscription. 1044 e.GenerateNewBlocks(t, 2*chBufSize) 1045 } 1046 1047 func TestBlockchain_RemoveUntraceable(t *testing.T) { 1048 neoCommitteeKey := []byte{0xfb, 0xff, 0xff, 0xff, 0x0e} 1049 check := func(t *testing.T, bc *core.Blockchain, tHash, bHash, sHash util.Uint256, errorExpected bool) { 1050 _, _, err := bc.GetTransaction(tHash) 1051 if errorExpected { 1052 require.Error(t, err) 1053 } else { 1054 require.NoError(t, err) 1055 } 1056 _, err = bc.GetAppExecResults(tHash, trigger.Application) 1057 if errorExpected { 1058 require.Error(t, err) 1059 } else { 1060 require.NoError(t, err) 1061 } 1062 _, err = bc.GetBlock(bHash) 1063 if errorExpected { 1064 require.Error(t, err) 1065 } else { 1066 require.NoError(t, err) 1067 } 1068 _, err = bc.GetHeader(bHash) 1069 require.NoError(t, err) 1070 if !sHash.Equals(util.Uint256{}) { 1071 sm := bc.GetStateModule() 1072 _, err = sm.GetState(sHash, neoCommitteeKey) 1073 if errorExpected { 1074 require.Error(t, err) 1075 } else { 1076 require.NoError(t, err) 1077 } 1078 } 1079 } 1080 t.Run("P2PStateExchangeExtensions off", func(t *testing.T) { 1081 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 1082 c.MaxTraceableBlocks = 2 1083 c.Ledger.GarbageCollectionPeriod = 2 1084 c.Ledger.RemoveUntraceableBlocks = true 1085 }) 1086 e := neotest.NewExecutor(t, bc, acc, acc) 1087 neoValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Neo)) 1088 1089 tx1Hash := neoValidatorInvoker.Invoke(t, true, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 1090 tx1Height := bc.BlockHeight() 1091 b1 := e.TopBlock(t) 1092 sRoot, err := bc.GetStateModule().GetStateRoot(tx1Height) 1093 require.NoError(t, err) 1094 1095 neoValidatorInvoker.Invoke(t, true, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 1096 1097 _, h1, err := bc.GetTransaction(tx1Hash) 1098 require.NoError(t, err) 1099 require.Equal(t, tx1Height, h1) 1100 1101 check(t, bc, tx1Hash, b1.Hash(), sRoot.Root, false) 1102 e.GenerateNewBlocks(t, 4) 1103 1104 sm := bc.GetStateModule() 1105 require.Eventually(t, func() bool { 1106 _, err = sm.GetState(sRoot.Root, neoCommitteeKey) 1107 return err != nil 1108 }, 2*bcPersistInterval, 10*time.Millisecond) 1109 check(t, bc, tx1Hash, b1.Hash(), sRoot.Root, true) 1110 }) 1111 t.Run("P2PStateExchangeExtensions on", func(t *testing.T) { 1112 bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { 1113 c.MaxTraceableBlocks = 2 1114 c.Ledger.GarbageCollectionPeriod = 2 1115 c.Ledger.RemoveUntraceableBlocks = true 1116 c.P2PStateExchangeExtensions = true 1117 c.StateSyncInterval = 2 1118 c.StateRootInHeader = true 1119 }) 1120 e := neotest.NewExecutor(t, bc, acc, acc) 1121 neoValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Neo)) 1122 1123 tx1Hash := neoValidatorInvoker.Invoke(t, true, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 1124 tx1Height := bc.BlockHeight() 1125 b1 := e.TopBlock(t) 1126 sRoot, err := bc.GetStateModule().GetStateRoot(tx1Height) 1127 require.NoError(t, err) 1128 1129 tx2Hash := neoValidatorInvoker.Invoke(t, true, "transfer", acc.ScriptHash(), util.Uint160{1, 2, 3}, 1, nil) 1130 tx2Height := bc.BlockHeight() 1131 b2 := e.TopBlock(t) 1132 1133 _, h1, err := bc.GetTransaction(tx1Hash) 1134 require.NoError(t, err) 1135 require.Equal(t, tx1Height, h1) 1136 1137 e.GenerateNewBlocks(t, 3) 1138 1139 check(t, bc, tx1Hash, b1.Hash(), sRoot.Root, false) 1140 check(t, bc, tx2Hash, b2.Hash(), sRoot.Root, false) 1141 1142 e.AddNewBlock(t) 1143 1144 check(t, bc, tx1Hash, b1.Hash(), util.Uint256{}, true) 1145 check(t, bc, tx2Hash, b2.Hash(), util.Uint256{}, false) 1146 _, h2, err := bc.GetTransaction(tx2Hash) 1147 require.NoError(t, err) 1148 require.Equal(t, tx2Height, h2) 1149 }) 1150 } 1151 1152 func TestBlockchain_InvalidNotification(t *testing.T) { 1153 bc, acc := chain.NewSingle(t) 1154 e := neotest.NewExecutor(t, bc, acc, acc) 1155 1156 cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash()) 1157 e.DeployContract(t, &neotest.Contract{ 1158 Hash: cs.Hash, 1159 NEF: &cs.NEF, 1160 Manifest: &cs.Manifest, 1161 }, nil) 1162 1163 cValidatorInvoker := e.ValidatorInvoker(cs.Hash) 1164 cValidatorInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { 1165 require.Equal(t, 1, len(stack)) 1166 require.Nil(t, stack[0]) 1167 }, "invalidStack1") 1168 cValidatorInvoker.Invoke(t, stackitem.NewInterop(nil), "invalidStack2") 1169 } 1170 1171 // Test that deletion of non-existent doesn't result in error in tx or block addition. 1172 func TestBlockchain_MPTDeleteNoKey(t *testing.T) { 1173 bc, acc := chain.NewSingle(t) 1174 e := neotest.NewExecutor(t, bc, acc, acc) 1175 1176 cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash()) 1177 e.DeployContract(t, &neotest.Contract{ 1178 Hash: cs.Hash, 1179 NEF: &cs.NEF, 1180 Manifest: &cs.Manifest, 1181 }, nil) 1182 1183 cValidatorInvoker := e.ValidatorInvoker(cs.Hash) 1184 cValidatorInvoker.Invoke(t, stackitem.Null{}, "delValue", "non-existent-key") 1185 } 1186 1187 // Test that all default configurations are loadable. 1188 func TestConfig_LoadDefaultConfigs(t *testing.T) { 1189 var prefixPath = filepath.Join("..", "..", "config") 1190 check := func(t *testing.T, cfgFileSuffix any) { 1191 cfgPath := filepath.Join(prefixPath, fmt.Sprintf("protocol.%s.yml", cfgFileSuffix)) 1192 _, err := config.LoadFile(cfgPath) 1193 require.NoError(t, err, fmt.Errorf("failed to load %s", cfgPath)) 1194 } 1195 testCases := []any{ 1196 netmode.MainNet, 1197 netmode.PrivNet, 1198 netmode.TestNet, 1199 netmode.UnitTestNet, 1200 "privnet.docker.one", 1201 "privnet.docker.two", 1202 "privnet.docker.three", 1203 "privnet.docker.four", 1204 "privnet.docker.single", 1205 "unit_testnet.single", 1206 } 1207 for _, tc := range testCases { 1208 t.Run(fmt.Sprintf("%s", tc), func(t *testing.T) { 1209 check(t, tc) 1210 }) 1211 } 1212 } 1213 1214 func TestBlockchain_VerifyTx(t *testing.T) { 1215 bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 1216 c.P2PSigExtensions = true 1217 c.ReservedAttributes = true 1218 }) 1219 e := neotest.NewExecutor(t, bc, validator, committee) 1220 1221 accs := make([]*wallet.Account, 5) 1222 for i := range accs { 1223 var err error 1224 accs[i], err = wallet.NewAccount() 1225 require.NoError(t, err) 1226 } 1227 1228 notaryServiceFeePerKey := bc.GetNotaryServiceFeePerKey() 1229 1230 oracleAcc := accs[2] 1231 oraclePubs := keys.PublicKeys{oracleAcc.PublicKey()} 1232 require.NoError(t, oracleAcc.ConvertMultisig(1, oraclePubs)) 1233 1234 neoHash := e.NativeHash(t, nativenames.Neo) 1235 gasHash := e.NativeHash(t, nativenames.Gas) 1236 policyHash := e.NativeHash(t, nativenames.Policy) 1237 designateHash := e.NativeHash(t, nativenames.Designation) 1238 notaryHash := e.NativeHash(t, nativenames.Notary) 1239 oracleHash := e.NativeHash(t, nativenames.Oracle) 1240 1241 neoValidatorsInvoker := e.ValidatorInvoker(neoHash) 1242 gasValidatorsInvoker := e.ValidatorInvoker(gasHash) 1243 policySuperInvoker := e.NewInvoker(policyHash, validator, committee) 1244 designateSuperInvoker := e.NewInvoker(designateHash, validator, committee) 1245 neoOwner := validator.ScriptHash() 1246 1247 neoAmount := int64(1_000_000) 1248 gasAmount := int64(1_000_000_000) 1249 txs := make([]*transaction.Transaction, 0, 2*len(accs)+2) 1250 for _, a := range accs { 1251 txs = append(txs, neoValidatorsInvoker.PrepareInvoke(t, "transfer", neoOwner, a.Contract.ScriptHash(), neoAmount, nil)) 1252 txs = append(txs, gasValidatorsInvoker.PrepareInvoke(t, "transfer", neoOwner, a.Contract.ScriptHash(), gasAmount, nil)) 1253 } 1254 txs = append(txs, neoValidatorsInvoker.PrepareInvoke(t, "transfer", neoOwner, committee.ScriptHash(), neoAmount, nil)) 1255 txs = append(txs, gasValidatorsInvoker.PrepareInvoke(t, "transfer", neoOwner, committee.ScriptHash(), gasAmount, nil)) 1256 e.AddNewBlock(t, txs...) 1257 for _, tx := range txs { 1258 e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) 1259 } 1260 policySuperInvoker.Invoke(t, true, "blockAccount", accs[1].PrivateKey().GetScriptHash().BytesBE()) 1261 1262 checkErr := func(t *testing.T, expectedErr error, tx *transaction.Transaction) { 1263 err := bc.VerifyTx(tx) 1264 require.ErrorIs(t, err, expectedErr) 1265 } 1266 1267 testScript := []byte{byte(opcode.PUSH1)} 1268 newTestTx := func(t *testing.T, signer util.Uint160, script []byte) *transaction.Transaction { 1269 tx := transaction.New(script, 1_000_000) 1270 tx.Nonce = neotest.Nonce() 1271 tx.ValidUntilBlock = e.Chain.BlockHeight() + 5 1272 tx.Signers = []transaction.Signer{{ 1273 Account: signer, 1274 Scopes: transaction.CalledByEntry, 1275 }} 1276 tx.NetworkFee = int64(io.GetVarSize(tx)+200 /* witness */) * bc.FeePerByte() 1277 tx.NetworkFee += 1_000_000 // verification cost 1278 return tx 1279 } 1280 1281 h := accs[0].PrivateKey().GetScriptHash() 1282 t.Run("Expired", func(t *testing.T) { 1283 tx := newTestTx(t, h, testScript) 1284 tx.ValidUntilBlock = 1 1285 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1286 checkErr(t, core.ErrTxExpired, tx) 1287 }) 1288 t.Run("BlockedAccount", func(t *testing.T) { 1289 tx := newTestTx(t, accs[1].PrivateKey().GetScriptHash(), testScript) 1290 require.NoError(t, accs[1].SignTx(netmode.UnitTestNet, tx)) 1291 checkErr(t, core.ErrPolicy, tx) 1292 }) 1293 t.Run("InsufficientGas", func(t *testing.T) { 1294 balance := bc.GetUtilityTokenBalance(h) 1295 tx := newTestTx(t, h, testScript) 1296 tx.SystemFee = balance.Int64() + 1 1297 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1298 checkErr(t, core.ErrInsufficientFunds, tx) 1299 }) 1300 t.Run("TooBigSystemFee", func(t *testing.T) { 1301 tx := newTestTx(t, h, testScript) 1302 tx.SystemFee = bc.GetConfig().MaxBlockSystemFee + 100500 1303 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1304 checkErr(t, core.ErrPolicy, tx) 1305 }) 1306 t.Run("TooBigTx", func(t *testing.T) { 1307 script := make([]byte, transaction.MaxTransactionSize) 1308 tx := newTestTx(t, h, script) 1309 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1310 checkErr(t, core.ErrTxTooBig, tx) 1311 }) 1312 t.Run("NetworkFee", func(t *testing.T) { 1313 t.Run("SmallNetworkFee", func(t *testing.T) { 1314 tx := newTestTx(t, h, testScript) 1315 tx.NetworkFee = 1 1316 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1317 checkErr(t, core.ErrTxSmallNetworkFee, tx) 1318 }) 1319 t.Run("AlmostEnoughNetworkFee", func(t *testing.T) { 1320 tx := newTestTx(t, h, testScript) 1321 verificationNetFee, calcultedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script) 1322 expectedSize := io.GetVarSize(tx) + calcultedScriptSize 1323 calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() 1324 tx.NetworkFee = calculatedNetFee - 1 1325 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1326 require.Equal(t, expectedSize, io.GetVarSize(tx)) 1327 checkErr(t, core.ErrVerificationFailed, tx) 1328 }) 1329 t.Run("EnoughNetworkFee", func(t *testing.T) { 1330 tx := newTestTx(t, h, testScript) 1331 verificationNetFee, calcultedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script) 1332 expectedSize := io.GetVarSize(tx) + calcultedScriptSize 1333 calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() 1334 tx.NetworkFee = calculatedNetFee 1335 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1336 require.Equal(t, expectedSize, io.GetVarSize(tx)) 1337 require.NoError(t, bc.VerifyTx(tx)) 1338 }) 1339 t.Run("CalculateNetworkFee, signature script", func(t *testing.T) { 1340 tx := newTestTx(t, h, testScript) 1341 expectedSize := io.GetVarSize(tx) 1342 verificationNetFee, calculatedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script) 1343 expectedSize += calculatedScriptSize 1344 expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() 1345 tx.NetworkFee = expectedNetFee 1346 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1347 actualSize := io.GetVarSize(tx) 1348 require.Equal(t, expectedSize, actualSize) 1349 gasConsumed, err := bc.VerifyWitness(h, tx, &tx.Scripts[0], -1) 1350 require.NoError(t, err) 1351 require.Equal(t, verificationNetFee, gasConsumed) 1352 require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed) 1353 }) 1354 t.Run("CalculateNetworkFee, multisignature script", func(t *testing.T) { 1355 multisigAcc := accs[4] 1356 pKeys := keys.PublicKeys{multisigAcc.PublicKey()} 1357 require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys)) 1358 multisigHash := hash.Hash160(multisigAcc.Contract.Script) 1359 tx := newTestTx(t, multisigHash, testScript) 1360 verificationNetFee, calculatedScriptSize := fee.Calculate(bc.GetBaseExecFee(), multisigAcc.Contract.Script) 1361 expectedSize := io.GetVarSize(tx) + calculatedScriptSize 1362 expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() 1363 tx.NetworkFee = expectedNetFee 1364 require.NoError(t, multisigAcc.SignTx(netmode.UnitTestNet, tx)) 1365 actualSize := io.GetVarSize(tx) 1366 require.Equal(t, expectedSize, actualSize) 1367 gasConsumed, err := bc.VerifyWitness(multisigHash, tx, &tx.Scripts[0], -1) 1368 require.NoError(t, err) 1369 require.Equal(t, verificationNetFee, gasConsumed) 1370 require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed) 1371 }) 1372 }) 1373 t.Run("InvalidTxScript", func(t *testing.T) { 1374 tx := newTestTx(t, h, testScript) 1375 tx.Script = append(tx.Script, 0xff) 1376 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1377 checkErr(t, core.ErrInvalidScript, tx) 1378 }) 1379 t.Run("InvalidVerificationScript", func(t *testing.T) { 1380 tx := newTestTx(t, h, testScript) 1381 verif := []byte{byte(opcode.JMP), 3, 0xff, byte(opcode.PUSHT)} 1382 tx.Signers = append(tx.Signers, transaction.Signer{ 1383 Account: hash.Hash160(verif), 1384 Scopes: transaction.Global, 1385 }) 1386 tx.NetworkFee += 1000000 1387 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1388 tx.Scripts = append(tx.Scripts, transaction.Witness{ 1389 InvocationScript: []byte{}, 1390 VerificationScript: verif, 1391 }) 1392 checkErr(t, core.ErrInvalidVerificationScript, tx) 1393 }) 1394 t.Run("InvalidInvocationScript", func(t *testing.T) { 1395 tx := newTestTx(t, h, testScript) 1396 verif := []byte{byte(opcode.PUSHT)} 1397 tx.Signers = append(tx.Signers, transaction.Signer{ 1398 Account: hash.Hash160(verif), 1399 Scopes: transaction.Global, 1400 }) 1401 tx.NetworkFee += 1000000 1402 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1403 tx.Scripts = append(tx.Scripts, transaction.Witness{ 1404 InvocationScript: []byte{byte(opcode.JMP), 3, 0xff}, 1405 VerificationScript: verif, 1406 }) 1407 checkErr(t, core.ErrInvalidInvocationScript, tx) 1408 }) 1409 t.Run("Conflict", func(t *testing.T) { 1410 balance := bc.GetUtilityTokenBalance(h).Int64() 1411 tx := newTestTx(t, h, testScript) 1412 tx.NetworkFee = balance / 2 1413 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1414 require.NoError(t, bc.PoolTx(tx)) 1415 1416 tx2 := newTestTx(t, h, testScript) 1417 tx2.NetworkFee = balance / 2 1418 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx2)) 1419 err := bc.PoolTx(tx2) 1420 require.ErrorIs(t, err, core.ErrMemPoolConflict) 1421 }) 1422 t.Run("InvalidWitnessHash", func(t *testing.T) { 1423 tx := newTestTx(t, h, testScript) 1424 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1425 tx.Scripts[0].VerificationScript = []byte{byte(opcode.PUSHT)} 1426 checkErr(t, core.ErrWitnessHashMismatch, tx) 1427 }) 1428 t.Run("InvalidWitnessSignature", func(t *testing.T) { 1429 tx := newTestTx(t, h, testScript) 1430 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1431 tx.Scripts[0].InvocationScript[10] = ^tx.Scripts[0].InvocationScript[10] 1432 checkErr(t, core.ErrVerificationFailed, tx) 1433 }) 1434 t.Run("InsufficientNetworkFeeForSecondWitness", func(t *testing.T) { 1435 tx := newTestTx(t, h, testScript) 1436 tx.Signers = append(tx.Signers, transaction.Signer{ 1437 Account: accs[3].PrivateKey().GetScriptHash(), 1438 Scopes: transaction.Global, 1439 }) 1440 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1441 require.NoError(t, accs[3].SignTx(netmode.UnitTestNet, tx)) 1442 checkErr(t, core.ErrVerificationFailed, tx) 1443 }) 1444 t.Run("OldTX", func(t *testing.T) { 1445 tx := newTestTx(t, h, testScript) 1446 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1447 e.AddNewBlock(t, tx) 1448 1449 checkErr(t, core.ErrAlreadyExists, tx) 1450 }) 1451 t.Run("MemPooledTX", func(t *testing.T) { 1452 tx := newTestTx(t, h, testScript) 1453 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1454 require.NoError(t, bc.PoolTx(tx)) 1455 1456 err := bc.PoolTx(tx) 1457 require.ErrorIs(t, err, core.ErrAlreadyInPool) 1458 }) 1459 t.Run("MemPoolOOM", func(t *testing.T) { 1460 mp := mempool.New(1, 0, false, nil) 1461 tx1 := newTestTx(t, h, testScript) 1462 tx1.NetworkFee += 10000 // Give it more priority. 1463 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx1)) 1464 require.NoError(t, bc.PoolTx(tx1, mp)) 1465 1466 tx2 := newTestTx(t, h, testScript) 1467 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx2)) 1468 err := bc.PoolTx(tx2, mp) 1469 require.ErrorIs(t, err, core.ErrOOM) 1470 }) 1471 t.Run("Attribute", func(t *testing.T) { 1472 t.Run("InvalidHighPriority", func(t *testing.T) { 1473 tx := newTestTx(t, h, testScript) 1474 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.HighPriority}) 1475 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1476 checkErr(t, core.ErrInvalidAttribute, tx) 1477 }) 1478 t.Run("ValidHighPriority", func(t *testing.T) { 1479 tx := newTestTx(t, h, testScript) 1480 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.HighPriority}) 1481 tx.NetworkFee += 4_000_000 // multisig check 1482 tx.Signers = []transaction.Signer{{ 1483 Account: committee.ScriptHash(), 1484 Scopes: transaction.None, 1485 }} 1486 rawScript := committee.Script() 1487 size := io.GetVarSize(tx) 1488 netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript) 1489 tx.NetworkFee += netFee 1490 tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte() 1491 tx.Scripts = []transaction.Witness{{ 1492 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 1493 VerificationScript: rawScript, 1494 }} 1495 require.NoError(t, bc.VerifyTx(tx)) 1496 }) 1497 t.Run("Oracle", func(t *testing.T) { 1498 cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0) 1499 e.DeployContract(t, &neotest.Contract{ 1500 Hash: cs.Hash, 1501 NEF: &cs.NEF, 1502 Manifest: &cs.Manifest, 1503 }, nil) 1504 cInvoker := e.ValidatorInvoker(cs.Hash) 1505 1506 const gasForResponse int64 = 10_000_000 1507 cInvoker.Invoke(t, stackitem.Null{}, "requestURL", "https://get.1234", "", "handle", []byte{}, gasForResponse) 1508 1509 oracleScript, err := smartcontract.CreateMajorityMultiSigRedeemScript(oraclePubs) 1510 require.NoError(t, err) 1511 oracleMultisigHash := hash.Hash160(oracleScript) 1512 1513 respScript := native.CreateOracleResponseScript(oracleHash) 1514 1515 // We need to create new transaction, 1516 // because hashes are cached after signing. 1517 getOracleTx := func(t *testing.T) *transaction.Transaction { 1518 tx := transaction.New(respScript, 1000_0000) 1519 tx.Nonce = neotest.Nonce() 1520 tx.ValidUntilBlock = bc.BlockHeight() + 1 1521 resp := &transaction.OracleResponse{ 1522 ID: 0, 1523 Code: transaction.Success, 1524 Result: []byte{1, 2, 3}, 1525 } 1526 tx.Attributes = []transaction.Attribute{{ 1527 Type: transaction.OracleResponseT, 1528 Value: resp, 1529 }} 1530 tx.NetworkFee += 4_000_000 // multisig check 1531 tx.SystemFee = gasForResponse - tx.NetworkFee 1532 tx.Signers = []transaction.Signer{{ 1533 Account: oracleMultisigHash, 1534 Scopes: transaction.None, 1535 }} 1536 size := io.GetVarSize(tx) 1537 netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), oracleScript) 1538 tx.NetworkFee += netFee 1539 tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte() 1540 return tx 1541 } 1542 1543 t.Run("NoOracleNodes", func(t *testing.T) { 1544 tx := getOracleTx(t) 1545 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1546 checkErr(t, core.ErrInvalidAttribute, tx) 1547 }) 1548 1549 keys := make([]any, 0, len(oraclePubs)) 1550 for _, p := range oraclePubs { 1551 keys = append(keys, p.Bytes()) 1552 } 1553 designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", 1554 int64(noderoles.Oracle), keys) 1555 1556 t.Run("Valid", func(t *testing.T) { 1557 tx := getOracleTx(t) 1558 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1559 require.NoError(t, bc.VerifyTx(tx)) 1560 1561 t.Run("NativeVerify", func(t *testing.T) { 1562 tx.Signers = append(tx.Signers, transaction.Signer{ 1563 Account: oracleHash, 1564 Scopes: transaction.None, 1565 }) 1566 tx.Scripts = append(tx.Scripts, transaction.Witness{}) 1567 t.Run("NonZeroVerification", func(t *testing.T) { 1568 w := io.NewBufBinWriter() 1569 emit.Opcodes(w.BinWriter, opcode.ABORT) 1570 emit.Bytes(w.BinWriter, util.Uint160{}.BytesBE()) 1571 emit.Int(w.BinWriter, 0) 1572 emit.String(w.BinWriter, nativenames.Oracle) 1573 tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes() 1574 err := bc.VerifyTx(tx) 1575 require.ErrorIs(t, err, core.ErrNativeContractWitness) 1576 }) 1577 t.Run("Good", func(t *testing.T) { 1578 tx.Scripts[len(tx.Scripts)-1].VerificationScript = nil 1579 require.NoError(t, bc.VerifyTx(tx)) 1580 }) 1581 }) 1582 }) 1583 t.Run("InvalidRequestID", func(t *testing.T) { 1584 tx := getOracleTx(t) 1585 tx.Attributes[0].Value.(*transaction.OracleResponse).ID = 2 1586 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1587 checkErr(t, core.ErrInvalidAttribute, tx) 1588 }) 1589 t.Run("InvalidScope", func(t *testing.T) { 1590 tx := getOracleTx(t) 1591 tx.Signers[0].Scopes = transaction.Global 1592 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1593 checkErr(t, core.ErrInvalidAttribute, tx) 1594 }) 1595 t.Run("InvalidScript", func(t *testing.T) { 1596 tx := getOracleTx(t) 1597 tx.Script = append(tx.Script, byte(opcode.NOP)) 1598 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1599 checkErr(t, core.ErrInvalidAttribute, tx) 1600 }) 1601 t.Run("InvalidSigner", func(t *testing.T) { 1602 tx := getOracleTx(t) 1603 tx.Signers[0].Account = accs[0].Contract.ScriptHash() 1604 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1605 checkErr(t, core.ErrInvalidAttribute, tx) 1606 }) 1607 t.Run("SmallFee", func(t *testing.T) { 1608 tx := getOracleTx(t) 1609 tx.SystemFee = 0 1610 require.NoError(t, oracleAcc.SignTx(netmode.UnitTestNet, tx)) 1611 checkErr(t, core.ErrInvalidAttribute, tx) 1612 }) 1613 }) 1614 t.Run("NotValidBefore", func(t *testing.T) { 1615 getNVBTx := func(e *neotest.Executor, height uint32) *transaction.Transaction { 1616 tx := newTestTx(t, h, testScript) 1617 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: height}}) 1618 tx.NetworkFee += 4_000_000 // multisig check 1619 tx.Signers = []transaction.Signer{{ 1620 Account: e.Validator.ScriptHash(), 1621 Scopes: transaction.None, 1622 }} 1623 size := io.GetVarSize(tx) 1624 rawScript := e.Validator.Script() 1625 netFee, sizeDelta := fee.Calculate(e.Chain.GetBaseExecFee(), rawScript) 1626 tx.NetworkFee += netFee 1627 tx.NetworkFee += int64(size+sizeDelta) * e.Chain.FeePerByte() 1628 tx.Scripts = []transaction.Witness{{ 1629 InvocationScript: e.Validator.SignHashable(uint32(netmode.UnitTestNet), tx), 1630 VerificationScript: rawScript, 1631 }} 1632 return tx 1633 } 1634 t.Run("Disabled", func(t *testing.T) { // check that NVB attribute is not an extension anymore. 1635 bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 1636 c.P2PSigExtensions = false 1637 c.ReservedAttributes = false 1638 }) 1639 eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 1640 tx := getNVBTx(eBad, bcBad.BlockHeight()) 1641 err := bcBad.VerifyTx(tx) 1642 require.NoError(t, err) 1643 }) 1644 t.Run("Enabled", func(t *testing.T) { 1645 t.Run("NotYetValid", func(t *testing.T) { 1646 tx := getNVBTx(e, bc.BlockHeight()+1) 1647 require.ErrorIs(t, bc.VerifyTx(tx), core.ErrInvalidAttribute) 1648 }) 1649 t.Run("positive", func(t *testing.T) { 1650 tx := getNVBTx(e, bc.BlockHeight()) 1651 require.NoError(t, bc.VerifyTx(tx)) 1652 }) 1653 }) 1654 }) 1655 t.Run("Reserved", func(t *testing.T) { 1656 getReservedTx := func(e *neotest.Executor, attrType transaction.AttrType) *transaction.Transaction { 1657 tx := newTestTx(t, h, testScript) 1658 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: attrType, Value: &transaction.Reserved{Value: []byte{1, 2, 3}}}) 1659 tx.NetworkFee += 4_000_000 // multisig check 1660 tx.Signers = []transaction.Signer{{ 1661 Account: e.Validator.ScriptHash(), 1662 Scopes: transaction.None, 1663 }} 1664 rawScript := e.Validator.Script() 1665 size := io.GetVarSize(tx) 1666 netFee, sizeDelta := fee.Calculate(e.Chain.GetBaseExecFee(), rawScript) 1667 tx.NetworkFee += netFee 1668 tx.NetworkFee += int64(size+sizeDelta) * e.Chain.FeePerByte() 1669 tx.Scripts = []transaction.Witness{{ 1670 InvocationScript: e.Validator.SignHashable(uint32(netmode.UnitTestNet), tx), 1671 VerificationScript: rawScript, 1672 }} 1673 return tx 1674 } 1675 t.Run("Disabled", func(t *testing.T) { 1676 bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 1677 c.P2PSigExtensions = false 1678 c.ReservedAttributes = false 1679 }) 1680 eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 1681 tx := getReservedTx(eBad, transaction.ReservedLowerBound+3) 1682 err := bcBad.VerifyTx(tx) 1683 require.Error(t, err) 1684 require.True(t, strings.Contains(err.Error(), "invalid attribute: attribute of reserved type was found, but ReservedAttributes are disabled")) 1685 }) 1686 t.Run("Enabled", func(t *testing.T) { 1687 tx := getReservedTx(e, transaction.ReservedLowerBound+3) 1688 require.NoError(t, bc.VerifyTx(tx)) 1689 }) 1690 }) 1691 t.Run("Conflicts", func(t *testing.T) { 1692 getConflictsTx := func(e *neotest.Executor, hashes ...util.Uint256) *transaction.Transaction { 1693 tx := newTestTx(t, h, testScript) 1694 tx.Attributes = make([]transaction.Attribute, len(hashes)) 1695 for i, h := range hashes { 1696 tx.Attributes[i] = transaction.Attribute{ 1697 Type: transaction.ConflictsT, 1698 Value: &transaction.Conflicts{ 1699 Hash: h, 1700 }, 1701 } 1702 } 1703 tx.NetworkFee += 4_000_000 // multisig check 1704 tx.Signers = []transaction.Signer{{ 1705 Account: e.Validator.ScriptHash(), 1706 Scopes: transaction.None, 1707 }} 1708 rawScript := e.Validator.Script() 1709 size := io.GetVarSize(tx) 1710 netFee, sizeDelta := fee.Calculate(e.Chain.GetBaseExecFee(), rawScript) 1711 tx.NetworkFee += netFee 1712 tx.NetworkFee += int64(size+sizeDelta) * e.Chain.FeePerByte() 1713 tx.Scripts = []transaction.Witness{{ 1714 InvocationScript: e.Validator.SignHashable(uint32(netmode.UnitTestNet), tx), 1715 VerificationScript: rawScript, 1716 }} 1717 return tx 1718 } 1719 t.Run("disabled", func(t *testing.T) { // check that Conflicts attribute is not an extension anymore. 1720 bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 1721 c.P2PSigExtensions = false 1722 c.ReservedAttributes = false 1723 }) 1724 eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 1725 tx := getConflictsTx(eBad, util.Uint256{1, 2, 3}) 1726 err := bcBad.VerifyTx(tx) 1727 require.NoError(t, err) 1728 }) 1729 t.Run("enabled", func(t *testing.T) { 1730 t.Run("dummy on-chain conflict", func(t *testing.T) { 1731 t.Run("on-chain conflict signed by malicious party", func(t *testing.T) { 1732 tx := newTestTx(t, h, testScript) 1733 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) 1734 conflicting := transaction.New([]byte{byte(opcode.RET)}, 1000_0000) 1735 conflicting.ValidUntilBlock = bc.BlockHeight() + 1 1736 conflicting.Signers = []transaction.Signer{ 1737 { 1738 Account: validator.ScriptHash(), 1739 Scopes: transaction.CalledByEntry, 1740 }, 1741 } 1742 conflicting.Attributes = []transaction.Attribute{ 1743 { 1744 Type: transaction.ConflictsT, 1745 Value: &transaction.Conflicts{ 1746 Hash: tx.Hash(), 1747 }, 1748 }, 1749 } 1750 conflicting.NetworkFee = 1000_0000 1751 require.NoError(t, validator.SignTx(netmode.UnitTestNet, conflicting)) 1752 e.AddNewBlock(t, conflicting) 1753 // We expect `tx` to pass verification, because on-chained `conflicting` doesn't have 1754 // `tx`'s payer in the signers list, thus, `conflicting` should be considered as 1755 // malicious conflict. 1756 require.NoError(t, bc.VerifyTx(tx)) 1757 }) 1758 t.Run("multiple on-chain conflicts signed by malicious parties", func(t *testing.T) { 1759 m1 := e.NewAccount(t) 1760 m2 := e.NewAccount(t) 1761 m3 := e.NewAccount(t) 1762 good := e.NewAccount(t) 1763 1764 // txGood doesn't conflict with anyone and signed by good signer. 1765 txGood := newTestTx(t, good.ScriptHash(), testScript) 1766 require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) 1767 1768 // txM1 conflicts with txGood and signed by two malicious signers. 1769 txM1 := newTestTx(t, m1.ScriptHash(), testScript) 1770 txM1.Signers = append(txM1.Signers, transaction.Signer{Account: m2.ScriptHash()}) 1771 txM1.Attributes = []transaction.Attribute{ 1772 { 1773 Type: transaction.ConflictsT, 1774 Value: &transaction.Conflicts{ 1775 Hash: txGood.Hash(), 1776 }, 1777 }, 1778 } 1779 txM1.NetworkFee = 1_000_0000 1780 require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) 1781 require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM1)) 1782 e.AddNewBlock(t, txM1) 1783 1784 // txM2 conflicts with txGood and signed by one malicious signer. 1785 txM2 := newTestTx(t, m3.ScriptHash(), testScript) 1786 txM2.Attributes = []transaction.Attribute{ 1787 { 1788 Type: transaction.ConflictsT, 1789 Value: &transaction.Conflicts{ 1790 Hash: txGood.Hash(), 1791 }, 1792 }, 1793 } 1794 txM2.NetworkFee = 1_000_0000 1795 require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) 1796 e.AddNewBlock(t, txM2) 1797 1798 // We expect `tx` to pass verification, because on-chained `conflicting` doesn't have 1799 // `tx`'s payer in the signers list, thus, `conflicting` should be considered as 1800 // malicious conflict. 1801 require.NoError(t, bc.VerifyTx(txGood)) 1802 1803 // After that txGood can be added to the chain normally. 1804 e.AddNewBlock(t, txGood) 1805 1806 // And after that ErrAlreadyExist is expected on verification. 1807 require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrAlreadyExists) 1808 }) 1809 1810 t.Run("multiple on-chain conflicts signed by [valid+malicious] parties", func(t *testing.T) { 1811 m1 := e.NewAccount(t) 1812 m2 := e.NewAccount(t) 1813 m3 := e.NewAccount(t) 1814 good := e.NewAccount(t) 1815 1816 // txGood doesn't conflict with anyone and signed by good signer. 1817 txGood := newTestTx(t, good.ScriptHash(), testScript) 1818 require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) 1819 1820 // txM1 conflicts with txGood and signed by one malicious and one good signers. 1821 txM1 := newTestTx(t, m1.ScriptHash(), testScript) 1822 txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) 1823 txM1.Attributes = []transaction.Attribute{ 1824 { 1825 Type: transaction.ConflictsT, 1826 Value: &transaction.Conflicts{ 1827 Hash: txGood.Hash(), 1828 }, 1829 }, 1830 } 1831 txM1.NetworkFee = 1_000_0000 1832 require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) 1833 require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) 1834 e.AddNewBlock(t, txM1) 1835 1836 // txM2 conflicts with txGood and signed by two malicious signers. 1837 txM2 := newTestTx(t, m2.ScriptHash(), testScript) 1838 txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) 1839 txM2.Attributes = []transaction.Attribute{ 1840 { 1841 Type: transaction.ConflictsT, 1842 Value: &transaction.Conflicts{ 1843 Hash: txGood.Hash(), 1844 }, 1845 }, 1846 } 1847 txM2.NetworkFee = 1_000_0000 1848 require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) 1849 require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) 1850 e.AddNewBlock(t, txM2) 1851 1852 // We expect `tx` to fail verification, because one of the on-chained `conflicting` 1853 // transactions has common signers with `tx`, thus, `conflicting` should be 1854 // considered as a valid conflict. 1855 require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) 1856 }) 1857 1858 t.Run("multiple on-chain conflicts signed by [malicious+valid] parties", func(t *testing.T) { 1859 m1 := e.NewAccount(t) 1860 m2 := e.NewAccount(t) 1861 m3 := e.NewAccount(t) 1862 good := e.NewAccount(t) 1863 1864 // txGood doesn't conflict with anyone and signed by good signer. 1865 txGood := newTestTx(t, good.ScriptHash(), testScript) 1866 require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) 1867 1868 // txM2 conflicts with txGood and signed by two malicious signers. 1869 txM2 := newTestTx(t, m2.ScriptHash(), testScript) 1870 txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) 1871 txM2.Attributes = []transaction.Attribute{ 1872 { 1873 Type: transaction.ConflictsT, 1874 Value: &transaction.Conflicts{ 1875 Hash: txGood.Hash(), 1876 }, 1877 }, 1878 } 1879 txM2.NetworkFee = 1_000_0000 1880 require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) 1881 require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) 1882 e.AddNewBlock(t, txM2) 1883 1884 // txM1 conflicts with txGood and signed by one malicious and one good signers. 1885 txM1 := newTestTx(t, m1.ScriptHash(), testScript) 1886 txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) 1887 txM1.Attributes = []transaction.Attribute{ 1888 { 1889 Type: transaction.ConflictsT, 1890 Value: &transaction.Conflicts{ 1891 Hash: txGood.Hash(), 1892 }, 1893 }, 1894 } 1895 txM1.NetworkFee = 1_000_0000 1896 require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) 1897 require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) 1898 e.AddNewBlock(t, txM1) 1899 1900 // We expect `tx` to fail verification, because one of the on-chained `conflicting` 1901 // transactions has common signers with `tx`, thus, `conflicting` should be 1902 // considered as a valid conflict. 1903 require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) 1904 }) 1905 1906 t.Run("multiple on-chain conflicts signed by [valid + malicious + valid] parties", func(t *testing.T) { 1907 m1 := e.NewAccount(t) 1908 m2 := e.NewAccount(t) 1909 m3 := e.NewAccount(t) 1910 good := e.NewAccount(t) 1911 1912 // txGood doesn't conflict with anyone and signed by good signer. 1913 txGood := newTestTx(t, good.ScriptHash(), testScript) 1914 require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) 1915 1916 // txM1 conflicts with txGood and signed by one malicious and one good signers. 1917 txM1 := newTestTx(t, m1.ScriptHash(), testScript) 1918 txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) 1919 txM1.Attributes = []transaction.Attribute{ 1920 { 1921 Type: transaction.ConflictsT, 1922 Value: &transaction.Conflicts{ 1923 Hash: txGood.Hash(), 1924 }, 1925 }, 1926 } 1927 txM1.NetworkFee = 1_000_0000 1928 require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) 1929 require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) 1930 e.AddNewBlock(t, txM1) 1931 1932 // txM2 conflicts with txGood and signed by two malicious signers. 1933 txM2 := newTestTx(t, m2.ScriptHash(), testScript) 1934 txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) 1935 txM2.Attributes = []transaction.Attribute{ 1936 { 1937 Type: transaction.ConflictsT, 1938 Value: &transaction.Conflicts{ 1939 Hash: txGood.Hash(), 1940 }, 1941 }, 1942 } 1943 txM2.NetworkFee = 1_000_0000 1944 require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) 1945 require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) 1946 e.AddNewBlock(t, txM2) 1947 1948 // txM3 conflicts with txGood and signed by one good and one malicious signers. 1949 txM3 := newTestTx(t, good.ScriptHash(), testScript) 1950 txM3.Signers = append(txM3.Signers, transaction.Signer{Account: m1.ScriptHash()}) 1951 txM3.Attributes = []transaction.Attribute{ 1952 { 1953 Type: transaction.ConflictsT, 1954 Value: &transaction.Conflicts{ 1955 Hash: txGood.Hash(), 1956 }, 1957 }, 1958 } 1959 txM3.NetworkFee = 1_000_0000 1960 require.NoError(t, good.SignTx(netmode.UnitTestNet, txM3)) 1961 require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM3)) 1962 e.AddNewBlock(t, txM3) 1963 1964 // We expect `tx` to fail verification, because one of the on-chained `conflicting` 1965 // transactions has common signers with `tx`, thus, `conflicting` should be 1966 // considered as a valid conflict. 1967 require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) 1968 }) 1969 1970 t.Run("on-chain conflict signed by single valid sender", func(t *testing.T) { 1971 tx := newTestTx(t, h, testScript) 1972 tx.Signers = []transaction.Signer{{Account: validator.ScriptHash()}} 1973 require.NoError(t, validator.SignTx(netmode.UnitTestNet, tx)) 1974 conflicting := transaction.New([]byte{byte(opcode.RET)}, 1000_0000) 1975 conflicting.ValidUntilBlock = bc.BlockHeight() + 1 1976 conflicting.Signers = []transaction.Signer{ 1977 { 1978 Account: validator.ScriptHash(), 1979 Scopes: transaction.CalledByEntry, 1980 }, 1981 } 1982 conflicting.Attributes = []transaction.Attribute{ 1983 { 1984 Type: transaction.ConflictsT, 1985 Value: &transaction.Conflicts{ 1986 Hash: tx.Hash(), 1987 }, 1988 }, 1989 } 1990 conflicting.NetworkFee = 1000_0000 1991 require.NoError(t, validator.SignTx(netmode.UnitTestNet, conflicting)) 1992 e.AddNewBlock(t, conflicting) 1993 // We expect `tx` to fail verification, because on-chained `conflicting` has 1994 // `tx`'s payer as a signer. 1995 require.ErrorIs(t, bc.VerifyTx(tx), core.ErrHasConflicts) 1996 }) 1997 }) 1998 t.Run("attribute on-chain conflict", func(t *testing.T) { 1999 tx := neoValidatorsInvoker.Invoke(t, stackitem.NewBool(true), "transfer", neoOwner, neoOwner, 1, nil) 2000 txConflict := getConflictsTx(e, tx) 2001 require.Error(t, bc.VerifyTx(txConflict)) 2002 }) 2003 t.Run("positive", func(t *testing.T) { 2004 tx := getConflictsTx(e, random.Uint256()) 2005 require.NoError(t, bc.VerifyTx(tx)) 2006 }) 2007 }) 2008 }) 2009 t.Run("NotaryAssisted", func(t *testing.T) { 2010 notary, err := wallet.NewAccount() 2011 require.NoError(t, err) 2012 designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", 2013 int64(noderoles.P2PNotary), []any{notary.PublicKey().Bytes()}) 2014 txSetNotary := transaction.New([]byte{byte(opcode.RET)}, 0) 2015 txSetNotary.Signers = []transaction.Signer{ 2016 { 2017 Account: committee.ScriptHash(), 2018 Scopes: transaction.Global, 2019 }, 2020 } 2021 txSetNotary.Scripts = []transaction.Witness{{ 2022 InvocationScript: e.Committee.SignHashable(uint32(netmode.UnitTestNet), txSetNotary), 2023 VerificationScript: e.Committee.Script(), 2024 }} 2025 2026 getNotaryAssistedTx := func(e *neotest.Executor, signaturesCount uint8, serviceFee int64) *transaction.Transaction { 2027 tx := newTestTx(t, h, testScript) 2028 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{ 2029 NKeys: signaturesCount, 2030 }}) 2031 tx.NetworkFee += serviceFee // additional fee for NotaryAssisted attribute 2032 tx.NetworkFee += 4_000_000 // multisig check 2033 tx.Signers = []transaction.Signer{{ 2034 Account: e.CommitteeHash, 2035 Scopes: transaction.None, 2036 }, 2037 { 2038 Account: notaryHash, 2039 Scopes: transaction.None, 2040 }, 2041 } 2042 rawScript := committee.Script() 2043 size := io.GetVarSize(tx) 2044 netFee, sizeDelta := fee.Calculate(e.Chain.GetBaseExecFee(), rawScript) 2045 tx.NetworkFee += netFee 2046 tx.NetworkFee += int64(size+sizeDelta) * e.Chain.FeePerByte() 2047 tx.Scripts = []transaction.Witness{ 2048 { 2049 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2050 VerificationScript: rawScript, 2051 }, 2052 { 2053 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...), 2054 }, 2055 } 2056 return tx 2057 } 2058 t.Run("Disabled", func(t *testing.T) { 2059 bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 2060 c.P2PSigExtensions = false 2061 c.ReservedAttributes = false 2062 }) 2063 eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 2064 tx := transaction.New(testScript, 1_000_000) 2065 tx.Nonce = neotest.Nonce() 2066 tx.ValidUntilBlock = e.Chain.BlockHeight() + 5 2067 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}) 2068 tx.NetworkFee = 1_0000_0000 2069 eBad.SignTx(t, tx, 1_0000_0000, eBad.Committee) 2070 err := bcBad.VerifyTx(tx) 2071 require.Error(t, err) 2072 require.True(t, strings.Contains(err.Error(), "invalid attribute: NotaryAssisted attribute was found, but P2PSigExtensions are disabled")) 2073 }) 2074 t.Run("Enabled, insufficient network fee", func(t *testing.T) { 2075 tx := getNotaryAssistedTx(e, 1, 0) 2076 require.Error(t, bc.VerifyTx(tx)) 2077 }) 2078 t.Run("Test verify", func(t *testing.T) { 2079 t.Run("no NotaryAssisted attribute", func(t *testing.T) { 2080 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2081 tx.Attributes = []transaction.Attribute{} 2082 tx.Signers = []transaction.Signer{ 2083 { 2084 Account: committee.ScriptHash(), 2085 Scopes: transaction.None, 2086 }, 2087 { 2088 Account: notaryHash, 2089 Scopes: transaction.None, 2090 }, 2091 } 2092 tx.Scripts = []transaction.Witness{ 2093 { 2094 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2095 VerificationScript: committee.Script(), 2096 }, 2097 { 2098 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...), 2099 }, 2100 } 2101 require.Error(t, bc.VerifyTx(tx)) 2102 }) 2103 t.Run("no deposit", func(t *testing.T) { 2104 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2105 tx.Signers = []transaction.Signer{ 2106 { 2107 Account: notaryHash, 2108 Scopes: transaction.None, 2109 }, 2110 { 2111 Account: committee.ScriptHash(), 2112 Scopes: transaction.None, 2113 }, 2114 } 2115 tx.Scripts = []transaction.Witness{ 2116 { 2117 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...), 2118 }, 2119 { 2120 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2121 VerificationScript: committee.Script(), 2122 }, 2123 } 2124 require.Error(t, bc.VerifyTx(tx)) 2125 }) 2126 t.Run("bad Notary signer scope", func(t *testing.T) { 2127 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2128 tx.Signers = []transaction.Signer{ 2129 { 2130 Account: committee.ScriptHash(), 2131 Scopes: transaction.None, 2132 }, 2133 { 2134 Account: notaryHash, 2135 Scopes: transaction.CalledByEntry, 2136 }, 2137 } 2138 tx.Scripts = []transaction.Witness{ 2139 { 2140 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2141 VerificationScript: committee.Script(), 2142 }, 2143 { 2144 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...), 2145 }, 2146 } 2147 require.Error(t, bc.VerifyTx(tx)) 2148 }) 2149 t.Run("not signed by Notary", func(t *testing.T) { 2150 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2151 tx.Signers = []transaction.Signer{ 2152 { 2153 Account: committee.ScriptHash(), 2154 Scopes: transaction.None, 2155 }, 2156 } 2157 tx.Scripts = []transaction.Witness{ 2158 { 2159 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2160 VerificationScript: committee.Script(), 2161 }, 2162 } 2163 require.Error(t, bc.VerifyTx(tx)) 2164 }) 2165 t.Run("bad Notary node witness", func(t *testing.T) { 2166 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2167 tx.Signers = []transaction.Signer{ 2168 { 2169 Account: committee.ScriptHash(), 2170 Scopes: transaction.None, 2171 }, 2172 { 2173 Account: notaryHash, 2174 Scopes: transaction.None, 2175 }, 2176 } 2177 acc, err := keys.NewPrivateKey() 2178 require.NoError(t, err) 2179 tx.Scripts = []transaction.Witness{ 2180 { 2181 InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx), 2182 VerificationScript: committee.Script(), 2183 }, 2184 { 2185 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.SignHashable(uint32(netmode.UnitTestNet), tx)...), 2186 }, 2187 } 2188 require.Error(t, bc.VerifyTx(tx)) 2189 }) 2190 t.Run("missing payer", func(t *testing.T) { 2191 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2192 tx.Signers = []transaction.Signer{ 2193 { 2194 Account: notaryHash, 2195 Scopes: transaction.None, 2196 }, 2197 } 2198 tx.Scripts = []transaction.Witness{ 2199 { 2200 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...), 2201 }, 2202 } 2203 require.Error(t, bc.VerifyTx(tx)) 2204 }) 2205 t.Run("positive", func(t *testing.T) { 2206 tx := getNotaryAssistedTx(e, 1, (1+1)*notaryServiceFeePerKey) 2207 require.NoError(t, bc.VerifyTx(tx)) 2208 }) 2209 }) 2210 }) 2211 }) 2212 t.Run("Partially-filled transaction", func(t *testing.T) { 2213 getPartiallyFilledTx := func(nvb uint32, validUntil uint32) *transaction.Transaction { 2214 tx := newTestTx(t, h, testScript) 2215 tx.ValidUntilBlock = validUntil 2216 tx.Attributes = []transaction.Attribute{ 2217 { 2218 Type: transaction.NotValidBeforeT, 2219 Value: &transaction.NotValidBefore{Height: nvb}, 2220 }, 2221 { 2222 Type: transaction.NotaryAssistedT, 2223 Value: &transaction.NotaryAssisted{NKeys: 0}, 2224 }, 2225 } 2226 tx.Signers = []transaction.Signer{ 2227 { 2228 Account: notaryHash, 2229 Scopes: transaction.None, 2230 }, 2231 { 2232 Account: validator.ScriptHash(), 2233 Scopes: transaction.None, 2234 }, 2235 } 2236 size := io.GetVarSize(tx) 2237 netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), validator.Script()) 2238 tx.NetworkFee = netFee + // multisig witness verification price 2239 int64(size)*bc.FeePerByte() + // fee for unsigned size 2240 int64(sizeDelta)*bc.FeePerByte() + // fee for multisig size 2241 66*bc.FeePerByte() + // fee for Notary signature size (66 bytes for Invocation script and 0 bytes for Verification script) 2242 2*bc.FeePerByte() + // fee for the length of each script in Notary witness (they are nil, so we did not take them into account during `size` calculation) 2243 notaryServiceFeePerKey + // fee for Notary attribute 2244 fee.Opcode(bc.GetBaseExecFee(), // Notary verification script 2245 opcode.PUSHDATA1, opcode.RET, // invocation script 2246 opcode.PUSH0, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call 2247 nativeprices.NotaryVerificationPrice*bc.GetBaseExecFee() // Notary witness verification price 2248 tx.Scripts = []transaction.Witness{ 2249 { 2250 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), 2251 VerificationScript: []byte{}, 2252 }, 2253 { 2254 InvocationScript: validator.SignHashable(uint32(netmode.UnitTestNet), tx), 2255 VerificationScript: validator.Script(), 2256 }, 2257 } 2258 return tx 2259 } 2260 2261 mp := mempool.New(10, 1, false, nil) 2262 verificationF := func(tx *transaction.Transaction, data any) error { 2263 if data.(int) > 5 { 2264 return errors.New("bad data") 2265 } 2266 return nil 2267 } 2268 t.Run("failed pre-verification", func(t *testing.T) { 2269 tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+1) 2270 require.Error(t, bc.PoolTxWithData(tx, 6, mp, bc, verificationF)) // here and below let's use `bc` instead of proper NotaryFeer for the test simplicity. 2271 }) 2272 t.Run("GasLimitExceeded during witness verification", func(t *testing.T) { 2273 tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+1) 2274 tx.NetworkFee-- // to check that NetworkFee was set correctly in getPartiallyFilledTx 2275 tx.Scripts = []transaction.Witness{ 2276 { 2277 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), 2278 VerificationScript: []byte{}, 2279 }, 2280 { 2281 InvocationScript: validator.SignHashable(uint32(netmode.UnitTestNet), tx), 2282 VerificationScript: validator.Script(), 2283 }, 2284 } 2285 require.Error(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF)) 2286 }) 2287 t.Run("bad NVB: too big", func(t *testing.T) { 2288 maxNVB, err := bc.GetMaxNotValidBeforeDelta() 2289 require.NoError(t, err) 2290 tx := getPartiallyFilledTx(bc.BlockHeight()+maxNVB+1, bc.BlockHeight()+1) 2291 require.ErrorIs(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute) 2292 }) 2293 t.Run("bad ValidUntilBlock: too small", func(t *testing.T) { 2294 maxNVB, err := bc.GetMaxNotValidBeforeDelta() 2295 require.NoError(t, err) 2296 tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+maxNVB+1) 2297 require.ErrorIs(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute) 2298 }) 2299 t.Run("good", func(t *testing.T) { 2300 tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+1) 2301 require.NoError(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF)) 2302 }) 2303 }) 2304 } 2305 2306 func TestBlockchain_Bug1728(t *testing.T) { 2307 bc, acc := chain.NewSingle(t) 2308 e := neotest.NewExecutor(t, bc, acc, acc) 2309 managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management)) 2310 2311 src := `package example 2312 import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" 2313 func init() { if true { } else { } } 2314 func _deploy(_ any, isUpdate bool) { 2315 runtime.Log("Deploy") 2316 }` 2317 c := neotest.CompileSource(t, acc.ScriptHash(), strings.NewReader(src), &compiler.Options{Name: "TestContract"}) 2318 managementInvoker.DeployContract(t, c, nil) 2319 } 2320 2321 func TestBlockchain_ResetStateErrors(t *testing.T) { 2322 chainHeight := 3 2323 checkResetErr := func(t *testing.T, cfg func(c *config.Blockchain), h uint32, errText string) { 2324 db, path := newLevelDBForTestingWithPath(t, t.TempDir()) 2325 bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, cfg, db, false) 2326 e := neotest.NewExecutor(t, bc, validators, committee) 2327 go bc.Run() 2328 for i := 0; i < chainHeight; i++ { 2329 e.AddNewBlock(t) // get some height 2330 } 2331 bc.Close() 2332 2333 db, _ = newLevelDBForTestingWithPath(t, path) 2334 defer db.Close() 2335 bc, _, _ = chain.NewMultiWithCustomConfigAndStore(t, cfg, db, false) 2336 err := bc.Reset(h) 2337 if errText != "" { 2338 require.Error(t, err) 2339 require.True(t, strings.Contains(err.Error(), errText), err) 2340 } else { 2341 require.NoError(t, err) 2342 } 2343 } 2344 t.Run("large height", func(t *testing.T) { 2345 checkResetErr(t, nil, uint32(chainHeight+1), "can't reset state to height 4") 2346 }) 2347 t.Run("already at height", func(t *testing.T) { 2348 checkResetErr(t, nil, uint32(chainHeight), "") 2349 }) 2350 t.Run("KeepOnlyLatestState is enabled", func(t *testing.T) { 2351 checkResetErr(t, func(c *config.Blockchain) { 2352 c.Ledger.KeepOnlyLatestState = true 2353 }, uint32(chainHeight-1), "KeepOnlyLatestState is enabled") 2354 }) 2355 t.Run("some blocks where removed", func(t *testing.T) { 2356 checkResetErr(t, func(c *config.Blockchain) { 2357 c.Ledger.RemoveUntraceableBlocks = true 2358 c.MaxTraceableBlocks = 2 2359 }, uint32(chainHeight-3), "RemoveUntraceableBlocks is enabled, a necessary batch of traceable blocks has already been removed") 2360 }) 2361 } 2362 2363 // TestBlockchain_ResetState is based on knowledge about basic chain transactions, 2364 // it performs basic chain reset and checks that reset chain has proper state. 2365 func TestBlockchain_ResetState(t *testing.T) { 2366 // Create the DB. 2367 db, path := newLevelDBForTestingWithPath(t, t.TempDir()) 2368 bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, func(cfg *config.Blockchain) { 2369 cfg.P2PSigExtensions = true 2370 }, db, false) 2371 go bc.Run() 2372 e := neotest.NewExecutor(t, bc, validators, committee) 2373 basicchain.Init(t, "../../", e) 2374 2375 // Gather some reference information. 2376 resetBlockIndex := uint32(15) 2377 staleID := basicchain.NFSOContractID // NEP11 2378 rublesH := e.ContractHash(t, basicchain.RublesContractID) 2379 nnsH := e.ContractHash(t, basicchain.NNSContractID) 2380 staleH := e.ContractHash(t, staleID) 2381 gasH := e.NativeHash(t, nativenames.Gas) 2382 neoH := e.NativeHash(t, nativenames.Neo) 2383 gasID := e.NativeID(t, nativenames.Gas) 2384 neoID := e.NativeID(t, nativenames.Neo) 2385 resetBlockHash := bc.GetHeaderHash(resetBlockIndex) 2386 resetBlockHeader, err := bc.GetHeader(resetBlockHash) 2387 require.NoError(t, err) 2388 topBlockHeight := bc.BlockHeight() 2389 topBH := bc.GetHeaderHash(bc.BlockHeight()) 2390 staleBH := bc.GetHeaderHash(resetBlockIndex + 1) 2391 staleB, err := bc.GetBlock(staleBH) 2392 require.NoError(t, err) 2393 staleTx := staleB.Transactions[0] 2394 _, err = bc.GetAppExecResults(staleTx.Hash(), trigger.Application) 2395 require.NoError(t, err) 2396 sr, err := bc.GetStateModule().GetStateRoot(resetBlockIndex) 2397 require.NoError(t, err) 2398 staleSR, err := bc.GetStateModule().GetStateRoot(resetBlockIndex + 1) 2399 require.NoError(t, err) 2400 rublesKey := []byte("testkey") 2401 rublesStaleKey := []byte("aa") 2402 rublesStaleValue := bc.GetStorageItem(basicchain.RublesContractID, rublesKey) // check value is there 2403 require.Equal(t, []byte(basicchain.RublesNewTestvalue), []byte(rublesStaleValue)) 2404 acc0 := e.Validator.(neotest.MultiSigner).Single(2) // priv0 index->order and order->index conversion 2405 priv0ScriptHash := acc0.ScriptHash() 2406 var ( 2407 expectedNEP11t []*state.NEP11Transfer 2408 expectedNEP17t []*state.NEP17Transfer 2409 ) 2410 require.NoError(t, bc.ForEachNEP11Transfer(priv0ScriptHash, resetBlockHeader.Timestamp, func(t *state.NEP11Transfer) (bool, error) { 2411 if t.Block <= resetBlockIndex { 2412 expectedNEP11t = append(expectedNEP11t, t) 2413 } 2414 return true, nil 2415 })) 2416 require.NoError(t, bc.ForEachNEP17Transfer(priv0ScriptHash, resetBlockHeader.Timestamp, func(t *state.NEP17Transfer) (bool, error) { 2417 if t.Block <= resetBlockIndex { 2418 expectedNEP17t = append(expectedNEP17t, t) 2419 } 2420 return true, nil 2421 })) 2422 2423 // checkProof checks that some stale proof is reachable 2424 checkProof := func() { 2425 rublesStaleFullKey := make([]byte, 4) 2426 binary.LittleEndian.PutUint32(rublesStaleFullKey, uint32(basicchain.RublesContractID)) 2427 rublesStaleFullKey = append(rublesStaleFullKey, rublesStaleKey...) 2428 proof, err := bc.GetStateModule().GetStateProof(staleSR.Root, rublesStaleFullKey) 2429 require.NoError(t, err) 2430 require.NotEmpty(t, proof) 2431 } 2432 checkProof() 2433 2434 // Ensure all changes were persisted. 2435 bc.Close() 2436 2437 // Start new chain with existing DB, but do not run it. 2438 db, _ = newLevelDBForTestingWithPath(t, path) 2439 bc, _, _ = chain.NewMultiWithCustomConfigAndStore(t, func(cfg *config.Blockchain) { 2440 cfg.P2PSigExtensions = true 2441 }, db, false) 2442 defer db.Close() 2443 require.Equal(t, topBlockHeight, bc.BlockHeight()) // ensure DB was properly initialized. 2444 2445 // Reset state. 2446 require.NoError(t, bc.Reset(resetBlockIndex)) 2447 2448 // Check that state was properly reset. 2449 require.Equal(t, resetBlockIndex, bc.BlockHeight()) 2450 require.Equal(t, resetBlockIndex, bc.HeaderHeight()) 2451 require.Equal(t, resetBlockHash, bc.CurrentHeaderHash()) 2452 require.Equal(t, resetBlockHash, bc.CurrentBlockHash()) 2453 require.Equal(t, resetBlockIndex, bc.GetStateModule().CurrentLocalHeight()) 2454 require.Equal(t, sr.Root, bc.GetStateModule().CurrentLocalStateRoot()) 2455 require.Equal(t, uint32(0), bc.GetStateModule().CurrentValidatedHeight()) 2456 2457 // Try to get the latest block\header. 2458 bh := bc.GetHeaderHash(resetBlockIndex) 2459 require.Equal(t, resetBlockHash, bh) 2460 h, err := bc.GetHeader(bh) 2461 require.NoError(t, err) 2462 require.Equal(t, resetBlockHeader, h) 2463 actualRublesHash, err := bc.GetContractScriptHash(basicchain.RublesContractID) 2464 require.NoError(t, err) 2465 require.Equal(t, rublesH, actualRublesHash) 2466 2467 // Check that stale blocks/headers/txs/aers/sr are not reachable. 2468 for i := resetBlockIndex + 1; i <= topBlockHeight; i++ { 2469 hHash := bc.GetHeaderHash(i) 2470 require.Equal(t, util.Uint256{}, hHash) 2471 _, err = bc.GetStateRoot(i) 2472 require.Error(t, err) 2473 } 2474 for _, h := range []util.Uint256{staleBH, topBH} { 2475 _, err = bc.GetHeader(h) 2476 require.Error(t, err) 2477 _, err = bc.GetHeader(h) 2478 require.Error(t, err) 2479 } 2480 _, _, err = bc.GetTransaction(staleTx.Hash()) 2481 require.Error(t, err) 2482 _, err = bc.GetAppExecResults(staleTx.Hash(), trigger.Application) 2483 require.Error(t, err) 2484 2485 // However, proofs and everything related to stale MPT nodes still should work properly, 2486 // because we don't remove stale MPT nodes. 2487 checkProof() 2488 2489 // Check NEP-compatible contracts. 2490 nep11 := bc.GetNEP11Contracts() 2491 require.Equal(t, 1, len(nep11)) // NNS 2492 require.Equal(t, nnsH, nep11[0]) 2493 nep17 := bc.GetNEP17Contracts() 2494 require.Equal(t, 3, len(nep17)) // Neo, Gas, Rubles 2495 require.ElementsMatch(t, []util.Uint160{gasH, neoH, rublesH}, nep17) 2496 2497 // Retrieve stale contract. 2498 cs := bc.GetContractState(staleH) 2499 require.Nil(t, cs) 2500 2501 // Retrieve stale storage item. 2502 rublesValue := bc.GetStorageItem(basicchain.RublesContractID, rublesKey) 2503 require.Equal(t, []byte(basicchain.RublesOldTestvalue), []byte(rublesValue)) // the one with historic state 2504 require.Nil(t, bc.GetStorageItem(basicchain.RublesContractID, rublesStaleKey)) // the one that was added after target reset block 2505 db.Seek(storage.SeekRange{ 2506 Prefix: []byte{byte(storage.STStorage)}, // no items with old prefix 2507 }, func(k, v []byte) bool { 2508 t.Fatal("no stale items must be left in storage") 2509 return false 2510 }) 2511 2512 // Check transfers. 2513 var ( 2514 actualNEP11t []*state.NEP11Transfer 2515 actualNEP17t []*state.NEP17Transfer 2516 ) 2517 require.NoError(t, bc.ForEachNEP11Transfer(priv0ScriptHash, e.TopBlock(t).Timestamp, func(t *state.NEP11Transfer) (bool, error) { 2518 actualNEP11t = append(actualNEP11t, t) 2519 return true, nil 2520 })) 2521 require.NoError(t, bc.ForEachNEP17Transfer(priv0ScriptHash, e.TopBlock(t).Timestamp, func(t *state.NEP17Transfer) (bool, error) { 2522 actualNEP17t = append(actualNEP17t, t) 2523 return true, nil 2524 })) 2525 assert.Equal(t, expectedNEP11t, actualNEP11t) 2526 assert.Equal(t, expectedNEP17t, actualNEP17t) 2527 lub, err := bc.GetTokenLastUpdated(priv0ScriptHash) 2528 require.NoError(t, err) 2529 expectedLUB := map[int32]uint32{ // this information is extracted from basic chain initialization code 2530 basicchain.NNSContractID: resetBlockIndex - 1, // `neo.com` registration 2531 basicchain.RublesContractID: 6, // transfer of 123 RUR to priv1 2532 gasID: resetBlockIndex, // fee for `1.2.3.4` A record registration 2533 neoID: 4, // transfer of 1000 NEO to priv1 2534 } 2535 require.Equal(t, expectedLUB, lub) 2536 } 2537 2538 func TestBlockchain_GenesisTransactionExtension(t *testing.T) { 2539 priv0 := testchain.PrivateKeyByID(0) 2540 acc0 := wallet.NewAccountFromPrivateKey(priv0) 2541 require.NoError(t, acc0.ConvertMultisig(1, []*keys.PublicKey{priv0.PublicKey()})) 2542 from := acc0.ScriptHash() 2543 to := util.Uint160{1, 2, 3} 2544 amount := 1 2545 2546 script := io.NewBufBinWriter() 2547 emit.Bytes(script.BinWriter, from.BytesBE()) 2548 emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness) 2549 emit.Bytes(script.BinWriter, to.BytesBE()) 2550 emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness) 2551 emit.AppCall(script.BinWriter, nativehashes.NeoToken, "transfer", callflag.All, from, to, amount, nil) 2552 emit.Opcodes(script.BinWriter, opcode.ASSERT) 2553 2554 var sysFee int64 = 1_0000_0000 2555 bc, acc := chain.NewSingleWithCustomConfig(t, func(blockchain *config.Blockchain) { 2556 blockchain.Genesis.Transaction = &config.GenesisTransaction{ 2557 Script: script.Bytes(), 2558 SystemFee: sysFee, 2559 } 2560 }) 2561 e := neotest.NewExecutor(t, bc, acc, acc) 2562 b := e.GetBlockByIndex(t, 0) 2563 tx := b.Transactions[0] 2564 e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true), stackitem.NewBool(false)) 2565 e.CheckGASBalance(t, e.Validator.ScriptHash(), big.NewInt(core.DefaultInitialGAS-sysFee)) 2566 actualNeo, lub := e.Chain.GetGoverningTokenBalance(to) 2567 require.Equal(t, int64(amount), actualNeo.Int64()) 2568 require.Equal(t, 0, int(lub)) 2569 } 2570 2571 // TestNativenames ensures that nativenames.All contains all expected native contract names 2572 // in the right order. 2573 func TestNativenames(t *testing.T) { 2574 bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) { 2575 cfg.Hardforks = map[string]uint32{} 2576 cfg.P2PSigExtensions = true 2577 }) 2578 natives := bc.GetNatives() 2579 require.Equal(t, len(natives), len(nativenames.All)) 2580 for i, cs := range natives { 2581 require.Equal(t, cs.Manifest.Name, nativenames.All[i], i) 2582 } 2583 } 2584 2585 // TestBlockchain_StoreAsTransaction_ExecutableConflict ensures that transaction conflicting with 2586 // some on-chain block can be properly stored and doesn't break the database. 2587 func TestBlockchain_StoreAsTransaction_ExecutableConflict(t *testing.T) { 2588 bc, acc := chain.NewSingleWithCustomConfig(t, nil) 2589 e := neotest.NewExecutor(t, bc, acc, acc) 2590 genesisH := bc.GetHeaderHash(0) 2591 currHeight := bc.BlockHeight() 2592 2593 // Ensure AER can be retrieved for genesis block. 2594 aer, err := bc.GetAppExecResults(genesisH, trigger.All) 2595 require.NoError(t, err) 2596 require.Equal(t, 2, len(aer)) 2597 2598 tx := transaction.New([]byte{byte(opcode.PUSHT)}, 0) 2599 tx.Nonce = 5 2600 tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 2601 tx.Attributes = []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: genesisH}}} 2602 e.SignTx(t, tx, -1, acc) 2603 e.AddNewBlock(t, tx) 2604 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) 2605 2606 // Ensure original tx can be retrieved. 2607 actual, actualHeight, err := bc.GetTransaction(tx.Hash()) 2608 require.NoError(t, err) 2609 require.Equal(t, currHeight+1, actualHeight) 2610 require.Equal(t, tx, actual, tx) 2611 2612 // Ensure conflict stub is not stored. This check doesn't give us 100% sure that 2613 // there's no specific conflict record since GetTransaction doesn't return conflict records, 2614 // but at least it allows to ensure that no transaction record is present. 2615 _, _, err = bc.GetTransaction(genesisH) 2616 require.ErrorIs(t, err, storage.ErrKeyNotFound) 2617 2618 // Ensure AER still can be retrieved for genesis block. 2619 aer, err = bc.GetAppExecResults(genesisH, trigger.All) 2620 require.NoError(t, err) 2621 require.Equal(t, 2, len(aer)) 2622 }