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

Allow export and JSON encoding/decoding of type values and capabilities #374

Merged
merged 2 commits into from
Sep 28, 2020
Merged
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
38 changes: 37 additions & 1 deletion encoding/json/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ const (
borrowTypeKey = "borrowType"
domainKey = "domain"
identifierKey = "identifier"
staticTypeKey = "staticType"
addressKey = "address"
pathKey = "path"
)

var ErrInvalidJSONCadence = errors.New("invalid JSON Cadence structure")
Expand Down Expand Up @@ -190,6 +193,10 @@ func decodeJSON(v interface{}) cadence.Value {
return decodeLink(valueJSON)
case pathTypeStr:
return decodePath(valueJSON)
case typeTypeStr:
return decodeTypeValue(valueJSON)
case capabilityTypeStr:
return decodeCapability(valueJSON)
}

panic(ErrInvalidJSONCadence)
Expand Down Expand Up @@ -579,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),
)
}
Expand All @@ -594,6 +606,30 @@ func decodePath(valueJSON interface{}) cadence.Path {
}
}

func decodeTypeValue(valueJSON interface{}) cadence.TypeValue {
obj := toObject(valueJSON)

return cadence.TypeValue{
StaticType: obj.GetString(staticTypeKey),
}
}

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{}
Expand Down
42 changes: 39 additions & 3 deletions encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,25 @@ 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 {
Domain string `json:"domain"`
Identifier string `json:"identifier"`
}

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"
Expand Down Expand Up @@ -176,6 +186,8 @@ const (
storageReferenceTypeStr = "StorageReference"
linkTypeStr = "Link"
pathTypeStr = "Path"
typeTypeStr = "Type"
capabilityTypeStr = "Capability"
)

// prepare traverses the object graph of the provided value and constructs
Expand Down Expand Up @@ -250,6 +262,10 @@ func (e *Encoder) prepare(v cadence.Value) jsonValue {
return e.prepareLink(x)
case cadence.Path:
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))
}
Expand Down Expand Up @@ -531,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,
},
}
Expand All @@ -547,6 +563,26 @@ func (e *Encoder) preparePath(x cadence.Path) jsonValue {
}
}

func (e *Encoder) prepareTypeValue(x cadence.TypeValue) jsonValue {
return jsonValueObject{
Type: typeTypeStr,
Value: jsonTypeValue{
StaticType: x.StaticType,
},
}
}

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)
}
Expand Down
47 changes: 38 additions & 9 deletions encoding/json/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -978,8 +978,39 @@ 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"}}`,
)
}

func TestEncodeType(t *testing.T) {

t.Parallel()

testEncodeAndDecode(
t,
cadence.TypeValue{
StaticType: "Int",
},
`{"type":"Type","value":{"staticType":"Int"}}`,
)
}

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"}}`,
)
}

Expand Down Expand Up @@ -1238,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 {
Expand Down
51 changes: 35 additions & 16 deletions runtime/convertValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,16 @@ func exportValueWithInterpreter(value interpreter.Value, inter *interpreter.Inte
return exportLinkValue(v, inter)
case interpreter.PathValue:
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)
}
Expand All @@ -127,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 {
Expand Down Expand Up @@ -181,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 {
Expand All @@ -203,19 +207,41 @@ 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),
v.TargetKey,
)
}

func exportLinkValue(v interpreter.LinkValue, inter *interpreter.Interpreter) cadence.Value {
return cadence.NewLink(
v.TargetPath.String(),
inter.ConvertStaticToSemaType(v.Type).QualifiedString(),
)
func exportLinkValue(v interpreter.LinkValue, inter *interpreter.Interpreter) cadence.Link {
path := exportPathValue(v.TargetPath)
ty := inter.ConvertStaticToSemaType(v.Type).QualifiedString()
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.
Expand Down Expand Up @@ -350,10 +376,3 @@ func importCompositeValue(
nil,
)
}

func exportPathValue(v interpreter.PathValue) cadence.Value {
return cadence.Path{
Domain: v.Domain.Name(),
Identifier: v.Identifier,
}
}
43 changes: 43 additions & 0 deletions runtime/convertValues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,49 @@ func exportValueFromScript(t *testing.T, script string) cadence.Value {
return value
}

func TestExportTypeValue(t *testing.T) {

t.Parallel()

script := `
access(all) fun main(): Type {
return Type<Int>()
}
`

actual := exportValueFromScript(t, script)
expected := cadence.TypeValue{
StaticType: "Int",
}

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)
Expand Down
Loading