Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add json marshal and unmarshal api for neovm contractt #984

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ func GetStateHashCheckHeight(id uint32) uint32 {
return STATE_HASH_CHECK_HEIGHT[id]
}

var OPCODE_UPDATE_CHECK_HEIGHT = map[uint32]uint32{
NETWORK_ID_MAIN_NET: constants.OPCODE_HEIGHT_UPDATE_FIRST_MAINNET, //Network main
NETWORK_ID_POLARIS_NET: constants.OPCODE_HEIGHT_UPDATE_FIRST_POLARIS, //Network polaris
NETWORK_ID_SOLO_NET: 0, //Network solo
}

func GetOpcodeUpdateCheckHeight(id uint32) uint32 {
return OPCODE_UPDATE_CHECK_HEIGHT[id]
}

func GetNetworkName(id uint32) string {
name, ok := NETWORK_NAME[id]
if ok {
Expand Down
4 changes: 4 additions & 0 deletions common/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,7 @@ const (
// ledger state hash check height
const STATE_HASH_HEIGHT_MAINNET = 3000000
const STATE_HASH_HEIGHT_POLARIS = 850000

// neovm opcode update check height
const OPCODE_HEIGHT_UPDATE_FIRST_MAINNET = 6000000
const OPCODE_HEIGHT_UPDATE_FIRST_POLARIS = 2100000
2 changes: 1 addition & 1 deletion consensus/vbft/node_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func testCalcParticipantPeers(t *testing.T, n, c int) {
for _, p := range pc {
peers[p] = true
}
if len(peers) <= 2*c+1 {
if len(peers) < 2*c+1 {
t.Fatalf("peers(%d, %d, %d, %d, %d, %d): %v, %v, %v", n, c, len(peers), len(pp), len(pe), len(pc), pp, pe, pc)
}
}
26 changes: 17 additions & 9 deletions smartcontract/service/neovm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ var (
STORAGE_DELETE_GAS uint64 = 100
RUNTIME_CHECKWITNESS_GAS uint64 = 200
RUNTIME_VERIFYMUTISIG_GAS uint64 = 400
RUNTIME_ADDRESSTOBASE58_GAS uint64 = 40
RUNTIME_BASE58TOADDRESS_GAS uint64 = 30
APPCALL_GAS uint64 = 10
TAILCALL_GAS uint64 = 10
SHA1_GAS uint64 = 10
SHA256_GAS uint64 = 10
HASH160_GAS uint64 = 20
HASH256_GAS uint64 = 20
OPCODE_GAS uint64 = 1
RUNTIME_JSON_MARSHAL_GAS uint64 = 400
RUNTIME_JSON_UNMARSHAL_GAS uint64 = 400

RUNTIME_ADDRESSTOBASE58_GAS uint64 = 40
RUNTIME_BASE58TOADDRESS_GAS uint64 = 30
APPCALL_GAS uint64 = 10
TAILCALL_GAS uint64 = 10
SHA1_GAS uint64 = 10
SHA256_GAS uint64 = 10
HASH160_GAS uint64 = 20
HASH256_GAS uint64 = 20
OPCODE_GAS uint64 = 1

PER_UNIT_CODE_LEN int = 1024
METHOD_LENGTH_LIMIT int = 1024
Expand Down Expand Up @@ -104,6 +107,8 @@ var (
RUNTIME_ADDRESSTOBASE58_NAME = "Ontology.Runtime.AddressToBase58"
RUNTIME_GETCURRENTBLOCKHASH_NAME = "Ontology.Runtime.GetCurrentBlockHash"
RUNTIME_VERIFYMUTISIG_NAME = "Ontology.Runtime.VerifyMutiSig"
RUNTIME_JSON_MARSHAL_NAME = "Ontology.Runtime.JsonMarshal"
RUNTIME_JSON_UNMARSHAL_NAME = "Ontology.Runtime.JsonUnmarshal"

NATIVE_INVOKE_NAME = "Ontology.Native.Invoke"

Expand Down Expand Up @@ -195,5 +200,8 @@ func initGAS_TABLE() *sync.Map {

m.Store(RUNTIME_VERIFYMUTISIG_NAME, RUNTIME_VERIFYMUTISIG_GAS)

m.Store(RUNTIME_JSON_MARSHAL_NAME, RUNTIME_JSON_MARSHAL_GAS)
m.Store(RUNTIME_JSON_UNMARSHAL_NAME, RUNTIME_JSON_UNMARSHAL_GAS)

return &m
}
2 changes: 2 additions & 0 deletions smartcontract/service/neovm/neovm_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ var (
RUNTIME_SERIALIZE_NAME: {Execute: RuntimeSerialize, Validator: validatorSerialize},
RUNTIME_DESERIALIZE_NAME: {Execute: RuntimeDeserialize, Validator: validatorDeserialize},
RUNTIME_VERIFYMUTISIG_NAME: {Execute: RuntimeVerifyMutiSig},
RUNTIME_JSON_MARSHAL_NAME: {Execute: RuntimeJsonMarshal},
RUNTIME_JSON_UNMARSHAL_NAME: {Execute: RuntimeJsonUnmarshal},
NATIVE_INVOKE_NAME: {Execute: NativeInvoke},
STORAGE_GET_NAME: {Execute: StorageGet},
STORAGE_PUT_NAME: {Execute: StoragePut},
Expand Down
253 changes: 253 additions & 0 deletions smartcontract/service/neovm/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"reflect"
"sort"

"encoding/json"
"github.com/ontio/ontology-crypto/keypair"
"github.com/ontio/ontology/common"
"github.com/ontio/ontology/common/serialization"
Expand All @@ -34,8 +35,15 @@ import (
"github.com/ontio/ontology/smartcontract/event"
vm "github.com/ontio/ontology/vm/neovm"
vmtypes "github.com/ontio/ontology/vm/neovm/types"
"math/big"
)

//allow json depth
var JSON_DEPTH = 10

//max json string length
var MAX_JSONBYTES_LENGTH = 1024

// HeaderGetNextConsensus put current block time to vm stack
func RuntimeGetTime(service *NeoVmService, engine *vm.ExecutionEngine) error {
vm.PushData(engine, int(service.Time))
Expand Down Expand Up @@ -212,6 +220,251 @@ func RuntimeGetCurrentBlockHash(service *NeoVmService, engine *vm.ExecutionEngin
return nil
}

func RuntimeJsonMarshal(service *NeoVmService, engine *vm.ExecutionEngine) error {
if vm.EvaluationStackCount(engine) < 1 {
return errors.NewErr("[RuntimeJsonMarshal] Too few input parameters")
}
item := vm.PopStackItem(engine)

m := make(map[string]interface{})
err := StackitemToMap(item, m, 0)
if err != nil {
return err
}
res, err := json.Marshal(m)
if err != nil {
return err
}
vm.PushData(engine, []byte(res))
return nil

}

func RuntimeJsonUnmarshal(service *NeoVmService, engine *vm.ExecutionEngine) error {
if vm.EvaluationStackCount(engine) < 1 {
return errors.NewErr("[RuntimeJsonUnmarshal] Too few input parameters")
}

jsonbytes, err := vm.PopByteArray(engine)
if err != nil {
return err
}
if len(jsonbytes) > MAX_JSONBYTES_LENGTH {
return errors.NewErr("[RuntimeJsonUnmarshal] Input Json bytes too long")
}

m := make(map[string]interface{})
err = json.Unmarshal(jsonbytes, &m)
if err != nil {
return err
}

mapitem := vmtypes.NewMap()
err = MapToStackitem(m, *mapitem)
if err != nil {
return err
}
vm.PushData(engine, mapitem)

return nil
}

func MapToStackitem(m map[string]interface{}, mapitem vmtypes.Map) error {

for k, v := range m {
vi, err := makeStackItem(v)
if err != nil {
return err
}
key := vmtypes.NewByteArray([]byte(k))
mapitem.Add(key, vi)
}
return nil

}

func ArrayToStackItem(arr []interface{}, arritem *vmtypes.Array) error {

for _, v := range arr {
vi, err := makeStackItem(v)
if err != nil {
return err
}
arritem.Add(vi)
}
return nil

}

func makeStackItem(v interface{}) (vmtypes.StackItems, error) {
switch v.(type) {
case []byte:
return vmtypes.NewByteArray(v.([]byte)), nil

case string:
return vmtypes.NewByteArray([]byte(v.(string))), nil

case float64:
return vmtypes.NewInteger(big.NewInt(int64(v.(float64)))), nil

case bool:
return vmtypes.NewBoolean(v.(bool)), nil

case map[string]interface{}:
item := vmtypes.NewMap()
err := MapToStackitem(v.(map[string]interface{}), *item)
if err != nil {
return nil, err
}
return item, nil

case []interface{}:
item := vmtypes.NewArray(nil)
err := ArrayToStackItem(v.([]interface{}), item)
if err != nil {
return nil, err
}
return item, nil

default:
return nil, errors.NewErr("not supported type")
}
}

func StackitemToMap(item vmtypes.StackItems, m map[string]interface{}, depth int) error {

depth, err := checkDepth(depth)
if err != nil {
return err
}

itemMap, err := item.GetMap()
if err != nil {
return err
}

for k, v := range itemMap {
switch k.(type) {
case *vmtypes.ByteArray:
key, err := k.GetByteArray()
if err != nil {
return err
}
switch v.(type) {
case *vmtypes.ByteArray:
val, err := v.GetByteArray()
if err != nil {
return err
}
m[string(key)] = string(val)
case *vmtypes.Integer, *vmtypes.Boolean:
val, err := v.GetByteArray()
if err != nil {
return err
}
m[string(key)] = common.ToHexString(val)
case *vmtypes.Array:
arr, err := v.GetArray()
if err != nil {
return err
}
err = StackitemArrayToMap(arr, m, string(key), depth)
if err != nil {
return err
}
case *vmtypes.Map:
tmp := make(map[string]interface{})
m[string(key)] = tmp
err := StackitemToMap(v, tmp, depth)
if err != nil {
return err
}
default:
return errors.NewErr("Not support json value type")
}

default:
return errors.NewErr("Not support json key type")
}

}
return nil
}

func StackitemArrayToMap(items []vmtypes.StackItems, m map[string]interface{}, key string, depth int) error {
depth, err := checkDepth(depth)
if err != nil {
return err
}

tmparr := make([]interface{}, len(items))
m[key] = tmparr
for i, v := range items {
switch v.(type) {
case *vmtypes.Array:
arr, err := v.GetArray()
if err != nil {
return err
}
subarr := make([]interface{}, len(arr))
tmparr[i] = subarr
err = StackitemArrayToArray(arr, subarr, depth)
if err != nil {
return err
}
case *vmtypes.Map:
tmp := make(map[string]interface{})
tmparr[i] = tmp
err = StackitemToMap(v, tmp, depth)
if err != nil {
return err
}
default:
return errors.NewErr("Not a supported type")
}
}
return nil
}

func StackitemArrayToArray(items []vmtypes.StackItems, arr []interface{}, depth int) error {
depth, err := checkDepth(depth)
if err != nil {
return err
}
for i, item := range items {
switch item.(type) {
case *vmtypes.Map:
m := make(map[string]interface{})
arr[i] = m
err = StackitemToMap(item, m, depth)
if err != nil {
return err
}
case *vmtypes.Array:
arritem, err := item.GetArray()
if err != nil {
return err
}
tmparr := make([]interface{}, len(arritem))
arr[i] = tmparr
err = StackitemArrayToArray(arritem, tmparr, depth)
if err != nil {
return err
}
default:
return errors.NewErr("Not a supported type")
}
}
return nil
}

func checkDepth(depth int) (int, error) {
if depth >= JSON_DEPTH {
return -1, errors.NewErr("json depth exceed!")
}
return depth + 1, nil
}

func SerializeStackItem(item vmtypes.StackItems) ([]byte, error) {
if CircularRefAndDepthDetection(item) {
return nil, errors.NewErr("runtime serialize: can not serialize circular reference data")
Expand Down
Loading