diff --git a/encoding/json/decode.go b/encoding/json/decode.go index c71e596a2b..c791e7d19b 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -100,6 +100,8 @@ const ( targetKeyKey = "targetKey" targetPathKey = "targetPath" borrowTypeKey = "borrowType" + domainKey = "domain" + identifierKey = "identifier" ) var ErrInvalidJSONCadence = errors.New("invalid JSON Cadence structure") @@ -186,6 +188,8 @@ func decodeJSON(v interface{}) cadence.Value { return decodeStorageReference(valueJSON) case linkTypeStr: return decodeLink(valueJSON) + case pathTypeStr: + return decodePath(valueJSON) } panic(ErrInvalidJSONCadence) @@ -581,6 +585,15 @@ func decodeLink(valueJSON interface{}) cadence.Link { ) } +func decodePath(valueJSON interface{}) cadence.Path { + obj := toObject(valueJSON) + + return cadence.Path{ + Domain: obj.GetString(domainKey), + Identifier: obj.GetString(identifierKey), + } +} + // JSON types type jsonObject map[string]interface{} diff --git a/encoding/json/encode.go b/encoding/json/encode.go index b32a191ef8..4f647ba41a 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -129,6 +129,11 @@ type jsonLinkValue struct { BorrowType string `json:"borrowType"` } +type jsonPathValue struct { + Domain string `json:"domain"` + Identifier string `json:"identifier"` +} + const ( voidTypeStr = "Void" optionalTypeStr = "Optional" @@ -163,6 +168,7 @@ const ( contractTypeStr = "Contract" storageReferenceTypeStr = "StorageReference" linkTypeStr = "Link" + pathTypeStr = "Path" ) // prepare traverses the object graph of the provided value and constructs @@ -235,6 +241,8 @@ func (e *Encoder) prepare(v cadence.Value) jsonValue { return e.prepareStorageReference(x) case cadence.Link: return e.prepareLink(x) + case cadence.Path: + return e.preparePath(x) default: panic(fmt.Errorf("unsupported value: %T, %v", v, v)) } @@ -522,6 +530,16 @@ func (e *Encoder) prepareLink(x cadence.Link) jsonValue { } } +func (e *Encoder) preparePath(x cadence.Path) jsonValue { + return jsonValueObject{ + Type: pathTypeStr, + Value: jsonPathValue{ + Domain: x.Domain, + Identifier: x.Identifier, + }, + } +} + func encodeBytes(v []byte) string { return fmt.Sprintf("0x%x", v) } diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 48d082a3b2..1189576067 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1204,6 +1204,19 @@ func TestDecodeFixedPoints(t *testing.T) { }) } +func TestEncodePath(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Simple", + cadence.Path{Domain: "storage", Identifier: "foo"}, + `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, + }, + }...) +} + func convertValueFromScript(t *testing.T, script string) cadence.Value { rt := runtime.NewInterpreterRuntime() @@ -1220,11 +1233,20 @@ func convertValueFromScript(t *testing.T, script string) cadence.Value { } func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - testEncodeAndDecode(t, test.val, test.expected) + + test := func(testCase encodeTest) { + + t.Run(testCase.name, func(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode(t, testCase.val, testCase.expected) }) } + + for _, testCase := range tests { + test(testCase) + } } func testEncodeAndDecode(t *testing.T, val cadence.Value, expectedJSON string) { diff --git a/runtime/common/pathdomain.go b/runtime/common/pathdomain.go index bd9f570389..b8193ab11e 100644 --- a/runtime/common/pathdomain.go +++ b/runtime/common/pathdomain.go @@ -83,3 +83,18 @@ func (i PathDomain) Name() string { panic(errors.NewUnreachableError()) } + +func PathDomainFromName(name string) PathDomain { + switch name { + case "storage": + return PathDomainStorage + + case "private": + return PathDomainPrivate + + case "public": + return PathDomainPublic + } + + panic(errors.NewUnreachableError()) +} diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 95271eeb68..8ebde01646 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -110,6 +110,8 @@ func exportValueWithInterpreter(value interpreter.Value, inter *interpreter.Inte return exportStorageReferenceValue(v) case interpreter.LinkValue: return exportLinkValue(v, inter) + case interpreter.PathValue: + return exportPathValue(v) } panic(fmt.Sprintf("cannot export value of type %T", value)) @@ -281,6 +283,11 @@ func importValue(value cadence.Value) interpreter.Value { return importCompositeValue(common.CompositeKindResource, v.ResourceType.ID(), v.ResourceType.Fields, v.Fields) case cadence.Event: return importCompositeValue(common.CompositeKindEvent, v.EventType.ID(), v.EventType.Fields, v.Fields) + case cadence.Path: + return interpreter.PathValue{ + Domain: common.PathDomainFromName(v.Domain), + Identifier: v.Identifier, + } } panic(fmt.Sprintf("cannot import value of type %T", value)) @@ -343,3 +350,10 @@ func importCompositeValue( nil, ) } + +func exportPathValue(v interpreter.PathValue) cadence.Value { + return cadence.Path{ + Domain: v.Domain.Name(), + Identifier: v.Identifier, + } +} diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index f7204bde19..3d09082b9e 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/tests/utils" @@ -193,6 +194,17 @@ var exportTests = []exportTest{ value: interpreter.UFix64Value(123000000), expected: cadence.UFix64(123000000), }, + { + label: "Path", + value: interpreter.PathValue{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, + expected: cadence.Path{ + Domain: "storage", + Identifier: "foo", + }, + }, } func TestExportValue(t *testing.T) { diff --git a/values.go b/values.go index b25e61977d..7499d11eb1 100644 --- a/values.go +++ b/values.go @@ -1009,3 +1009,20 @@ func (v StorageReference) Type() Type { func (v StorageReference) ToGoValue() interface{} { return nil } + +// Path + +type Path struct { + Domain string + Identifier string +} + +func (Path) isValue() {} + +func (Path) Type() Type { + return PathType{} +} + +func (Path) ToGoValue() interface{} { + return nil +}