Skip to content

Commit

Permalink
refactor inference of address location name from type ID, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent committed Nov 25, 2020
1 parent e027ad0 commit 973aeaa
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 53 deletions.
77 changes: 36 additions & 41 deletions runtime/interpreter/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,28 +460,9 @@ func (d *Decoder) decodeComposite(v interface{}, path []string) (*CompositeValue
}
typeID := sema.TypeID(encodedTypeID)

// Special case: The decoded location is an address location without name.
//
// In the first version of the storage format, accounts could only store one contract
// instead of several contracts (separated by name), so composite's locations were
// address locations without a name, i.e. just the bare address.
//
// An update added support for multiple contracts per account, which added names to address locations:
// Each contract of an account is stored in a distinct location.
//
// So to keep backwards-compatibility:
// If the location is an address location without a name,
// then infer the name from the type ID.
// Special case: The decoded location might be an address location which has no name

if addressLocation, ok := location.(ast.AddressLocation); ok && addressLocation.Name == "" {
qualifiedIdentifier := location.QualifiedIdentifier(typeID)
parts := strings.SplitN(qualifiedIdentifier, ".", 2)

location = ast.AddressLocation{
Address: addressLocation.Address,
Name: parts[0],
}
}
location = d.inferAddressLocationName(location, typeID)

// Kind

Expand Down Expand Up @@ -1118,30 +1099,44 @@ func (d *Decoder) decodeLocationAndTypeID(
}
typeID := sema.TypeID(encodedTypeID)

// Special case: The decoded location is an address location without name.
//
// In the first version of the storage format, accounts could only store one contract
// instead of several contracts (separated by name), so composite's locations were
// address locations without a name, i.e. just the bare address.
//
// An update added support for multiple contracts per account, which added names to address locations:
// Each contract of an account is stored in a distinct location.
//
// So to keep backwards-compatibility:
// If the location is an address location without a name,
// then infer the name from the type ID.
// Special case: The decoded location might be an address location which has no name

if addressLocation, ok := location.(ast.AddressLocation); ok && addressLocation.Name == "" {
qualifiedIdentifier := location.QualifiedIdentifier(typeID)
parts := strings.SplitN(qualifiedIdentifier, ".", 2)
location = d.inferAddressLocationName(location, typeID)

location = ast.AddressLocation{
Address: addressLocation.Address,
Name: parts[0],
}
return location, typeID, nil
}

// inferAddressLocationName infers the name for an address location from a type ID.
//
// In the first version of the storage format, accounts could only store one contract
// instead of several contracts (separated by name), so composite's locations were
// address locations without a name, i.e. just the bare address.
//
// An update added support for multiple contracts per account, which added names to address locations:
// Each contract of an account is stored in a distinct location.
//
// So to keep backwards-compatibility:
// If the location is an address location without a name,
// then infer the name from the type ID.
//
func (d *Decoder) inferAddressLocationName(location ast.Location, typeID sema.TypeID) ast.Location {

// Only consider address locations which have no name

addressLocation, ok := location.(ast.AddressLocation)
if !ok || addressLocation.Name != "" {
return location
}

return location, typeID, nil
// The first component of the type ID is the location name

qualifiedIdentifier := location.QualifiedIdentifier(typeID)
parts := strings.SplitN(qualifiedIdentifier, ".", 2)

return ast.AddressLocation{
Address: addressLocation.Address,
Name: parts[0],
}
}

func (d *Decoder) decodeCompositeStaticType(v interface{}) (StaticType, error) {
Expand Down
118 changes: 106 additions & 12 deletions runtime/interpreter/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3286,7 +3286,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
TargetPath: publicPathValue,
Type: ConvertSemaToPrimitiveStaticType(&sema.BoolType{}),
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagPrimitiveStaticType,
0x6,
Expand All @@ -3304,7 +3305,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Type: PrimitiveStaticTypeBool,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagOptionalStaticType,
// tag
Expand All @@ -3325,7 +3327,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Location: utils.TestLocation,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagCompositeStaticType,
// map, 2 pairs of items follow
Expand All @@ -3351,6 +3354,47 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
)
})

t.Run("composite, struct, address location without name", func(t *testing.T) {
testEncodeDecode(t,
encodeDecodeTest{
decodeOnly: true,
decodedValue: LinkValue{
TargetPath: publicPathValue,
Type: CompositeStaticType{
TypeID: "A.0x1.SimpleStruct",
Location: ast.AddressLocation{
Address: common.BytesToAddress([]byte{0x1}),
Name: "SimpleStruct",
},
},
},
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagCompositeStaticType,
// map, 2 pairs of items follow
0xa2,
// key 0
0x0,
// tag
0xd8, cborTagAddressLocation,
// byte sequence, length 1
0x41,
// positive integer 1
0x1,
// key 1
0x1,
// UTF-8 string, length 18
0x72,
// A.0x1.SimpleStruct
0x41,
0x2E, 0x30, 0x78, 0x31,
0x2E, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
),
},
)
})

t.Run("interface, struct", func(t *testing.T) {
testEncodeDecode(t,
encodeDecodeTest{
Expand All @@ -3361,7 +3405,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Location: utils.TestLocation,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagInterfaceStaticType,
// map, 2 pairs of items follow
Expand All @@ -3387,6 +3432,47 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
)
})

t.Run("interface, struct, address location without name", func(t *testing.T) {
testEncodeDecode(t,
encodeDecodeTest{
decodeOnly: true,
decodedValue: LinkValue{
TargetPath: publicPathValue,
Type: InterfaceStaticType{
TypeID: "A.0x1.SimpleInterface",
Location: ast.AddressLocation{
Address: common.BytesToAddress([]byte{0x1}),
Name: "SimpleInterface",
},
},
},
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagInterfaceStaticType,
// map, 2 pairs of items follow
0xa2,
// key 0
0x0,
// tag
0xd8, cborTagAddressLocation,
// byte sequence, length 1
0x41,
// positive integer 1
0x1,
// key 1
0x1,
// UTF-8 string, length 21
0x75,
// A.0x1.SimpleInterface
0x41,
0x2E, 0x30, 0x78, 0x31,
0x2E, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
),
},
)
})

t.Run("variable-sized, bool", func(t *testing.T) {
testEncodeDecode(t,
encodeDecodeTest{
Expand All @@ -3396,7 +3482,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Type: PrimitiveStaticTypeBool,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagVariableSizedStaticType,
// tag
Expand All @@ -3417,7 +3504,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Size: 42,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagConstantSizedStaticType,
// map, 2 pairs of items follow
Expand Down Expand Up @@ -3446,7 +3534,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Type: PrimitiveStaticTypeBool,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagReferenceStaticType,
// map, 2 pairs of items follow
Expand Down Expand Up @@ -3475,7 +3564,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
Type: PrimitiveStaticTypeBool,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagReferenceStaticType,
// map, 2 pairs of items follow
Expand Down Expand Up @@ -3504,7 +3594,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
ValueType: PrimitiveStaticTypeString,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagDictionaryStaticType,
// map, 2 pairs of items follow
Expand Down Expand Up @@ -3546,7 +3637,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
},
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagRestrictedStaticType,
// map, 2 pairs of items follow
Expand Down Expand Up @@ -3623,7 +3715,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
TargetPath: publicPathValue,
Type: CapabilityStaticType{},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagCapabilityStaticType,
// null
Expand All @@ -3642,7 +3735,8 @@ func TestEncodeDecodeLinkValue(t *testing.T) {
BorrowType: PrimitiveStaticTypeBool,
},
},
encoded: append(expectedLinkEncodingPrefix[:],
encoded: append(
expectedLinkEncodingPrefix[:],
// tag
0xd8, cborTagCapabilityStaticType,
// tag
Expand Down

0 comments on commit 973aeaa

Please sign in to comment.