Skip to content

Commit 5c91279

Browse files
authored
Merge pull request #1451 from alex-semenyuk/map_impl
Tezos Connector map converter
2 parents bab1bc4 + 6a51572 commit 5c91279

File tree

2 files changed

+272
-7
lines changed

2 files changed

+272
-7
lines changed

internal/blockchain/tezos/ffi2michelson.go

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2023 Kaleido, Inc.
1+
// Copyright © 2024 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -19,6 +19,7 @@ package tezos
1919
import (
2020
"errors"
2121
"fmt"
22+
"slices"
2223

2324
"blockwatch.cc/tzgo/micheline"
2425
"blockwatch.cc/tzgo/tezos"
@@ -34,6 +35,7 @@ const (
3435
_internalBoolean = "boolean"
3536
_internalList = "list"
3637
_internalStruct = "struct"
38+
_internalMap = "map"
3739
_internalInteger = "integer"
3840
_internalNat = "nat"
3941
_internalString = "string"
@@ -43,6 +45,13 @@ const (
4345
_internalBytes = "bytes"
4446
)
4547

48+
// Tezos map
49+
const (
50+
_key = "key"
51+
_value = "value"
52+
_mapEntries = "mapEntries"
53+
)
54+
4655
func processArgs(payloadSchema map[string]interface{}, input map[string]interface{}, methodName string) (micheline.Parameters, error) {
4756
params := micheline.Parameters{
4857
Entrypoint: methodName,
@@ -143,6 +152,44 @@ func processMichelson(entry interface{}, details map[string]interface{}) (resp m
143152
func processSchemaEntry(entry interface{}, schema map[string]interface{}) (resp micheline.Prim, err error) {
144153
entryType := schema["type"].(string)
145154
switch entryType {
155+
case _internalMap:
156+
schemaArgs := schema["args"].([]interface{})
157+
158+
mapResp := micheline.NewMap()
159+
mapEntries := entry.(map[string]interface{})[_mapEntries]
160+
if mapEntries == nil {
161+
return resp, fmt.Errorf("mapEntries schema property must be present")
162+
}
163+
for _, mapEntry := range mapEntries.([]interface{}) {
164+
for name := range mapEntry.(map[string]interface{}) {
165+
if !slices.Contains([]string{_key, _value}, name) {
166+
return resp, errors.New("Unknown schema field '" + name + "' in map entry")
167+
}
168+
}
169+
170+
var k micheline.Prim
171+
var v micheline.Prim
172+
for i := len(schemaArgs) - 1; i >= 0; i-- {
173+
arg := schemaArgs[i].(map[string]interface{})
174+
175+
if arg["name"] == _key {
176+
if k, err = extractValue(_key, arg, mapEntry); err != nil {
177+
return resp, err
178+
}
179+
}
180+
181+
if arg["name"] == _value {
182+
if v, err = extractValue(_value, arg, mapEntry); err != nil {
183+
return resp, err
184+
}
185+
}
186+
}
187+
188+
mapElem := micheline.NewMapElem(k, v)
189+
mapResp.Args = append(mapResp.Args, mapElem)
190+
}
191+
192+
resp = mapResp
146193
case _internalStruct:
147194
schemaArgs := schema["args"].([]interface{})
148195

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

153200
argName := arg["name"].(string)
154-
elem := entry.(map[string]interface{})
155-
if _, ok := elem[argName]; !ok {
156-
return resp, errors.New("Schema field '" + argName + "' wasn't found")
157-
}
158-
159-
processedEntry, err := processSchemaEntry(elem[argName], arg)
201+
processedEntry, err := extractValue(argName, arg, entry)
160202
if err != nil {
161203
return resp, err
162204
}
205+
163206
newPair := forgePair(processedEntry, rightPairElem)
164207
rightPairElem = &newPair
165208

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

253+
func extractValue(argName string, arg map[string]interface{}, entry interface{}) (resp micheline.Prim, err error) {
254+
elem := entry.(map[string]interface{})
255+
if _, ok := elem[argName]; !ok {
256+
return resp, errors.New("Schema field '" + argName + "' wasn't found")
257+
}
258+
259+
return processSchemaEntry(elem[argName], arg)
260+
}
261+
210262
// TODO: define an algorithm to support any number of variants.
211263
// at the moment, support for up to 4 variants covers most cases
212264
func wrapWithVariant(elem micheline.Prim, variantPos int, totalVariantsCount int) (resp micheline.Prim) {

internal/blockchain/tezos/ffi2michelson_test.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,48 @@ func Test_processArgsOk(t *testing.T) {
503503
},
504504
},
505505
},
506+
{
507+
name: "valid map input param",
508+
processSchemaReq: map[string]interface{}{
509+
"type": "array",
510+
"prefixItems": []interface{}{
511+
map[string]interface{}{
512+
"name": "varMap",
513+
"type": "object",
514+
"details": map[string]interface{}{
515+
"type": "schema",
516+
"internalSchema": map[string]interface{}{
517+
"type": "map",
518+
"args": []interface{}{
519+
map[string]interface{}{
520+
"name": "key",
521+
"type": "integer",
522+
},
523+
map[string]interface{}{
524+
"name": "value",
525+
"type": "string",
526+
},
527+
},
528+
},
529+
},
530+
},
531+
},
532+
},
533+
input: map[string]interface{}{
534+
"varMap": map[string]interface{}{
535+
"mapEntries": []interface{}{
536+
map[string]interface{}{
537+
"key": float64(1),
538+
"value": "val1",
539+
},
540+
map[string]interface{}{
541+
"key": float64(3),
542+
"value": "val3",
543+
},
544+
},
545+
},
546+
},
547+
},
506548
}
507549
for _, tc := range testCases {
508550
t.Run(tc.name, func(t *testing.T) {
@@ -944,6 +986,177 @@ func Test_processArgsErr(t *testing.T) {
944986
},
945987
expectedError: "invalid object passed",
946988
},
989+
{
990+
name: "no mapEntries for map input param",
991+
processSchemaReq: map[string]interface{}{
992+
"type": "array",
993+
"prefixItems": []interface{}{
994+
map[string]interface{}{
995+
"name": "varMap",
996+
"type": "object",
997+
"details": map[string]interface{}{
998+
"type": "schema",
999+
"internalSchema": map[string]interface{}{
1000+
"type": "map",
1001+
"args": []interface{}{
1002+
map[string]interface{}{
1003+
"name": "key",
1004+
"type": "integer",
1005+
},
1006+
map[string]interface{}{
1007+
"name": "value",
1008+
"type": "string",
1009+
},
1010+
},
1011+
},
1012+
},
1013+
},
1014+
},
1015+
},
1016+
input: map[string]interface{}{
1017+
"varMap": map[string]interface{}{
1018+
"invalid": []interface{}{
1019+
map[string]interface{}{
1020+
"key": float64(1),
1021+
"value": "val1",
1022+
},
1023+
map[string]interface{}{
1024+
"key": float64(3),
1025+
"value": "val3",
1026+
},
1027+
},
1028+
},
1029+
},
1030+
expectedError: "mapEntries schema property must be present",
1031+
},
1032+
{
1033+
name: "value missing for map input param",
1034+
processSchemaReq: map[string]interface{}{
1035+
"type": "array",
1036+
"prefixItems": []interface{}{
1037+
map[string]interface{}{
1038+
"name": "varMap",
1039+
"type": "object",
1040+
"details": map[string]interface{}{
1041+
"type": "schema",
1042+
"internalSchema": map[string]interface{}{
1043+
"type": "map",
1044+
"args": []interface{}{
1045+
map[string]interface{}{
1046+
"name": "key",
1047+
"type": "integer",
1048+
},
1049+
map[string]interface{}{
1050+
"name": "value",
1051+
"type": "string",
1052+
},
1053+
},
1054+
},
1055+
},
1056+
},
1057+
},
1058+
},
1059+
input: map[string]interface{}{
1060+
"varMap": map[string]interface{}{
1061+
"mapEntries": []interface{}{
1062+
map[string]interface{}{
1063+
"key": float64(1),
1064+
},
1065+
map[string]interface{}{
1066+
"key": float64(3),
1067+
"value": "val3",
1068+
},
1069+
},
1070+
},
1071+
},
1072+
expectedError: "Schema field 'value' wasn't found",
1073+
},
1074+
{
1075+
name: "key missing for map input param",
1076+
processSchemaReq: map[string]interface{}{
1077+
"type": "array",
1078+
"prefixItems": []interface{}{
1079+
map[string]interface{}{
1080+
"name": "varMap",
1081+
"type": "object",
1082+
"details": map[string]interface{}{
1083+
"type": "schema",
1084+
"internalSchema": map[string]interface{}{
1085+
"type": "map",
1086+
"args": []interface{}{
1087+
map[string]interface{}{
1088+
"name": "key",
1089+
"type": "integer",
1090+
},
1091+
map[string]interface{}{
1092+
"name": "value",
1093+
"type": "string",
1094+
},
1095+
},
1096+
},
1097+
},
1098+
},
1099+
},
1100+
},
1101+
input: map[string]interface{}{
1102+
"varMap": map[string]interface{}{
1103+
"mapEntries": []interface{}{
1104+
map[string]interface{}{
1105+
"value": "val1",
1106+
},
1107+
map[string]interface{}{
1108+
"key": float64(3),
1109+
"value": "val3",
1110+
},
1111+
},
1112+
},
1113+
},
1114+
expectedError: "Schema field 'key' wasn't found",
1115+
},
1116+
{
1117+
name: "unknown field for map input param",
1118+
processSchemaReq: map[string]interface{}{
1119+
"type": "array",
1120+
"prefixItems": []interface{}{
1121+
map[string]interface{}{
1122+
"name": "varMap",
1123+
"type": "object",
1124+
"details": map[string]interface{}{
1125+
"type": "schema",
1126+
"internalSchema": map[string]interface{}{
1127+
"type": "map",
1128+
"args": []interface{}{
1129+
map[string]interface{}{
1130+
"name": "key",
1131+
"type": "integer",
1132+
},
1133+
map[string]interface{}{
1134+
"name": "value",
1135+
"type": "string",
1136+
},
1137+
},
1138+
},
1139+
},
1140+
},
1141+
},
1142+
},
1143+
input: map[string]interface{}{
1144+
"varMap": map[string]interface{}{
1145+
"mapEntries": []interface{}{
1146+
map[string]interface{}{
1147+
"key": float64(1),
1148+
"value": "val1",
1149+
"unknown": float64(1),
1150+
},
1151+
map[string]interface{}{
1152+
"key": float64(3),
1153+
"value": "val3",
1154+
},
1155+
},
1156+
},
1157+
},
1158+
expectedError: "Unknown schema field 'unknown' in map entry",
1159+
},
9471160
}
9481161

9491162
for _, tc := range testCases {

0 commit comments

Comments
 (0)