Merge pull request #1221 from nspcc-dev/neo3/interop/post-preview2_adjustment3

interop: post-preview2 adjustment, part 3
This commit is contained in:
Roman Khimov 2020-07-23 20:35:53 +03:00 committed by GitHub
commit d8a1c3de46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 80 additions and 24 deletions

View file

@ -16,7 +16,7 @@ var (
goBuiltins = []string{"len", "append", "panic"}
// Custom builtin utility functions.
customBuiltins = []string{
"SHA256", "AppCall",
"AppCall",
"FromAddress", "Equals",
"ToBool", "ToByteArray", "ToInteger",
}

View file

@ -1220,8 +1220,6 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
typ = stackitem.BooleanT
}
c.emitConvert(typ)
case "SHA256":
emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256")
case "AppCall":
c.emitReverse(len(expr.Args))
buf := c.getByteArray(expr.Args[0])

View file

@ -35,6 +35,8 @@ var syscalls = map[string]map[string]Syscall{
"ECDSASecp256k1CheckMultisig": {"Neo.Crypto.CheckMultisigWithECDsaSecp256k1", false},
"ECDsaSecp256r1Verify": {"Neo.Crypto.VerifyWithECDsaSecp256r1", false},
"ECDSASecp256r1CheckMultisig": {"Neo.Crypto.CheckMultisigWithECDsaSecp256r1", false},
"RIPEMD160": {"Neo.Crypto.RIPEMD160", false},
"SHA256": {"Neo.Crypto.SHA256", false},
},
"enumerator": {
"Concat": {"System.Enumerator.Concat", false},
@ -78,5 +80,6 @@ var syscalls = map[string]map[string]Syscall{
"GetContext": {"System.Storage.GetContext", false},
"GetReadOnlyContext": {"System.Storage.GetReadOnlyContext", false},
"Put": {"System.Storage.Put", false},
"PutEx": {"System.Storage.PutEx", false},
},
}

View file

@ -13,3 +13,11 @@ func Sha256(ic *interop.Context, v *vm.VM) error {
v.Estack().PushVal(h)
return nil
}
// RipeMD160 returns RipeMD160 hash of the data.
func RipeMD160(ic *interop.Context, v *vm.VM) error {
msg := getMessage(ic, v.Estack().Pop().Item())
h := hash.RipeMD160(msg).BytesBE()
v.Estack().PushVal(h)
return nil
}

View file

@ -28,3 +28,19 @@ func TestSHA256(t *testing.T) {
assert.Equal(t, 1, v.Estack().Len())
assert.Equal(t, res, hex.EncodeToString(v.Estack().Pop().Bytes()))
}
func TestRIPEMD160(t *testing.T) {
// 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3
res := "213492c0c6fc5d61497cf17249dd31cd9964b8a3"
buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, []byte{1, 0})
emit.Syscall(buf.BinWriter, "Neo.Crypto.RIPEMD160")
prog := buf.Bytes()
v := vm.New()
ic := &interop.Context{Trigger: trigger.Verification}
v.RegisterInteropGetter(GetInterop(ic))
v.Load(prog)
require.NoError(t, v.Run())
assert.Equal(t, 1, v.Estack().Len())
assert.Equal(t, res, hex.EncodeToString(v.Estack().Pop().Bytes()))
}

View file

@ -10,6 +10,7 @@ var (
ecdsaSecp256r1VerifyID = emit.InteropNameToID([]byte("Neo.Crypto.VerifyWithECDsaSecp256r1"))
ecdsaSecp256r1CheckMultisigID = emit.InteropNameToID([]byte("Neo.Crypto.CheckMultisigWithECDsaSecp256r1"))
sha256ID = emit.InteropNameToID([]byte("Neo.Crypto.SHA256"))
ripemd160ID = emit.InteropNameToID([]byte("Neo.Crypto.RIPEMD160"))
)
// GetInterop returns interop getter for crypto-related stuff.
@ -34,6 +35,12 @@ func GetInterop(ic *interop.Context) func(uint32) *vm.InteropFuncPrice {
return Sha256(ic, v)
},
}
case ripemd160ID:
return &vm.InteropFuncPrice{
Func: func(v *vm.VM) error {
return RipeMD160(ic, v)
},
}
default:
return nil
}

View file

@ -24,7 +24,10 @@ import (
const (
// MaxStorageKeyLen is the maximum length of a key for storage items.
MaxStorageKeyLen = 1024
MaxStorageKeyLen = 64
// MaxStorageValueLen is the maximum length of a value for storage items.
// It is set to be the maximum value for uint16.
MaxStorageValueLen = 65535
// MaxTraceableBlocks is the maximum number of blocks before current chain
// height we're able to give information about.
MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement
@ -41,6 +44,17 @@ type StorageContext struct {
ReadOnly bool
}
// StorageFlag represents storage flag which denotes whether the stored value is
// a constant.
type StorageFlag byte
const (
// None is a storage flag for non-constant items.
None StorageFlag = 0
// Constant is a storage flag for constant items.
Constant StorageFlag = 0x01
)
// getBlockHashFromElement converts given vm.Element to block hash using given
// Blockchainer if needed. Interop functions accept both block numbers and
// block hashes as parameters, thus this function is needed.
@ -346,6 +360,17 @@ func storageGet(ic *interop.Context, v *vm.VM) error {
// storageGetContext returns storage context (scripthash).
func storageGetContext(ic *interop.Context, v *vm.VM) error {
return storageGetContextInternal(ic, v, false)
}
// storageGetReadOnlyContext returns read-only context (scripthash).
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
return storageGetContextInternal(ic, v, true)
}
// storageGetContextInternal is internal version of storageGetContext and
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) error {
contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
if err != nil {
return err
@ -355,24 +380,7 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error {
}
sc := &StorageContext{
ID: contract.ID,
ReadOnly: false,
}
v.Estack().PushVal(stackitem.NewInterop(sc))
return nil
}
// storageGetReadOnlyContext returns read-only context (scripthash).
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
if err != nil {
return err
}
if !contract.HasStorage() {
return err
}
sc := &StorageContext{
ID: contract.ID,
ReadOnly: true,
ReadOnly: isReadOnly,
}
v.Estack().PushVal(stackitem.NewInterop(sc))
return nil
@ -382,6 +390,9 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext,
if len(key) > MaxStorageKeyLen {
return errors.New("key is too big")
}
if len(value) > MaxStorageValueLen {
return errors.New("value is too big")
}
if stc.ReadOnly {
return errors.New("StorageContext is read only")
}
@ -417,7 +428,7 @@ func storagePutInternal(ic *interop.Context, v *vm.VM, getFlag bool) error {
if getFlag {
flag = int(v.Estack().Pop().BigInt().Int64())
}
return putWithContextAndFlags(ic, v, stc, key, value, flag == 1)
return putWithContextAndFlags(ic, v, stc, key, value, int(Constant)&flag != 0)
}
// storagePut puts key-value pair into the storage.

View file

@ -149,6 +149,7 @@ var neoInterops = []interop.Function{
{Name: "Neo.Crypto.CheckMultisigWithECDsaSecp256r1", Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0},
{Name: "Neo.Crypto.CheckMultisigWithECDsaSecp256k1", Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0},
{Name: "Neo.Crypto.SHA256", Func: crypto.Sha256, Price: 1000000},
{Name: "Neo.Crypto.RIPEMD160", Func: crypto.RipeMD160, Price: 1000000},
{Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 0,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
}

View file

@ -12,7 +12,7 @@ import (
// Deploy deploys native contract.
func Deploy(ic *interop.Context, _ *vm.VM) error {
if ic.Block.Index != 0 {
if ic.Block == nil || ic.Block.Index != 0 {
return errors.New("native contracts can be deployed only at 0 block")
}

View file

@ -8,6 +8,11 @@ func SHA256(b []byte) []byte {
return nil
}
// RIPEMD160 computes RIPEMD160 hash of b. It uses `Neo.Crypto.RIPEMD160` syscall.
func RIPEMD160(b []byte) []byte {
return nil
}
// ECDsaSecp256r1Verify checks that sig is correct msg's signature for a given pub
// (serialized public key). It uses `Neo.Crypto.VerifyWithECDsaSecp256r1` syscall.
func ECDsaSecp256r1Verify(msg []byte, pub []byte, sig []byte) bool {

View file

@ -37,6 +37,13 @@ func GetReadOnlyContext() Context { return Context{} }
// runtime.Serialize. This function uses `System.Storage.Put` syscall.
func Put(ctx Context, key interface{}, value interface{}) {}
// PutEx is an advanced version of Put which saves given value with given key
// and given ReadOnly flag in the storage using given Context. `flag` argument
// can either be odd for constant storage items or even for variable storage items.
// Refer to Put function description for details on how to pass the remaining
// arguments. This function uses `System.Storage.PutEx` syscall.
func PutEx(ctx Context, key interface{}, value interface{}, flag int64) {}
// Get retrieves value stored for the given key using given Context. See Put
// documentation on possible key and value types. If the value is not present in
// the database it returns nil. This function uses `System.Storage.Get` syscall.