oracle: integrate module in core and RPC
1. Initialization is performed via `Blockchain` methods. 2. Native Oracle contract updates list of oracle nodes and in-fly requests in `PostPersist`. 3. RPC uses Oracle module directly.
This commit is contained in:
parent
7e16bea126
commit
43e4d3af88
31 changed files with 590 additions and 44 deletions
|
@ -7,7 +7,9 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer/services"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||
|
@ -36,6 +38,11 @@ type Oracle struct {
|
|||
|
||||
Desig *Designate
|
||||
oracleScript []byte
|
||||
|
||||
// Module is an oracle module capable of talking with the external world.
|
||||
Module atomic.Value
|
||||
// newRequests contains new requests created during current block.
|
||||
newRequests map[uint64]*state.OracleRequest
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -112,7 +119,11 @@ func (o *Oracle) GetOracleResponseScript() []byte {
|
|||
|
||||
// OnPersist implements Contract interface.
|
||||
func (o *Oracle) OnPersist(ic *interop.Context) error {
|
||||
return nil
|
||||
var err error
|
||||
if o.newRequests == nil {
|
||||
o.newRequests, err = o.getRequests(ic.DAO)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// PostPersist represents `postPersist` method.
|
||||
|
@ -120,6 +131,9 @@ func (o *Oracle) PostPersist(ic *interop.Context) error {
|
|||
var nodes keys.PublicKeys
|
||||
var reward []big.Int
|
||||
single := new(big.Int).SetUint64(oracleRequestPrice)
|
||||
var removedIDs []uint64
|
||||
|
||||
orc, _ := o.Module.Load().(services.Oracle)
|
||||
for _, tx := range ic.Block.Transactions {
|
||||
resp := getResponse(tx)
|
||||
if resp == nil {
|
||||
|
@ -133,6 +147,9 @@ func (o *Oracle) PostPersist(ic *interop.Context) error {
|
|||
if err := ic.DAO.DeleteStorageItem(o.ContractID, reqKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if orc != nil {
|
||||
removedIDs = append(removedIDs, resp.ID)
|
||||
}
|
||||
|
||||
idKey := makeIDListKey(req.URL)
|
||||
idList := new(IDList)
|
||||
|
@ -170,7 +187,11 @@ func (o *Oracle) PostPersist(ic *interop.Context) error {
|
|||
for i := range reward {
|
||||
o.GAS.mint(ic, nodes[i].GetScriptHash(), &reward[i], false)
|
||||
}
|
||||
return nil
|
||||
|
||||
if len(removedIDs) != 0 && orc != nil {
|
||||
orc.RemoveRequests(removedIDs)
|
||||
}
|
||||
return o.updateCache(ic.DAO)
|
||||
}
|
||||
|
||||
// Metadata returns contract metadata.
|
||||
|
@ -338,6 +359,7 @@ func (o *Oracle) PutRequestInternal(id uint64, req *state.OracleRequest, d dao.D
|
|||
if err := d.PutStorageItem(o.ContractID, reqKey, reqItem); err != nil {
|
||||
return err
|
||||
}
|
||||
o.newRequests[id] = req
|
||||
|
||||
// Add request ID to the id list.
|
||||
lst := new(IDList)
|
||||
|
@ -393,6 +415,29 @@ func (o *Oracle) getOriginalTxID(d dao.DAO, tx *transaction.Transaction) util.Ui
|
|||
return tx.Hash()
|
||||
}
|
||||
|
||||
// getRequests returns all requests which have not been finished yet.
|
||||
func (o *Oracle) getRequests(d dao.DAO) (map[uint64]*state.OracleRequest, error) {
|
||||
m, err := d.GetStorageItemsWithPrefix(o.ContractID, prefixRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqs := make(map[uint64]*state.OracleRequest, len(m))
|
||||
for k, si := range m {
|
||||
if len(k) != 8 {
|
||||
return nil, errors.New("invalid request ID")
|
||||
}
|
||||
r := io.NewBinReaderFromBuf(si.Value)
|
||||
req := new(state.OracleRequest)
|
||||
req.DecodeBinary(r)
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
id := binary.LittleEndian.Uint64([]byte(k))
|
||||
reqs[id] = req
|
||||
}
|
||||
return reqs, nil
|
||||
}
|
||||
|
||||
func makeRequestKey(id uint64) []byte {
|
||||
k := make([]byte, 9)
|
||||
k[0] = prefixRequest[0]
|
||||
|
@ -407,3 +452,22 @@ func makeIDListKey(url string) []byte {
|
|||
func (o *Oracle) getSerializableFromDAO(d dao.DAO, key []byte, item io.Serializable) error {
|
||||
return getSerializableFromDAO(o.ContractID, d, key, item)
|
||||
}
|
||||
|
||||
// updateCache updates cached Oracle values if they've been changed
|
||||
func (o *Oracle) updateCache(d dao.DAO) error {
|
||||
orc, _ := o.Module.Load().(services.Oracle)
|
||||
if orc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reqs := o.newRequests
|
||||
o.newRequests = make(map[uint64]*state.OracleRequest)
|
||||
for id := range reqs {
|
||||
key := makeRequestKey(id)
|
||||
if si := d.GetStorageItem(o.ContractID, key); si == nil { // tx has failed
|
||||
delete(reqs, id)
|
||||
}
|
||||
}
|
||||
orc.AddRequests(reqs)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue