Skip to content

Commit

Permalink
Merge pull request #1451 from alex-semenyuk/map_impl
Browse files Browse the repository at this point in the history
Tezos Connector map converter
  • Loading branch information
nguyer authored Feb 12, 2024
2 parents bab1bc4 + 6a51572 commit 5c91279
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 7 deletions.
66 changes: 59 additions & 7 deletions internal/blockchain/tezos/ffi2michelson.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2023 Kaleido, Inc.
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand All @@ -19,6 +19,7 @@ package tezos
import (
"errors"
"fmt"
"slices"

"blockwatch.cc/tzgo/micheline"
"blockwatch.cc/tzgo/tezos"
Expand All @@ -34,6 +35,7 @@ const (
_internalBoolean = "boolean"
_internalList = "list"
_internalStruct = "struct"
_internalMap = "map"
_internalInteger = "integer"
_internalNat = "nat"
_internalString = "string"
Expand All @@ -43,6 +45,13 @@ const (
_internalBytes = "bytes"
)

// Tezos map
const (
_key = "key"
_value = "value"
_mapEntries = "mapEntries"
)

func processArgs(payloadSchema map[string]interface{}, input map[string]interface{}, methodName string) (micheline.Parameters, error) {
params := micheline.Parameters{
Entrypoint: methodName,
Expand Down Expand Up @@ -143,6 +152,44 @@ func processMichelson(entry interface{}, details map[string]interface{}) (resp m
func processSchemaEntry(entry interface{}, schema map[string]interface{}) (resp micheline.Prim, err error) {
entryType := schema["type"].(string)
switch entryType {
case _internalMap:
schemaArgs := schema["args"].([]interface{})

mapResp := micheline.NewMap()
mapEntries := entry.(map[string]interface{})[_mapEntries]
if mapEntries == nil {
return resp, fmt.Errorf("mapEntries schema property must be present")
}
for _, mapEntry := range mapEntries.([]interface{}) {
for name := range mapEntry.(map[string]interface{}) {
if !slices.Contains([]string{_key, _value}, name) {
return resp, errors.New("Unknown schema field '" + name + "' in map entry")
}
}

var k micheline.Prim
var v micheline.Prim
for i := len(schemaArgs) - 1; i >= 0; i-- {
arg := schemaArgs[i].(map[string]interface{})

if arg["name"] == _key {
if k, err = extractValue(_key, arg, mapEntry); err != nil {
return resp, err
}
}

if arg["name"] == _value {
if v, err = extractValue(_value, arg, mapEntry); err != nil {
return resp, err
}
}
}

mapElem := micheline.NewMapElem(k, v)
mapResp.Args = append(mapResp.Args, mapElem)
}

resp = mapResp
case _internalStruct:
schemaArgs := schema["args"].([]interface{})

Expand All @@ -151,15 +198,11 @@ func processSchemaEntry(entry interface{}, schema map[string]interface{}) (resp
arg := schemaArgs[i].(map[string]interface{})

argName := arg["name"].(string)
elem := entry.(map[string]interface{})
if _, ok := elem[argName]; !ok {
return resp, errors.New("Schema field '" + argName + "' wasn't found")
}

processedEntry, err := processSchemaEntry(elem[argName], arg)
processedEntry, err := extractValue(argName, arg, entry)
if err != nil {
return resp, err
}

newPair := forgePair(processedEntry, rightPairElem)
rightPairElem = &newPair

Expand Down Expand Up @@ -207,6 +250,15 @@ func processSchemaEntry(entry interface{}, schema map[string]interface{}) (resp
return resp, err
}

func extractValue(argName string, arg map[string]interface{}, entry interface{}) (resp micheline.Prim, err error) {
elem := entry.(map[string]interface{})
if _, ok := elem[argName]; !ok {
return resp, errors.New("Schema field '" + argName + "' wasn't found")
}

return processSchemaEntry(elem[argName], arg)
}

// TODO: define an algorithm to support any number of variants.
// at the moment, support for up to 4 variants covers most cases
func wrapWithVariant(elem micheline.Prim, variantPos int, totalVariantsCount int) (resp micheline.Prim) {
Expand Down
213 changes: 213 additions & 0 deletions internal/blockchain/tezos/ffi2michelson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,48 @@ func Test_processArgsOk(t *testing.T) {
},
},
},
{
name: "valid map input param",
processSchemaReq: map[string]interface{}{
"type": "array",
"prefixItems": []interface{}{
map[string]interface{}{
"name": "varMap",
"type": "object",
"details": map[string]interface{}{
"type": "schema",
"internalSchema": map[string]interface{}{
"type": "map",
"args": []interface{}{
map[string]interface{}{
"name": "key",
"type": "integer",
},
map[string]interface{}{
"name": "value",
"type": "string",
},
},
},
},
},
},
},
input: map[string]interface{}{
"varMap": map[string]interface{}{
"mapEntries": []interface{}{
map[string]interface{}{
"key": float64(1),
"value": "val1",
},
map[string]interface{}{
"key": float64(3),
"value": "val3",
},
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down Expand Up @@ -944,6 +986,177 @@ func Test_processArgsErr(t *testing.T) {
},
expectedError: "invalid object passed",
},
{
name: "no mapEntries for map input param",
processSchemaReq: map[string]interface{}{
"type": "array",
"prefixItems": []interface{}{
map[string]interface{}{
"name": "varMap",
"type": "object",
"details": map[string]interface{}{
"type": "schema",
"internalSchema": map[string]interface{}{
"type": "map",
"args": []interface{}{
map[string]interface{}{
"name": "key",
"type": "integer",
},
map[string]interface{}{
"name": "value",
"type": "string",
},
},
},
},
},
},
},
input: map[string]interface{}{
"varMap": map[string]interface{}{
"invalid": []interface{}{
map[string]interface{}{
"key": float64(1),
"value": "val1",
},
map[string]interface{}{
"key": float64(3),
"value": "val3",
},
},
},
},
expectedError: "mapEntries schema property must be present",
},
{
name: "value missing for map input param",
processSchemaReq: map[string]interface{}{
"type": "array",
"prefixItems": []interface{}{
map[string]interface{}{
"name": "varMap",
"type": "object",
"details": map[string]interface{}{
"type": "schema",
"internalSchema": map[string]interface{}{
"type": "map",
"args": []interface{}{
map[string]interface{}{
"name": "key",
"type": "integer",
},
map[string]interface{}{
"name": "value",
"type": "string",
},
},
},
},
},
},
},
input: map[string]interface{}{
"varMap": map[string]interface{}{
"mapEntries": []interface{}{
map[string]interface{}{
"key": float64(1),
},
map[string]interface{}{
"key": float64(3),
"value": "val3",
},
},
},
},
expectedError: "Schema field 'value' wasn't found",
},
{
name: "key missing for map input param",
processSchemaReq: map[string]interface{}{
"type": "array",
"prefixItems": []interface{}{
map[string]interface{}{
"name": "varMap",
"type": "object",
"details": map[string]interface{}{
"type": "schema",
"internalSchema": map[string]interface{}{
"type": "map",
"args": []interface{}{
map[string]interface{}{
"name": "key",
"type": "integer",
},
map[string]interface{}{
"name": "value",
"type": "string",
},
},
},
},
},
},
},
input: map[string]interface{}{
"varMap": map[string]interface{}{
"mapEntries": []interface{}{
map[string]interface{}{
"value": "val1",
},
map[string]interface{}{
"key": float64(3),
"value": "val3",
},
},
},
},
expectedError: "Schema field 'key' wasn't found",
},
{
name: "unknown field for map input param",
processSchemaReq: map[string]interface{}{
"type": "array",
"prefixItems": []interface{}{
map[string]interface{}{
"name": "varMap",
"type": "object",
"details": map[string]interface{}{
"type": "schema",
"internalSchema": map[string]interface{}{
"type": "map",
"args": []interface{}{
map[string]interface{}{
"name": "key",
"type": "integer",
},
map[string]interface{}{
"name": "value",
"type": "string",
},
},
},
},
},
},
},
input: map[string]interface{}{
"varMap": map[string]interface{}{
"mapEntries": []interface{}{
map[string]interface{}{
"key": float64(1),
"value": "val1",
"unknown": float64(1),
},
map[string]interface{}{
"key": float64(3),
"value": "val3",
},
},
},
},
expectedError: "Unknown schema field 'unknown' in map entry",
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit 5c91279

Please sign in to comment.