From 8ea5185c59ba217c919d8da314dbde2e3ff31c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 14 Sep 2020 18:02:00 -0700 Subject: [PATCH] add support for exporting, encoding, and decoding capabilities --- encoding/json/decode.go | 27 ++++++++++++++++- encoding/json/encode.go | 26 ++++++++++++++-- encoding/json/encoding_test.go | 34 +++++++++++++++------ runtime/convertValues.go | 55 ++++++++++++++++++++-------------- runtime/convertValues_test.go | 25 ++++++++++++++++ values.go | 23 ++++++++++++-- 6 files changed, 152 insertions(+), 38 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 8a9cc82788..f1319ca7e5 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -103,6 +103,8 @@ const ( domainKey = "domain" identifierKey = "identifier" staticTypeKey = "staticType" + addressKey = "address" + pathKey = "path" ) var ErrInvalidJSONCadence = errors.New("invalid JSON Cadence structure") @@ -193,6 +195,8 @@ func decodeJSON(v interface{}) cadence.Value { return decodePath(valueJSON) case typeTypeStr: return decodeTypeValue(valueJSON) + case capabilityTypeStr: + return decodeCapability(valueJSON) } panic(ErrInvalidJSONCadence) @@ -582,8 +586,13 @@ func decodeStorageReference(valueJSON interface{}) cadence.StorageReference { func decodeLink(valueJSON interface{}) cadence.Link { obj := toObject(valueJSON) + targetPath, ok := decodeJSON(obj.Get(targetPathKey)).(cadence.Path) + if !ok { + // TODO: improve error message + panic(ErrInvalidJSONCadence) + } return cadence.NewLink( - obj.GetString(targetPathKey), + targetPath, obj.GetString(borrowTypeKey), ) } @@ -605,6 +614,22 @@ func decodeTypeValue(valueJSON interface{}) cadence.TypeValue { } } +func decodeCapability(valueJSON interface{}) cadence.Capability { + obj := toObject(valueJSON) + + path, ok := decodeJSON(obj.Get(pathKey)).(cadence.Path) + if !ok { + // TODO: improve error message + panic(ErrInvalidJSONCadence) + } + + return cadence.Capability{ + Path: path, + Address: decodeAddress(obj.Get(addressKey)), + BorrowType: obj.GetString(borrowTypeKey), + } +} + // JSON types type jsonObject map[string]interface{} diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 3644d31da5..a6249b0c40 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -132,8 +132,8 @@ type jsonStorageReferenceValue struct { } type jsonLinkValue struct { - TargetPath string `json:"targetPath"` - BorrowType string `json:"borrowType"` + TargetPath jsonValue `json:"targetPath"` + BorrowType string `json:"borrowType"` } type jsonPathValue struct { @@ -145,6 +145,12 @@ type jsonTypeValue struct { StaticType string `json:"staticType"` } +type jsonCapabilityValue struct { + Path jsonValue `json:"path"` + Address string `json:"address"` + BorrowType string `json:"borrowType"` +} + const ( voidTypeStr = "Void" optionalTypeStr = "Optional" @@ -181,6 +187,7 @@ const ( linkTypeStr = "Link" pathTypeStr = "Path" typeTypeStr = "Type" + capabilityTypeStr = "Capability" ) // prepare traverses the object graph of the provided value and constructs @@ -257,6 +264,8 @@ func (e *Encoder) prepare(v cadence.Value) jsonValue { return e.preparePath(x) case cadence.TypeValue: return e.prepareTypeValue(x) + case cadence.Capability: + return e.prepareCapability(x) default: panic(fmt.Errorf("unsupported value: %T, %v", v, v)) } @@ -538,7 +547,7 @@ func (e *Encoder) prepareLink(x cadence.Link) jsonValue { return jsonValueObject{ Type: linkTypeStr, Value: jsonLinkValue{ - TargetPath: x.TargetPath, + TargetPath: e.preparePath(x.TargetPath), BorrowType: x.BorrowType, }, } @@ -563,6 +572,17 @@ func (e *Encoder) prepareTypeValue(x cadence.TypeValue) jsonValue { } } +func (e *Encoder) prepareCapability(x cadence.Capability) jsonValue { + return jsonValueObject{ + Type: capabilityTypeStr, + Value: jsonCapabilityValue{ + Path: e.preparePath(x.Path), + Address: encodeBytes(x.Address.Bytes()), + BorrowType: x.BorrowType, + }, + } +} + 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 36ee42023f..9003ab5197 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -978,8 +978,11 @@ func TestEncodeLink(t *testing.T) { testEncodeAndDecode( t, - cadence.NewLink("/storage/foo", "Bar"), - `{"type":"Link","value":{"targetPath":"/storage/foo","borrowType":"Bar"}}`, + cadence.NewLink( + cadence.Path{Domain: "storage", Identifier: "foo"}, + "Bar", + ), + `{"type":"Link","value":{"targetPath":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":"Bar"}}`, ) } @@ -996,6 +999,21 @@ func TestEncodeType(t *testing.T) { ) } +func TestEncodeCapability(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Capability{ + Path: cadence.Path{Domain: "storage", Identifier: "foo"}, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: "Int", + }, + `{"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":"Int","address":"0x0000000102030405"}}`, + ) +} + func TestDecodeFixedPoints(t *testing.T) { t.Parallel() @@ -1251,13 +1269,11 @@ func TestEncodePath(t *testing.T) { t.Parallel() - testAllEncodeAndDecode(t, []encodeTest{ - { - "Simple", - cadence.Path{Domain: "storage", Identifier: "foo"}, - `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, - }, - }...) + testEncodeAndDecode( + t, + cadence.Path{Domain: "storage", Identifier: "foo"}, + `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, + ) } func convertValueFromScript(t *testing.T, script string) cadence.Value { diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 1355fd31f2..6db2c8faa9 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -114,12 +114,14 @@ func exportValueWithInterpreter(value interpreter.Value, inter *interpreter.Inte return exportPathValue(v) case interpreter.TypeValue: return exportTypeValue(v, inter) + case interpreter.CapabilityValue: + return exportCapabilityValue(v, inter) } panic(fmt.Sprintf("cannot export value of type %T", value)) } -func exportSomeValue(v *interpreter.SomeValue, inter *interpreter.Interpreter) cadence.Value { +func exportSomeValue(v *interpreter.SomeValue, inter *interpreter.Interpreter) cadence.Optional { if v.Value == nil { return cadence.NewOptional(nil) } @@ -129,7 +131,7 @@ func exportSomeValue(v *interpreter.SomeValue, inter *interpreter.Interpreter) c return cadence.NewOptional(value) } -func exportArrayValue(v *interpreter.ArrayValue, inter *interpreter.Interpreter) cadence.Value { +func exportArrayValue(v *interpreter.ArrayValue, inter *interpreter.Interpreter) cadence.Array { values := make([]cadence.Value, len(v.Values)) for i, value := range v.Values { @@ -183,7 +185,7 @@ func exportCompositeValue(v *interpreter.CompositeValue, inter *interpreter.Inte )) } -func exportDictionaryValue(v *interpreter.DictionaryValue, inter *interpreter.Interpreter) cadence.Value { +func exportDictionaryValue(v *interpreter.DictionaryValue, inter *interpreter.Interpreter) cadence.Dictionary { pairs := make([]cadence.KeyValuePair, v.Count()) for i, keyValue := range v.Keys.Values { @@ -205,7 +207,7 @@ func exportDictionaryValue(v *interpreter.DictionaryValue, inter *interpreter.In return cadence.NewDictionary(pairs) } -func exportStorageReferenceValue(v *interpreter.StorageReferenceValue) cadence.Value { +func exportStorageReferenceValue(v *interpreter.StorageReferenceValue) cadence.StorageReference { return cadence.NewStorageReference( v.Authorized, cadence.NewAddress(v.TargetStorageAddress), @@ -213,12 +215,33 @@ func exportStorageReferenceValue(v *interpreter.StorageReferenceValue) cadence.V ) } -func exportLinkValue(v interpreter.LinkValue, inter *interpreter.Interpreter) cadence.Value { +func exportLinkValue(v interpreter.LinkValue, inter *interpreter.Interpreter) cadence.Link { + path := exportPathValue(v.TargetPath) ty := inter.ConvertStaticToSemaType(v.Type).QualifiedString() - return cadence.NewLink( - v.TargetPath.String(), - ty, - ) + return cadence.NewLink(path, ty) +} + +func exportPathValue(v interpreter.PathValue) cadence.Path { + return cadence.Path{ + Domain: v.Domain.Name(), + Identifier: v.Identifier, + } +} + +func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) cadence.TypeValue { + ty := inter.ConvertStaticToSemaType(v.Type).QualifiedString() + return cadence.TypeValue{ + StaticType: ty, + } +} + +func exportCapabilityValue(v interpreter.CapabilityValue, inter *interpreter.Interpreter) cadence.Capability { + borrowType := inter.ConvertStaticToSemaType(v.BorrowType).QualifiedString() + return cadence.Capability{ + Path: exportPathValue(v.Path), + Address: cadence.NewAddress(v.Address), + BorrowType: borrowType, + } } // importValue converts a Cadence value to a runtime value. @@ -353,17 +376,3 @@ func importCompositeValue( nil, ) } - -func exportPathValue(v interpreter.PathValue) cadence.Value { - return cadence.Path{ - Domain: v.Domain.Name(), - Identifier: v.Identifier, - } -} - -func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) cadence.Value { - ty := inter.ConvertStaticToSemaType(v.Type).QualifiedString() - return cadence.TypeValue{ - StaticType: ty, - } -} diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 17db79badc..281b50055d 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -639,6 +639,31 @@ func TestExportTypeValue(t *testing.T) { assert.Equal(t, expected, actual) } +func TestExportCapabilityValue(t *testing.T) { + + t.Parallel() + + capability := interpreter.CapabilityValue{ + Address: interpreter.AddressValue{0x1}, + Path: interpreter.PathValue{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, + BorrowType: interpreter.PrimitiveStaticTypeInt, + } + actual := exportValueWithInterpreter(capability, nil) + expected := cadence.Capability{ + Path: cadence.Path{ + Domain: "storage", + Identifier: "foo", + }, + Address: cadence.Address{0x1}, + BorrowType: "Int", + } + + assert.Equal(t, expected, actual) +} + const fooID = "Foo" var fooTypeID = fmt.Sprintf("S.%s.%s", utils.TestLocation, fooID) diff --git a/values.go b/values.go index 1feb005b70..22a18ebf6b 100644 --- a/values.go +++ b/values.go @@ -962,12 +962,12 @@ func (v Contract) ToGoValue() interface{} { // Link type Link struct { - TargetPath string + TargetPath Path // TODO: a future version might want to export the whole type BorrowType string } -func NewLink(targetPath string, borrowType string) Link { +func NewLink(targetPath Path, borrowType string) Link { return Link{ TargetPath: targetPath, BorrowType: borrowType, @@ -1043,3 +1043,22 @@ func (TypeValue) Type() Type { func (TypeValue) ToGoValue() interface{} { return nil } + +// Capability + +type Capability struct { + Path Path + Address Address + // TODO: a future version might want to export the whole type + BorrowType string +} + +func (Capability) isValue() {} + +func (Capability) Type() Type { + return CapabilityType{} +} + +func (Capability) ToGoValue() interface{} { + return nil +}