native: make Oracle service handle native Oracle updates
A part of #3213. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
ec6fc54bc6
commit
235f4398c6
14 changed files with 55 additions and 33 deletions
|
@ -171,7 +171,7 @@ type HFSpecificEvent struct {
|
||||||
type Contract interface {
|
type Contract interface {
|
||||||
// Initialize performs native contract initialization on contract deploy or update.
|
// Initialize performs native contract initialization on contract deploy or update.
|
||||||
// Active hardfork is passed as the second argument.
|
// Active hardfork is passed as the second argument.
|
||||||
Initialize(*Context, *config.Hardfork) error
|
Initialize(*Context, *config.Hardfork, *HFSpecificContractMD) error
|
||||||
// ActiveIn returns the hardfork native contract is active starting from or nil in case
|
// ActiveIn returns the hardfork native contract is active starting from or nil in case
|
||||||
// it's always active.
|
// it's always active.
|
||||||
ActiveIn() *config.Hardfork
|
ActiveIn() *config.Hardfork
|
||||||
|
|
|
@ -310,7 +310,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
||||||
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
||||||
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
||||||
// data in the storage.
|
// data in the storage.
|
||||||
func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != s.ActiveIn() {
|
if hf != s.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -627,7 +627,8 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
md := native.Metadata()
|
md := native.Metadata()
|
||||||
base := md.HFSpecificContractMD(&latestHF).ContractBase
|
hfSpecificMD := md.HFSpecificContractMD(&latestHF)
|
||||||
|
base := hfSpecificMD.ContractBase
|
||||||
var cs *state.Contract
|
var cs *state.Contract
|
||||||
switch {
|
switch {
|
||||||
case isDeploy:
|
case isDeploy:
|
||||||
|
@ -653,7 +654,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to put contract state: %w", err)
|
return fmt.Errorf("failed to put contract state: %w", err)
|
||||||
}
|
}
|
||||||
if err := native.Initialize(ic, activeIn); err != nil {
|
if err := native.Initialize(ic, activeIn, hfSpecificMD); err != nil {
|
||||||
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
|
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
|
||||||
}
|
}
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
|
@ -728,7 +729,7 @@ func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (m *Management) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (m *Management) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != m.ActiveIn() {
|
if hf != m.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
ic := &interop.Context{DAO: d}
|
ic := &interop.Context{DAO: d}
|
||||||
err := mgmt.Initialize(ic, nil)
|
err := mgmt.Initialize(ic, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
script := []byte{byte(opcode.RET)}
|
script := []byte{byte(opcode.RET)}
|
||||||
sender := util.Uint160{1, 2, 3}
|
sender := util.Uint160{1, 2, 3}
|
||||||
ne, err := nef.NewFile(script)
|
ne, err := nef.NewFile(script)
|
||||||
|
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
mgmt := newManagement()
|
mgmt := newManagement()
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d}, nil)
|
err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
err = mgmt.InitializeCache(0, d)
|
err = mgmt.InitializeCache(0, d)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a GAS contract.
|
// Initialize initializes a GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != g.ActiveIn() {
|
if hf != g.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,7 +286,7 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a NEO contract.
|
// Initialize initializes a NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != n.ActiveIn() {
|
if hf != n.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ func (n *Notary) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes Notary native contract and implements the Contract interface.
|
// Initialize initializes Notary native contract and implements the Contract interface.
|
||||||
func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != n.ActiveIn() {
|
if hf != n.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,18 +246,28 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes an Oracle contract.
|
// Initialize initializes an Oracle contract.
|
||||||
func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != o.ActiveIn() {
|
switch hf {
|
||||||
return nil
|
case o.ActiveIn():
|
||||||
|
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
||||||
|
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
||||||
|
|
||||||
|
cache := &OracleCache{
|
||||||
|
requestPrice: int64(DefaultOracleRequestPrice),
|
||||||
|
}
|
||||||
|
ic.DAO.SetCache(o.ID, cache)
|
||||||
|
default:
|
||||||
|
orc, _ := o.Module.Load().(*OracleService)
|
||||||
|
if orc != nil && *orc != nil {
|
||||||
|
md, ok := newMD.GetMethod(manifest.MethodVerify, -1)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
||||||
|
}
|
||||||
|
(*orc).UpdateNativeContract(newMD.NEF.Script, o.GetOracleResponseScript(),
|
||||||
|
o.Hash, md.MD.Offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
|
||||||
|
|
||||||
cache := &OracleCache{
|
|
||||||
requestPrice: int64(DefaultOracleRequestPrice),
|
|
||||||
}
|
|
||||||
ic.DAO.SetCache(o.ID, cache)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ func (p *Policy) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes Policy native contract and implements the Contract interface.
|
// Initialize initializes Policy native contract and implements the Contract interface.
|
||||||
func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
if hf != p.ActiveIn() {
|
if hf != p.ActiveIn() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -439,7 +439,7 @@ func (s *Std) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,9 @@ type (
|
||||||
Oracle struct {
|
Oracle struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
// This fields are readonly thus not protected by mutex.
|
// Native Oracle contract related information that may be updated on Oracle contract
|
||||||
oracleHash util.Uint160
|
// update.
|
||||||
|
oracleInfoLock sync.RWMutex
|
||||||
oracleResponse []byte
|
oracleResponse []byte
|
||||||
oracleScript []byte
|
oracleScript []byte
|
||||||
verifyOffset int
|
verifyOffset int
|
||||||
|
@ -277,10 +278,11 @@ drain:
|
||||||
|
|
||||||
// UpdateNativeContract updates native oracle contract info for tx verification.
|
// UpdateNativeContract updates native oracle contract info for tx verification.
|
||||||
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
|
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
|
||||||
|
o.oracleInfoLock.Lock()
|
||||||
|
defer o.oracleInfoLock.Unlock()
|
||||||
|
|
||||||
o.oracleScript = bytes.Clone(script)
|
o.oracleScript = bytes.Clone(script)
|
||||||
o.oracleResponse = bytes.Clone(resp)
|
o.oracleResponse = bytes.Clone(resp)
|
||||||
|
|
||||||
o.oracleHash = h
|
|
||||||
o.verifyOffset = verifyOffset
|
o.verifyOffset = verifyOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -102,7 +103,12 @@ func checkUTF8(v []byte) ([]byte, error) {
|
||||||
|
|
||||||
// CreateResponseTx creates an unsigned oracle response transaction.
|
// CreateResponseTx creates an unsigned oracle response transaction.
|
||||||
func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) {
|
func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) {
|
||||||
tx := transaction.New(o.oracleResponse, 0)
|
var respScript []byte
|
||||||
|
o.oracleInfoLock.RLock()
|
||||||
|
respScript = o.oracleResponse
|
||||||
|
o.oracleInfoLock.RUnlock()
|
||||||
|
|
||||||
|
tx := transaction.New(respScript, 0)
|
||||||
tx.Nonce = uint32(resp.ID)
|
tx.Nonce = uint32(resp.ID)
|
||||||
tx.ValidUntilBlock = vub
|
tx.ValidUntilBlock = vub
|
||||||
tx.Attributes = []transaction.Attribute{{
|
tx.Attributes = []transaction.Attribute{{
|
||||||
|
@ -113,7 +119,7 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
|
||||||
oracleSignContract := o.getOracleSignContract()
|
oracleSignContract := o.getOracleSignContract()
|
||||||
tx.Signers = []transaction.Signer{
|
tx.Signers = []transaction.Signer{
|
||||||
{
|
{
|
||||||
Account: o.oracleHash,
|
Account: nativehashes.Oracle,
|
||||||
Scopes: transaction.None,
|
Scopes: transaction.None,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -166,8 +172,11 @@ func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool, error) {
|
||||||
return 0, false, fmt.Errorf("failed to create test VM: %w", err)
|
return 0, false, fmt.Errorf("failed to create test VM: %w", err)
|
||||||
}
|
}
|
||||||
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
|
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
|
||||||
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
|
|
||||||
|
o.oracleInfoLock.RLock()
|
||||||
|
ic.VM.LoadScriptWithHash(o.oracleScript, nativehashes.Oracle, callflag.ReadOnly)
|
||||||
ic.VM.Context().Jump(o.verifyOffset)
|
ic.VM.Context().Jump(o.verifyOffset)
|
||||||
|
o.oracleInfoLock.RUnlock()
|
||||||
|
|
||||||
ok := isVerifyOk(ic)
|
ok := isVerifyOk(ic)
|
||||||
return ic.VM.GasConsumed(), ok, nil
|
return ic.VM.GasConsumed(), ok, nil
|
||||||
|
|
Loading…
Reference in a new issue