core: set native script and hash in SetOracle

This commit is contained in:
Evgeniy Stratonikov 2021-02-15 17:06:00 +03:00
parent f9f1fe03b2
commit 61ce4a7f79
5 changed files with 42 additions and 16 deletions

View file

@ -197,7 +197,14 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
// SetOracle sets oracle module. It doesn't protected by mutex and // SetOracle sets oracle module. It doesn't protected by mutex and
// must be called before `bc.Run()` to avoid data race. // must be called before `bc.Run()` to avoid data race.
func (bc *Blockchain) SetOracle(mod services.Oracle) { func (bc *Blockchain) SetOracle(mod services.Oracle) {
bc.contracts.Oracle.Module.Store(mod) orc := bc.contracts.Oracle
md, ok := orc.GetMethod(manifest.MethodVerify, -1)
if !ok {
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
}
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
orc.Hash, md.MD.Offset)
orc.Module.Store(mod)
bc.contracts.Designate.OracleService.Store(mod) bc.contracts.Designate.OracleService.Store(mod)
} }

View file

@ -3,6 +3,7 @@ package services
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
) )
// Oracle specifies oracle service interface. // Oracle specifies oracle service interface.
@ -13,6 +14,8 @@ type Oracle interface {
RemoveRequests([]uint64) RemoveRequests([]uint64)
// UpdateOracleNodes updates oracle nodes. // UpdateOracleNodes updates oracle nodes.
UpdateOracleNodes(keys.PublicKeys) UpdateOracleNodes(keys.PublicKeys)
// UpdateNativeContract updates oracle contract native script and hash.
UpdateNativeContract([]byte, []byte, util.Uint160, int)
// Run runs oracle module. Must be invoked in a separate goroutine. // Run runs oracle module. Must be invoked in a separate goroutine.
Run() Run()
// Shutdown shutdowns oracle module. // Shutdown shutdowns oracle module.

View file

@ -30,7 +30,6 @@ import (
const oracleModulePath = "../services/oracle/" const oracleModulePath = "../services/oracle/"
func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string) oracle.Config { func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string) oracle.Config {
m := bc.contracts.Oracle.Manifest.ABI.GetMethod(manifest.MethodVerify, 0)
return oracle.Config{ return oracle.Config{
Log: zaptest.NewLogger(t), Log: zaptest.NewLogger(t),
Network: netmode.UnitTestNet, Network: netmode.UnitTestNet,
@ -41,12 +40,8 @@ func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string) oracle.Config
Password: pass, Password: pass,
}, },
}, },
Chain: bc, Chain: bc,
Client: newDefaultHTTPClient(), Client: newDefaultHTTPClient(),
OracleScript: bc.contracts.Oracle.NEF.Script,
OracleResponse: bc.contracts.Oracle.GetOracleResponseScript(),
OracleHash: bc.contracts.Oracle.Hash,
VerifyOffset: m.Offset,
} }
} }
@ -100,6 +95,7 @@ func TestCreateResponseTx(t *testing.T) {
} }
require.NoError(t, bc.contracts.Oracle.PutRequestInternal(1, req, bc.dao)) require.NoError(t, bc.contracts.Oracle.PutRequestInternal(1, req, bc.dao))
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()}) orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
bc.SetOracle(orc)
tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp) tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 167, tx.Size()) assert.Equal(t, 167, tx.Size())
@ -130,6 +126,12 @@ func TestOracle(t *testing.T) {
orc1.UpdateOracleNodes(oracleNodes.Copy()) orc1.UpdateOracleNodes(oracleNodes.Copy())
orc2.UpdateOracleNodes(oracleNodes.Copy()) orc2.UpdateOracleNodes(oracleNodes.Copy())
orcNative := bc.contracts.Oracle
md, ok := orcNative.GetMethod(manifest.MethodVerify, -1)
require.True(t, ok)
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
cs := getOracleContractState(bc.contracts.Oracle.Hash) cs := getOracleContractState(bc.contracts.Oracle.Hash)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))

View file

@ -24,6 +24,12 @@ type (
Oracle struct { Oracle struct {
Config Config
// This fields are readonly thus not protected by mutex.
oracleHash util.Uint160
oracleResponse []byte
oracleScript []byte
verifyOffset int
// mtx protects setting callbacks. // mtx protects setting callbacks.
mtx sync.RWMutex mtx sync.RWMutex
@ -56,10 +62,6 @@ type (
ResponseHandler Broadcaster ResponseHandler Broadcaster
OnTransaction TxCallback OnTransaction TxCallback
URIValidator URIValidator URIValidator URIValidator
OracleScript []byte
OracleResponse []byte
VerifyOffset int
OracleHash util.Uint160
} }
// HTTPClient is an interface capable of doing oracle requests. // HTTPClient is an interface capable of doing oracle requests.
@ -203,6 +205,18 @@ func (o *Oracle) Run() {
} }
} }
// UpdateNativeContract updates native oracle contract info for tx verification.
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
o.oracleScript = make([]byte, len(script))
copy(o.oracleScript, script)
o.oracleResponse = make([]byte, len(resp))
copy(o.oracleResponse, resp)
o.oracleHash = h
o.verifyOffset = verifyOffset
}
func (o *Oracle) getOnTransaction() TxCallback { func (o *Oracle) getOnTransaction() TxCallback {
o.mtx.RLock() o.mtx.RLock()
defer o.mtx.RUnlock() defer o.mtx.RUnlock()

View file

@ -82,7 +82,7 @@ func readResponse(rc gio.ReadCloser, limit int) ([]byte, error) {
// CreateResponseTx creates unsigned oracle response transaction. // CreateResponseTx creates unsigned oracle response transaction.
func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) { func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) {
tx := transaction.New(o.Network, o.OracleResponse, 0) tx := transaction.New(o.Network, o.oracleResponse, 0)
tx.Nonce = uint32(resp.ID) tx.Nonce = uint32(resp.ID)
tx.ValidUntilBlock = height + transaction.MaxValidUntilBlockIncrement tx.ValidUntilBlock = height + transaction.MaxValidUntilBlockIncrement
tx.Attributes = []transaction.Attribute{{ tx.Attributes = []transaction.Attribute{{
@ -93,7 +93,7 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *tra
oracleSignContract := o.getOracleSignContract() oracleSignContract := o.getOracleSignContract()
tx.Signers = []transaction.Signer{ tx.Signers = []transaction.Signer{
{ {
Account: o.OracleHash, Account: o.oracleHash,
Scopes: transaction.None, Scopes: transaction.None,
}, },
{ {
@ -136,8 +136,8 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *tra
func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) { func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) {
v := o.Chain.GetTestVM(trigger.Verification, tx, nil) v := o.Chain.GetTestVM(trigger.Verification, tx, nil)
v.GasLimit = o.Chain.GetPolicer().GetMaxVerificationGAS() v.GasLimit = o.Chain.GetPolicer().GetMaxVerificationGAS()
v.LoadScriptWithHash(o.OracleScript, o.OracleHash, callflag.ReadOnly) v.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
v.Jump(v.Context(), o.VerifyOffset) v.Jump(v.Context(), o.verifyOffset)
ok := isVerifyOk(v) ok := isVerifyOk(v)
return v.GasConsumed(), ok return v.GasConsumed(), ok