mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 13:41:37 +00:00
Merge pull request #2814 from nspcc-dev/startup-time
Optimize startup time and memory usage
This commit is contained in:
commit
3ef66239a8
26 changed files with 328 additions and 252 deletions
|
@ -63,7 +63,7 @@ func TestQueryTx(t *testing.T) {
|
||||||
|
|
||||||
_, height, err := e.Chain.GetTransaction(txHash)
|
_, height, err := e.Chain.GetTransaction(txHash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
|
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
|
||||||
e.CheckNextLine(t, `Success:\s+true`)
|
e.CheckNextLine(t, `Success:\s+true`)
|
||||||
e.CheckEOF(t)
|
e.CheckEOF(t)
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ func compareQueryTxVerbose(t *testing.T, e *testcli.Executor, tx *transaction.Tr
|
||||||
e.CheckNextLine(t, `OnChain:\s+true`)
|
e.CheckNextLine(t, `OnChain:\s+true`)
|
||||||
_, height, err := e.Chain.GetTransaction(tx.Hash())
|
_, height, err := e.Chain.GetTransaction(tx.Hash())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
|
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
|
||||||
|
|
||||||
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
e.CheckNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vmstate.Halt))
|
e.CheckNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vmstate.Halt))
|
||||||
|
|
|
@ -2,7 +2,6 @@ package fakechain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
@ -236,11 +235,8 @@ func (chain *FakeChain) GetNativeContractScriptHash(name string) (util.Uint160,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeaderHash implements the Blockchainer interface.
|
// GetHeaderHash implements the Blockchainer interface.
|
||||||
func (chain *FakeChain) GetHeaderHash(n int) util.Uint256 {
|
func (chain *FakeChain) GetHeaderHash(n uint32) util.Uint256 {
|
||||||
if n < 0 || n > math.MaxUint32 {
|
return chain.hdrHashes[n]
|
||||||
return util.Uint256{}
|
|
||||||
}
|
|
||||||
return chain.hdrHashes[uint32(n)]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeader implements the Blockchainer interface.
|
// GetHeader implements the Blockchainer interface.
|
||||||
|
|
|
@ -158,7 +158,7 @@ func SignCommittee(h hash.Hashable) []byte {
|
||||||
func NewBlock(t *testing.T, bc Ledger, offset uint32, primary uint32, txs ...*transaction.Transaction) *block.Block {
|
func NewBlock(t *testing.T, bc Ledger, offset uint32, primary uint32, txs ...*transaction.Transaction) *block.Block {
|
||||||
witness := transaction.Witness{VerificationScript: MultisigVerificationScript()}
|
witness := transaction.Witness{VerificationScript: MultisigVerificationScript()}
|
||||||
height := bc.BlockHeight()
|
height := bc.BlockHeight()
|
||||||
h := bc.GetHeaderHash(int(height))
|
h := bc.GetHeaderHash(height)
|
||||||
hdr, err := bc.GetHeader(h)
|
hdr, err := bc.GetHeader(h)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
b := &block.Block{
|
b := &block.Block{
|
||||||
|
|
|
@ -27,7 +27,7 @@ type Ledger interface {
|
||||||
FeePerByte() int64
|
FeePerByte() int64
|
||||||
GetBaseExecFee() int64
|
GetBaseExecFee() int64
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
ManagementContractHash() util.Uint160
|
ManagementContractHash() util.Uint160
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ func TestService_NextConsensus(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
checkNextConsensus := func(t *testing.T, bc *core.Blockchain, height uint32, h util.Uint160) {
|
checkNextConsensus := func(t *testing.T, bc *core.Blockchain, height uint32, h util.Uint160) {
|
||||||
hdrHash := bc.GetHeaderHash(int(height))
|
hdrHash := bc.GetHeaderHash(height)
|
||||||
hdr, err := bc.GetHeader(hdrHash)
|
hdr, err := bc.GetHeader(hdrHash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, h, hdr.NextConsensus)
|
require.Equal(t, h, hdr.NextConsensus)
|
||||||
|
|
|
@ -109,10 +109,10 @@ func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBloc
|
||||||
e.CheckHalt(t, tx.Hash())
|
e.CheckHalt(t, tx.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
newestB, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()) - startFromBlock + 1))
|
newestB, err := bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight() - uint32(startFromBlock) + 1))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
newestTimestamp := newestB.Timestamp
|
newestTimestamp := newestB.Timestamp
|
||||||
oldestB, err := bc.GetBlock(bc.GetHeaderHash(int(newestB.Index) - nBlocksToTake))
|
oldestB, err := bc.GetBlock(bc.GetHeaderHash(newestB.Index - uint32(nBlocksToTake)))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
oldestTimestamp := oldestB.Timestamp
|
oldestTimestamp := oldestB.Timestamp
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,6 @@ import (
|
||||||
|
|
||||||
// Tuning parameters.
|
// Tuning parameters.
|
||||||
const (
|
const (
|
||||||
headerBatchCount = 2000
|
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
|
||||||
defaultInitialGAS = 52000000_00000000
|
defaultInitialGAS = 52000000_00000000
|
||||||
|
@ -115,6 +114,8 @@ var (
|
||||||
// the state of the ledger that can be accessed in various ways and changed by
|
// the state of the ledger that can be accessed in various ways and changed by
|
||||||
// adding new blocks or headers.
|
// adding new blocks or headers.
|
||||||
type Blockchain struct {
|
type Blockchain struct {
|
||||||
|
HeaderHashes
|
||||||
|
|
||||||
config config.ProtocolConfiguration
|
config config.ProtocolConfiguration
|
||||||
|
|
||||||
// The only way chain state changes is by adding blocks, so we can't
|
// The only way chain state changes is by adding blocks, so we can't
|
||||||
|
@ -151,13 +152,6 @@ type Blockchain struct {
|
||||||
// Current persisted block count.
|
// Current persisted block count.
|
||||||
persistedHeight uint32
|
persistedHeight uint32
|
||||||
|
|
||||||
// Number of headers stored in the chain file.
|
|
||||||
storedHeaderCount uint32
|
|
||||||
|
|
||||||
// Header hashes list with associated lock.
|
|
||||||
headerHashesLock sync.RWMutex
|
|
||||||
headerHashes []util.Uint256
|
|
||||||
|
|
||||||
// Stop synchronization mechanisms.
|
// Stop synchronization mechanisms.
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
runToExitCh chan struct{}
|
runToExitCh chan struct{}
|
||||||
|
@ -380,8 +374,7 @@ func (bc *Blockchain) init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bc.headerHashes = []util.Uint256{genesisBlock.Hash()}
|
bc.HeaderHashes.initGenesis(bc.dao, genesisBlock.Hash())
|
||||||
bc.dao.PutCurrentHeader(genesisBlock.Hash(), genesisBlock.Index)
|
|
||||||
if err := bc.stateRoot.Init(0); err != nil {
|
if err := bc.stateRoot.Init(0); err != nil {
|
||||||
return fmt.Errorf("can't init MPT: %w", err)
|
return fmt.Errorf("can't init MPT: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -414,53 +407,11 @@ func (bc *Blockchain) init() error {
|
||||||
// and the genesis block as first block.
|
// and the genesis block as first block.
|
||||||
bc.log.Info("restoring blockchain", zap.String("version", version))
|
bc.log.Info("restoring blockchain", zap.String("version", version))
|
||||||
|
|
||||||
bc.headerHashes, err = bc.dao.GetHeaderHashes()
|
err = bc.HeaderHashes.init(bc.dao)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.storedHeaderCount = uint32(len(bc.headerHashes))
|
|
||||||
|
|
||||||
currHeaderHeight, currHeaderHash, err := bc.dao.GetCurrentHeaderHeight()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve current header info: %w", err)
|
|
||||||
}
|
|
||||||
if bc.storedHeaderCount == 0 && currHeaderHeight == 0 {
|
|
||||||
bc.headerHashes = append(bc.headerHashes, currHeaderHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a high chance that the Node is stopped before the next
|
|
||||||
// batch of 2000 headers was stored. Via the currentHeaders stored we can sync
|
|
||||||
// that with stored blocks.
|
|
||||||
if currHeaderHeight >= bc.storedHeaderCount {
|
|
||||||
hash := currHeaderHash
|
|
||||||
var targetHash util.Uint256
|
|
||||||
if len(bc.headerHashes) > 0 {
|
|
||||||
targetHash = bc.headerHashes[len(bc.headerHashes)-1]
|
|
||||||
} else {
|
|
||||||
genesisBlock, err := CreateGenesisBlock(bc.config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
targetHash = genesisBlock.Hash()
|
|
||||||
bc.headerHashes = append(bc.headerHashes, targetHash)
|
|
||||||
}
|
|
||||||
headers := make([]*block.Header, 0)
|
|
||||||
|
|
||||||
for hash != targetHash {
|
|
||||||
header, err := bc.GetHeader(hash)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not get header %s: %w", hash, err)
|
|
||||||
}
|
|
||||||
headers = append(headers, header)
|
|
||||||
hash = header.PrevHash
|
|
||||||
}
|
|
||||||
headerSliceReverse(headers)
|
|
||||||
for _, h := range headers {
|
|
||||||
bc.headerHashes = append(bc.headerHashes, h.Hash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether StateChangeState stage is in the storage and continue interrupted state jump / state reset if so.
|
// Check whether StateChangeState stage is in the storage and continue interrupted state jump / state reset if so.
|
||||||
stateChStage, err := bc.dao.Store.Get([]byte{byte(storage.SYSStateChangeStage)})
|
stateChStage, err := bc.dao.Store.Get([]byte{byte(storage.SYSStateChangeStage)})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -551,8 +502,8 @@ func (bc *Blockchain) jumpToState(p uint32) error {
|
||||||
// jump stage. All the data needed for the jump must be in the DB, otherwise an
|
// jump stage. All the data needed for the jump must be in the DB, otherwise an
|
||||||
// error is returned. It is not protected by mutex.
|
// error is returned. It is not protected by mutex.
|
||||||
func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) error {
|
func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) error {
|
||||||
if p+1 >= uint32(len(bc.headerHashes)) {
|
if p >= bc.HeaderHeight() {
|
||||||
return fmt.Errorf("invalid state sync point %d: headerHeignt is %d", p, len(bc.headerHashes))
|
return fmt.Errorf("invalid state sync point %d: headerHeignt is %d", p, bc.HeaderHeight())
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.log.Info("jumping to state sync point", zap.Uint32("state sync point", p))
|
bc.log.Info("jumping to state sync point", zap.Uint32("state sync point", p))
|
||||||
|
@ -587,7 +538,7 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
|
||||||
// After current state is updated, we need to remove outdated state-related data if so.
|
// After current state is updated, we need to remove outdated state-related data if so.
|
||||||
// The only outdated data we might have is genesis-related data, so check it.
|
// The only outdated data we might have is genesis-related data, so check it.
|
||||||
if p-bc.config.MaxTraceableBlocks > 0 {
|
if p-bc.config.MaxTraceableBlocks > 0 {
|
||||||
err := cache.DeleteBlock(bc.headerHashes[0])
|
err := cache.DeleteBlock(bc.GetHeaderHash(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to remove outdated state data for the genesis block: %w", err)
|
return fmt.Errorf("failed to remove outdated state data for the genesis block: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -600,7 +551,7 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update SYS-prefixed info.
|
// Update SYS-prefixed info.
|
||||||
block, err := bc.dao.GetBlock(bc.headerHashes[p])
|
block, err := bc.dao.GetBlock(bc.GetHeaderHash(p))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get current block: %w", err)
|
return fmt.Errorf("failed to get current block: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -616,7 +567,7 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown state jump stage: %d", stage)
|
return fmt.Errorf("unknown state jump stage: %d", stage)
|
||||||
}
|
}
|
||||||
block, err := bc.dao.GetBlock(bc.headerHashes[p+1])
|
block, err := bc.dao.GetBlock(bc.GetHeaderHash(p + 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get block to init MPT: %w", err)
|
return fmt.Errorf("failed to get block to init MPT: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -637,10 +588,12 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
|
||||||
// resetRAMState resets in-memory cached info.
|
// resetRAMState resets in-memory cached info.
|
||||||
func (bc *Blockchain) resetRAMState(height uint32, resetHeaders bool) error {
|
func (bc *Blockchain) resetRAMState(height uint32, resetHeaders bool) error {
|
||||||
if resetHeaders {
|
if resetHeaders {
|
||||||
bc.headerHashes = bc.headerHashes[:height+1]
|
err := bc.HeaderHashes.init(bc.dao)
|
||||||
bc.storedHeaderCount = height + 1
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
block, err := bc.dao.GetBlock(bc.headerHashes[height])
|
}
|
||||||
|
block, err := bc.dao.GetBlock(bc.GetHeaderHash(height))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get current block: %w", err)
|
return fmt.Errorf("failed to get current block: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -697,7 +650,7 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve necessary state before the DB modification.
|
// Retrieve necessary state before the DB modification.
|
||||||
b, err := bc.GetBlock(bc.headerHashes[height])
|
b, err := bc.GetBlock(bc.GetHeaderHash(height))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve block %d: %w", height, err)
|
return fmt.Errorf("failed to retrieve block %d: %w", height, err)
|
||||||
}
|
}
|
||||||
|
@ -733,7 +686,7 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage)
|
||||||
blocksCnt, batchCnt, keysCnt int
|
blocksCnt, batchCnt, keysCnt int
|
||||||
)
|
)
|
||||||
for i := height + 1; i <= currHeight; i++ {
|
for i := height + 1; i <= currHeight; i++ {
|
||||||
err := cache.DeleteBlock(bc.GetHeaderHash(int(i)))
|
err := cache.DeleteBlock(bc.GetHeaderHash(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while removing block %d: %w", i, err)
|
return fmt.Errorf("error while removing block %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
@ -861,7 +814,7 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage)
|
||||||
// Reset SYS-prefixed and IX-prefixed information.
|
// Reset SYS-prefixed and IX-prefixed information.
|
||||||
bc.log.Info("trying to reset headers information")
|
bc.log.Info("trying to reset headers information")
|
||||||
for i := height + 1; i <= hHeight; i++ {
|
for i := height + 1; i <= hHeight; i++ {
|
||||||
cache.PurgeHeader(bc.GetHeaderHash(int(i)))
|
cache.PurgeHeader(bc.GetHeaderHash(i))
|
||||||
}
|
}
|
||||||
cache.DeleteHeaderHashes(height+1, headerBatchCount)
|
cache.DeleteHeaderHashes(height+1, headerBatchCount)
|
||||||
cache.StoreAsCurrentBlock(b)
|
cache.StoreAsCurrentBlock(b)
|
||||||
|
@ -1186,7 +1139,7 @@ func appendTokenTransferInfo(transferData *state.TokenTransferInfo,
|
||||||
func (bc *Blockchain) removeOldTransfers(index uint32) time.Duration {
|
func (bc *Blockchain) removeOldTransfers(index uint32) time.Duration {
|
||||||
bc.log.Info("starting transfer data garbage collection", zap.Uint32("index", index))
|
bc.log.Info("starting transfer data garbage collection", zap.Uint32("index", index))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
h, err := bc.GetHeader(bc.GetHeaderHash(int(index)))
|
h, err := bc.GetHeader(bc.GetHeaderHash(index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dur := time.Since(start)
|
dur := time.Since(start)
|
||||||
bc.log.Error("failed to find block header for transfer GC", zap.Duration("time", dur), zap.Error(err))
|
bc.log.Error("failed to find block header for transfer GC", zap.Duration("time", dur), zap.Error(err))
|
||||||
|
@ -1418,7 +1371,6 @@ func (bc *Blockchain) AddHeaders(headers ...*block.Header) error {
|
||||||
func (bc *Blockchain) addHeaders(verify bool, headers ...*block.Header) error {
|
func (bc *Blockchain) addHeaders(verify bool, headers ...*block.Header) error {
|
||||||
var (
|
var (
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
batch = bc.dao.GetPrivate()
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1448,44 +1400,14 @@ func (bc *Blockchain) addHeaders(verify bool, headers ...*block.Header) error {
|
||||||
lastHeader = h
|
lastHeader = h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res := bc.HeaderHashes.addHeaders(headers...)
|
||||||
bc.headerHashesLock.Lock()
|
if res == nil {
|
||||||
defer bc.headerHashesLock.Unlock()
|
|
||||||
oldlen := len(bc.headerHashes)
|
|
||||||
var lastHeader *block.Header
|
|
||||||
for _, h := range headers {
|
|
||||||
if int(h.Index) != len(bc.headerHashes) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = batch.StoreHeader(h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bc.headerHashes = append(bc.headerHashes, h.Hash())
|
|
||||||
lastHeader = h
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldlen != len(bc.headerHashes) {
|
|
||||||
for int(lastHeader.Index)-headerBatchCount >= int(bc.storedHeaderCount) {
|
|
||||||
err = batch.StoreHeaderHashes(bc.headerHashes[bc.storedHeaderCount:bc.storedHeaderCount+headerBatchCount],
|
|
||||||
bc.storedHeaderCount)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bc.storedHeaderCount += headerBatchCount
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.PutCurrentHeader(lastHeader.Hash(), lastHeader.Index)
|
|
||||||
updateHeaderHeightMetric(len(bc.headerHashes) - 1)
|
|
||||||
if _, err = batch.Persist(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bc.log.Debug("done processing headers",
|
bc.log.Debug("done processing headers",
|
||||||
zap.Int("headerIndex", len(bc.headerHashes)-1),
|
zap.Uint32("headerIndex", bc.HeaderHeight()),
|
||||||
zap.Uint32("blockHeight", bc.BlockHeight()),
|
zap.Uint32("blockHeight", bc.BlockHeight()),
|
||||||
zap.Duration("took", time.Since(start)))
|
zap.Duration("took", time.Since(start)))
|
||||||
}
|
}
|
||||||
return nil
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStateRoot returns state root for the given height.
|
// GetStateRoot returns state root for the given height.
|
||||||
|
@ -1540,7 +1462,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
stop = start + 1
|
stop = start + 1
|
||||||
}
|
}
|
||||||
for index := start; index < stop; index++ {
|
for index := start; index < stop; index++ {
|
||||||
err := kvcache.DeleteBlock(bc.headerHashes[index])
|
err := kvcache.DeleteBlock(bc.GetHeaderHash(index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.log.Warn("error while removing old block",
|
bc.log.Warn("error while removing old block",
|
||||||
zap.Uint32("index", index),
|
zap.Uint32("index", index),
|
||||||
|
@ -1662,7 +1584,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
return fmt.Errorf("error while trying to apply MPT changes: %w", err)
|
return fmt.Errorf("error while trying to apply MPT changes: %w", err)
|
||||||
}
|
}
|
||||||
if bc.config.StateRootInHeader && bc.HeaderHeight() > sr.Index {
|
if bc.config.StateRootInHeader && bc.HeaderHeight() > sr.Index {
|
||||||
h, err := bc.GetHeader(bc.GetHeaderHash(int(sr.Index) + 1))
|
h, err := bc.GetHeader(bc.GetHeaderHash(sr.Index + 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to get next header: %w", err)
|
err = fmt.Errorf("failed to get next header: %w", err)
|
||||||
} else if h.PrevStateRoot != sr.Root {
|
} else if h.PrevStateRoot != sr.Root {
|
||||||
|
@ -2163,15 +2085,9 @@ func (bc *Blockchain) HasTransaction(hash util.Uint256) bool {
|
||||||
// HasBlock returns true if the blockchain contains the given
|
// HasBlock returns true if the blockchain contains the given
|
||||||
// block hash.
|
// block hash.
|
||||||
func (bc *Blockchain) HasBlock(hash util.Uint256) bool {
|
func (bc *Blockchain) HasBlock(hash util.Uint256) bool {
|
||||||
var height = bc.BlockHeight()
|
if bc.HeaderHashes.haveRecentHash(hash, bc.BlockHeight()) {
|
||||||
bc.headerHashesLock.RLock()
|
|
||||||
for i := int(height); i >= int(height)-4 && i >= 0; i-- {
|
|
||||||
if hash.Equals(bc.headerHashes[i]) {
|
|
||||||
bc.headerHashesLock.RUnlock()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
bc.headerHashesLock.RUnlock()
|
|
||||||
|
|
||||||
if header, err := bc.GetHeader(hash); err == nil {
|
if header, err := bc.GetHeader(hash); err == nil {
|
||||||
return header.Index <= bc.BlockHeight()
|
return header.Index <= bc.BlockHeight()
|
||||||
|
@ -2186,28 +2102,7 @@ func (bc *Blockchain) CurrentBlockHash() util.Uint256 {
|
||||||
tb := topBlock.(*block.Block)
|
tb := topBlock.(*block.Block)
|
||||||
return tb.Hash()
|
return tb.Hash()
|
||||||
}
|
}
|
||||||
return bc.GetHeaderHash(int(bc.BlockHeight()))
|
return bc.GetHeaderHash(bc.BlockHeight())
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentHeaderHash returns the hash of the latest known header.
|
|
||||||
func (bc *Blockchain) CurrentHeaderHash() util.Uint256 {
|
|
||||||
bc.headerHashesLock.RLock()
|
|
||||||
hash := bc.headerHashes[len(bc.headerHashes)-1]
|
|
||||||
bc.headerHashesLock.RUnlock()
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHeaderHash returns hash of the header/block with specified index, if
|
|
||||||
// Blockchain doesn't have a hash for this height, zero Uint256 value is returned.
|
|
||||||
func (bc *Blockchain) GetHeaderHash(i int) util.Uint256 {
|
|
||||||
bc.headerHashesLock.RLock()
|
|
||||||
defer bc.headerHashesLock.RUnlock()
|
|
||||||
|
|
||||||
hashesLen := len(bc.headerHashes)
|
|
||||||
if hashesLen <= i {
|
|
||||||
return util.Uint256{}
|
|
||||||
}
|
|
||||||
return bc.headerHashes[i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockHeight returns the height/index of the highest block.
|
// BlockHeight returns the height/index of the highest block.
|
||||||
|
@ -2215,14 +2110,6 @@ func (bc *Blockchain) BlockHeight() uint32 {
|
||||||
return atomic.LoadUint32(&bc.blockHeight)
|
return atomic.LoadUint32(&bc.blockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeaderHeight returns the index/height of the highest header.
|
|
||||||
func (bc *Blockchain) HeaderHeight() uint32 {
|
|
||||||
bc.headerHashesLock.RLock()
|
|
||||||
n := len(bc.headerHashes)
|
|
||||||
bc.headerHashesLock.RUnlock()
|
|
||||||
return uint32(n - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContractState returns contract by its script hash.
|
// GetContractState returns contract by its script hash.
|
||||||
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
||||||
contract, err := bc.contracts.Management.GetContract(bc.dao, hash)
|
contract, err := bc.contracts.Management.GetContract(bc.dao, hash)
|
||||||
|
@ -2759,7 +2646,7 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact
|
||||||
func (bc *Blockchain) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) {
|
func (bc *Blockchain) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) {
|
||||||
b := block.New(bc.config.StateRootInHeader)
|
b := block.New(bc.config.StateRootInHeader)
|
||||||
b.Index = nextBlockHeight
|
b.Index = nextBlockHeight
|
||||||
hdr, err := bc.GetHeader(bc.GetHeaderHash(int(nextBlockHeight - 1)))
|
hdr, err := bc.GetHeader(bc.GetHeaderHash(nextBlockHeight - 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,7 +225,7 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) {
|
||||||
t.Run("invalid state sync point", func(t *testing.T) {
|
t.Run("invalid state sync point", func(t *testing.T) {
|
||||||
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)})
|
bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)})
|
||||||
point := make([]byte, 4)
|
point := make([]byte, 4)
|
||||||
binary.LittleEndian.PutUint32(point, uint32(len(bcSpout.headerHashes)))
|
binary.LittleEndian.PutUint32(point, bcSpout.lastHeaderIndex()+1)
|
||||||
bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point)
|
bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point)
|
||||||
checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state sync point")
|
checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state sync point")
|
||||||
})
|
})
|
||||||
|
@ -304,7 +304,7 @@ func TestChainWithVolatileNumOfValidators(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
curWit = nextWit
|
curWit = nextWit
|
||||||
b.PrevHash = bc.GetHeaderHash(i - 1)
|
b.PrevHash = bc.GetHeaderHash(uint32(i) - 1)
|
||||||
b.Timestamp = uint64(time.Now().UTC().Unix())*1000 + uint64(i)
|
b.Timestamp = uint64(time.Now().UTC().Unix())*1000 + uint64(i)
|
||||||
b.Index = uint32(i)
|
b.Index = uint32(i)
|
||||||
b.RebuildMerkleRoot()
|
b.RebuildMerkleRoot()
|
||||||
|
|
|
@ -146,14 +146,15 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
|
||||||
|
|
||||||
// Corrupt headers hashes batch.
|
// Corrupt headers hashes batch.
|
||||||
cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption.
|
cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption.
|
||||||
key := make([]byte, 5)
|
// Make the chain think we're at 2000+ which will trigger page 0 read.
|
||||||
key[0] = byte(storage.IXHeaderHashList)
|
buf := io.NewBufBinWriter()
|
||||||
binary.BigEndian.PutUint32(key[1:], 1)
|
buf.WriteBytes(util.Uint256{}.BytesLE())
|
||||||
cache.Put(key, []byte{1, 2, 3})
|
buf.WriteU32LE(2000)
|
||||||
|
cache.Put([]byte{byte(storage.SYSCurrentHeader)}, buf.Bytes())
|
||||||
|
|
||||||
_, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
_, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.True(t, strings.Contains(err.Error(), "failed to read batch of 2000"), err)
|
require.True(t, strings.Contains(err.Error(), "failed to retrieve header hash page"), err)
|
||||||
})
|
})
|
||||||
t.Run("corrupted current header height", func(t *testing.T) {
|
t.Run("corrupted current header height", func(t *testing.T) {
|
||||||
ps = newPS(t)
|
ps = newPS(t)
|
||||||
|
@ -1970,12 +1971,12 @@ func TestBlockchain_ResetState(t *testing.T) {
|
||||||
neoH := e.NativeHash(t, nativenames.Neo)
|
neoH := e.NativeHash(t, nativenames.Neo)
|
||||||
gasID := e.NativeID(t, nativenames.Gas)
|
gasID := e.NativeID(t, nativenames.Gas)
|
||||||
neoID := e.NativeID(t, nativenames.Neo)
|
neoID := e.NativeID(t, nativenames.Neo)
|
||||||
resetBlockHash := bc.GetHeaderHash(int(resetBlockIndex))
|
resetBlockHash := bc.GetHeaderHash(resetBlockIndex)
|
||||||
resetBlockHeader, err := bc.GetHeader(resetBlockHash)
|
resetBlockHeader, err := bc.GetHeader(resetBlockHash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
topBlockHeight := bc.BlockHeight()
|
topBlockHeight := bc.BlockHeight()
|
||||||
topBH := bc.GetHeaderHash(int(bc.BlockHeight()))
|
topBH := bc.GetHeaderHash(bc.BlockHeight())
|
||||||
staleBH := bc.GetHeaderHash(int(resetBlockIndex + 1))
|
staleBH := bc.GetHeaderHash(resetBlockIndex + 1)
|
||||||
staleB, err := bc.GetBlock(staleBH)
|
staleB, err := bc.GetBlock(staleBH)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
staleTx := staleB.Transactions[0]
|
staleTx := staleB.Transactions[0]
|
||||||
|
@ -2043,7 +2044,7 @@ func TestBlockchain_ResetState(t *testing.T) {
|
||||||
require.Equal(t, uint32(0), bc.GetStateModule().CurrentValidatedHeight())
|
require.Equal(t, uint32(0), bc.GetStateModule().CurrentValidatedHeight())
|
||||||
|
|
||||||
// Try to get the latest block\header.
|
// Try to get the latest block\header.
|
||||||
bh := bc.GetHeaderHash(int(resetBlockIndex))
|
bh := bc.GetHeaderHash(resetBlockIndex)
|
||||||
require.Equal(t, resetBlockHash, bh)
|
require.Equal(t, resetBlockHash, bh)
|
||||||
h, err := bc.GetHeader(bh)
|
h, err := bc.GetHeader(bh)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -2054,7 +2055,7 @@ func TestBlockchain_ResetState(t *testing.T) {
|
||||||
|
|
||||||
// Check that stale blocks/headers/txs/aers/sr are not reachable.
|
// Check that stale blocks/headers/txs/aers/sr are not reachable.
|
||||||
for i := resetBlockIndex + 1; i <= topBlockHeight; i++ {
|
for i := resetBlockIndex + 1; i <= topBlockHeight; i++ {
|
||||||
hHash := bc.GetHeaderHash(int(i))
|
hHash := bc.GetHeaderHash(i)
|
||||||
require.Equal(t, util.Uint256{}, hHash)
|
require.Equal(t, util.Uint256{}, hHash)
|
||||||
_, err = bc.GetStateRoot(i)
|
_, err = bc.GetStateRoot(i)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -14,14 +14,14 @@ type DumperRestorer interface {
|
||||||
AddBlock(block *block.Block) error
|
AddBlock(block *block.Block) error
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump writes count blocks from start to the provided writer.
|
// Dump writes count blocks from start to the provided writer.
|
||||||
// Note: header needs to be written separately by a client.
|
// Note: header needs to be written separately by a client.
|
||||||
func Dump(bc DumperRestorer, w *io.BinWriter, start, count uint32) error {
|
func Dump(bc DumperRestorer, w *io.BinWriter, start, count uint32) error {
|
||||||
for i := start; i < start+count; i++ {
|
for i := start; i < start+count; i++ {
|
||||||
bh := bc.GetHeaderHash(int(i))
|
bh := bc.GetHeaderHash(i)
|
||||||
b, err := bc.GetBlock(bh)
|
b, err := bc.GetBlock(bh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dao
|
package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -582,25 +581,23 @@ func (dao *Simple) GetStateSyncCurrentBlockHeight() (uint32, error) {
|
||||||
return binary.LittleEndian.Uint32(b), nil
|
return binary.LittleEndian.Uint32(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeaderHashes returns a sorted list of header hashes retrieved from
|
// GetHeaderHashes returns a page of header hashes retrieved from
|
||||||
// the given underlying store.
|
// the given underlying store.
|
||||||
func (dao *Simple) GetHeaderHashes() ([]util.Uint256, error) {
|
func (dao *Simple) GetHeaderHashes(height uint32) ([]util.Uint256, error) {
|
||||||
var hashes = make([]util.Uint256, 0)
|
var hashes []util.Uint256
|
||||||
|
|
||||||
var seekErr error
|
key := dao.mkHeaderHashKey(height)
|
||||||
dao.Store.Seek(storage.SeekRange{
|
b, err := dao.Store.Get(key)
|
||||||
Prefix: dao.mkKeyPrefix(storage.IXHeaderHashList),
|
|
||||||
}, func(k, v []byte) bool {
|
|
||||||
newHashes, err := read2000Uint256Hashes(v)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
seekErr = fmt.Errorf("failed to read batch of 2000 header hashes: %w", err)
|
return nil, err
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
hashes = append(hashes, newHashes...)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return hashes, seekErr
|
br := io.NewBinReaderFromBuf(b)
|
||||||
|
br.ReadArray(&hashes)
|
||||||
|
if br.Err != nil {
|
||||||
|
return nil, br.Err
|
||||||
|
}
|
||||||
|
return hashes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHeaderHashes removes batches of header hashes starting from the one that
|
// DeleteHeaderHashes removes batches of header hashes starting from the one that
|
||||||
|
@ -683,19 +680,6 @@ func (dao *Simple) PutStateSyncCurrentBlockHeight(h uint32) {
|
||||||
dao.Store.Put(dao.mkKeyPrefix(storage.SYSStateSyncCurrentBlockHeight), buf.Bytes())
|
dao.Store.Put(dao.mkKeyPrefix(storage.SYSStateSyncCurrentBlockHeight), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// read2000Uint256Hashes attempts to read 2000 Uint256 hashes from
|
|
||||||
// the given byte array.
|
|
||||||
func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) {
|
|
||||||
r := bytes.NewReader(b)
|
|
||||||
br := io.NewBinReaderFromIO(r)
|
|
||||||
hashes := make([]util.Uint256, 0)
|
|
||||||
br.ReadArray(&hashes)
|
|
||||||
if br.Err != nil {
|
|
||||||
return nil, br.Err
|
|
||||||
}
|
|
||||||
return hashes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dao *Simple) mkHeaderHashKey(h uint32) []byte {
|
func (dao *Simple) mkHeaderHashKey(h uint32) []byte {
|
||||||
b := dao.getKeyBuf(1 + 4)
|
b := dao.getKeyBuf(1 + 4)
|
||||||
b[0] = byte(storage.IXHeaderHashList)
|
b[0] = byte(storage.IXHeaderHashList)
|
||||||
|
|
211
pkg/core/headerhashes.go
Normal file
211
pkg/core/headerhashes.go
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
headerBatchCount = 2000
|
||||||
|
pagesCache = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeaderHashes is a header hash manager part of the Blockchain. It can't be used
|
||||||
|
// without Blockchain.
|
||||||
|
type HeaderHashes struct {
|
||||||
|
// Backing storage.
|
||||||
|
dao *dao.Simple
|
||||||
|
|
||||||
|
// Lock for all internal state fields.
|
||||||
|
lock sync.RWMutex
|
||||||
|
|
||||||
|
// The latest header hashes (storedHeaderCount+).
|
||||||
|
latest []util.Uint256
|
||||||
|
|
||||||
|
// Previously completed page of header hashes (pre-storedHeaderCount).
|
||||||
|
previous []util.Uint256
|
||||||
|
|
||||||
|
// Number of headers stored in the chain file.
|
||||||
|
storedHeaderCount uint32
|
||||||
|
|
||||||
|
// Cache for accessed pages of header hashes.
|
||||||
|
cache *lru.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeaderHashes) initGenesis(dao *dao.Simple, hash util.Uint256) {
|
||||||
|
h.dao = dao
|
||||||
|
h.cache, _ = lru.New(pagesCache) // Never errors for positive size.
|
||||||
|
h.previous = make([]util.Uint256, headerBatchCount)
|
||||||
|
h.latest = make([]util.Uint256, 0, headerBatchCount)
|
||||||
|
h.latest = append(h.latest, hash)
|
||||||
|
dao.PutCurrentHeader(hash, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeaderHashes) init(dao *dao.Simple) error {
|
||||||
|
h.dao = dao
|
||||||
|
h.cache, _ = lru.New(pagesCache) // Never errors for positive size.
|
||||||
|
|
||||||
|
currHeaderHeight, currHeaderHash, err := h.dao.GetCurrentHeaderHeight()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve current header info: %w", err)
|
||||||
|
}
|
||||||
|
h.storedHeaderCount = ((currHeaderHeight + 1) / headerBatchCount) * headerBatchCount
|
||||||
|
|
||||||
|
if h.storedHeaderCount >= headerBatchCount {
|
||||||
|
h.previous, err = h.dao.GetHeaderHashes(h.storedHeaderCount - headerBatchCount)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve header hash page %d: %w", h.storedHeaderCount-headerBatchCount, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.previous = make([]util.Uint256, headerBatchCount)
|
||||||
|
}
|
||||||
|
h.latest = make([]util.Uint256, 0, headerBatchCount)
|
||||||
|
|
||||||
|
// There is a high chance that the Node is stopped before the next
|
||||||
|
// batch of 2000 headers was stored. Via the currentHeaders stored we can sync
|
||||||
|
// that with stored blocks.
|
||||||
|
if currHeaderHeight >= h.storedHeaderCount {
|
||||||
|
hash := currHeaderHash
|
||||||
|
var targetHash util.Uint256
|
||||||
|
if h.storedHeaderCount >= headerBatchCount {
|
||||||
|
targetHash = h.previous[len(h.previous)-1]
|
||||||
|
}
|
||||||
|
headers := make([]util.Uint256, 0, headerBatchCount)
|
||||||
|
|
||||||
|
for hash != targetHash {
|
||||||
|
blk, err := h.dao.GetBlock(hash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get header %s: %w", hash, err)
|
||||||
|
}
|
||||||
|
headers = append(headers, blk.Hash())
|
||||||
|
hash = blk.PrevHash
|
||||||
|
}
|
||||||
|
hashSliceReverse(headers)
|
||||||
|
h.latest = append(h.latest, headers...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeaderHashes) lastHeaderIndex() uint32 {
|
||||||
|
return h.storedHeaderCount + uint32(len(h.latest)) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderHeight returns the index/height of the highest header.
|
||||||
|
func (h *HeaderHashes) HeaderHeight() uint32 {
|
||||||
|
h.lock.RLock()
|
||||||
|
n := h.lastHeaderIndex()
|
||||||
|
h.lock.RUnlock()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeaderHashes) addHeaders(headers ...*block.Header) error {
|
||||||
|
var (
|
||||||
|
batch = h.dao.GetPrivate()
|
||||||
|
lastHeader *block.Header
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
|
for _, head := range headers {
|
||||||
|
if head.Index != h.lastHeaderIndex()+1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = batch.StoreHeader(head)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastHeader = head
|
||||||
|
h.latest = append(h.latest, head.Hash())
|
||||||
|
if len(h.latest) == headerBatchCount {
|
||||||
|
err = batch.StoreHeaderHashes(h.latest, h.storedHeaderCount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(h.previous, h.latest)
|
||||||
|
h.latest = h.latest[:0]
|
||||||
|
h.storedHeaderCount += headerBatchCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lastHeader != nil {
|
||||||
|
batch.PutCurrentHeader(lastHeader.Hash(), lastHeader.Index)
|
||||||
|
updateHeaderHeightMetric(lastHeader.Index)
|
||||||
|
if _, err = batch.Persist(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentHeaderHash returns the hash of the latest known header.
|
||||||
|
func (h *HeaderHashes) CurrentHeaderHash() util.Uint256 {
|
||||||
|
var hash util.Uint256
|
||||||
|
|
||||||
|
h.lock.RLock()
|
||||||
|
if len(h.latest) > 0 {
|
||||||
|
hash = h.latest[len(h.latest)-1]
|
||||||
|
} else {
|
||||||
|
hash = h.previous[len(h.previous)-1]
|
||||||
|
}
|
||||||
|
h.lock.RUnlock()
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeaderHash returns hash of the header/block with specified index, if
|
||||||
|
// HeaderHashes doesn't have a hash for this height, zero Uint256 value is returned.
|
||||||
|
func (h *HeaderHashes) GetHeaderHash(i uint32) util.Uint256 {
|
||||||
|
h.lock.RLock()
|
||||||
|
res, ok := h.getLocalHeaderHash(i)
|
||||||
|
h.lock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
// If it's not in the latest/previous, then it's in the cache or DB, those
|
||||||
|
// need no additional locks.
|
||||||
|
page := (i / headerBatchCount) * headerBatchCount
|
||||||
|
cache, ok := h.cache.Get(page)
|
||||||
|
if ok {
|
||||||
|
hashes := cache.([]util.Uint256)
|
||||||
|
return hashes[i-page]
|
||||||
|
}
|
||||||
|
hashes, err := h.dao.GetHeaderHashes(page)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}
|
||||||
|
}
|
||||||
|
_ = h.cache.Add(page, hashes)
|
||||||
|
return hashes[i-page]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLocalHeaderHash looks for the index in the latest and previous caches.
|
||||||
|
// Locking is left to the user.
|
||||||
|
func (h *HeaderHashes) getLocalHeaderHash(i uint32) (util.Uint256, bool) {
|
||||||
|
if i > h.lastHeaderIndex() {
|
||||||
|
return util.Uint256{}, false
|
||||||
|
}
|
||||||
|
if i >= h.storedHeaderCount {
|
||||||
|
return h.latest[i-h.storedHeaderCount], true
|
||||||
|
}
|
||||||
|
previousStored := h.storedHeaderCount - headerBatchCount
|
||||||
|
if i >= previousStored {
|
||||||
|
return h.previous[i-previousStored], true
|
||||||
|
}
|
||||||
|
return util.Uint256{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeaderHashes) haveRecentHash(hash util.Uint256, i uint32) bool {
|
||||||
|
h.lock.RLock()
|
||||||
|
defer h.lock.RUnlock()
|
||||||
|
for ; i > 0; i-- {
|
||||||
|
lh, ok := h.getLocalHeaderHash(i)
|
||||||
|
if ok && hash.Equals(lh) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -59,7 +59,7 @@ func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block {
|
||||||
lastBlock, ok := bc.topBlock.Load().(*block.Block)
|
lastBlock, ok := bc.topBlock.Load().(*block.Block)
|
||||||
if !ok {
|
if !ok {
|
||||||
var err error
|
var err error
|
||||||
lastBlock, err = bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight())))
|
lastBlock, err = bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ type Ledger interface {
|
||||||
CurrentBlockHash() util.Uint256
|
CurrentBlockHash() util.Uint256
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context represents context in which interops are executed.
|
// Context represents context in which interops are executed.
|
||||||
|
@ -377,7 +377,7 @@ func (ic *Context) BlockHeight() uint32 {
|
||||||
// CurrentBlockHash returns current block hash got from Context's block if it's set.
|
// CurrentBlockHash returns current block hash got from Context's block if it's set.
|
||||||
func (ic *Context) CurrentBlockHash() util.Uint256 {
|
func (ic *Context) CurrentBlockHash() util.Uint256 {
|
||||||
if ic.Block != nil {
|
if ic.Block != nil {
|
||||||
return ic.Chain.GetHeaderHash(int(ic.Block.Index - 1)) // Persisting block is not yet stored.
|
return ic.Chain.GetHeaderHash(ic.Block.Index - 1) // Persisting block is not yet stored.
|
||||||
}
|
}
|
||||||
return ic.Chain.CurrentBlockHash()
|
return ic.Chain.CurrentBlockHash()
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ func getBlockHashFromItem(ic *interop.Context, item stackitem.Item) util.Uint256
|
||||||
if uint32(index) > ic.BlockHeight() {
|
if uint32(index) > ic.BlockHeight() {
|
||||||
panic(fmt.Errorf("no block with index %d", index))
|
panic(fmt.Errorf("no block with index %d", index))
|
||||||
}
|
}
|
||||||
return ic.Chain.GetHeaderHash(int(index))
|
return ic.Chain.GetHeaderHash(uint32(index))
|
||||||
}
|
}
|
||||||
hash, err := getUint256FromItem(item)
|
hash, err := getUint256FromItem(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -111,7 +111,7 @@ func TestLedger_GetTransactionFromBlock(t *testing.T) {
|
||||||
ledgerInvoker := c.WithSigners(c.Committee)
|
ledgerInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
ledgerInvoker.Invoke(t, e.Chain.BlockHeight(), "currentIndex") // Adds a block.
|
ledgerInvoker.Invoke(t, e.Chain.BlockHeight(), "currentIndex") // Adds a block.
|
||||||
b := e.GetBlockByIndex(t, int(e.Chain.BlockHeight()))
|
b := e.GetBlockByIndex(t, e.Chain.BlockHeight())
|
||||||
|
|
||||||
check := func(t testing.TB, stack []stackitem.Item) {
|
check := func(t testing.TB, stack []stackitem.Item) {
|
||||||
require.Equal(t, 1, len(stack))
|
require.Equal(t, 1, len(stack))
|
||||||
|
@ -148,8 +148,8 @@ func TestLedger_GetBlock(t *testing.T) {
|
||||||
e := c.Executor
|
e := c.Executor
|
||||||
ledgerInvoker := c.WithSigners(c.Committee)
|
ledgerInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
||||||
ledgerInvoker.Invoke(t, e.Chain.GetHeaderHash(int(e.Chain.BlockHeight())).BytesBE(), "currentHash") // Adds a block.
|
ledgerInvoker.Invoke(t, e.Chain.GetHeaderHash(e.Chain.BlockHeight()).BytesBE(), "currentHash") // Adds a block.
|
||||||
b := e.GetBlockByIndex(t, int(e.Chain.BlockHeight()))
|
b := e.GetBlockByIndex(t, e.Chain.BlockHeight())
|
||||||
|
|
||||||
expected := []stackitem.Item{
|
expected := []stackitem.Item{
|
||||||
stackitem.NewByteArray(b.Hash().BytesBE()),
|
stackitem.NewByteArray(b.Hash().BytesBE()),
|
||||||
|
|
|
@ -44,7 +44,7 @@ func updatePersistedHeightMetric(pHeight uint32) {
|
||||||
persistedHeight.Set(float64(pHeight))
|
persistedHeight.Set(float64(pHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateHeaderHeightMetric(hHeight int) {
|
func updateHeaderHeightMetric(hHeight uint32) {
|
||||||
headerHeight.Set(float64(hHeight))
|
headerHeight.Set(float64(hHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ type Ledger interface {
|
||||||
BlockHeight() uint32
|
BlockHeight() uint32
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ func (s *Module) defineSyncStage() error {
|
||||||
s.log.Info("MPT is in sync",
|
s.log.Info("MPT is in sync",
|
||||||
zap.Uint32("stateroot height", s.stateMod.CurrentLocalHeight()))
|
zap.Uint32("stateroot height", s.stateMod.CurrentLocalHeight()))
|
||||||
} else if s.syncStage&headersSynced != 0 {
|
} else if s.syncStage&headersSynced != 0 {
|
||||||
header, err := s.bc.GetHeader(s.bc.GetHeaderHash(int(s.syncPoint + 1)))
|
header, err := s.bc.GetHeader(s.bc.GetHeaderHash(s.syncPoint + 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get header to initialize MPT billet: %w", err)
|
return fmt.Errorf("failed to get header to initialize MPT billet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStateSyncModule_Init(t *testing.T) {
|
func TestStateSyncModule_Init(t *testing.T) {
|
||||||
var (
|
const (
|
||||||
stateSyncInterval = 2
|
stateSyncInterval = 2
|
||||||
maxTraceable uint32 = 3
|
maxTraceable = 3
|
||||||
)
|
)
|
||||||
spoutCfg := func(c *config.ProtocolConfiguration) {
|
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||||
c.StateRootInHeader = true
|
c.StateRootInHeader = true
|
||||||
|
@ -55,7 +55,7 @@ func TestStateSyncModule_Init(t *testing.T) {
|
||||||
|
|
||||||
t.Run("inactive: bolt chain height is close enough to spout chain height", func(t *testing.T) {
|
t.Run("inactive: bolt chain height is close enough to spout chain height", func(t *testing.T) {
|
||||||
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
bcBolt, _, _ := chain.NewMultiWithCustomConfig(t, boltCfg)
|
||||||
for i := 1; i < int(bcSpout.BlockHeight())-stateSyncInterval; i++ {
|
for i := uint32(1); i < bcSpout.BlockHeight()-stateSyncInterval; i++ {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, bcBolt.AddBlock(b))
|
require.NoError(t, bcBolt.AddBlock(b))
|
||||||
|
@ -114,9 +114,9 @@ func TestStateSyncModule_Init(t *testing.T) {
|
||||||
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
require.NoError(t, module.Init(bcSpout.BlockHeight()))
|
||||||
|
|
||||||
// firstly, fetch all headers to create proper DB state (where headers are in sync)
|
// firstly, fetch all headers to create proper DB state (where headers are in sync)
|
||||||
stateSyncPoint := (int(bcSpout.BlockHeight()) / stateSyncInterval) * stateSyncInterval
|
stateSyncPoint := (bcSpout.BlockHeight() / stateSyncInterval) * stateSyncInterval
|
||||||
var expectedHeader *block.Header
|
var expectedHeader *block.Header
|
||||||
for i := 1; i <= int(bcSpout.HeaderHeight()); i++ {
|
for i := uint32(1); i <= bcSpout.HeaderHeight(); i++ {
|
||||||
header, err := bcSpout.GetHeader(bcSpout.GetHeaderHash(i))
|
header, err := bcSpout.GetHeader(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, module.AddHeaders(header))
|
require.NoError(t, module.AddHeaders(header))
|
||||||
|
@ -142,7 +142,7 @@ func TestStateSyncModule_Init(t *testing.T) {
|
||||||
require.Equal(t, expectedHeader.PrevStateRoot, unknownNodes[0])
|
require.Equal(t, expectedHeader.PrevStateRoot, unknownNodes[0])
|
||||||
|
|
||||||
// add several blocks to create DB state where blocks are not in sync yet, but it's not a genesis.
|
// add several blocks to create DB state where blocks are not in sync yet, but it's not a genesis.
|
||||||
for i := stateSyncPoint - int(maxTraceable) + 1; i <= stateSyncPoint-stateSyncInterval-1; i++ {
|
for i := stateSyncPoint - maxTraceable + 1; i <= stateSyncPoint-stateSyncInterval-1; i++ {
|
||||||
block, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
block, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, module.AddBlock(block))
|
require.NoError(t, module.AddBlock(block))
|
||||||
|
@ -283,9 +283,9 @@ func TestStateSyncModule_Init(t *testing.T) {
|
||||||
|
|
||||||
func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
check := func(t *testing.T, spoutEnableGC bool) {
|
check := func(t *testing.T, spoutEnableGC bool) {
|
||||||
var (
|
const (
|
||||||
stateSyncInterval = 4
|
stateSyncInterval = 4
|
||||||
maxTraceable uint32 = 6
|
maxTraceable = 6
|
||||||
stateSyncPoint = 24
|
stateSyncPoint = 24
|
||||||
)
|
)
|
||||||
spoutCfg := func(c *config.ProtocolConfiguration) {
|
spoutCfg := func(c *config.ProtocolConfiguration) {
|
||||||
|
@ -325,7 +325,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
require.Error(t, module.AddHeaders(h))
|
require.Error(t, module.AddHeaders(h))
|
||||||
})
|
})
|
||||||
t.Run("no error: add blocks before initialisation", func(t *testing.T) {
|
t.Run("no error: add blocks before initialisation", func(t *testing.T) {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(int(bcSpout.BlockHeight())))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(bcSpout.BlockHeight()))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, module.AddBlock(b))
|
require.NoError(t, module.AddBlock(b))
|
||||||
})
|
})
|
||||||
|
@ -342,7 +342,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
// add headers to module
|
// add headers to module
|
||||||
headers := make([]*block.Header, 0, bcSpout.HeaderHeight())
|
headers := make([]*block.Header, 0, bcSpout.HeaderHeight())
|
||||||
for i := uint32(1); i <= bcSpout.HeaderHeight(); i++ {
|
for i := uint32(1); i <= bcSpout.HeaderHeight(); i++ {
|
||||||
h, err := bcSpout.GetHeader(bcSpout.GetHeaderHash(int(i)))
|
h, err := bcSpout.GetHeader(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
headers = append(headers, h)
|
headers = append(headers, h)
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
|
|
||||||
// add blocks
|
// add blocks
|
||||||
t.Run("error: unexpected block index", func(t *testing.T) {
|
t.Run("error: unexpected block index", func(t *testing.T) {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(stateSyncPoint - int(maxTraceable)))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(stateSyncPoint - maxTraceable))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Error(t, module.AddBlock(b))
|
require.Error(t, module.AddBlock(b))
|
||||||
})
|
})
|
||||||
|
@ -379,7 +379,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
require.Error(t, module.AddBlock(b))
|
require.Error(t, module.AddBlock(b))
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := stateSyncPoint - int(maxTraceable) + 1; i <= stateSyncPoint; i++ {
|
for i := uint32(stateSyncPoint - maxTraceable + 1); i <= stateSyncPoint; i++ {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, module.AddBlock(b))
|
require.NoError(t, module.AddBlock(b))
|
||||||
|
@ -432,7 +432,7 @@ func TestStateSyncModule_RestoreBasicChain(t *testing.T) {
|
||||||
require.Equal(t, uint32(stateSyncPoint), bcBolt.BlockHeight())
|
require.Equal(t, uint32(stateSyncPoint), bcBolt.BlockHeight())
|
||||||
|
|
||||||
// add missing blocks to bcBolt: should be ok, because state is synced
|
// add missing blocks to bcBolt: should be ok, because state is synced
|
||||||
for i := stateSyncPoint + 1; i <= int(bcSpout.BlockHeight()); i++ {
|
for i := uint32(stateSyncPoint + 1); i <= bcSpout.BlockHeight(); i++ {
|
||||||
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
b, err := bcSpout.GetBlock(bcSpout.GetHeaderHash(i))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, bcBolt.AddBlock(b))
|
require.NoError(t, bcBolt.AddBlock(b))
|
||||||
|
|
|
@ -64,8 +64,8 @@ func getNextConsensusAddress(validators []*keys.PublicKey) (val util.Uint160, er
|
||||||
return hash.Hash160(raw), nil
|
return hash.Hash160(raw), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// headerSliceReverse reverses the given slice of *Header.
|
// hashSliceReverse reverses the given slice of util.Uint256.
|
||||||
func headerSliceReverse(dest []*block.Header) {
|
func hashSliceReverse(dest []util.Uint256) {
|
||||||
for i, j := 0, len(dest)-1; i < j; i, j = i+1, j-1 {
|
for i, j := 0, len(dest)-1; i < j; i, j = i+1, j-1 {
|
||||||
dest[i], dest[j] = dest[j], dest[i]
|
dest[i], dest[j] = dest[j], dest[i]
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func NewExecutor(t testing.TB, bc *core.Blockchain, validator, committee Signer)
|
||||||
|
|
||||||
// TopBlock returns the block with the highest index.
|
// TopBlock returns the block with the highest index.
|
||||||
func (e *Executor) TopBlock(t testing.TB) *block.Block {
|
func (e *Executor) TopBlock(t testing.TB) *block.Block {
|
||||||
b, err := e.Chain.GetBlock(e.Chain.GetHeaderHash(int(e.Chain.BlockHeight())))
|
b, err := e.Chain.GetBlock(e.Chain.GetHeaderHash(e.Chain.BlockHeight()))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ func (e *Executor) AddBlockCheckHalt(t testing.TB, txs ...*transaction.Transacti
|
||||||
|
|
||||||
// TestInvoke creates a test VM with a dummy block and executes a transaction in it.
|
// TestInvoke creates a test VM with a dummy block and executes a transaction in it.
|
||||||
func TestInvoke(bc *core.Blockchain, tx *transaction.Transaction) (*vm.VM, error) {
|
func TestInvoke(bc *core.Blockchain, tx *transaction.Transaction) (*vm.VM, error) {
|
||||||
lastBlock, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight())))
|
lastBlock, err := bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -392,7 +392,7 @@ func (e *Executor) GetTransaction(t testing.TB, h util.Uint256) (*transaction.Tr
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByIndex returns a block by the specified index.
|
// GetBlockByIndex returns a block by the specified index.
|
||||||
func (e *Executor) GetBlockByIndex(t testing.TB, idx int) *block.Block {
|
func (e *Executor) GetBlockByIndex(t testing.TB, idx uint32) *block.Block {
|
||||||
h := e.Chain.GetHeaderHash(idx)
|
h := e.Chain.GetHeaderHash(idx)
|
||||||
require.NotEmpty(t, h)
|
require.NotEmpty(t, h)
|
||||||
b, err := e.Chain.GetBlock(h)
|
b, err := e.Chain.GetBlock(h)
|
||||||
|
|
|
@ -61,7 +61,7 @@ type (
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
GetMaxVerificationGAS() int64
|
GetMaxVerificationGAS() int64
|
||||||
GetMemPool() *mempool.Pool
|
GetMemPool() *mempool.Pool
|
||||||
GetNotaryBalance(acc util.Uint160) *big.Int
|
GetNotaryBalance(acc util.Uint160) *big.Int
|
||||||
|
@ -972,7 +972,7 @@ func (s *Server) handleGetBlocksCmd(p Peer, gb *payload.GetBlocks) error {
|
||||||
}
|
}
|
||||||
blockHashes := make([]util.Uint256, 0)
|
blockHashes := make([]util.Uint256, 0)
|
||||||
for i := start.Index + 1; i <= start.Index+uint32(count); i++ {
|
for i := start.Index + 1; i <= start.Index+uint32(count); i++ {
|
||||||
hash := s.chain.GetHeaderHash(int(i))
|
hash := s.chain.GetHeaderHash(i)
|
||||||
if hash.Equals(util.Uint256{}) {
|
if hash.Equals(util.Uint256{}) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -995,7 +995,7 @@ func (s *Server) handleGetBlockByIndexCmd(p Peer, gbd *payload.GetBlockByIndex)
|
||||||
count = payload.MaxHashesCount
|
count = payload.MaxHashesCount
|
||||||
}
|
}
|
||||||
for i := gbd.IndexStart; i < gbd.IndexStart+uint32(count); i++ {
|
for i := gbd.IndexStart; i < gbd.IndexStart+uint32(count); i++ {
|
||||||
hash := s.chain.GetHeaderHash(int(i))
|
hash := s.chain.GetHeaderHash(i)
|
||||||
if hash.Equals(util.Uint256{}) {
|
if hash.Equals(util.Uint256{}) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1026,7 +1026,7 @@ func (s *Server) handleGetHeadersCmd(p Peer, gh *payload.GetBlockByIndex) error
|
||||||
resp := payload.Headers{}
|
resp := payload.Headers{}
|
||||||
resp.Hdrs = make([]*block.Header, 0, count)
|
resp.Hdrs = make([]*block.Header, 0, count)
|
||||||
for i := gh.IndexStart; i < gh.IndexStart+uint32(count); i++ {
|
for i := gh.IndexStart; i < gh.IndexStart+uint32(count); i++ {
|
||||||
hash := s.chain.GetHeaderHash(int(i))
|
hash := s.chain.GetHeaderHash(i)
|
||||||
if hash.Equals(util.Uint256{}) {
|
if hash.Equals(util.Uint256{}) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -1272,7 +1272,7 @@ func TestInvokeVerify(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("positive, historic, by block, with signer", func(t *testing.T) {
|
t.Run("positive, historic, by block, with signer", func(t *testing.T) {
|
||||||
res, err := c.InvokeContractVerifyWithState(chain.GetHeaderHash(int(chain.BlockHeight())-1), contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
res, err := c.InvokeContractVerifyWithState(chain.GetHeaderHash(chain.BlockHeight()-1), contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "HALT", res.State)
|
require.Equal(t, "HALT", res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
|
|
@ -78,7 +78,7 @@ type (
|
||||||
GetEnrollments() ([]state.Validator, error)
|
GetEnrollments() ([]state.Validator, error)
|
||||||
GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32)
|
GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32)
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(uint32) util.Uint256
|
||||||
GetMaxVerificationGAS() int64
|
GetMaxVerificationGAS() int64
|
||||||
GetMemPool() *mempool.Pool
|
GetMemPool() *mempool.Pool
|
||||||
GetNEP11Contracts() []util.Uint160
|
GetNEP11Contracts() []util.Uint160
|
||||||
|
@ -652,7 +652,7 @@ func (s *Server) fillBlockMetadata(obj io.Serializable, h *block.Header) result.
|
||||||
Confirmations: s.chain.BlockHeight() - h.Index + 1,
|
Confirmations: s.chain.BlockHeight() - h.Index + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := s.chain.GetHeaderHash(int(h.Index) + 1)
|
hash := s.chain.GetHeaderHash(h.Index + 1)
|
||||||
if !hash.Equals(util.Uint256{}) {
|
if !hash.Equals(util.Uint256{}) {
|
||||||
res.NextBlockHash = &hash
|
res.NextBlockHash = &hash
|
||||||
}
|
}
|
||||||
|
@ -1646,7 +1646,7 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *neorp
|
||||||
if height == math.MaxUint32 { // Mempooled transaction.
|
if height == math.MaxUint32 { // Mempooled transaction.
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
_header := s.chain.GetHeaderHash(int(height))
|
_header := s.chain.GetHeaderHash(height)
|
||||||
header, err := s.chain.GetHeader(_header)
|
header, err := s.chain.GetHeader(_header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, neorpc.NewRPCError("Failed to get header for the transaction", err.Error())
|
return nil, neorpc.NewRPCError("Failed to get header for the transaction", err.Error())
|
||||||
|
@ -2037,15 +2037,12 @@ func (s *Server) getHistoricParams(reqParams params.Params) (uint32, *neorpc.Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, neorpc.NewInvalidParamsError(fmt.Sprintf("unknown block or stateroot: %s", err))
|
return 0, neorpc.NewInvalidParamsError(fmt.Sprintf("unknown block or stateroot: %s", err))
|
||||||
}
|
}
|
||||||
height = int(stateH)
|
height = stateH
|
||||||
} else {
|
} else {
|
||||||
height = int(b.Index)
|
height = b.Index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if height > math.MaxUint32 {
|
return height + 1, nil
|
||||||
return 0, neorpc.NewInvalidParamsError("historic height exceeds max uint32 value")
|
|
||||||
}
|
|
||||||
return uint32(height) + 1, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, nextH *uint32, verbose bool) (*interop.Context, *neorpc.Error) {
|
func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, nextH *uint32, verbose bool) (*interop.Context, *neorpc.Error) {
|
||||||
|
@ -2683,16 +2680,16 @@ drainloop:
|
||||||
close(s.notaryRequestCh)
|
close(s.notaryRequestCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) blockHeightFromParam(param *params.Param) (int, *neorpc.Error) {
|
func (s *Server) blockHeightFromParam(param *params.Param) (uint32, *neorpc.Error) {
|
||||||
num, err := param.GetInt()
|
num, err := param.GetInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, neorpc.ErrInvalidParams
|
return 0, neorpc.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
if num < 0 || num > int(s.chain.BlockHeight()) {
|
if num < 0 || int64(num) > int64(s.chain.BlockHeight()) {
|
||||||
return 0, invalidBlockHeightError(0, num)
|
return 0, invalidBlockHeightError(0, num)
|
||||||
}
|
}
|
||||||
return num, nil
|
return uint32(num), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) packResponse(r *params.In, result interface{}, respErr *neorpc.Error) abstract {
|
func (s *Server) packResponse(r *params.In, result interface{}, respErr *neorpc.Error) abstract {
|
||||||
|
|
|
@ -2271,7 +2271,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("verbose != 0", func(t *testing.T) {
|
t.Run("verbose != 0", func(t *testing.T) {
|
||||||
nextHash := chain.GetHeaderHash(int(hdr.Index) + 1)
|
nextHash := chain.GetHeaderHash(hdr.Index + 1)
|
||||||
expected := &result.Header{
|
expected := &result.Header{
|
||||||
Header: *hdr,
|
Header: *hdr,
|
||||||
BlockMetadata: result.BlockMetadata{
|
BlockMetadata: result.BlockMetadata{
|
||||||
|
@ -2315,7 +2315,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
testNEP17T := func(t *testing.T, start, stop, limit, page int, sent, rcvd []int) {
|
testNEP17T := func(t *testing.T, start, stop, limit, page int, sent, rcvd []int) {
|
||||||
ps := []string{`"` + testchain.PrivateKeyByID(0).Address() + `"`}
|
ps := []string{`"` + testchain.PrivateKeyByID(0).Address() + `"`}
|
||||||
if start != 0 {
|
if start != 0 {
|
||||||
h, err := e.chain.GetHeader(e.chain.GetHeaderHash(start))
|
h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(start)))
|
||||||
var ts uint64
|
var ts uint64
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ts = h.Timestamp
|
ts = h.Timestamp
|
||||||
|
@ -2325,7 +2325,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
ps = append(ps, strconv.FormatUint(ts, 10))
|
ps = append(ps, strconv.FormatUint(ts, 10))
|
||||||
}
|
}
|
||||||
if stop != 0 {
|
if stop != 0 {
|
||||||
h, err := e.chain.GetHeader(e.chain.GetHeaderHash(stop))
|
h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(stop)))
|
||||||
var ts uint64
|
var ts uint64
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ts = h.Timestamp
|
ts = h.Timestamp
|
||||||
|
@ -2846,7 +2846,7 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
||||||
rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
|
rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
blockWithFAULTedTx, err := e.chain.GetBlock(e.chain.GetHeaderHash(int(faultedTxBlock))) // Transaction with ABORT inside.
|
blockWithFAULTedTx, err := e.chain.GetBlock(e.chain.GetHeaderHash(faultedTxBlock)) // Transaction with ABORT inside.
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(blockWithFAULTedTx.Transactions))
|
require.Equal(t, 1, len(blockWithFAULTedTx.Transactions))
|
||||||
txFAULTed := blockWithFAULTedTx.Transactions[0]
|
txFAULTed := blockWithFAULTedTx.Transactions[0]
|
||||||
|
|
|
@ -66,7 +66,7 @@ func main() {
|
||||||
handleError("can't get next block validators", err)
|
handleError("can't get next block validators", err)
|
||||||
valScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(nbVals)
|
valScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(nbVals)
|
||||||
handleError("can't create verification script", err)
|
handleError("can't create verification script", err)
|
||||||
lastBlock, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight())))
|
lastBlock, err := bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight()))
|
||||||
handleError("can't fetch last block", err)
|
handleError("can't fetch last block", err)
|
||||||
|
|
||||||
txMoveNeo, err := testchain.NewTransferFromOwner(bc, bc.GoverningTokenHash(), h, native.NEOTotalSupply, 0, 2)
|
txMoveNeo, err := testchain.NewTransferFromOwner(bc, bc.GoverningTokenHash(), h, native.NEOTotalSupply, 0, 2)
|
||||||
|
|
Loading…
Reference in a new issue