diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 5465a9b94..8865b207f 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -650,6 +650,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error return err } bc.contracts.Policy.OnPersistEnd(bc.dao) + bc.contracts.Oracle.OnPersistEnd(bc.dao) bc.dao.MPT.Flush() // Every persist cycle we also compact our in-memory MPT. persistedHeight := atomic.LoadUint32(&bc.persistedHeight) diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index fbbdb8859..a0008f314 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -5,6 +5,7 @@ import ( "errors" "math/big" "sort" + "sync/atomic" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -30,6 +31,11 @@ type Oracle struct { interop.ContractMD GAS *GAS NEO *NEO + + // nodesChanged is true if `SetOracleNodes` was called. + nodesChanged atomic.Value + // nodes contains cached list of oracle nodes. + nodes atomic.Value } const ( @@ -165,10 +171,7 @@ func (o *Oracle) PostPersist(ic *interop.Context) error { } if nodes == nil { - nodes, err = o.GetOracleNodes(ic.DAO) - if err != nil { - return err - } + nodes = o.GetOracleNodes() reward = make([]big.Int, len(nodes)) } @@ -194,6 +197,8 @@ func (o *Oracle) Initialize(ic *interop.Context) error { if err := ic.DAO.PutStorageItem(o.ContractID, prefixNodeList, si); err != nil { return err } + o.nodes.Store(keys.PublicKeys(nil)) + o.nodesChanged.Store(false) si = &state.StorageItem{Value: make([]byte, 8)} // uint64(0) LE return ic.DAO.PutStorageItem(o.ContractID, prefixRequestID, si) } @@ -324,17 +329,13 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url, filter, cb string, us } func (o *Oracle) getOracleNodes(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - pubs, err := o.GetOracleNodes(ic.DAO) - if err != nil { - panic(err) - } + pubs := o.GetOracleNodes() return pubsToArray(pubs) } // GetOracleNodes returns public keys of oracle nodes. -func (o *Oracle) GetOracleNodes(d dao.DAO) (keys.PublicKeys, error) { - ns := new(NodeList) - return keys.PublicKeys(*ns), o.getSerializableFromDAO(d, prefixNodeList, ns) +func (o *Oracle) GetOracleNodes() keys.PublicKeys { + return o.nodes.Load().(keys.PublicKeys).Copy() } func (o *Oracle) setOracleNodes(ic *interop.Context, _ []stackitem.Item) stackitem.Item { @@ -357,6 +358,7 @@ func (o *Oracle) SetOracleNodes(ic *interop.Context, pubs keys.PublicKeys) error } sort.Sort(pubs) + o.nodesChanged.Store(true) si := &state.StorageItem{Value: NodeList(pubs).Bytes()} return ic.DAO.PutStorageItem(o.ContractID, prefixNodeList, si) } @@ -410,3 +412,16 @@ func (o *Oracle) getSerializableFromDAO(d dao.DAO, key []byte, item io.Serializa item.DecodeBinary(r) return r.Err } + +// OnPersistEnd updates cached Oracle values if they've been changed +func (o *Oracle) OnPersistEnd(d dao.DAO) { + if !o.nodesChanged.Load().(bool) { + return + } + + ns := new(NodeList) + _ = o.getSerializableFromDAO(d, prefixNodeList, ns) + o.nodes.Store(keys.PublicKeys(*ns)) + o.nodesChanged.Store(false) + return +} diff --git a/pkg/core/native_oracle_test.go b/pkg/core/native_oracle_test.go index 7c4ad3164..7f2304e75 100644 --- a/pkg/core/native_oracle_test.go +++ b/pkg/core/native_oracle_test.go @@ -141,6 +141,7 @@ func TestOracle_Request(t *testing.T) { ic := bc.newInteropContext(trigger.Application, bc.dao, nil, tx) err = orc.SetOracleNodes(ic, keys.PublicKeys{pub}) require.NoError(t, err) + orc.OnPersistEnd(ic.DAO) tx = transaction.New(netmode.UnitTestNet, native.GetOracleResponseScript(), 0) ic.Tx = tx @@ -223,11 +224,10 @@ func TestOracle_SetOracleNodes(t *testing.T) { ic := bc.newInteropContext(trigger.System, bc.dao, nil, tx) ic.VM = vm.New() - pubs, err := orc.GetOracleNodes(ic.DAO) - require.NoError(t, err) + pubs := orc.GetOracleNodes() require.Equal(t, 0, len(pubs)) - err = orc.SetOracleNodes(ic, keys.PublicKeys{}) + err := orc.SetOracleNodes(ic, keys.PublicKeys{}) require.True(t, errors.Is(err, native.ErrEmptyNodeList), "got: %v", err) priv, err := keys.NewPrivateKey() @@ -239,8 +239,8 @@ func TestOracle_SetOracleNodes(t *testing.T) { setSigner(tx, testchain.CommitteeScriptHash()) require.NoError(t, orc.SetOracleNodes(ic, keys.PublicKeys{pub})) + orc.OnPersistEnd(ic.DAO) - pubs, err = orc.GetOracleNodes(ic.DAO) - require.NoError(t, err) + pubs = orc.GetOracleNodes() require.Equal(t, keys.PublicKeys{pub}, pubs) }