Skip to content

Commit

Permalink
native: make Oracle service handle native Oracle updates
Browse files Browse the repository at this point in the history
A part of #3213.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
  • Loading branch information
AnnaShaleva committed Apr 18, 2024
1 parent b7e247b commit c337b00
Show file tree
Hide file tree
Showing 14 changed files with 56 additions and 34 deletions.
2 changes: 1 addition & 1 deletion pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/designate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
9 changes: 5 additions & 4 deletions pkg/core/native/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,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:
Expand All @@ -656,14 +657,14 @@ 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 {
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
}
updateContractCache(cache, cs)

ntfName := contractDeployNotificationName
if isUpdate {
ntfName = contractUpdateNotificationName
Expand Down Expand Up @@ -731,7 +732,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
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/core/native/management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/native_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/notary.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
30 changes: 20 additions & 10 deletions pkg/core/native/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/std.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
10 changes: 6 additions & 4 deletions pkg/services/oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
15 changes: 12 additions & 3 deletions pkg/services/oracle/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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{{
Expand All @@ -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,
},
{
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c337b00

Please sign in to comment.