diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 18f589832..863da412d 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -55,6 +55,9 @@ type Blockchain struct { // Write access should only happen in storeBlock(). blockHeight uint32 + // Current top Block wrapped in an atomic.Value for safe access. + topBlock atomic.Value + // Current persisted block count. persistedHeight uint32 @@ -558,6 +561,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { if err != nil { return err } + bc.topBlock.Store(block) atomic.StoreUint32(&bc.blockHeight, block.Index) updateBlockHeightMetric(block.Index) for _, tx := range block.Transactions { @@ -749,6 +753,13 @@ func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*state.Stor // GetBlock returns a Block by the given hash. func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) { + topBlock := bc.topBlock.Load() + if topBlock != nil { + if tb, ok := topBlock.(*Block); ok && tb.Hash().Equals(hash) { + return tb, nil + } + } + block, err := bc.dao.GetBlock(hash) if err != nil { return nil, err @@ -768,6 +779,12 @@ func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) { // GetHeader returns data block header identified with the given hash value. func (bc *Blockchain) GetHeader(hash util.Uint256) (*Header, error) { + topBlock := bc.topBlock.Load() + if topBlock != nil { + if tb, ok := topBlock.(*Block); ok && tb.Hash().Equals(hash) { + return tb.Header(), nil + } + } block, err := bc.dao.GetBlock(hash) if err != nil { return nil, err diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 204846f49..eae4aaab5 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -238,6 +238,14 @@ func (v *VM) LoadScript(b []byte) { v.istack.PushVal(ctx) } +// loadScriptWithHash if similar to the LoadScript method, but it also loads +// given script hash directly into the Context to avoid its recalculations. It's +// up to user of this function to make sure the script and hash match each other. +func (v *VM) loadScriptWithHash(b []byte, hash util.Uint160) { + v.LoadScript(b) + v.istack.Top().Value().(*Context).scriptHash = hash +} + // Context returns the current executed context. Nil if there is no context, // which implies no program is loaded. func (v *VM) Context() *Context { @@ -1120,7 +1128,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro _ = v.istack.Pop() } - v.LoadScript(script) + v.loadScriptWithHash(script, hash) case opcode.RET: oldCtx := v.istack.Pop().Value().(*Context) @@ -1336,6 +1344,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic(fmt.Sprintf("could not find script %s", hash)) } newCtx = NewContext(script) + newCtx.scriptHash = hash } newCtx.rvcount = rvcount newCtx.estack = NewStack("evaluation")