forked from TrueCloudLab/neoneo-go
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 {
|
||||
// Initialize performs native contract initialization on contract deploy or update.
|
||||
// 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
|
||||
// it's always active.
|
||||
ActiveIn() *config.Hardfork
|
||||
|
|
|
@ -310,7 +310,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -627,7 +627,8 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
|||
continue
|
||||
}
|
||||
md := native.Metadata()
|
||||
base := md.HFSpecificContractMD(&latestHF).ContractBase
|
||||
hfSpecificMD := md.HFSpecificContractMD(&latestHF)
|
||||
base := hfSpecificMD.ContractBase
|
||||
var cs *state.Contract
|
||||
switch {
|
||||
case isDeploy:
|
||||
|
@ -653,7 +654,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
|||
if err != nil {
|
||||
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)
|
||||
}
|
||||
if cache == nil {
|
||||
|
@ -728,7 +729,7 @@ func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
|||
mgmt.Policy = newPolicy(false)
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||
ic := &interop.Context{DAO: d}
|
||||
err := mgmt.Initialize(ic, nil)
|
||||
err := mgmt.Initialize(ic, nil, nil)
|
||||
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)}
|
||||
sender := util.Uint160{1, 2, 3}
|
||||
ne, err := nef.NewFile(script)
|
||||
|
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
|||
mgmt := newManagement()
|
||||
mgmt.Policy = newPolicy(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, 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)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ func (n *Notary) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -246,18 +246,28 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// Initialize initializes an Oracle contract.
|
||||
func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork) error {
|
||||
if hf != o.ActiveIn() {
|
||||
return nil
|
||||
func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||
switch hf {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ func (p *Policy) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -439,7 +439,7 @@ func (s *Std) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,9 @@ type (
|
|||
Oracle struct {
|
||||
Config
|
||||
|
||||
// This fields are readonly thus not protected by mutex.
|
||||
oracleHash util.Uint160
|
||||
// Native Oracle contract related information that may be updated on Oracle contract
|
||||
// update.
|
||||
oracleInfoLock sync.RWMutex
|
||||
oracleResponse []byte
|
||||
oracleScript []byte
|
||||
verifyOffset int
|
||||
|
@ -277,10 +278,11 @@ drain:
|
|||
|
||||
// UpdateNativeContract updates native oracle contract info for tx verification.
|
||||
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.oracleResponse = bytes.Clone(resp)
|
||||
|
||||
o.oracleHash = h
|
||||
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/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/crypto/hash"
|
||||
"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.
|
||||
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.ValidUntilBlock = vub
|
||||
tx.Attributes = []transaction.Attribute{{
|
||||
|
@ -113,7 +119,7 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
|
|||
oracleSignContract := o.getOracleSignContract()
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: o.oracleHash,
|
||||
Account: nativehashes.Oracle,
|
||||
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)
|
||||
}
|
||||
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)
|
||||
o.oracleInfoLock.RUnlock()
|
||||
|
||||
ok := isVerifyOk(ic)
|
||||
return ic.VM.GasConsumed(), ok, nil
|
||||
|
|
Loading…
Reference in a new issue