Merge pull request #1054 from nspcc-dev/fix/blockchain_interop

Adjust contract/blockchain syscalls
This commit is contained in:
Roman Khimov 2020-06-16 12:15:24 +03:00 committed by GitHub
commit 901181a392
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 9 deletions

View file

@ -12,6 +12,7 @@ import (
"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/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/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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"
@ -470,3 +471,30 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error {
} }
return nil return nil
} }
// contractIsStandard checks if contract is standard (sig or multisig) contract.
func contractIsStandard(ic *interop.Context, v *vm.VM) error {
h := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(h)
if err != nil {
return err
}
var result bool
cs, _ := ic.DAO.GetContractState(u)
if cs == nil || vm.IsStandardContract(cs.Script) {
result = true
}
v.Estack().PushVal(result)
return nil
}
// contractCreateStandardAccount calculates contract scripthash for a given public key.
func contractCreateStandardAccount(ic *interop.Context, v *vm.VM) error {
h := v.Estack().Pop().Bytes()
p, err := keys.NewPublicKeyFromBytes(h)
if err != nil {
return err
}
v.Estack().PushVal(p.GetScriptHash().BytesBE())
return nil
}

View file

@ -4,6 +4,11 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/dbft/crypto"
"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"
"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"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -145,3 +150,50 @@ func TestBCGetBlock(t *testing.T) {
require.True(t, ok) require.True(t, ok)
}) })
} }
func TestContractIsStandard(t *testing.T) {
v, ic, chain := createVM(t)
defer chain.Close()
t.Run("True", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
err = ic.DAO.PutContractState(&state.Contract{ID: 42, Script: pub.GetVerificationScript()})
require.NoError(t, err)
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
require.NoError(t, contractIsStandard(ic, v))
require.True(t, v.Estack().Pop().Bool())
})
t.Run("False", func(t *testing.T) {
script := []byte{byte(opcode.PUSHT)}
require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script}))
v.Estack().PushVal(crypto.Hash160(script).BytesBE())
require.NoError(t, contractIsStandard(ic, v))
require.False(t, v.Estack().Pop().Bool())
})
}
func TestContractCreateAccount(t *testing.T) {
v, ic, chain := createVM(t)
defer chain.Close()
t.Run("Good", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
v.Estack().PushVal(pub.Bytes())
require.NoError(t, contractCreateStandardAccount(ic, v))
value := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, pub.GetScriptHash(), u)
})
t.Run("InvalidKey", func(t *testing.T) {
v.Estack().PushVal([]byte{1, 2, 3})
require.Error(t, contractCreateStandardAccount(ic, v))
})
}

View file

@ -71,26 +71,28 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin
var systemInterops = []interop.Function{ var systemInterops = []interop.Function{
{Name: "System.Binary.Deserialize", Func: runtimeDeserialize, Price: 500000}, {Name: "System.Binary.Deserialize", Func: runtimeDeserialize, Price: 500000},
{Name: "System.Binary.Serialize", Func: runtimeSerialize, Price: 100000}, {Name: "System.Binary.Serialize", Func: runtimeSerialize, Price: 100000},
{Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250, {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 2500000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100, {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 1000000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1, {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 400,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100, {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 1000000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100, {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 1000000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100, {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 1000000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Contract.Call", Func: contractCall, Price: 1, {Name: "System.Contract.Call", Func: contractCall, Price: 1000000,
AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall},
{Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1, {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1000000,
AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall},
{Name: "System.Contract.Create", Func: contractCreate, Price: 0, {Name: "System.Contract.Create", Func: contractCreate, Price: 0,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
{Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1, {Name: "System.Contract.CreateStandardAccount", Func: contractCreateStandardAccount, Price: 10000},
{Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1000000,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
{Name: "System.Contract.IsStandard", Func: contractIsStandard, Price: 30000},
{Name: "System.Contract.Update", Func: contractUpdate, Price: 0, {Name: "System.Contract.Update", Func: contractUpdate, Price: 0,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
{Name: "System.Enumerator.Concat", Func: enumerator.Concat, Price: 400}, {Name: "System.Enumerator.Concat", Func: enumerator.Concat, Price: 400},