Merge pull request #1744 from nspcc-dev/fix/nativecall
Update `CallNative` to post-preview5
This commit is contained in:
commit
c9212f29e6
34 changed files with 165 additions and 113 deletions
BIN
cli/testdata/chain50x2.acc
vendored
BIN
cli/testdata/chain50x2.acc
vendored
Binary file not shown.
|
@ -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
|
||||
// must be called before `bc.Run()` to avoid data race.
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1753,7 +1760,6 @@ var (
|
|||
|
||||
// initVerificationVM initializes VM for witness check.
|
||||
func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error {
|
||||
isNative := false
|
||||
v := ic.VM
|
||||
if len(witness.VerificationScript) != 0 {
|
||||
if witness.ScriptHash() != hash {
|
||||
|
@ -1781,8 +1787,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
|||
v.Context().NEF = &cs.NEF
|
||||
v.Jump(v.Context(), md.Offset)
|
||||
|
||||
isNative = cs.ID <= 0
|
||||
if !isNative && initMD != nil {
|
||||
if initMD != nil {
|
||||
v.Call(v.Context(), initMD.Offset)
|
||||
}
|
||||
}
|
||||
|
@ -1792,14 +1797,6 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
|||
return fmt.Errorf("%w: %v", ErrInvalidInvocation, err)
|
||||
}
|
||||
v.LoadScript(witness.InvocationScript)
|
||||
if isNative {
|
||||
if err := v.StepOut(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if isNative {
|
||||
v.Estack().PushVal(manifest.MethodVerify)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"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"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
|
@ -586,7 +587,7 @@ func TestVerifyTx(t *testing.T) {
|
|||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||
emit.Bytes(w.BinWriter, util.Uint160{}.BytesBE())
|
||||
emit.Int(w.BinWriter, int64(orc.NEF.Checksum))
|
||||
emit.Int(w.BinWriter, 0)
|
||||
emit.String(w.BinWriter, orc.Manifest.Name)
|
||||
tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes()
|
||||
err := bc.VerifyTx(tx)
|
||||
|
@ -1000,7 +1001,7 @@ func TestVerifyTx(t *testing.T) {
|
|||
transaction.NotaryServiceFeePerKey + // fee for Notary attribute
|
||||
fee.Opcode(bc.GetBaseExecFee(), // Notary verification script
|
||||
opcode.PUSHDATA1, opcode.RET, // invocation script
|
||||
opcode.PUSHINT8, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call
|
||||
opcode.PUSH0, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call
|
||||
native.NotaryVerificationPrice // Notary witness verification price
|
||||
tx.Scripts = []transaction.Witness{
|
||||
{
|
||||
|
@ -1197,7 +1198,8 @@ func TestIsTxStillRelevant(t *testing.T) {
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
)
|
||||
func Verify() bool {
|
||||
currentHeight := contract.Call(util.FromAddress("NV5WuMGkwhQexQ4afTwuRojMeWwfWrEAdv"), "currentIndex", contract.ReadStates)
|
||||
addr := util.FromAddress("`+address.Uint160ToString(bc.contracts.Ledger.Hash)+`")
|
||||
currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates)
|
||||
return currentHeight.(int) < %d
|
||||
}`, bc.BlockHeight()+2) // deploy + next block
|
||||
txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src))
|
||||
|
|
|
@ -3,6 +3,7 @@ package services
|
|||
import (
|
||||
"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/util"
|
||||
)
|
||||
|
||||
// Oracle specifies oracle service interface.
|
||||
|
@ -13,6 +14,8 @@ type Oracle interface {
|
|||
RemoveRequests([]uint64)
|
||||
// UpdateOracleNodes updates oracle nodes.
|
||||
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()
|
||||
// Shutdown shutdowns oracle module.
|
||||
|
|
|
@ -4,19 +4,24 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -86,6 +91,7 @@ type MethodAndPrice struct {
|
|||
Func Method
|
||||
MD *manifest.Method
|
||||
Price int64
|
||||
SyscallOffset int
|
||||
RequiredFlags callflag.CallFlag
|
||||
}
|
||||
|
||||
|
@ -101,21 +107,12 @@ type Contract interface {
|
|||
type ContractMD struct {
|
||||
state.NativeContract
|
||||
Name string
|
||||
Methods map[MethodAndArgCount]MethodAndPrice
|
||||
}
|
||||
|
||||
// MethodAndArgCount represents method's signature.
|
||||
type MethodAndArgCount struct {
|
||||
Name string
|
||||
ArgCount int
|
||||
Methods []MethodAndPrice
|
||||
}
|
||||
|
||||
// NewContractMD returns Contract with the specified list of methods.
|
||||
func NewContractMD(name string, id int32) *ContractMD {
|
||||
c := &ContractMD{
|
||||
Name: name,
|
||||
Methods: make(map[MethodAndArgCount]MethodAndPrice),
|
||||
}
|
||||
c := &ContractMD{Name: name}
|
||||
|
||||
c.ID = id
|
||||
|
||||
|
@ -123,14 +120,32 @@ func NewContractMD(name string, id int32) *ContractMD {
|
|||
// Therefore values are taken from C# node.
|
||||
c.NEF.Header.Compiler = "neo-core-v3.0"
|
||||
c.NEF.Header.Magic = nef.Magic
|
||||
c.NEF.Script = state.CreateNativeContractScript(id)
|
||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
||||
c.Hash = state.CreateContractHash(util.Uint160{}, c.NEF.Checksum, name)
|
||||
c.Hash = state.CreateContractHash(util.Uint160{}, 0, c.Name)
|
||||
c.Manifest = *manifest.DefaultManifest(name)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// UpdateHash creates native contract script and updates hash.
|
||||
func (c *ContractMD) UpdateHash() {
|
||||
w := io.NewBufBinWriter()
|
||||
for i := range c.Methods {
|
||||
offset := w.Len()
|
||||
c.Methods[i].MD.Offset = offset
|
||||
c.Manifest.ABI.Methods[i].Offset = offset
|
||||
emit.Int(w.BinWriter, 0)
|
||||
c.Methods[i].SyscallOffset = w.Len()
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||
}
|
||||
if w.Err != nil {
|
||||
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
||||
}
|
||||
|
||||
c.NEF.Script = w.Bytes()
|
||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
||||
}
|
||||
|
||||
// AddMethod adds new method to a native contract.
|
||||
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
||||
md.MD = desc
|
||||
|
@ -147,21 +162,42 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
|||
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
|
||||
c.Manifest.ABI.Methods[index] = *desc
|
||||
|
||||
key := MethodAndArgCount{
|
||||
Name: desc.Name,
|
||||
ArgCount: len(desc.Parameters),
|
||||
// Cache follows the same order.
|
||||
c.Methods = append(c.Methods, MethodAndPrice{})
|
||||
copy(c.Methods[index+1:], c.Methods[index:])
|
||||
c.Methods[index] = *md
|
||||
}
|
||||
|
||||
// GetMethodByOffset returns with the provided offset.
|
||||
// Offset is offset of `System.Contract.CallNative` syscall.
|
||||
func (c *ContractMD) GetMethodByOffset(offset int) (MethodAndPrice, bool) {
|
||||
for k := range c.Methods {
|
||||
if c.Methods[k].SyscallOffset == offset {
|
||||
return c.Methods[k], true
|
||||
}
|
||||
}
|
||||
c.Methods[key] = *md
|
||||
return MethodAndPrice{}, false
|
||||
}
|
||||
|
||||
// GetMethod returns method `name` with specified number of parameters.
|
||||
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
|
||||
key := MethodAndArgCount{
|
||||
Name: name,
|
||||
ArgCount: paramCount,
|
||||
index := sort.Search(len(c.Methods), func(i int) bool {
|
||||
md := c.Methods[i]
|
||||
res := strings.Compare(name, md.MD.Name)
|
||||
switch res {
|
||||
case -1, 1:
|
||||
return res == -1
|
||||
default:
|
||||
return paramCount <= len(md.MD.Parameters)
|
||||
}
|
||||
})
|
||||
if index < len(c.Methods) {
|
||||
md := c.Methods[index]
|
||||
if md.MD.Name == name && (paramCount == -1 || len(md.MD.Parameters) == paramCount) {
|
||||
return md, true
|
||||
}
|
||||
}
|
||||
mp, ok := c.Methods[key]
|
||||
return mp, ok
|
||||
return MethodAndPrice{}, false
|
||||
}
|
||||
|
||||
// AddEvent adds new event to a native contract.
|
||||
|
|
|
@ -98,24 +98,13 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
|
|||
}
|
||||
|
||||
ic.VM.Invocations[cs.Hash]++
|
||||
ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, true, uint16(len(args)))
|
||||
ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, hasReturn, uint16(len(args)))
|
||||
ic.VM.Context().NEF = &cs.NEF
|
||||
var isNative bool
|
||||
for i := range ic.Natives {
|
||||
if ic.Natives[i].Metadata().Hash.Equals(cs.Hash) {
|
||||
isNative = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
ic.VM.Estack().PushVal(args[i])
|
||||
}
|
||||
if isNative {
|
||||
ic.VM.Estack().PushVal(name)
|
||||
} else {
|
||||
// use Jump not Call here because context was loaded in LoadScript above.
|
||||
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
||||
}
|
||||
// use Jump not Call here because context was loaded in LoadScript above.
|
||||
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
||||
if hasReturn {
|
||||
ic.VM.Context().RetCount = 1
|
||||
} else {
|
||||
|
|
|
@ -12,8 +12,8 @@ func TestNamesASCII(t *testing.T) {
|
|||
cs := NewContracts(true)
|
||||
for _, c := range cs.Contracts {
|
||||
require.True(t, isASCII(c.Metadata().Name))
|
||||
for m := range c.Metadata().Methods {
|
||||
require.True(t, isASCII(m.Name))
|
||||
for _, m := range c.Metadata().Methods {
|
||||
require.True(t, isASCII(m.MD.Name))
|
||||
}
|
||||
for _, e := range c.Metadata().Manifest.ABI.Events {
|
||||
require.True(t, isASCII(e.Name))
|
||||
|
|
|
@ -81,6 +81,7 @@ func (s *Designate) isValidRole(r Role) bool {
|
|||
func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
||||
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
||||
defer s.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
||||
manifest.NewParameter("role", smartcontract.IntegerType),
|
||||
|
|
|
@ -12,28 +12,27 @@ import (
|
|||
|
||||
// Call calls specified native contract method.
|
||||
func Call(ic *interop.Context) error {
|
||||
id := int32(ic.VM.Estack().Pop().BigInt().Int64())
|
||||
version := ic.VM.Estack().Pop().BigInt().Int64()
|
||||
if version != 0 {
|
||||
return fmt.Errorf("native contract of version %d is not active", version)
|
||||
}
|
||||
var c interop.Contract
|
||||
for _, ctr := range ic.Natives {
|
||||
if ctr.Metadata().ID == id {
|
||||
if ctr.Metadata().Hash == ic.VM.GetCurrentScriptHash() {
|
||||
c = ctr
|
||||
break
|
||||
}
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("native contract %d not found", id)
|
||||
return fmt.Errorf("native contract %d not found", version)
|
||||
}
|
||||
h := ic.VM.GetCurrentScriptHash()
|
||||
if !h.Equals(c.Metadata().Hash) {
|
||||
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
|
||||
}
|
||||
operation := ic.VM.Estack().Pop().String()
|
||||
m, ok := c.Metadata().GetMethod(operation, ic.VM.Estack().Len())
|
||||
m, ok := c.Metadata().GetMethodByOffset(ic.VM.Context().IP())
|
||||
if !ok {
|
||||
return fmt.Errorf("method %s not found", operation)
|
||||
return fmt.Errorf("method not found")
|
||||
}
|
||||
if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) {
|
||||
return fmt.Errorf("missing call flags for native %d `%s` operation call: %05b vs %05b", id, operation, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
|
||||
return fmt.Errorf("missing call flags for native %d `%s` operation call: %05b vs %05b",
|
||||
version, m.MD.Name, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
|
||||
}
|
||||
// Native contract prices are not multiplied by `BaseExecFee`.
|
||||
if !ic.VM.AddGas(m.Price) {
|
||||
|
|
|
@ -39,6 +39,8 @@ func newLedger() *Ledger {
|
|||
var l = &Ledger{
|
||||
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
||||
}
|
||||
defer l.UpdateHash()
|
||||
|
||||
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
||||
md := newMethodAndPrice(l.currentHash, 1000000, callflag.ReadStates)
|
||||
l.AddMethod(md, desc)
|
||||
|
|
|
@ -65,6 +65,7 @@ func newManagement() *Management {
|
|||
ContractMD: *interop.NewContractMD(nativenames.Management, managementContractID),
|
||||
contracts: make(map[util.Uint160]*state.Contract),
|
||||
}
|
||||
defer m.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
||||
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
||||
|
|
|
@ -100,6 +100,7 @@ func newNameService() *NameService {
|
|||
}
|
||||
|
||||
n := &NameService{nonfungible: *nf}
|
||||
defer n.UpdateHash()
|
||||
|
||||
desc := newDescriptor("addRoot", smartcontract.VoidType,
|
||||
manifest.NewParameter("root", smartcontract.StringType))
|
||||
|
|
|
@ -27,6 +27,8 @@ const initialGAS = 30000000
|
|||
// newGAS returns GAS native contract.
|
||||
func newGAS() *GAS {
|
||||
g := &GAS{}
|
||||
defer g.UpdateHash()
|
||||
|
||||
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
||||
nep17.symbol = "GAS"
|
||||
nep17.decimals = 8
|
||||
|
|
|
@ -94,6 +94,8 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
|||
// newNEO returns NEO native contract.
|
||||
func newNEO() *NEO {
|
||||
n := &NEO{}
|
||||
defer n.UpdateHash()
|
||||
|
||||
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
||||
nep17.symbol = "NEO"
|
||||
nep17.decimals = 0
|
||||
|
|
|
@ -54,6 +54,7 @@ var maxNotValidBeforeDeltaKey = []byte{10}
|
|||
// newNotary returns Notary native contract.
|
||||
func newNotary() *Notary {
|
||||
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
||||
defer n.UpdateHash()
|
||||
|
||||
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||
|
|
|
@ -75,6 +75,7 @@ var (
|
|||
|
||||
func newOracle() *Oracle {
|
||||
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)}
|
||||
defer o.UpdateHash()
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0)
|
||||
|
|
|
@ -86,6 +86,7 @@ var _ interop.Contract = (*Policy)(nil)
|
|||
// newPolicy returns Policy native contract.
|
||||
func newPolicy() *Policy {
|
||||
p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)}
|
||||
defer p.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType)
|
||||
md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, callflag.ReadStates)
|
||||
|
|
|
@ -10,7 +10,6 @@ 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/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -64,6 +63,8 @@ func newTestNative() *testNative {
|
|||
meta: *interop.NewContractMD("Test.Native.Sum", 0),
|
||||
blocks: make(chan uint32, 1),
|
||||
}
|
||||
defer tn.meta.UpdateHash()
|
||||
|
||||
desc := &manifest.Method{
|
||||
Name: "sum",
|
||||
Parameters: []manifest.Parameter{
|
||||
|
@ -220,27 +221,33 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
|||
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, chain.config.StateRootInHeader)
|
||||
ic := chain.newInteropContext(trigger.Application, d, nil, nil)
|
||||
v := ic.SpawnVM()
|
||||
|
||||
sumOffset := 0
|
||||
for _, md := range tn.Metadata().Methods {
|
||||
if md.MD.Name == "sum" {
|
||||
sumOffset = md.MD.Offset
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
||||
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, callflag.All)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, util.Uint160{1, 2, 3}, callflag.All)
|
||||
v.Estack().PushVal(14)
|
||||
v.Estack().PushVal(28)
|
||||
v.Estack().PushVal("sum")
|
||||
v.Estack().PushVal(tn.Metadata().Name)
|
||||
v.Jump(v.Context(), sumOffset)
|
||||
|
||||
// it's prohibited to call natives directly
|
||||
require.Error(t, native.Call(ic))
|
||||
require.Error(t, v.Run())
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
v.LoadScriptWithHash([]byte{1}, tn.Metadata().Hash, callflag.All)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
|
||||
v.Estack().PushVal(14)
|
||||
v.Estack().PushVal(28)
|
||||
v.Estack().PushVal("sum")
|
||||
v.Estack().PushVal(tn.Metadata().ID)
|
||||
|
||||
require.NoError(t, native.Call(ic))
|
||||
v.Jump(v.Context(), sumOffset)
|
||||
require.NoError(t, v.Run())
|
||||
|
||||
value := v.Estack().Pop().BigInt()
|
||||
require.Equal(t, int64(42), value.Int64())
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -39,11 +40,8 @@ func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string) oracle.Config
|
|||
Password: pass,
|
||||
},
|
||||
},
|
||||
Chain: bc,
|
||||
Client: newDefaultHTTPClient(),
|
||||
OracleScript: bc.contracts.Oracle.NEF.Script,
|
||||
OracleResponse: bc.contracts.Oracle.GetOracleResponseScript(),
|
||||
OracleHash: bc.contracts.Oracle.Hash,
|
||||
Chain: bc,
|
||||
Client: newDefaultHTTPClient(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +94,7 @@ func TestCreateResponseTx(t *testing.T) {
|
|||
}
|
||||
require.NoError(t, bc.contracts.Oracle.PutRequestInternal(1, req, bc.dao))
|
||||
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
|
||||
bc.SetOracle(orc)
|
||||
tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 167, tx.Size())
|
||||
|
@ -125,6 +124,12 @@ func TestOracle(t *testing.T) {
|
|||
orc1.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)
|
||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
|
@ -135,14 +134,3 @@ func CreateContractHash(sender util.Uint160, checksum uint32, name string) util.
|
|||
}
|
||||
return hash.Hash160(w.Bytes())
|
||||
}
|
||||
|
||||
// CreateNativeContractScript returns script for the native contract.
|
||||
func CreateNativeContractScript(id int32) []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Int(w.BinWriter, int64(id))
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||
if w.Err != nil {
|
||||
panic(w.Err)
|
||||
}
|
||||
return w.Bytes()
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents GAS contract hash.
|
||||
const Hash = "\x28\xb3\xad\xab\x72\x69\xf9\xc2\x18\x1d\xb3\xcb\x74\x1e\xbf\x55\x19\x30\xe2\x70"
|
||||
const Hash = "\xcf\x76\xe2\x8b\xd0\x06\x2c\x4a\x47\x8e\xe3\x55\x61\x01\x13\x19\xf3\xcf\xa4\xd2"
|
||||
|
||||
// Symbol represents `symbol` method of GAS native contract.
|
||||
func Symbol() string {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents Ledger contract hash.
|
||||
const Hash = "\x64\x87\x5a\x12\xc6\x03\xc6\x1d\xec\xff\xdf\xe7\x88\xce\x10\xdd\xc6\x69\x1d\x97"
|
||||
const Hash = "\xbe\xf2\x04\x31\x40\x36\x2a\x77\xc1\x50\x99\xc7\xe6\x4c\x12\xf7\x00\xb6\x65\xda"
|
||||
|
||||
// CurrentHash represents `currentHash` method of Ledger native contract.
|
||||
func CurrentHash() interop.Hash256 {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents Management contract hash.
|
||||
const Hash = "\x43\x0e\x9f\x6f\xb3\x13\xa8\xd3\xa2\xb7\x61\x3b\x67\x83\x09\xd1\xd7\xd7\x01\xa5"
|
||||
const Hash = "\xfd\xa3\xfa\x43\x46\xea\x53\x2a\x25\x8f\xc4\x97\xdd\xad\xdb\x64\x37\xc9\xfd\xff"
|
||||
|
||||
// Deploy represents `deploy` method of Management native contract.
|
||||
func Deploy(script, manifest []byte) *Contract {
|
||||
|
|
|
@ -18,7 +18,7 @@ const (
|
|||
)
|
||||
|
||||
// Hash represents NameService contract hash.
|
||||
const Hash = "\x8c\x02\xb8\x43\x98\x6b\x3c\x44\x4f\xf8\x6a\xd5\xa9\x43\xfe\x8d\xb6\x24\xb5\xa2"
|
||||
const Hash = "\x6b\x59\x2b\x87\x66\xcc\x45\x8e\xfa\x7a\x90\x47\x56\x62\xcd\x92\x03\xcf\x8f\x7a"
|
||||
|
||||
// Symbol represents `symbol` method of NameService native contract.
|
||||
func Symbol() string {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents NEO contract hash.
|
||||
const Hash = "\x83\xab\x06\x79\xad\x55\xc0\x50\xa1\x3a\xd4\x3f\x59\x36\xea\x73\xf5\xeb\x1e\xf6"
|
||||
const Hash = "\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef"
|
||||
|
||||
// Symbol represents `symbol` method of NEO native contract.
|
||||
func Symbol() string {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents Notary contract hash.
|
||||
const Hash = "\x0c\xcf\x26\x94\x3f\xb5\xc9\xb6\x05\xe2\x06\xd2\xa2\x75\xbe\x3e\xa6\xa4\x75\xf4"
|
||||
const Hash = "\x3b\xec\x35\x31\x11\x9b\xba\xd7\x6d\xd0\x44\x92\x0b\x0d\xe6\xc3\x19\x4f\xe1\xc1"
|
||||
|
||||
// LockDepositUntil represents `lockDepositUntil` method of Notary native contract.
|
||||
func LockDepositUntil(addr interop.Hash160, till int) bool {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents Oracle contract hash.
|
||||
const Hash = "\xee\x80\x4c\x14\x29\x68\xd4\x78\x8b\x8a\xff\x51\xda\xde\xdf\xcb\x42\xe7\xc0\x8d"
|
||||
const Hash = "\x58\x87\x17\x11\x7e\x0a\xa8\x10\x72\xaf\xab\x71\xd2\xdd\x89\xfe\x7c\x4b\x92\xfe"
|
||||
|
||||
// Request represents `request` method of Oracle native contract.
|
||||
func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents Policy contract hash.
|
||||
const Hash = "\xf2\xe2\x08\xed\xcd\x14\x6c\xbe\xe4\x67\x6e\xdf\x79\xb7\x5e\x50\x98\xd3\xbc\x79"
|
||||
const Hash = "\x7b\xc6\x81\xc0\xa1\xf7\x1d\x54\x34\x57\xb6\x8b\xba\x8d\x5f\x9f\xdd\x4e\x5e\xcc"
|
||||
|
||||
// GetMaxTransactionsPerBlock represents `getMaxTransactionsPerBlock` method of Policy native contract.
|
||||
func GetMaxTransactionsPerBlock() int {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// Hash represents RoleManagement contract hash.
|
||||
const Hash = "\x02\x8b\x00\x50\x70\xb6\x0d\xf1\xc8\xe2\x09\x78\x7b\x49\xce\xbb\x71\x14\x7b\x59"
|
||||
const Hash = "\xe2\x95\xe3\x91\x54\x4c\x17\x8a\xd9\x4f\x03\xec\x4d\xcd\xff\x78\x53\x4e\xcf\x49"
|
||||
|
||||
// Role represents node role.
|
||||
type Role byte
|
||||
|
|
|
@ -685,7 +685,7 @@ func (c *Client) CalculateNotaryFee(nKeys uint8) (int64, error) {
|
|||
return int64((nKeys+1))*transaction.NotaryServiceFeePerKey + // fee for NotaryAssisted attribute
|
||||
fee.Opcode(baseExecFee, // Notary node witness
|
||||
opcode.PUSHDATA1, opcode.RET, // invocation script
|
||||
opcode.PUSHINT8, opcode.SYSCALL, opcode.RET) + // System.Contract.CallNative
|
||||
opcode.PUSH0, opcode.SYSCALL, opcode.RET) + // System.Contract.CallNative
|
||||
native.NotaryVerificationPrice + // Notary witness verification price
|
||||
feePerByte*int64(io.GetVarSize(make([]byte, 66))) + // invocation script per-byte fee
|
||||
feePerByte*int64(io.GetVarSize([]byte{})), // verification script per-byte fee
|
||||
|
|
|
@ -60,8 +60,8 @@ type rpcTestCase struct {
|
|||
}
|
||||
|
||||
const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a"
|
||||
const deploymentTxHash = "d0de42d5d23211174a50d74fbd4a919631236a63f16431a5a7e7126759e7ba23"
|
||||
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
|
||||
const deploymentTxHash = "050e2189d7cd7b719d9c4bbc525d3edcd89ffedb28cc974862d17dda14377612"
|
||||
const genesisBlockHash = "41d1099030004d60f3b4b7ffe00bf184e84fac3a39ee7136c761e005f0186d93"
|
||||
|
||||
const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943"
|
||||
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -24,6 +24,12 @@ type (
|
|||
Oracle struct {
|
||||
Config
|
||||
|
||||
// This fields are readonly thus not protected by mutex.
|
||||
oracleHash util.Uint160
|
||||
oracleResponse []byte
|
||||
oracleScript []byte
|
||||
verifyOffset int
|
||||
|
||||
// mtx protects setting callbacks.
|
||||
mtx sync.RWMutex
|
||||
|
||||
|
@ -56,9 +62,6 @@ type (
|
|||
ResponseHandler Broadcaster
|
||||
OnTransaction TxCallback
|
||||
URIValidator URIValidator
|
||||
OracleScript []byte
|
||||
OracleResponse []byte
|
||||
OracleHash util.Uint160
|
||||
}
|
||||
|
||||
// HTTPClient is an interface capable of doing oracle requests.
|
||||
|
@ -202,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 {
|
||||
o.mtx.RLock()
|
||||
defer o.mtx.RUnlock()
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"go.uber.org/zap"
|
||||
|
@ -83,7 +82,7 @@ func readResponse(rc gio.ReadCloser, limit int) ([]byte, error) {
|
|||
|
||||
// CreateResponseTx creates unsigned oracle response transaction.
|
||||
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.ValidUntilBlock = height + transaction.MaxValidUntilBlockIncrement
|
||||
tx.Attributes = []transaction.Attribute{{
|
||||
|
@ -94,7 +93,7 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *tra
|
|||
oracleSignContract := o.getOracleSignContract()
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: o.OracleHash,
|
||||
Account: o.oracleHash,
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
{
|
||||
|
@ -137,8 +136,8 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, height uint32, resp *tra
|
|||
func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) {
|
||||
v := o.Chain.GetTestVM(trigger.Verification, tx, nil)
|
||||
v.GasLimit = o.Chain.GetPolicer().GetMaxVerificationGAS()
|
||||
v.LoadScriptWithHash(o.OracleScript, o.OracleHash, callflag.ReadStates)
|
||||
v.Estack().PushVal(manifest.MethodVerify)
|
||||
v.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
|
||||
v.Jump(v.Context(), o.verifyOffset)
|
||||
|
||||
ok := isVerifyOk(v)
|
||||
return v.GasConsumed(), ok
|
||||
|
|
Loading…
Reference in a new issue