Merge pull request #1744 from nspcc-dev/fix/nativecall

Update `CallNative` to post-preview5
This commit is contained in:
Roman Khimov 2021-03-02 15:28:00 +03:00 committed by GitHub
commit c9212f29e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 165 additions and 113 deletions

Binary file not shown.

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)
} }
@ -1753,7 +1760,6 @@ var (
// initVerificationVM initializes VM for witness check. // initVerificationVM initializes VM for witness check.
func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error { func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error {
isNative := false
v := ic.VM v := ic.VM
if len(witness.VerificationScript) != 0 { if len(witness.VerificationScript) != 0 {
if witness.ScriptHash() != hash { if witness.ScriptHash() != hash {
@ -1781,8 +1787,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
v.Context().NEF = &cs.NEF v.Context().NEF = &cs.NEF
v.Jump(v.Context(), md.Offset) v.Jump(v.Context(), md.Offset)
isNative = cs.ID <= 0 if initMD != nil {
if !isNative && initMD != nil {
v.Call(v.Context(), initMD.Offset) 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) return fmt.Errorf("%w: %v", ErrInvalidInvocation, err)
} }
v.LoadScript(witness.InvocationScript) v.LoadScript(witness.InvocationScript)
if isNative {
if err := v.StepOut(); err != nil {
return err
}
}
}
if isNative {
v.Estack().PushVal(manifest.MethodVerify)
} }
return nil return nil
} }

View file

@ -25,6 +25,7 @@ import (
"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"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
@ -586,7 +587,7 @@ func TestVerifyTx(t *testing.T) {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.Opcodes(w.BinWriter, opcode.ABORT) emit.Opcodes(w.BinWriter, opcode.ABORT)
emit.Bytes(w.BinWriter, util.Uint160{}.BytesBE()) 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) emit.String(w.BinWriter, orc.Manifest.Name)
tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes() tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes()
err := bc.VerifyTx(tx) err := bc.VerifyTx(tx)
@ -1000,7 +1001,7 @@ func TestVerifyTx(t *testing.T) {
transaction.NotaryServiceFeePerKey + // fee for Notary attribute transaction.NotaryServiceFeePerKey + // fee for Notary attribute
fee.Opcode(bc.GetBaseExecFee(), // Notary verification script fee.Opcode(bc.GetBaseExecFee(), // Notary verification script
opcode.PUSHDATA1, opcode.RET, // invocation 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 native.NotaryVerificationPrice // Notary witness verification price
tx.Scripts = []transaction.Witness{ tx.Scripts = []transaction.Witness{
{ {
@ -1197,7 +1198,8 @@ func TestIsTxStillRelevant(t *testing.T) {
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
func Verify() bool { 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 return currentHeight.(int) < %d
}`, bc.BlockHeight()+2) // deploy + next block }`, bc.BlockHeight()+2) // deploy + next block
txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src)) txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src))

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

@ -4,19 +4,24 @@ import (
"errors" "errors"
"fmt" "fmt"
"sort" "sort"
"strings"
"github.com/nspcc-dev/neo-go/pkg/core/block" "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/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/dao" "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/state"
"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" "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/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -86,6 +91,7 @@ type MethodAndPrice struct {
Func Method Func Method
MD *manifest.Method MD *manifest.Method
Price int64 Price int64
SyscallOffset int
RequiredFlags callflag.CallFlag RequiredFlags callflag.CallFlag
} }
@ -101,21 +107,12 @@ type Contract interface {
type ContractMD struct { type ContractMD struct {
state.NativeContract state.NativeContract
Name string Name string
Methods map[MethodAndArgCount]MethodAndPrice Methods []MethodAndPrice
}
// MethodAndArgCount represents method's signature.
type MethodAndArgCount struct {
Name string
ArgCount int
} }
// NewContractMD returns Contract with the specified list of methods. // NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string, id int32) *ContractMD { func NewContractMD(name string, id int32) *ContractMD {
c := &ContractMD{ c := &ContractMD{Name: name}
Name: name,
Methods: make(map[MethodAndArgCount]MethodAndPrice),
}
c.ID = id c.ID = id
@ -123,14 +120,32 @@ func NewContractMD(name string, id int32) *ContractMD {
// Therefore values are taken from C# node. // Therefore values are taken from C# node.
c.NEF.Header.Compiler = "neo-core-v3.0" c.NEF.Header.Compiler = "neo-core-v3.0"
c.NEF.Header.Magic = nef.Magic c.NEF.Header.Magic = nef.Magic
c.NEF.Script = state.CreateNativeContractScript(id) c.Hash = state.CreateContractHash(util.Uint160{}, 0, c.Name)
c.NEF.Checksum = c.NEF.CalculateChecksum()
c.Hash = state.CreateContractHash(util.Uint160{}, c.NEF.Checksum, name)
c.Manifest = *manifest.DefaultManifest(name) c.Manifest = *manifest.DefaultManifest(name)
return c 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. // AddMethod adds new method to a native contract.
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) { func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
md.MD = desc 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:]) copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
c.Manifest.ABI.Methods[index] = *desc c.Manifest.ABI.Methods[index] = *desc
key := MethodAndArgCount{ // Cache follows the same order.
Name: desc.Name, c.Methods = append(c.Methods, MethodAndPrice{})
ArgCount: len(desc.Parameters), copy(c.Methods[index+1:], c.Methods[index:])
c.Methods[index] = *md
} }
c.Methods[key] = *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
}
}
return MethodAndPrice{}, false
} }
// GetMethod returns method `name` with specified number of parameters. // GetMethod returns method `name` with specified number of parameters.
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) { func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
key := MethodAndArgCount{ index := sort.Search(len(c.Methods), func(i int) bool {
Name: name, md := c.Methods[i]
ArgCount: paramCount, res := strings.Compare(name, md.MD.Name)
switch res {
case -1, 1:
return res == -1
default:
return paramCount <= len(md.MD.Parameters)
} }
mp, ok := c.Methods[key] })
return mp, ok if index < len(c.Methods) {
md := c.Methods[index]
if md.MD.Name == name && (paramCount == -1 || len(md.MD.Parameters) == paramCount) {
return md, true
}
}
return MethodAndPrice{}, false
} }
// AddEvent adds new event to a native contract. // AddEvent adds new event to a native contract.

View file

@ -98,24 +98,13 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
} }
ic.VM.Invocations[cs.Hash]++ 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 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-- { for i := len(args) - 1; i >= 0; i-- {
ic.VM.Estack().PushVal(args[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. // use Jump not Call here because context was loaded in LoadScript above.
ic.VM.Jump(ic.VM.Context(), md.Offset) ic.VM.Jump(ic.VM.Context(), md.Offset)
}
if hasReturn { if hasReturn {
ic.VM.Context().RetCount = 1 ic.VM.Context().RetCount = 1
} else { } else {

View file

@ -12,8 +12,8 @@ func TestNamesASCII(t *testing.T) {
cs := NewContracts(true) cs := NewContracts(true)
for _, c := range cs.Contracts { for _, c := range cs.Contracts {
require.True(t, isASCII(c.Metadata().Name)) require.True(t, isASCII(c.Metadata().Name))
for m := range c.Metadata().Methods { for _, m := range c.Metadata().Methods {
require.True(t, isASCII(m.Name)) require.True(t, isASCII(m.MD.Name))
} }
for _, e := range c.Metadata().Manifest.ABI.Events { for _, e := range c.Metadata().Manifest.ABI.Events {
require.True(t, isASCII(e.Name)) require.True(t, isASCII(e.Name))

View file

@ -81,6 +81,7 @@ func (s *Designate) isValidRole(r Role) bool {
func newDesignate(p2pSigExtensionsEnabled bool) *Designate { func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)} s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
defer s.UpdateHash()
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType, desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("role", smartcontract.IntegerType),

View file

@ -12,28 +12,27 @@ import (
// Call calls specified native contract method. // Call calls specified native contract method.
func Call(ic *interop.Context) error { 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 var c interop.Contract
for _, ctr := range ic.Natives { for _, ctr := range ic.Natives {
if ctr.Metadata().ID == id { if ctr.Metadata().Hash == ic.VM.GetCurrentScriptHash() {
c = ctr c = ctr
break break
} }
} }
if c == nil { 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() m, ok := c.Metadata().GetMethodByOffset(ic.VM.Context().IP())
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())
if !ok { if !ok {
return fmt.Errorf("method %s not found", operation) return fmt.Errorf("method not found")
} }
if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) { 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`. // Native contract prices are not multiplied by `BaseExecFee`.
if !ic.VM.AddGas(m.Price) { if !ic.VM.AddGas(m.Price) {

View file

@ -39,6 +39,8 @@ func newLedger() *Ledger {
var l = &Ledger{ var l = &Ledger{
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID), ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
} }
defer l.UpdateHash()
desc := newDescriptor("currentHash", smartcontract.Hash256Type) desc := newDescriptor("currentHash", smartcontract.Hash256Type)
md := newMethodAndPrice(l.currentHash, 1000000, callflag.ReadStates) md := newMethodAndPrice(l.currentHash, 1000000, callflag.ReadStates)
l.AddMethod(md, desc) l.AddMethod(md, desc)

View file

@ -65,6 +65,7 @@ func newManagement() *Management {
ContractMD: *interop.NewContractMD(nativenames.Management, managementContractID), ContractMD: *interop.NewContractMD(nativenames.Management, managementContractID),
contracts: make(map[util.Uint160]*state.Contract), contracts: make(map[util.Uint160]*state.Contract),
} }
defer m.UpdateHash()
desc := newDescriptor("getContract", smartcontract.ArrayType, desc := newDescriptor("getContract", smartcontract.ArrayType,
manifest.NewParameter("hash", smartcontract.Hash160Type)) manifest.NewParameter("hash", smartcontract.Hash160Type))

View file

@ -100,6 +100,7 @@ func newNameService() *NameService {
} }
n := &NameService{nonfungible: *nf} n := &NameService{nonfungible: *nf}
defer n.UpdateHash()
desc := newDescriptor("addRoot", smartcontract.VoidType, desc := newDescriptor("addRoot", smartcontract.VoidType,
manifest.NewParameter("root", smartcontract.StringType)) manifest.NewParameter("root", smartcontract.StringType))

View file

@ -27,6 +27,8 @@ const initialGAS = 30000000
// newGAS returns GAS native contract. // newGAS returns GAS native contract.
func newGAS() *GAS { func newGAS() *GAS {
g := &GAS{} g := &GAS{}
defer g.UpdateHash()
nep17 := newNEP17Native(nativenames.Gas, gasContractID) nep17 := newNEP17Native(nativenames.Gas, gasContractID)
nep17.symbol = "GAS" nep17.symbol = "GAS"
nep17.decimals = 8 nep17.decimals = 8

View file

@ -94,6 +94,8 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
// newNEO returns NEO native contract. // newNEO returns NEO native contract.
func newNEO() *NEO { func newNEO() *NEO {
n := &NEO{} n := &NEO{}
defer n.UpdateHash()
nep17 := newNEP17Native(nativenames.Neo, neoContractID) nep17 := newNEP17Native(nativenames.Neo, neoContractID)
nep17.symbol = "NEO" nep17.symbol = "NEO"
nep17.decimals = 0 nep17.decimals = 0

View file

@ -54,6 +54,7 @@ var maxNotValidBeforeDeltaKey = []byte{10}
// newNotary returns Notary native contract. // newNotary returns Notary native contract.
func newNotary() *Notary { func newNotary() *Notary {
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)} n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
defer n.UpdateHash()
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType, desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("from", smartcontract.Hash160Type),

View file

@ -75,6 +75,7 @@ var (
func newOracle() *Oracle { func newOracle() *Oracle {
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)} o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID)}
defer o.UpdateHash()
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0) emit.Opcodes(w.BinWriter, opcode.NEWARRAY0)

View file

@ -86,6 +86,7 @@ var _ interop.Contract = (*Policy)(nil)
// newPolicy returns Policy native contract. // newPolicy returns Policy native contract.
func newPolicy() *Policy { func newPolicy() *Policy {
p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)} p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)}
defer p.UpdateHash()
desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType) desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType)
md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, callflag.ReadStates) md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, callflag.ReadStates)

View file

@ -10,7 +10,6 @@ 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/interop/contract" "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/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
@ -64,6 +63,8 @@ func newTestNative() *testNative {
meta: *interop.NewContractMD("Test.Native.Sum", 0), meta: *interop.NewContractMD("Test.Native.Sum", 0),
blocks: make(chan uint32, 1), blocks: make(chan uint32, 1),
} }
defer tn.meta.UpdateHash()
desc := &manifest.Method{ desc := &manifest.Method{
Name: "sum", Name: "sum",
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
@ -220,27 +221,33 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, chain.config.StateRootInHeader) d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, chain.config.StateRootInHeader)
ic := chain.newInteropContext(trigger.Application, d, nil, nil) 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) { 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(14)
v.Estack().PushVal(28) v.Estack().PushVal(28)
v.Estack().PushVal("sum") v.Jump(v.Context(), sumOffset)
v.Estack().PushVal(tn.Metadata().Name)
// it's prohibited to call natives directly // 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) { 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(14)
v.Estack().PushVal(28) v.Estack().PushVal(28)
v.Estack().PushVal("sum") v.Jump(v.Context(), sumOffset)
v.Estack().PushVal(tn.Metadata().ID) require.NoError(t, v.Run())
require.NoError(t, native.Call(ic))
value := v.Estack().Pop().BigInt() value := v.Estack().Pop().BigInt()
require.Equal(t, int64(42), value.Int64()) require.Equal(t, int64(42), value.Int64())

View file

@ -19,6 +19,7 @@ import (
"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/keys" "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/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/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -41,9 +42,6 @@ func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string) oracle.Config
}, },
Chain: bc, Chain: bc,
Client: newDefaultHTTPClient(), Client: newDefaultHTTPClient(),
OracleScript: bc.contracts.Oracle.NEF.Script,
OracleResponse: bc.contracts.Oracle.GetOracleResponseScript(),
OracleHash: bc.contracts.Oracle.Hash,
} }
} }
@ -96,6 +94,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())
@ -125,6 +124,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

@ -5,7 +5,6 @@ import (
"math" "math"
"math/big" "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/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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()) 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()
}

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents GAS contract hash. // 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. // Symbol represents `symbol` method of GAS native contract.
func Symbol() string { func Symbol() string {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents Ledger contract hash. // 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. // CurrentHash represents `currentHash` method of Ledger native contract.
func CurrentHash() interop.Hash256 { func CurrentHash() interop.Hash256 {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents Management contract hash. // 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. // Deploy represents `deploy` method of Management native contract.
func Deploy(script, manifest []byte) *Contract { func Deploy(script, manifest []byte) *Contract {

View file

@ -18,7 +18,7 @@ const (
) )
// Hash represents NameService contract hash. // 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. // Symbol represents `symbol` method of NameService native contract.
func Symbol() string { func Symbol() string {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents NEO contract hash. // 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. // Symbol represents `symbol` method of NEO native contract.
func Symbol() string { func Symbol() string {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents Notary contract hash. // 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. // LockDepositUntil represents `lockDepositUntil` method of Notary native contract.
func LockDepositUntil(addr interop.Hash160, till int) bool { func LockDepositUntil(addr interop.Hash160, till int) bool {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents Oracle contract hash. // 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. // Request represents `request` method of Oracle native contract.
func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) { func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents Policy contract hash. // 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. // GetMaxTransactionsPerBlock represents `getMaxTransactionsPerBlock` method of Policy native contract.
func GetMaxTransactionsPerBlock() int { func GetMaxTransactionsPerBlock() int {

View file

@ -6,7 +6,7 @@ import (
) )
// Hash represents RoleManagement contract hash. // 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. // Role represents node role.
type Role byte type Role byte

View file

@ -685,7 +685,7 @@ func (c *Client) CalculateNotaryFee(nKeys uint8) (int64, error) {
return int64((nKeys+1))*transaction.NotaryServiceFeePerKey + // fee for NotaryAssisted attribute return int64((nKeys+1))*transaction.NotaryServiceFeePerKey + // fee for NotaryAssisted attribute
fee.Opcode(baseExecFee, // Notary node witness fee.Opcode(baseExecFee, // Notary node witness
opcode.PUSHDATA1, opcode.RET, // invocation script 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 native.NotaryVerificationPrice + // Notary witness verification price
feePerByte*int64(io.GetVarSize(make([]byte, 66))) + // invocation script per-byte fee feePerByte*int64(io.GetVarSize(make([]byte, 66))) + // invocation script per-byte fee
feePerByte*int64(io.GetVarSize([]byte{})), // verification script per-byte fee feePerByte*int64(io.GetVarSize([]byte{})), // verification script per-byte fee

View file

@ -60,8 +60,8 @@ type rpcTestCase struct {
} }
const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a" const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a"
const deploymentTxHash = "d0de42d5d23211174a50d74fbd4a919631236a63f16431a5a7e7126759e7ba23" const deploymentTxHash = "050e2189d7cd7b719d9c4bbc525d3edcd89ffedb28cc974862d17dda14377612"
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70" const genesisBlockHash = "41d1099030004d60f3b4b7ffe00bf184e84fac3a39ee7136c761e005f0186d93"
const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943" const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943"
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740" const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"

Binary file not shown.

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,9 +62,6 @@ type (
ResponseHandler Broadcaster ResponseHandler Broadcaster
OnTransaction TxCallback OnTransaction TxCallback
URIValidator URIValidator URIValidator URIValidator
OracleScript []byte
OracleResponse []byte
OracleHash util.Uint160
} }
// HTTPClient is an interface capable of doing oracle requests. // 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 { func (o *Oracle) getOnTransaction() TxCallback {
o.mtx.RLock() o.mtx.RLock()
defer o.mtx.RUnlock() defer o.mtx.RUnlock()

View file

@ -11,7 +11,6 @@ import (
"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/io" "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/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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"go.uber.org/zap" "go.uber.org/zap"
@ -83,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{{
@ -94,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,
}, },
{ {
@ -137,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.ReadStates) v.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
v.Estack().PushVal(manifest.MethodVerify) v.Jump(v.Context(), o.verifyOffset)
ok := isVerifyOk(v) ok := isVerifyOk(v)
return v.GasConsumed(), ok return v.GasConsumed(), ok