From 6dc9459359f50c7e86405c32acd595c7fb50de65 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 21 Sep 2022 10:06:56 -0700 Subject: [PATCH 001/246] Allow conformances for interface delcarations in parser --- runtime/ast/interface.go | 5 ++++- runtime/parser/declaration.go | 7 +------ runtime/tests/checker/interface_test.go | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/runtime/ast/interface.go b/runtime/ast/interface.go index 0ba113f60e..7a73cc321a 100644 --- a/runtime/ast/interface.go +++ b/runtime/ast/interface.go @@ -32,6 +32,7 @@ type InterfaceDeclaration struct { Access Access CompositeKind common.CompositeKind Identifier Identifier + Conformances []*NominalType Members *Members DocString string Range @@ -46,6 +47,7 @@ func NewInterfaceDeclaration( access Access, compositeKind common.CompositeKind, identifier Identifier, + conformances []*NominalType, members *Members, docString string, declRange Range, @@ -56,6 +58,7 @@ func NewInterfaceDeclaration( Access: access, CompositeKind: compositeKind, Identifier: identifier, + Conformances: conformances, Members: members, DocString: docString, Range: declRange, @@ -114,7 +117,7 @@ func (d *InterfaceDeclaration) Doc() prettier.Doc { d.CompositeKind, true, d.Identifier.Identifier, - nil, + d.Conformances, d.Members, ) } diff --git a/runtime/parser/declaration.go b/runtime/parser/declaration.go index a91a35a1a2..1d9156c762 100644 --- a/runtime/parser/declaration.go +++ b/runtime/parser/declaration.go @@ -957,17 +957,12 @@ func parseCompositeOrInterfaceDeclaration( ) if isInterface { - // TODO: remove once interface conformances are supported - if len(conformances) > 0 { - // TODO: improve - return nil, p.syntaxError("unexpected conformances") - } - return ast.NewInterfaceDeclaration( p.memoryGauge, access, compositeKind, identifier, + conformances, members, docString, declarationRange, diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index b28b6d53a7..b82335257e 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2691,3 +2691,24 @@ func TestCheckBadStructInterface(t *testing.T) { assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[10]) assert.IsType(t, &sema.RedeclarationError{}, errs[11]) } + +func TestCheckInterfaceImplementationRequirement(t *testing.T) { + + t.Parallel() + + t.Run("struct interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + let x: Int + + fun test(): Int + } + + struct interface Bar: Foo {} + `) + require.NoError(t, err) + }) +} From f6aa36a574a75efd57982c52366576f7493beb7b Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 22 Sep 2022 08:06:30 -0700 Subject: [PATCH 002/246] check conformances of interface declarations --- runtime/ast/composite.go | 5 + runtime/ast/declaration.go | 5 + runtime/ast/interface.go | 5 + runtime/ast/interface_test.go | 111 ++++++++++++---- runtime/common/orderedmap/orderedmap.go | 12 ++ runtime/interpreter/interpreter.go | 6 +- runtime/sema/check_composite_declaration.go | 132 ++++++++++++++------ runtime/sema/check_interface_declaration.go | 29 +++++ runtime/sema/errors.go | 10 +- runtime/sema/interfaceset.go | 7 ++ runtime/sema/type.go | 60 ++++++++- runtime/tests/checker/interface_test.go | 62 ++++++++- runtime/tests/checker/resources_test.go | 20 ++- 13 files changed, 381 insertions(+), 83 deletions(-) diff --git a/runtime/ast/composite.go b/runtime/ast/composite.go index b7469739d4..857230d63f 100644 --- a/runtime/ast/composite.go +++ b/runtime/ast/composite.go @@ -44,6 +44,7 @@ type CompositeDeclaration struct { var _ Element = &CompositeDeclaration{} var _ Declaration = &CompositeDeclaration{} var _ Statement = &CompositeDeclaration{} +var _ HasConformance = &CompositeDeclaration{} func NewCompositeDeclaration( memoryGauge common.MemoryGauge, @@ -163,6 +164,10 @@ func (d *CompositeDeclaration) String() string { return Prettier(d) } +func (d *CompositeDeclaration) InterfaceConformances() []*NominalType { + return d.Conformances +} + var interfaceKeywordSpaceDoc = prettier.Text("interface ") var compositeConformancesSeparatorDoc = prettier.Text(":") var compositeConformanceSeparatorDoc prettier.Doc = prettier.Concat{ diff --git a/runtime/ast/declaration.go b/runtime/ast/declaration.go index 53a0b81282..510bcd35ae 100644 --- a/runtime/ast/declaration.go +++ b/runtime/ast/declaration.go @@ -37,3 +37,8 @@ type Declaration interface { DeclarationDocString() string Doc() prettier.Doc } + +type HasConformance interface { + Declaration + InterfaceConformances() []*NominalType +} diff --git a/runtime/ast/interface.go b/runtime/ast/interface.go index 7a73cc321a..3cfdd8ebc0 100644 --- a/runtime/ast/interface.go +++ b/runtime/ast/interface.go @@ -41,6 +41,7 @@ type InterfaceDeclaration struct { var _ Element = &InterfaceDeclaration{} var _ Declaration = &InterfaceDeclaration{} var _ Statement = &InterfaceDeclaration{} +var _ HasConformance = &CompositeDeclaration{} func NewInterfaceDeclaration( gauge common.MemoryGauge, @@ -125,3 +126,7 @@ func (d *InterfaceDeclaration) Doc() prettier.Doc { func (d *InterfaceDeclaration) String() string { return Prettier(d) } + +func (d *InterfaceDeclaration) InterfaceConformances() []*NominalType { + return d.Conformances +} diff --git a/runtime/ast/interface_test.go b/runtime/ast/interface_test.go index a0f04c9465..17fe281582 100644 --- a/runtime/ast/interface_test.go +++ b/runtime/ast/interface_test.go @@ -33,26 +33,80 @@ func TestInterfaceDeclaration_MarshalJSON(t *testing.T) { t.Parallel() - decl := &InterfaceDeclaration{ - Access: AccessPublic, - CompositeKind: common.CompositeKindResource, - Identifier: Identifier{ - Identifier: "AB", - Pos: Position{Offset: 1, Line: 2, Column: 3}, - }, - Members: NewUnmeteredMembers([]Declaration{}), - DocString: "test", - Range: Range{ - StartPos: Position{Offset: 7, Line: 8, Column: 9}, - EndPos: Position{Offset: 10, Line: 11, Column: 12}, - }, - } - - actual, err := json.Marshal(decl) - require.NoError(t, err) - - assert.JSONEq(t, - ` + t.Run("no conformances", func(t *testing.T) { + + decl := &InterfaceDeclaration{ + Access: AccessPublic, + CompositeKind: common.CompositeKindResource, + Identifier: Identifier{ + Identifier: "AB", + Pos: Position{Offset: 1, Line: 2, Column: 3}, + }, + Members: NewUnmeteredMembers([]Declaration{}), + DocString: "test", + Range: Range{ + StartPos: Position{Offset: 7, Line: 8, Column: 9}, + EndPos: Position{Offset: 10, Line: 11, Column: 12}, + }, + } + + actual, err := json.Marshal(decl) + require.NoError(t, err) + + assert.JSONEq(t, + ` + { + "Type": "InterfaceDeclaration", + "Access": "AccessPublic", + "CompositeKind": "CompositeKindResource", + "Identifier": { + "Identifier": "AB", + "StartPos": {"Offset": 1, "Line": 2, "Column": 3}, + "EndPos": {"Offset": 2, "Line": 2, "Column": 4} + }, + "Conformances": null, + "Members": { + "Declarations": [] + }, + "DocString": "test", + "StartPos": {"Offset": 7, "Line": 8, "Column": 9}, + "EndPos": {"Offset": 10, "Line": 11, "Column": 12} + } + `, + string(actual), + ) + }) + + t.Run("with conformances", func(t *testing.T) { + + decl := &InterfaceDeclaration{ + Access: AccessPublic, + CompositeKind: common.CompositeKindResource, + Identifier: Identifier{ + Identifier: "AB", + Pos: Position{Offset: 1, Line: 2, Column: 3}, + }, + Conformances: []*NominalType{ + { + Identifier: Identifier{ + Identifier: "CD", + Pos: Position{Offset: 4, Line: 5, Column: 6}, + }, + }, + }, + Members: NewUnmeteredMembers([]Declaration{}), + DocString: "test", + Range: Range{ + StartPos: Position{Offset: 7, Line: 8, Column: 9}, + EndPos: Position{Offset: 10, Line: 11, Column: 12}, + }, + } + + actual, err := json.Marshal(decl) + require.NoError(t, err) + + assert.JSONEq(t, + ` { "Type": "InterfaceDeclaration", "Access": "AccessPublic", @@ -62,6 +116,18 @@ func TestInterfaceDeclaration_MarshalJSON(t *testing.T) { "StartPos": {"Offset": 1, "Line": 2, "Column": 3}, "EndPos": {"Offset": 2, "Line": 2, "Column": 4} }, + "Conformances": [ + { + "Type": "NominalType", + "Identifier": { + "Identifier": "CD", + "StartPos": {"Offset": 4, "Line": 5, "Column": 6}, + "EndPos": {"Offset": 5, "Line": 5, "Column": 7} + }, + "StartPos": {"Offset": 4, "Line": 5, "Column": 6}, + "EndPos": {"Offset": 5, "Line": 5, "Column": 7} + } + ], "Members": { "Declarations": [] }, @@ -70,8 +136,9 @@ func TestInterfaceDeclaration_MarshalJSON(t *testing.T) { "EndPos": {"Offset": 10, "Line": 11, "Column": 12} } `, - string(actual), - ) + string(actual), + ) + }) } func TestInterfaceDeclaration_Doc(t *testing.T) { diff --git a/runtime/common/orderedmap/orderedmap.go b/runtime/common/orderedmap/orderedmap.go index 4499d3f3b2..813cea6b76 100644 --- a/runtime/common/orderedmap/orderedmap.go +++ b/runtime/common/orderedmap/orderedmap.go @@ -180,6 +180,18 @@ func (om OrderedMap[K, V]) ForeachWithError(f func(key K, value V) error) error return nil } +// ForeachReverse iterates over the entries of the map in the reverse insertion order (LIFO), and invokes +// the provided function for each key-value pair. +func (om OrderedMap[K, V]) ForeachReverse(f func(key K, value V)) { + if om.pairs == nil { + return + } + + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + f(pair.Key, pair.Value) + } +} + // Pair is an entry in an OrderedMap // type Pair[K any, V any] struct { diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 9351c3a1ce..67c1e7f765 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1122,11 +1122,9 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( // in reverse order: first the conformances, then the type requirements; // each conformances and type requirements in reverse order as well. - for i := len(compositeType.ExplicitInterfaceConformances) - 1; i >= 0; i-- { - conformance := compositeType.ExplicitInterfaceConformances[i] - + compositeType.ExplicitInterfaceConformanceSet().ForEachReverse(func(conformance *sema.InterfaceType) { wrapFunctions(interpreter.sharedState.typeCodes.InterfaceCodes[conformance.ID()]) - } + }) typeRequirements := compositeType.TypeRequirements() diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 8e4b75893b..fb94a09910 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -164,14 +164,24 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl inheritedMembers := map[string]struct{}{} typeRequirementsInheritedMembers := map[string]map[string]struct{}{} - for i, interfaceType := range compositeType.ExplicitInterfaceConformances { - interfaceNominalType := declaration.Conformances[i] - + // It is required to visit the duplicate conformances (i.e: that are in different interface chains). + // Because the validity of the root conformance depends on the validity of a nested conformance. + // e.g: One common invalid interface in the chain may cause two conformances to be invalid. + // B, conforms to + // / \ + // A, conforms to D + // \ / + // C, conforms to + // + // If A doesn't conform to D, then both B and C also doesn't conform to D, + // and hence need to report errors for B and C as well. + // + compositeType.ExplicitInterfaceConformances.Foreach(func(conformanceChainRoot, conformance *InterfaceType) bool { checker.checkCompositeConformance( declaration, compositeType, - interfaceType, - interfaceNominalType.Identifier, + conformance, + conformanceChainRoot, compositeConformanceCheckOptions{ checkMissingMembers: checkMissingMembers, interfaceTypeIsTypeRequirement: false, @@ -179,7 +189,9 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl inheritedMembers, typeRequirementsInheritedMembers, ) - } + + return true + }) // NOTE: check destructors after initializer and functions @@ -594,17 +606,17 @@ func (checker *Checker) declareCompositeMembersAndValue( var inheritedMembers StringMemberOrderedMap - for _, compositeTypeConformance := range compositeType.ExplicitInterfaceConformances { + compositeType.ExplicitInterfaceConformances.Foreach(func(_, compositeTypeConformance *InterfaceType) bool { conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) if !ok { - continue + return true } typeRequirement, ok := nestedType.(*CompositeType) if !ok { - continue + return true } nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) @@ -648,7 +660,9 @@ func (checker *Checker) declareCompositeMembersAndValue( inheritedMembers.Set(memberName, member) } }) - } + + return true + }) inheritedMembers.Foreach(func(memberName string, member *Member) { inheritedMember := *member @@ -932,14 +946,14 @@ func (checker *Checker) initializerParameters(initializers []*ast.SpecialFunctio } func (checker *Checker) explicitInterfaceConformances( - declaration *ast.CompositeDeclaration, - compositeType *CompositeType, + declaration ast.HasConformance, + compositeType CompositeKindedType, ) []*InterfaceType { var interfaceTypes []*InterfaceType seenConformances := map[*InterfaceType]bool{} - for _, conformance := range declaration.Conformances { + for _, conformance := range declaration.InterfaceConformances() { convertedType := checker.ConvertType(conformance) if interfaceType, ok := convertedType.(*InterfaceType); ok { @@ -1046,8 +1060,8 @@ type compositeConformanceCheckOptions struct { func (checker *Checker) checkCompositeConformance( compositeDeclaration *ast.CompositeDeclaration, compositeType *CompositeType, - interfaceType *InterfaceType, - compositeKindMismatchIdentifier ast.Identifier, + conformance *InterfaceType, + conformanceChainRoot *InterfaceType, options compositeConformanceCheckOptions, inheritedMembers map[string]struct{}, // type requirement name -> inherited members @@ -1061,29 +1075,20 @@ func (checker *Checker) checkCompositeConformance( // Ensure the composite kinds match, e.g. a structure shouldn't be able // to conform to a resource interface - - if interfaceType.CompositeKind != compositeType.Kind { - checker.report( - &CompositeKindMismatchError{ - ExpectedKind: compositeType.Kind, - ActualKind: interfaceType.CompositeKind, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeKindMismatchIdentifier), - }, - ) - } + checker.checkConformanceKindMatch(compositeDeclaration, compositeType, conformance) // Check initializer requirement // TODO: add support for overloaded initializers - if interfaceType.InitializerParameters != nil { + if conformance.InitializerParameters != nil { initializerType := &FunctionType{ Parameters: compositeType.ConstructorParameters, ReturnTypeAnnotation: NewTypeAnnotation(VoidType), } interfaceInitializerType := &FunctionType{ - Parameters: interfaceType.InitializerParameters, + Parameters: conformance.InitializerParameters, ReturnTypeAnnotation: NewTypeAnnotation(VoidType), } @@ -1091,14 +1096,14 @@ func (checker *Checker) checkCompositeConformance( if !initializerType.Equal(interfaceInitializerType) { initializerMismatch = &InitializerMismatch{ CompositeParameters: compositeType.ConstructorParameters, - InterfaceParameters: interfaceType.InitializerParameters, + InterfaceParameters: conformance.InitializerParameters, } } } // Determine missing members and member conformance - interfaceType.Members.Foreach(func(name string, interfaceMember *Member) { + conformance.Members.Foreach(func(name string, interfaceMember *Member) { // Conforming types do not provide a concrete member // for the member in the interface if it is predeclared @@ -1162,7 +1167,7 @@ func (checker *Checker) checkCompositeConformance( // Determine missing nested composite type definitions - interfaceType.NestedTypes.Foreach(func(name string, typeRequirement Type) { + conformance.NestedTypes.Foreach(func(name string, typeRequirement Type) { // Only nested composite declarations are type requirements of the interface @@ -1196,19 +1201,65 @@ func (checker *Checker) checkCompositeConformance( &ConformanceError{ CompositeDeclaration: compositeDeclaration, CompositeType: compositeType, - InterfaceType: interfaceType, + InterfaceType: conformanceChainRoot, Pos: compositeDeclaration.Identifier.Pos, InitializerMismatch: initializerMismatch, MissingMembers: missingMembers, MemberMismatches: memberMismatches, MissingNestedCompositeTypes: missingNestedCompositeTypes, InterfaceTypeIsTypeRequirement: options.interfaceTypeIsTypeRequirement, + NestedInterfaceType: conformance, }, ) } } +func (checker *Checker) checkConformanceKindMatch( + compositeDeclaration ast.HasConformance, + compositeType CompositeKindedType, + interfaceConformance *InterfaceType, +) { + + if interfaceConformance.CompositeKind == compositeType.GetCompositeKind() { + return + } + + var compositeKindMismatchIdentifier ast.Identifier + + reportError := false + + conformances := compositeDeclaration.InterfaceConformances() + + if len(conformances) == 0 { + // This is for type requirements + compositeKindMismatchIdentifier = *compositeDeclaration.DeclarationIdentifier() + reportError = true + } else { + for _, conformance := range conformances { + if conformance.Identifier.Identifier == interfaceConformance.Identifier { + compositeKindMismatchIdentifier = conformance.Identifier + reportError = true + break + } + } + + // If not found, then that means, the mismatching interface is a nested conformance. + // Then it should have already been reported when checking that interface. + // Hence, no need to report an error here again. + } + + if reportError { + checker.report( + &CompositeKindMismatchError{ + ExpectedKind: compositeType.GetCompositeKind(), + ActualKind: interfaceConformance.CompositeKind, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeKindMismatchIdentifier), + }, + ) + } +} + // TODO: return proper error func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member) bool { @@ -1394,14 +1445,19 @@ func (checker *Checker) checkTypeRequirement( // Check that the composite declaration declares at least the conformances // that the type requirement stated - for _, requiredConformance := range requiredCompositeType.ExplicitInterfaceConformances { + requiredCompositeType.ExplicitInterfaceConformances.Foreach(func(_, requiredConformance *InterfaceType) bool { found := false - for _, conformance := range declaredCompositeType.ExplicitInterfaceConformances { + + declaredCompositeType.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { if conformance == requiredConformance { found = true - break + // stop further checking + return false } - } + + return true + }) + if !found { checker.report( &MissingConformanceError{ @@ -1411,7 +1467,9 @@ func (checker *Checker) checkTypeRequirement( }, ) } - } + + return true + }) // Check the conformance of the composite to the type requirement // like a top-level composite declaration to an interface type @@ -1422,7 +1480,7 @@ func (checker *Checker) checkTypeRequirement( compositeDeclaration, declaredCompositeType, requiredInterfaceType, - compositeDeclaration.Identifier, + requiredInterfaceType, compositeConformanceCheckOptions{ checkMissingMembers: true, interfaceTypeIsTypeRequirement: true, diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index d852573473..e524d93c41 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -52,6 +52,17 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl true, ) + interfaceType.ExplicitInterfaceConformances.Foreach(func(conformanceChainRoot, conformance *InterfaceType) bool { + checker.checkInterfaceConformance( + declaration, + interfaceType, + conformance, + conformanceChainRoot, + ) + + return true + }) + // NOTE: functions are checked separately checker.checkFieldsAccessModifier(declaration.Members.Fields()) @@ -251,6 +262,10 @@ func (checker *Checker) declareInterfaceType(declaration *ast.InterfaceDeclarati ) } + // Resolve conformances + interfaceType.ExplicitInterfaceConformances = + checker.explicitInterfaceConformances(declaration, interfaceType) + checker.Elaboration.InterfaceDeclarationTypes[declaration] = interfaceType checker.Elaboration.InterfaceTypeDeclarations[interfaceType] = declaration @@ -357,3 +372,17 @@ func (checker *Checker) declareInterfaceMembers(declaration *ast.InterfaceDeclar checker.declareCompositeMembersAndValue(nestedCompositeDeclaration, ContainerKindInterface) } } + +func (checker *Checker) checkInterfaceConformance( + interfaceDeclaration *ast.InterfaceDeclaration, + interfaceType *InterfaceType, + conformance *InterfaceType, + conformanceChainRoot *InterfaceType, +) { + + // Ensure the composite kinds match, e.g. a structure shouldn't be able + // to conform to a resource interface + checker.checkConformanceKindMatch(interfaceDeclaration, interfaceType, conformance) + + // TODO: check conflicting names, default implementations. +} diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 187e514ab7..907054a06e 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1146,6 +1146,7 @@ type ConformanceError struct { CompositeDeclaration *ast.CompositeDeclaration CompositeType *CompositeType InterfaceType *InterfaceType + NestedInterfaceType *InterfaceType InitializerMismatch *InitializerMismatch MissingMembers []*Member MemberMismatches []MemberMismatch @@ -1156,6 +1157,7 @@ type ConformanceError struct { var _ SemanticError = &ConformanceError{} var _ errors.UserError = &ConformanceError{} +var _ errors.SecondaryError = &ConformanceError{} func (*ConformanceError) isSemanticError() {} @@ -1201,6 +1203,10 @@ func (e *ConformanceError) ErrorNotes() (notes []errors.ErrorNote) { return } +func (e *ConformanceError) SecondaryError() string { + return fmt.Sprintf("does not confirm to nested interface requirement `%s`", e.NestedInterfaceType) +} + // MemberMismatchNote type MemberMismatchNote struct { @@ -1216,7 +1222,7 @@ func (n MemberMismatchNote) Message() string { // TODO: just make this a warning? // type DuplicateConformanceError struct { - CompositeType *CompositeType + CompositeType CompositeKindedType InterfaceType *InterfaceType ast.Range } @@ -1231,7 +1237,7 @@ func (*DuplicateConformanceError) IsUserError() {} func (e *DuplicateConformanceError) Error() string { return fmt.Sprintf( "%s `%s` repeats conformance to %s `%s`", - e.CompositeType.Kind.Name(), + e.CompositeType.GetCompositeKind().Name(), e.CompositeType.QualifiedString(), e.InterfaceType.CompositeKind.DeclarationKind(true).Name(), e.InterfaceType.QualifiedString(), diff --git a/runtime/sema/interfaceset.go b/runtime/sema/interfaceset.go index 1e72aab8cb..5d8a252136 100644 --- a/runtime/sema/interfaceset.go +++ b/runtime/sema/interfaceset.go @@ -61,6 +61,13 @@ func (s InterfaceSet) ForEach(f func(*InterfaceType)) { }) } +func (s InterfaceSet) ForEachReverse(f func(*InterfaceType)) { + orderedMap := (orderedmap.OrderedMap[*InterfaceType, struct{}])(s) + orderedMap.ForeachReverse(func(interfaceType *InterfaceType, _ struct{}) { + f(interfaceType) + }) +} + func (s InterfaceSet) Len() int { orderedMap := (orderedmap.OrderedMap[*InterfaceType, struct{}])(s) return orderedMap.Len() diff --git a/runtime/sema/type.go b/runtime/sema/type.go index d6a2629e63..1bdcd77c2e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3473,7 +3473,7 @@ type CompositeType struct { // an internal set of field `ExplicitInterfaceConformances` explicitInterfaceConformanceSet *InterfaceSet explicitInterfaceConformanceSetOnce sync.Once - ExplicitInterfaceConformances []*InterfaceType + ExplicitInterfaceConformances InterfaceConformances ImplicitTypeRequirementConformances []*CompositeType Members *StringMemberOrderedMap memberResolvers map[string]MemberResolver @@ -3507,13 +3507,14 @@ func (t *CompositeType) ExplicitInterfaceConformanceSet() *InterfaceSet { func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { t.explicitInterfaceConformanceSetOnce.Do(func() { - // TODO: also include conformances' conformances recursively - // once interface can have conformances - t.explicitInterfaceConformanceSet = NewInterfaceSet() - for _, conformance := range t.ExplicitInterfaceConformances { + + // Interfaces can also have conformances. + // So add conformances' conformance recursively. + t.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { t.explicitInterfaceConformanceSet.Add(conformance) - } + return true + }) }) } @@ -3746,6 +3747,7 @@ func (t *CompositeType) TypeRequirements() []*CompositeType { var typeRequirements []*CompositeType if containerComposite, ok := t.containerType.(*CompositeType); ok { + // TODO: get nested conformances. i.e: use 'explicitInterfaceConformanceSet' method for _, conformance := range containerComposite.ExplicitInterfaceConformances { ty, ok := conformance.NestedTypes.Get(t.Identifier) if !ok { @@ -3829,6 +3831,30 @@ func (t *CompositeType) FieldPosition(name string, declaration *ast.CompositeDec return pos } +type InterfaceConformances []*InterfaceType + +// Foreach iterates over the conformances and its nested conformances in a breadth-first manner. +// `conformance` refers to the currently visiting conformance. +// `origin` refers to root of the current conformance chain. +// +func (c InterfaceConformances) Foreach(f func(origin, conformance *InterfaceType) bool) { + for _, conformance := range c { + if !f(conformance, conformance) { + break + } + + cont := true + conformance.ExplicitInterfaceConformances.Foreach(func(_, nestedConformance *InterfaceType) bool { + cont = f(conformance, nestedConformance) + return cont + }) + + if cont { + continue + } + } +} + // Member type Member struct { @@ -4016,6 +4042,10 @@ type InterfaceType struct { QualifiedIdentifier string } cachedIdentifiersLock sync.RWMutex + + explicitInterfaceConformanceSet *InterfaceSet + explicitInterfaceConformanceSetOnce sync.Once + ExplicitInterfaceConformances InterfaceConformances } func (*InterfaceType) IsType() {} @@ -4237,6 +4267,24 @@ func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDec return declaration.Members.FieldPosition(name, declaration.CompositeKind) } +func (t *InterfaceType) ExplicitInterfaceConformanceSet() *InterfaceSet { + t.initializeExplicitInterfaceConformanceSet() + return t.explicitInterfaceConformanceSet +} + +func (t *InterfaceType) initializeExplicitInterfaceConformanceSet() { + t.explicitInterfaceConformanceSetOnce.Do(func() { + t.explicitInterfaceConformanceSet = NewInterfaceSet() + + // Interfaces can also have conformances. + // So add conformances' conformance recursively. + t.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { + t.explicitInterfaceConformanceSet.Add(conformance) + return true + }) + }) +} + // DictionaryType consists of the key and value type // for all key-value pairs in the dictionary: // All keys have to be a subtype of the key type, diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index b82335257e..5a6df2bca6 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2708,7 +2708,67 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } struct interface Bar: Foo {} + + struct Baz: Bar {} `) - require.NoError(t, err) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + + assert.Equal(t, conformanceError.InterfaceType.Identifier, "Bar") + assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") + }) + + t.Run("resource interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface Foo { + let x: Int + + fun test(): Int + } + + resource interface Bar: Foo {} + + resource Baz: Bar {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + + assert.Equal(t, conformanceError.InterfaceType.Identifier, "Bar") + assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") + }) + + t.Run("mixed interfaces", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface Foo { + let x: Int + + fun test(): Int + } + + struct interface Bar: Foo {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + conformanceError := &sema.CompositeKindMismatchError{} + require.ErrorAs(t, errs[0], &conformanceError) + + assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) + assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) }) } diff --git a/runtime/tests/checker/resources_test.go b/runtime/tests/checker/resources_test.go index c55ef944f6..26663fe2c6 100644 --- a/runtime/tests/checker/resources_test.go +++ b/runtime/tests/checker/resources_test.go @@ -8638,7 +8638,7 @@ func TestCheckBadResourceInterface(t *testing.T) { _, err := ParseAndCheck(t, "resource interface struct{struct d:struct{ contract d:struct{ contract x:struct{ struct d{} contract d:struct{ contract d:struct {}}}}}}") - errs := ExpectCheckerErrors(t, err, 24) + errs := ExpectCheckerErrors(t, err, 22) assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[1]) @@ -8652,17 +8652,15 @@ func TestCheckBadResourceInterface(t *testing.T) { assert.IsType(t, &sema.RedeclarationError{}, errs[9]) assert.IsType(t, &sema.RedeclarationError{}, errs[10]) assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[11]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[12]) - assert.IsType(t, &sema.ConformanceError{}, errs[13]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[14]) - assert.IsType(t, &sema.ConformanceError{}, errs[15]) - assert.IsType(t, &sema.RedeclarationError{}, errs[16]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[17]) - assert.IsType(t, &sema.RedeclarationError{}, errs[18]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[19]) + assert.IsType(t, &sema.ConformanceError{}, errs[12]) + assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[13]) + assert.IsType(t, &sema.ConformanceError{}, errs[14]) + assert.IsType(t, &sema.RedeclarationError{}, errs[15]) + assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[16]) + assert.IsType(t, &sema.RedeclarationError{}, errs[17]) + assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[18]) + assert.IsType(t, &sema.ConformanceError{}, errs[19]) assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[20]) assert.IsType(t, &sema.ConformanceError{}, errs[21]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[22]) - assert.IsType(t, &sema.ConformanceError{}, errs[23]) }) } From c3fdd3ea38535d99a45c2b1da44479421693e1c6 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 22 Sep 2022 14:39:40 -0700 Subject: [PATCH 003/246] Add validation for conflicting members --- runtime/sema/check_composite_declaration.go | 50 ++- runtime/sema/check_interface_declaration.go | 100 ++++- runtime/sema/errors.go | 54 ++- runtime/sema/type.go | 16 + runtime/tests/checker/interface_test.go | 387 +++++++++++++++++++- 5 files changed, 557 insertions(+), 50 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index fb94a09910..ffd13684c8 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -164,34 +164,24 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl inheritedMembers := map[string]struct{}{} typeRequirementsInheritedMembers := map[string]map[string]struct{}{} - // It is required to visit the duplicate conformances (i.e: that are in different interface chains). - // Because the validity of the root conformance depends on the validity of a nested conformance. - // e.g: One common invalid interface in the chain may cause two conformances to be invalid. - // B, conforms to - // / \ - // A, conforms to D - // \ / - // C, conforms to - // - // If A doesn't conform to D, then both B and C also doesn't conform to D, - // and hence need to report errors for B and C as well. - // - compositeType.ExplicitInterfaceConformances.Foreach(func(conformanceChainRoot, conformance *InterfaceType) bool { - checker.checkCompositeConformance( - declaration, - compositeType, - conformance, - conformanceChainRoot, - compositeConformanceCheckOptions{ - checkMissingMembers: checkMissingMembers, - interfaceTypeIsTypeRequirement: false, - }, - inheritedMembers, - typeRequirementsInheritedMembers, - ) + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(conformanceChainRoot, conformance *InterfaceType) bool { + checker.checkCompositeConformance( + declaration, + compositeType, + conformance, + conformanceChainRoot, + compositeConformanceCheckOptions{ + checkMissingMembers: checkMissingMembers, + interfaceTypeIsTypeRequirement: false, + }, + inheritedMembers, + typeRequirementsInheritedMembers, + ) - return true - }) + return true + }, + ) // NOTE: check destructors after initializer and functions @@ -1689,7 +1679,10 @@ func (checker *Checker) defaultMembersAndOrigins( ) } - hasImplementation := function.FunctionBlock.HasStatements() + functionBlock := function.FunctionBlock + hasImplementation := functionBlock.HasStatements() + hasConditions := functionBlock != nil && + (functionBlock.PreConditions.IsEmpty() || functionBlock.PostConditions.IsEmpty()) members.Set( identifier, @@ -1703,6 +1696,7 @@ func (checker *Checker) defaultMembersAndOrigins( ArgumentLabels: argumentLabels, DocString: function.DocString, HasImplementation: hasImplementation, + HasConditions: hasConditions, }) if checker.PositionInfo != nil && origins != nil { diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index e524d93c41..231ef289ae 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -52,16 +52,20 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl true, ) - interfaceType.ExplicitInterfaceConformances.Foreach(func(conformanceChainRoot, conformance *InterfaceType) bool { - checker.checkInterfaceConformance( - declaration, - interfaceType, - conformance, - conformanceChainRoot, - ) + inheritedMembers := map[string]*Member{} + + interfaceType.ExplicitInterfaceConformances.ForeachDistinct( + func(_, conformance *InterfaceType) bool { + checker.checkInterfaceConformance( + declaration, + interfaceType, + conformance, + inheritedMembers, + ) - return true - }) + return true + }, + ) // NOTE: functions are checked separately checker.checkFieldsAccessModifier(declaration.Members.Fields()) @@ -377,12 +381,86 @@ func (checker *Checker) checkInterfaceConformance( interfaceDeclaration *ast.InterfaceDeclaration, interfaceType *InterfaceType, conformance *InterfaceType, - conformanceChainRoot *InterfaceType, + inheritedMembers map[string]*Member, ) { // Ensure the composite kinds match, e.g. a structure shouldn't be able // to conform to a resource interface checker.checkConformanceKindMatch(interfaceDeclaration, interfaceType, conformance) - // TODO: check conflicting names, default implementations. + conformance.Members.Foreach(func(name string, conformanceMember *Member) { + + // Check if the members coming from other conformances have conflicts. + if conflictingMember, ok := inheritedMembers[name]; ok { + conflictingInterface := conflictingMember.ContainerType.(*InterfaceType) + checker.checkDuplicateInterfaceMembers( + conformance, + conformanceMember, + conflictingInterface, + conflictingMember, + func() ast.Range { + return ast.NewRangeFromPositioned(checker.memoryGauge, interfaceDeclaration.Identifier) + }, + ) + } + + inheritedMembers[name] = conformanceMember + + // Check if the members coming from other current declaration have conflicts. + declarationMember, ok := interfaceType.Members.Get(name) + if !ok { + return + } + + checker.checkDuplicateInterfaceMembers( + interfaceType, + declarationMember, + conformance, + conformanceMember, + func() ast.Range { + return ast.NewRangeFromPositioned(checker.memoryGauge, declarationMember.Identifier) + }, + ) + }) +} + +func (checker *Checker) checkDuplicateInterfaceMembers( + interfaceType *InterfaceType, + interfaceMember *Member, + conflictingInterfaceType *InterfaceType, + conflictingMember *Member, + getRange func() ast.Range, +) { + + // Check if the two members have identical signatures. + // If yes, they are allowed, but subject to the conditions below. + // If not, report an error. + if !checker.memberSatisfied(interfaceMember, conflictingMember) { + checker.report(NewInterfaceMemberConflictError( + interfaceType, + interfaceMember, + conflictingInterfaceType, + conflictingMember, + getRange, + )) + return + } + + // If they are functions with same name, check whether any of them have + // default implementations or conditions. i.e: Anything more than just the signature. + // It is invalid to have default impl / conditions, because it creates ambiguity. + + if interfaceMember.HasConditions || + interfaceMember.HasImplementation || + conflictingMember.HasConditions || + conflictingMember.HasImplementation { + + checker.report(NewInterfaceMemberConflictError( + interfaceType, + interfaceMember, + conflictingInterfaceType, + conflictingMember, + getRange, + )) + } } diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 907054a06e..05dab29db4 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1247,7 +1247,7 @@ func (e *DuplicateConformanceError) Error() string { // MultipleInterfaceDefaultImplementationsError // type MultipleInterfaceDefaultImplementationsError struct { - CompositeType *CompositeType + CompositeType CompositeKindedType Member *Member } @@ -1261,7 +1261,7 @@ func (*MultipleInterfaceDefaultImplementationsError) IsUserError() {} func (e *MultipleInterfaceDefaultImplementationsError) Error() string { return fmt.Sprintf( "%s `%s` has multiple interface default implementations for function `%s`", - e.CompositeType.Kind.Name(), + e.CompositeType.GetCompositeKind().Name(), e.CompositeType.QualifiedString(), e.Member.Identifier.Identifier, ) @@ -1310,7 +1310,7 @@ func (e *SpecialFunctionDefaultImplementationError) EndPosition(memoryGauge comm // DefaultFunctionConflictError // type DefaultFunctionConflictError struct { - CompositeType *CompositeType + CompositeType CompositeKindedType Member *Member } @@ -1324,7 +1324,7 @@ func (*DefaultFunctionConflictError) IsUserError() {} func (e *DefaultFunctionConflictError) Error() string { return fmt.Sprintf( "%s `%s` has conflicting requirements for function `%s`", - e.CompositeType.Kind.Name(), + e.CompositeType.GetCompositeKind().Name(), e.CompositeType.QualifiedString(), e.Member.Identifier.Identifier, ) @@ -1338,6 +1338,52 @@ func (e *DefaultFunctionConflictError) EndPosition(memoryGauge common.MemoryGaug return e.Member.Identifier.EndPosition(memoryGauge) } +// InterfaceMemberConflictError +// +type InterfaceMemberConflictError struct { + InterfaceType *InterfaceType + ConflictingInterfaceType *InterfaceType + MemberName string + MemberKind common.DeclarationKind + ConflictingMemberKind common.DeclarationKind + ast.Range +} + +func NewInterfaceMemberConflictError( + interfaceType *InterfaceType, + interfaceMember *Member, + conflictingInterfaceType *InterfaceType, + conflictingMember *Member, + getRange func() ast.Range, +) *InterfaceMemberConflictError { + return &InterfaceMemberConflictError{ + InterfaceType: interfaceType, + ConflictingInterfaceType: conflictingInterfaceType, + MemberName: interfaceMember.Identifier.Identifier, + MemberKind: interfaceMember.DeclarationKind, + ConflictingMemberKind: conflictingMember.DeclarationKind, + Range: getRange(), + } +} + +var _ SemanticError = &InterfaceMemberConflictError{} +var _ errors.UserError = &InterfaceMemberConflictError{} + +func (*InterfaceMemberConflictError) isSemanticError() {} + +func (*InterfaceMemberConflictError) IsUserError() {} + +func (e *InterfaceMemberConflictError) Error() string { + return fmt.Sprintf( + "`%s` %s of `%s` conflicts with a %s with the same name in `%s`", + e.MemberName, + e.MemberKind.Name(), + e.InterfaceType.Identifier, + e.ConflictingMemberKind.Name(), + e.ConflictingInterfaceType.Identifier, + ) +} + // MissingConformanceError // type MissingConformanceError struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 1bdcd77c2e..d486207d9a 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3855,6 +3855,21 @@ func (c InterfaceConformances) Foreach(f func(origin, conformance *InterfaceType } } +func (c InterfaceConformances) ForeachDistinct(f func(origin, conformance *InterfaceType) bool) { + seenConformances := map[*InterfaceType]struct{}{} + + c.Foreach(func(origin, conformance *InterfaceType) bool { + if _, ok := seenConformances[conformance]; ok { + return true + } + + seenConformances[conformance] = struct{}{} + + return f(origin, conformance) + }) + +} + // Member type Member struct { @@ -3869,6 +3884,7 @@ type Member struct { // Predeclared fields can be considered initialized Predeclared bool HasImplementation bool + HasConditions bool // IgnoreInSerialization fields are ignored in serialization IgnoreInSerialization bool DocString string diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 5a6df2bca6..2d744590e3 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2696,7 +2696,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { t.Parallel() - t.Run("struct interface", func(t *testing.T) { + t.Run("struct interface non-conforming", func(t *testing.T) { t.Parallel() @@ -2722,7 +2722,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") }) - t.Run("resource interface", func(t *testing.T) { + t.Run("resource interface non-conforming", func(t *testing.T) { t.Parallel() @@ -2748,16 +2748,32 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") }) - t.Run("mixed interfaces", func(t *testing.T) { + t.Run("mismatching conformance kind on composite", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - resource interface Foo { - let x: Int + resource interface Foo {} - fun test(): Int - } + struct Bar: Foo {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + conformanceError := &sema.CompositeKindMismatchError{} + require.ErrorAs(t, errs[0], &conformanceError) + + assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) + assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + }) + + t.Run("mismatching conformance kind on interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface Foo {} struct interface Bar: Foo {} `) @@ -2771,4 +2787,361 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) }) + + t.Run("mismatching inner conformance", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface Foo {} + + struct interface Bar: Foo {} + + struct Baz: Bar {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + conformanceError := &sema.CompositeKindMismatchError{} + require.ErrorAs(t, errs[0], &conformanceError) + + assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) + assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + }) + + t.Run("nested mismatching conformance", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo {} + + resource interface Bar: Foo {} + + struct Baz: Bar {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 2) + + conformanceError := &sema.CompositeKindMismatchError{} + require.ErrorAs(t, errs[0], &conformanceError) + assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindResource) + assert.Equal(t, conformanceError.ActualKind, common.CompositeKindStructure) + + require.ErrorAs(t, errs[1], &conformanceError) + assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) + assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + }) + + t.Run("duplicate methods matching", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() + } + + struct interface Bar: Foo { + pub fun hello() + } + `) + + // If none of them have default methods then that's ok + require.NoError(t, err) + }) + + t.Run("duplicate methods mismatching", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() + } + + struct interface Bar: Foo { + pub fun hello(): String + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate fields matching", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub var x: String + } + + struct interface Bar: Foo { + pub var x: String + } + `) + + // If none of them have default methods then that's ok + require.NoError(t, err) + }) + + t.Run("duplicate fields mismatching", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub var x: String + } + + struct interface Bar: Foo { + pub var x: Int + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "x") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate members mixed type", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() + } + + struct interface Bar: Foo { + pub var hello: Void + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate methods with conditions in super", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() { + pre { true } + } + } + + struct interface Bar: Foo { + pub fun hello() + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate methods with default impl in super", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() { + var a = 1 + } + } + + struct interface Bar: Foo { + pub fun hello() + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate methods with conditions in child", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() + } + + struct interface Bar: Foo { + pub fun hello() { + pre { true } + } + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate methods with default impl in child", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub fun hello() + } + + struct interface Bar: Foo { + pub fun hello() { + var a = 1 + } + } + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + }) + + t.Run("duplicate methods indirect", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello(): Int + } + + struct interface B: A {} + + struct interface P { + pub fun hello(): String + } + + struct interface Q: P {} + + struct interface X: B, Q {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.InterfaceType.QualifiedIdentifier(), "P") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + }) + + t.Run("duplicate methods indirect for struct", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello(): Int + } + + struct interface B: A {} + + struct interface P { + pub fun hello(): String + } + + struct interface Q: P {} + + struct X: B, Q {} + `) + + require.Error(t, err) + errs := ExpectCheckerErrors(t, err, 2) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + assert.Equal(t, conformanceError.InterfaceType.QualifiedIdentifier(), "B") + assert.Equal(t, conformanceError.NestedInterfaceType.QualifiedIdentifier(), "A") + + require.ErrorAs(t, errs[1], &conformanceError) + assert.Equal(t, conformanceError.InterfaceType.QualifiedIdentifier(), "Q") + assert.Equal(t, conformanceError.NestedInterfaceType.QualifiedIdentifier(), "P") + }) + + t.Run("same conformance via different paths", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface P: A {} + + struct interface Q: A {} + + struct interface X: P, Q {} + `) + + require.NoError(t, err) + }) + + t.Run("same conformance via different paths for struct", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface P: A {} + + struct interface Q: A {} + + struct X: P, Q {} + `) + + require.NoError(t, err) + }) } From f3a36bdca8c471fe9109cc6fa044b27ff332dae5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 11 Oct 2022 14:59:08 -0700 Subject: [PATCH 004/246] Fix tests and refactor code --- runtime/sema/check_composite_declaration.go | 164 ++++++++++---------- runtime/sema/errors.go | 8 +- runtime/sema/type.go | 5 +- runtime/tests/checker/interface_test.go | 30 ++-- 4 files changed, 107 insertions(+), 100 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index a43740a7a5..90e7e8684b 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -592,63 +592,65 @@ func (checker *Checker) declareCompositeMembersAndValue( var inheritedMembers StringMemberOrderedMap - compositeType.ExplicitInterfaceConformances.Foreach(func(_, compositeTypeConformance *InterfaceType) bool { - conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_, compositeTypeConformance *InterfaceType) bool { + conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() - nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) - if !ok { - return true - } - - typeRequirement, ok := nestedType.(*CompositeType) - if !ok { - return true - } + nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) + if !ok { + return true + } - nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) + typeRequirement, ok := nestedType.(*CompositeType) + if !ok { + return true + } - // Add default functions + nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) - typeRequirement.Members.Foreach(func(memberName string, member *Member) { + // Add default functions - if member.Predeclared || - member.DeclarationKind != common.DeclarationKindFunction { + typeRequirement.Members.Foreach(func(memberName string, member *Member) { - return - } + if member.Predeclared || + member.DeclarationKind != common.DeclarationKindFunction { - _, existing := nestedCompositeType.Members.Get(memberName) - if existing { - return - } + return + } - if _, ok := inheritedMembers.Get(memberName); ok { - if member.HasImplementation { - checker.report( - &MultipleInterfaceDefaultImplementationsError{ - CompositeType: nestedCompositeType, - Member: member, - }, - ) - } else { - checker.report( - &DefaultFunctionConflictError{ - CompositeType: nestedCompositeType, - Member: member, - }, - ) + _, existing := nestedCompositeType.Members.Get(memberName) + if existing { + return } - return - } + if _, ok := inheritedMembers.Get(memberName); ok { + if member.HasImplementation { + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) + } else { + checker.report( + &DefaultFunctionConflictError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) + } + + return + } - if member.HasImplementation { - inheritedMembers.Set(memberName, member) - } - }) + if member.HasImplementation { + inheritedMembers.Set(memberName, member) + } + }) - return true - }) + return true + }, + ) inheritedMembers.Foreach(func(memberName string, member *Member) { inheritedMember := *member @@ -1209,21 +1211,17 @@ func (checker *Checker) checkConformanceKindMatch( return } - var compositeKindMismatchIdentifier ast.Identifier - - reportError := false + var compositeKindMismatchIdentifier *ast.Identifier conformances := compositeDeclaration.InterfaceConformances() if len(conformances) == 0 { // This is for type requirements - compositeKindMismatchIdentifier = *compositeDeclaration.DeclarationIdentifier() - reportError = true + compositeKindMismatchIdentifier = compositeDeclaration.DeclarationIdentifier() } else { for _, conformance := range conformances { if conformance.Identifier.Identifier == interfaceConformance.Identifier { - compositeKindMismatchIdentifier = conformance.Identifier - reportError = true + compositeKindMismatchIdentifier = &conformance.Identifier break } } @@ -1233,15 +1231,17 @@ func (checker *Checker) checkConformanceKindMatch( // Hence, no need to report an error here again. } - if reportError { - checker.report( - &CompositeKindMismatchError{ - ExpectedKind: compositeType.GetCompositeKind(), - ActualKind: interfaceConformance.CompositeKind, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeKindMismatchIdentifier), - }, - ) + if compositeKindMismatchIdentifier == nil { + return } + + checker.report( + &CompositeKindMismatchError{ + ExpectedKind: compositeType.GetCompositeKind(), + ActualKind: interfaceConformance.CompositeKind, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeKindMismatchIdentifier), + }, + ) } // TODO: return proper error @@ -1428,31 +1428,35 @@ func (checker *Checker) checkTypeRequirement( // Check that the composite declaration declares at least the conformances // that the type requirement stated - requiredCompositeType.ExplicitInterfaceConformances.Foreach(func(_, requiredConformance *InterfaceType) bool { - found := false + requiredCompositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_, requiredConformance *InterfaceType) bool { + found := false - declaredCompositeType.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { - if conformance == requiredConformance { - found = true - // stop further checking - return false - } - - return true - }) + declaredCompositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_, conformance *InterfaceType) bool { + if conformance == requiredConformance { + found = true + // stop further checking + return false + } - if !found { - checker.report( - &MissingConformanceError{ - CompositeType: declaredCompositeType, - InterfaceType: requiredConformance, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.Identifier), + return true }, ) - } - return true - }) + if !found { + checker.report( + &MissingConformanceError{ + CompositeType: declaredCompositeType, + InterfaceType: requiredConformance, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.Identifier), + }, + ) + } + + return true + }, + ) // Check the conformance of the composite to the type requirement // like a top-level composite declaration to an interface type diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 70607e892d..5ce3d2e71e 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1202,7 +1202,12 @@ func (e *ConformanceError) ErrorNotes() (notes []errors.ErrorNote) { } func (e *ConformanceError) SecondaryError() string { - return fmt.Sprintf("does not confirm to nested interface requirement `%s`", e.NestedInterfaceType) + // If the conformance mismatch is at the first level, then no secondary error. + if e.NestedInterfaceType == e.InterfaceType { + return "" + } + + return fmt.Sprintf("does not conform to nested interface requirement `%s`", e.NestedInterfaceType) } // MemberMismatchNote @@ -1333,7 +1338,6 @@ func (e *DefaultFunctionConflictError) EndPosition(memoryGauge common.MemoryGaug } // InterfaceMemberConflictError -// type InterfaceMemberConflictError struct { InterfaceType *InterfaceType ConflictingInterfaceType *InterfaceType diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 94a5f4e440..7d72ac790f 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3546,7 +3546,7 @@ func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { // Interfaces can also have conformances. // So add conformances' conformance recursively. - t.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { + t.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { t.explicitInterfaceConformanceSet.Add(conformance) return true }) @@ -3871,7 +3871,6 @@ type InterfaceConformances []*InterfaceType // Foreach iterates over the conformances and its nested conformances in a breadth-first manner. // `conformance` refers to the currently visiting conformance. // `origin` refers to root of the current conformance chain. -// func (c InterfaceConformances) Foreach(f func(origin, conformance *InterfaceType) bool) { for _, conformance := range c { if !f(conformance, conformance) { @@ -4330,7 +4329,7 @@ func (t *InterfaceType) initializeExplicitInterfaceConformanceSet() { // Interfaces can also have conformances. // So add conformances' conformance recursively. - t.ExplicitInterfaceConformances.Foreach(func(_, conformance *InterfaceType) bool { + t.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { t.explicitInterfaceConformanceSet.Add(conformance) return true }) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 8a82c584c0..9a7c3eb396 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2713,7 +2713,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2739,7 +2739,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2759,7 +2759,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2779,7 +2779,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2801,7 +2801,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2823,7 +2823,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 2) conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) @@ -2868,7 +2868,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -2909,7 +2909,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -2932,7 +2932,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -2957,7 +2957,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -2982,7 +2982,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -3007,7 +3007,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -3032,7 +3032,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -3061,7 +3061,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 1) + errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) @@ -3091,7 +3091,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { `) require.Error(t, err) - errs := ExpectCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 2) conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) From e68af876a9ed3a49a100f1e181088f86c4a5aeb5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 12 Oct 2022 14:12:38 -0700 Subject: [PATCH 005/246] Add interpreter tests --- runtime/tests/checker/interface_test.go | 73 ++++++-- runtime/tests/interpreter/interface_test.go | 183 ++++++++++++++++++++ 2 files changed, 241 insertions(+), 15 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 9a7c3eb396..1c41373bec 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2696,6 +2696,64 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { t.Parallel() + t.Run("struct interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + let x: Int + + fun test(): Int + } + + struct interface Bar: Foo {} + + struct Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + `) + + require.NoError(t, err) + }) + + t.Run("resource interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface Foo { + let x: Int + + fun test(): Int + } + + resource interface Bar: Foo {} + + resource Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + `) + + require.NoError(t, err) + }) + t.Run("struct interface non-conforming", func(t *testing.T) { t.Parallel() @@ -2712,7 +2770,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct Baz: Bar {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.ConformanceError{} @@ -2738,7 +2795,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { resource Baz: Bar {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.ConformanceError{} @@ -2758,7 +2814,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct Bar: Foo {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} @@ -2778,7 +2833,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct interface Bar: Foo {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} @@ -2800,7 +2854,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct Baz: Bar {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) conformanceError := &sema.CompositeKindMismatchError{} @@ -2822,7 +2875,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct Baz: Bar {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 2) conformanceError := &sema.CompositeKindMismatchError{} @@ -2867,7 +2919,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -2908,7 +2959,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -2931,7 +2981,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -2956,7 +3005,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -2981,7 +3029,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -3006,7 +3053,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -3031,7 +3077,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -3060,7 +3105,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct interface X: B, Q {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 1) memberConflictError := &sema.InterfaceMemberConflictError{} @@ -3090,7 +3134,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { struct X: B, Q {} `) - require.Error(t, err) errs := RequireCheckerErrors(t, err, 2) conformanceError := &sema.ConformanceError{} diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 1f64c65b61..0381cfd9ab 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -241,3 +241,186 @@ func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { }) } + +func TestInterpretInterfaceImplementationRequirement(t *testing.T) { + + t.Parallel() + + t.Run("struct interface", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface Foo { + let x: Int + + fun test(): Int + } + + struct interface Bar: Foo {} + + struct Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + + pub fun main(): Int { + let baz = Baz() + return baz.test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) + + t.Run("resource interface", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + resource interface Foo { + let x: Int + + fun test(): Int + } + + resource interface Bar: Foo {} + + resource Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + + pub fun main(): Int { + let baz <- create Baz() + let x = baz.test() + destroy baz + return x + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) + + t.Run("duplicate default methods", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface Foo { + pub fun test(): Int + } + + struct interface Bar: Foo { + pub fun test(): Int + } + + struct Baz: Bar { + fun test(): Int { + return 3 + } + } + + pub fun main(): Int { + let baz = Baz() + return baz.test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) + + t.Run("indirect default method", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface Foo { + pub fun test(): Int { + return 3 + } + } + + struct interface Bar: Foo {} + + struct Baz: Bar {} + + pub fun main(): Int { + let baz = Baz() + return baz.test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) + + t.Run("default method via different paths", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface A { + pub fun test(): Int { + return 3 + } + } + + struct interface P: A {} + + struct interface Q: A {} + + struct Foo: P, Q {} + + pub fun main(): Int { + let foo = Foo() + return foo.test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) +} From 77d36eae33c7ffc7dc09bec7fb4d91bf31370f72 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Oct 2022 08:30:27 -0700 Subject: [PATCH 006/246] Handle type requirements --- runtime/sema/check_composite_declaration.go | 15 +- runtime/sema/check_interface_declaration.go | 54 +++- runtime/tests/checker/interface_test.go | 286 ++++++++++++++++++++ 3 files changed, 351 insertions(+), 4 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 90e7e8684b..0ee4ba84c4 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1103,7 +1103,7 @@ func (checker *Checker) checkCompositeConformance( // If the composite member exists, check if it satisfies the mem - if !checker.memberSatisfied(compositeMember, interfaceMember) { + if !checker.memberSatisfied(compositeType, compositeMember, interfaceMember) { memberMismatches = append( memberMismatches, MemberMismatch{ @@ -1245,7 +1245,10 @@ func (checker *Checker) checkConformanceKindMatch( } // TODO: return proper error -func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member) bool { +func (checker *Checker) memberSatisfied( + compositeType CompositeKindedType, + compositeMember, interfaceMember *Member, +) bool { // Check declaration kind if compositeMember.DeclarationKind != interfaceMember.DeclarationKind { @@ -1321,6 +1324,14 @@ func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member return false } + case common.DeclarationKindStructure, + common.DeclarationKindResource, + common.DeclarationKindEnum: + // Interfaces and their conformances cannot have nested composite declarations + // with conflicting names (i.e: no type requirements for interfaces). + if _, isInterface := compositeType.(*InterfaceType); isInterface { + return false + } } } diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 930d840e34..c468dbf3bd 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -52,6 +52,7 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl ) inheritedMembers := map[string]*Member{} + inheritedTypes := map[string]Type{} interfaceType.ExplicitInterfaceConformances.ForeachDistinct( func(_, conformance *InterfaceType) bool { @@ -60,6 +61,7 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl interfaceType, conformance, inheritedMembers, + inheritedTypes, ) return true @@ -378,6 +380,7 @@ func (checker *Checker) checkInterfaceConformance( interfaceType *InterfaceType, conformance *InterfaceType, inheritedMembers map[string]*Member, + inheritedNestedTypes map[string]Type, ) { // Ensure the composite kinds match, e.g. a structure shouldn't be able @@ -402,7 +405,7 @@ func (checker *Checker) checkInterfaceConformance( inheritedMembers[name] = conformanceMember - // Check if the members coming from other current declaration have conflicts. + // Check if the members coming from the current declaration have conflicts. declarationMember, ok := interfaceType.Members.Get(name) if !ok { return @@ -418,6 +421,53 @@ func (checker *Checker) checkInterfaceConformance( }, ) }) + + reportError := func(typeName string, typ CompositeKindedType, otherType Type) { + nestedCompositeType, ok := otherType.(CompositeKindedType) + if !ok { + return + } + + _, isInterface := typ.(*InterfaceType) + _, isNestedInterface := nestedCompositeType.(*InterfaceType) + + checker.report(&InterfaceMemberConflictError{ + InterfaceType: interfaceType, + ConflictingInterfaceType: conformance, + MemberName: typeName, + MemberKind: typ.GetCompositeKind().DeclarationKind(isInterface), + ConflictingMemberKind: nestedCompositeType.GetCompositeKind().DeclarationKind(isNestedInterface), + Range: ast.NewRangeFromPositioned( + checker.memoryGauge, + interfaceDeclaration.Identifier, + ), + }) + } + + conformance.NestedTypes.Foreach(func(name string, typeRequirement Type) { + compositeType, ok := typeRequirement.(CompositeKindedType) + if !ok { + return + } + + // Check if the type definitions coming from other conformances have conflicts. + if inheritedType, ok := inheritedNestedTypes[name]; ok { + inheritedCompositeType, ok := inheritedType.(CompositeKindedType) + if !ok { + return + } + + reportError(name, compositeType, inheritedCompositeType) + } + + inheritedNestedTypes[name] = typeRequirement + + // Check if the type definitions coming from the current declaration have conflicts. + nestedType, ok := interfaceType.NestedTypes.Get(name) + if ok { + reportError(name, compositeType, nestedType) + } + }) } func (checker *Checker) checkDuplicateInterfaceMembers( @@ -431,7 +481,7 @@ func (checker *Checker) checkDuplicateInterfaceMembers( // Check if the two members have identical signatures. // If yes, they are allowed, but subject to the conditions below. // If not, report an error. - if !checker.memberSatisfied(interfaceMember, conflictingMember) { + if !checker.memberSatisfied(interfaceType, interfaceMember, conflictingMember) { checker.report(NewInterfaceMemberConflictError( interfaceType, interfaceMember, diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 1c41373bec..41d9abea8d 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2754,6 +2754,35 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { require.NoError(t, err) }) + t.Run("contract interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface Foo { + let x: Int + + fun test(): Int + } + + contract interface Bar: Foo {} + + contract Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + `) + + require.NoError(t, err) + }) + t.Run("struct interface non-conforming", func(t *testing.T) { t.Parallel() @@ -3187,4 +3216,261 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { require.NoError(t, err) }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B: A {} + + contract interface C: B {} + + contract X: C { + struct Nested { + pub fun test(): Int { + return 3 + } + } + } + `) + + require.NoError(t, err) + }) + + t.Run("type requirement negative", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B: A {} + + contract interface C: B {} + + contract X: C {} + `) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.ConformanceError{}, errs[0]) + }) + + t.Run("type requirement multiple", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct ANested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B { + struct BNested { + pub fun test(): Int { + return 4 + } + } + } + + contract interface C: A, B {} + + contract X: C { + struct ANested { + pub fun test(): Int { + return 3 + } + } + + struct BNested { + pub fun test(): Int { + return 3 + } + } + } + `) + + require.NoError(t, err) + }) + + t.Run("type requirement multiple not conforming", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct ANested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B { + struct BNested { + pub fun test(): Int { + return 4 + } + } + } + + contract interface C: A, B {} + + contract X: C { + struct ANested { + pub fun test(): Int { + return 3 + } + } + } + + contract Y: C { + struct BNested { + pub fun test(): Int { + return 3 + } + } + } + `) + + errs := RequireCheckerErrors(t, err, 2) + assert.IsType(t, &sema.ConformanceError{}, errs[0]) + assert.IsType(t, &sema.ConformanceError{}, errs[1]) + }) + + t.Run("nested struct conflicting", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B: A { + struct Nested { + pub fun test(): String { + return "three" + } + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) + assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) + }) + + t.Run("nested resource interface conflicting", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + resource interface Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B: A { + resource interface Nested { + pub fun test(): String { + return "three" + } + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, common.DeclarationKindResourceInterface, memberConflictError.MemberKind) + assert.Equal(t, common.DeclarationKindResourceInterface, memberConflictError.ConflictingMemberKind) + }) + + t.Run("nested mixed types conflicting", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct interface Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B: A { + resource Nested { + pub fun test(): String { + return "three" + } + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, common.DeclarationKindStructureInterface, memberConflictError.MemberKind) + assert.Equal(t, common.DeclarationKindResource, memberConflictError.ConflictingMemberKind) + }) + + t.Run("nested struct conflicting indirect", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct Nested { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B { + struct Nested { + pub fun test(): String { + return "three" + } + } + } + + contract interface C: A, B {} + `) + + errs := RequireCheckerErrors(t, err, 1) + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) + assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) + }) } From 9240ca26cfb87ecc9815fe280333b673073db8ef Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Oct 2022 15:11:06 -0700 Subject: [PATCH 007/246] Refactor error construction --- runtime/sema/check_interface_declaration.go | 43 +++++++++++---------- runtime/sema/errors.go | 17 -------- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index c468dbf3bd..5bb062b96a 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -387,6 +387,8 @@ func (checker *Checker) checkInterfaceConformance( // to conform to a resource interface checker.checkConformanceKindMatch(interfaceDeclaration, interfaceType, conformance) + // Check for member (functions and fields) conflicts + conformance.Members.Foreach(func(name string, conformanceMember *Member) { // Check if the members coming from other conformances have conflicts. @@ -422,21 +424,23 @@ func (checker *Checker) checkInterfaceConformance( ) }) - reportError := func(typeName string, typ CompositeKindedType, otherType Type) { - nestedCompositeType, ok := otherType.(CompositeKindedType) + // Check for nested type conflicts + + reportTypeConflictError := func(typeName string, typ CompositeKindedType, otherType Type) { + otherCompositeType, ok := otherType.(CompositeKindedType) if !ok { return } _, isInterface := typ.(*InterfaceType) - _, isNestedInterface := nestedCompositeType.(*InterfaceType) + _, isOtherTypeInterface := otherCompositeType.(*InterfaceType) checker.report(&InterfaceMemberConflictError{ InterfaceType: interfaceType, ConflictingInterfaceType: conformance, MemberName: typeName, MemberKind: typ.GetCompositeKind().DeclarationKind(isInterface), - ConflictingMemberKind: nestedCompositeType.GetCompositeKind().DeclarationKind(isNestedInterface), + ConflictingMemberKind: otherCompositeType.GetCompositeKind().DeclarationKind(isOtherTypeInterface), Range: ast.NewRangeFromPositioned( checker.memoryGauge, interfaceDeclaration.Identifier, @@ -457,7 +461,7 @@ func (checker *Checker) checkInterfaceConformance( return } - reportError(name, compositeType, inheritedCompositeType) + reportTypeConflictError(name, compositeType, inheritedCompositeType) } inheritedNestedTypes[name] = typeRequirement @@ -465,7 +469,7 @@ func (checker *Checker) checkInterfaceConformance( // Check if the type definitions coming from the current declaration have conflicts. nestedType, ok := interfaceType.NestedTypes.Get(name) if ok { - reportError(name, compositeType, nestedType) + reportTypeConflictError(name, compositeType, nestedType) } }) } @@ -478,17 +482,22 @@ func (checker *Checker) checkDuplicateInterfaceMembers( getRange func() ast.Range, ) { + reportMemberConflictError := func() { + checker.report(&InterfaceMemberConflictError{ + InterfaceType: interfaceType, + ConflictingInterfaceType: conflictingInterfaceType, + MemberName: interfaceMember.Identifier.Identifier, + MemberKind: interfaceMember.DeclarationKind, + ConflictingMemberKind: conflictingMember.DeclarationKind, + Range: getRange(), + }) + } + // Check if the two members have identical signatures. // If yes, they are allowed, but subject to the conditions below. // If not, report an error. if !checker.memberSatisfied(interfaceType, interfaceMember, conflictingMember) { - checker.report(NewInterfaceMemberConflictError( - interfaceType, - interfaceMember, - conflictingInterfaceType, - conflictingMember, - getRange, - )) + reportMemberConflictError() return } @@ -501,12 +510,6 @@ func (checker *Checker) checkDuplicateInterfaceMembers( conflictingMember.HasConditions || conflictingMember.HasImplementation { - checker.report(NewInterfaceMemberConflictError( - interfaceType, - interfaceMember, - conflictingInterfaceType, - conflictingMember, - getRange, - )) + reportMemberConflictError() } } diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 5ce3d2e71e..ff8a1561d2 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1347,23 +1347,6 @@ type InterfaceMemberConflictError struct { ast.Range } -func NewInterfaceMemberConflictError( - interfaceType *InterfaceType, - interfaceMember *Member, - conflictingInterfaceType *InterfaceType, - conflictingMember *Member, - getRange func() ast.Range, -) *InterfaceMemberConflictError { - return &InterfaceMemberConflictError{ - InterfaceType: interfaceType, - ConflictingInterfaceType: conflictingInterfaceType, - MemberName: interfaceMember.Identifier.Identifier, - MemberKind: interfaceMember.DeclarationKind, - ConflictingMemberKind: conflictingMember.DeclarationKind, - Range: getRange(), - } -} - var _ SemanticError = &InterfaceMemberConflictError{} var _ errors.UserError = &InterfaceMemberConflictError{} From d5a8b2601d14a91a9f3e3fdb20269ffad079ba34 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Oct 2022 15:27:02 -0700 Subject: [PATCH 008/246] Fix runtime type nested requirements --- runtime/sema/type.go | 16 +++--- runtime/tests/checker/interface_test.go | 32 ++++++++++++ runtime/tests/interpreter/interface_test.go | 58 +++++++++++++++++++++ 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7d72ac790f..79bdeea185 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3782,20 +3782,20 @@ func (t *CompositeType) TypeRequirements() []*CompositeType { var typeRequirements []*CompositeType if containerComposite, ok := t.containerType.(*CompositeType); ok { - // TODO: get nested conformances. i.e: use 'explicitInterfaceConformanceSet' method - for _, conformance := range containerComposite.ExplicitInterfaceConformances { + containerComposite.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { ty, ok := conformance.NestedTypes.Get(t.Identifier) if !ok { - continue + return true } typeRequirement, ok := ty.(*CompositeType) if !ok { - continue + return true } typeRequirements = append(typeRequirements, typeRequirement) - } + return true + }) } return typeRequirements @@ -3871,7 +3871,7 @@ type InterfaceConformances []*InterfaceType // Foreach iterates over the conformances and its nested conformances in a breadth-first manner. // `conformance` refers to the currently visiting conformance. // `origin` refers to root of the current conformance chain. -func (c InterfaceConformances) Foreach(f func(origin, conformance *InterfaceType) bool) { +func (c InterfaceConformances) Foreach(f func(*InterfaceType, *InterfaceType) bool) { for _, conformance := range c { if !f(conformance, conformance) { break @@ -3889,19 +3889,17 @@ func (c InterfaceConformances) Foreach(f func(origin, conformance *InterfaceType } } -func (c InterfaceConformances) ForeachDistinct(f func(origin, conformance *InterfaceType) bool) { +func (c InterfaceConformances) ForeachDistinct(f func(*InterfaceType, *InterfaceType) bool) { seenConformances := map[*InterfaceType]struct{}{} c.Foreach(func(origin, conformance *InterfaceType) bool { if _, ok := seenConformances[conformance]; ok { return true } - seenConformances[conformance] = struct{}{} return f(origin, conformance) }) - } // Member diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 41d9abea8d..c5962d3f05 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3473,4 +3473,36 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) }) + + t.Run("nested type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + struct NestedA { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B { + struct NestedB { + pub fun test(): String { + return "three" + } + } + } + + contract interface C: A, B {} + + contract D: C {} + `) + + errs := RequireCheckerErrors(t, err, 2) + conformance := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformance) + require.ErrorAs(t, errs[1], &conformance) + }) } diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 0381cfd9ab..7abacc86d7 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -423,4 +423,62 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { value, ) }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + inter, err := parseCheckAndInterpretWithOptions(t, ` + contract interface A { + struct NestedA { + pub fun test(): Int { + return 3 + } + } + } + + contract interface B { + struct NestedB { + pub fun test(): String { + return "three" + } + } + } + + contract interface C: A, B {} + + contract D: C { + struct NestedA {} + + struct NestedB {} + + pub fun getNestedA(): NestedA { + return NestedA() + } + + pub fun getNestedB(): NestedB { + return NestedB() + } + } + + pub fun main(): Int { + return D.getNestedA().test() + }`, + + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + ContractValueHandler: makeContractValueHandler(nil, nil, nil), + }, + }, + ) + require.NoError(t, err) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) } From 3eed769dbfe5d41b18ea7e19e9d6be1aa032b190 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Oct 2022 15:29:49 -0700 Subject: [PATCH 009/246] Allow multiple conditions. Allow at most one default implementation --- runtime/sema/check_composite_declaration.go | 6 +- runtime/sema/check_interface_declaration.go | 12 +- runtime/sema/type.go | 1 - runtime/tests/checker/interface_test.go | 253 +++++++++++++++----- runtime/tests/interpreter/interface_test.go | 238 +++++++++++++++--- 5 files changed, 405 insertions(+), 105 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 0ee4ba84c4..649b4ab476 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1687,10 +1687,7 @@ func (checker *Checker) defaultMembersAndOrigins( ) } - functionBlock := function.FunctionBlock - hasImplementation := functionBlock.HasStatements() - hasConditions := functionBlock != nil && - (functionBlock.PreConditions.IsEmpty() || functionBlock.PostConditions.IsEmpty()) + hasImplementation := function.FunctionBlock.HasStatements() members.Set( identifier, @@ -1704,7 +1701,6 @@ func (checker *Checker) defaultMembersAndOrigins( ArgumentLabels: argumentLabels, DocString: function.DocString, HasImplementation: hasImplementation, - HasConditions: hasConditions, }) if checker.PositionInfo != nil && origins != nil { diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 5bb062b96a..6183c2691a 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -501,15 +501,9 @@ func (checker *Checker) checkDuplicateInterfaceMembers( return } - // If they are functions with same name, check whether any of them have - // default implementations or conditions. i.e: Anything more than just the signature. - // It is invalid to have default impl / conditions, because it creates ambiguity. - - if interfaceMember.HasConditions || - interfaceMember.HasImplementation || - conflictingMember.HasConditions || - conflictingMember.HasImplementation { - + // If there are functions with same name, check whether any of them have default implementations. + // It is invalid to have more than one default impl, because it creates ambiguity. + if interfaceMember.HasImplementation && conflictingMember.HasImplementation { reportMemberConflictError() } } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 50a98e43e4..e2a3d9d4fc 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3835,7 +3835,6 @@ type Member struct { // Predeclared fields can be considered initialized Predeclared bool HasImplementation bool - HasConditions bool // IgnoreInSerialization fields are ignored in serialization IgnoreInSerialization bool DocString string diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index c5962d3f05..d4f695a070 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2692,7 +2692,7 @@ func TestCheckBadStructInterface(t *testing.T) { assert.IsType(t, &sema.RedeclarationError{}, errs[11]) } -func TestCheckInterfaceImplementationRequirement(t *testing.T) { +func TestCheckInterfaceInheritance(t *testing.T) { t.Parallel() @@ -2970,7 +2970,6 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - // If none of them have default methods then that's ok require.NoError(t, err) }) @@ -3034,36 +3033,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - errs := RequireCheckerErrors(t, err, 1) - - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") - }) - - t.Run("duplicate methods with default impl in super", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - struct interface Foo { - pub fun hello() { - var a = 1 - } - } - - struct interface Bar: Foo { - pub fun hello() - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + require.NoError(t, err) }) t.Run("duplicate methods with conditions in child", func(t *testing.T) { @@ -3082,36 +3052,7 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { } `) - errs := RequireCheckerErrors(t, err, 1) - - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") - }) - - t.Run("duplicate methods with default impl in child", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - struct interface Foo { - pub fun hello() - } - - struct interface Bar: Foo { - pub fun hello() { - var a = 1 - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + require.NoError(t, err) }) t.Run("duplicate methods indirect", func(t *testing.T) { @@ -3216,6 +3157,194 @@ func TestCheckInterfaceImplementationRequirement(t *testing.T) { require.NoError(t, err) }) +} + +func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { + + t.Parallel() + + t.Run("default impl in super", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B: A { + pub fun hello() + } + `) + + require.NoError(t, err) + }) + + t.Run("default impl in child", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() + } + + struct interface B: A { + pub fun hello() { + var a = 1 + } + } + `) + + require.NoError(t, err) + }) + + t.Run("default impl in both", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B: A { + pub fun hello() { + var a = 2 + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + }) + + t.Run("default impl from two paths", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B { + pub fun hello() { + var a = 2 + } + } + + struct interface C: A, B {} + `) + + errs := RequireCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + }) + + t.Run("overridden default impl in one path", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B: A { + pub fun hello() { + var a = 2 + } + } + + struct interface C: A, B {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + + require.ErrorAs(t, errs[1], &memberConflictError) + assert.Equal(t, memberConflictError.MemberName, "hello") + assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + }) + + t.Run("default impl in one path and condition in another", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B { + pub fun hello() { + pre { true } + } + } + + struct interface C: A, B {} + `) + + require.NoError(t, err) + }) + + t.Run("default impl in one path and condition in another, in concrete type", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B { + pub fun hello() { + pre { true } + } + } + + struct interface C: A, B {} + + struct D: C {} + `) + + // The interface `C` allows to have a default implementation coming from one path, + // and a condition from another path, from inherited types. + // However, for the concrete type `D`, it is as if `B.hello` doesn't have an implementation. + // Hence, the concrete type is required to have an explicit implementation. + errs := RequireCheckerErrors(t, err, 1) + memberConflictError := &sema.DefaultFunctionConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + }) +} + +func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { + + t.Parallel() t.Run("type requirement", func(t *testing.T) { diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 7abacc86d7..348362582c 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/tests/utils" ) func TestInterpretInterfaceDefaultImplementation(t *testing.T) { @@ -242,7 +243,7 @@ func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { } -func TestInterpretInterfaceImplementationRequirement(t *testing.T) { +func TestInterpretInterfaceInheritance(t *testing.T) { t.Parallel() @@ -251,15 +252,15 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` - struct interface Foo { + struct interface A { let x: Int fun test(): Int } - struct interface Bar: Foo {} + struct interface B: A {} - struct Baz: Bar { + struct C: B { let x: Int init() { @@ -272,8 +273,8 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { } pub fun main(): Int { - let baz = Baz() - return baz.test() + let c = C() + return c.test() } `) @@ -291,15 +292,15 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` - resource interface Foo { + resource interface A { let x: Int fun test(): Int } - resource interface Bar: Foo {} + resource interface B: A {} - resource Baz: Bar { + resource C: B { let x: Int init() { @@ -312,9 +313,9 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { } pub fun main(): Int { - let baz <- create Baz() - let x = baz.test() - destroy baz + let c <- create C() + let x = c.test() + destroy c return x } `) @@ -328,28 +329,28 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { ) }) - t.Run("duplicate default methods", func(t *testing.T) { + t.Run("duplicate methods", func(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` - struct interface Foo { + struct interface A { pub fun test(): Int } - struct interface Bar: Foo { + struct interface B: A { pub fun test(): Int } - struct Baz: Bar { + struct C: B { fun test(): Int { return 3 } } pub fun main(): Int { - let baz = Baz() - return baz.test() + let c = C() + return c.test() } `) @@ -367,19 +368,19 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` - struct interface Foo { + struct interface A { pub fun test(): Int { return 3 } } - struct interface Bar: Foo {} + struct interface B: A {} - struct Baz: Bar {} + struct C: B {} pub fun main(): Int { - let baz = Baz() - return baz.test() + let c = C() + return c.test() } `) @@ -403,15 +404,15 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { } } - struct interface P: A {} + struct interface B: A {} - struct interface Q: A {} + struct interface C: A {} - struct Foo: P, Q {} + struct D: B, C {} pub fun main(): Int { - let foo = Foo() - return foo.test() + let d = D() + return d.test() } `) @@ -482,3 +483,184 @@ func TestInterpretInterfaceImplementationRequirement(t *testing.T) { ) }) } + +func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { + + t.Parallel() + + t.Run("condition in super", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface A { + pub fun test(_ a: Int): Int { + pre { a > 10 } + } + } + + struct interface B: A { + pub fun test(_ a: Int): Int + } + + struct C: B { + fun test(_ a: Int): Int { + return a + 3 + } + } + + pub fun main(_ a: Int): Int { + let c = C() + return c.test(a) + } + `) + + value, err := inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(15)) + require.NoError(t, err) + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(18), + value, + ) + + // Implementation should satisfy inherited conditions + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(5)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + }) + + t.Run("condition in child", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface A { + pub fun test(_ a: Int): Int + } + + struct interface B: A { + pub fun test(_ a: Int): Int { + pre { a > 10 } + } + } + + struct C: B { + fun test(_ a: Int): Int { + return a + 3 + } + } + + pub fun main(_ a: Int): Int { + let c = C() + return c.test(a) + } + `) + + value, err := inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(15)) + require.NoError(t, err) + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(18), + value, + ) + + // Implementation should satisfy inherited conditions + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(5)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + }) + + t.Run("conditions in both", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface A { + pub fun test(_ a: Int): Int { + pre { a < 20 } + } + } + + struct interface B: A { + pub fun test(_ a: Int): Int { + pre { a > 10 } + } + } + + struct C: B { + fun test(_ a: Int): Int { + return a + 3 + } + } + + pub fun main(_ a: Int): Int { + let c = C() + return c.test(a) + } + `) + + value, err := inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(15)) + require.NoError(t, err) + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(18), + value, + ) + + // Implementation should satisfy both inherited conditions + + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(5)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(25)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + }) + + t.Run("conditions from two paths", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface A { + pub fun test(_ a: Int): Int { + pre { a < 20 } + } + } + + struct interface B { + pub fun test(_ a: Int): Int { + pre { a > 10 } + } + } + + struct interface C: A, B {} + + struct D: C { + fun test(_ a: Int): Int { + return a + 3 + } + } + + pub fun main(_ a: Int): Int { + let d = D() + return d.test(a) + } + `) + + value, err := inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(15)) + require.NoError(t, err) + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(18), + value, + ) + + // Implementation should satisfy both inherited conditions + + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(5)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + + _, err = inter.Invoke("main", interpreter.NewUnmeteredIntValueFromInt64(25)) + utils.RequireError(t, err) + assert.ErrorAs(t, err, &interpreter.ConditionError{}) + }) +} From e44106d7f7408281d5299ec39d0229606747c6ba Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Oct 2022 16:31:03 -0700 Subject: [PATCH 010/246] Improve comments --- runtime/sema/check_composite_declaration.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 649b4ab476..42130c8e91 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1216,9 +1216,12 @@ func (checker *Checker) checkConformanceKindMatch( conformances := compositeDeclaration.InterfaceConformances() if len(conformances) == 0 { - // This is for type requirements + // For type requirements, there is no explicit conformance. + // Hence, log the error at the type requirement (i.e: declaration identifier) compositeKindMismatchIdentifier = compositeDeclaration.DeclarationIdentifier() } else { + // Otherwise, find the conformance which resulted in the mismatch, + // and log the error there. for _, conformance := range conformances { if conformance.Identifier.Identifier == interfaceConformance.Identifier { compositeKindMismatchIdentifier = &conformance.Identifier @@ -1226,8 +1229,8 @@ func (checker *Checker) checkConformanceKindMatch( } } - // If not found, then that means, the mismatching interface is a nested conformance. - // Then it should have already been reported when checking that interface. + // If not found, then that means, the mismatching interface is a grandparent. + // Then it should have already been reported when checking the parent. // Hence, no need to report an error here again. } From 52539f72c947281c5f88e9a5645761c2b1262552 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 31 Oct 2022 14:42:18 -0700 Subject: [PATCH 011/246] Update supertype inference --- runtime/sema/type_tags.go | 26 +++++++----- runtime/sema/type_test.go | 38 +++++++++++++++++ runtime/tests/checker/type_inference_test.go | 44 ++++++++++++++++++++ 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 90030e4c7e..4f69cce6b9 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -834,21 +834,27 @@ func commonSuperTypeOfComposites(types []Type) Type { // NOTE: index 0 may not always be the first type, since there can be 'Never' types. if firstType { - for _, interfaceType := range compositeType.ExplicitInterfaceConformances { - commonInterfaces[interfaceType.QualifiedIdentifier()] = true - commonInterfacesList = append(commonInterfacesList, interfaceType) - } + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_ *InterfaceType, interfaceType *InterfaceType) bool { + commonInterfaces[interfaceType.QualifiedIdentifier()] = true + commonInterfacesList = append(commonInterfacesList, interfaceType) + return true + }, + ) firstType = false } else { intersection := map[string]bool{} commonInterfacesList = make([]*InterfaceType, 0) - for _, interfaceType := range compositeType.ExplicitInterfaceConformances { - if _, ok := commonInterfaces[interfaceType.QualifiedIdentifier()]; ok { - intersection[interfaceType.QualifiedIdentifier()] = true - commonInterfacesList = append(commonInterfacesList, interfaceType) - } - } + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_ *InterfaceType, interfaceType *InterfaceType) bool { + if _, ok := commonInterfaces[interfaceType.QualifiedIdentifier()]; ok { + intersection[interfaceType.QualifiedIdentifier()] = true + commonInterfacesList = append(commonInterfacesList, interfaceType) + } + return true + }, + ) commonInterfaces = intersection } diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index e49ec4c6a0..8b19ed3bd7 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -938,6 +938,33 @@ func TestCommonSuperType(t *testing.T) { Members: &StringMemberOrderedMap{}, } + superInterfaceType := &InterfaceType{ + Location: testLocation, + Identifier: "SI", + CompositeKind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + } + + inheritedInterfaceType1 := &InterfaceType{ + Location: testLocation, + Identifier: "II1", + CompositeKind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{ + superInterfaceType, + }, + } + + inheritedInterfaceType2 := &InterfaceType{ + Location: testLocation, + Identifier: "II2", + CompositeKind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{ + superInterfaceType, + }, + } + newCompositeWithInterfaces := func(name string, interfaces ...*InterfaceType) *CompositeType { return &CompositeType{ Location: testLocation, @@ -1029,6 +1056,17 @@ func TestCommonSuperType(t *testing.T) { }, expectedSuperType: AnyStructType, }, + { + name: "inherited common interface", + types: []Type{ + newCompositeWithInterfaces("Foo", inheritedInterfaceType1), + newCompositeWithInterfaces("Bar", inheritedInterfaceType2), + }, + expectedSuperType: &RestrictedType{ + Type: AnyStructType, + Restrictions: []*InterfaceType{superInterfaceType}, + }, + }, { name: "structs with never", types: []Type{ diff --git a/runtime/tests/checker/type_inference_test.go b/runtime/tests/checker/type_inference_test.go index e2b55aafc5..45951f611f 100644 --- a/runtime/tests/checker/type_inference_test.go +++ b/runtime/tests/checker/type_inference_test.go @@ -1183,3 +1183,47 @@ func TestCheckDictionarySupertypeInference(t *testing.T) { require.IsType(t, &sema.TypeAnnotationRequiredError{}, errs[0]) }) } + +func TestCheckCompositeSupertypeInference(t *testing.T) { + + t.Parallel() + + t.Run("common inherited interface", func(t *testing.T) { + t.Parallel() + + code := ` + let x = true ? Foo() : Bar() + + pub struct interface I1 {} + + pub struct interface I2: I1 {} + + pub struct interface I3: I1 {} + + pub struct Foo: I2 {} + + pub struct Bar: I3 {} + ` + + expectedType := &sema.RestrictedType{ + Type: sema.AnyStructType, + Restrictions: []*sema.InterfaceType{ + { + Location: common.StringLocation("test"), + Identifier: "I1", + CompositeKind: common.CompositeKindStructure, + }, + }, + } + + checker, err := ParseAndCheck(t, code) + require.NoError(t, err) + + xType := RequireGlobalValue(t, checker.Elaboration, "x") + + require.IsType(t, &sema.RestrictedType{}, xType) + restrictedType := xType.(*sema.RestrictedType) + + assert.Equal(t, expectedType.ID(), restrictedType.ID()) + }) +} From 65cc960389a3d8bff4209c4846fef73e5dddd281 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 31 Oct 2022 15:54:37 -0700 Subject: [PATCH 012/246] Add tests for event inheritance --- runtime/tests/checker/interface_test.go | 67 +++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index d4f695a070..1f3537f675 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3630,8 +3630,69 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { `) errs := RequireCheckerErrors(t, err, 2) - conformance := &sema.ConformanceError{} - require.ErrorAs(t, errs[0], &conformance) - require.ErrorAs(t, errs[1], &conformance) + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + require.ErrorAs(t, errs[1], &conformanceError) + }) +} + +func TestCheckInterfaceEventsInheritance(t *testing.T) { + + t.Parallel() + + t.Run("non inherited interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + event FooEvent(_ x: String) + } + + contract X: A { + pub fun test() { + emit FooEvent("hello") + } + } + `) + + require.Error(t, err) + errs := RequireCheckerErrors(t, err, 2) + + notDeclaredError := &sema.NotDeclaredError{} + require.ErrorAs(t, errs[0], ¬DeclaredError) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[1], &conformanceError) + }) + + t.Run("inherited interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + event FooEvent(_ x: String) + } + + contract interface B: A {} + + contract interface C: B {} + + contract X: C { + pub fun test() { + emit FooEvent("hello") + } + } + `) + + require.Error(t, err) + errs := RequireCheckerErrors(t, err, 2) + + notDeclaredError := &sema.NotDeclaredError{} + require.ErrorAs(t, errs[0], ¬DeclaredError) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[1], &conformanceError) }) } From 039cec82eb42bc572fb6e7cd18f83dadfb50084b Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 31 Oct 2022 16:54:03 -0700 Subject: [PATCH 013/246] Refactor assertions --- runtime/sema/type.go | 17 ++++-- runtime/sema/type_tags.go | 51 ++++++++-------- runtime/sema/type_test.go | 14 +++-- runtime/tests/checker/interface_test.go | 78 +++++++++++++------------ 4 files changed, 89 insertions(+), 71 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 37e375e267..91ee378a25 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3788,10 +3788,11 @@ func (t *CompositeType) FieldPosition(name string, declaration *ast.CompositeDec type InterfaceConformances []*InterfaceType -// Foreach iterates over the conformances and its nested conformances in a breadth-first manner. -// `conformance` refers to the currently visiting conformance. -// `origin` refers to root of the current conformance chain. -func (c InterfaceConformances) Foreach(f func(*InterfaceType, *InterfaceType) bool) { +// Foreach iterates over the conformances and its nested conformances in a breadth-first manner, +// and invokes the given function. The function have two parameters: +// - `origin` refers to root of the current conformance chain. +// - `conformance` refers to the currently visiting conformance. +func (c InterfaceConformances) Foreach(f func(origin *InterfaceType, conformance *InterfaceType) bool) { for _, conformance := range c { if !f(conformance, conformance) { break @@ -3809,7 +3810,13 @@ func (c InterfaceConformances) Foreach(f func(*InterfaceType, *InterfaceType) bo } } -func (c InterfaceConformances) ForeachDistinct(f func(*InterfaceType, *InterfaceType) bool) { +// ForeachDistinct iterates over the conformances and its nested conformances in a breadth-first manner, +// and invokes the given function. Any duplicate conformance would be skipped. +// +// The function have two parameters: +// - `origin` refers to root of the current conformance chain. +// - `conformance` refers to the currently visiting conformance. +func (c InterfaceConformances) ForeachDistinct(f func(origin *InterfaceType, conformance *InterfaceType) bool) { seenConformances := map[*InterfaceType]struct{}{} c.Foreach(func(origin, conformance *InterfaceType) bool { diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 64450eb73b..32fdeaaa54 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -857,31 +857,34 @@ func commonSuperTypeOfComposites(types []Type) Type { panic(errors.NewUnreachableError()) } - // NOTE: index 0 may not always be the first type, since there can be 'Never' types. - if firstType { - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_ *InterfaceType, interfaceType *InterfaceType) bool { - commonInterfaces[interfaceType.QualifiedIdentifier()] = true - commonInterfacesList = append(commonInterfacesList, interfaceType) - return true - }, - ) - firstType = false - } else { - intersection := map[string]bool{} - commonInterfacesList = make([]*InterfaceType, 0) - - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_ *InterfaceType, interfaceType *InterfaceType) bool { - if _, ok := commonInterfaces[interfaceType.QualifiedIdentifier()]; ok { - intersection[interfaceType.QualifiedIdentifier()] = true - commonInterfacesList = append(commonInterfacesList, interfaceType) - } - return true - }, - ) + if len(compositeType.ExplicitInterfaceConformances) > 0 { - commonInterfaces = intersection + // NOTE: index 0 may not always be the first type, since there can be 'Never' types. + if firstType { + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_ *InterfaceType, interfaceType *InterfaceType) bool { + commonInterfaces[interfaceType.QualifiedIdentifier()] = true + commonInterfacesList = append(commonInterfacesList, interfaceType) + return true + }, + ) + firstType = false + } else { + intersection := map[string]bool{} + commonInterfacesList = make([]*InterfaceType, 0) + + compositeType.ExplicitInterfaceConformances.ForeachDistinct( + func(_ *InterfaceType, interfaceType *InterfaceType) bool { + if _, ok := commonInterfaces[interfaceType.QualifiedIdentifier()]; ok { + intersection[interfaceType.QualifiedIdentifier()] = true + commonInterfacesList = append(commonInterfacesList, interfaceType) + } + return true + }, + ) + + commonInterfaces = intersection + } } if len(commonInterfaces) == 0 { diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index caa32a5480..54385e56e5 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -1072,10 +1072,16 @@ func TestCommonSuperType(t *testing.T) { newCompositeWithInterfaces("Foo", inheritedInterfaceType1), newCompositeWithInterfaces("Bar", inheritedInterfaceType2), }, - expectedSuperType: &RestrictedType{ - Type: AnyStructType, - Restrictions: []*InterfaceType{superInterfaceType}, - }, + expectedSuperType: func() Type { + typ := &RestrictedType{ + Type: AnyStructType, + Restrictions: []*InterfaceType{superInterfaceType}, + } + + // just initialize for equality + typ.initializeRestrictionSet() + return typ + }(), }, { name: "structs with never", diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 1f3537f675..58591f849f 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2788,15 +2788,17 @@ func TestCheckInterfaceInheritance(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - struct interface Foo { + struct interface A { let x: Int fun test(): Int } - struct interface Bar: Foo {} + struct interface B: A {} - struct Baz: Bar {} + struct interface C: B {} + + struct Foo: C {} `) errs := RequireCheckerErrors(t, err, 1) @@ -2804,8 +2806,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.InterfaceType.Identifier, "Bar") - assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") + assert.Equal(t, "C", conformanceError.InterfaceType.Identifier) + assert.Equal(t, "A", conformanceError.NestedInterfaceType.Identifier) }) t.Run("resource interface non-conforming", func(t *testing.T) { @@ -2829,8 +2831,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.InterfaceType.Identifier, "Bar") - assert.Equal(t, conformanceError.NestedInterfaceType.Identifier, "Foo") + assert.Equal(t, "Bar", conformanceError.InterfaceType.Identifier) + assert.Equal(t, "Foo", conformanceError.NestedInterfaceType.Identifier) }) t.Run("mismatching conformance kind on composite", func(t *testing.T) { @@ -2848,8 +2850,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) - assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + assert.Equal(t, common.CompositeKindStructure, conformanceError.ExpectedKind) + assert.Equal(t, common.CompositeKindResource, conformanceError.ActualKind) }) t.Run("mismatching conformance kind on interface", func(t *testing.T) { @@ -2867,8 +2869,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) - assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + assert.Equal(t, common.CompositeKindStructure, conformanceError.ExpectedKind) + assert.Equal(t, common.CompositeKindResource, conformanceError.ActualKind) }) t.Run("mismatching inner conformance", func(t *testing.T) { @@ -2888,8 +2890,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) - assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + assert.Equal(t, common.CompositeKindStructure, conformanceError.ExpectedKind) + assert.Equal(t, common.CompositeKindResource, conformanceError.ActualKind) }) t.Run("nested mismatching conformance", func(t *testing.T) { @@ -2908,12 +2910,12 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.CompositeKindMismatchError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindResource) - assert.Equal(t, conformanceError.ActualKind, common.CompositeKindStructure) + assert.Equal(t, common.CompositeKindResource, conformanceError.ExpectedKind) + assert.Equal(t, common.CompositeKindStructure, conformanceError.ActualKind) require.ErrorAs(t, errs[1], &conformanceError) - assert.Equal(t, conformanceError.ExpectedKind, common.CompositeKindStructure) - assert.Equal(t, conformanceError.ActualKind, common.CompositeKindResource) + assert.Equal(t, common.CompositeKindStructure, conformanceError.ExpectedKind) + assert.Equal(t, common.CompositeKindResource, conformanceError.ActualKind) }) t.Run("duplicate methods matching", func(t *testing.T) { @@ -2952,8 +2954,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("duplicate fields matching", func(t *testing.T) { @@ -2991,8 +2993,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "x") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + assert.Equal(t, "x", memberConflictError.MemberName) + assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("duplicate members mixed type", func(t *testing.T) { @@ -3013,8 +3015,8 @@ func TestCheckInterfaceInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "Foo") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("duplicate methods with conditions in super", func(t *testing.T) { @@ -3079,9 +3081,9 @@ func TestCheckInterfaceInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.InterfaceType.QualifiedIdentifier(), "P") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "P", memberConflictError.InterfaceType.QualifiedIdentifier()) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("duplicate methods indirect for struct", func(t *testing.T) { @@ -3108,12 +3110,12 @@ func TestCheckInterfaceInheritance(t *testing.T) { conformanceError := &sema.ConformanceError{} require.ErrorAs(t, errs[0], &conformanceError) - assert.Equal(t, conformanceError.InterfaceType.QualifiedIdentifier(), "B") - assert.Equal(t, conformanceError.NestedInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "B", conformanceError.InterfaceType.QualifiedIdentifier()) + assert.Equal(t, "A", conformanceError.NestedInterfaceType.QualifiedIdentifier()) require.ErrorAs(t, errs[1], &conformanceError) - assert.Equal(t, conformanceError.InterfaceType.QualifiedIdentifier(), "Q") - assert.Equal(t, conformanceError.NestedInterfaceType.QualifiedIdentifier(), "P") + assert.Equal(t, "Q", conformanceError.InterfaceType.QualifiedIdentifier()) + assert.Equal(t, "P", conformanceError.NestedInterfaceType.QualifiedIdentifier()) }) t.Run("same conformance via different paths", func(t *testing.T) { @@ -3223,8 +3225,8 @@ func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("default impl from two paths", func(t *testing.T) { @@ -3251,8 +3253,8 @@ func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("overridden default impl in one path", func(t *testing.T) { @@ -3279,12 +3281,12 @@ func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { memberConflictError := &sema.InterfaceMemberConflictError{} require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) require.ErrorAs(t, errs[1], &memberConflictError) - assert.Equal(t, memberConflictError.MemberName, "hello") - assert.Equal(t, memberConflictError.ConflictingInterfaceType.QualifiedIdentifier(), "A") + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) t.Run("default impl in one path and condition in another", func(t *testing.T) { From 0b0c47b960ef8d9c0728ded80e9c6734d998d1d8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 1 Nov 2022 12:09:22 -0700 Subject: [PATCH 014/246] Refactor --- runtime/sema/type_tags.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 32fdeaaa54..7f582f40e3 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -823,7 +823,7 @@ func commonSuperTypeOfHeterogeneousTypes(types []Type) Type { func commonSuperTypeOfComposites(types []Type) Type { var hasStructs, hasResources bool - commonInterfaces := map[string]bool{} + commonInterfaces := map[*InterfaceType]struct{}{} commonInterfacesList := make([]*InterfaceType, 0) hasCommonInterface := true @@ -863,20 +863,20 @@ func commonSuperTypeOfComposites(types []Type) Type { if firstType { compositeType.ExplicitInterfaceConformances.ForeachDistinct( func(_ *InterfaceType, interfaceType *InterfaceType) bool { - commonInterfaces[interfaceType.QualifiedIdentifier()] = true + commonInterfaces[interfaceType] = struct{}{} commonInterfacesList = append(commonInterfacesList, interfaceType) return true }, ) firstType = false } else { - intersection := map[string]bool{} + intersection := map[*InterfaceType]struct{}{} commonInterfacesList = make([]*InterfaceType, 0) compositeType.ExplicitInterfaceConformances.ForeachDistinct( func(_ *InterfaceType, interfaceType *InterfaceType) bool { - if _, ok := commonInterfaces[interfaceType.QualifiedIdentifier()]; ok { - intersection[interfaceType.QualifiedIdentifier()] = true + if _, ok := commonInterfaces[interfaceType]; ok { + intersection[interfaceType] = struct{}{} commonInterfacesList = append(commonInterfacesList, interfaceType) } return true From a1bb982c9101a176c246d6dfa503d989056a04f5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 1 Nov 2022 15:12:07 -0700 Subject: [PATCH 015/246] Optimize conformance iteration --- runtime/common/orderedmap/orderedmap.go | 12 -- runtime/interpreter/interpreter.go | 6 +- runtime/sema/check_composite_declaration.go | 164 +++++++++----------- runtime/sema/check_interface_declaration.go | 22 ++- runtime/sema/interfaceset.go | 7 - runtime/sema/type.go | 154 ++++++++++-------- runtime/sema/type_tags.go | 33 ++-- 7 files changed, 195 insertions(+), 203 deletions(-) diff --git a/runtime/common/orderedmap/orderedmap.go b/runtime/common/orderedmap/orderedmap.go index 8a277be624..038fb53d80 100644 --- a/runtime/common/orderedmap/orderedmap.go +++ b/runtime/common/orderedmap/orderedmap.go @@ -190,18 +190,6 @@ func (om *OrderedMap[K, V]) ForeachWithError(f func(key K, value V) error) error return nil } -// ForeachReverse iterates over the entries of the map in the reverse insertion order (LIFO), and invokes -// the provided function for each key-value pair. -func (om OrderedMap[K, V]) ForeachReverse(f func(key K, value V)) { - if om.pairs == nil { - return - } - - for pair := om.Newest(); pair != nil; pair = pair.Prev() { - f(pair.Key, pair.Value) - } -} - // Pair is an entry in an OrderedMap type Pair[K any, V any] struct { Key K diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 48fb2ed597..4f5a2e25ae 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1039,9 +1039,11 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( // in reverse order: first the conformances, then the type requirements; // each conformances and type requirements in reverse order as well. - compositeType.ExplicitInterfaceConformanceSet().ForEachReverse(func(conformance *sema.InterfaceType) { + conformances := compositeType.InterfaceConformances() + for i := len(conformances) - 1; i >= 0; i-- { + conformance := conformances[i].InterfaceType wrapFunctions(interpreter.SharedState.typeCodes.InterfaceCodes[conformance.ID()]) - }) + } typeRequirements := compositeType.TypeRequirements() diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 42130c8e91..9692f88ed1 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -163,24 +163,20 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl inheritedMembers := map[string]struct{}{} typeRequirementsInheritedMembers := map[string]map[string]struct{}{} - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(conformanceChainRoot, conformance *InterfaceType) bool { - checker.checkCompositeConformance( - declaration, - compositeType, - conformance, - conformanceChainRoot, - compositeConformanceCheckOptions{ - checkMissingMembers: checkMissingMembers, - interfaceTypeIsTypeRequirement: false, - }, - inheritedMembers, - typeRequirementsInheritedMembers, - ) - - return true - }, - ) + for _, conformance := range compositeType.InterfaceConformances() { + checker.checkCompositeConformance( + declaration, + compositeType, + conformance.InterfaceType, + conformance.ConformanceChainRoot, + compositeConformanceCheckOptions{ + checkMissingMembers: checkMissingMembers, + interfaceTypeIsTypeRequirement: false, + }, + inheritedMembers, + typeRequirementsInheritedMembers, + ) + } // NOTE: check destructors after initializer and functions @@ -592,65 +588,62 @@ func (checker *Checker) declareCompositeMembersAndValue( var inheritedMembers StringMemberOrderedMap - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_, compositeTypeConformance *InterfaceType) bool { - conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() + for _, compositeTypeConformance := range compositeType.InterfaceConformances() { + conformanceNestedTypes := compositeTypeConformance.InterfaceType.GetNestedTypes() - nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) - if !ok { - return true - } - - typeRequirement, ok := nestedType.(*CompositeType) - if !ok { - return true - } + nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) + if !ok { + continue + } - nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) + typeRequirement, ok := nestedType.(*CompositeType) + if !ok { + continue + } - // Add default functions + nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) - typeRequirement.Members.Foreach(func(memberName string, member *Member) { + // Add default functions - if member.Predeclared || - member.DeclarationKind != common.DeclarationKindFunction { + typeRequirement.Members.Foreach(func(memberName string, member *Member) { - return - } + if member.Predeclared || + member.DeclarationKind != common.DeclarationKindFunction { - _, existing := nestedCompositeType.Members.Get(memberName) - if existing { - return - } + return + } - if _, ok := inheritedMembers.Get(memberName); ok { - if member.HasImplementation { - checker.report( - &MultipleInterfaceDefaultImplementationsError{ - CompositeType: nestedCompositeType, - Member: member, - }, - ) - } else { - checker.report( - &DefaultFunctionConflictError{ - CompositeType: nestedCompositeType, - Member: member, - }, - ) - } - - return - } + _, existing := nestedCompositeType.Members.Get(memberName) + if existing { + return + } + if _, ok := inheritedMembers.Get(memberName); ok { if member.HasImplementation { - inheritedMembers.Set(memberName, member) + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) + } else { + checker.report( + &DefaultFunctionConflictError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) } - }) - return true - }, - ) + return + } + + if member.HasImplementation { + inheritedMembers.Set(memberName, member) + } + }) + + } inheritedMembers.Foreach(func(memberName string, member *Member) { inheritedMember := *member @@ -1442,35 +1435,28 @@ func (checker *Checker) checkTypeRequirement( // Check that the composite declaration declares at least the conformances // that the type requirement stated - requiredCompositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_, requiredConformance *InterfaceType) bool { - found := false + for _, requiredConformance := range requiredCompositeType.InterfaceConformances() { + found := false - declaredCompositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_, conformance *InterfaceType) bool { - if conformance == requiredConformance { - found = true - // stop further checking - return false - } + for _, conformance := range declaredCompositeType.InterfaceConformances() { + if conformance.InterfaceType == requiredConformance.InterfaceType { + found = true + break + } + + } - return true + if !found { + checker.report( + &MissingConformanceError{ + CompositeType: declaredCompositeType, + InterfaceType: requiredConformance.InterfaceType, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.Identifier), }, ) + } - if !found { - checker.report( - &MissingConformanceError{ - CompositeType: declaredCompositeType, - InterfaceType: requiredConformance, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.Identifier), - }, - ) - } - - return true - }, - ) + } // Check the conformance of the composite to the type requirement // like a top-level composite declaration to an interface type diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 6183c2691a..b92d589179 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -54,19 +54,15 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl inheritedMembers := map[string]*Member{} inheritedTypes := map[string]Type{} - interfaceType.ExplicitInterfaceConformances.ForeachDistinct( - func(_, conformance *InterfaceType) bool { - checker.checkInterfaceConformance( - declaration, - interfaceType, - conformance, - inheritedMembers, - inheritedTypes, - ) - - return true - }, - ) + for _, conformance := range interfaceType.InterfaceConformances() { + checker.checkInterfaceConformance( + declaration, + interfaceType, + conformance.InterfaceType, + inheritedMembers, + inheritedTypes, + ) + } // NOTE: functions are checked separately checker.checkFieldsAccessModifier(declaration.Members.Fields()) diff --git a/runtime/sema/interfaceset.go b/runtime/sema/interfaceset.go index 9c3ff6f4cf..1e8dfe048d 100644 --- a/runtime/sema/interfaceset.go +++ b/runtime/sema/interfaceset.go @@ -60,13 +60,6 @@ func (s InterfaceSet) ForEach(f func(*InterfaceType)) { }) } -func (s InterfaceSet) ForEachReverse(f func(*InterfaceType)) { - orderedMap := (orderedmap.OrderedMap[*InterfaceType, struct{}])(s) - orderedMap.ForeachReverse(func(interfaceType *InterfaceType, _ struct{}) { - f(interfaceType) - }) -} - func (s InterfaceSet) Len() int { orderedMap := (orderedmap.OrderedMap[*InterfaceType, struct{}])(s) return orderedMap.Len() diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 91ee378a25..02ce4aa75e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3421,6 +3421,11 @@ type EnumInfo struct { Cases []string } +type Conformance struct { + InterfaceType *InterfaceType + ConformanceChainRoot *InterfaceType +} + type CompositeType struct { Location common.Location Identifier string @@ -3428,7 +3433,9 @@ type CompositeType struct { // an internal set of field `ExplicitInterfaceConformances` explicitInterfaceConformanceSet *InterfaceSet explicitInterfaceConformanceSetOnce sync.Once - ExplicitInterfaceConformances InterfaceConformances + interfaceConformancesOnce sync.Once + interfaceConformances []Conformance + ExplicitInterfaceConformances []*InterfaceType ImplicitTypeRequirementConformances []*CompositeType Members *StringMemberOrderedMap memberResolvers map[string]MemberResolver @@ -3464,15 +3471,24 @@ func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { t.explicitInterfaceConformanceSetOnce.Do(func() { t.explicitInterfaceConformanceSet = NewInterfaceSet() - // Interfaces can also have conformances. - // So add conformances' conformance recursively. - t.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { - t.explicitInterfaceConformanceSet.Add(conformance) - return true - }) + for _, conformance := range t.InterfaceConformances() { + t.explicitInterfaceConformanceSet.Add(conformance.InterfaceType) + } }) } +func (t *CompositeType) InterfaceConformances() []Conformance { + t.interfaceConformancesOnce.Do(func() { + t.interfaceConformances = distinctConformances( + t.ExplicitInterfaceConformances, + nil, + map[*InterfaceType]struct{}{}, + ) + }) + + return t.interfaceConformances +} + func (t *CompositeType) addImplicitTypeRequirementConformance(typeRequirement *CompositeType) { t.ImplicitTypeRequirementConformances = append(t.ImplicitTypeRequirementConformances, typeRequirement) @@ -3702,20 +3718,19 @@ func (t *CompositeType) TypeRequirements() []*CompositeType { var typeRequirements []*CompositeType if containerComposite, ok := t.containerType.(*CompositeType); ok { - containerComposite.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { - ty, ok := conformance.NestedTypes.Get(t.Identifier) + for _, conformance := range containerComposite.InterfaceConformances() { + ty, ok := conformance.InterfaceType.NestedTypes.Get(t.Identifier) if !ok { - return true + continue } typeRequirement, ok := ty.(*CompositeType) if !ok { - return true + continue } typeRequirements = append(typeRequirements, typeRequirement) - return true - }) + } } return typeRequirements @@ -3786,49 +3801,6 @@ func (t *CompositeType) FieldPosition(name string, declaration *ast.CompositeDec return pos } -type InterfaceConformances []*InterfaceType - -// Foreach iterates over the conformances and its nested conformances in a breadth-first manner, -// and invokes the given function. The function have two parameters: -// - `origin` refers to root of the current conformance chain. -// - `conformance` refers to the currently visiting conformance. -func (c InterfaceConformances) Foreach(f func(origin *InterfaceType, conformance *InterfaceType) bool) { - for _, conformance := range c { - if !f(conformance, conformance) { - break - } - - cont := true - conformance.ExplicitInterfaceConformances.Foreach(func(_, nestedConformance *InterfaceType) bool { - cont = f(conformance, nestedConformance) - return cont - }) - - if cont { - continue - } - } -} - -// ForeachDistinct iterates over the conformances and its nested conformances in a breadth-first manner, -// and invokes the given function. Any duplicate conformance would be skipped. -// -// The function have two parameters: -// - `origin` refers to root of the current conformance chain. -// - `conformance` refers to the currently visiting conformance. -func (c InterfaceConformances) ForeachDistinct(f func(origin *InterfaceType, conformance *InterfaceType) bool) { - seenConformances := map[*InterfaceType]struct{}{} - - c.Foreach(func(origin, conformance *InterfaceType) bool { - if _, ok := seenConformances[conformance]; ok { - return true - } - seenConformances[conformance] = struct{}{} - - return f(origin, conformance) - }) -} - // Member type Member struct { @@ -4020,7 +3992,9 @@ type InterfaceType struct { explicitInterfaceConformanceSet *InterfaceSet explicitInterfaceConformanceSetOnce sync.Once - ExplicitInterfaceConformances InterfaceConformances + ExplicitInterfaceConformances []*InterfaceType + interfaceConformancesOnce sync.Once + interfaceConformances []Conformance } func (*InterfaceType) IsType() {} @@ -4251,15 +4225,69 @@ func (t *InterfaceType) initializeExplicitInterfaceConformanceSet() { t.explicitInterfaceConformanceSetOnce.Do(func() { t.explicitInterfaceConformanceSet = NewInterfaceSet() - // Interfaces can also have conformances. - // So add conformances' conformance recursively. - t.ExplicitInterfaceConformances.ForeachDistinct(func(_, conformance *InterfaceType) bool { - t.explicitInterfaceConformanceSet.Add(conformance) - return true - }) + for _, conformance := range t.InterfaceConformances() { + t.explicitInterfaceConformanceSet.Add(conformance.InterfaceType) + } }) } +func (t *InterfaceType) InterfaceConformances() []Conformance { + t.interfaceConformancesOnce.Do(func() { + t.interfaceConformances = distinctConformances( + t.ExplicitInterfaceConformances, + nil, + map[*InterfaceType]struct{}{}, + ) + }) + + return t.interfaceConformances +} + +// distinctConformances recursively visit conformances and their conformances, +// and return all the distinct conformances as an array. +func distinctConformances( + conformances []*InterfaceType, + parent *InterfaceType, + seenConformances map[*InterfaceType]struct{}, +) []Conformance { + + collectedConformances := make([]Conformance, 0) + + var origin *InterfaceType + + for _, conformance := range conformances { + if _, ok := seenConformances[conformance]; ok { + continue + } + seenConformances[conformance] = struct{}{} + + if parent != nil { + origin = parent + } else { + origin = conformance + } + + collectedConformances = append( + collectedConformances, + Conformance{ + InterfaceType: conformance, + ConformanceChainRoot: origin, + }, + ) + + // Recursively collect conformances + nestedConformances := distinctConformances( + conformance.ExplicitInterfaceConformances, + origin, + seenConformances, + ) + + collectedConformances = append(collectedConformances, nestedConformances...) + } + + return collectedConformances +} + // DictionaryType consists of the key and value type // for all key-value pairs in the dictionary: // All keys have to be a subtype of the key type, diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 7f582f40e3..43411bfb8a 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -857,31 +857,30 @@ func commonSuperTypeOfComposites(types []Type) Type { panic(errors.NewUnreachableError()) } - if len(compositeType.ExplicitInterfaceConformances) > 0 { + conformances := compositeType.InterfaceConformances() + + if len(conformances) > 0 { // NOTE: index 0 may not always be the first type, since there can be 'Never' types. if firstType { - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_ *InterfaceType, interfaceType *InterfaceType) bool { - commonInterfaces[interfaceType] = struct{}{} - commonInterfacesList = append(commonInterfacesList, interfaceType) - return true - }, - ) + for _, interfaceType := range conformances { + conformance := interfaceType.InterfaceType + commonInterfaces[conformance] = struct{}{} + commonInterfacesList = append(commonInterfacesList, conformance) + } + firstType = false } else { intersection := map[*InterfaceType]struct{}{} commonInterfacesList = make([]*InterfaceType, 0) - compositeType.ExplicitInterfaceConformances.ForeachDistinct( - func(_ *InterfaceType, interfaceType *InterfaceType) bool { - if _, ok := commonInterfaces[interfaceType]; ok { - intersection[interfaceType] = struct{}{} - commonInterfacesList = append(commonInterfacesList, interfaceType) - } - return true - }, - ) + for _, interfaceType := range conformances { + conformance := interfaceType.InterfaceType + if _, ok := commonInterfaces[conformance]; ok { + intersection[conformance] = struct{}{} + commonInterfacesList = append(commonInterfacesList, conformance) + } + } commonInterfaces = intersection } From d179650be5218f1b48aa6ca67c2bc3f8211b6fa4 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 1 Nov 2022 15:57:19 -0700 Subject: [PATCH 016/246] Refactor code --- runtime/sema/type.go | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 02ce4aa75e..9b04d61492 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3990,11 +3990,9 @@ type InterfaceType struct { } cachedIdentifiersLock sync.RWMutex - explicitInterfaceConformanceSet *InterfaceSet - explicitInterfaceConformanceSetOnce sync.Once - ExplicitInterfaceConformances []*InterfaceType - interfaceConformancesOnce sync.Once - interfaceConformances []Conformance + ExplicitInterfaceConformances []*InterfaceType + interfaceConformancesOnce sync.Once + interfaceConformances []Conformance } func (*InterfaceType) IsType() {} @@ -4216,21 +4214,6 @@ func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDec return declaration.Members.FieldPosition(name, declaration.CompositeKind) } -func (t *InterfaceType) ExplicitInterfaceConformanceSet() *InterfaceSet { - t.initializeExplicitInterfaceConformanceSet() - return t.explicitInterfaceConformanceSet -} - -func (t *InterfaceType) initializeExplicitInterfaceConformanceSet() { - t.explicitInterfaceConformanceSetOnce.Do(func() { - t.explicitInterfaceConformanceSet = NewInterfaceSet() - - for _, conformance := range t.InterfaceConformances() { - t.explicitInterfaceConformanceSet.Add(conformance.InterfaceType) - } - }) -} - func (t *InterfaceType) InterfaceConformances() []Conformance { t.interfaceConformancesOnce.Do(func() { t.interfaceConformances = distinctConformances( From bef0bdc3c9a710a5eb1494f51dca12dca5b2531b Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 11:43:00 -0800 Subject: [PATCH 017/246] add capcon codegen boilerplate --- runtime/sema/capability_controller.cdc | 26 ++++++++++++++++++++++++++ runtime/sema/capability_controller.go | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 runtime/sema/capability_controller.cdc create mode 100644 runtime/sema/capability_controller.go diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/capability_controller.cdc new file mode 100644 index 0000000000..b743fc3b5f --- /dev/null +++ b/runtime/sema/capability_controller.cdc @@ -0,0 +1,26 @@ +pub struct CapabilityController { + /// The block height when the capability was created. + pub let issueHeight: UInt64 + + /// The Type of the capability, i.e.: the T in Capability. + pub let borrowType: Type + + /// The id of the related capability. + /// This is the UUID of the created capability. + /// All copies of the same capability will have the same UUID + pub let capabilityID: UInt64 + + /// Is the capability revoked. + pub let isRevoked: Bool + + /// Returns the targeted storage path of the capability. + pub fun target(): StoragePath {} + + /// Revoke the capability making it no longer usable. + /// When borrowing from a revoked capability the borrow returns nil. + pub fun revoke() {} + + /// Retarget the capability. + /// This moves the CapCon from one CapCon array to another. + pub fun retarget(target: StoragePath) {} +} \ No newline at end of file diff --git a/runtime/sema/capability_controller.go b/runtime/sema/capability_controller.go new file mode 100644 index 0000000000..3ecd37d2de --- /dev/null +++ b/runtime/sema/capability_controller.go @@ -0,0 +1,23 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen/main.go capability_controller.cdc capability_controller.gen.go + +// var CapabilityControllerTypeAnnotation = NewTypeAnnotation(CapabilityControllerType) From 8d1fb210029d909cf7adc158d814ca529fcee794 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 11:47:12 -0800 Subject: [PATCH 018/246] generate capcon type, add capcon type mask --- runtime/sema/capability_controller.gen.go | 225 ++++++++++++++++++++++ runtime/sema/type_tags.go | 2 + 2 files changed, 227 insertions(+) create mode 100644 runtime/sema/capability_controller.gen.go diff --git a/runtime/sema/capability_controller.gen.go b/runtime/sema/capability_controller.gen.go new file mode 100644 index 0000000000..a611113fbc --- /dev/null +++ b/runtime/sema/capability_controller.gen.go @@ -0,0 +1,225 @@ +// Code generated from capability_controller.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" +) + +const CapabilityControllerTypeIssueHeightFieldName = "issueHeight" + +var CapabilityControllerTypeIssueHeightFieldType = UInt64Type + +const CapabilityControllerTypeIssueHeightFieldDocString = `The block height when the capability was created. +` + +const CapabilityControllerTypeBorrowTypeFieldName = "borrowType" + +var CapabilityControllerTypeBorrowTypeFieldType = MetaType + +const CapabilityControllerTypeBorrowTypeFieldDocString = `The Type of the capability, i.e.: the T in Capability. +` + +const CapabilityControllerTypeCapabilityIDFieldName = "capabilityID" + +var CapabilityControllerTypeCapabilityIDFieldType = UInt64Type + +const CapabilityControllerTypeCapabilityIDFieldDocString = `The id of the related capability. +This is the UUID of the created capability. +All copies of the same capability will have the same UUID +` + +const CapabilityControllerTypeIsRevokedFieldName = "isRevoked" + +var CapabilityControllerTypeIsRevokedFieldType = BoolType + +const CapabilityControllerTypeIsRevokedFieldDocString = `Is the capability revoked. +` + +const CapabilityControllerTypeTargetFunctionName = "target" + +var CapabilityControllerTypeTargetFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + StoragePathType, + ), +} + +const CapabilityControllerTypeTargetFunctionDocString = `Returns the targeted storage path of the capability. +` + +const CapabilityControllerTypeRevokeFunctionName = "revoke" + +var CapabilityControllerTypeRevokeFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const CapabilityControllerTypeRevokeFunctionDocString = `Revoke the capability making it no longer usable. +When borrowing from a revoked capability the borrow returns nil. +` + +const CapabilityControllerTypeRetargetFunctionName = "retarget" + +var CapabilityControllerTypeRetargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const CapabilityControllerTypeRetargetFunctionDocString = `Retarget the capability. +This moves the CapCon from one CapCon array to another. +` + +const CapabilityControllerTypeName = "CapabilityController" + +var CapabilityControllerType = &SimpleType{ + Name: CapabilityControllerTypeName, + QualifiedName: CapabilityControllerTypeName, + TypeID: CapabilityControllerTypeName, + tag: CapabilityControllerTypeTag, + IsResource: false, + Storable: false, + Equatable: false, + Exportable: false, + Importable: false, + Members: func(t *SimpleType) map[string]MemberResolver { + return map[string]MemberResolver{ + CapabilityControllerTypeIssueHeightFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeIssueHeightFieldType, + CapabilityControllerTypeIssueHeightFieldDocString, + ) + }, + }, + CapabilityControllerTypeBorrowTypeFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeBorrowTypeFieldType, + CapabilityControllerTypeBorrowTypeFieldDocString, + ) + }, + }, + CapabilityControllerTypeCapabilityIDFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeCapabilityIDFieldType, + CapabilityControllerTypeCapabilityIDFieldDocString, + ) + }, + }, + CapabilityControllerTypeIsRevokedFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeIsRevokedFieldType, + CapabilityControllerTypeIsRevokedFieldDocString, + ) + }, + }, + CapabilityControllerTypeTargetFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeTargetFunctionType, + CapabilityControllerTypeTargetFunctionDocString, + ) + }, + }, + CapabilityControllerTypeRevokeFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeRevokeFunctionType, + CapabilityControllerTypeRevokeFunctionDocString, + ) + }, + }, + CapabilityControllerTypeRetargetFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + CapabilityControllerTypeRetargetFunctionType, + CapabilityControllerTypeRetargetFunctionDocString, + ) + }, + }, + } + }, +} diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 119a10626c..327f7328ad 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -220,6 +220,7 @@ const ( anyStructAttachmentMask invalidTypeMask + capabilityControllerTypeMask ) var ( @@ -333,6 +334,7 @@ var ( TransactionTypeTag = newTypeTagFromUpperMask(transactionTypeMask) AnyResourceAttachmentTypeTag = newTypeTagFromUpperMask(anyResourceAttachmentMask) AnyStructAttachmentTypeTag = newTypeTagFromUpperMask(anyStructAttachmentMask) + CapabilityControllerTypeTag = newTypeTagFromUpperMask(capabilityControllerTypeMask) // AnyStructTypeTag only includes the types that are pre-known // to belong to AnyStruct type. This is more of an optimization. From 1cda300c4de66dc0bb18a361ffe9d0afe69e0fd9 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 11:48:06 -0800 Subject: [PATCH 019/246] add capcon typeannotation --- runtime/sema/capability_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/capability_controller.go b/runtime/sema/capability_controller.go index 3ecd37d2de..780cb57870 100644 --- a/runtime/sema/capability_controller.go +++ b/runtime/sema/capability_controller.go @@ -20,4 +20,4 @@ package sema //go:generate go run ./gen/main.go capability_controller.cdc capability_controller.gen.go -// var CapabilityControllerTypeAnnotation = NewTypeAnnotation(CapabilityControllerType) +var CapabilityControllerTypeAnnotation = NewTypeAnnotation(CapabilityControllerType) From c64774ecb6efb295a1550677e749e4e40b0d4cc1 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 13:21:12 -0800 Subject: [PATCH 020/246] add capcon type to getsupertypefromuppermask --- runtime/sema/type_tags.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 327f7328ad..5570bd0d5e 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -657,10 +657,16 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { restrictedTypeMask, transactionTypeMask: return getSuperTypeOfDerivedTypes(types) + case anyResourceAttachmentMask: return AnyResourceAttachmentType + case anyStructAttachmentMask: return AnyStructAttachmentType + + case capabilityControllerTypeMask: + return CapabilityControllerType + default: return nil } From 103d828281b2a9b6a95cdaa9dd895fefba5b8f4e Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 15:02:27 -0800 Subject: [PATCH 021/246] begin value implementation for capcon type --- runtime/common/memorykind.go | 3 + runtime/common/metering.go | 39 +++++----- runtime/format/capability.go | 12 +++ runtime/interpreter/encode.go | 19 +++++ runtime/interpreter/statictype.go | 36 +++++++++ runtime/interpreter/value.go | 125 ++++++++++++++++++++++++++++++ runtime/interpreter/visitor.go | 95 +++++++++++++---------- 7 files changed, 268 insertions(+), 61 deletions(-) diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 04ea030df6..85a8ec4882 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -51,6 +51,7 @@ const ( MemoryKindBigInt MemoryKindSimpleCompositeValue MemoryKindPublishedValue + MemoryKindCapabilityControllerValue // Atree Nodes MemoryKindAtreeArrayDataSlab @@ -74,6 +75,7 @@ const ( MemoryKindReferenceStaticType MemoryKindCapabilityStaticType MemoryKindFunctionStaticType + MemoryKindCapabilityControllerStaticType // Cadence Values MemoryKindCadenceVoidValue @@ -237,6 +239,7 @@ const ( MemoryKindRestrictedSemaType MemoryKindReferenceSemaType MemoryKindCapabilitySemaType + MemoryKindCapabilityControllerSemaType // ordered-map MemoryKindOrderedMap diff --git a/runtime/common/metering.go b/runtime/common/metering.go index c36ff7ed9c..173628ff4c 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -156,27 +156,29 @@ var ( // Static Types - PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) - CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) - InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) - VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) - ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) - DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) - OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) - RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) - ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) - CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) - FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) + PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) + CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) + InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) + VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) + ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) + DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) + OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) + RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) + ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) + CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) + FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) + CapabilityControllerStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityControllerStaticType) // Sema types - VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) - ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) - DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) - OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) - RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) - ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) - CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) + VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) + ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) + DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) + OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) + RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) + ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) + CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) + CapabilityControllerSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityControllerSemaType) // Storage related memory usages @@ -248,6 +250,7 @@ var ( PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) + CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) diff --git a/runtime/format/capability.go b/runtime/format/capability.go index b5bc057e51..0a5b550a8c 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -35,3 +35,15 @@ func StorageCapability(borrowType string, address string, path string) string { path, ) } + +func CapabilityController(borrowType string) string { + var typeArgument string + if borrowType != "" { + typeArgument = fmt.Sprintf("<%s>", borrowType) + } + + return fmt.Sprintf( + "CapabilityController(borrowType: %s)", + typeArgument, + ) +} diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index a6f3a23509..0b2d69f9a0 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -218,6 +218,7 @@ const ( CBORTagReferenceStaticType CBORTagRestrictedStaticType CBORTagCapabilityStaticType + CBORTagCapabilityControllerStaticType // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -1385,6 +1386,24 @@ func (t FunctionStaticType) Encode(_ *cbor.StreamEncoder) error { } } +// Encode encodes CapabilityControllerStaticType as +// +// cbor.Tag{ +// Number: CBORTagCapabilityControllerStaticType, +// Content: StaticType(v.BorrowType) +// } +func (t CapabilityControllerStaticType) Encode(e *cbor.StreamEncoder) error { + err := e.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagCapabilityControllerStaticType, + }) + if err != nil { + return err + } + + return EncodeStaticType(e, t.BorrowType) +} + // compositeTypeInfo type compositeTypeInfo struct { location common.Location diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index 434db60151..18fa3cd1d3 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -945,3 +945,39 @@ func (p TypeParameter) String() string { } return builder.String() } + +type CapabilityControllerStaticType struct { + BorrowType StaticType +} + +func NewCapabilityControllerStaticType(gauge common.MemoryGauge, borrowType StaticType) CapabilityControllerStaticType { + common.UseMemory(gauge, common.CapabilityControllerStaticTypeMemoryUsage) + + return CapabilityControllerStaticType{ + BorrowType: borrowType, + } +} + +var _ StaticType = &CapabilityControllerStaticType{} + +func (t CapabilityControllerStaticType) String() string { + panic("not implemented") // TODO: Implement +} + +func (t CapabilityControllerStaticType) isStaticType() {} + +/* + this returns the size (in bytes) of the largest inhabitant of this type, + or UnknownElementSize if the largest inhabitant has arbitrary size +*/ +func (t CapabilityControllerStaticType) elementSize() uint { + panic("not implemented") // TODO: Implement +} + +func (t CapabilityControllerStaticType) Equal(other StaticType) bool { + panic("not implemented") // TODO: Implement +} + +func (t CapabilityControllerStaticType) MeteredString(memoryGauge common.MemoryGauge) string { + panic("not implemented") // TODO: Implement +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index b092e6bf05..af8f0eb808 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18835,3 +18835,128 @@ func (v AccountLinkValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) func (v AccountLinkValue) ChildStorables() []atree.Storable { return nil } + +type CapabilityControllerValue struct { + IssueHeight uint64 + BorrowType StaticType + CapabilityID uint64 + IsRevoked bool +} + +var _ Value = &CapabilityControllerValue{} +var _ MemberAccessibleValue = &CapabilityControllerValue{} + +func (v CapabilityControllerValue) Storable(_ atree.SlabStorage, _ atree.Address, _ uint64) (atree.Storable, error) { + panic("not implemented") // TODO: Implement +} + +func (v CapabilityControllerValue) String() string { + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.String() + } + + return format.CapabilityController(borrowType) +} + +func (v CapabilityControllerValue) IsValue() {} + +func (v CapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { + visitor.VisitCapabilityControllerValue(interpreter, v) +} + +func (v CapabilityControllerValue) Walk(interpreter *Interpreter, walkChild func(Value)) { + panic("not implemented") // TODO: Implement +} + +func (v CapabilityControllerValue) StaticType(interpreter *Interpreter) StaticType { + return NewCapabilityControllerStaticType(interpreter, v.BorrowType) +} + +// ConformsToStaticType returns true if the value (i.e. its dynamic type) +// conforms to its own static type. +// Non-container values trivially always conform to their own static type. +// Container values conform to their own static type, +// and this function recursively checks conformance for nested values. +// If the container contains static type information about nested values, +// e.g. the element type of an array, it also ensures the nested values' +// static types are subtypes. +func (v CapabilityControllerValue) ConformsToStaticType(interpreter *Interpreter, locationRange LocationRange, results TypeConformanceResults) bool { + panic("not implemented") // TODO: Implement +} + +func (v CapabilityControllerValue) RecursiveString(_ SeenReferences) string { + return v.String() +} + +func (v CapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) + + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.MeteredString(memoryGauge) + } + return format.CapabilityController(borrowType) +} + +func (v CapabilityControllerValue) IsResourceKinded(_ *Interpreter) bool { + return false +} + +func (v CapabilityControllerValue) NeedsStoreTo(_ atree.Address) bool { + return false +} + +func (v CapabilityControllerValue) Transfer(interpreter *Interpreter, locationRange LocationRange, address atree.Address, remove bool, storable atree.Storable) Value { + if remove { + interpreter.RemoveReferencedSlab(storable) + } + return v +} + +func (v CapabilityControllerValue) DeepRemove(interpreter *Interpreter) { + // NO-OP +} + +// Clone returns a new value that is equal to this value. +// NOTE: not used by interpreter, but used externally (e.g. state migration) +// NOTE: memory metering is unnecessary for Clone methods +func (v CapabilityControllerValue) Clone(interpreter *Interpreter) Value { + // TODO ask about this, since we expose a block height when this was issued. state migrations might affect this + return v +} + +func (CapabilityControllerValue) IsImportable(interpreter *Interpreter) bool { + return sema.CapabilityControllerType.Importable +} + +func (v CapabilityControllerValue) GetMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { + switch name { + case sema.CapabilityControllerTypeIssueHeightFieldName: + return NewUInt64Value(interpreter, func() uint64 { return v.IssueHeight }) + case sema.CapabilityControllerTypeBorrowTypeFieldName: + return NewTypeValue(interpreter, v.BorrowType) + case sema.CapabilityControllerTypeCapabilityIDFieldName: + return NewUInt64Value(interpreter, func() uint64 { return v.CapabilityID }) + case sema.CapabilityControllerTypeIsRevokedFieldName: + return AsBoolValue(v.IsRevoked) + case sema.CapabilityControllerTypeTargetFunctionName: + panic("not implemented") + case sema.CapabilityControllerTypeRevokeFunctionName: + panic("not implemented") + case sema.CapabilityControllerTypeRetargetFunctionName: + panic("not implemented") + } + + return nil +} + +func (v CapabilityControllerValue) RemoveMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { + // Capabilities have no removable members (fields / functions) + panic(errors.NewUnreachableError()) +} + +func (v CapabilityControllerValue) SetMember(interpreter *Interpreter, locationRange LocationRange, name string, value Value) { + // Capabilities have no settable members (fields / functions) + panic(errors.NewUnreachableError()) +} diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index 754b766d6f..bf0e2152d9 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -62,52 +62,54 @@ type Visitor interface { VisitInterpretedFunctionValue(interpreter *Interpreter, value *InterpretedFunctionValue) VisitHostFunctionValue(interpreter *Interpreter, value *HostFunctionValue) VisitBoundFunctionValue(interpreter *Interpreter, value BoundFunctionValue) + VisitCapabilityControllerValue(interpreter *Interpreter, value CapabilityControllerValue) } type EmptyVisitor struct { - SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) - TypeValueVisitor func(interpreter *Interpreter, value TypeValue) - VoidValueVisitor func(interpreter *Interpreter, value VoidValue) - BoolValueVisitor func(interpreter *Interpreter, value BoolValue) - CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) - StringValueVisitor func(interpreter *Interpreter, value *StringValue) - ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool - IntValueVisitor func(interpreter *Interpreter, value IntValue) - Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) - Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) - Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) - Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) - Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) - Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) - UIntValueVisitor func(interpreter *Interpreter, value UIntValue) - UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) - UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) - UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) - UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) - UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) - UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) - Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) - Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) - Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) - Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) - Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) - UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) - CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool - DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool - NilValueVisitor func(interpreter *Interpreter, value NilValue) - SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool - StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) - AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) - EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) - AddressValueVisitor func(interpreter *Interpreter, value AddressValue) - PathValueVisitor func(interpreter *Interpreter, value PathValue) - StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) - PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) - AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) - PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) - InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) - HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) - BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) + SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) + TypeValueVisitor func(interpreter *Interpreter, value TypeValue) + VoidValueVisitor func(interpreter *Interpreter, value VoidValue) + BoolValueVisitor func(interpreter *Interpreter, value BoolValue) + CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) + StringValueVisitor func(interpreter *Interpreter, value *StringValue) + ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool + IntValueVisitor func(interpreter *Interpreter, value IntValue) + Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) + Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) + Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) + Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) + Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) + Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) + UIntValueVisitor func(interpreter *Interpreter, value UIntValue) + UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) + UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) + UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) + UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) + UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) + UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) + Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) + Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) + Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) + Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) + Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) + UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) + CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool + DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool + NilValueVisitor func(interpreter *Interpreter, value NilValue) + SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool + StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) + AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) + EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) + AddressValueVisitor func(interpreter *Interpreter, value AddressValue) + PathValueVisitor func(interpreter *Interpreter, value PathValue) + StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) + PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) + AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) + PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) + InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) + HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) + BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) + CapabilityControllerValueVisitor func(interpreter *Interpreter, value CapabilityControllerValue) } var _ Visitor = &EmptyVisitor{} @@ -412,3 +414,10 @@ func (v EmptyVisitor) VisitBoundFunctionValue(interpreter *Interpreter, value Bo } v.BoundFunctionValueVisitor(interpreter, value) } + +func (v EmptyVisitor) VisitCapabilityControllerValue(interpreter *Interpreter, value CapabilityControllerValue) { + if v.CapabilityControllerValueVisitor == nil { + return + } + v.CapabilityControllerValueVisitor(interpreter, value) +} From c1c2d9059833c96b59ec15b93c5a8ae62be1ddcd Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 15:03:02 -0800 Subject: [PATCH 022/246] run go generate --- runtime/common/memorykind_string.go | 337 ++++++++++++++-------------- 1 file changed, 170 insertions(+), 167 deletions(-) diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 922940626b..9110683535 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -32,176 +32,179 @@ func _() { _ = x[MemoryKindBigInt-21] _ = x[MemoryKindSimpleCompositeValue-22] _ = x[MemoryKindPublishedValue-23] - _ = x[MemoryKindAtreeArrayDataSlab-24] - _ = x[MemoryKindAtreeArrayMetaDataSlab-25] - _ = x[MemoryKindAtreeArrayElementOverhead-26] - _ = x[MemoryKindAtreeMapDataSlab-27] - _ = x[MemoryKindAtreeMapMetaDataSlab-28] - _ = x[MemoryKindAtreeMapElementOverhead-29] - _ = x[MemoryKindAtreeMapPreAllocatedElement-30] - _ = x[MemoryKindAtreeEncodedSlab-31] - _ = x[MemoryKindPrimitiveStaticType-32] - _ = x[MemoryKindCompositeStaticType-33] - _ = x[MemoryKindInterfaceStaticType-34] - _ = x[MemoryKindVariableSizedStaticType-35] - _ = x[MemoryKindConstantSizedStaticType-36] - _ = x[MemoryKindDictionaryStaticType-37] - _ = x[MemoryKindOptionalStaticType-38] - _ = x[MemoryKindRestrictedStaticType-39] - _ = x[MemoryKindReferenceStaticType-40] - _ = x[MemoryKindCapabilityStaticType-41] - _ = x[MemoryKindFunctionStaticType-42] - _ = x[MemoryKindCadenceVoidValue-43] - _ = x[MemoryKindCadenceOptionalValue-44] - _ = x[MemoryKindCadenceBoolValue-45] - _ = x[MemoryKindCadenceStringValue-46] - _ = x[MemoryKindCadenceCharacterValue-47] - _ = x[MemoryKindCadenceAddressValue-48] - _ = x[MemoryKindCadenceIntValue-49] - _ = x[MemoryKindCadenceNumberValue-50] - _ = x[MemoryKindCadenceArrayValueBase-51] - _ = x[MemoryKindCadenceArrayValueLength-52] - _ = x[MemoryKindCadenceDictionaryValue-53] - _ = x[MemoryKindCadenceKeyValuePair-54] - _ = x[MemoryKindCadenceStructValueBase-55] - _ = x[MemoryKindCadenceStructValueSize-56] - _ = x[MemoryKindCadenceResourceValueBase-57] - _ = x[MemoryKindCadenceAttachmentValueBase-58] - _ = x[MemoryKindCadenceResourceValueSize-59] - _ = x[MemoryKindCadenceAttachmentValueSize-60] - _ = x[MemoryKindCadenceEventValueBase-61] - _ = x[MemoryKindCadenceEventValueSize-62] - _ = x[MemoryKindCadenceContractValueBase-63] - _ = x[MemoryKindCadenceContractValueSize-64] - _ = x[MemoryKindCadenceEnumValueBase-65] - _ = x[MemoryKindCadenceEnumValueSize-66] - _ = x[MemoryKindCadencePathLinkValue-67] - _ = x[MemoryKindCadenceAccountLinkValue-68] - _ = x[MemoryKindCadencePathValue-69] - _ = x[MemoryKindCadenceTypeValue-70] - _ = x[MemoryKindCadenceStorageCapabilityValue-71] - _ = x[MemoryKindCadenceFunctionValue-72] - _ = x[MemoryKindCadenceOptionalType-73] - _ = x[MemoryKindCadenceVariableSizedArrayType-74] - _ = x[MemoryKindCadenceConstantSizedArrayType-75] - _ = x[MemoryKindCadenceDictionaryType-76] - _ = x[MemoryKindCadenceField-77] - _ = x[MemoryKindCadenceParameter-78] - _ = x[MemoryKindCadenceTypeParameter-79] - _ = x[MemoryKindCadenceStructType-80] - _ = x[MemoryKindCadenceResourceType-81] - _ = x[MemoryKindCadenceAttachmentType-82] - _ = x[MemoryKindCadenceEventType-83] - _ = x[MemoryKindCadenceContractType-84] - _ = x[MemoryKindCadenceStructInterfaceType-85] - _ = x[MemoryKindCadenceResourceInterfaceType-86] - _ = x[MemoryKindCadenceContractInterfaceType-87] - _ = x[MemoryKindCadenceFunctionType-88] - _ = x[MemoryKindCadenceReferenceType-89] - _ = x[MemoryKindCadenceRestrictedType-90] - _ = x[MemoryKindCadenceCapabilityType-91] - _ = x[MemoryKindCadenceEnumType-92] - _ = x[MemoryKindRawString-93] - _ = x[MemoryKindAddressLocation-94] - _ = x[MemoryKindBytes-95] - _ = x[MemoryKindVariable-96] - _ = x[MemoryKindCompositeTypeInfo-97] - _ = x[MemoryKindCompositeField-98] - _ = x[MemoryKindInvocation-99] - _ = x[MemoryKindStorageMap-100] - _ = x[MemoryKindStorageKey-101] - _ = x[MemoryKindTypeToken-102] - _ = x[MemoryKindErrorToken-103] - _ = x[MemoryKindSpaceToken-104] - _ = x[MemoryKindProgram-105] - _ = x[MemoryKindIdentifier-106] - _ = x[MemoryKindArgument-107] - _ = x[MemoryKindBlock-108] - _ = x[MemoryKindFunctionBlock-109] - _ = x[MemoryKindParameter-110] - _ = x[MemoryKindParameterList-111] - _ = x[MemoryKindTypeParameter-112] - _ = x[MemoryKindTypeParameterList-113] - _ = x[MemoryKindTransfer-114] - _ = x[MemoryKindMembers-115] - _ = x[MemoryKindTypeAnnotation-116] - _ = x[MemoryKindDictionaryEntry-117] - _ = x[MemoryKindFunctionDeclaration-118] - _ = x[MemoryKindCompositeDeclaration-119] - _ = x[MemoryKindAttachmentDeclaration-120] - _ = x[MemoryKindInterfaceDeclaration-121] - _ = x[MemoryKindEnumCaseDeclaration-122] - _ = x[MemoryKindFieldDeclaration-123] - _ = x[MemoryKindTransactionDeclaration-124] - _ = x[MemoryKindImportDeclaration-125] - _ = x[MemoryKindVariableDeclaration-126] - _ = x[MemoryKindSpecialFunctionDeclaration-127] - _ = x[MemoryKindPragmaDeclaration-128] - _ = x[MemoryKindAssignmentStatement-129] - _ = x[MemoryKindBreakStatement-130] - _ = x[MemoryKindContinueStatement-131] - _ = x[MemoryKindEmitStatement-132] - _ = x[MemoryKindExpressionStatement-133] - _ = x[MemoryKindForStatement-134] - _ = x[MemoryKindIfStatement-135] - _ = x[MemoryKindReturnStatement-136] - _ = x[MemoryKindSwapStatement-137] - _ = x[MemoryKindSwitchStatement-138] - _ = x[MemoryKindWhileStatement-139] - _ = x[MemoryKindRemoveStatement-140] - _ = x[MemoryKindBooleanExpression-141] - _ = x[MemoryKindVoidExpression-142] - _ = x[MemoryKindNilExpression-143] - _ = x[MemoryKindStringExpression-144] - _ = x[MemoryKindIntegerExpression-145] - _ = x[MemoryKindFixedPointExpression-146] - _ = x[MemoryKindArrayExpression-147] - _ = x[MemoryKindDictionaryExpression-148] - _ = x[MemoryKindIdentifierExpression-149] - _ = x[MemoryKindInvocationExpression-150] - _ = x[MemoryKindMemberExpression-151] - _ = x[MemoryKindIndexExpression-152] - _ = x[MemoryKindConditionalExpression-153] - _ = x[MemoryKindUnaryExpression-154] - _ = x[MemoryKindBinaryExpression-155] - _ = x[MemoryKindFunctionExpression-156] - _ = x[MemoryKindCastingExpression-157] - _ = x[MemoryKindCreateExpression-158] - _ = x[MemoryKindDestroyExpression-159] - _ = x[MemoryKindReferenceExpression-160] - _ = x[MemoryKindForceExpression-161] - _ = x[MemoryKindPathExpression-162] - _ = x[MemoryKindAttachExpression-163] - _ = x[MemoryKindConstantSizedType-164] - _ = x[MemoryKindDictionaryType-165] - _ = x[MemoryKindFunctionType-166] - _ = x[MemoryKindInstantiationType-167] - _ = x[MemoryKindNominalType-168] - _ = x[MemoryKindOptionalType-169] - _ = x[MemoryKindReferenceType-170] - _ = x[MemoryKindRestrictedType-171] - _ = x[MemoryKindVariableSizedType-172] - _ = x[MemoryKindPosition-173] - _ = x[MemoryKindRange-174] - _ = x[MemoryKindElaboration-175] - _ = x[MemoryKindActivation-176] - _ = x[MemoryKindActivationEntries-177] - _ = x[MemoryKindVariableSizedSemaType-178] - _ = x[MemoryKindConstantSizedSemaType-179] - _ = x[MemoryKindDictionarySemaType-180] - _ = x[MemoryKindOptionalSemaType-181] - _ = x[MemoryKindRestrictedSemaType-182] - _ = x[MemoryKindReferenceSemaType-183] - _ = x[MemoryKindCapabilitySemaType-184] - _ = x[MemoryKindOrderedMap-185] - _ = x[MemoryKindOrderedMapEntryList-186] - _ = x[MemoryKindOrderedMapEntry-187] - _ = x[MemoryKindLast-188] + _ = x[MemoryKindCapabilityControllerValue-24] + _ = x[MemoryKindAtreeArrayDataSlab-25] + _ = x[MemoryKindAtreeArrayMetaDataSlab-26] + _ = x[MemoryKindAtreeArrayElementOverhead-27] + _ = x[MemoryKindAtreeMapDataSlab-28] + _ = x[MemoryKindAtreeMapMetaDataSlab-29] + _ = x[MemoryKindAtreeMapElementOverhead-30] + _ = x[MemoryKindAtreeMapPreAllocatedElement-31] + _ = x[MemoryKindAtreeEncodedSlab-32] + _ = x[MemoryKindPrimitiveStaticType-33] + _ = x[MemoryKindCompositeStaticType-34] + _ = x[MemoryKindInterfaceStaticType-35] + _ = x[MemoryKindVariableSizedStaticType-36] + _ = x[MemoryKindConstantSizedStaticType-37] + _ = x[MemoryKindDictionaryStaticType-38] + _ = x[MemoryKindOptionalStaticType-39] + _ = x[MemoryKindRestrictedStaticType-40] + _ = x[MemoryKindReferenceStaticType-41] + _ = x[MemoryKindCapabilityStaticType-42] + _ = x[MemoryKindFunctionStaticType-43] + _ = x[MemoryKindCapabilityControllerStaticType-44] + _ = x[MemoryKindCadenceVoidValue-45] + _ = x[MemoryKindCadenceOptionalValue-46] + _ = x[MemoryKindCadenceBoolValue-47] + _ = x[MemoryKindCadenceStringValue-48] + _ = x[MemoryKindCadenceCharacterValue-49] + _ = x[MemoryKindCadenceAddressValue-50] + _ = x[MemoryKindCadenceIntValue-51] + _ = x[MemoryKindCadenceNumberValue-52] + _ = x[MemoryKindCadenceArrayValueBase-53] + _ = x[MemoryKindCadenceArrayValueLength-54] + _ = x[MemoryKindCadenceDictionaryValue-55] + _ = x[MemoryKindCadenceKeyValuePair-56] + _ = x[MemoryKindCadenceStructValueBase-57] + _ = x[MemoryKindCadenceStructValueSize-58] + _ = x[MemoryKindCadenceResourceValueBase-59] + _ = x[MemoryKindCadenceAttachmentValueBase-60] + _ = x[MemoryKindCadenceResourceValueSize-61] + _ = x[MemoryKindCadenceAttachmentValueSize-62] + _ = x[MemoryKindCadenceEventValueBase-63] + _ = x[MemoryKindCadenceEventValueSize-64] + _ = x[MemoryKindCadenceContractValueBase-65] + _ = x[MemoryKindCadenceContractValueSize-66] + _ = x[MemoryKindCadenceEnumValueBase-67] + _ = x[MemoryKindCadenceEnumValueSize-68] + _ = x[MemoryKindCadencePathLinkValue-69] + _ = x[MemoryKindCadenceAccountLinkValue-70] + _ = x[MemoryKindCadencePathValue-71] + _ = x[MemoryKindCadenceTypeValue-72] + _ = x[MemoryKindCadenceStorageCapabilityValue-73] + _ = x[MemoryKindCadenceFunctionValue-74] + _ = x[MemoryKindCadenceOptionalType-75] + _ = x[MemoryKindCadenceVariableSizedArrayType-76] + _ = x[MemoryKindCadenceConstantSizedArrayType-77] + _ = x[MemoryKindCadenceDictionaryType-78] + _ = x[MemoryKindCadenceField-79] + _ = x[MemoryKindCadenceParameter-80] + _ = x[MemoryKindCadenceTypeParameter-81] + _ = x[MemoryKindCadenceStructType-82] + _ = x[MemoryKindCadenceResourceType-83] + _ = x[MemoryKindCadenceAttachmentType-84] + _ = x[MemoryKindCadenceEventType-85] + _ = x[MemoryKindCadenceContractType-86] + _ = x[MemoryKindCadenceStructInterfaceType-87] + _ = x[MemoryKindCadenceResourceInterfaceType-88] + _ = x[MemoryKindCadenceContractInterfaceType-89] + _ = x[MemoryKindCadenceFunctionType-90] + _ = x[MemoryKindCadenceReferenceType-91] + _ = x[MemoryKindCadenceRestrictedType-92] + _ = x[MemoryKindCadenceCapabilityType-93] + _ = x[MemoryKindCadenceEnumType-94] + _ = x[MemoryKindRawString-95] + _ = x[MemoryKindAddressLocation-96] + _ = x[MemoryKindBytes-97] + _ = x[MemoryKindVariable-98] + _ = x[MemoryKindCompositeTypeInfo-99] + _ = x[MemoryKindCompositeField-100] + _ = x[MemoryKindInvocation-101] + _ = x[MemoryKindStorageMap-102] + _ = x[MemoryKindStorageKey-103] + _ = x[MemoryKindTypeToken-104] + _ = x[MemoryKindErrorToken-105] + _ = x[MemoryKindSpaceToken-106] + _ = x[MemoryKindProgram-107] + _ = x[MemoryKindIdentifier-108] + _ = x[MemoryKindArgument-109] + _ = x[MemoryKindBlock-110] + _ = x[MemoryKindFunctionBlock-111] + _ = x[MemoryKindParameter-112] + _ = x[MemoryKindParameterList-113] + _ = x[MemoryKindTypeParameter-114] + _ = x[MemoryKindTypeParameterList-115] + _ = x[MemoryKindTransfer-116] + _ = x[MemoryKindMembers-117] + _ = x[MemoryKindTypeAnnotation-118] + _ = x[MemoryKindDictionaryEntry-119] + _ = x[MemoryKindFunctionDeclaration-120] + _ = x[MemoryKindCompositeDeclaration-121] + _ = x[MemoryKindAttachmentDeclaration-122] + _ = x[MemoryKindInterfaceDeclaration-123] + _ = x[MemoryKindEnumCaseDeclaration-124] + _ = x[MemoryKindFieldDeclaration-125] + _ = x[MemoryKindTransactionDeclaration-126] + _ = x[MemoryKindImportDeclaration-127] + _ = x[MemoryKindVariableDeclaration-128] + _ = x[MemoryKindSpecialFunctionDeclaration-129] + _ = x[MemoryKindPragmaDeclaration-130] + _ = x[MemoryKindAssignmentStatement-131] + _ = x[MemoryKindBreakStatement-132] + _ = x[MemoryKindContinueStatement-133] + _ = x[MemoryKindEmitStatement-134] + _ = x[MemoryKindExpressionStatement-135] + _ = x[MemoryKindForStatement-136] + _ = x[MemoryKindIfStatement-137] + _ = x[MemoryKindReturnStatement-138] + _ = x[MemoryKindSwapStatement-139] + _ = x[MemoryKindSwitchStatement-140] + _ = x[MemoryKindWhileStatement-141] + _ = x[MemoryKindRemoveStatement-142] + _ = x[MemoryKindBooleanExpression-143] + _ = x[MemoryKindVoidExpression-144] + _ = x[MemoryKindNilExpression-145] + _ = x[MemoryKindStringExpression-146] + _ = x[MemoryKindIntegerExpression-147] + _ = x[MemoryKindFixedPointExpression-148] + _ = x[MemoryKindArrayExpression-149] + _ = x[MemoryKindDictionaryExpression-150] + _ = x[MemoryKindIdentifierExpression-151] + _ = x[MemoryKindInvocationExpression-152] + _ = x[MemoryKindMemberExpression-153] + _ = x[MemoryKindIndexExpression-154] + _ = x[MemoryKindConditionalExpression-155] + _ = x[MemoryKindUnaryExpression-156] + _ = x[MemoryKindBinaryExpression-157] + _ = x[MemoryKindFunctionExpression-158] + _ = x[MemoryKindCastingExpression-159] + _ = x[MemoryKindCreateExpression-160] + _ = x[MemoryKindDestroyExpression-161] + _ = x[MemoryKindReferenceExpression-162] + _ = x[MemoryKindForceExpression-163] + _ = x[MemoryKindPathExpression-164] + _ = x[MemoryKindAttachExpression-165] + _ = x[MemoryKindConstantSizedType-166] + _ = x[MemoryKindDictionaryType-167] + _ = x[MemoryKindFunctionType-168] + _ = x[MemoryKindInstantiationType-169] + _ = x[MemoryKindNominalType-170] + _ = x[MemoryKindOptionalType-171] + _ = x[MemoryKindReferenceType-172] + _ = x[MemoryKindRestrictedType-173] + _ = x[MemoryKindVariableSizedType-174] + _ = x[MemoryKindPosition-175] + _ = x[MemoryKindRange-176] + _ = x[MemoryKindElaboration-177] + _ = x[MemoryKindActivation-178] + _ = x[MemoryKindActivationEntries-179] + _ = x[MemoryKindVariableSizedSemaType-180] + _ = x[MemoryKindConstantSizedSemaType-181] + _ = x[MemoryKindDictionarySemaType-182] + _ = x[MemoryKindOptionalSemaType-183] + _ = x[MemoryKindRestrictedSemaType-184] + _ = x[MemoryKindReferenceSemaType-185] + _ = x[MemoryKindCapabilitySemaType-186] + _ = x[MemoryKindCapabilityControllerSemaType-187] + _ = x[MemoryKindOrderedMap-188] + _ = x[MemoryKindOrderedMapEntryList-189] + _ = x[MemoryKindOrderedMapEntry-190] + _ = x[MemoryKindLast-191] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCapabilityControllerStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeCapabilityControllerSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 394, 416, 441, 457, 477, 500, 527, 543, 562, 581, 600, 623, 646, 666, 684, 704, 723, 743, 761, 777, 797, 813, 831, 852, 871, 886, 904, 925, 948, 970, 989, 1011, 1033, 1057, 1083, 1107, 1133, 1154, 1175, 1199, 1223, 1243, 1263, 1283, 1306, 1322, 1338, 1367, 1387, 1406, 1435, 1464, 1485, 1497, 1513, 1533, 1550, 1569, 1590, 1606, 1625, 1651, 1679, 1707, 1726, 1746, 1767, 1788, 1803, 1812, 1827, 1832, 1840, 1857, 1871, 1881, 1891, 1901, 1910, 1920, 1930, 1937, 1947, 1955, 1960, 1973, 1982, 1995, 2008, 2025, 2033, 2040, 2054, 2069, 2088, 2108, 2129, 2149, 2168, 2184, 2206, 2223, 2242, 2268, 2285, 2304, 2318, 2335, 2348, 2367, 2379, 2390, 2405, 2418, 2433, 2447, 2462, 2479, 2493, 2506, 2522, 2539, 2559, 2574, 2594, 2614, 2634, 2650, 2665, 2686, 2701, 2717, 2735, 2752, 2768, 2785, 2804, 2819, 2833, 2849, 2866, 2880, 2892, 2909, 2920, 2932, 2945, 2959, 2976, 2984, 2989, 3000, 3010, 3027, 3048, 3069, 3087, 3103, 3121, 3138, 3156, 3166, 3185, 3200, 3204} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 401, 419, 441, 466, 482, 502, 525, 552, 568, 587, 606, 625, 648, 671, 691, 709, 729, 748, 768, 786, 816, 832, 852, 868, 886, 907, 926, 941, 959, 980, 1003, 1025, 1044, 1066, 1088, 1112, 1138, 1162, 1188, 1209, 1230, 1254, 1278, 1298, 1318, 1338, 1361, 1377, 1393, 1422, 1442, 1461, 1490, 1519, 1540, 1552, 1568, 1588, 1605, 1624, 1645, 1661, 1680, 1706, 1734, 1762, 1781, 1801, 1822, 1843, 1858, 1867, 1882, 1887, 1895, 1912, 1926, 1936, 1946, 1956, 1965, 1975, 1985, 1992, 2002, 2010, 2015, 2028, 2037, 2050, 2063, 2080, 2088, 2095, 2109, 2124, 2143, 2163, 2184, 2204, 2223, 2239, 2261, 2278, 2297, 2323, 2340, 2359, 2373, 2390, 2403, 2422, 2434, 2445, 2460, 2473, 2488, 2502, 2517, 2534, 2548, 2561, 2577, 2594, 2614, 2629, 2649, 2669, 2689, 2705, 2720, 2741, 2756, 2772, 2790, 2807, 2823, 2840, 2859, 2874, 2888, 2904, 2921, 2935, 2947, 2964, 2975, 2987, 3000, 3014, 3031, 3039, 3044, 3055, 3065, 3082, 3103, 3124, 3142, 3158, 3176, 3193, 3211, 3239, 3249, 3268, 3283, 3287} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { From 2be36137f19dd3049dd205c072483edc0953d472 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 15:13:23 -0800 Subject: [PATCH 023/246] remove cbor encoding for capcons, continue implementing value --- runtime/interpreter/encode.go | 18 +++--------------- runtime/interpreter/value.go | 10 +++++----- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 0b2d69f9a0..f437932a11 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -28,6 +28,7 @@ import ( "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/sema" ) const cborTagSize = 2 @@ -218,7 +219,6 @@ const ( CBORTagReferenceStaticType CBORTagRestrictedStaticType CBORTagCapabilityStaticType - CBORTagCapabilityControllerStaticType // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -1386,22 +1386,10 @@ func (t FunctionStaticType) Encode(_ *cbor.StreamEncoder) error { } } -// Encode encodes CapabilityControllerStaticType as -// -// cbor.Tag{ -// Number: CBORTagCapabilityControllerStaticType, -// Content: StaticType(v.BorrowType) -// } func (t CapabilityControllerStaticType) Encode(e *cbor.StreamEncoder) error { - err := e.EncodeRawBytes([]byte{ - // tag number - 0xd8, CBORTagCapabilityControllerStaticType, - }) - if err != nil { - return err + return NonStorableStaticTypeError{ + Type: sema.CapabilityControllerType, } - - return EncodeStaticType(e, t.BorrowType) } // compositeTypeInfo diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index af8f0eb808..9a67099e68 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18847,7 +18847,7 @@ var _ Value = &CapabilityControllerValue{} var _ MemberAccessibleValue = &CapabilityControllerValue{} func (v CapabilityControllerValue) Storable(_ atree.SlabStorage, _ atree.Address, _ uint64) (atree.Storable, error) { - panic("not implemented") // TODO: Implement + return NonStorable{Value: v}, nil } func (v CapabilityControllerValue) String() string { @@ -18866,7 +18866,7 @@ func (v CapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visi } func (v CapabilityControllerValue) Walk(interpreter *Interpreter, walkChild func(Value)) { - panic("not implemented") // TODO: Implement + // NO-OP } func (v CapabilityControllerValue) StaticType(interpreter *Interpreter) StaticType { @@ -18882,7 +18882,7 @@ func (v CapabilityControllerValue) StaticType(interpreter *Interpreter) StaticTy // e.g. the element type of an array, it also ensures the nested values' // static types are subtypes. func (v CapabilityControllerValue) ConformsToStaticType(interpreter *Interpreter, locationRange LocationRange, results TypeConformanceResults) bool { - panic("not implemented") // TODO: Implement + return true // TODO verify this, given that we store the borrowed type as a field. Capabilities themselves are invariant though } func (v CapabilityControllerValue) RecursiveString(_ SeenReferences) string { @@ -18952,11 +18952,11 @@ func (v CapabilityControllerValue) GetMember(interpreter *Interpreter, locationR } func (v CapabilityControllerValue) RemoveMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { - // Capabilities have no removable members (fields / functions) + // Capability controllers have no removable members (fields / functions) panic(errors.NewUnreachableError()) } func (v CapabilityControllerValue) SetMember(interpreter *Interpreter, locationRange LocationRange, name string, value Value) { - // Capabilities have no settable members (fields / functions) + // Capability controllers have no settable members (fields / functions) panic(errors.NewUnreachableError()) } From 757e255e178028f46c97b0b0f0688a812cd79803 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 7 Feb 2023 16:48:45 -0800 Subject: [PATCH 024/246] add TargetPath field and stubs --- runtime/common/metering.go | 2 +- runtime/format/capability.go | 5 +++-- runtime/interpreter/value.go | 21 +++++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 173628ff4c..f6c890af0e 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -250,7 +250,7 @@ var ( PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) - CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: )")) + CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: , target: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 0a5b550a8c..2379703e1f 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -36,14 +36,15 @@ func StorageCapability(borrowType string, address string, path string) string { ) } -func CapabilityController(borrowType string) string { +func CapabilityController(borrowType string, targetPath string) string { var typeArgument string if borrowType != "" { typeArgument = fmt.Sprintf("<%s>", borrowType) } return fmt.Sprintf( - "CapabilityController(borrowType: %s)", + "CapabilityController(borrowType: %s, target: %s)", typeArgument, + targetPath, ) } diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 9a67099e68..7b734f7df7 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18841,6 +18841,7 @@ type CapabilityControllerValue struct { BorrowType StaticType CapabilityID uint64 IsRevoked bool + TargetPath PathValue } var _ Value = &CapabilityControllerValue{} @@ -18851,12 +18852,7 @@ func (v CapabilityControllerValue) Storable(_ atree.SlabStorage, _ atree.Address } func (v CapabilityControllerValue) String() string { - var borrowType string - if v.BorrowType != nil { - borrowType = v.BorrowType.String() - } - - return format.CapabilityController(borrowType) + return v.RecursiveString(SeenReferences{}) } func (v CapabilityControllerValue) IsValue() {} @@ -18885,8 +18881,13 @@ func (v CapabilityControllerValue) ConformsToStaticType(interpreter *Interpreter return true // TODO verify this, given that we store the borrowed type as a field. Capabilities themselves are invariant though } -func (v CapabilityControllerValue) RecursiveString(_ SeenReferences) string { - return v.String() +func (v CapabilityControllerValue) RecursiveString(seenReferences SeenReferences) string { + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.String() + } + + return format.CapabilityController(borrowType, v.TargetPath.RecursiveString(seenReferences)) } func (v CapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { @@ -18896,7 +18897,7 @@ func (v CapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, if v.BorrowType != nil { borrowType = v.BorrowType.MeteredString(memoryGauge) } - return format.CapabilityController(borrowType) + return format.CapabilityController(borrowType, v.TargetPath.MeteredString(memoryGauge, seenReferences)) } func (v CapabilityControllerValue) IsResourceKinded(_ *Interpreter) bool { @@ -18941,7 +18942,7 @@ func (v CapabilityControllerValue) GetMember(interpreter *Interpreter, locationR case sema.CapabilityControllerTypeIsRevokedFieldName: return AsBoolValue(v.IsRevoked) case sema.CapabilityControllerTypeTargetFunctionName: - panic("not implemented") + return v.TargetPath // TODO add indirection to actually track this path in case it's retargeted case sema.CapabilityControllerTypeRevokeFunctionName: panic("not implemented") case sema.CapabilityControllerTypeRetargetFunctionName: From 302eaa7c8e6028273535dbc18d369bde63ff0f75 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 9 Feb 2023 16:53:17 -0800 Subject: [PATCH 025/246] switch isRevoked field to be a getter function --- runtime/sema/capability_controller.cdc | 2 +- runtime/sema/capability_controller.gen.go | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/capability_controller.cdc index b743fc3b5f..301b004799 100644 --- a/runtime/sema/capability_controller.cdc +++ b/runtime/sema/capability_controller.cdc @@ -11,7 +11,7 @@ pub struct CapabilityController { pub let capabilityID: UInt64 /// Is the capability revoked. - pub let isRevoked: Bool + pub fun isRevoked(): Bool /// Returns the targeted storage path of the capability. pub fun target(): StoragePath {} diff --git a/runtime/sema/capability_controller.gen.go b/runtime/sema/capability_controller.gen.go index a611113fbc..3fe1caa41f 100644 --- a/runtime/sema/capability_controller.gen.go +++ b/runtime/sema/capability_controller.gen.go @@ -47,11 +47,15 @@ This is the UUID of the created capability. All copies of the same capability will have the same UUID ` -const CapabilityControllerTypeIsRevokedFieldName = "isRevoked" +const CapabilityControllerTypeIsRevokedFunctionName = "isRevoked" -var CapabilityControllerTypeIsRevokedFieldType = BoolType +var CapabilityControllerTypeIsRevokedFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), +} -const CapabilityControllerTypeIsRevokedFieldDocString = `Is the capability revoked. +const CapabilityControllerTypeIsRevokedFunctionDocString = `Is the capability revoked. ` const CapabilityControllerTypeTargetFunctionName = "target" @@ -156,19 +160,19 @@ var CapabilityControllerType = &SimpleType{ ) }, }, - CapabilityControllerTypeIsRevokedFieldName: { - Kind: common.DeclarationKindField, + CapabilityControllerTypeIsRevokedFunctionName: { + Kind: common.DeclarationKindFunction, Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { - return NewPublicConstantFieldMember( + return NewPublicFunctionMember( memoryGauge, t, identifier, - CapabilityControllerTypeIsRevokedFieldType, - CapabilityControllerTypeIsRevokedFieldDocString, + CapabilityControllerTypeIsRevokedFunctionType, + CapabilityControllerTypeIsRevokedFunctionDocString, ) }, }, From 848316d46f454b4290d7488991ce6ee6fee23207 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 9 Feb 2023 16:53:43 -0800 Subject: [PATCH 026/246] change formatting of capcon type --- runtime/common/metering.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/common/metering.go b/runtime/common/metering.go index f6c890af0e..a131e7e21d 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -250,7 +250,7 @@ var ( PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) - CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: , target: )")) + CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: , capabilityID: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) From 0f9248833029ec1bcad9d82c68c76323547540b9 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 9 Feb 2023 16:54:28 -0800 Subject: [PATCH 027/246] switch capcon value to be simplecomposite type, add it to checker types --- runtime/interpreter/capability_controller.go | 125 ++++++++++++++++++ runtime/interpreter/value.go | 126 ------------------- runtime/sema/type.go | 1 + 3 files changed, 126 insertions(+), 126 deletions(-) create mode 100644 runtime/interpreter/capability_controller.go diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go new file mode 100644 index 0000000000..2221698f37 --- /dev/null +++ b/runtime/interpreter/capability_controller.go @@ -0,0 +1,125 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/format" + "github.com/onflow/cadence/runtime/sema" +) + +type CapabilityControllerValue struct { + IssueHeight uint64 + BorrowType StaticType + CapabilityID uint64 + IsRevoked bool + TargetPath PathValue +} + +var capabilityControllerFieldNames = []string{ + sema.CapabilityControllerTypeIssueHeightFieldName, + sema.CapabilityControllerTypeBorrowTypeFieldName, + sema.CapabilityControllerTypeCapabilityIDFieldName, +} + +func NewCapabilityControllerValue( + gauge common.MemoryGauge, + issueHeight uint64, + capabilityID uint64, + targetPath PathValue, + borrowType StaticType, + isRevoked bool, + revoke func() error, + retarget func(newPath PathValue) error, +) Value { + staticType := CapabilityControllerStaticType{BorrowType: borrowType} + + fields := map[string]Value{ + sema.CapabilityControllerTypeIssueHeightFieldName: NewUInt64Value(gauge, func() uint64 { + return issueHeight + }), + sema.CapabilityControllerTypeBorrowTypeFieldName: NewTypeValue(gauge, borrowType), + sema.CapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { + return capabilityID + }), + } + + computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { + switch name { + case sema.CapabilityControllerTypeIsRevokedFunctionName: + return NewHostFunctionValue(inter, func(invocation Invocation) Value { + return AsBoolValue(isRevoked) + }, sema.CapabilityControllerTypeIsRevokedFunctionType) + + case sema.CapabilityControllerTypeTargetFunctionName: + return NewHostFunctionValue(gauge, func(invocation Invocation) Value { + return targetPath + }, sema.CapabilityControllerTypeTargetFunctionType) + + case sema.CapabilityControllerTypeRevokeFunctionName: + return NewHostFunctionValue(gauge, func(invocation Invocation) Value { + err := revoke() + if err != nil { + panic(err) + } + + isRevoked = true + return Void + }, sema.CapabilityControllerTypeRevokeFunctionType) + + case sema.CapabilityControllerTypeRetargetFunctionName: + return NewHostFunctionValue(gauge, func(invocation Invocation) Value { + newTarget, ok := invocation.Arguments[0].(PathValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + err := retarget(newTarget) + if !ok { + panic(err) + } + + targetPath = newTarget + return Void + }, sema.CapabilityControllerTypeRetargetFunctionType) + } + + return nil + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) + borrowTypeStr := borrowType.MeteredString(memoryGauge) + + idStr := fmt.Sprint(capabilityID) // probably better to take the log10(capabilityID) first + common.UseMemory(memoryGauge, common.NewStringMemoryUsage(len(idStr))) + + str = format.CapabilityController(borrowTypeStr, idStr) + } + + return str + } + + return NewSimpleCompositeValue(gauge, sema.CapabilityControllerType.ID(), staticType, capabilityControllerFieldNames, fields, computeField, nil, stringer) +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 7b734f7df7..b092e6bf05 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18835,129 +18835,3 @@ func (v AccountLinkValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) func (v AccountLinkValue) ChildStorables() []atree.Storable { return nil } - -type CapabilityControllerValue struct { - IssueHeight uint64 - BorrowType StaticType - CapabilityID uint64 - IsRevoked bool - TargetPath PathValue -} - -var _ Value = &CapabilityControllerValue{} -var _ MemberAccessibleValue = &CapabilityControllerValue{} - -func (v CapabilityControllerValue) Storable(_ atree.SlabStorage, _ atree.Address, _ uint64) (atree.Storable, error) { - return NonStorable{Value: v}, nil -} - -func (v CapabilityControllerValue) String() string { - return v.RecursiveString(SeenReferences{}) -} - -func (v CapabilityControllerValue) IsValue() {} - -func (v CapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { - visitor.VisitCapabilityControllerValue(interpreter, v) -} - -func (v CapabilityControllerValue) Walk(interpreter *Interpreter, walkChild func(Value)) { - // NO-OP -} - -func (v CapabilityControllerValue) StaticType(interpreter *Interpreter) StaticType { - return NewCapabilityControllerStaticType(interpreter, v.BorrowType) -} - -// ConformsToStaticType returns true if the value (i.e. its dynamic type) -// conforms to its own static type. -// Non-container values trivially always conform to their own static type. -// Container values conform to their own static type, -// and this function recursively checks conformance for nested values. -// If the container contains static type information about nested values, -// e.g. the element type of an array, it also ensures the nested values' -// static types are subtypes. -func (v CapabilityControllerValue) ConformsToStaticType(interpreter *Interpreter, locationRange LocationRange, results TypeConformanceResults) bool { - return true // TODO verify this, given that we store the borrowed type as a field. Capabilities themselves are invariant though -} - -func (v CapabilityControllerValue) RecursiveString(seenReferences SeenReferences) string { - var borrowType string - if v.BorrowType != nil { - borrowType = v.BorrowType.String() - } - - return format.CapabilityController(borrowType, v.TargetPath.RecursiveString(seenReferences)) -} - -func (v CapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { - common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) - - var borrowType string - if v.BorrowType != nil { - borrowType = v.BorrowType.MeteredString(memoryGauge) - } - return format.CapabilityController(borrowType, v.TargetPath.MeteredString(memoryGauge, seenReferences)) -} - -func (v CapabilityControllerValue) IsResourceKinded(_ *Interpreter) bool { - return false -} - -func (v CapabilityControllerValue) NeedsStoreTo(_ atree.Address) bool { - return false -} - -func (v CapabilityControllerValue) Transfer(interpreter *Interpreter, locationRange LocationRange, address atree.Address, remove bool, storable atree.Storable) Value { - if remove { - interpreter.RemoveReferencedSlab(storable) - } - return v -} - -func (v CapabilityControllerValue) DeepRemove(interpreter *Interpreter) { - // NO-OP -} - -// Clone returns a new value that is equal to this value. -// NOTE: not used by interpreter, but used externally (e.g. state migration) -// NOTE: memory metering is unnecessary for Clone methods -func (v CapabilityControllerValue) Clone(interpreter *Interpreter) Value { - // TODO ask about this, since we expose a block height when this was issued. state migrations might affect this - return v -} - -func (CapabilityControllerValue) IsImportable(interpreter *Interpreter) bool { - return sema.CapabilityControllerType.Importable -} - -func (v CapabilityControllerValue) GetMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { - switch name { - case sema.CapabilityControllerTypeIssueHeightFieldName: - return NewUInt64Value(interpreter, func() uint64 { return v.IssueHeight }) - case sema.CapabilityControllerTypeBorrowTypeFieldName: - return NewTypeValue(interpreter, v.BorrowType) - case sema.CapabilityControllerTypeCapabilityIDFieldName: - return NewUInt64Value(interpreter, func() uint64 { return v.CapabilityID }) - case sema.CapabilityControllerTypeIsRevokedFieldName: - return AsBoolValue(v.IsRevoked) - case sema.CapabilityControllerTypeTargetFunctionName: - return v.TargetPath // TODO add indirection to actually track this path in case it's retargeted - case sema.CapabilityControllerTypeRevokeFunctionName: - panic("not implemented") - case sema.CapabilityControllerTypeRetargetFunctionName: - panic("not implemented") - } - - return nil -} - -func (v CapabilityControllerValue) RemoveMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { - // Capability controllers have no removable members (fields / functions) - panic(errors.NewUnreachableError()) -} - -func (v CapabilityControllerValue) SetMember(interpreter *Interpreter, locationRange LocationRange, name string, value Value) { - // Capability controllers have no settable members (fields / functions) - panic(errors.NewUnreachableError()) -} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 1fcab3cb48..9076066e1f 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3058,6 +3058,7 @@ func init() { PublicKeyType, SignatureAlgorithmType, HashAlgorithmType, + CapabilityControllerType, ) for _, ty := range types { From 1f2825da98f3bc7396db47b5e188a5a05f06eb32 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 9 Feb 2023 16:57:58 -0800 Subject: [PATCH 028/246] start writing capcon tests --- .../interpreter/capability_controller_test.go | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 runtime/interpreter/capability_controller_test.go diff --git a/runtime/interpreter/capability_controller_test.go b/runtime/interpreter/capability_controller_test.go new file mode 100644 index 0000000000..e8075a8ae8 --- /dev/null +++ b/runtime/interpreter/capability_controller_test.go @@ -0,0 +1,26 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "testing" +) + +func TestCapabilityControllerValue(t *testing.T) { +} From f858a825f40f48de3c8ca69e54eae30ed664fa88 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Fri, 10 Feb 2023 14:03:06 -0800 Subject: [PATCH 029/246] add todo msg --- runtime/interpreter/capability_controller_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/interpreter/capability_controller_test.go b/runtime/interpreter/capability_controller_test.go index e8075a8ae8..5957651fe2 100644 --- a/runtime/interpreter/capability_controller_test.go +++ b/runtime/interpreter/capability_controller_test.go @@ -23,4 +23,5 @@ import ( ) func TestCapabilityControllerValue(t *testing.T) { + // TODO add tests when capcons are integrated with accounts } From 7bfb2f30614b12e44fd8814eba3b74913f0b8614 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Fri, 10 Feb 2023 14:21:31 -0800 Subject: [PATCH 030/246] add capcontypetag back to anystruct type tag --- runtime/sema/type_tags.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 5570bd0d5e..a1a9337647 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -357,7 +357,8 @@ var ( Or(BlockTypeTag). Or(DeployedContractTypeTag). Or(CapabilityTypeTag). - Or(FunctionTypeTag) + Or(FunctionTypeTag). + Or(CapabilityControllerTypeTag) AnyResourceTypeTag = newTypeTagFromLowerMask(anyResourceTypeMask). Or(AnyResourceAttachmentTypeTag) From 2388e301ff2b62d017690d7b295be89583142752 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Mon, 13 Feb 2023 14:59:35 -0800 Subject: [PATCH 031/246] remove capabilitycontrollervalue --- runtime/interpreter/capability_controller.go | 8 -- runtime/interpreter/visitor.go | 95 +++++++++----------- 2 files changed, 43 insertions(+), 60 deletions(-) diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index 2221698f37..dcd7d6c713 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -27,14 +27,6 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -type CapabilityControllerValue struct { - IssueHeight uint64 - BorrowType StaticType - CapabilityID uint64 - IsRevoked bool - TargetPath PathValue -} - var capabilityControllerFieldNames = []string{ sema.CapabilityControllerTypeIssueHeightFieldName, sema.CapabilityControllerTypeBorrowTypeFieldName, diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index bf0e2152d9..754b766d6f 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -62,54 +62,52 @@ type Visitor interface { VisitInterpretedFunctionValue(interpreter *Interpreter, value *InterpretedFunctionValue) VisitHostFunctionValue(interpreter *Interpreter, value *HostFunctionValue) VisitBoundFunctionValue(interpreter *Interpreter, value BoundFunctionValue) - VisitCapabilityControllerValue(interpreter *Interpreter, value CapabilityControllerValue) } type EmptyVisitor struct { - SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) - TypeValueVisitor func(interpreter *Interpreter, value TypeValue) - VoidValueVisitor func(interpreter *Interpreter, value VoidValue) - BoolValueVisitor func(interpreter *Interpreter, value BoolValue) - CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) - StringValueVisitor func(interpreter *Interpreter, value *StringValue) - ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool - IntValueVisitor func(interpreter *Interpreter, value IntValue) - Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) - Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) - Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) - Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) - Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) - Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) - UIntValueVisitor func(interpreter *Interpreter, value UIntValue) - UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) - UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) - UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) - UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) - UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) - UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) - Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) - Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) - Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) - Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) - Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) - UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) - CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool - DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool - NilValueVisitor func(interpreter *Interpreter, value NilValue) - SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool - StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) - AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) - EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) - AddressValueVisitor func(interpreter *Interpreter, value AddressValue) - PathValueVisitor func(interpreter *Interpreter, value PathValue) - StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) - PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) - AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) - PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) - InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) - HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) - BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) - CapabilityControllerValueVisitor func(interpreter *Interpreter, value CapabilityControllerValue) + SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) + TypeValueVisitor func(interpreter *Interpreter, value TypeValue) + VoidValueVisitor func(interpreter *Interpreter, value VoidValue) + BoolValueVisitor func(interpreter *Interpreter, value BoolValue) + CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) + StringValueVisitor func(interpreter *Interpreter, value *StringValue) + ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool + IntValueVisitor func(interpreter *Interpreter, value IntValue) + Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) + Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) + Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) + Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) + Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) + Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) + UIntValueVisitor func(interpreter *Interpreter, value UIntValue) + UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) + UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) + UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) + UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) + UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) + UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) + Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) + Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) + Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) + Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) + Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) + UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) + CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool + DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool + NilValueVisitor func(interpreter *Interpreter, value NilValue) + SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool + StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) + AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) + EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) + AddressValueVisitor func(interpreter *Interpreter, value AddressValue) + PathValueVisitor func(interpreter *Interpreter, value PathValue) + StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) + PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) + AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) + PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) + InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) + HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) + BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) } var _ Visitor = &EmptyVisitor{} @@ -414,10 +412,3 @@ func (v EmptyVisitor) VisitBoundFunctionValue(interpreter *Interpreter, value Bo } v.BoundFunctionValueVisitor(interpreter, value) } - -func (v EmptyVisitor) VisitCapabilityControllerValue(interpreter *Interpreter, value CapabilityControllerValue) { - if v.CapabilityControllerValueVisitor == nil { - return - } - v.CapabilityControllerValueVisitor(interpreter, value) -} From f47ebd49f6d7bf491be0e87d9cb5bba13b8cbb3b Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Mon, 13 Feb 2023 15:37:21 -0800 Subject: [PATCH 032/246] use primitivestatictype for capcon type --- runtime/common/metering.go | 38 +++--- runtime/interpreter/primitivestatictype.go | 1 + .../interpreter/primitivestatictype_string.go | 114 +++++++++--------- runtime/interpreter/statictype.go | 36 ------ 4 files changed, 77 insertions(+), 112 deletions(-) diff --git a/runtime/common/metering.go b/runtime/common/metering.go index a131e7e21d..281283dfae 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -156,29 +156,27 @@ var ( // Static Types - PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) - CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) - InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) - VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) - ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) - DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) - OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) - RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) - ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) - CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) - FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) - CapabilityControllerStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityControllerStaticType) + PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) + CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) + InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) + VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) + ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) + DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) + OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) + RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) + ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) + CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) + FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) // Sema types - VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) - ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) - DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) - OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) - RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) - ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) - CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) - CapabilityControllerSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityControllerSemaType) + VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) + ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) + DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) + OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) + RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) + ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) + CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) // Storage related memory usages diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index 9fb8f5a43e..2d265f60b5 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -200,6 +200,7 @@ const ( PrimitiveStaticTypePublicAccountKeys PrimitiveStaticTypeAccountKey PrimitiveStaticTypeAuthAccountInbox + PrimitiveStaticTypeCapabilityController // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. diff --git a/runtime/interpreter/primitivestatictype_string.go b/runtime/interpreter/primitivestatictype_string.go index 76dee30f5c..66b4106ebd 100644 --- a/runtime/interpreter/primitivestatictype_string.go +++ b/runtime/interpreter/primitivestatictype_string.go @@ -61,66 +61,68 @@ func _() { _ = x[PrimitiveStaticTypePublicAccountKeys-96] _ = x[PrimitiveStaticTypeAccountKey-97] _ = x[PrimitiveStaticTypeAuthAccountInbox-98] - _ = x[PrimitiveStaticType_Count-99] + _ = x[PrimitiveStaticTypeCapabilityController-99] + _ = x[PrimitiveStaticType_Count-100] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInbox_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxCapabilityController_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ - 0: _PrimitiveStaticType_name[0:7], - 1: _PrimitiveStaticType_name[7:11], - 2: _PrimitiveStaticType_name[11:14], - 3: _PrimitiveStaticType_name[14:19], - 4: _PrimitiveStaticType_name[19:28], - 5: _PrimitiveStaticType_name[28:39], - 6: _PrimitiveStaticType_name[39:43], - 7: _PrimitiveStaticType_name[43:50], - 8: _PrimitiveStaticType_name[50:56], - 9: _PrimitiveStaticType_name[56:65], - 10: _PrimitiveStaticType_name[65:73], - 11: _PrimitiveStaticType_name[73:78], - 18: _PrimitiveStaticType_name[78:84], - 19: _PrimitiveStaticType_name[84:96], - 24: _PrimitiveStaticType_name[96:103], - 25: _PrimitiveStaticType_name[103:116], - 30: _PrimitiveStaticType_name[116:126], - 31: _PrimitiveStaticType_name[126:142], - 36: _PrimitiveStaticType_name[142:145], - 37: _PrimitiveStaticType_name[145:149], - 38: _PrimitiveStaticType_name[149:154], - 39: _PrimitiveStaticType_name[154:159], - 40: _PrimitiveStaticType_name[159:164], - 41: _PrimitiveStaticType_name[164:170], - 42: _PrimitiveStaticType_name[170:176], - 44: _PrimitiveStaticType_name[176:180], - 45: _PrimitiveStaticType_name[180:185], - 46: _PrimitiveStaticType_name[185:191], - 47: _PrimitiveStaticType_name[191:197], - 48: _PrimitiveStaticType_name[197:203], - 49: _PrimitiveStaticType_name[203:210], - 50: _PrimitiveStaticType_name[210:217], - 53: _PrimitiveStaticType_name[217:222], - 54: _PrimitiveStaticType_name[222:228], - 55: _PrimitiveStaticType_name[228:234], - 56: _PrimitiveStaticType_name[234:240], - 64: _PrimitiveStaticType_name[240:245], - 72: _PrimitiveStaticType_name[245:251], - 76: _PrimitiveStaticType_name[251:255], - 77: _PrimitiveStaticType_name[255:265], - 78: _PrimitiveStaticType_name[265:276], - 79: _PrimitiveStaticType_name[276:290], - 80: _PrimitiveStaticType_name[290:300], - 81: _PrimitiveStaticType_name[300:311], - 90: _PrimitiveStaticType_name[311:322], - 91: _PrimitiveStaticType_name[322:335], - 92: _PrimitiveStaticType_name[335:351], - 93: _PrimitiveStaticType_name[351:371], - 94: _PrimitiveStaticType_name[371:393], - 95: _PrimitiveStaticType_name[393:408], - 96: _PrimitiveStaticType_name[408:425], - 97: _PrimitiveStaticType_name[425:435], - 98: _PrimitiveStaticType_name[435:451], - 99: _PrimitiveStaticType_name[451:457], + 0: _PrimitiveStaticType_name[0:7], + 1: _PrimitiveStaticType_name[7:11], + 2: _PrimitiveStaticType_name[11:14], + 3: _PrimitiveStaticType_name[14:19], + 4: _PrimitiveStaticType_name[19:28], + 5: _PrimitiveStaticType_name[28:39], + 6: _PrimitiveStaticType_name[39:43], + 7: _PrimitiveStaticType_name[43:50], + 8: _PrimitiveStaticType_name[50:56], + 9: _PrimitiveStaticType_name[56:65], + 10: _PrimitiveStaticType_name[65:73], + 11: _PrimitiveStaticType_name[73:78], + 18: _PrimitiveStaticType_name[78:84], + 19: _PrimitiveStaticType_name[84:96], + 24: _PrimitiveStaticType_name[96:103], + 25: _PrimitiveStaticType_name[103:116], + 30: _PrimitiveStaticType_name[116:126], + 31: _PrimitiveStaticType_name[126:142], + 36: _PrimitiveStaticType_name[142:145], + 37: _PrimitiveStaticType_name[145:149], + 38: _PrimitiveStaticType_name[149:154], + 39: _PrimitiveStaticType_name[154:159], + 40: _PrimitiveStaticType_name[159:164], + 41: _PrimitiveStaticType_name[164:170], + 42: _PrimitiveStaticType_name[170:176], + 44: _PrimitiveStaticType_name[176:180], + 45: _PrimitiveStaticType_name[180:185], + 46: _PrimitiveStaticType_name[185:191], + 47: _PrimitiveStaticType_name[191:197], + 48: _PrimitiveStaticType_name[197:203], + 49: _PrimitiveStaticType_name[203:210], + 50: _PrimitiveStaticType_name[210:217], + 53: _PrimitiveStaticType_name[217:222], + 54: _PrimitiveStaticType_name[222:228], + 55: _PrimitiveStaticType_name[228:234], + 56: _PrimitiveStaticType_name[234:240], + 64: _PrimitiveStaticType_name[240:245], + 72: _PrimitiveStaticType_name[245:251], + 76: _PrimitiveStaticType_name[251:255], + 77: _PrimitiveStaticType_name[255:265], + 78: _PrimitiveStaticType_name[265:276], + 79: _PrimitiveStaticType_name[276:290], + 80: _PrimitiveStaticType_name[290:300], + 81: _PrimitiveStaticType_name[300:311], + 90: _PrimitiveStaticType_name[311:322], + 91: _PrimitiveStaticType_name[322:335], + 92: _PrimitiveStaticType_name[335:351], + 93: _PrimitiveStaticType_name[351:371], + 94: _PrimitiveStaticType_name[371:393], + 95: _PrimitiveStaticType_name[393:408], + 96: _PrimitiveStaticType_name[408:425], + 97: _PrimitiveStaticType_name[425:435], + 98: _PrimitiveStaticType_name[435:451], + 99: _PrimitiveStaticType_name[451:471], + 100: _PrimitiveStaticType_name[471:477], } func (i PrimitiveStaticType) String() string { diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index 18fa3cd1d3..434db60151 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -945,39 +945,3 @@ func (p TypeParameter) String() string { } return builder.String() } - -type CapabilityControllerStaticType struct { - BorrowType StaticType -} - -func NewCapabilityControllerStaticType(gauge common.MemoryGauge, borrowType StaticType) CapabilityControllerStaticType { - common.UseMemory(gauge, common.CapabilityControllerStaticTypeMemoryUsage) - - return CapabilityControllerStaticType{ - BorrowType: borrowType, - } -} - -var _ StaticType = &CapabilityControllerStaticType{} - -func (t CapabilityControllerStaticType) String() string { - panic("not implemented") // TODO: Implement -} - -func (t CapabilityControllerStaticType) isStaticType() {} - -/* - this returns the size (in bytes) of the largest inhabitant of this type, - or UnknownElementSize if the largest inhabitant has arbitrary size -*/ -func (t CapabilityControllerStaticType) elementSize() uint { - panic("not implemented") // TODO: Implement -} - -func (t CapabilityControllerStaticType) Equal(other StaticType) bool { - panic("not implemented") // TODO: Implement -} - -func (t CapabilityControllerStaticType) MeteredString(memoryGauge common.MemoryGauge) string { - panic("not implemented") // TODO: Implement -} From 6f7dac994d23d3e883f190b50d2362cc7cf7a0b2 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Mon, 13 Feb 2023 15:54:48 -0800 Subject: [PATCH 033/246] remove capabilitycontrollerstatictype encode impl, switch back to old cadence definition --- runtime/interpreter/capability_controller.go | 18 ++++++++++++++---- runtime/interpreter/encode.go | 7 ------- runtime/sema/capability_controller.cdc | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index dcd7d6c713..2896e10a1d 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -43,7 +43,6 @@ func NewCapabilityControllerValue( revoke func() error, retarget func(newPath PathValue) error, ) Value { - staticType := CapabilityControllerStaticType{BorrowType: borrowType} fields := map[string]Value{ sema.CapabilityControllerTypeIssueHeightFieldName: NewUInt64Value(gauge, func() uint64 { @@ -104,8 +103,10 @@ func NewCapabilityControllerValue( common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) borrowTypeStr := borrowType.MeteredString(memoryGauge) - idStr := fmt.Sprint(capabilityID) // probably better to take the log10(capabilityID) first - common.UseMemory(memoryGauge, common.NewStringMemoryUsage(len(idStr))) + memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) + common.UseMemory(memoryGauge, memoryUsage) + + idStr := fmt.Sprint(capabilityID) str = format.CapabilityController(borrowTypeStr, idStr) } @@ -113,5 +114,14 @@ func NewCapabilityControllerValue( return str } - return NewSimpleCompositeValue(gauge, sema.CapabilityControllerType.ID(), staticType, capabilityControllerFieldNames, fields, computeField, nil, stringer) + return NewSimpleCompositeValue( + gauge, + sema.CapabilityControllerType.ID(), + PrimitiveStaticTypeCapabilityController, + capabilityControllerFieldNames, + fields, + computeField, + nil, + stringer, + ) } diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index f437932a11..a6f3a23509 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -28,7 +28,6 @@ import ( "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/sema" ) const cborTagSize = 2 @@ -1386,12 +1385,6 @@ func (t FunctionStaticType) Encode(_ *cbor.StreamEncoder) error { } } -func (t CapabilityControllerStaticType) Encode(e *cbor.StreamEncoder) error { - return NonStorableStaticTypeError{ - Type: sema.CapabilityControllerType, - } -} - // compositeTypeInfo type compositeTypeInfo struct { location common.Location diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/capability_controller.cdc index 301b004799..3ff81c70cc 100644 --- a/runtime/sema/capability_controller.cdc +++ b/runtime/sema/capability_controller.cdc @@ -11,7 +11,7 @@ pub struct CapabilityController { pub let capabilityID: UInt64 /// Is the capability revoked. - pub fun isRevoked(): Bool + pub fun isRevoked(): Bool {} /// Returns the targeted storage path of the capability. pub fun target(): StoragePath {} From 8f56cc327242d3db197760793a717f3e16c30473 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Tue, 14 Feb 2023 12:06:43 -0800 Subject: [PATCH 034/246] remove empty function blocks after merging #2302 --- runtime/sema/capability_controller.cdc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/capability_controller.cdc index 3ff81c70cc..b7aa4230dc 100644 --- a/runtime/sema/capability_controller.cdc +++ b/runtime/sema/capability_controller.cdc @@ -11,16 +11,16 @@ pub struct CapabilityController { pub let capabilityID: UInt64 /// Is the capability revoked. - pub fun isRevoked(): Bool {} + pub fun isRevoked(): Bool /// Returns the targeted storage path of the capability. - pub fun target(): StoragePath {} + pub fun target(): StoragePath /// Revoke the capability making it no longer usable. /// When borrowing from a revoked capability the borrow returns nil. - pub fun revoke() {} + pub fun revoke() /// Retarget the capability. /// This moves the CapCon from one CapCon array to another. - pub fun retarget(target: StoragePath) {} + pub fun retarget(target: StoragePath) } \ No newline at end of file From 9cabe640f2e608395e6580aa38c17484b9b7f1d4 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Wed, 15 Feb 2023 14:35:01 -0800 Subject: [PATCH 035/246] fix capcon string formatting --- runtime/format/capability.go | 13 ++++--------- runtime/interpreter/capability_controller.go | 7 +++++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 2379703e1f..80697c3901 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -36,15 +36,10 @@ func StorageCapability(borrowType string, address string, path string) string { ) } -func CapabilityController(borrowType string, targetPath string) string { - var typeArgument string - if borrowType != "" { - typeArgument = fmt.Sprintf("<%s>", borrowType) - } - +func CapabilityController(borrowType string, capabilityID string) string { return fmt.Sprintf( - "CapabilityController(borrowType: %s, target: %s)", - typeArgument, - targetPath, + "CapabilityController(borrowType: %s, capabilityID: %s)", + borrowType, + capabilityID, ) } diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index 2896e10a1d..6220072f46 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -44,11 +44,12 @@ func NewCapabilityControllerValue( retarget func(newPath PathValue) error, ) Value { + borrowTypeValue := NewTypeValue(gauge, borrowType) fields := map[string]Value{ sema.CapabilityControllerTypeIssueHeightFieldName: NewUInt64Value(gauge, func() uint64 { return issueHeight }), - sema.CapabilityControllerTypeBorrowTypeFieldName: NewTypeValue(gauge, borrowType), + sema.CapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, sema.CapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { return capabilityID }), @@ -101,7 +102,9 @@ func NewCapabilityControllerValue( stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { if str == "" { common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) - borrowTypeStr := borrowType.MeteredString(memoryGauge) + + // "Type()" + borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) common.UseMemory(memoryGauge, memoryUsage) From 4dbd772667b96ea37f2f4cdf5fcc3ff5b96843ff Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Wed, 15 Feb 2023 14:36:40 -0800 Subject: [PATCH 036/246] add changes to capcon FLIP, regenerate capcon definitions --- runtime/sema/capability_controller.cdc | 15 ++++++++++++--- runtime/sema/capability_controller.gen.go | 22 +++++++++++++++------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/capability_controller.cdc index b7aa4230dc..094ddf34c2 100644 --- a/runtime/sema/capability_controller.cdc +++ b/runtime/sema/capability_controller.cdc @@ -16,9 +16,18 @@ pub struct CapabilityController { /// Returns the targeted storage path of the capability. pub fun target(): StoragePath - /// Revoke the capability making it no longer usable. - /// When borrowing from a revoked capability the borrow returns nil. - pub fun revoke() + /// Delete this capability controller, + /// and disable the controlled capability and its copies. + /// + /// The controller will be deleted from storage, + /// but the controlled capability and its copies remain. + /// + /// Once this function returns, the controller is no longer usable, + /// all further operations on the controller will panic. + /// + /// Borrowing from the controlled capability or its copies will return nil. + /// + pub fun delete() /// Retarget the capability. /// This moves the CapCon from one CapCon array to another. diff --git a/runtime/sema/capability_controller.gen.go b/runtime/sema/capability_controller.gen.go index 3fe1caa41f..9286724b7c 100644 --- a/runtime/sema/capability_controller.gen.go +++ b/runtime/sema/capability_controller.gen.go @@ -69,16 +69,24 @@ var CapabilityControllerTypeTargetFunctionType = &FunctionType{ const CapabilityControllerTypeTargetFunctionDocString = `Returns the targeted storage path of the capability. ` -const CapabilityControllerTypeRevokeFunctionName = "revoke" +const CapabilityControllerTypeDeleteFunctionName = "delete" -var CapabilityControllerTypeRevokeFunctionType = &FunctionType{ +var CapabilityControllerTypeDeleteFunctionType = &FunctionType{ ReturnTypeAnnotation: NewTypeAnnotation( VoidType, ), } -const CapabilityControllerTypeRevokeFunctionDocString = `Revoke the capability making it no longer usable. -When borrowing from a revoked capability the borrow returns nil. +const CapabilityControllerTypeDeleteFunctionDocString = `Delete this capability controller, +and disable the controlled capability and its copies. + +The controller will be deleted from storage, +but the controlled capability and its copies remain. + +Once this function returns, the controller is no longer usable, +all further operations on the controller will panic. + +Borrowing from the controlled capability or its copies will return nil. ` const CapabilityControllerTypeRetargetFunctionName = "retarget" @@ -192,7 +200,7 @@ var CapabilityControllerType = &SimpleType{ ) }, }, - CapabilityControllerTypeRevokeFunctionName: { + CapabilityControllerTypeDeleteFunctionName: { Kind: common.DeclarationKindFunction, Resolve: func(memoryGauge common.MemoryGauge, identifier string, @@ -203,8 +211,8 @@ var CapabilityControllerType = &SimpleType{ memoryGauge, t, identifier, - CapabilityControllerTypeRevokeFunctionType, - CapabilityControllerTypeRevokeFunctionDocString, + CapabilityControllerTypeDeleteFunctionType, + CapabilityControllerTypeDeleteFunctionDocString, ) }, }, From ad0730553dde0c198db8f17fc49bd506270d8000 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 16 Feb 2023 15:21:53 -0800 Subject: [PATCH 037/246] remove references to revoke() fn --- runtime/interpreter/capability_controller.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index 6220072f46..bbce684f56 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -39,8 +39,7 @@ func NewCapabilityControllerValue( capabilityID uint64, targetPath PathValue, borrowType StaticType, - isRevoked bool, - revoke func() error, + delete func() error, retarget func(newPath PathValue) error, ) Value { @@ -57,26 +56,20 @@ func NewCapabilityControllerValue( computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { switch name { - case sema.CapabilityControllerTypeIsRevokedFunctionName: - return NewHostFunctionValue(inter, func(invocation Invocation) Value { - return AsBoolValue(isRevoked) - }, sema.CapabilityControllerTypeIsRevokedFunctionType) - case sema.CapabilityControllerTypeTargetFunctionName: return NewHostFunctionValue(gauge, func(invocation Invocation) Value { return targetPath }, sema.CapabilityControllerTypeTargetFunctionType) - case sema.CapabilityControllerTypeRevokeFunctionName: + case sema.CapabilityControllerTypeDeleteFunctionName: return NewHostFunctionValue(gauge, func(invocation Invocation) Value { - err := revoke() + err := delete() if err != nil { panic(err) } - isRevoked = true return Void - }, sema.CapabilityControllerTypeRevokeFunctionType) + }, sema.CapabilityControllerTypeDeleteFunctionType) case sema.CapabilityControllerTypeRetargetFunctionName: return NewHostFunctionValue(gauge, func(invocation Invocation) Value { From a71980b0c921d23a00c8f991facab8888d47fe7d Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 16 Feb 2023 15:25:47 -0800 Subject: [PATCH 038/246] handle capabilitycontroller primitive static type in other methods --- runtime/interpreter/primitivestatictype.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index 2d265f60b5..fa7a8270c5 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -281,7 +281,8 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAuthAccountInbox, PrimitiveStaticTypeAuthAccountKeys, PrimitiveStaticTypePublicAccountKeys, - PrimitiveStaticTypeAccountKey: + PrimitiveStaticTypeAccountKey, + PrimitiveStaticTypeCapabilityController: return UnknownElementSize } return UnknownElementSize @@ -424,6 +425,8 @@ func (i PrimitiveStaticType) SemaType() sema.Type { return sema.AccountKeyType case PrimitiveStaticTypeAuthAccountInbox: return sema.AuthAccountInboxType + case PrimitiveStaticTypeCapabilityController: + return sema.CapabilityControllerType default: panic(errors.NewUnexpectedError("missing case for %s", i)) } @@ -556,6 +559,8 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeAccountKey case sema.AuthAccountInboxType: typ = PrimitiveStaticTypeAuthAccountInbox + case sema.CapabilityControllerType: + typ = PrimitiveStaticTypeCapabilityController } switch t.(type) { From be10557966f5e6b8ee4fdf074e90b8fb259bc890 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 16 Feb 2023 15:38:00 -0800 Subject: [PATCH 039/246] fix statictype_test to reflect newly appended statictype --- runtime/interpreter/statictype_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index e4f1acecf3..961fe46d2c 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -969,6 +969,6 @@ func TestPrimitiveStaticTypeCount(t *testing.T) { // (before the PrimitiveStaticType_Count of course). // Only update this test if you are certain your change to this enum was to append new types to the end. t.Run("No new types added in between", func(t *testing.T) { - require.Equal(t, byte(99), byte(PrimitiveStaticType_Count)) + require.Equal(t, byte(100), byte(PrimitiveStaticType_Count)) }) } From 2f28be9b2d35724a090d6d4472ea0081f52a9550 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Thu, 16 Feb 2023 15:56:50 -0800 Subject: [PATCH 040/246] address review comments --- runtime/interpreter/capability_controller.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index bbce684f56..992d2574d6 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -37,9 +37,9 @@ func NewCapabilityControllerValue( gauge common.MemoryGauge, issueHeight uint64, capabilityID uint64, - targetPath PathValue, borrowType StaticType, delete func() error, + getTarget func() (PathValue, error), retarget func(newPath PathValue) error, ) Value { @@ -58,7 +58,12 @@ func NewCapabilityControllerValue( switch name { case sema.CapabilityControllerTypeTargetFunctionName: return NewHostFunctionValue(gauge, func(invocation Invocation) Value { - return targetPath + target, err := getTarget() + if err != nil { + panic(err) + } + + return target }, sema.CapabilityControllerTypeTargetFunctionType) case sema.CapabilityControllerTypeDeleteFunctionName: @@ -83,7 +88,6 @@ func NewCapabilityControllerValue( panic(err) } - targetPath = newTarget return Void }, sema.CapabilityControllerTypeRetargetFunctionType) } @@ -96,7 +100,6 @@ func NewCapabilityControllerValue( if str == "" { common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) - // "Type()" borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) From f46e0d82f64d980789d0b8980b277ef52675391e Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Fri, 17 Feb 2023 14:50:21 -0800 Subject: [PATCH 041/246] add simple checker tests for capcons. will add more once account capabilities pr is in --- .../checker/capability_controller_test.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 runtime/tests/checker/capability_controller_test.go diff --git a/runtime/tests/checker/capability_controller_test.go b/runtime/tests/checker/capability_controller_test.go new file mode 100644 index 0000000000..47caa73699 --- /dev/null +++ b/runtime/tests/checker/capability_controller_test.go @@ -0,0 +1,63 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package checker + +import ( + "testing" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" + "github.com/stretchr/testify/require" +) + +func ParseAndCheckCapcon(t *testing.T, code string) (*sema.Checker, error) { + baseActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseActivation.DeclareValue(stdlib.StandardLibraryValue{ + Name: "controller", + Type: sema.CapabilityControllerType, + Kind: common.DeclarationKindConstant, + }) + + return ParseAndCheckWithOptions( + t, + code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseActivation, + }, + }, + ) +} + +func TestCapconNonEquatable(t *testing.T) { + _, err := ParseAndCheckCapcon(t, ` + let kaboom: Bool = controller == controller + `) + + errs := RequireCheckerErrors(t, err, 1) + require.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) +} + +func TestCapconTypeInScope(t *testing.T) { + _, err := ParseAndCheckCapcon(t, ` + let typ = Type() + `) + require.NoError(t, err) +} From 455806f81da865e6dcb0fbd4588453c1b364a150 Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Fri, 17 Feb 2023 14:54:10 -0800 Subject: [PATCH 042/246] move invalidTypeMask back to the highest bit --- runtime/sema/type_tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index a1a9337647..523d75546c 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -218,9 +218,9 @@ const ( transactionTypeMask anyResourceAttachmentMask anyStructAttachmentMask + capabilityControllerTypeMask invalidTypeMask - capabilityControllerTypeMask ) var ( From 0855da4cb7107419c092c96cec15416d6f66c1eb Mon Sep 17 00:00:00 2001 From: Naomi Liu Date: Fri, 17 Feb 2023 14:59:35 -0800 Subject: [PATCH 043/246] swap hostfunctionvalue arguments after merging --- runtime/interpreter/capability_controller.go | 70 ++++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/capability_controller.go index 992d2574d6..81eea65070 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/capability_controller.go @@ -57,39 +57,51 @@ func NewCapabilityControllerValue( computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { switch name { case sema.CapabilityControllerTypeTargetFunctionName: - return NewHostFunctionValue(gauge, func(invocation Invocation) Value { - target, err := getTarget() - if err != nil { - panic(err) - } - - return target - }, sema.CapabilityControllerTypeTargetFunctionType) + return NewHostFunctionValue( + gauge, + sema.CapabilityControllerTypeTargetFunctionType, + func(invocation Invocation) Value { + target, err := getTarget() + if err != nil { + panic(err) + } + + return target + }, + ) case sema.CapabilityControllerTypeDeleteFunctionName: - return NewHostFunctionValue(gauge, func(invocation Invocation) Value { - err := delete() - if err != nil { - panic(err) - } - - return Void - }, sema.CapabilityControllerTypeDeleteFunctionType) + return NewHostFunctionValue( + gauge, + sema.CapabilityControllerTypeDeleteFunctionType, + func(invocation Invocation) Value { + err := delete() + if err != nil { + panic(err) + } + + return Void + }, + ) case sema.CapabilityControllerTypeRetargetFunctionName: - return NewHostFunctionValue(gauge, func(invocation Invocation) Value { - newTarget, ok := invocation.Arguments[0].(PathValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - err := retarget(newTarget) - if !ok { - panic(err) - } - - return Void - }, sema.CapabilityControllerTypeRetargetFunctionType) + return NewHostFunctionValue( + gauge, + sema.CapabilityControllerTypeRetargetFunctionType, + func(invocation Invocation) Value { + newTarget, ok := invocation.Arguments[0].(PathValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + err := retarget(newTarget) + if !ok { + panic(err) + } + + return Void + }, + ) } return nil From 9f244cc31cbd5646fcefb52c14494fa152a3808b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 23 Mar 2023 13:07:50 -0700 Subject: [PATCH 044/246] Apply suggestions from code review --- runtime/tests/checker/capability_controller_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/capability_controller_test.go b/runtime/tests/checker/capability_controller_test.go index 47caa73699..9d6c59867d 100644 --- a/runtime/tests/checker/capability_controller_test.go +++ b/runtime/tests/checker/capability_controller_test.go @@ -46,7 +46,9 @@ func ParseAndCheckCapcon(t *testing.T, code string) (*sema.Checker, error) { ) } -func TestCapconNonEquatable(t *testing.T) { +func TestCheckCapconNonEquatable(t *testing.T) { + t.Parallel() + _, err := ParseAndCheckCapcon(t, ` let kaboom: Bool = controller == controller `) @@ -55,7 +57,9 @@ func TestCapconNonEquatable(t *testing.T) { require.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) } -func TestCapconTypeInScope(t *testing.T) { +func TestCheckCapconTypeInScope(t *testing.T) { + t.Parallel() + _, err := ParseAndCheckCapcon(t, ` let typ = Type() `) From 4393153c383f4aa7daf9789d5f63638fd143beec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 23 Mar 2023 14:42:50 -0700 Subject: [PATCH 045/246] update and rename capability controller --- runtime/common/metering.go | 42 ++-- runtime/format/capability.go | 4 +- runtime/interpreter/primitivestatictype.go | 12 +- .../interpreter/primitivestatictype_string.go | 8 +- ...er.go => storage_capability_controller.go} | 39 ++- ... => storage_capability_controller_test.go} | 2 +- runtime/sema/capability_controller.gen.go | 237 ------------------ ....cdc => storage_capability_controller.cdc} | 27 +- ...er.go => storage_capability_controller.go} | 4 +- runtime/sema/type.go | 2 +- runtime/sema/type_tags.go | 22 +- .../checker/capability_controller_test.go | 47 ++-- 12 files changed, 107 insertions(+), 339 deletions(-) rename runtime/interpreter/{capability_controller.go => storage_capability_controller.go} (68%) rename runtime/interpreter/{capability_controller_test.go => storage_capability_controller_test.go} (93%) delete mode 100644 runtime/sema/capability_controller.gen.go rename runtime/sema/{capability_controller.cdc => storage_capability_controller.cdc} (59%) rename runtime/sema/{capability_controller.go => storage_capability_controller.go} (76%) diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 281283dfae..68f81ee1f9 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -230,27 +230,27 @@ var ( // Following are the known memory usage amounts for string representation of interpreter values. // Same as `len(format.X)`. However, values are hard-coded to avoid the circular dependency. - VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) - TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) - FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) - TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) - NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) - StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) - AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) - SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) - AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) - HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) - AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) - PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) - AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) - PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) - AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) - PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) - AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) - CapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("CapabilityController(borrowType: , capabilityID: )")) - PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) - PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) + VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) + TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) + FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) + TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) + NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) + StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) + AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) + SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) + AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) + HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) + AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) + PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) + AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) + PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) + AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) + PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) + AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) + StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) + StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) + PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) + PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) // Static types string representations diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 80697c3901..858e3bc8f1 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -36,9 +36,9 @@ func StorageCapability(borrowType string, address string, path string) string { ) } -func CapabilityController(borrowType string, capabilityID string) string { +func StorageCapabilityController(borrowType string, capabilityID string) string { return fmt.Sprintf( - "CapabilityController(borrowType: %s, capabilityID: %s)", + "StorageCapabilityController(borrowType: %s, capabilityID: %s)", borrowType, capabilityID, ) diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index fa7a8270c5..0ff3e507f8 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -200,7 +200,7 @@ const ( PrimitiveStaticTypePublicAccountKeys PrimitiveStaticTypeAccountKey PrimitiveStaticTypeAuthAccountInbox - PrimitiveStaticTypeCapabilityController + PrimitiveStaticTypeStorageCapabilityController // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -282,7 +282,7 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAuthAccountKeys, PrimitiveStaticTypePublicAccountKeys, PrimitiveStaticTypeAccountKey, - PrimitiveStaticTypeCapabilityController: + PrimitiveStaticTypeStorageCapabilityController: return UnknownElementSize } return UnknownElementSize @@ -425,8 +425,8 @@ func (i PrimitiveStaticType) SemaType() sema.Type { return sema.AccountKeyType case PrimitiveStaticTypeAuthAccountInbox: return sema.AuthAccountInboxType - case PrimitiveStaticTypeCapabilityController: - return sema.CapabilityControllerType + case PrimitiveStaticTypeStorageCapabilityController: + return sema.StorageCapabilityControllerType default: panic(errors.NewUnexpectedError("missing case for %s", i)) } @@ -559,8 +559,8 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeAccountKey case sema.AuthAccountInboxType: typ = PrimitiveStaticTypeAuthAccountInbox - case sema.CapabilityControllerType: - typ = PrimitiveStaticTypeCapabilityController + case sema.StorageCapabilityControllerType: + typ = PrimitiveStaticTypeStorageCapabilityController } switch t.(type) { diff --git a/runtime/interpreter/primitivestatictype_string.go b/runtime/interpreter/primitivestatictype_string.go index 66b4106ebd..84de818708 100644 --- a/runtime/interpreter/primitivestatictype_string.go +++ b/runtime/interpreter/primitivestatictype_string.go @@ -61,11 +61,11 @@ func _() { _ = x[PrimitiveStaticTypePublicAccountKeys-96] _ = x[PrimitiveStaticTypeAccountKey-97] _ = x[PrimitiveStaticTypeAuthAccountInbox-98] - _ = x[PrimitiveStaticTypeCapabilityController-99] + _ = x[PrimitiveStaticTypeStorageCapabilityController-99] _ = x[PrimitiveStaticType_Count-100] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxCapabilityController_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityController_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 0: _PrimitiveStaticType_name[0:7], @@ -121,8 +121,8 @@ var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 96: _PrimitiveStaticType_name[408:425], 97: _PrimitiveStaticType_name[425:435], 98: _PrimitiveStaticType_name[435:451], - 99: _PrimitiveStaticType_name[451:471], - 100: _PrimitiveStaticType_name[471:477], + 99: _PrimitiveStaticType_name[451:478], + 100: _PrimitiveStaticType_name[478:484], } func (i PrimitiveStaticType) String() string { diff --git a/runtime/interpreter/capability_controller.go b/runtime/interpreter/storage_capability_controller.go similarity index 68% rename from runtime/interpreter/capability_controller.go rename to runtime/interpreter/storage_capability_controller.go index 81eea65070..8b8474eb30 100644 --- a/runtime/interpreter/capability_controller.go +++ b/runtime/interpreter/storage_capability_controller.go @@ -27,15 +27,13 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -var capabilityControllerFieldNames = []string{ - sema.CapabilityControllerTypeIssueHeightFieldName, - sema.CapabilityControllerTypeBorrowTypeFieldName, - sema.CapabilityControllerTypeCapabilityIDFieldName, +var storageCapabilityControllerFieldNames = []string{ + sema.StorageCapabilityControllerTypeBorrowTypeFieldName, + sema.StorageCapabilityControllerTypeCapabilityIDFieldName, } -func NewCapabilityControllerValue( +func NewStorageCapabilityControllerValue( gauge common.MemoryGauge, - issueHeight uint64, capabilityID uint64, borrowType StaticType, delete func() error, @@ -45,21 +43,18 @@ func NewCapabilityControllerValue( borrowTypeValue := NewTypeValue(gauge, borrowType) fields := map[string]Value{ - sema.CapabilityControllerTypeIssueHeightFieldName: NewUInt64Value(gauge, func() uint64 { - return issueHeight - }), - sema.CapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, - sema.CapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { + sema.StorageCapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, + sema.StorageCapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { return capabilityID }), } computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { switch name { - case sema.CapabilityControllerTypeTargetFunctionName: + case sema.StorageCapabilityControllerTypeTargetFunctionName: return NewHostFunctionValue( gauge, - sema.CapabilityControllerTypeTargetFunctionType, + sema.StorageCapabilityControllerTypeTargetFunctionType, func(invocation Invocation) Value { target, err := getTarget() if err != nil { @@ -70,10 +65,10 @@ func NewCapabilityControllerValue( }, ) - case sema.CapabilityControllerTypeDeleteFunctionName: + case sema.StorageCapabilityControllerTypeDeleteFunctionName: return NewHostFunctionValue( gauge, - sema.CapabilityControllerTypeDeleteFunctionType, + sema.StorageCapabilityControllerTypeDeleteFunctionType, func(invocation Invocation) Value { err := delete() if err != nil { @@ -84,10 +79,10 @@ func NewCapabilityControllerValue( }, ) - case sema.CapabilityControllerTypeRetargetFunctionName: + case sema.StorageCapabilityControllerTypeRetargetFunctionName: return NewHostFunctionValue( gauge, - sema.CapabilityControllerTypeRetargetFunctionType, + sema.StorageCapabilityControllerTypeRetargetFunctionType, func(invocation Invocation) Value { newTarget, ok := invocation.Arguments[0].(PathValue) if !ok { @@ -110,7 +105,7 @@ func NewCapabilityControllerValue( var str string stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { if str == "" { - common.UseMemory(memoryGauge, common.CapabilityControllerStringMemoryUsage) + common.UseMemory(memoryGauge, common.StorageCapabilityControllerStringMemoryUsage) borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) @@ -119,7 +114,7 @@ func NewCapabilityControllerValue( idStr := fmt.Sprint(capabilityID) - str = format.CapabilityController(borrowTypeStr, idStr) + str = format.StorageCapabilityController(borrowTypeStr, idStr) } return str @@ -127,9 +122,9 @@ func NewCapabilityControllerValue( return NewSimpleCompositeValue( gauge, - sema.CapabilityControllerType.ID(), - PrimitiveStaticTypeCapabilityController, - capabilityControllerFieldNames, + sema.StorageCapabilityControllerType.ID(), + PrimitiveStaticTypeStorageCapabilityController, + storageCapabilityControllerFieldNames, fields, computeField, nil, diff --git a/runtime/interpreter/capability_controller_test.go b/runtime/interpreter/storage_capability_controller_test.go similarity index 93% rename from runtime/interpreter/capability_controller_test.go rename to runtime/interpreter/storage_capability_controller_test.go index 5957651fe2..4dafd83ad5 100644 --- a/runtime/interpreter/capability_controller_test.go +++ b/runtime/interpreter/storage_capability_controller_test.go @@ -22,6 +22,6 @@ import ( "testing" ) -func TestCapabilityControllerValue(t *testing.T) { +func TestStorageCapabilityControllerValue(t *testing.T) { // TODO add tests when capcons are integrated with accounts } diff --git a/runtime/sema/capability_controller.gen.go b/runtime/sema/capability_controller.gen.go deleted file mode 100644 index 9286724b7c..0000000000 --- a/runtime/sema/capability_controller.gen.go +++ /dev/null @@ -1,237 +0,0 @@ -// Code generated from capability_controller.cdc. DO NOT EDIT. -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - -const CapabilityControllerTypeIssueHeightFieldName = "issueHeight" - -var CapabilityControllerTypeIssueHeightFieldType = UInt64Type - -const CapabilityControllerTypeIssueHeightFieldDocString = `The block height when the capability was created. -` - -const CapabilityControllerTypeBorrowTypeFieldName = "borrowType" - -var CapabilityControllerTypeBorrowTypeFieldType = MetaType - -const CapabilityControllerTypeBorrowTypeFieldDocString = `The Type of the capability, i.e.: the T in Capability. -` - -const CapabilityControllerTypeCapabilityIDFieldName = "capabilityID" - -var CapabilityControllerTypeCapabilityIDFieldType = UInt64Type - -const CapabilityControllerTypeCapabilityIDFieldDocString = `The id of the related capability. -This is the UUID of the created capability. -All copies of the same capability will have the same UUID -` - -const CapabilityControllerTypeIsRevokedFunctionName = "isRevoked" - -var CapabilityControllerTypeIsRevokedFunctionType = &FunctionType{ - ReturnTypeAnnotation: NewTypeAnnotation( - BoolType, - ), -} - -const CapabilityControllerTypeIsRevokedFunctionDocString = `Is the capability revoked. -` - -const CapabilityControllerTypeTargetFunctionName = "target" - -var CapabilityControllerTypeTargetFunctionType = &FunctionType{ - ReturnTypeAnnotation: NewTypeAnnotation( - StoragePathType, - ), -} - -const CapabilityControllerTypeTargetFunctionDocString = `Returns the targeted storage path of the capability. -` - -const CapabilityControllerTypeDeleteFunctionName = "delete" - -var CapabilityControllerTypeDeleteFunctionType = &FunctionType{ - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const CapabilityControllerTypeDeleteFunctionDocString = `Delete this capability controller, -and disable the controlled capability and its copies. - -The controller will be deleted from storage, -but the controlled capability and its copies remain. - -Once this function returns, the controller is no longer usable, -all further operations on the controller will panic. - -Borrowing from the controlled capability or its copies will return nil. -` - -const CapabilityControllerTypeRetargetFunctionName = "retarget" - -var CapabilityControllerTypeRetargetFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const CapabilityControllerTypeRetargetFunctionDocString = `Retarget the capability. -This moves the CapCon from one CapCon array to another. -` - -const CapabilityControllerTypeName = "CapabilityController" - -var CapabilityControllerType = &SimpleType{ - Name: CapabilityControllerTypeName, - QualifiedName: CapabilityControllerTypeName, - TypeID: CapabilityControllerTypeName, - tag: CapabilityControllerTypeTag, - IsResource: false, - Storable: false, - Equatable: false, - Exportable: false, - Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - CapabilityControllerTypeIssueHeightFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeIssueHeightFieldType, - CapabilityControllerTypeIssueHeightFieldDocString, - ) - }, - }, - CapabilityControllerTypeBorrowTypeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeBorrowTypeFieldType, - CapabilityControllerTypeBorrowTypeFieldDocString, - ) - }, - }, - CapabilityControllerTypeCapabilityIDFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeCapabilityIDFieldType, - CapabilityControllerTypeCapabilityIDFieldDocString, - ) - }, - }, - CapabilityControllerTypeIsRevokedFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeIsRevokedFunctionType, - CapabilityControllerTypeIsRevokedFunctionDocString, - ) - }, - }, - CapabilityControllerTypeTargetFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeTargetFunctionType, - CapabilityControllerTypeTargetFunctionDocString, - ) - }, - }, - CapabilityControllerTypeDeleteFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeDeleteFunctionType, - CapabilityControllerTypeDeleteFunctionDocString, - ) - }, - }, - CapabilityControllerTypeRetargetFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityControllerTypeRetargetFunctionType, - CapabilityControllerTypeRetargetFunctionDocString, - ) - }, - }, - } - }, -} diff --git a/runtime/sema/capability_controller.cdc b/runtime/sema/storage_capability_controller.cdc similarity index 59% rename from runtime/sema/capability_controller.cdc rename to runtime/sema/storage_capability_controller.cdc index 094ddf34c2..4cab4a2457 100644 --- a/runtime/sema/capability_controller.cdc +++ b/runtime/sema/storage_capability_controller.cdc @@ -1,21 +1,11 @@ -pub struct CapabilityController { - /// The block height when the capability was created. - pub let issueHeight: UInt64 - - /// The Type of the capability, i.e.: the T in Capability. +pub struct StorageCapabilityController { + /// The type of the controlled capability, i.e. the T in `Capability`. pub let borrowType: Type - - /// The id of the related capability. - /// This is the UUID of the created capability. - /// All copies of the same capability will have the same UUID + + /// The identifier of the controlled capability. + /// All copies of a capability have the same ID. pub let capabilityID: UInt64 - - /// Is the capability revoked. - pub fun isRevoked(): Bool - /// Returns the targeted storage path of the capability. - pub fun target(): StoragePath - /// Delete this capability controller, /// and disable the controlled capability and its copies. /// @@ -28,8 +18,11 @@ pub struct CapabilityController { /// Borrowing from the controlled capability or its copies will return nil. /// pub fun delete() - + + /// Returns the targeted storage path of the controlled capability. + pub fun target(): StoragePath + /// Retarget the capability. /// This moves the CapCon from one CapCon array to another. pub fun retarget(target: StoragePath) -} \ No newline at end of file +} diff --git a/runtime/sema/capability_controller.go b/runtime/sema/storage_capability_controller.go similarity index 76% rename from runtime/sema/capability_controller.go rename to runtime/sema/storage_capability_controller.go index 780cb57870..8123053a17 100644 --- a/runtime/sema/capability_controller.go +++ b/runtime/sema/storage_capability_controller.go @@ -18,6 +18,6 @@ package sema -//go:generate go run ./gen/main.go capability_controller.cdc capability_controller.gen.go +//go:generate go run ./gen/main.go storage_capability_controller.cdc storage_capability_controller.gen.go -var CapabilityControllerTypeAnnotation = NewTypeAnnotation(CapabilityControllerType) +var StorageCapabilityControllerTypeAnnotation = NewTypeAnnotation(StorageCapabilityControllerType) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9076066e1f..06bf10b12e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3058,7 +3058,7 @@ func init() { PublicKeyType, SignatureAlgorithmType, HashAlgorithmType, - CapabilityControllerType, + StorageCapabilityControllerType, ) for _, ty := range types { diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 523d75546c..a8a1b5b5da 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -218,7 +218,7 @@ const ( transactionTypeMask anyResourceAttachmentMask anyStructAttachmentMask - capabilityControllerTypeMask + storageCapabilityControllerTypeMask invalidTypeMask ) @@ -328,13 +328,13 @@ var ( FunctionTypeTag = newTypeTagFromLowerMask(functionTypeMask) InterfaceTypeTag = newTypeTagFromLowerMask(interfaceTypeMask) - RestrictedTypeTag = newTypeTagFromUpperMask(restrictedTypeMask) - CapabilityTypeTag = newTypeTagFromUpperMask(capabilityTypeMask) - InvalidTypeTag = newTypeTagFromUpperMask(invalidTypeMask) - TransactionTypeTag = newTypeTagFromUpperMask(transactionTypeMask) - AnyResourceAttachmentTypeTag = newTypeTagFromUpperMask(anyResourceAttachmentMask) - AnyStructAttachmentTypeTag = newTypeTagFromUpperMask(anyStructAttachmentMask) - CapabilityControllerTypeTag = newTypeTagFromUpperMask(capabilityControllerTypeMask) + RestrictedTypeTag = newTypeTagFromUpperMask(restrictedTypeMask) + CapabilityTypeTag = newTypeTagFromUpperMask(capabilityTypeMask) + InvalidTypeTag = newTypeTagFromUpperMask(invalidTypeMask) + TransactionTypeTag = newTypeTagFromUpperMask(transactionTypeMask) + AnyResourceAttachmentTypeTag = newTypeTagFromUpperMask(anyResourceAttachmentMask) + AnyStructAttachmentTypeTag = newTypeTagFromUpperMask(anyStructAttachmentMask) + StorageCapabilityControllerTypeTag = newTypeTagFromUpperMask(storageCapabilityControllerTypeMask) // AnyStructTypeTag only includes the types that are pre-known // to belong to AnyStruct type. This is more of an optimization. @@ -358,7 +358,7 @@ var ( Or(DeployedContractTypeTag). Or(CapabilityTypeTag). Or(FunctionTypeTag). - Or(CapabilityControllerTypeTag) + Or(StorageCapabilityControllerTypeTag) AnyResourceTypeTag = newTypeTagFromLowerMask(anyResourceTypeMask). Or(AnyResourceAttachmentTypeTag) @@ -665,8 +665,8 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { case anyStructAttachmentMask: return AnyStructAttachmentType - case capabilityControllerTypeMask: - return CapabilityControllerType + case storageCapabilityControllerTypeMask: + return StorageCapabilityControllerType default: return nil diff --git a/runtime/tests/checker/capability_controller_test.go b/runtime/tests/checker/capability_controller_test.go index 9d6c59867d..f7ef9166f7 100644 --- a/runtime/tests/checker/capability_controller_test.go +++ b/runtime/tests/checker/capability_controller_test.go @@ -21,17 +21,18 @@ package checker import ( "testing" + "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" - "github.com/stretchr/testify/require" ) func ParseAndCheckCapcon(t *testing.T, code string) (*sema.Checker, error) { baseActivation := sema.NewVariableActivation(sema.BaseValueActivation) baseActivation.DeclareValue(stdlib.StandardLibraryValue{ Name: "controller", - Type: sema.CapabilityControllerType, + Type: sema.StorageCapabilityControllerType, Kind: common.DeclarationKindConstant, }) @@ -46,22 +47,38 @@ func ParseAndCheckCapcon(t *testing.T, code string) (*sema.Checker, error) { ) } -func TestCheckCapconNonEquatable(t *testing.T) { +func TestCheckStorageCapabilityController(t *testing.T) { t.Parallel() - _, err := ParseAndCheckCapcon(t, ` - let kaboom: Bool = controller == controller - `) + t.Run("not equatable", func(t *testing.T) { - errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) -} + _, err := ParseAndCheckCapcon(t, ` + let equal = controller == controller + `) -func TestCheckCapconTypeInScope(t *testing.T) { - t.Parallel() + errs := RequireCheckerErrors(t, err, 1) + require.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) + }) - _, err := ParseAndCheckCapcon(t, ` - let typ = Type() - `) - require.NoError(t, err) + t.Run("in scope", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckCapcon(t, ` + let typ = Type() + `) + require.NoError(t, err) + }) + + t.Run("members", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckCapcon(t, ` + let borrowType: Type = controller.borrowType + let capabilityID: UInt64 = controller.capabilityID + let target: StoragePath = controller.target() + let _: Void = controller.retarget(target: /storage/test) + `) + + require.NoError(t, err) + }) } From 0def4a5c000ec6861ec9adfc09592f291211733a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 15:28:42 -0700 Subject: [PATCH 046/246] add generated code --- .../sema/storage_capability_controller.gen.go | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 runtime/sema/storage_capability_controller.gen.go diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go new file mode 100644 index 0000000000..023c6b8b5e --- /dev/null +++ b/runtime/sema/storage_capability_controller.gen.go @@ -0,0 +1,191 @@ +// Code generated from storage_capability_controller.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" +) + +const StorageCapabilityControllerTypeBorrowTypeFieldName = "borrowType" + +var StorageCapabilityControllerTypeBorrowTypeFieldType = MetaType + +const StorageCapabilityControllerTypeBorrowTypeFieldDocString = ` +The type of the controlled capability, i.e. the T in ` + "`Capability`" + `. +` + +const StorageCapabilityControllerTypeCapabilityIDFieldName = "capabilityID" + +var StorageCapabilityControllerTypeCapabilityIDFieldType = UInt64Type + +const StorageCapabilityControllerTypeCapabilityIDFieldDocString = ` +The identifier of the controlled capability. +All copies of a capability have the same ID. +` + +const StorageCapabilityControllerTypeDeleteFunctionName = "delete" + +var StorageCapabilityControllerTypeDeleteFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const StorageCapabilityControllerTypeDeleteFunctionDocString = ` +Delete this capability controller, +and disable the controlled capability and its copies. + +The controller will be deleted from storage, +but the controlled capability and its copies remain. + +Once this function returns, the controller is no longer usable, +all further operations on the controller will panic. + +Borrowing from the controlled capability or its copies will return nil. +` + +const StorageCapabilityControllerTypeTargetFunctionName = "target" + +var StorageCapabilityControllerTypeTargetFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + StoragePathType, + ), +} + +const StorageCapabilityControllerTypeTargetFunctionDocString = ` +Returns the targeted storage path of the controlled capability. +` + +const StorageCapabilityControllerTypeRetargetFunctionName = "retarget" + +var StorageCapabilityControllerTypeRetargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const StorageCapabilityControllerTypeRetargetFunctionDocString = ` +Retarget the capability. +This moves the CapCon from one CapCon array to another. +` + +const StorageCapabilityControllerTypeName = "StorageCapabilityController" + +var StorageCapabilityControllerType = &SimpleType{ + Name: StorageCapabilityControllerTypeName, + QualifiedName: StorageCapabilityControllerTypeName, + TypeID: StorageCapabilityControllerTypeName, + tag: StorageCapabilityControllerTypeTag, + IsResource: false, + Storable: false, + Equatable: false, + Exportable: false, + Importable: false, + Members: func(t *SimpleType) map[string]MemberResolver { + return map[string]MemberResolver{ + StorageCapabilityControllerTypeBorrowTypeFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + StorageCapabilityControllerTypeBorrowTypeFieldType, + StorageCapabilityControllerTypeBorrowTypeFieldDocString, + ) + }, + }, + StorageCapabilityControllerTypeCapabilityIDFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + StorageCapabilityControllerTypeCapabilityIDFieldType, + StorageCapabilityControllerTypeCapabilityIDFieldDocString, + ) + }, + }, + StorageCapabilityControllerTypeDeleteFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + StorageCapabilityControllerTypeDeleteFunctionType, + StorageCapabilityControllerTypeDeleteFunctionDocString, + ) + }, + }, + StorageCapabilityControllerTypeTargetFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + StorageCapabilityControllerTypeTargetFunctionType, + StorageCapabilityControllerTypeTargetFunctionDocString, + ) + }, + }, + StorageCapabilityControllerTypeRetargetFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + StorageCapabilityControllerTypeRetargetFunctionType, + StorageCapabilityControllerTypeRetargetFunctionDocString, + ) + }, + }, + } + }, +} From 5a13bddc6a49f8309446320a8a2cc8e8b5bc1ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 15:42:07 -0700 Subject: [PATCH 047/246] add AccountCapabilityController --- runtime/common/metering.go | 1 + runtime/format/capability.go | 8 ++ .../account_capability_controller.go | 98 +++++++++++++++++++ .../account_capability_controller_test.go | 27 +++++ runtime/interpreter/primitivestatictype.go | 8 +- .../interpreter/primitivestatictype_string.go | 8 +- runtime/interpreter/statictype_test.go | 2 +- .../sema/storage_capability_controller.gen.go | 1 + runtime/sema/type.go | 1 + runtime/sema/type_tags.go | 8 +- .../checker/capability_controller_test.go | 97 +++++++++++++----- 11 files changed, 231 insertions(+), 28 deletions(-) create mode 100644 runtime/interpreter/account_capability_controller.go create mode 100644 runtime/interpreter/account_capability_controller_test.go diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 68f81ee1f9..a45e8b40cb 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -249,6 +249,7 @@ var ( AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) + AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 858e3bc8f1..5ee162ff40 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -43,3 +43,11 @@ func StorageCapabilityController(borrowType string, capabilityID string) string capabilityID, ) } + +func AccountCapabilityController(borrowType string, capabilityID string) string { + return fmt.Sprintf( + "AccountCapabilityController(borrowType: %s, capabilityID: %s)", + borrowType, + capabilityID, + ) +} diff --git a/runtime/interpreter/account_capability_controller.go b/runtime/interpreter/account_capability_controller.go new file mode 100644 index 0000000000..0e3ce51fbb --- /dev/null +++ b/runtime/interpreter/account_capability_controller.go @@ -0,0 +1,98 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/format" + "github.com/onflow/cadence/runtime/sema" +) + +var AccountCapabilityControllerFieldNames = []string{ + sema.AccountCapabilityControllerTypeBorrowTypeFieldName, + sema.AccountCapabilityControllerTypeCapabilityIDFieldName, +} + +func NewAccountCapabilityControllerValue( + gauge common.MemoryGauge, + capabilityID uint64, + borrowType StaticType, + delete func() error, +) Value { + + borrowTypeValue := NewTypeValue(gauge, borrowType) + fields := map[string]Value{ + sema.AccountCapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, + sema.AccountCapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { + return capabilityID + }), + } + + computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { + switch name { + case sema.AccountCapabilityControllerTypeDeleteFunctionName: + return NewHostFunctionValue( + gauge, + sema.AccountCapabilityControllerTypeDeleteFunctionType, + func(invocation Invocation) Value { + err := delete() + if err != nil { + panic(err) + } + + return Void + }, + ) + + } + + return nil + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.AccountCapabilityControllerStringMemoryUsage) + + borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) + + memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) + common.UseMemory(memoryGauge, memoryUsage) + + idStr := fmt.Sprint(capabilityID) + + str = format.AccountCapabilityController(borrowTypeStr, idStr) + } + + return str + } + + return NewSimpleCompositeValue( + gauge, + sema.AccountCapabilityControllerType.ID(), + PrimitiveStaticTypeAccountCapabilityController, + AccountCapabilityControllerFieldNames, + fields, + computeField, + nil, + stringer, + ) +} diff --git a/runtime/interpreter/account_capability_controller_test.go b/runtime/interpreter/account_capability_controller_test.go new file mode 100644 index 0000000000..2251031d44 --- /dev/null +++ b/runtime/interpreter/account_capability_controller_test.go @@ -0,0 +1,27 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "testing" +) + +func TestAccountCapabilityControllerValue(t *testing.T) { + // TODO add tests when capcons are integrated with accounts +} diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index 0ff3e507f8..32af27f151 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -201,6 +201,7 @@ const ( PrimitiveStaticTypeAccountKey PrimitiveStaticTypeAuthAccountInbox PrimitiveStaticTypeStorageCapabilityController + PrimitiveStaticTypeAccountCapabilityController // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -282,7 +283,8 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAuthAccountKeys, PrimitiveStaticTypePublicAccountKeys, PrimitiveStaticTypeAccountKey, - PrimitiveStaticTypeStorageCapabilityController: + PrimitiveStaticTypeStorageCapabilityController, + PrimitiveStaticTypeAccountCapabilityController: return UnknownElementSize } return UnknownElementSize @@ -427,6 +429,8 @@ func (i PrimitiveStaticType) SemaType() sema.Type { return sema.AuthAccountInboxType case PrimitiveStaticTypeStorageCapabilityController: return sema.StorageCapabilityControllerType + case PrimitiveStaticTypeAccountCapabilityController: + return sema.AccountCapabilityControllerType default: panic(errors.NewUnexpectedError("missing case for %s", i)) } @@ -561,6 +565,8 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeAuthAccountInbox case sema.StorageCapabilityControllerType: typ = PrimitiveStaticTypeStorageCapabilityController + case sema.AccountCapabilityControllerType: + typ = PrimitiveStaticTypeAccountCapabilityController } switch t.(type) { diff --git a/runtime/interpreter/primitivestatictype_string.go b/runtime/interpreter/primitivestatictype_string.go index 84de818708..b86f8bc2c5 100644 --- a/runtime/interpreter/primitivestatictype_string.go +++ b/runtime/interpreter/primitivestatictype_string.go @@ -62,10 +62,11 @@ func _() { _ = x[PrimitiveStaticTypeAccountKey-97] _ = x[PrimitiveStaticTypeAuthAccountInbox-98] _ = x[PrimitiveStaticTypeStorageCapabilityController-99] - _ = x[PrimitiveStaticType_Count-100] + _ = x[PrimitiveStaticTypeAccountCapabilityController-100] + _ = x[PrimitiveStaticType_Count-101] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityController_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityController_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 0: _PrimitiveStaticType_name[0:7], @@ -122,7 +123,8 @@ var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 97: _PrimitiveStaticType_name[425:435], 98: _PrimitiveStaticType_name[435:451], 99: _PrimitiveStaticType_name[451:478], - 100: _PrimitiveStaticType_name[478:484], + 100: _PrimitiveStaticType_name[478:505], + 101: _PrimitiveStaticType_name[505:511], } func (i PrimitiveStaticType) String() string { diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index 961fe46d2c..fe1fede2c1 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -969,6 +969,6 @@ func TestPrimitiveStaticTypeCount(t *testing.T) { // (before the PrimitiveStaticType_Count of course). // Only update this test if you are certain your change to this enum was to append new types to the end. t.Run("No new types added in between", func(t *testing.T) { - require.Equal(t, byte(100), byte(PrimitiveStaticType_Count)) + require.Equal(t, byte(101), byte(PrimitiveStaticType_Count)) }) } diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go index 023c6b8b5e..14b76f531d 100644 --- a/runtime/sema/storage_capability_controller.gen.go +++ b/runtime/sema/storage_capability_controller.gen.go @@ -79,6 +79,7 @@ const StorageCapabilityControllerTypeRetargetFunctionName = "retarget" var StorageCapabilityControllerTypeRetargetFunctionType = &FunctionType{ Parameters: []Parameter{ { + Identifier: "target", TypeAnnotation: NewTypeAnnotation(StoragePathType), }, }, diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 06bf10b12e..761bdb99ed 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3059,6 +3059,7 @@ func init() { SignatureAlgorithmType, HashAlgorithmType, StorageCapabilityControllerType, + AccountCapabilityControllerType, ) for _, ty := range types { diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index a8a1b5b5da..f603c9da9a 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -219,6 +219,7 @@ const ( anyResourceAttachmentMask anyStructAttachmentMask storageCapabilityControllerTypeMask + accountCapabilityControllerTypeMask invalidTypeMask ) @@ -335,6 +336,7 @@ var ( AnyResourceAttachmentTypeTag = newTypeTagFromUpperMask(anyResourceAttachmentMask) AnyStructAttachmentTypeTag = newTypeTagFromUpperMask(anyStructAttachmentMask) StorageCapabilityControllerTypeTag = newTypeTagFromUpperMask(storageCapabilityControllerTypeMask) + AccountCapabilityControllerTypeTag = newTypeTagFromUpperMask(accountCapabilityControllerTypeMask) // AnyStructTypeTag only includes the types that are pre-known // to belong to AnyStruct type. This is more of an optimization. @@ -358,7 +360,8 @@ var ( Or(DeployedContractTypeTag). Or(CapabilityTypeTag). Or(FunctionTypeTag). - Or(StorageCapabilityControllerTypeTag) + Or(StorageCapabilityControllerTypeTag). + Or(AccountCapabilityControllerTypeTag) AnyResourceTypeTag = newTypeTagFromLowerMask(anyResourceTypeMask). Or(AnyResourceAttachmentTypeTag) @@ -668,6 +671,9 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { case storageCapabilityControllerTypeMask: return StorageCapabilityControllerType + case accountCapabilityControllerTypeMask: + return AccountCapabilityControllerType + default: return nil } diff --git a/runtime/tests/checker/capability_controller_test.go b/runtime/tests/checker/capability_controller_test.go index f7ef9166f7..74812934f6 100644 --- a/runtime/tests/checker/capability_controller_test.go +++ b/runtime/tests/checker/capability_controller_test.go @@ -28,31 +28,31 @@ import ( "github.com/onflow/cadence/runtime/stdlib" ) -func ParseAndCheckCapcon(t *testing.T, code string) (*sema.Checker, error) { - baseActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseActivation.DeclareValue(stdlib.StandardLibraryValue{ - Name: "controller", - Type: sema.StorageCapabilityControllerType, - Kind: common.DeclarationKindConstant, - }) - - return ParseAndCheckWithOptions( - t, - code, - ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseActivation, - }, - }, - ) -} - func TestCheckStorageCapabilityController(t *testing.T) { t.Parallel() + parseAndCheck := func(t *testing.T, code string) (*sema.Checker, error) { + baseActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseActivation.DeclareValue(stdlib.StandardLibraryValue{ + Name: "controller", + Type: sema.StorageCapabilityControllerType, + Kind: common.DeclarationKindConstant, + }) + + return ParseAndCheckWithOptions( + t, + code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseActivation, + }, + }, + ) + } + t.Run("not equatable", func(t *testing.T) { - _, err := ParseAndCheckCapcon(t, ` + _, err := parseAndCheck(t, ` let equal = controller == controller `) @@ -63,7 +63,7 @@ func TestCheckStorageCapabilityController(t *testing.T) { t.Run("in scope", func(t *testing.T) { t.Parallel() - _, err := ParseAndCheckCapcon(t, ` + _, err := parseAndCheck(t, ` let typ = Type() `) require.NoError(t, err) @@ -72,7 +72,7 @@ func TestCheckStorageCapabilityController(t *testing.T) { t.Run("members", func(t *testing.T) { t.Parallel() - _, err := ParseAndCheckCapcon(t, ` + _, err := parseAndCheck(t, ` let borrowType: Type = controller.borrowType let capabilityID: UInt64 = controller.capabilityID let target: StoragePath = controller.target() @@ -82,3 +82,56 @@ func TestCheckStorageCapabilityController(t *testing.T) { require.NoError(t, err) }) } + +func TestCheckAccountCapabilityController(t *testing.T) { + t.Parallel() + + parseAndCheck := func(t *testing.T, code string) (*sema.Checker, error) { + baseActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseActivation.DeclareValue(stdlib.StandardLibraryValue{ + Name: "controller", + Type: sema.AccountCapabilityControllerType, + Kind: common.DeclarationKindConstant, + }) + + return ParseAndCheckWithOptions( + t, + code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseActivation, + }, + }, + ) + } + + t.Run("not equatable", func(t *testing.T) { + + _, err := parseAndCheck(t, ` + let equal = controller == controller + `) + + errs := RequireCheckerErrors(t, err, 1) + require.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) + }) + + t.Run("in scope", func(t *testing.T) { + t.Parallel() + + _, err := parseAndCheck(t, ` + let typ = Type() + `) + require.NoError(t, err) + }) + + t.Run("members", func(t *testing.T) { + t.Parallel() + + _, err := parseAndCheck(t, ` + let borrowType: Type = controller.borrowType + let capabilityID: UInt64 = controller.capabilityID + `) + + require.NoError(t, err) + }) +} From 8677e15391988eb515d000dd26a92c0fe37d2021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 10:54:59 -0700 Subject: [PATCH 048/246] add missing files --- .../sema/account_capability_controller.cdc | 21 +++ .../sema/account_capability_controller.gen.go | 129 ++++++++++++++++++ runtime/sema/account_capability_controller.go | 23 ++++ 3 files changed, 173 insertions(+) create mode 100644 runtime/sema/account_capability_controller.cdc create mode 100644 runtime/sema/account_capability_controller.gen.go create mode 100644 runtime/sema/account_capability_controller.go diff --git a/runtime/sema/account_capability_controller.cdc b/runtime/sema/account_capability_controller.cdc new file mode 100644 index 0000000000..64d59bc08f --- /dev/null +++ b/runtime/sema/account_capability_controller.cdc @@ -0,0 +1,21 @@ +pub struct AccountCapabilityController { + /// The type of the controlled capability, i.e. the T in `Capability`. + pub let borrowType: Type + + /// The identifier of the controlled capability. + /// All copies of a capability have the same ID. + pub let capabilityID: UInt64 + + /// Delete this capability controller, + /// and disable the controlled capability and its copies. + /// + /// The controller will be deleted from storage, + /// but the controlled capability and its copies remain. + /// + /// Once this function returns, the controller is no longer usable, + /// all further operations on the controller will panic. + /// + /// Borrowing from the controlled capability or its copies will return nil. + /// + pub fun delete() +} \ No newline at end of file diff --git a/runtime/sema/account_capability_controller.gen.go b/runtime/sema/account_capability_controller.gen.go new file mode 100644 index 0000000000..bd281d5084 --- /dev/null +++ b/runtime/sema/account_capability_controller.gen.go @@ -0,0 +1,129 @@ +// Code generated from account_capability_controller.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" +) + +const AccountCapabilityControllerTypeBorrowTypeFieldName = "borrowType" + +var AccountCapabilityControllerTypeBorrowTypeFieldType = MetaType + +const AccountCapabilityControllerTypeBorrowTypeFieldDocString = ` +The type of the controlled capability, i.e. the T in ` + "`Capability`" + `. +` + +const AccountCapabilityControllerTypeCapabilityIDFieldName = "capabilityID" + +var AccountCapabilityControllerTypeCapabilityIDFieldType = UInt64Type + +const AccountCapabilityControllerTypeCapabilityIDFieldDocString = ` +The identifier of the controlled capability. +All copies of a capability have the same ID. +` + +const AccountCapabilityControllerTypeDeleteFunctionName = "delete" + +var AccountCapabilityControllerTypeDeleteFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AccountCapabilityControllerTypeDeleteFunctionDocString = ` +Delete this capability controller, +and disable the controlled capability and its copies. + +The controller will be deleted from storage, +but the controlled capability and its copies remain. + +Once this function returns, the controller is no longer usable, +all further operations on the controller will panic. + +Borrowing from the controlled capability or its copies will return nil. +` + +const AccountCapabilityControllerTypeName = "AccountCapabilityController" + +var AccountCapabilityControllerType = &SimpleType{ + Name: AccountCapabilityControllerTypeName, + QualifiedName: AccountCapabilityControllerTypeName, + TypeID: AccountCapabilityControllerTypeName, + tag: AccountCapabilityControllerTypeTag, + IsResource: false, + Storable: false, + Equatable: false, + Exportable: false, + Importable: false, + Members: func(t *SimpleType) map[string]MemberResolver { + return map[string]MemberResolver{ + AccountCapabilityControllerTypeBorrowTypeFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + AccountCapabilityControllerTypeBorrowTypeFieldType, + AccountCapabilityControllerTypeBorrowTypeFieldDocString, + ) + }, + }, + AccountCapabilityControllerTypeCapabilityIDFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicConstantFieldMember( + memoryGauge, + t, + identifier, + AccountCapabilityControllerTypeCapabilityIDFieldType, + AccountCapabilityControllerTypeCapabilityIDFieldDocString, + ) + }, + }, + AccountCapabilityControllerTypeDeleteFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error)) *Member { + + return NewPublicFunctionMember( + memoryGauge, + t, + identifier, + AccountCapabilityControllerTypeDeleteFunctionType, + AccountCapabilityControllerTypeDeleteFunctionDocString, + ) + }, + }, + } + }, +} diff --git a/runtime/sema/account_capability_controller.go b/runtime/sema/account_capability_controller.go new file mode 100644 index 0000000000..fd0357ecc1 --- /dev/null +++ b/runtime/sema/account_capability_controller.go @@ -0,0 +1,23 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen/main.go account_capability_controller.cdc account_capability_controller.gen.go + +var AccountCapabilityControllerTypeAnnotation = NewTypeAnnotation(AccountCapabilityControllerType) From 526b5305b238d63f99b68890960c4c37e29eab61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 31 Mar 2023 12:53:04 -0700 Subject: [PATCH 049/246] remove unnecessary memory kinds --- runtime/common/memorykind.go | 3 - runtime/common/memorykind_string.go | 337 ++++++++++++++-------------- 2 files changed, 167 insertions(+), 173 deletions(-) diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 85a8ec4882..04ea030df6 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -51,7 +51,6 @@ const ( MemoryKindBigInt MemoryKindSimpleCompositeValue MemoryKindPublishedValue - MemoryKindCapabilityControllerValue // Atree Nodes MemoryKindAtreeArrayDataSlab @@ -75,7 +74,6 @@ const ( MemoryKindReferenceStaticType MemoryKindCapabilityStaticType MemoryKindFunctionStaticType - MemoryKindCapabilityControllerStaticType // Cadence Values MemoryKindCadenceVoidValue @@ -239,7 +237,6 @@ const ( MemoryKindRestrictedSemaType MemoryKindReferenceSemaType MemoryKindCapabilitySemaType - MemoryKindCapabilityControllerSemaType // ordered-map MemoryKindOrderedMap diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 9110683535..922940626b 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -32,179 +32,176 @@ func _() { _ = x[MemoryKindBigInt-21] _ = x[MemoryKindSimpleCompositeValue-22] _ = x[MemoryKindPublishedValue-23] - _ = x[MemoryKindCapabilityControllerValue-24] - _ = x[MemoryKindAtreeArrayDataSlab-25] - _ = x[MemoryKindAtreeArrayMetaDataSlab-26] - _ = x[MemoryKindAtreeArrayElementOverhead-27] - _ = x[MemoryKindAtreeMapDataSlab-28] - _ = x[MemoryKindAtreeMapMetaDataSlab-29] - _ = x[MemoryKindAtreeMapElementOverhead-30] - _ = x[MemoryKindAtreeMapPreAllocatedElement-31] - _ = x[MemoryKindAtreeEncodedSlab-32] - _ = x[MemoryKindPrimitiveStaticType-33] - _ = x[MemoryKindCompositeStaticType-34] - _ = x[MemoryKindInterfaceStaticType-35] - _ = x[MemoryKindVariableSizedStaticType-36] - _ = x[MemoryKindConstantSizedStaticType-37] - _ = x[MemoryKindDictionaryStaticType-38] - _ = x[MemoryKindOptionalStaticType-39] - _ = x[MemoryKindRestrictedStaticType-40] - _ = x[MemoryKindReferenceStaticType-41] - _ = x[MemoryKindCapabilityStaticType-42] - _ = x[MemoryKindFunctionStaticType-43] - _ = x[MemoryKindCapabilityControllerStaticType-44] - _ = x[MemoryKindCadenceVoidValue-45] - _ = x[MemoryKindCadenceOptionalValue-46] - _ = x[MemoryKindCadenceBoolValue-47] - _ = x[MemoryKindCadenceStringValue-48] - _ = x[MemoryKindCadenceCharacterValue-49] - _ = x[MemoryKindCadenceAddressValue-50] - _ = x[MemoryKindCadenceIntValue-51] - _ = x[MemoryKindCadenceNumberValue-52] - _ = x[MemoryKindCadenceArrayValueBase-53] - _ = x[MemoryKindCadenceArrayValueLength-54] - _ = x[MemoryKindCadenceDictionaryValue-55] - _ = x[MemoryKindCadenceKeyValuePair-56] - _ = x[MemoryKindCadenceStructValueBase-57] - _ = x[MemoryKindCadenceStructValueSize-58] - _ = x[MemoryKindCadenceResourceValueBase-59] - _ = x[MemoryKindCadenceAttachmentValueBase-60] - _ = x[MemoryKindCadenceResourceValueSize-61] - _ = x[MemoryKindCadenceAttachmentValueSize-62] - _ = x[MemoryKindCadenceEventValueBase-63] - _ = x[MemoryKindCadenceEventValueSize-64] - _ = x[MemoryKindCadenceContractValueBase-65] - _ = x[MemoryKindCadenceContractValueSize-66] - _ = x[MemoryKindCadenceEnumValueBase-67] - _ = x[MemoryKindCadenceEnumValueSize-68] - _ = x[MemoryKindCadencePathLinkValue-69] - _ = x[MemoryKindCadenceAccountLinkValue-70] - _ = x[MemoryKindCadencePathValue-71] - _ = x[MemoryKindCadenceTypeValue-72] - _ = x[MemoryKindCadenceStorageCapabilityValue-73] - _ = x[MemoryKindCadenceFunctionValue-74] - _ = x[MemoryKindCadenceOptionalType-75] - _ = x[MemoryKindCadenceVariableSizedArrayType-76] - _ = x[MemoryKindCadenceConstantSizedArrayType-77] - _ = x[MemoryKindCadenceDictionaryType-78] - _ = x[MemoryKindCadenceField-79] - _ = x[MemoryKindCadenceParameter-80] - _ = x[MemoryKindCadenceTypeParameter-81] - _ = x[MemoryKindCadenceStructType-82] - _ = x[MemoryKindCadenceResourceType-83] - _ = x[MemoryKindCadenceAttachmentType-84] - _ = x[MemoryKindCadenceEventType-85] - _ = x[MemoryKindCadenceContractType-86] - _ = x[MemoryKindCadenceStructInterfaceType-87] - _ = x[MemoryKindCadenceResourceInterfaceType-88] - _ = x[MemoryKindCadenceContractInterfaceType-89] - _ = x[MemoryKindCadenceFunctionType-90] - _ = x[MemoryKindCadenceReferenceType-91] - _ = x[MemoryKindCadenceRestrictedType-92] - _ = x[MemoryKindCadenceCapabilityType-93] - _ = x[MemoryKindCadenceEnumType-94] - _ = x[MemoryKindRawString-95] - _ = x[MemoryKindAddressLocation-96] - _ = x[MemoryKindBytes-97] - _ = x[MemoryKindVariable-98] - _ = x[MemoryKindCompositeTypeInfo-99] - _ = x[MemoryKindCompositeField-100] - _ = x[MemoryKindInvocation-101] - _ = x[MemoryKindStorageMap-102] - _ = x[MemoryKindStorageKey-103] - _ = x[MemoryKindTypeToken-104] - _ = x[MemoryKindErrorToken-105] - _ = x[MemoryKindSpaceToken-106] - _ = x[MemoryKindProgram-107] - _ = x[MemoryKindIdentifier-108] - _ = x[MemoryKindArgument-109] - _ = x[MemoryKindBlock-110] - _ = x[MemoryKindFunctionBlock-111] - _ = x[MemoryKindParameter-112] - _ = x[MemoryKindParameterList-113] - _ = x[MemoryKindTypeParameter-114] - _ = x[MemoryKindTypeParameterList-115] - _ = x[MemoryKindTransfer-116] - _ = x[MemoryKindMembers-117] - _ = x[MemoryKindTypeAnnotation-118] - _ = x[MemoryKindDictionaryEntry-119] - _ = x[MemoryKindFunctionDeclaration-120] - _ = x[MemoryKindCompositeDeclaration-121] - _ = x[MemoryKindAttachmentDeclaration-122] - _ = x[MemoryKindInterfaceDeclaration-123] - _ = x[MemoryKindEnumCaseDeclaration-124] - _ = x[MemoryKindFieldDeclaration-125] - _ = x[MemoryKindTransactionDeclaration-126] - _ = x[MemoryKindImportDeclaration-127] - _ = x[MemoryKindVariableDeclaration-128] - _ = x[MemoryKindSpecialFunctionDeclaration-129] - _ = x[MemoryKindPragmaDeclaration-130] - _ = x[MemoryKindAssignmentStatement-131] - _ = x[MemoryKindBreakStatement-132] - _ = x[MemoryKindContinueStatement-133] - _ = x[MemoryKindEmitStatement-134] - _ = x[MemoryKindExpressionStatement-135] - _ = x[MemoryKindForStatement-136] - _ = x[MemoryKindIfStatement-137] - _ = x[MemoryKindReturnStatement-138] - _ = x[MemoryKindSwapStatement-139] - _ = x[MemoryKindSwitchStatement-140] - _ = x[MemoryKindWhileStatement-141] - _ = x[MemoryKindRemoveStatement-142] - _ = x[MemoryKindBooleanExpression-143] - _ = x[MemoryKindVoidExpression-144] - _ = x[MemoryKindNilExpression-145] - _ = x[MemoryKindStringExpression-146] - _ = x[MemoryKindIntegerExpression-147] - _ = x[MemoryKindFixedPointExpression-148] - _ = x[MemoryKindArrayExpression-149] - _ = x[MemoryKindDictionaryExpression-150] - _ = x[MemoryKindIdentifierExpression-151] - _ = x[MemoryKindInvocationExpression-152] - _ = x[MemoryKindMemberExpression-153] - _ = x[MemoryKindIndexExpression-154] - _ = x[MemoryKindConditionalExpression-155] - _ = x[MemoryKindUnaryExpression-156] - _ = x[MemoryKindBinaryExpression-157] - _ = x[MemoryKindFunctionExpression-158] - _ = x[MemoryKindCastingExpression-159] - _ = x[MemoryKindCreateExpression-160] - _ = x[MemoryKindDestroyExpression-161] - _ = x[MemoryKindReferenceExpression-162] - _ = x[MemoryKindForceExpression-163] - _ = x[MemoryKindPathExpression-164] - _ = x[MemoryKindAttachExpression-165] - _ = x[MemoryKindConstantSizedType-166] - _ = x[MemoryKindDictionaryType-167] - _ = x[MemoryKindFunctionType-168] - _ = x[MemoryKindInstantiationType-169] - _ = x[MemoryKindNominalType-170] - _ = x[MemoryKindOptionalType-171] - _ = x[MemoryKindReferenceType-172] - _ = x[MemoryKindRestrictedType-173] - _ = x[MemoryKindVariableSizedType-174] - _ = x[MemoryKindPosition-175] - _ = x[MemoryKindRange-176] - _ = x[MemoryKindElaboration-177] - _ = x[MemoryKindActivation-178] - _ = x[MemoryKindActivationEntries-179] - _ = x[MemoryKindVariableSizedSemaType-180] - _ = x[MemoryKindConstantSizedSemaType-181] - _ = x[MemoryKindDictionarySemaType-182] - _ = x[MemoryKindOptionalSemaType-183] - _ = x[MemoryKindRestrictedSemaType-184] - _ = x[MemoryKindReferenceSemaType-185] - _ = x[MemoryKindCapabilitySemaType-186] - _ = x[MemoryKindCapabilityControllerSemaType-187] - _ = x[MemoryKindOrderedMap-188] - _ = x[MemoryKindOrderedMapEntryList-189] - _ = x[MemoryKindOrderedMapEntry-190] - _ = x[MemoryKindLast-191] + _ = x[MemoryKindAtreeArrayDataSlab-24] + _ = x[MemoryKindAtreeArrayMetaDataSlab-25] + _ = x[MemoryKindAtreeArrayElementOverhead-26] + _ = x[MemoryKindAtreeMapDataSlab-27] + _ = x[MemoryKindAtreeMapMetaDataSlab-28] + _ = x[MemoryKindAtreeMapElementOverhead-29] + _ = x[MemoryKindAtreeMapPreAllocatedElement-30] + _ = x[MemoryKindAtreeEncodedSlab-31] + _ = x[MemoryKindPrimitiveStaticType-32] + _ = x[MemoryKindCompositeStaticType-33] + _ = x[MemoryKindInterfaceStaticType-34] + _ = x[MemoryKindVariableSizedStaticType-35] + _ = x[MemoryKindConstantSizedStaticType-36] + _ = x[MemoryKindDictionaryStaticType-37] + _ = x[MemoryKindOptionalStaticType-38] + _ = x[MemoryKindRestrictedStaticType-39] + _ = x[MemoryKindReferenceStaticType-40] + _ = x[MemoryKindCapabilityStaticType-41] + _ = x[MemoryKindFunctionStaticType-42] + _ = x[MemoryKindCadenceVoidValue-43] + _ = x[MemoryKindCadenceOptionalValue-44] + _ = x[MemoryKindCadenceBoolValue-45] + _ = x[MemoryKindCadenceStringValue-46] + _ = x[MemoryKindCadenceCharacterValue-47] + _ = x[MemoryKindCadenceAddressValue-48] + _ = x[MemoryKindCadenceIntValue-49] + _ = x[MemoryKindCadenceNumberValue-50] + _ = x[MemoryKindCadenceArrayValueBase-51] + _ = x[MemoryKindCadenceArrayValueLength-52] + _ = x[MemoryKindCadenceDictionaryValue-53] + _ = x[MemoryKindCadenceKeyValuePair-54] + _ = x[MemoryKindCadenceStructValueBase-55] + _ = x[MemoryKindCadenceStructValueSize-56] + _ = x[MemoryKindCadenceResourceValueBase-57] + _ = x[MemoryKindCadenceAttachmentValueBase-58] + _ = x[MemoryKindCadenceResourceValueSize-59] + _ = x[MemoryKindCadenceAttachmentValueSize-60] + _ = x[MemoryKindCadenceEventValueBase-61] + _ = x[MemoryKindCadenceEventValueSize-62] + _ = x[MemoryKindCadenceContractValueBase-63] + _ = x[MemoryKindCadenceContractValueSize-64] + _ = x[MemoryKindCadenceEnumValueBase-65] + _ = x[MemoryKindCadenceEnumValueSize-66] + _ = x[MemoryKindCadencePathLinkValue-67] + _ = x[MemoryKindCadenceAccountLinkValue-68] + _ = x[MemoryKindCadencePathValue-69] + _ = x[MemoryKindCadenceTypeValue-70] + _ = x[MemoryKindCadenceStorageCapabilityValue-71] + _ = x[MemoryKindCadenceFunctionValue-72] + _ = x[MemoryKindCadenceOptionalType-73] + _ = x[MemoryKindCadenceVariableSizedArrayType-74] + _ = x[MemoryKindCadenceConstantSizedArrayType-75] + _ = x[MemoryKindCadenceDictionaryType-76] + _ = x[MemoryKindCadenceField-77] + _ = x[MemoryKindCadenceParameter-78] + _ = x[MemoryKindCadenceTypeParameter-79] + _ = x[MemoryKindCadenceStructType-80] + _ = x[MemoryKindCadenceResourceType-81] + _ = x[MemoryKindCadenceAttachmentType-82] + _ = x[MemoryKindCadenceEventType-83] + _ = x[MemoryKindCadenceContractType-84] + _ = x[MemoryKindCadenceStructInterfaceType-85] + _ = x[MemoryKindCadenceResourceInterfaceType-86] + _ = x[MemoryKindCadenceContractInterfaceType-87] + _ = x[MemoryKindCadenceFunctionType-88] + _ = x[MemoryKindCadenceReferenceType-89] + _ = x[MemoryKindCadenceRestrictedType-90] + _ = x[MemoryKindCadenceCapabilityType-91] + _ = x[MemoryKindCadenceEnumType-92] + _ = x[MemoryKindRawString-93] + _ = x[MemoryKindAddressLocation-94] + _ = x[MemoryKindBytes-95] + _ = x[MemoryKindVariable-96] + _ = x[MemoryKindCompositeTypeInfo-97] + _ = x[MemoryKindCompositeField-98] + _ = x[MemoryKindInvocation-99] + _ = x[MemoryKindStorageMap-100] + _ = x[MemoryKindStorageKey-101] + _ = x[MemoryKindTypeToken-102] + _ = x[MemoryKindErrorToken-103] + _ = x[MemoryKindSpaceToken-104] + _ = x[MemoryKindProgram-105] + _ = x[MemoryKindIdentifier-106] + _ = x[MemoryKindArgument-107] + _ = x[MemoryKindBlock-108] + _ = x[MemoryKindFunctionBlock-109] + _ = x[MemoryKindParameter-110] + _ = x[MemoryKindParameterList-111] + _ = x[MemoryKindTypeParameter-112] + _ = x[MemoryKindTypeParameterList-113] + _ = x[MemoryKindTransfer-114] + _ = x[MemoryKindMembers-115] + _ = x[MemoryKindTypeAnnotation-116] + _ = x[MemoryKindDictionaryEntry-117] + _ = x[MemoryKindFunctionDeclaration-118] + _ = x[MemoryKindCompositeDeclaration-119] + _ = x[MemoryKindAttachmentDeclaration-120] + _ = x[MemoryKindInterfaceDeclaration-121] + _ = x[MemoryKindEnumCaseDeclaration-122] + _ = x[MemoryKindFieldDeclaration-123] + _ = x[MemoryKindTransactionDeclaration-124] + _ = x[MemoryKindImportDeclaration-125] + _ = x[MemoryKindVariableDeclaration-126] + _ = x[MemoryKindSpecialFunctionDeclaration-127] + _ = x[MemoryKindPragmaDeclaration-128] + _ = x[MemoryKindAssignmentStatement-129] + _ = x[MemoryKindBreakStatement-130] + _ = x[MemoryKindContinueStatement-131] + _ = x[MemoryKindEmitStatement-132] + _ = x[MemoryKindExpressionStatement-133] + _ = x[MemoryKindForStatement-134] + _ = x[MemoryKindIfStatement-135] + _ = x[MemoryKindReturnStatement-136] + _ = x[MemoryKindSwapStatement-137] + _ = x[MemoryKindSwitchStatement-138] + _ = x[MemoryKindWhileStatement-139] + _ = x[MemoryKindRemoveStatement-140] + _ = x[MemoryKindBooleanExpression-141] + _ = x[MemoryKindVoidExpression-142] + _ = x[MemoryKindNilExpression-143] + _ = x[MemoryKindStringExpression-144] + _ = x[MemoryKindIntegerExpression-145] + _ = x[MemoryKindFixedPointExpression-146] + _ = x[MemoryKindArrayExpression-147] + _ = x[MemoryKindDictionaryExpression-148] + _ = x[MemoryKindIdentifierExpression-149] + _ = x[MemoryKindInvocationExpression-150] + _ = x[MemoryKindMemberExpression-151] + _ = x[MemoryKindIndexExpression-152] + _ = x[MemoryKindConditionalExpression-153] + _ = x[MemoryKindUnaryExpression-154] + _ = x[MemoryKindBinaryExpression-155] + _ = x[MemoryKindFunctionExpression-156] + _ = x[MemoryKindCastingExpression-157] + _ = x[MemoryKindCreateExpression-158] + _ = x[MemoryKindDestroyExpression-159] + _ = x[MemoryKindReferenceExpression-160] + _ = x[MemoryKindForceExpression-161] + _ = x[MemoryKindPathExpression-162] + _ = x[MemoryKindAttachExpression-163] + _ = x[MemoryKindConstantSizedType-164] + _ = x[MemoryKindDictionaryType-165] + _ = x[MemoryKindFunctionType-166] + _ = x[MemoryKindInstantiationType-167] + _ = x[MemoryKindNominalType-168] + _ = x[MemoryKindOptionalType-169] + _ = x[MemoryKindReferenceType-170] + _ = x[MemoryKindRestrictedType-171] + _ = x[MemoryKindVariableSizedType-172] + _ = x[MemoryKindPosition-173] + _ = x[MemoryKindRange-174] + _ = x[MemoryKindElaboration-175] + _ = x[MemoryKindActivation-176] + _ = x[MemoryKindActivationEntries-177] + _ = x[MemoryKindVariableSizedSemaType-178] + _ = x[MemoryKindConstantSizedSemaType-179] + _ = x[MemoryKindDictionarySemaType-180] + _ = x[MemoryKindOptionalSemaType-181] + _ = x[MemoryKindRestrictedSemaType-182] + _ = x[MemoryKindReferenceSemaType-183] + _ = x[MemoryKindCapabilitySemaType-184] + _ = x[MemoryKindOrderedMap-185] + _ = x[MemoryKindOrderedMapEntryList-186] + _ = x[MemoryKindOrderedMapEntry-187] + _ = x[MemoryKindLast-188] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCapabilityControllerStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeCapabilityControllerSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 401, 419, 441, 466, 482, 502, 525, 552, 568, 587, 606, 625, 648, 671, 691, 709, 729, 748, 768, 786, 816, 832, 852, 868, 886, 907, 926, 941, 959, 980, 1003, 1025, 1044, 1066, 1088, 1112, 1138, 1162, 1188, 1209, 1230, 1254, 1278, 1298, 1318, 1338, 1361, 1377, 1393, 1422, 1442, 1461, 1490, 1519, 1540, 1552, 1568, 1588, 1605, 1624, 1645, 1661, 1680, 1706, 1734, 1762, 1781, 1801, 1822, 1843, 1858, 1867, 1882, 1887, 1895, 1912, 1926, 1936, 1946, 1956, 1965, 1975, 1985, 1992, 2002, 2010, 2015, 2028, 2037, 2050, 2063, 2080, 2088, 2095, 2109, 2124, 2143, 2163, 2184, 2204, 2223, 2239, 2261, 2278, 2297, 2323, 2340, 2359, 2373, 2390, 2403, 2422, 2434, 2445, 2460, 2473, 2488, 2502, 2517, 2534, 2548, 2561, 2577, 2594, 2614, 2629, 2649, 2669, 2689, 2705, 2720, 2741, 2756, 2772, 2790, 2807, 2823, 2840, 2859, 2874, 2888, 2904, 2921, 2935, 2947, 2964, 2975, 2987, 3000, 3014, 3031, 3039, 3044, 3055, 3065, 3082, 3103, 3124, 3142, 3158, 3176, 3193, 3211, 3239, 3249, 3268, 3283, 3287} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 394, 416, 441, 457, 477, 500, 527, 543, 562, 581, 600, 623, 646, 666, 684, 704, 723, 743, 761, 777, 797, 813, 831, 852, 871, 886, 904, 925, 948, 970, 989, 1011, 1033, 1057, 1083, 1107, 1133, 1154, 1175, 1199, 1223, 1243, 1263, 1283, 1306, 1322, 1338, 1367, 1387, 1406, 1435, 1464, 1485, 1497, 1513, 1533, 1550, 1569, 1590, 1606, 1625, 1651, 1679, 1707, 1726, 1746, 1767, 1788, 1803, 1812, 1827, 1832, 1840, 1857, 1871, 1881, 1891, 1901, 1910, 1920, 1930, 1937, 1947, 1955, 1960, 1973, 1982, 1995, 2008, 2025, 2033, 2040, 2054, 2069, 2088, 2108, 2129, 2149, 2168, 2184, 2206, 2223, 2242, 2268, 2285, 2304, 2318, 2335, 2348, 2367, 2379, 2390, 2405, 2418, 2433, 2447, 2462, 2479, 2493, 2506, 2522, 2539, 2559, 2574, 2594, 2614, 2634, 2650, 2665, 2686, 2701, 2717, 2735, 2752, 2768, 2785, 2804, 2819, 2833, 2849, 2866, 2880, 2892, 2909, 2920, 2932, 2945, 2959, 2976, 2984, 2989, 3000, 3010, 3027, 3048, 3069, 3087, 3103, 3121, 3138, 3156, 3166, 3185, 3200, 3204} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { From cd2c687bf4fe145426b95f6f472bb1f45ee0f84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 10:18:11 -0700 Subject: [PATCH 050/246] re-generate capability controller type definitions --- .../sema/account_capability_controller.gen.go | 75 +++-------- .../sema/storage_capability_controller.gen.go | 119 +++++------------- 2 files changed, 52 insertions(+), 142 deletions(-) diff --git a/runtime/sema/account_capability_controller.gen.go b/runtime/sema/account_capability_controller.gen.go index bd281d5084..a7722ebf06 100644 --- a/runtime/sema/account_capability_controller.gen.go +++ b/runtime/sema/account_capability_controller.gen.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const AccountCapabilityControllerTypeBorrowTypeFieldName = "borrowType" var AccountCapabilityControllerTypeBorrowTypeFieldType = MetaType @@ -75,55 +70,25 @@ var AccountCapabilityControllerType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - AccountCapabilityControllerTypeBorrowTypeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - AccountCapabilityControllerTypeBorrowTypeFieldType, - AccountCapabilityControllerTypeBorrowTypeFieldDocString, - ) - }, - }, - AccountCapabilityControllerTypeCapabilityIDFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - AccountCapabilityControllerTypeCapabilityIDFieldType, - AccountCapabilityControllerTypeCapabilityIDFieldDocString, - ) - }, - }, - AccountCapabilityControllerTypeDeleteFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - AccountCapabilityControllerTypeDeleteFunctionType, - AccountCapabilityControllerTypeDeleteFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + AccountCapabilityControllerTypeBorrowTypeFieldName, + AccountCapabilityControllerTypeBorrowTypeFieldType, + AccountCapabilityControllerTypeBorrowTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + AccountCapabilityControllerTypeCapabilityIDFieldName, + AccountCapabilityControllerTypeCapabilityIDFieldType, + AccountCapabilityControllerTypeCapabilityIDFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + AccountCapabilityControllerTypeDeleteFunctionName, + AccountCapabilityControllerTypeDeleteFunctionType, + AccountCapabilityControllerTypeDeleteFunctionDocString, + ), + }) }, } diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go index 14b76f531d..47ad2b7a25 100644 --- a/runtime/sema/storage_capability_controller.gen.go +++ b/runtime/sema/storage_capability_controller.gen.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const StorageCapabilityControllerTypeBorrowTypeFieldName = "borrowType" var StorageCapabilityControllerTypeBorrowTypeFieldType = MetaType @@ -106,87 +101,37 @@ var StorageCapabilityControllerType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - StorageCapabilityControllerTypeBorrowTypeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StorageCapabilityControllerTypeBorrowTypeFieldType, - StorageCapabilityControllerTypeBorrowTypeFieldDocString, - ) - }, - }, - StorageCapabilityControllerTypeCapabilityIDFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StorageCapabilityControllerTypeCapabilityIDFieldType, - StorageCapabilityControllerTypeCapabilityIDFieldDocString, - ) - }, - }, - StorageCapabilityControllerTypeDeleteFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StorageCapabilityControllerTypeDeleteFunctionType, - StorageCapabilityControllerTypeDeleteFunctionDocString, - ) - }, - }, - StorageCapabilityControllerTypeTargetFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StorageCapabilityControllerTypeTargetFunctionType, - StorageCapabilityControllerTypeTargetFunctionDocString, - ) - }, - }, - StorageCapabilityControllerTypeRetargetFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StorageCapabilityControllerTypeRetargetFunctionType, - StorageCapabilityControllerTypeRetargetFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + StorageCapabilityControllerTypeBorrowTypeFieldName, + StorageCapabilityControllerTypeBorrowTypeFieldType, + StorageCapabilityControllerTypeBorrowTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StorageCapabilityControllerTypeCapabilityIDFieldName, + StorageCapabilityControllerTypeCapabilityIDFieldType, + StorageCapabilityControllerTypeCapabilityIDFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StorageCapabilityControllerTypeDeleteFunctionName, + StorageCapabilityControllerTypeDeleteFunctionType, + StorageCapabilityControllerTypeDeleteFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StorageCapabilityControllerTypeTargetFunctionName, + StorageCapabilityControllerTypeTargetFunctionType, + StorageCapabilityControllerTypeTargetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StorageCapabilityControllerTypeRetargetFunctionName, + StorageCapabilityControllerTypeRetargetFunctionType, + StorageCapabilityControllerTypeRetargetFunctionDocString, + ), + }) }, } From 4e2d48c0544bd9865dab5dbda0351f646fbd84c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 10:18:50 -0700 Subject: [PATCH 051/246] add Auth/PublicAccount.StorageCapabilities/AccountCapabilities types --- runtime/sema/authaccount.cdc | 52 ++++ runtime/sema/authaccount.gen.go | 423 ++++++++++++++++++++++++++ runtime/sema/publicaccount.cdc | 14 + runtime/sema/publicaccount.gen.go | 122 ++++++++ runtime/tests/checker/account_test.go | 127 ++++++++ 5 files changed, 738 insertions(+) diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index df666a5507..f7a35099ee 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -25,6 +25,12 @@ pub struct AuthAccount { /// The inbox allows bootstrapping (sending and receiving) capabilities. pub let inbox: AuthAccount.Inbox + /// The storage capabilities of the account. + let storageCapabilities: &AuthAccount.StorageCapabilities + + /// The account capabilities of the account. + let accountCapabilities: &AuthAccount.AccountCapabilities + /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -294,4 +300,50 @@ pub struct AuthAccount { /// Errors if the Capability under that name does not match the provided type. pub fun claim(_ name: String, provider: Address): Capability? } + + pub struct StorageCapabilities { + /// get returns the storage capability at the given path, if one was stored there. + pub fun get(_ path: PublicPath): Capability? + + /// borrow gets the storage capability at the given path, and borrows the capability if it exists. + /// + /// Returns nil if the capability does not exist or cannot be borrowed using the given type. + /// + /// The function is equivalent to `getCapability(path)?.borrow()`. + pub fun borrow(_ path: PublicPath): T? + + /// Get the storage capability controller for the capability with the specified ID. + /// + /// Returns nil if the ID does not reference an existing storage capability. + pub fun getController(byCapabilityID: UInt64): &StorageCapabilityController? + + /// Get all storage capability controllers for capabilities that target this storage path + pub fun getControllers(forPath: StoragePath): [&StorageCapabilityController] + + /// Iterate through all storage capability controllers for capabilities that target this storage path. + /// + /// Returning false from the function stops the iteration. + pub fun forEachController(forPath: StoragePath, function: ((&StorageCapabilityController): Bool)) + + /// Issue/create a new storage capability. + pub fun issue(_ path: StoragePath): Capability + } + + pub struct AccountCapabilities { + /// Get capability controller for capability with the specified ID. + /// + /// Returns nil if the ID does not reference an existing account capability. + pub fun getController(byCapabilityID: UInt64): &AccountCapabilityController? + + /// Get all capability controllers for all account capabilities. + pub fun getControllers(): [&AccountCapabilityController] + + /// Iterate through all account capability controllers for all account capabilities. + /// + /// Returning false from the function stops the iteration. + pub fun forEachController(_ function: ((&AccountCapabilityController): Bool)) + + /// Issue/create a new account capability. + pub fun issue(): Capability + } } diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index a67ab0ad7f..c8efee8ddb 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -85,6 +85,26 @@ const AuthAccountTypeInboxFieldDocString = ` The inbox allows bootstrapping (sending and receiving) capabilities. ` +const AuthAccountTypeStorageCapabilitiesFieldName = "storageCapabilities" + +var AuthAccountTypeStorageCapabilitiesFieldType = &ReferenceType{ + Type: AuthAccountStorageCapabilitiesType, +} + +const AuthAccountTypeStorageCapabilitiesFieldDocString = ` +The storage capabilities of the account. +` + +const AuthAccountTypeAccountCapabilitiesFieldName = "accountCapabilities" + +var AuthAccountTypeAccountCapabilitiesFieldType = &ReferenceType{ + Type: AuthAccountAccountCapabilitiesType, +} + +const AuthAccountTypeAccountCapabilitiesFieldDocString = ` +The account capabilities of the account. +` + const AuthAccountTypePublicPathsFieldName = "publicPaths" var AuthAccountTypePublicPathsFieldType = &VariableSizedType{ @@ -1174,6 +1194,395 @@ func init() { AuthAccountInboxType.Fields = MembersFieldNames(members) } +const AuthAccountStorageCapabilitiesTypeGetFunctionName = "get" + +var AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountStorageCapabilitiesTypeGetFunctionDocString = ` +get returns the storage capability at the given path, if one was stored there. +` + +const AuthAccountStorageCapabilitiesTypeBorrowFunctionName = "borrow" + +var AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountStorageCapabilitiesTypeBorrowFunctionDocString = ` +borrow gets the storage capability at the given path, and borrows the capability if it exists. + +Returns nil if the capability does not exist or cannot be borrowed using the given type. + +The function is equivalent to ` + "`getCapability(path)?.borrow()`" + `. +` + +const AuthAccountStorageCapabilitiesTypeGetControllerFunctionName = "getController" + +var AuthAccountStorageCapabilitiesTypeGetControllerFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "byCapabilityID", + TypeAnnotation: NewTypeAnnotation(UInt64Type), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &ReferenceType{ + Type: StorageCapabilityControllerType, + }, + }, + ), +} + +const AuthAccountStorageCapabilitiesTypeGetControllerFunctionDocString = ` +Get the storage capability controller for the capability with the specified ID. + +Returns nil if the ID does not reference an existing storage capability. +` + +const AuthAccountStorageCapabilitiesTypeGetControllersFunctionName = "getControllers" + +var AuthAccountStorageCapabilitiesTypeGetControllersFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "forPath", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &VariableSizedType{ + Type: &ReferenceType{ + Type: StorageCapabilityControllerType, + }, + }, + ), +} + +const AuthAccountStorageCapabilitiesTypeGetControllersFunctionDocString = ` +Get all storage capability controllers for capabilities that target this storage path +` + +const AuthAccountStorageCapabilitiesTypeForEachControllerFunctionName = "forEachController" + +var AuthAccountStorageCapabilitiesTypeForEachControllerFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "forPath", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + { + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(&ReferenceType{ + Type: StorageCapabilityControllerType, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountStorageCapabilitiesTypeForEachControllerFunctionDocString = ` +Iterate through all storage capability controllers for capabilities that target this storage path. + +Returning false from the function stops the iteration. +` + +const AuthAccountStorageCapabilitiesTypeIssueFunctionName = "issue" + +var AuthAccountStorageCapabilitiesTypeIssueFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountStorageCapabilitiesTypeIssueFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountStorageCapabilitiesTypeIssueFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountStorageCapabilitiesTypeIssueFunctionTypeParameterT, + }, + ), + ), +} + +const AuthAccountStorageCapabilitiesTypeIssueFunctionDocString = ` +Issue/create a new storage capability. +` + +const AuthAccountStorageCapabilitiesTypeName = "StorageCapabilities" + +var AuthAccountStorageCapabilitiesType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountStorageCapabilitiesTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeGetFunctionName, + AuthAccountStorageCapabilitiesTypeGetFunctionType, + AuthAccountStorageCapabilitiesTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeBorrowFunctionName, + AuthAccountStorageCapabilitiesTypeBorrowFunctionType, + AuthAccountStorageCapabilitiesTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeGetControllerFunctionName, + AuthAccountStorageCapabilitiesTypeGetControllerFunctionType, + AuthAccountStorageCapabilitiesTypeGetControllerFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeGetControllersFunctionName, + AuthAccountStorageCapabilitiesTypeGetControllersFunctionType, + AuthAccountStorageCapabilitiesTypeGetControllersFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeForEachControllerFunctionName, + AuthAccountStorageCapabilitiesTypeForEachControllerFunctionType, + AuthAccountStorageCapabilitiesTypeForEachControllerFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountStorageCapabilitiesType, + AuthAccountStorageCapabilitiesTypeIssueFunctionName, + AuthAccountStorageCapabilitiesTypeIssueFunctionType, + AuthAccountStorageCapabilitiesTypeIssueFunctionDocString, + ), + } + + AuthAccountStorageCapabilitiesType.Members = MembersAsMap(members) + AuthAccountStorageCapabilitiesType.Fields = MembersFieldNames(members) +} + +const AuthAccountAccountCapabilitiesTypeGetControllerFunctionName = "getController" + +var AuthAccountAccountCapabilitiesTypeGetControllerFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "byCapabilityID", + TypeAnnotation: NewTypeAnnotation(UInt64Type), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &ReferenceType{ + Type: AccountCapabilityControllerType, + }, + }, + ), +} + +const AuthAccountAccountCapabilitiesTypeGetControllerFunctionDocString = ` +Get capability controller for capability with the specified ID. + +Returns nil if the ID does not reference an existing account capability. +` + +const AuthAccountAccountCapabilitiesTypeGetControllersFunctionName = "getControllers" + +var AuthAccountAccountCapabilitiesTypeGetControllersFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + &VariableSizedType{ + Type: &ReferenceType{ + Type: AccountCapabilityControllerType, + }, + }, + ), +} + +const AuthAccountAccountCapabilitiesTypeGetControllersFunctionDocString = ` +Get all capability controllers for all account capabilities. +` + +const AuthAccountAccountCapabilitiesTypeForEachControllerFunctionName = "forEachController" + +var AuthAccountAccountCapabilitiesTypeForEachControllerFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(&ReferenceType{ + Type: AccountCapabilityControllerType, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountAccountCapabilitiesTypeForEachControllerFunctionDocString = ` +Iterate through all account capability controllers for all account capabilities. + +Returning false from the function stops the iteration. +` + +const AuthAccountAccountCapabilitiesTypeIssueFunctionName = "issue" + +var AuthAccountAccountCapabilitiesTypeIssueFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AuthAccountType, + }, +} + +var AuthAccountAccountCapabilitiesTypeIssueFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountAccountCapabilitiesTypeIssueFunctionTypeParameterT, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountAccountCapabilitiesTypeIssueFunctionTypeParameterT, + }, + ), + ), +} + +const AuthAccountAccountCapabilitiesTypeIssueFunctionDocString = ` +Issue/create a new account capability. +` + +const AuthAccountAccountCapabilitiesTypeName = "AccountCapabilities" + +var AuthAccountAccountCapabilitiesType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountAccountCapabilitiesTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountAccountCapabilitiesType, + AuthAccountAccountCapabilitiesTypeGetControllerFunctionName, + AuthAccountAccountCapabilitiesTypeGetControllerFunctionType, + AuthAccountAccountCapabilitiesTypeGetControllerFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountAccountCapabilitiesType, + AuthAccountAccountCapabilitiesTypeGetControllersFunctionName, + AuthAccountAccountCapabilitiesTypeGetControllersFunctionType, + AuthAccountAccountCapabilitiesTypeGetControllersFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountAccountCapabilitiesType, + AuthAccountAccountCapabilitiesTypeForEachControllerFunctionName, + AuthAccountAccountCapabilitiesTypeForEachControllerFunctionType, + AuthAccountAccountCapabilitiesTypeForEachControllerFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountAccountCapabilitiesType, + AuthAccountAccountCapabilitiesTypeIssueFunctionName, + AuthAccountAccountCapabilitiesTypeIssueFunctionType, + AuthAccountAccountCapabilitiesTypeIssueFunctionDocString, + ), + } + + AuthAccountAccountCapabilitiesType.Members = MembersAsMap(members) + AuthAccountAccountCapabilitiesType.Fields = MembersFieldNames(members) +} + const AuthAccountTypeName = "AuthAccount" var AuthAccountType = func() *CompositeType { @@ -1187,6 +1596,8 @@ var AuthAccountType = func() *CompositeType { t.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) t.SetNestedType(AuthAccountKeysTypeName, AuthAccountKeysType) t.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) + t.SetNestedType(AuthAccountStorageCapabilitiesTypeName, AuthAccountStorageCapabilitiesType) + t.SetNestedType(AuthAccountAccountCapabilitiesTypeName, AuthAccountAccountCapabilitiesType) return t }() @@ -1240,6 +1651,18 @@ func init() { AuthAccountTypeInboxFieldType, AuthAccountTypeInboxFieldDocString, ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStorageCapabilitiesFieldName, + AuthAccountTypeStorageCapabilitiesFieldType, + AuthAccountTypeStorageCapabilitiesFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeAccountCapabilitiesFieldName, + AuthAccountTypeAccountCapabilitiesFieldType, + AuthAccountTypeAccountCapabilitiesFieldDocString, + ), NewUnmeteredPublicConstantFieldMember( AuthAccountType, AuthAccountTypePublicPathsFieldName, diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc index 940b88b5d4..39f8370f08 100644 --- a/runtime/sema/publicaccount.cdc +++ b/runtime/sema/publicaccount.cdc @@ -22,6 +22,9 @@ pub struct PublicAccount { /// The keys assigned to the account. pub let keys: PublicAccount.Keys + /// The storage capabilities of the account. + let storageCapabilities: &PublicAccount.StorageCapabilities + /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -79,4 +82,15 @@ pub struct PublicAccount { /// The total number of unrevoked keys in this account. pub let count: UInt64 } + + struct StorageCapabilities { + /// get returns the storage capability at the given path, if one was stored there. + fun get(_ path: PublicPath): Capability? + + /// borrow gets the storage capability at the given path, and borrows the capability if it exists. + /// + /// Returns nil if the capability does not exist or cannot be borrowed using the given type. + /// The function is equivalent to `getCapability(path)?.borrow()`. + fun borrow(_ path: PublicPath): T? + } } diff --git a/runtime/sema/publicaccount.gen.go b/runtime/sema/publicaccount.gen.go index a06fc53e78..0a8792684a 100644 --- a/runtime/sema/publicaccount.gen.go +++ b/runtime/sema/publicaccount.gen.go @@ -77,6 +77,16 @@ const PublicAccountTypeKeysFieldDocString = ` The keys assigned to the account. ` +const PublicAccountTypeStorageCapabilitiesFieldName = "storageCapabilities" + +var PublicAccountTypeStorageCapabilitiesFieldType = &ReferenceType{ + Type: PublicAccountStorageCapabilitiesType, +} + +const PublicAccountTypeStorageCapabilitiesFieldDocString = ` +The storage capabilities of the account. +` + const PublicAccountTypePublicPathsFieldName = "publicPaths" var PublicAccountTypePublicPathsFieldType = &VariableSizedType{ @@ -391,6 +401,111 @@ func init() { PublicAccountKeysType.Fields = MembersFieldNames(members) } +const PublicAccountStorageCapabilitiesTypeGetFunctionName = "get" + +var PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + }, + ), + }, + ), +} + +const PublicAccountStorageCapabilitiesTypeGetFunctionDocString = ` +get returns the storage capability at the given path, if one was stored there. +` + +const PublicAccountStorageCapabilitiesTypeBorrowFunctionName = "borrow" + +var PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const PublicAccountStorageCapabilitiesTypeBorrowFunctionDocString = ` +borrow gets the storage capability at the given path, and borrows the capability if it exists. + +Returns nil if the capability does not exist or cannot be borrowed using the given type. +The function is equivalent to ` + "`getCapability(path)?.borrow()`" + `. +` + +const PublicAccountStorageCapabilitiesTypeName = "StorageCapabilities" + +var PublicAccountStorageCapabilitiesType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountStorageCapabilitiesTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + PublicAccountStorageCapabilitiesType, + PublicAccountStorageCapabilitiesTypeGetFunctionName, + PublicAccountStorageCapabilitiesTypeGetFunctionType, + PublicAccountStorageCapabilitiesTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountStorageCapabilitiesType, + PublicAccountStorageCapabilitiesTypeBorrowFunctionName, + PublicAccountStorageCapabilitiesTypeBorrowFunctionType, + PublicAccountStorageCapabilitiesTypeBorrowFunctionDocString, + ), + } + + PublicAccountStorageCapabilitiesType.Members = MembersAsMap(members) + PublicAccountStorageCapabilitiesType.Fields = MembersFieldNames(members) +} + const PublicAccountTypeName = "PublicAccount" var PublicAccountType = func() *CompositeType { @@ -403,6 +518,7 @@ var PublicAccountType = func() *CompositeType { t.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) t.SetNestedType(PublicAccountKeysTypeName, PublicAccountKeysType) + t.SetNestedType(PublicAccountStorageCapabilitiesTypeName, PublicAccountStorageCapabilitiesType) return t }() @@ -450,6 +566,12 @@ func init() { PublicAccountTypeKeysFieldType, PublicAccountTypeKeysFieldDocString, ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeStorageCapabilitiesFieldName, + PublicAccountTypeStorageCapabilitiesFieldType, + PublicAccountTypeStorageCapabilitiesFieldDocString, + ), NewUnmeteredPublicConstantFieldMember( PublicAccountType, PublicAccountTypePublicPathsFieldName, diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index 7f9a6977b9..9ba068eb31 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -2264,3 +2264,130 @@ func TestCheckAccountClaim(t *testing.T) { require.IsType(t, &sema.TypeParameterTypeInferenceError{}, errors[0]) }) } + +func TestCheckStorageCapabilities(t *testing.T) { + + t.Parallel() + + t.Run("AuthAccount.storageCapabilities", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + fun test() { + let capabilities: &AuthAccount.StorageCapabilities = authAccount.storageCapabilities + + let cap: Capability<&Int>? = capabilities.get<&Int>(/public/foo) + + let ref: &Int? = capabilities.borrow<&Int>(/public/foo) + + let controller: &StorageCapabilityController? = capabilities.getController(byCapabilityID: 1) + + let controllers: [&StorageCapabilityController] = capabilities.getControllers(forPath: /storage/foo) + + capabilities.forEachController( + forPath: /storage/bar, + function: fun (controller: &StorageCapabilityController): Bool { + return true + } + ) + + let cap2: Capability<&String> = capabilities.issue<&String>(/storage/baz) + } + `) + require.NoError(t, err) + }) + + t.Run("AuthAccount.accountCapabilities", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + fun test() { + let capabilities: &AuthAccount.AccountCapabilities = authAccount.accountCapabilities + + let controller: &AccountCapabilityController? = capabilities.getController(byCapabilityID: 1) + + let controllers: [&AccountCapabilityController] = capabilities.getControllers() + + capabilities.forEachController(fun (controller: &AccountCapabilityController): Bool { + return true + }) + + let cap: Capability<&AuthAccount> = capabilities.issue<&AuthAccount>() + } + `) + require.NoError(t, err) + }) + + t.Run("PublicAccount.storageCapabilities", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + fun test() { + let capabilities: &PublicAccount.StorageCapabilities = publicAccount.storageCapabilities + + let cap: Capability<&Int>? = capabilities.get<&Int>(/public/foo) + + let ref: &Int? = capabilities.borrow<&Int>(/public/foo) + } + `) + require.NoError(t, err) + }) + + t.Run("PublicAccount.storageCapabilities: invalid", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + fun test() { + let capabilities: &PublicAccount.StorageCapabilities = publicAccount.storageCapabilities + + capabilities.getController(byCapabilityID: 1) + + capabilities.getControllers(forPath: /storage/foo) + + capabilities.forEachController( + forPath: /storage/bar, + function: fun (controller: &StorageCapabilityController): Bool { + return true + } + ) + + capabilities.issue<&String>(/storage/baz) + } + `) + require.Error(t, err) + errors := RequireCheckerErrors(t, err, 4) + + var err1 *sema.NotDeclaredMemberError + require.ErrorAs(t, errors[0], &err1) + assert.Equal(t, "getController", err1.Name) + + var err2 *sema.NotDeclaredMemberError + require.ErrorAs(t, errors[1], &err2) + assert.Equal(t, "getControllers", err2.Name) + + var err3 *sema.NotDeclaredMemberError + require.ErrorAs(t, errors[2], &err3) + assert.Equal(t, "forEachController", err3.Name) + + var err4 *sema.NotDeclaredMemberError + require.ErrorAs(t, errors[3], &err4) + assert.Equal(t, "issue", err4.Name) + }) + + t.Run("PublicAccount.accountCapabilities", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + let capabilitiesRef: &PublicAccount.AccountCapabilities = publicAccount.accountCapabilities + `) + require.Error(t, err) + errors := RequireCheckerErrors(t, err, 2) + require.IsType(t, &sema.NotDeclaredError{}, errors[0]) + require.IsType(t, &sema.NotDeclaredMemberError{}, errors[1]) + }) +} From 43c9c80d9b38bcd496d6dc473934718cb7299cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 12:05:42 -0700 Subject: [PATCH 052/246] add Auth/PublicAccount.Storage/AccountCapabilities values --- runtime/common/metering.go | 47 ++++---- runtime/interpreter/account.go | 29 +++++ .../account_storagecapabilities.go | 113 ++++++++++++++++++ runtime/interpreter/accountcapabilities.go | 70 +++++++++++ runtime/interpreter/accountcontracts.go | 4 +- runtime/interpreter/accountinbox.go | 2 +- runtime/interpreter/accountkeys.go | 2 +- runtime/interpreter/primitivestatictype.go | 20 +++- .../interpreter/primitivestatictype_string.go | 12 +- runtime/interpreter/statictype_test.go | 2 +- runtime/stdlib/account.go | 75 +++++++++++- runtime/tests/interpreter/interpreter_test.go | 30 +++++ .../tests/interpreter/memory_metering_test.go | 22 ++-- 13 files changed, 380 insertions(+), 48 deletions(-) create mode 100644 runtime/interpreter/account_storagecapabilities.go create mode 100644 runtime/interpreter/accountcapabilities.go diff --git a/runtime/common/metering.go b/runtime/common/metering.go index a45e8b40cb..02bdb0c524 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -230,28 +230,31 @@ var ( // Following are the known memory usage amounts for string representation of interpreter values. // Same as `len(format.X)`. However, values are hard-coded to avoid the circular dependency. - VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) - TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) - FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) - TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) - NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) - StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) - AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) - SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) - AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) - HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) - AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) - PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) - AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) - PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) - AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) - PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) - AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) - StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) - AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) - PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) - PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) + VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) + TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) + FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) + TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) + NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) + StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) + AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) + SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) + AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) + HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) + AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) + PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) + AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) + PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) + AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) + PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) + AuthAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.StorageCapabilities()")) + PublicAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.StorageCapabilities()")) + AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) + AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) + StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) + StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) + AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) + PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) + PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) // Static types string representations diff --git a/runtime/interpreter/account.go b/runtime/interpreter/account.go index 5c5cc1279a..36c6298319 100644 --- a/runtime/interpreter/account.go +++ b/runtime/interpreter/account.go @@ -34,6 +34,8 @@ var authAccountFieldNames = []string{ sema.AuthAccountTypeContractsFieldName, sema.AuthAccountTypeKeysFieldName, sema.AuthAccountTypeInboxFieldName, + sema.AuthAccountTypeStorageCapabilitiesFieldName, + sema.AuthAccountTypeAccountCapabilitiesFieldName, } // NewAuthAccountValue constructs an auth account value. @@ -49,6 +51,8 @@ func NewAuthAccountValue( contractsConstructor func() Value, keysConstructor func() Value, inboxConstructor func() Value, + storageCapabilitiesConstructor func() Value, + accountCapabilitiesConstructor func() Value, ) Value { fields := map[string]Value{ @@ -66,6 +70,8 @@ func NewAuthAccountValue( var contracts Value var keys Value var inbox Value + var storageCapabilities Value + var accountCapabilities Value var forEachStoredFunction *HostFunctionValue var forEachPublicFunction *HostFunctionValue var forEachPrivateFunction *HostFunctionValue @@ -86,17 +92,31 @@ func NewAuthAccountValue( contracts = contractsConstructor() } return contracts + case sema.AuthAccountTypeKeysFieldName: if keys == nil { keys = keysConstructor() } return keys + case sema.AuthAccountTypeInboxFieldName: if inbox == nil { inbox = inboxConstructor() } return inbox + case sema.AuthAccountTypeStorageCapabilitiesFieldName: + if storageCapabilities == nil { + storageCapabilities = storageCapabilitiesConstructor() + } + return storageCapabilities + + case sema.AuthAccountTypeAccountCapabilitiesFieldName: + if accountCapabilities == nil { + accountCapabilities = accountCapabilitiesConstructor() + } + return accountCapabilities + case sema.AuthAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) @@ -243,6 +263,7 @@ var publicAccountFieldNames = []string{ sema.PublicAccountTypeAddressFieldName, sema.PublicAccountTypeContractsFieldName, sema.PublicAccountTypeKeysFieldName, + sema.PublicAccountTypeStorageCapabilitiesFieldName, } // NewPublicAccountValue constructs a public account value. @@ -255,6 +276,7 @@ func NewPublicAccountValue( storageCapacityGet func(interpreter *Interpreter) UInt64Value, keysConstructor func() Value, contractsConstructor func() Value, + storageCapabilitiesConstructor func() Value, ) Value { fields := map[string]Value{ @@ -269,6 +291,7 @@ func NewPublicAccountValue( var keys Value var contracts Value + var storageCapabilities Value var forEachPublicFunction *HostFunctionValue var getLinkTargetFunction *HostFunctionValue @@ -286,6 +309,12 @@ func NewPublicAccountValue( } return contracts + case sema.PublicAccountTypeStorageCapabilitiesFieldName: + if storageCapabilities == nil { + storageCapabilities = storageCapabilitiesConstructor() + } + return storageCapabilities + case sema.PublicAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) diff --git a/runtime/interpreter/account_storagecapabilities.go b/runtime/interpreter/account_storagecapabilities.go new file mode 100644 index 0000000000..9d48dfd085 --- /dev/null +++ b/runtime/interpreter/account_storagecapabilities.go @@ -0,0 +1,113 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" +) + +// AuthAccount.StorageCapabilities + +var authAccountStorageCapabilitiesTypeID = sema.AuthAccountStorageCapabilitiesType.ID() +var authAccountStorageCapabilitiesStaticType StaticType = PrimitiveStaticTypeAuthAccountStorageCapabilities // unmetered +var authAccountStorageCapabilitiesFieldNames []string = nil + +func NewAuthAccountStorageCapabilitiesValue( + gauge common.MemoryGauge, + address AddressValue, + getFunction FunctionValue, + borrowFunction FunctionValue, + getControllerFunction FunctionValue, + getControllersFunction FunctionValue, + forEachControllerFunction FunctionValue, + issueFunction FunctionValue, +) Value { + + fields := map[string]Value{ + sema.AuthAccountStorageCapabilitiesTypeGetFunctionName: getFunction, + sema.AuthAccountStorageCapabilitiesTypeBorrowFunctionName: borrowFunction, + sema.AuthAccountStorageCapabilitiesTypeGetControllerFunctionName: getControllerFunction, + sema.AuthAccountStorageCapabilitiesTypeGetControllersFunctionName: getControllersFunction, + sema.AuthAccountStorageCapabilitiesTypeForEachControllerFunctionName: forEachControllerFunction, + sema.AuthAccountStorageCapabilitiesTypeIssueFunctionName: issueFunction, + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.AuthAccountStorageCapabilitiesStringMemoryUsage) + addressStr := address.MeteredString(memoryGauge, seenReferences) + str = fmt.Sprintf("AuthAccount.StorageCapabilities(%s)", addressStr) + } + return str + } + + return NewSimpleCompositeValue( + gauge, + authAccountStorageCapabilitiesTypeID, + authAccountStorageCapabilitiesStaticType, + authAccountStorageCapabilitiesFieldNames, + fields, + nil, + nil, + stringer, + ) +} + +// PublicAccount.StorageCapabilities + +var publicAccountStorageCapabilitiesTypeID = sema.PublicAccountStorageCapabilitiesType.ID() +var publicAccountStorageCapabilitiesStaticType StaticType = PrimitiveStaticTypePublicAccountStorageCapabilities + +func NewPublicAccountStorageCapabilitiesValue( + gauge common.MemoryGauge, + address AddressValue, + getFunction FunctionValue, + borrowFunction FunctionValue, +) Value { + + fields := map[string]Value{ + sema.PublicAccountStorageCapabilitiesTypeGetFunctionName: getFunction, + sema.PublicAccountStorageCapabilitiesTypeBorrowFunctionName: borrowFunction, + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.PublicAccountStorageCapabilitiesStringMemoryUsage) + addressStr := address.MeteredString(memoryGauge, seenReferences) + str = fmt.Sprintf("PublicAccount.StorageCapabilities(%s)", addressStr) + } + return str + } + + return NewSimpleCompositeValue( + gauge, + publicAccountStorageCapabilitiesTypeID, + publicAccountStorageCapabilitiesStaticType, + nil, + fields, + nil, + nil, + stringer, + ) +} diff --git a/runtime/interpreter/accountcapabilities.go b/runtime/interpreter/accountcapabilities.go new file mode 100644 index 0000000000..b884a55e53 --- /dev/null +++ b/runtime/interpreter/accountcapabilities.go @@ -0,0 +1,70 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" +) + +// AuthAccount.AccountCapabilities + +var authAccountAccountCapabilitiesTypeID = sema.AuthAccountAccountCapabilitiesType.ID() +var authAccountAccountCapabilitiesStaticType StaticType = PrimitiveStaticTypeAuthAccountAccountCapabilities // unmetered +var authAccountAccountCapabilitiesFieldNames []string = nil + +func NewAuthAccountAccountCapabilitiesValue( + gauge common.MemoryGauge, + address AddressValue, + getControllerFunction FunctionValue, + getControllersFunction FunctionValue, + forEachControllerFunction FunctionValue, + issueFunction FunctionValue, +) Value { + + fields := map[string]Value{ + sema.AuthAccountAccountCapabilitiesTypeGetControllerFunctionName: getControllerFunction, + sema.AuthAccountAccountCapabilitiesTypeGetControllersFunctionName: getControllersFunction, + sema.AuthAccountAccountCapabilitiesTypeForEachControllerFunctionName: forEachControllerFunction, + sema.AuthAccountAccountCapabilitiesTypeIssueFunctionName: issueFunction, + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.AuthAccountAccountCapabilitiesStringMemoryUsage) + addressStr := address.MeteredString(memoryGauge, seenReferences) + str = fmt.Sprintf("AuthAccount.AccountCapabilities(%s)", addressStr) + } + return str + } + + return NewSimpleCompositeValue( + gauge, + authAccountAccountCapabilitiesTypeID, + authAccountAccountCapabilitiesStaticType, + authAccountAccountCapabilitiesFieldNames, + fields, + nil, + nil, + stringer, + ) +} diff --git a/runtime/interpreter/accountcontracts.go b/runtime/interpreter/accountcontracts.go index 57a58ad01d..47daddd3df 100644 --- a/runtime/interpreter/accountcontracts.go +++ b/runtime/interpreter/accountcontracts.go @@ -25,7 +25,7 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -// AuthAccountContractsValue +// AuthAccount.Contracts var authAccountContractsTypeID = sema.AuthAccountContractsType.ID() var authAccountContractsStaticType StaticType = PrimitiveStaticTypeAuthAccountContracts // unmetered @@ -86,7 +86,7 @@ func NewAuthAccountContractsValue( ) } -// PublicAccountContractsValue +// PublicAccount.Contracts var publicAccountContractsTypeID = sema.PublicAccountContractsType.ID() var publicAccountContractsStaticType StaticType = PrimitiveStaticTypePublicAccountContracts diff --git a/runtime/interpreter/accountinbox.go b/runtime/interpreter/accountinbox.go index 61bfa7c89d..2e10c78e82 100644 --- a/runtime/interpreter/accountinbox.go +++ b/runtime/interpreter/accountinbox.go @@ -25,7 +25,7 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -// AuthAccountInbox +// AuthAccount.Inbox var authAccountInboxTypeID = sema.AuthAccountInboxType.ID() var authAccountInboxStaticType StaticType = PrimitiveStaticTypeAuthAccountInbox diff --git a/runtime/interpreter/accountkeys.go b/runtime/interpreter/accountkeys.go index 8f5bbe52e1..a60ab309b6 100644 --- a/runtime/interpreter/accountkeys.go +++ b/runtime/interpreter/accountkeys.go @@ -25,7 +25,7 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -// AuthAccountKeys +// AuthAccount.Keys var authAccountKeysTypeID = sema.AuthAccountKeysType.ID() var authAccountKeysStaticType StaticType = PrimitiveStaticTypeAuthAccountKeys diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index 32af27f151..0c73d80020 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -202,6 +202,9 @@ const ( PrimitiveStaticTypeAuthAccountInbox PrimitiveStaticTypeStorageCapabilityController PrimitiveStaticTypeAccountCapabilityController + PrimitiveStaticTypeAuthAccountStorageCapabilities + PrimitiveStaticTypeAuthAccountAccountCapabilities + PrimitiveStaticTypePublicAccountStorageCapabilities // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -284,7 +287,10 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypePublicAccountKeys, PrimitiveStaticTypeAccountKey, PrimitiveStaticTypeStorageCapabilityController, - PrimitiveStaticTypeAccountCapabilityController: + PrimitiveStaticTypeAccountCapabilityController, + PrimitiveStaticTypeAuthAccountStorageCapabilities, + PrimitiveStaticTypeAuthAccountAccountCapabilities, + PrimitiveStaticTypePublicAccountStorageCapabilities: return UnknownElementSize } return UnknownElementSize @@ -431,6 +437,12 @@ func (i PrimitiveStaticType) SemaType() sema.Type { return sema.StorageCapabilityControllerType case PrimitiveStaticTypeAccountCapabilityController: return sema.AccountCapabilityControllerType + case PrimitiveStaticTypeAuthAccountStorageCapabilities: + return sema.AuthAccountStorageCapabilitiesType + case PrimitiveStaticTypeAuthAccountAccountCapabilities: + return sema.AuthAccountAccountCapabilitiesType + case PrimitiveStaticTypePublicAccountStorageCapabilities: + return sema.PublicAccountStorageCapabilitiesType default: panic(errors.NewUnexpectedError("missing case for %s", i)) } @@ -567,6 +579,12 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeStorageCapabilityController case sema.AccountCapabilityControllerType: typ = PrimitiveStaticTypeAccountCapabilityController + case sema.AuthAccountStorageCapabilitiesType: + typ = PrimitiveStaticTypeAuthAccountStorageCapabilities + case sema.AuthAccountAccountCapabilitiesType: + typ = PrimitiveStaticTypeAuthAccountAccountCapabilities + case sema.PublicAccountStorageCapabilitiesType: + typ = PrimitiveStaticTypePublicAccountStorageCapabilities } switch t.(type) { diff --git a/runtime/interpreter/primitivestatictype_string.go b/runtime/interpreter/primitivestatictype_string.go index b86f8bc2c5..e9fa557965 100644 --- a/runtime/interpreter/primitivestatictype_string.go +++ b/runtime/interpreter/primitivestatictype_string.go @@ -63,10 +63,13 @@ func _() { _ = x[PrimitiveStaticTypeAuthAccountInbox-98] _ = x[PrimitiveStaticTypeStorageCapabilityController-99] _ = x[PrimitiveStaticTypeAccountCapabilityController-100] - _ = x[PrimitiveStaticType_Count-101] + _ = x[PrimitiveStaticTypeAuthAccountStorageCapabilities-101] + _ = x[PrimitiveStaticTypeAuthAccountAccountCapabilities-102] + _ = x[PrimitiveStaticTypePublicAccountStorageCapabilities-103] + _ = x[PrimitiveStaticType_Count-104] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityController_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityControllerAuthAccountStorageCapabilitiesAuthAccountAccountCapabilitiesPublicAccountStorageCapabilities_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 0: _PrimitiveStaticType_name[0:7], @@ -124,7 +127,10 @@ var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 98: _PrimitiveStaticType_name[435:451], 99: _PrimitiveStaticType_name[451:478], 100: _PrimitiveStaticType_name[478:505], - 101: _PrimitiveStaticType_name[505:511], + 101: _PrimitiveStaticType_name[505:535], + 102: _PrimitiveStaticType_name[535:565], + 103: _PrimitiveStaticType_name[565:597], + 104: _PrimitiveStaticType_name[597:603], } func (i PrimitiveStaticType) String() string { diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index fe1fede2c1..2c8f8d4e51 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -969,6 +969,6 @@ func TestPrimitiveStaticTypeCount(t *testing.T) { // (before the PrimitiveStaticType_Count of course). // Only update this test if you are certain your change to this enum was to append new types to the end. t.Run("No new types added in between", func(t *testing.T) { - require.Equal(t, byte(101), byte(PrimitiveStaticType_Count)) + require.Equal(t, byte(104), byte(PrimitiveStaticType_Count)) }) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 38b25e0c23..44ef3c0b38 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -215,6 +215,18 @@ func NewAuthAccountValue( addressValue, ) }, + func() interpreter.Value { + return newAuthAccountStorageCapabilitiesValue( + gauge, + addressValue, + ) + }, + func() interpreter.Value { + return newAuthAccountAccountCapabilitiesValue( + gauge, + addressValue, + ) + }, ) } @@ -2081,10 +2093,24 @@ func NewPublicAccountValue( newStorageUsedGetFunction(handler, addressValue), newStorageCapacityGetFunction(handler, addressValue), func() interpreter.Value { - return newPublicAccountKeysValue(gauge, handler, addressValue) + return newPublicAccountKeysValue( + gauge, + handler, + addressValue, + ) + }, + func() interpreter.Value { + return newPublicAccountContractsValue( + gauge, + handler, + addressValue, + ) }, func() interpreter.Value { - return newPublicAccountContractsValue(gauge, handler, addressValue) + return newPublicAccountStorageCapabilitiesValue( + gauge, + addressValue, + ) }, ) } @@ -2146,3 +2172,48 @@ func CodeToHashValue(inter *interpreter.Interpreter, code []byte) *interpreter.A codeHash := sha3.Sum256(code) return interpreter.ByteSliceToByteArrayValue(inter, codeHash[:]) } + +func newAuthAccountStorageCapabilitiesValue( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.Value { + // TODO: + return interpreter.NewAuthAccountStorageCapabilitiesValue( + gauge, + addressValue, + nil, + nil, + nil, + nil, + nil, + nil, + ) +} + +func newAuthAccountAccountCapabilitiesValue( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.Value { + // TODO: + return interpreter.NewAuthAccountAccountCapabilitiesValue( + gauge, + addressValue, + nil, + nil, + nil, + nil, + ) +} + +func newPublicAccountStorageCapabilitiesValue( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.Value { + // TODO: + return interpreter.NewPublicAccountStorageCapabilitiesValue( + gauge, + addressValue, + nil, + nil, + ) +} diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index ecf8fb6856..28403d88fc 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9157,6 +9157,28 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. panicFunctionValue, ) }, + func() interpreter.Value { + return interpreter.NewAuthAccountStorageCapabilitiesValue( + gauge, + addressValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + ) + }, + func() interpreter.Value { + return interpreter.NewAuthAccountAccountCapabilitiesValue( + gauge, + addressValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + ) + }, ) } @@ -9203,6 +9225,14 @@ func newTestPublicAccountValue(gauge common.MemoryGauge, addressValue interprete }, ) }, + func() interpreter.Value { + return interpreter.NewPublicAccountStorageCapabilitiesValue( + gauge, + addressValue, + panicFunctionValue, + panicFunctionValue, + ) + }, ) } diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index d6b09fc7ad..8f3a1a3a96 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -9128,29 +9128,21 @@ func TestInterpretStaticTypeStringConversion(t *testing.T) { t.Run("Primitive static types", func(t *testing.T) { t.Parallel() - for staticType, typeName := range interpreter.PrimitiveStaticTypes { - switch staticType { - case interpreter.PrimitiveStaticTypeUnknown, - interpreter.PrimitiveStaticTypeAny, - interpreter.PrimitiveStaticTypeAuthAccountContracts, - interpreter.PrimitiveStaticTypePublicAccountContracts, - interpreter.PrimitiveStaticTypeAuthAccountKeys, - interpreter.PrimitiveStaticTypePublicAccountKeys, - interpreter.PrimitiveStaticTypeAuthAccountInbox, - interpreter.PrimitiveStaticTypeAccountKey, + for primitiveStaticType := range interpreter.PrimitiveStaticTypes { + + switch primitiveStaticType { + case interpreter.PrimitiveStaticTypeAny, + interpreter.PrimitiveStaticTypeUnknown, interpreter.PrimitiveStaticType_Count: continue - case interpreter.PrimitiveStaticTypeAnyResource: - typeName = "@" + typeName - case interpreter.PrimitiveStaticTypeMetaType: - typeName = "Type" } script := fmt.Sprintf(` pub fun main() { log(Type<%s>()) }`, - typeName, + sema.NewTypeAnnotation(primitiveStaticType.SemaType()). + QualifiedString(), ) testStaticTypeStringConversion(t, script) From cde2f8830c4efae12bad6a13e5a98a4cbb7bb127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 13:27:22 -0700 Subject: [PATCH 053/246] fix docstrings and add access modifiers --- runtime/sema/authaccount.cdc | 6 +++--- runtime/sema/authaccount.gen.go | 2 +- runtime/sema/publicaccount.cdc | 6 +++--- runtime/sema/publicaccount.gen.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index f7a35099ee..0c6fb565d5 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -26,10 +26,10 @@ pub struct AuthAccount { pub let inbox: AuthAccount.Inbox /// The storage capabilities of the account. - let storageCapabilities: &AuthAccount.StorageCapabilities + pub let storageCapabilities: &AuthAccount.StorageCapabilities /// The account capabilities of the account. - let accountCapabilities: &AuthAccount.AccountCapabilities + pub let accountCapabilities: &AuthAccount.AccountCapabilities /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -309,7 +309,7 @@ pub struct AuthAccount { /// /// Returns nil if the capability does not exist or cannot be borrowed using the given type. /// - /// The function is equivalent to `getCapability(path)?.borrow()`. + /// The function is equivalent to `get(path)?.borrow()`. pub fun borrow(_ path: PublicPath): T? /// Get the storage capability controller for the capability with the specified ID. diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index c8efee8ddb..7b69982593 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -1264,7 +1264,7 @@ borrow gets the storage capability at the given path, and borrows the capability Returns nil if the capability does not exist or cannot be borrowed using the given type. -The function is equivalent to ` + "`getCapability(path)?.borrow()`" + `. +The function is equivalent to ` + "`get(path)?.borrow()`" + `. ` const AuthAccountStorageCapabilitiesTypeGetControllerFunctionName = "getController" diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc index 39f8370f08..e64fa71328 100644 --- a/runtime/sema/publicaccount.cdc +++ b/runtime/sema/publicaccount.cdc @@ -23,7 +23,7 @@ pub struct PublicAccount { pub let keys: PublicAccount.Keys /// The storage capabilities of the account. - let storageCapabilities: &PublicAccount.StorageCapabilities + pub let storageCapabilities: &PublicAccount.StorageCapabilities /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -83,14 +83,14 @@ pub struct PublicAccount { pub let count: UInt64 } - struct StorageCapabilities { + pub struct StorageCapabilities { /// get returns the storage capability at the given path, if one was stored there. fun get(_ path: PublicPath): Capability? /// borrow gets the storage capability at the given path, and borrows the capability if it exists. /// /// Returns nil if the capability does not exist or cannot be borrowed using the given type. - /// The function is equivalent to `getCapability(path)?.borrow()`. + /// The function is equivalent to `get(path)?.borrow()`. fun borrow(_ path: PublicPath): T? } } diff --git a/runtime/sema/publicaccount.gen.go b/runtime/sema/publicaccount.gen.go index 0a8792684a..991d006e94 100644 --- a/runtime/sema/publicaccount.gen.go +++ b/runtime/sema/publicaccount.gen.go @@ -470,7 +470,7 @@ const PublicAccountStorageCapabilitiesTypeBorrowFunctionDocString = ` borrow gets the storage capability at the given path, and borrows the capability if it exists. Returns nil if the capability does not exist or cannot be borrowed using the given type. -The function is equivalent to ` + "`getCapability(path)?.borrow()`" + `. +The function is equivalent to ` + "`get(path)?.borrow()`" + `. ` const PublicAccountStorageCapabilitiesTypeName = "StorageCapabilities" From 1a59936b0786db29ec82a1d1dd9f642157ac9372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 13:27:40 -0700 Subject: [PATCH 054/246] update documentation --- docs/language/accounts.mdx | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index 077d01b589..615025bf2e 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -33,6 +33,9 @@ pub struct PublicAccount { /// The keys assigned to the account. pub let keys: PublicAccount.Keys + /// The storage capabilities of the account. + pub let storageCapabilities: &PublicAccount.StorageCapabilities + /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -90,6 +93,17 @@ pub struct PublicAccount { /// The total number of unrevoked keys in this account. pub let count: UInt64 } + + pub struct StorageCapabilities { + /// get returns the storage capability at the given path, if one was stored there. + fun get(_ path: PublicPath): Capability? + + /// borrow gets the storage capability at the given path, and borrows the capability if it exists. + /// + /// Returns nil if the capability does not exist or cannot be borrowed using the given type. + /// The function is equivalent to `get(path)?.borrow()`. + fun borrow(_ path: PublicPath): T? + } } ``` @@ -139,6 +153,12 @@ pub struct AuthAccount { /// The inbox allows bootstrapping (sending and receiving) capabilities. pub let inbox: AuthAccount.Inbox + /// The storage capabilities of the account. + pub let storageCapabilities: &AuthAccount.StorageCapabilities + + /// The account capabilities of the account. + pub let accountCapabilities: &AuthAccount.AccountCapabilities + /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -408,6 +428,52 @@ pub struct AuthAccount { /// Errors if the Capability under that name does not match the provided type. pub fun claim(_ name: String, provider: Address): Capability? } + + pub struct StorageCapabilities { + /// get returns the storage capability at the given path, if one was stored there. + pub fun get(_ path: PublicPath): Capability? + + /// borrow gets the storage capability at the given path, and borrows the capability if it exists. + /// + /// Returns nil if the capability does not exist or cannot be borrowed using the given type. + /// + /// The function is equivalent to `get(path)?.borrow()`. + pub fun borrow(_ path: PublicPath): T? + + /// Get the storage capability controller for the capability with the specified ID. + /// + /// Returns nil if the ID does not reference an existing storage capability. + pub fun getController(byCapabilityID: UInt64): &StorageCapabilityController? + + /// Get all storage capability controllers for capabilities that target this storage path + pub fun getControllers(forPath: StoragePath): [&StorageCapabilityController] + + /// Iterate through all storage capability controllers for capabilities that target this storage path. + /// + /// Returning false from the function stops the iteration. + pub fun forEachController(forPath: StoragePath, function: ((&StorageCapabilityController): Bool)) + + /// Issue/create a new storage capability. + pub fun issue(_ path: StoragePath): Capability + } + + pub struct AccountCapabilities { + /// Get capability controller for capability with the specified ID. + /// + /// Returns nil if the ID does not reference an existing account capability. + pub fun getController(byCapabilityID: UInt64): &AccountCapabilityController? + + /// Get all capability controllers for all account capabilities. + pub fun getControllers(): [&AccountCapabilityController] + + /// Iterate through all account capability controllers for all account capabilities. + /// + /// Returning false from the function stops the iteration. + pub fun forEachController(_ function: ((&AccountCapabilityController): Bool)) + + /// Issue/create a new account capability. + pub fun issue(): Capability + } } pub struct DeployedContract { From b07bdba73534a25987c3e155c6e496ae64b3e56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 13:52:08 -0700 Subject: [PATCH 055/246] provide references instead of values directly --- runtime/interpreter/value.go | 4 ++-- runtime/stdlib/account.go | 24 ++++++++++++++++--- runtime/tests/interpreter/interpreter_test.go | 24 ++++++++++++++++--- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index b092e6bf05..6088187b41 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17347,12 +17347,12 @@ func NewUnmeteredEphemeralReferenceValue( } func NewEphemeralReferenceValue( - interpreter *Interpreter, + gauge common.MemoryGauge, authorized bool, value Value, borrowedType sema.Type, ) *EphemeralReferenceValue { - common.UseMemory(interpreter, common.EphemeralReferenceValueMemoryUsage) + common.UseMemory(gauge, common.EphemeralReferenceValueMemoryUsage) return NewUnmeteredEphemeralReferenceValue(authorized, value, borrowedType) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 44ef3c0b38..84be260bf1 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -216,16 +216,28 @@ func NewAuthAccountValue( ) }, func() interpreter.Value { - return newAuthAccountStorageCapabilitiesValue( + storageCapabilities := newAuthAccountStorageCapabilitiesValue( gauge, addressValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.AuthAccountTypeStorageCapabilitiesFieldType, + ) }, func() interpreter.Value { - return newAuthAccountAccountCapabilitiesValue( + accountCapabilities := newAuthAccountAccountCapabilitiesValue( gauge, addressValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + accountCapabilities, + sema.AuthAccountTypeAccountCapabilitiesFieldType, + ) }, ) } @@ -2107,10 +2119,16 @@ func NewPublicAccountValue( ) }, func() interpreter.Value { - return newPublicAccountStorageCapabilitiesValue( + storageCapabilities := newPublicAccountStorageCapabilitiesValue( gauge, addressValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.PublicAccountTypeStorageCapabilitiesFieldType, + ) }, ) } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 28403d88fc..10568aafe2 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9158,7 +9158,7 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. ) }, func() interpreter.Value { - return interpreter.NewAuthAccountStorageCapabilitiesValue( + storageCapabilities := interpreter.NewAuthAccountStorageCapabilitiesValue( gauge, addressValue, panicFunctionValue, @@ -9168,9 +9168,15 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. panicFunctionValue, panicFunctionValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.AuthAccountTypeStorageCapabilitiesFieldType, + ) }, func() interpreter.Value { - return interpreter.NewAuthAccountAccountCapabilitiesValue( + accountCapabilities := interpreter.NewAuthAccountAccountCapabilitiesValue( gauge, addressValue, panicFunctionValue, @@ -9178,6 +9184,12 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. panicFunctionValue, panicFunctionValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + accountCapabilities, + sema.AuthAccountTypeAccountCapabilitiesFieldType, + ) }, ) } @@ -9226,12 +9238,18 @@ func newTestPublicAccountValue(gauge common.MemoryGauge, addressValue interprete ) }, func() interpreter.Value { - return interpreter.NewPublicAccountStorageCapabilitiesValue( + storageCapabilities := interpreter.NewPublicAccountStorageCapabilitiesValue( gauge, addressValue, panicFunctionValue, panicFunctionValue, ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.PublicAccountTypeStorageCapabilitiesFieldType, + ) }, ) } From 6abbaa4499e7fa81e06e3cbc497f763e486e5e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 12:19:13 -0700 Subject: [PATCH 056/246] add ID field to Capability type and value --- encoding/json/decode.go | 3 +- encoding/json/encode.go | 2 + encoding/json/encoding_test.go | 14 +- runtime/common/metering.go | 2 +- runtime/convertValues.go | 16 +- runtime/convertValues_test.go | 99 +++++----- runtime/format/capability.go | 5 +- runtime/interpreter/decode.go | 18 +- runtime/interpreter/encode.go | 21 ++- runtime/interpreter/encoding_test.go | 101 ++++++---- runtime/interpreter/interpreter.go | 7 + runtime/interpreter/value.go | 28 ++- runtime/interpreter/value_test.go | 174 +++++++++++------- runtime/runtime_test.go | 11 +- runtime/sema/type.go | 28 ++- runtime/storage_test.go | 12 +- runtime/tests/checker/capability_test.go | 27 +-- runtime/tests/interpreter/account_test.go | 84 +++++---- .../tests/interpreter/dynamic_casting_test.go | 12 +- runtime/tests/interpreter/equality_test.go | 10 +- .../tests/interpreter/memory_metering_test.go | 2 + runtime/tests/interpreter/values_test.go | 11 +- values.go | 25 ++- values_test.go | 13 +- 24 files changed, 470 insertions(+), 255 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index e6492216a3..3cc77191d9 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -1285,8 +1285,9 @@ func (d *Decoder) decodeCapability(valueJSON any) cadence.StorageCapability { return cadence.NewMeteredStorageCapability( d.gauge, - path, + d.decodeUInt64(obj.Get(idKey)), d.decodeAddress(obj.Get(addressKey)), + path, d.decodeType(obj.Get(borrowTypeKey), typeDecodingResults{}), ) } diff --git a/encoding/json/encode.go b/encoding/json/encode.go index dc46bcf7fe..c7088b329e 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -208,6 +208,7 @@ type jsonStorageCapabilityValue struct { Path jsonValue `json:"path"` BorrowType jsonValue `json:"borrowType"` Address string `json:"address"` + ID string `json:"id"` } type jsonFunctionValue struct { @@ -905,6 +906,7 @@ func prepareCapability(capability cadence.StorageCapability) jsonValue { return jsonValueObject{ Type: capabilityTypeStr, Value: jsonStorageCapabilityValue{ + ID: encodeUInt(uint64(capability.ID)), Path: preparePath(capability.Path), Address: encodeBytes(capability.Address.Bytes()), BorrowType: prepareType(capability.BorrowType, typePreparationResults{}), diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 419bb9acb9..0665851ec2 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2525,11 +2525,12 @@ func TestEncodeCapability(t *testing.T) { testEncodeAndDecode( t, - cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), - Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), - BorrowType: cadence.IntType{}, - }, + cadence.NewStorageCapability( + 6, + cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + cadence.NewPath("storage", "foo"), + cadence.IntType{}, + ), // language=json ` { @@ -2545,7 +2546,8 @@ func TestEncodeCapability(t *testing.T) { "borrowType": { "kind": "Int" }, - "address": "0x0000000102030405" + "address": "0x0000000102030405", + "id": "6" } } `, diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 02bdb0c524..548dcbd18d 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -250,7 +250,7 @@ var ( PublicAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.StorageCapabilities()")) AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) + StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 51018e2a56..5ef160de52 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -630,7 +630,10 @@ func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) ca ) } -func exportStorageCapabilityValue(v *interpreter.StorageCapabilityValue, inter *interpreter.Interpreter) cadence.StorageCapability { +func exportStorageCapabilityValue( + v *interpreter.StorageCapabilityValue, + inter *interpreter.Interpreter, +) cadence.StorageCapability { var borrowType sema.Type if v.BorrowType != nil { borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) @@ -638,8 +641,9 @@ func exportStorageCapabilityValue(v *interpreter.StorageCapabilityValue, inter * return cadence.NewMeteredStorageCapability( inter, - exportPathValue(inter, v.Path), + cadence.NewMeteredUInt64(inter, uint64(v.ID)), cadence.NewMeteredAddress(inter, v.Address), + exportPathValue(inter, v.Path), ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), ) } @@ -815,8 +819,9 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) return i.importTypeValue(v.StaticType) case cadence.StorageCapability: return i.importStorageCapability( - v.Path, + v.ID, v.Address, + v.Path, v.BorrowType, ) case cadence.Contract: @@ -1085,8 +1090,9 @@ func (i valueImporter) importTypeValue(v cadence.Type) (interpreter.TypeValue, e } func (i valueImporter) importStorageCapability( - path cadence.Path, + id cadence.UInt64, address cadence.Address, + path cadence.Path, borrowType cadence.Type, ) ( *interpreter.StorageCapabilityValue, @@ -1104,6 +1110,7 @@ func (i valueImporter) importStorageCapability( return interpreter.NewStorageCapabilityValue( inter, + i.importUInt64(id), interpreter.NewAddressValue( inter, common.Address(address), @@ -1111,7 +1118,6 @@ func (i valueImporter) importStorageCapability( i.importPathValue(path), ImportType(inter, borrowType), ), nil - } func (i valueImporter) importOptionalValue( diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 495807409b..c1200fd4f7 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -839,13 +839,15 @@ func TestImportValue(t *testing.T) { }, { label: "Capability (invalid)", - value: cadence.StorageCapability{ - Path: cadence.Path{ + value: cadence.NewStorageCapability( + 3, + cadence.Address{0x1}, + cadence.Path{ Domain: "public", Identifier: "test", }, - BorrowType: cadence.IntType{}, - }, + cadence.IntType{}, + ), expected: nil, }, { @@ -2062,14 +2064,15 @@ func TestExportStorageCapabilityValue(t *testing.T) { t.Run("Int", func(t *testing.T) { - capability := &interpreter.StorageCapabilityValue{ - Address: interpreter.AddressValue{0x1}, - Path: interpreter.PathValue{ + capability := interpreter.NewUnmeteredStorageCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + interpreter.PathValue{ Domain: common.PathDomainStorage, Identifier: "foo", }, - BorrowType: interpreter.PrimitiveStaticTypeInt, - } + interpreter.PrimitiveStaticTypeInt, + ) actual, err := exportValueWithInterpreter( capability, @@ -2079,14 +2082,15 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.StorageCapability{ - Path: cadence.Path{ + expected := cadence.NewStorageCapability( + 3, + cadence.Address{0x1}, + cadence.Path{ Domain: "storage", Identifier: "foo", }, - Address: cadence.Address{0x1}, - BorrowType: cadence.IntType{}, - } + cadence.IntType{}, + ) assert.Equal(t, expected, actual) @@ -2116,14 +2120,15 @@ func TestExportStorageCapabilityValue(t *testing.T) { inter := newTestInterpreter(t) inter.Program = interpreter.ProgramFromChecker(checker) - capability := &interpreter.StorageCapabilityValue{ - Address: interpreter.AddressValue{0x1}, - Path: interpreter.PathValue{ + capability := interpreter.NewUnmeteredStorageCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + interpreter.PathValue{ Domain: common.PathDomainStorage, Identifier: "foo", }, - BorrowType: interpreter.NewCompositeStaticTypeComputeTypeID(inter, TestLocation, "S"), - } + interpreter.NewCompositeStaticTypeComputeTypeID(inter, TestLocation, "S"), + ) actual, err := exportValueWithInterpreter( capability, @@ -2133,31 +2138,34 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.StorageCapability{ - Path: cadence.Path{ + expected := cadence.NewStorageCapability( + 3, + cadence.Address{0x1}, + cadence.Path{ Domain: "storage", Identifier: "foo", }, - Address: cadence.Address{0x1}, - BorrowType: &cadence.StructType{ + &cadence.StructType{ QualifiedIdentifier: "S", Location: TestLocation, Fields: []cadence.Field{}, }, - } + ) assert.Equal(t, expected, actual) }) t.Run("no borrow type", func(t *testing.T) { - capability := &interpreter.StorageCapabilityValue{ - Address: interpreter.AddressValue{0x1}, - Path: interpreter.PathValue{ + capability := interpreter.NewUnmeteredStorageCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + interpreter.PathValue{ Domain: common.PathDomainStorage, Identifier: "foo", }, - } + nil, + ) actual, err := exportValueWithInterpreter( capability, @@ -2167,13 +2175,15 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.StorageCapability{ - Path: cadence.Path{ + expected := cadence.NewStorageCapability( + 3, + cadence.Address{0x1}, + cadence.Path{ Domain: "storage", Identifier: "foo", }, - Address: cadence.Address{0x1}, - } + nil, + ) assert.Equal(t, expected, actual) }) @@ -3930,7 +3940,7 @@ func TestTypeValueImport(t *testing.T) { runtimeInterface := &testRuntimeInterface{ log: func(s string) { - assert.Equal(t, s, "\"Int\"") + assert.Equal(t, "\"Int\"", s) ok = true }, meterMemory: func(_ common.MemoryUsage) error { @@ -4035,7 +4045,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { runtimeInterface := &testRuntimeInterface{ log: func(s string) { - assert.Equal(t, s, "Capability<&Int>(address: 0x0100000000000000, path: /public/foo)") + assert.Equal(t, "Capability<&Int>(id: 0, address: 0x0100000000000000, path: /public/foo)", s) ok = true }, meterMemory: func(_ common.MemoryUsage) error { @@ -4065,14 +4075,15 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() - capabilityValue := cadence.StorageCapability{ - BorrowType: cadence.IntType{}, - Address: cadence.Address{0x1}, - Path: cadence.Path{ + capabilityValue := cadence.NewStorageCapability( + 3, + cadence.Address{0x1}, + cadence.Path{ Domain: common.PathDomainPublic.Identifier(), Identifier: "foo", }, - } + cadence.IntType{}, + ) script := ` pub fun main(s: Capability) { @@ -4113,12 +4124,14 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() capabilityValue := cadence.StorageCapability{ - BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, - Address: cadence.Address{0x1}, + Address: cadence.Address{0x1}, Path: cadence.Path{ Domain: common.PathDomainPrivate.Identifier(), Identifier: "foo", }, + BorrowType: &cadence.ReferenceType{ + Type: cadence.IntType{}, + }, } script := ` @@ -4424,7 +4437,7 @@ func TestRuntimePublicKeyImport(t *testing.T) { require.NoError(t, err) assert.True(t, verifyInvoked) - assert.Equal(t, actual, cadence.NewBool(true)) + assert.Equal(t, cadence.NewBool(true), actual) }) t.Run("Invalid raw public key", func(t *testing.T) { @@ -5255,7 +5268,7 @@ func TestNestedStructArgPassing(t *testing.T) { ) require.NoError(t, err) - assert.Equal(t, value, cadence.NewUInt8(32)) + assert.Equal(t, cadence.NewUInt8(32), value) }) t.Run("invalid interface", func(t *testing.T) { diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 5ee162ff40..2b314ede16 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -22,15 +22,16 @@ import ( "fmt" ) -func StorageCapability(borrowType string, address string, path string) string { +func StorageCapability(borrowType string, id string, address string, path string) string { var typeArgument string if borrowType != "" { typeArgument = fmt.Sprintf("<%s>", borrowType) } return fmt.Sprintf( - "Capability%s(address: %s, path: %s)", + "Capability%s(id: %s, address: %s, path: %s)", typeArgument, + id, address, path, ) diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 8b92cdd801..b0df451fd5 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -943,7 +943,23 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err return nil, errors.NewUnexpectedError("invalid capability borrow type encoding: %w", err) } - return NewStorageCapabilityValue(d.memoryGauge, address, pathValue, borrowType), nil + // Decode ID at array index encodedStorageCapabilityValueIDFieldKey + + id, err := d.decoder.DecodeUint64() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid capability ID: %w", + err, + ) + } + + return NewStorageCapabilityValue( + d.memoryGauge, + UInt64Value(id), + address, + pathValue, + borrowType, + ), nil } func (d StorableDecoder) decodePathLink() (PathLinkValue, error) { diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index a6f3a23509..c27d28ec23 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -725,12 +725,13 @@ const ( // encodedStorageCapabilityValueAddressFieldKey uint64 = 0 // encodedStorageCapabilityValuePathFieldKey uint64 = 1 // encodedStorageCapabilityValueBorrowTypeFieldKey uint64 = 2 + // encodedStorageCapabilityValueIDFieldKey uint64 = 3 // !!! *WARNING* !!! // // encodedStorageCapabilityValueLength MUST be updated when new element is added. // It is used to verify encoded capability length during decoding. - encodedStorageCapabilityValueLength = 3 + encodedStorageCapabilityValueLength = 4 ) // Encode encodes CapabilityStorable as @@ -741,6 +742,7 @@ const ( // encodedStorageCapabilityValueAddressFieldKey: AddressValue(v.Address), // encodedStorageCapabilityValuePathFieldKey: PathValue(v.Path), // encodedStorageCapabilityValueBorrowTypeFieldKey: StaticType(v.BorrowType), +// encodedStorageCapabilityValueIDFieldKey: v.ID, // }, // } func (v *StorageCapabilityValue) Encode(e *atree.Encoder) error { @@ -748,8 +750,8 @@ func (v *StorageCapabilityValue) Encode(e *atree.Encoder) error { err := e.CBOR.EncodeRawBytes([]byte{ // tag number 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, }) if err != nil { return err @@ -768,7 +770,18 @@ func (v *StorageCapabilityValue) Encode(e *atree.Encoder) error { } // Encode borrow type at array index encodedStorageCapabilityValueBorrowTypeFieldKey - return EncodeStaticType(e.CBOR, v.BorrowType) + err = EncodeStaticType(e.CBOR, v.BorrowType) + if err != nil { + return err + } + + // Encode ID at array index encodedStorageCapabilityValueIDFieldKey + err = e.CBOR.EncodeUint64(uint64(v.ID)) + if err != nil { + return err + } + + return nil } // NOTE: NEVER change, only add/increment; ensure uint64 diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index c627f70e91..e78030c05d 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -2889,16 +2889,18 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := &StorageCapabilityValue{ - Address: NewUnmeteredAddressValueFromBytes([]byte{0x2}), - Path: privatePathValue, - } + value := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), + privatePathValue, + nil, + ) encoded := []byte{ // tag 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -2917,6 +2919,8 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x66, 0x6f, 0x6f, // nil 0xf6, + // positive integer 4 + 0x4, } testEncodeDecode(t, @@ -2931,17 +2935,18 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := &StorageCapabilityValue{ - Address: NewUnmeteredAddressValueFromBytes([]byte{0x2}), - Path: privatePathValue, - BorrowType: PrimitiveStaticTypeBool, - } + value := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), + privatePathValue, + PrimitiveStaticTypeBool, + ) encoded := []byte{ // tag 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -2962,6 +2967,8 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0xd8, CBORTagPrimitiveStaticType, // bool 0x6, + // positive integer 4 + 0x4, } testEncodeDecode(t, @@ -2976,16 +2983,18 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := &StorageCapabilityValue{ - Address: NewUnmeteredAddressValueFromBytes([]byte{0x3}), - Path: publicPathValue, - } + value := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + publicPathValue, + nil, + ) encoded := []byte{ // tag 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3004,6 +3013,8 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x62, 0x61, 0x72, // nil 0xf6, + // positive integer 4 + 0x4, } testEncodeDecode(t, @@ -3019,17 +3030,18 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := &StorageCapabilityValue{ - Address: NewUnmeteredAddressValueFromBytes([]byte{0x3}), - Path: publicPathValue, - BorrowType: PrimitiveStaticTypeBool, - } + value := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + publicPathValue, + PrimitiveStaticTypeBool, + ) encoded := []byte{ // tag 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3050,6 +3062,8 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0xd8, CBORTagPrimitiveStaticType, // bool 0x6, + // positive integer 4 + 0x4, } testEncodeDecode(t, @@ -3065,17 +3079,18 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - capabilityValue := &StorageCapabilityValue{ - Address: NewUnmeteredAddressValueFromBytes([]byte{0x3}), - Path: publicPathValue, - BorrowType: PrimitiveStaticTypePublicAccount, - } + capabilityValue := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + publicPathValue, + PrimitiveStaticTypePublicAccount, + ) encoded := []byte{ // tag 0xd8, CBORTagStorageCapabilityValue, - // array, 3 items follow - 0x83, + // array, 4 items follow + 0x84, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3098,6 +3113,8 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x18, // public account (tag) 0x5b, + // positive integer 4 + 0x4, } testEncodeDecode(t, @@ -3133,9 +3150,12 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { } } - expected := &StorageCapabilityValue{ - Path: path, - } + expected := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + path, + nil, + ) testEncodeDecode(t, encodeDecodeTest{ @@ -3177,9 +3197,12 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { } } - expected := &StorageCapabilityValue{ - Path: path, - } + expected := NewUnmeteredStorageCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + path, + nil, + ) testEncodeDecode(t, encodeDecodeTest{ diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 3100d93505..8afac817ad 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3671,6 +3671,9 @@ func (interpreter *Interpreter) authAccountBorrowFunction(addressValue AddressVa ) } +// TODO: +const TodoCapabilityID = 0 + func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValue) *HostFunctionValue { // Converted addresses can be cached and don't have to be recomputed on each function invocation @@ -3731,6 +3734,8 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu interpreter, NewStorageCapabilityValue( interpreter, + // TODO: + TodoCapabilityID, addressValue, newCapabilityPath, borrowStaticType, @@ -3825,6 +3830,8 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr interpreter, NewStorageCapabilityValue( interpreter, + // TODO: + TodoCapabilityID, addressValue, newCapabilityPath, authAccountReferenceStaticType, diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index fa5b43dc97..0c4b8cd215 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17932,6 +17932,8 @@ func accountGetCapabilityFunction( return NewStorageCapabilityValue( gauge, + // TODO: + TodoCapabilityID, addressValue, path, borrowStaticType, @@ -18201,14 +18203,17 @@ type StorageCapabilityValue struct { BorrowType StaticType Path PathValue Address AddressValue + ID UInt64Value } func NewUnmeteredStorageCapabilityValue( + id UInt64Value, address AddressValue, path PathValue, borrowType StaticType, ) *StorageCapabilityValue { return &StorageCapabilityValue{ + ID: id, Address: address, Path: path, BorrowType: borrowType, @@ -18217,13 +18222,14 @@ func NewUnmeteredStorageCapabilityValue( func NewStorageCapabilityValue( memoryGauge common.MemoryGauge, + id UInt64Value, address AddressValue, path PathValue, borrowType StaticType, ) *StorageCapabilityValue { // Constant because its constituents are already metered. common.UseMemory(memoryGauge, common.StorageCapabilityValueMemoryUsage) - return NewUnmeteredStorageCapabilityValue(address, path, borrowType) + return NewUnmeteredStorageCapabilityValue(id, address, path, borrowType) } var _ Value = &StorageCapabilityValue{} @@ -18238,6 +18244,7 @@ func (v *StorageCapabilityValue) Accept(interpreter *Interpreter, visitor Visito } func (v *StorageCapabilityValue) Walk(_ *Interpreter, walkChild func(Value)) { + walkChild(v.ID) walkChild(v.Address) walkChild(v.Path) } @@ -18264,6 +18271,7 @@ func (v *StorageCapabilityValue) RecursiveString(seenReferences SeenReferences) } return format.StorageCapability( borrowType, + v.ID.RecursiveString(seenReferences), v.Address.RecursiveString(seenReferences), v.Path.RecursiveString(seenReferences), ) @@ -18279,6 +18287,7 @@ func (v *StorageCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, s return format.StorageCapability( borrowType, + v.ID.MeteredString(memoryGauge, seenReferences), v.Address.MeteredString(memoryGauge, seenReferences), v.Path.MeteredString(memoryGauge, seenReferences), ) @@ -18304,6 +18313,9 @@ func (v *StorageCapabilityValue) GetMember(interpreter *Interpreter, _ LocationR case sema.CapabilityTypeAddressFieldName: return v.Address + + case sema.CapabilityTypeIDFieldName: + return v.ID } return nil @@ -18343,7 +18355,8 @@ func (v *StorageCapabilityValue) Equal(interpreter *Interpreter, locationRange L return false } - return otherCapability.Address.Equal(interpreter, locationRange, v.Address) && + return otherCapability.ID == v.ID && + otherCapability.Address.Equal(interpreter, locationRange, v.Address) && otherCapability.Path.Equal(interpreter, locationRange, v.Path) } @@ -18387,11 +18400,12 @@ func (v *StorageCapabilityValue) Transfer( } func (v *StorageCapabilityValue) Clone(interpreter *Interpreter) Value { - return &StorageCapabilityValue{ - Address: v.Address.Clone(interpreter).(AddressValue), - Path: v.Path.Clone(interpreter).(PathValue), - BorrowType: v.BorrowType, - } + return NewUnmeteredStorageCapabilityValue( + v.ID, + v.Address.Clone(interpreter).(AddressValue), + v.Path.Clone(interpreter).(PathValue), + v.BorrowType, + ) } func (v *StorageCapabilityValue) DeepRemove(interpreter *Interpreter) { diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index e242ed1d4e..510dc2c9e2 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -1101,25 +1101,28 @@ func TestStringer(t *testing.T) { expected: "Type()", }, "Capability with borrow type": { - value: &StorageCapabilityValue{ - Path: PathValue{ + value: NewUnmeteredStorageCapabilityValue( + 6, + NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), + PathValue{ Domain: common.PathDomainStorage, Identifier: "foo", }, - Address: NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), - BorrowType: PrimitiveStaticTypeInt, - }, - expected: "Capability(address: 0x0000000102030405, path: /storage/foo)", + PrimitiveStaticTypeInt, + ), + expected: "Capability(id: 6, address: 0x0000000102030405, path: /storage/foo)", }, "Capability without borrow type": { - value: &StorageCapabilityValue{ - Path: PathValue{ + value: NewUnmeteredStorageCapabilityValue( + 6, + NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), + PathValue{ Domain: common.PathDomainStorage, Identifier: "foo", }, - Address: NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), - }, - expected: "Capability(address: 0x0000000102030405, path: /storage/foo)", + nil, + ), + expected: "Capability(id: 6, address: 0x0000000102030405, path: /storage/foo)", }, "Recursive ephemeral reference (array)": { value: func() Value { @@ -1709,24 +1712,26 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }).Equal( + PrimitiveStaticTypeInt, + ).Equal( inter, EmptyLocationRange, - &StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }, + PrimitiveStaticTypeInt, + ), ), ) }) @@ -1738,22 +1743,26 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - }).Equal( + nil, + ).Equal( inter, EmptyLocationRange, - &StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - }, + nil, + ), ), ) }) @@ -1765,24 +1774,26 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test1", }, - BorrowType: PrimitiveStaticTypeInt, - }).Equal( + PrimitiveStaticTypeInt, + ).Equal( inter, EmptyLocationRange, - &StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test2", }, - BorrowType: PrimitiveStaticTypeInt, - }, + PrimitiveStaticTypeInt, + ), ), ) }) @@ -1794,24 +1805,26 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }).Equal( + PrimitiveStaticTypeInt, + ).Equal( inter, EmptyLocationRange, - &StorageCapabilityValue{ - Address: AddressValue{0x2}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x2}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }, + PrimitiveStaticTypeInt, + ), ), ) }) @@ -1823,24 +1836,57 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }).Equal( + PrimitiveStaticTypeInt, + ).Equal( inter, EmptyLocationRange, - &StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeString, + PrimitiveStaticTypeString, + ), + ), + ) + }) + + t.Run("different ID", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.False(t, + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ + Domain: common.PathDomainStorage, + Identifier: "test", }, + PrimitiveStaticTypeInt, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredStorageCapabilityValue( + 5, + AddressValue{0x1}, + PathValue{ + Domain: common.PathDomainStorage, + Identifier: "test", + }, + PrimitiveStaticTypeInt, + ), ), ) }) @@ -1852,14 +1898,15 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&StorageCapabilityValue{ - Address: AddressValue{0x1}, - Path: PathValue{ + NewUnmeteredStorageCapabilityValue( + 4, + AddressValue{0x1}, + PathValue{ Domain: common.PathDomainStorage, Identifier: "test", }, - BorrowType: PrimitiveStaticTypeInt, - }).Equal( + PrimitiveStaticTypeInt, + ).Equal( inter, EmptyLocationRange, NewUnmeteredStringValue("test"), @@ -4047,6 +4094,7 @@ func TestValue_ConformsToStaticType(t *testing.T) { test( func(_ *Interpreter) Value { return NewUnmeteredStorageCapabilityValue( + NewUnmeteredUInt64Value(4), NewUnmeteredAddressValueFromBytes(testAddress.Bytes()), NewUnmeteredPathValue(common.PathDomainStorage, "test"), ReferenceStaticType{ diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index c6ecec9733..cc4f4df149 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -7022,13 +7022,16 @@ func TestRuntimeGetCapability(t *testing.T) { require.NoError(t, err) require.Equal(t, - cadence.StorageCapability{ - Address: cadence.BytesToAddress([]byte{0x1}), - Path: cadence.Path{ + cadence.NewStorageCapability( + // TODO: + interpreter.TodoCapabilityID, + cadence.BytesToAddress([]byte{0x1}), + cadence.Path{ Domain: "public", Identifier: "xxx", }, - }, + nil, + ), res, ) }) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9f43d303d6..83c91c2764 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6493,6 +6493,8 @@ func CapabilityTypeCheckFunctionType(borrowType Type) *FunctionType { } } +const CapabilityTypeBorrowFunctionName = "borrow" + const capabilityTypeBorrowFunctionDocString = ` Returns a reference to the object targeted by the capability. @@ -6502,23 +6504,33 @@ If there is an object stored, a reference is returned as an optional, provided i If the stored object cannot be borrowed using the given type, the function panics. ` +const CapabilityTypeCheckFunctionName = "check" + const capabilityTypeCheckFunctionDocString = ` Returns true if the capability currently targets an object that satisfies the given type, i.e. could be borrowed using the given type ` +var CapabilityTypeAddressFieldType = TheAddressType + +const CapabilityTypeAddressFieldName = "address" + const capabilityTypeAddressFieldDocString = ` The address of the capability ` +var CapabilityTypeIDFieldType = UInt64Type + +const CapabilityTypeIDFieldName = "id" + +const capabilityTypeIDFieldDocString = ` +The ID of the capability +` + func (t *CapabilityType) GetMembers() map[string]MemberResolver { t.initializeMemberResolvers() return t.memberResolvers } -const CapabilityTypeBorrowFunctionName = "borrow" -const CapabilityTypeCheckFunctionName = "check" -const CapabilityTypeAddressFieldName = "address" - func (t *CapabilityType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { members := MembersAsResolvers([]*Member{ @@ -6537,9 +6549,15 @@ func (t *CapabilityType) initializeMemberResolvers() { NewUnmeteredPublicConstantFieldMember( t, CapabilityTypeAddressFieldName, - TheAddressType, + CapabilityTypeAddressFieldType, capabilityTypeAddressFieldDocString, ), + NewUnmeteredPublicConstantFieldMember( + t, + CapabilityTypeIDFieldName, + CapabilityTypeIDFieldType, + capabilityTypeIDFieldDocString, + ), }) t.memberResolvers = withBuiltinMembers(t, members) }) diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 72b1e69753..88be70aa81 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -1443,14 +1443,16 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { value, err := runtime.ReadStored(signer, storagePath, context) require.NoError(t, err) - expected := cadence.StorageCapability{ - Path: cadence.Path{ + expected := cadence.NewStorageCapability( + // TODO: + interpreter.TodoCapabilityID, + cadence.Address(signer), + cadence.Path{ Domain: domain.Identifier(), Identifier: "test", }, - Address: cadence.Address(signer), - BorrowType: ty, - } + ty, + ) actual := cadence.ValueWithCachedTypeID(value) require.Equal(t, expected, actual) diff --git a/runtime/tests/checker/capability_test.go b/runtime/tests/checker/capability_test.go index a1853b6fe9..04098945ae 100644 --- a/runtime/tests/checker/capability_test.go +++ b/runtime/tests/checker/capability_test.go @@ -438,16 +438,21 @@ func TestCheckCapability_check(t *testing.T) { func TestCheckCapability_address(t *testing.T) { t.Parallel() - t.Run("check address", func(t *testing.T) { - checker, err := ParseAndCheckWithPanic(t, - ` - let capability: Capability = panic("") - let addr = capability.address - `, - ) - require.NoError(t, err) - addrType := RequireGlobalValue(t, checker.Elaboration, "addr") - require.Equal(t, sema.TheAddressType, addrType) - }) + _, err := ParseAndCheckWithPanic(t, ` + let capability: Capability = panic("") + let addr: Address = capability.address + `) + require.NoError(t, err) +} + +func TestCheckCapability_id(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithPanic(t, ` + let capability: Capability = panic("") + let addr: UInt64 = capability.id + `) + require.NoError(t, err) } diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index f04e438569..5a8cf2e18b 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -966,14 +966,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "rCap", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) @@ -1015,14 +1017,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "rCap2", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) @@ -1119,14 +1123,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "sCap", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) @@ -1169,14 +1175,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "sCap2", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) @@ -1276,14 +1284,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "sCap", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) }) @@ -1358,14 +1368,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "s2Cap", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) @@ -1387,14 +1399,16 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - &interpreter.StorageCapabilityValue{ - Address: address, - Path: interpreter.PathValue{ + interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + address, + interpreter.PathValue{ Domain: capabilityDomain, Identifier: "s1Cap", }, - BorrowType: expectedBorrowType, - }, + expectedBorrowType, + ), capability, ) }) diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index 04874baef5..13ac2ee999 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -3507,16 +3507,18 @@ func TestInterpretDynamicCastingCapability(t *testing.T) { sema.AnyStructType, } - capabilityValue := &interpreter.StorageCapabilityValue{ - Address: interpreter.AddressValue{}, - Path: interpreter.EmptyPathValue, - BorrowType: interpreter.ConvertSemaToStaticType( + capabilityValue := interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + interpreter.AddressValue{}, + interpreter.EmptyPathValue, + interpreter.ConvertSemaToStaticType( nil, &sema.ReferenceType{ Type: structType, }, ), - } + ) capabilityValueDeclaration := stdlib.StandardLibraryValue{ Name: "cap", diff --git a/runtime/tests/interpreter/equality_test.go b/runtime/tests/interpreter/equality_test.go index a509e9549d..83e80ae7ee 100644 --- a/runtime/tests/interpreter/equality_test.go +++ b/runtime/tests/interpreter/equality_test.go @@ -47,13 +47,15 @@ func TestInterpretEquality(t *testing.T) { capabilityValueDeclaration := stdlib.StandardLibraryValue{ Name: "cap", Type: &sema.CapabilityType{}, - Value: &interpreter.StorageCapabilityValue{ - Address: interpreter.NewUnmeteredAddressValueFromBytes([]byte{0x1}), - Path: interpreter.PathValue{ + Value: interpreter.NewUnmeteredStorageCapabilityValue( + 4, + interpreter.NewUnmeteredAddressValueFromBytes([]byte{0x1}), + interpreter.PathValue{ Domain: common.PathDomainStorage, Identifier: "something", }, - }, + nil, + ), Kind: common.DeclarationKindConstant, } diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index 8f3a1a3a96..fc0dcdf260 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -9039,6 +9039,8 @@ func TestInterpretValueStringConversion(t *testing.T) { testValueStringConversion(t, script, interpreter.NewUnmeteredStorageCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, interpreter.AddressValue{1}, interpreter.PathValue{ Domain: common.PathDomainPublic, diff --git a/runtime/tests/interpreter/values_test.go b/runtime/tests/interpreter/values_test.go index 3ad28f0b0a..7f7f633976 100644 --- a/runtime/tests/interpreter/values_test.go +++ b/runtime/tests/interpreter/values_test.go @@ -1143,14 +1143,15 @@ func randomStorableValue(inter *interpreter.Interpreter, currentDepth int) inter case Composite: return randomCompositeValue(inter, common.CompositeKindStructure, currentDepth) case Capability: - return &interpreter.StorageCapabilityValue{ - Address: randomAddressValue(), - Path: randomPathValue(), - BorrowType: interpreter.ReferenceStaticType{ + return interpreter.NewUnmeteredStorageCapabilityValue( + interpreter.UInt64Value(randomInt(math.MaxInt)), + randomAddressValue(), + randomPathValue(), + interpreter.ReferenceStaticType{ Authorized: false, BorrowedType: interpreter.PrimitiveStaticTypeAnyStruct, }, - } + ) case Some: return interpreter.NewUnmeteredSomeValueNonCopying( randomStorableValue(inter, currentDepth+1), diff --git a/values.go b/values.go index dbea97a266..3cc70816e7 100644 --- a/values.go +++ b/values.go @@ -2006,21 +2006,39 @@ type StorageCapability struct { BorrowType Type Path Path Address Address + ID UInt64 } var _ Value = StorageCapability{} -func NewStorageCapability(path Path, address Address, borrowType Type) StorageCapability { +func NewStorageCapability( + id UInt64, + address Address, + path Path, + borrowType Type, +) StorageCapability { return StorageCapability{ + ID: id, Path: path, Address: address, BorrowType: borrowType, } } -func NewMeteredStorageCapability(gauge common.MemoryGauge, path Path, address Address, borrowType Type) StorageCapability { +func NewMeteredStorageCapability( + gauge common.MemoryGauge, + id UInt64, + address Address, + path Path, + borrowType Type, +) StorageCapability { common.UseMemory(gauge, common.CadenceStorageCapabilityValueMemoryUsage) - return NewStorageCapability(path, address, borrowType) + return NewStorageCapability( + id, + address, + path, + borrowType, + ) } func (StorageCapability) isValue() {} @@ -2040,6 +2058,7 @@ func (StorageCapability) ToGoValue() any { func (v StorageCapability) String() string { return format.StorageCapability( v.BorrowType.ID(), + v.ID.String(), v.Address.String(), v.Path.String(), ) diff --git a/values_test.go b/values_test.go index 28f1d54ffb..c95b1fdde3 100644 --- a/values_test.go +++ b/values_test.go @@ -360,13 +360,14 @@ func newValueTestCases() map[string]valueTestCase { string: "Type()", }, "Capability": { - value: StorageCapability{ - Path: Path{Domain: "storage", Identifier: "foo"}, - Address: BytesToAddress([]byte{1, 2, 3, 4, 5}), - BorrowType: IntType{}, - }, + value: NewStorageCapability( + 3, + BytesToAddress([]byte{1, 2, 3, 4, 5}), + Path{Domain: "storage", Identifier: "foo"}, + IntType{}, + ), expectedType: NewCapabilityType(IntType{}), - string: "Capability(address: 0x0000000102030405, path: /storage/foo)", + string: "Capability(id: 3, address: 0x0000000102030405, path: /storage/foo)", }, "Function": { value: NewFunction( From 0572a2cbf72ea6e928c61e442a5577d74cb4b0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 6 Apr 2023 16:12:19 -0700 Subject: [PATCH 057/246] rename files of new value types --- ...ability_controller.go => value_accountcapabilitycontroller.go} | 0 ...ntroller_test.go => value_accountcapabilitycontroller_test.go} | 0 ...ability_controller.go => value_storagecapabilitycontroller.go} | 0 ...ntroller_test.go => value_storagecapabilitycontroller_test.go} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename runtime/interpreter/{account_capability_controller.go => value_accountcapabilitycontroller.go} (100%) rename runtime/interpreter/{account_capability_controller_test.go => value_accountcapabilitycontroller_test.go} (100%) rename runtime/interpreter/{storage_capability_controller.go => value_storagecapabilitycontroller.go} (100%) rename runtime/interpreter/{storage_capability_controller_test.go => value_storagecapabilitycontroller_test.go} (100%) diff --git a/runtime/interpreter/account_capability_controller.go b/runtime/interpreter/value_accountcapabilitycontroller.go similarity index 100% rename from runtime/interpreter/account_capability_controller.go rename to runtime/interpreter/value_accountcapabilitycontroller.go diff --git a/runtime/interpreter/account_capability_controller_test.go b/runtime/interpreter/value_accountcapabilitycontroller_test.go similarity index 100% rename from runtime/interpreter/account_capability_controller_test.go rename to runtime/interpreter/value_accountcapabilitycontroller_test.go diff --git a/runtime/interpreter/storage_capability_controller.go b/runtime/interpreter/value_storagecapabilitycontroller.go similarity index 100% rename from runtime/interpreter/storage_capability_controller.go rename to runtime/interpreter/value_storagecapabilitycontroller.go diff --git a/runtime/interpreter/storage_capability_controller_test.go b/runtime/interpreter/value_storagecapabilitycontroller_test.go similarity index 100% rename from runtime/interpreter/storage_capability_controller_test.go rename to runtime/interpreter/value_storagecapabilitycontroller_test.go From ff1a401390eb8af9618564ed6db2be4ff82b5d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 10 Apr 2023 14:37:54 -0700 Subject: [PATCH 058/246] rename value files --- runtime/interpreter/{account.go => value_account.go} | 0 ...ccountcapabilities.go => value_account_accountcapabilities.go} | 0 .../{accountcontracts.go => value_account_contracts.go} | 0 ...toragecapabilities.go => value_account_storagecapabilities.go} | 0 runtime/interpreter/{accountkey.go => value_accountkey.go} | 0 .../interpreter/{accountinbox.go => value_authaccount_inbox.go} | 0 runtime/interpreter/{accountkeys.go => value_authaccount_keys.go} | 0 runtime/interpreter/{block.go => value_block.go} | 0 .../{deployedcontract.go => value_deployedcontract.go} | 0 runtime/interpreter/{function.go => value_function.go} | 0 runtime/interpreter/{function_test.go => value_function_test.go} | 0 runtime/interpreter/{string.go => value_string.go} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename runtime/interpreter/{account.go => value_account.go} (100%) rename runtime/interpreter/{accountcapabilities.go => value_account_accountcapabilities.go} (100%) rename runtime/interpreter/{accountcontracts.go => value_account_contracts.go} (100%) rename runtime/interpreter/{account_storagecapabilities.go => value_account_storagecapabilities.go} (100%) rename runtime/interpreter/{accountkey.go => value_accountkey.go} (100%) rename runtime/interpreter/{accountinbox.go => value_authaccount_inbox.go} (100%) rename runtime/interpreter/{accountkeys.go => value_authaccount_keys.go} (100%) rename runtime/interpreter/{block.go => value_block.go} (100%) rename runtime/interpreter/{deployedcontract.go => value_deployedcontract.go} (100%) rename runtime/interpreter/{function.go => value_function.go} (100%) rename runtime/interpreter/{function_test.go => value_function_test.go} (100%) rename runtime/interpreter/{string.go => value_string.go} (100%) diff --git a/runtime/interpreter/account.go b/runtime/interpreter/value_account.go similarity index 100% rename from runtime/interpreter/account.go rename to runtime/interpreter/value_account.go diff --git a/runtime/interpreter/accountcapabilities.go b/runtime/interpreter/value_account_accountcapabilities.go similarity index 100% rename from runtime/interpreter/accountcapabilities.go rename to runtime/interpreter/value_account_accountcapabilities.go diff --git a/runtime/interpreter/accountcontracts.go b/runtime/interpreter/value_account_contracts.go similarity index 100% rename from runtime/interpreter/accountcontracts.go rename to runtime/interpreter/value_account_contracts.go diff --git a/runtime/interpreter/account_storagecapabilities.go b/runtime/interpreter/value_account_storagecapabilities.go similarity index 100% rename from runtime/interpreter/account_storagecapabilities.go rename to runtime/interpreter/value_account_storagecapabilities.go diff --git a/runtime/interpreter/accountkey.go b/runtime/interpreter/value_accountkey.go similarity index 100% rename from runtime/interpreter/accountkey.go rename to runtime/interpreter/value_accountkey.go diff --git a/runtime/interpreter/accountinbox.go b/runtime/interpreter/value_authaccount_inbox.go similarity index 100% rename from runtime/interpreter/accountinbox.go rename to runtime/interpreter/value_authaccount_inbox.go diff --git a/runtime/interpreter/accountkeys.go b/runtime/interpreter/value_authaccount_keys.go similarity index 100% rename from runtime/interpreter/accountkeys.go rename to runtime/interpreter/value_authaccount_keys.go diff --git a/runtime/interpreter/block.go b/runtime/interpreter/value_block.go similarity index 100% rename from runtime/interpreter/block.go rename to runtime/interpreter/value_block.go diff --git a/runtime/interpreter/deployedcontract.go b/runtime/interpreter/value_deployedcontract.go similarity index 100% rename from runtime/interpreter/deployedcontract.go rename to runtime/interpreter/value_deployedcontract.go diff --git a/runtime/interpreter/function.go b/runtime/interpreter/value_function.go similarity index 100% rename from runtime/interpreter/function.go rename to runtime/interpreter/value_function.go diff --git a/runtime/interpreter/function_test.go b/runtime/interpreter/value_function_test.go similarity index 100% rename from runtime/interpreter/function_test.go rename to runtime/interpreter/value_function_test.go diff --git a/runtime/interpreter/string.go b/runtime/interpreter/value_string.go similarity index 100% rename from runtime/interpreter/string.go rename to runtime/interpreter/value_string.go From 105b4f3bde0d802d1a0e0b8992445d36c6b98914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 10 Apr 2023 15:19:04 -0700 Subject: [PATCH 059/246] go generate --- runtime/sema/account_capability_controller.gen.go | 7 +++++-- runtime/sema/storage_capability_controller.gen.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/runtime/sema/account_capability_controller.gen.go b/runtime/sema/account_capability_controller.gen.go index a7722ebf06..c224f8fab2 100644 --- a/runtime/sema/account_capability_controller.gen.go +++ b/runtime/sema/account_capability_controller.gen.go @@ -69,7 +69,10 @@ var AccountCapabilityControllerType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + AccountCapabilityControllerType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicConstantFieldMember( t, @@ -90,5 +93,5 @@ var AccountCapabilityControllerType = &SimpleType{ AccountCapabilityControllerTypeDeleteFunctionDocString, ), }) - }, + } } diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go index 47ad2b7a25..bb5684cc22 100644 --- a/runtime/sema/storage_capability_controller.gen.go +++ b/runtime/sema/storage_capability_controller.gen.go @@ -100,7 +100,10 @@ var StorageCapabilityControllerType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + StorageCapabilityControllerType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicConstantFieldMember( t, @@ -133,5 +136,5 @@ var StorageCapabilityControllerType = &SimpleType{ StorageCapabilityControllerTypeRetargetFunctionDocString, ), }) - }, + } } From f7f90fc8e5120105556ea70ce23cdbf532d68111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 10 Apr 2023 16:33:31 -0700 Subject: [PATCH 060/246] prevent overflow --- runtime/tests/interpreter/values_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/interpreter/values_test.go b/runtime/tests/interpreter/values_test.go index 7f7f633976..83242c6f81 100644 --- a/runtime/tests/interpreter/values_test.go +++ b/runtime/tests/interpreter/values_test.go @@ -1144,7 +1144,7 @@ func randomStorableValue(inter *interpreter.Interpreter, currentDepth int) inter return randomCompositeValue(inter, common.CompositeKindStructure, currentDepth) case Capability: return interpreter.NewUnmeteredStorageCapabilityValue( - interpreter.UInt64Value(randomInt(math.MaxInt)), + interpreter.UInt64Value(randomInt(math.MaxInt-1)), randomAddressValue(), randomPathValue(), interpreter.ReferenceStaticType{ From 378a57f53e00391a5381b3c3b795a1302fcfe9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 10 Apr 2023 16:34:15 -0700 Subject: [PATCH 061/246] simplify Co-authored-by: Supun Setunga --- runtime/interpreter/encode.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index c27d28ec23..523e93ee00 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -776,12 +776,7 @@ func (v *StorageCapabilityValue) Encode(e *atree.Encoder) error { } // Encode ID at array index encodedStorageCapabilityValueIDFieldKey - err = e.CBOR.EncodeUint64(uint64(v.ID)) - if err != nil { - return err - } - - return nil + return e.CBOR.EncodeUint64(uint64(v.ID)) } // NOTE: NEVER change, only add/increment; ensure uint64 From 4b6d02ececbd014ae6efd3b97e619d857c378bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 10 Apr 2023 16:46:00 -0700 Subject: [PATCH 062/246] go fmt --- values_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/values_test.go b/values_test.go index 2229a02166..696d8eb7d1 100644 --- a/values_test.go +++ b/values_test.go @@ -381,7 +381,7 @@ func newValueTestCases() map[string]valueTestCase { 3, BytesToAddress([]byte{1, 2, 3, 4, 5}), Path{ - Domain: common.PathDomainStorage, + Domain: common.PathDomainStorage, Identifier: "foo", }, IntType{}, From 38f6b76eb266a9140a546ec3bf42b254e67210e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 14 Apr 2023 13:23:27 -0700 Subject: [PATCH 063/246] update to latest proposal. add deprecation notices. optimize Auth/PublicAccount.getCapability --- runtime/common/metering.go | 51 +++-- runtime/interpreter/primitivestatictype.go | 18 +- .../interpreter/primitivestatictype_string.go | 12 +- runtime/interpreter/statictype_test.go | 2 +- runtime/interpreter/value.go | 58 ----- runtime/interpreter/value_account.go | 131 ++++++++--- .../interpreter/value_account_capabilities.go | 131 +++++++++++ .../value_account_storagecapabilities.go | 43 ---- runtime/sema/authaccount.cdc | 52 ++++- runtime/sema/authaccount.gen.go | 215 +++++++++++++----- runtime/sema/publicaccount.cdc | 10 +- runtime/sema/publicaccount.gen.go | 72 +++--- runtime/stdlib/account.go | 69 ++++-- runtime/tests/checker/account_test.go | 73 +++--- runtime/tests/interpreter/interpreter_test.go | 66 +++--- .../tests/interpreter/memory_metering_test.go | 4 +- 16 files changed, 641 insertions(+), 366 deletions(-) create mode 100644 runtime/interpreter/value_account_capabilities.go diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 548dcbd18d..196d1bfc2e 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -230,31 +230,32 @@ var ( // Following are the known memory usage amounts for string representation of interpreter values. // Same as `len(format.X)`. However, values are hard-coded to avoid the circular dependency. - VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) - TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) - FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) - TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) - NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) - StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) - AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) - SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) - AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) - HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) - AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) - PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) - AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) - PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) - AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) - PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) - AuthAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.StorageCapabilities()")) - PublicAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.StorageCapabilities()")) - AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) - AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) - StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) - AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) - PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) - PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) + VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) + TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) + FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) + TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) + NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) + StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) + AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) + SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) + AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) + HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) + AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) + PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) + AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) + PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) + AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) + PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) + AuthAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.StorageCapabilities()")) + AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) + AuthAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Capabilities()")) + PublicAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Capabilities()")) + AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) + StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) + StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) + AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) + PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) + PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) // Static types string representations diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index 0c73d80020..2f26c192ee 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -204,7 +204,8 @@ const ( PrimitiveStaticTypeAccountCapabilityController PrimitiveStaticTypeAuthAccountStorageCapabilities PrimitiveStaticTypeAuthAccountAccountCapabilities - PrimitiveStaticTypePublicAccountStorageCapabilities + PrimitiveStaticTypeAuthAccountCapabilities + PrimitiveStaticTypePublicAccountCapabilities // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -290,7 +291,8 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAccountCapabilityController, PrimitiveStaticTypeAuthAccountStorageCapabilities, PrimitiveStaticTypeAuthAccountAccountCapabilities, - PrimitiveStaticTypePublicAccountStorageCapabilities: + PrimitiveStaticTypeAuthAccountCapabilities, + PrimitiveStaticTypePublicAccountCapabilities: return UnknownElementSize } return UnknownElementSize @@ -441,8 +443,10 @@ func (i PrimitiveStaticType) SemaType() sema.Type { return sema.AuthAccountStorageCapabilitiesType case PrimitiveStaticTypeAuthAccountAccountCapabilities: return sema.AuthAccountAccountCapabilitiesType - case PrimitiveStaticTypePublicAccountStorageCapabilities: - return sema.PublicAccountStorageCapabilitiesType + case PrimitiveStaticTypeAuthAccountCapabilities: + return sema.AuthAccountCapabilitiesType + case PrimitiveStaticTypePublicAccountCapabilities: + return sema.PublicAccountCapabilitiesType default: panic(errors.NewUnexpectedError("missing case for %s", i)) } @@ -583,8 +587,10 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeAuthAccountStorageCapabilities case sema.AuthAccountAccountCapabilitiesType: typ = PrimitiveStaticTypeAuthAccountAccountCapabilities - case sema.PublicAccountStorageCapabilitiesType: - typ = PrimitiveStaticTypePublicAccountStorageCapabilities + case sema.AuthAccountCapabilitiesType: + typ = PrimitiveStaticTypeAuthAccountCapabilities + case sema.PublicAccountCapabilitiesType: + typ = PrimitiveStaticTypePublicAccountCapabilities } switch t.(type) { diff --git a/runtime/interpreter/primitivestatictype_string.go b/runtime/interpreter/primitivestatictype_string.go index e9fa557965..90d5dc8abb 100644 --- a/runtime/interpreter/primitivestatictype_string.go +++ b/runtime/interpreter/primitivestatictype_string.go @@ -65,11 +65,12 @@ func _() { _ = x[PrimitiveStaticTypeAccountCapabilityController-100] _ = x[PrimitiveStaticTypeAuthAccountStorageCapabilities-101] _ = x[PrimitiveStaticTypeAuthAccountAccountCapabilities-102] - _ = x[PrimitiveStaticTypePublicAccountStorageCapabilities-103] - _ = x[PrimitiveStaticType_Count-104] + _ = x[PrimitiveStaticTypeAuthAccountCapabilities-103] + _ = x[PrimitiveStaticTypePublicAccountCapabilities-104] + _ = x[PrimitiveStaticType_Count-105] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityControllerAuthAccountStorageCapabilitiesAuthAccountAccountCapabilitiesPublicAccountStorageCapabilities_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockNumberSignedNumberIntegerSignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Fix64UFix64PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityControllerAuthAccountStorageCapabilitiesAuthAccountAccountCapabilitiesAuthAccountCapabilitiesPublicAccountCapabilities_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 0: _PrimitiveStaticType_name[0:7], @@ -129,8 +130,9 @@ var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 100: _PrimitiveStaticType_name[478:505], 101: _PrimitiveStaticType_name[505:535], 102: _PrimitiveStaticType_name[535:565], - 103: _PrimitiveStaticType_name[565:597], - 104: _PrimitiveStaticType_name[597:603], + 103: _PrimitiveStaticType_name[565:588], + 104: _PrimitiveStaticType_name[588:613], + 105: _PrimitiveStaticType_name[613:619], } func (i PrimitiveStaticType) String() string { diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index 2c8f8d4e51..f77619b121 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -969,6 +969,6 @@ func TestPrimitiveStaticTypeCount(t *testing.T) { // (before the PrimitiveStaticType_Count of course). // Only update this test if you are certain your change to this enum was to append new types to the end. t.Run("No new types added in between", func(t *testing.T) { - require.Equal(t, byte(104), byte(PrimitiveStaticType_Count)) + require.Equal(t, byte(105), byte(PrimitiveStaticType_Count)) }) } diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index aa50811eae..8791f15f2f 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17895,64 +17895,6 @@ func (AddressValue) ChildStorables() []atree.Storable { return nil } -func accountGetCapabilityFunction( - gauge common.MemoryGauge, - addressValue AddressValue, - pathType sema.Type, - funcType *sema.FunctionType, -) *HostFunctionValue { - - return NewHostFunctionValue( - gauge, - funcType, - func(invocation Invocation) Value { - - path, ok := invocation.Arguments[0].(PathValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - interpreter := invocation.Interpreter - - pathStaticType := path.StaticType(interpreter) - - if !interpreter.IsSubTypeOfSemaType(pathStaticType, pathType) { - pathSemaType := interpreter.MustConvertStaticToSemaType(pathStaticType) - - panic(TypeMismatchError{ - ExpectedType: pathType, - ActualType: pathSemaType, - LocationRange: invocation.LocationRange, - }) - } - - // NOTE: the type parameter is optional, for backwards compatibility - - var borrowType *sema.ReferenceType - typeParameterPair := invocation.TypeParameterTypes.Oldest() - if typeParameterPair != nil { - ty := typeParameterPair.Value - // we handle the nil case for this below - borrowType, _ = ty.(*sema.ReferenceType) - } - - var borrowStaticType StaticType - if borrowType != nil { - borrowStaticType = ConvertSemaToStaticType(interpreter, borrowType) - } - - return NewStorageCapabilityValue( - gauge, - // TODO: - TodoCapabilityID, - addressValue, - path, - borrowStaticType, - ) - }, - ) -} - // PathValue type PathValue struct { diff --git a/runtime/interpreter/value_account.go b/runtime/interpreter/value_account.go index 36c6298319..748b41be2a 100644 --- a/runtime/interpreter/value_account.go +++ b/runtime/interpreter/value_account.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/sema" ) @@ -34,8 +35,7 @@ var authAccountFieldNames = []string{ sema.AuthAccountTypeContractsFieldName, sema.AuthAccountTypeKeysFieldName, sema.AuthAccountTypeInboxFieldName, - sema.AuthAccountTypeStorageCapabilitiesFieldName, - sema.AuthAccountTypeAccountCapabilitiesFieldName, + sema.AuthAccountTypeCapabilitiesFieldName, } // NewAuthAccountValue constructs an auth account value. @@ -51,27 +51,19 @@ func NewAuthAccountValue( contractsConstructor func() Value, keysConstructor func() Value, inboxConstructor func() Value, - storageCapabilitiesConstructor func() Value, - accountCapabilitiesConstructor func() Value, + capabilitiesConstructor func() Value, ) Value { fields := map[string]Value{ sema.AuthAccountTypeAddressFieldName: address, sema.AuthAccountTypeAddPublicKeyFunctionName: addPublicKeyFunction, sema.AuthAccountTypeRemovePublicKeyFunctionName: removePublicKeyFunction, - sema.AuthAccountTypeGetCapabilityFunctionName: accountGetCapabilityFunction( - gauge, - address, - sema.CapabilityPathType, - sema.AuthAccountTypeGetCapabilityFunctionType, - ), } var contracts Value var keys Value var inbox Value - var storageCapabilities Value - var accountCapabilities Value + var capabilities Value var forEachStoredFunction *HostFunctionValue var forEachPublicFunction *HostFunctionValue var forEachPrivateFunction *HostFunctionValue @@ -84,6 +76,7 @@ func NewAuthAccountValue( var linkAccountFunction *HostFunctionValue var unlinkFunction *HostFunctionValue var getLinkTargetFunction *HostFunctionValue + var getCapabilityFunction *HostFunctionValue computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { switch name { @@ -105,17 +98,11 @@ func NewAuthAccountValue( } return inbox - case sema.AuthAccountTypeStorageCapabilitiesFieldName: - if storageCapabilities == nil { - storageCapabilities = storageCapabilitiesConstructor() + case sema.AuthAccountTypeCapabilitiesFieldName: + if capabilities == nil { + capabilities = capabilitiesConstructor() } - return storageCapabilities - - case sema.AuthAccountTypeAccountCapabilitiesFieldName: - if accountCapabilities == nil { - accountCapabilities = accountCapabilitiesConstructor() - } - return accountCapabilities + return capabilities case sema.AuthAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) @@ -228,6 +215,16 @@ func NewAuthAccountValue( } return getLinkTargetFunction + case sema.AuthAccountTypeGetCapabilityFunctionName: + if getCapabilityFunction == nil { + getCapabilityFunction = accountGetCapabilityFunction( + gauge, + address, + sema.CapabilityPathType, + sema.AuthAccountTypeGetCapabilityFunctionType, + ) + } + return getCapabilityFunction } return nil @@ -263,7 +260,7 @@ var publicAccountFieldNames = []string{ sema.PublicAccountTypeAddressFieldName, sema.PublicAccountTypeContractsFieldName, sema.PublicAccountTypeKeysFieldName, - sema.PublicAccountTypeStorageCapabilitiesFieldName, + sema.PublicAccountTypeCapabilitiesFieldName, } // NewPublicAccountValue constructs a public account value. @@ -276,24 +273,19 @@ func NewPublicAccountValue( storageCapacityGet func(interpreter *Interpreter) UInt64Value, keysConstructor func() Value, contractsConstructor func() Value, - storageCapabilitiesConstructor func() Value, + capabilitiesConstructor func() Value, ) Value { fields := map[string]Value{ sema.PublicAccountTypeAddressFieldName: address, - sema.PublicAccountTypeGetCapabilityFunctionName: accountGetCapabilityFunction( - gauge, - address, - sema.PublicPathType, - sema.PublicAccountTypeGetCapabilityFunctionType, - ), } var keys Value var contracts Value - var storageCapabilities Value + var capabilities Value var forEachPublicFunction *HostFunctionValue var getLinkTargetFunction *HostFunctionValue + var getCapabilityFunction *HostFunctionValue computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { switch name { @@ -309,11 +301,11 @@ func NewPublicAccountValue( } return contracts - case sema.PublicAccountTypeStorageCapabilitiesFieldName: - if storageCapabilities == nil { - storageCapabilities = storageCapabilitiesConstructor() + case sema.PublicAccountTypeCapabilitiesFieldName: + if capabilities == nil { + capabilities = capabilitiesConstructor() } - return storageCapabilities + return capabilities case sema.PublicAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) @@ -349,6 +341,17 @@ func NewPublicAccountValue( ) } return getLinkTargetFunction + + case sema.PublicAccountTypeGetCapabilityFunctionName: + if getCapabilityFunction == nil { + getCapabilityFunction = accountGetCapabilityFunction( + gauge, + address, + sema.PublicPathType, + sema.PublicAccountTypeGetCapabilityFunctionType, + ) + } + return getCapabilityFunction } return nil @@ -375,3 +378,61 @@ func NewPublicAccountValue( stringer, ) } + +func accountGetCapabilityFunction( + gauge common.MemoryGauge, + addressValue AddressValue, + pathType sema.Type, + funcType *sema.FunctionType, +) *HostFunctionValue { + + return NewHostFunctionValue( + gauge, + funcType, + func(invocation Invocation) Value { + + path, ok := invocation.Arguments[0].(PathValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + interpreter := invocation.Interpreter + + pathStaticType := path.StaticType(interpreter) + + if !interpreter.IsSubTypeOfSemaType(pathStaticType, pathType) { + pathSemaType := interpreter.MustConvertStaticToSemaType(pathStaticType) + + panic(TypeMismatchError{ + ExpectedType: pathType, + ActualType: pathSemaType, + LocationRange: invocation.LocationRange, + }) + } + + // NOTE: the type parameter is optional, for backwards compatibility + + var borrowType *sema.ReferenceType + typeParameterPair := invocation.TypeParameterTypes.Oldest() + if typeParameterPair != nil { + ty := typeParameterPair.Value + // we handle the nil case for this below + borrowType, _ = ty.(*sema.ReferenceType) + } + + var borrowStaticType StaticType + if borrowType != nil { + borrowStaticType = ConvertSemaToStaticType(interpreter, borrowType) + } + + return NewStorageCapabilityValue( + gauge, + // TODO: + TodoCapabilityID, + addressValue, + path, + borrowStaticType, + ) + }, + ) +} diff --git a/runtime/interpreter/value_account_capabilities.go b/runtime/interpreter/value_account_capabilities.go new file mode 100644 index 0000000000..b85fa03644 --- /dev/null +++ b/runtime/interpreter/value_account_capabilities.go @@ -0,0 +1,131 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" +) + +// AuthAccount.Capabilities + +var authAccountCapabilitiesTypeID = sema.AuthAccountCapabilitiesType.ID() +var authAccountCapabilitiesStaticType StaticType = PrimitiveStaticTypeAuthAccountCapabilities + +func NewAuthAccountCapabilitiesValue( + gauge common.MemoryGauge, + address AddressValue, + getFunction FunctionValue, + borrowFunction FunctionValue, + publishFunction FunctionValue, + unpublishFunction FunctionValue, + storageCapabilitiesConstructor func() Value, + accountCapabilitiesConstructor func() Value, +) Value { + + fields := map[string]Value{ + sema.AuthAccountCapabilitiesTypeGetFunctionName: getFunction, + sema.AuthAccountCapabilitiesTypeBorrowFunctionName: borrowFunction, + sema.AuthAccountCapabilitiesTypePublishFunctionName: publishFunction, + sema.AuthAccountCapabilitiesTypeUnpublishFunctionName: unpublishFunction, + } + + var storageCapabilities Value + var accountCapabilities Value + + computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { + switch name { + case sema.AuthAccountCapabilitiesTypeStorageFieldName: + if storageCapabilities == nil { + storageCapabilities = storageCapabilitiesConstructor() + } + return storageCapabilities + + case sema.AuthAccountCapabilitiesTypeAccountFieldName: + if accountCapabilities == nil { + accountCapabilities = accountCapabilitiesConstructor() + } + return accountCapabilities + } + + return nil + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.AuthAccountCapabilitiesStringMemoryUsage) + addressStr := address.MeteredString(memoryGauge, seenReferences) + str = fmt.Sprintf("AuthAccount.Capabilities(%s)", addressStr) + } + return str + } + + return NewSimpleCompositeValue( + gauge, + authAccountCapabilitiesTypeID, + authAccountCapabilitiesStaticType, + nil, + fields, + computeField, + nil, + stringer, + ) +} + +// PublicAccount.Capabilities + +var publicAccountCapabilitiesTypeID = sema.PublicAccountCapabilitiesType.ID() +var publicAccountCapabilitiesStaticType StaticType = PrimitiveStaticTypePublicAccountCapabilities + +func NewPublicAccountCapabilitiesValue( + gauge common.MemoryGauge, + address AddressValue, + getFunction FunctionValue, + borrowFunction FunctionValue, +) Value { + + fields := map[string]Value{ + sema.PublicAccountCapabilitiesTypeGetFunctionName: getFunction, + sema.PublicAccountCapabilitiesTypeBorrowFunctionName: borrowFunction, + } + + var str string + stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + if str == "" { + common.UseMemory(memoryGauge, common.PublicAccountCapabilitiesStringMemoryUsage) + addressStr := address.MeteredString(memoryGauge, seenReferences) + str = fmt.Sprintf("PublicAccount.Capabilities(%s)", addressStr) + } + return str + } + + return NewSimpleCompositeValue( + gauge, + publicAccountCapabilitiesTypeID, + publicAccountCapabilitiesStaticType, + nil, + fields, + nil, + nil, + stringer, + ) +} diff --git a/runtime/interpreter/value_account_storagecapabilities.go b/runtime/interpreter/value_account_storagecapabilities.go index 9d48dfd085..9f7cfc73e8 100644 --- a/runtime/interpreter/value_account_storagecapabilities.go +++ b/runtime/interpreter/value_account_storagecapabilities.go @@ -34,8 +34,6 @@ var authAccountStorageCapabilitiesFieldNames []string = nil func NewAuthAccountStorageCapabilitiesValue( gauge common.MemoryGauge, address AddressValue, - getFunction FunctionValue, - borrowFunction FunctionValue, getControllerFunction FunctionValue, getControllersFunction FunctionValue, forEachControllerFunction FunctionValue, @@ -43,8 +41,6 @@ func NewAuthAccountStorageCapabilitiesValue( ) Value { fields := map[string]Value{ - sema.AuthAccountStorageCapabilitiesTypeGetFunctionName: getFunction, - sema.AuthAccountStorageCapabilitiesTypeBorrowFunctionName: borrowFunction, sema.AuthAccountStorageCapabilitiesTypeGetControllerFunctionName: getControllerFunction, sema.AuthAccountStorageCapabilitiesTypeGetControllersFunctionName: getControllersFunction, sema.AuthAccountStorageCapabilitiesTypeForEachControllerFunctionName: forEachControllerFunction, @@ -72,42 +68,3 @@ func NewAuthAccountStorageCapabilitiesValue( stringer, ) } - -// PublicAccount.StorageCapabilities - -var publicAccountStorageCapabilitiesTypeID = sema.PublicAccountStorageCapabilitiesType.ID() -var publicAccountStorageCapabilitiesStaticType StaticType = PrimitiveStaticTypePublicAccountStorageCapabilities - -func NewPublicAccountStorageCapabilitiesValue( - gauge common.MemoryGauge, - address AddressValue, - getFunction FunctionValue, - borrowFunction FunctionValue, -) Value { - - fields := map[string]Value{ - sema.PublicAccountStorageCapabilitiesTypeGetFunctionName: getFunction, - sema.PublicAccountStorageCapabilitiesTypeBorrowFunctionName: borrowFunction, - } - - var str string - stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { - if str == "" { - common.UseMemory(memoryGauge, common.PublicAccountStorageCapabilitiesStringMemoryUsage) - addressStr := address.MeteredString(memoryGauge, seenReferences) - str = fmt.Sprintf("PublicAccount.StorageCapabilities(%s)", addressStr) - } - return str - } - - return NewSimpleCompositeValue( - gauge, - publicAccountStorageCapabilitiesTypeID, - publicAccountStorageCapabilitiesStaticType, - nil, - fields, - nil, - nil, - stringer, - ) -} diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index 0c6fb565d5..a71f964d0b 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -25,11 +25,8 @@ pub struct AuthAccount { /// The inbox allows bootstrapping (sending and receiving) capabilities. pub let inbox: AuthAccount.Inbox - /// The storage capabilities of the account. - pub let storageCapabilities: &AuthAccount.StorageCapabilities - - /// The account capabilities of the account. - pub let accountCapabilities: &AuthAccount.AccountCapabilities + /// The capabilities of the account. + pub let capabilities: &AuthAccount.Capabilities /// All public paths of this account. pub let publicPaths: [PublicPath] @@ -112,6 +109,8 @@ pub struct AuthAccount { /// The path must be a storage path, i.e., only the domain `storage` is allowed pub fun borrow(from: StoragePath): T? + /// **DEPRECATED**: Instead, use `capabilities.storage.issue`, and `capabilities.publish` if the path is public. + /// /// Creates a capability at the given public or private path, /// which targets the given public, private, or storage path. /// @@ -132,18 +131,26 @@ pub struct AuthAccount { /// and the target value might be moved out after the link has been created. pub fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + /// **DEPRECATED**: Use `capabilities.account.issue` instead. + /// /// Creates a capability at the given public or private path which targets this account. /// /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. pub fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>? + /// **DEPRECATED**: Use `capabilities.get` instead. + /// /// Returns the capability at the given private or public path. pub fun getCapability(_ path: CapabilityPath): Capability + /// **DEPRECATED** + /// /// Returns the target path of the capability at the given public or private path, /// or nil if there exists no capability at the given path. pub fun getLinkTarget(_ path: CapabilityPath): Path? + /// **DEPRECATED**: Use `capabilities.unpublish` instead if the path is public. + /// /// Removes the capability at the given public or private path. pub fun unlink(_ path: CapabilityPath) @@ -301,17 +308,40 @@ pub struct AuthAccount { pub fun claim(_ name: String, provider: Address): Capability? } - pub struct StorageCapabilities { - /// get returns the storage capability at the given path, if one was stored there. + pub struct Capabilities { + + /// The storage capabilities of the account. + pub let storage: &AuthAccount.StorageCapabilities + + /// The account capabilities of the account. + pub let account: &AuthAccount.AccountCapabilities + + /// Returns the capability at the given public path. + /// Returns nil if the capability does not exist, + /// or if the given type is not a supertype of the capability's borrow type. pub fun get(_ path: PublicPath): Capability? - /// borrow gets the storage capability at the given path, and borrows the capability if it exists. - /// - /// Returns nil if the capability does not exist or cannot be borrowed using the given type. - /// + /// Borrows the capability at the given public path. + /// Returns nil if the capability does not exist, or cannot be borrowed using the given type. /// The function is equivalent to `get(path)?.borrow()`. pub fun borrow(_ path: PublicPath): T? + /// Publish the capability at the given public path. + /// + /// If there is already a capability published under the given path, the program aborts. + /// + /// The path must be a public path, i.e., only the domain `public` is allowed. + pub fun publish(_ capability: Capability, at: PublicPath) + + /// Unpublish the capability published at the given path. + /// + /// Returns the capability if one was published at the path. + /// Returns nil if no capability was published at the path. + pub fun unpublish(_ path: PublicPath): Capability? + } + + pub struct StorageCapabilities { + /// Get the storage capability controller for the capability with the specified ID. /// /// Returns nil if the ID does not reference an existing storage capability. diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index 7b69982593..693d2b173a 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -85,24 +85,14 @@ const AuthAccountTypeInboxFieldDocString = ` The inbox allows bootstrapping (sending and receiving) capabilities. ` -const AuthAccountTypeStorageCapabilitiesFieldName = "storageCapabilities" +const AuthAccountTypeCapabilitiesFieldName = "capabilities" -var AuthAccountTypeStorageCapabilitiesFieldType = &ReferenceType{ - Type: AuthAccountStorageCapabilitiesType, +var AuthAccountTypeCapabilitiesFieldType = &ReferenceType{ + Type: AuthAccountCapabilitiesType, } -const AuthAccountTypeStorageCapabilitiesFieldDocString = ` -The storage capabilities of the account. -` - -const AuthAccountTypeAccountCapabilitiesFieldName = "accountCapabilities" - -var AuthAccountTypeAccountCapabilitiesFieldType = &ReferenceType{ - Type: AuthAccountAccountCapabilitiesType, -} - -const AuthAccountTypeAccountCapabilitiesFieldDocString = ` -The account capabilities of the account. +const AuthAccountTypeCapabilitiesFieldDocString = ` +The capabilities of the account. ` const AuthAccountTypePublicPathsFieldName = "publicPaths" @@ -409,6 +399,8 @@ var AuthAccountTypeLinkFunctionType = &FunctionType{ } const AuthAccountTypeLinkFunctionDocString = ` +**DEPRECATED**: Instead, use ` + "`capabilities.storage.issue`" + `, and ` + "`capabilities.publish`" + ` if the path is public. + Creates a capability at the given public or private path, which targets the given public, private, or storage path. @@ -452,6 +444,8 @@ var AuthAccountTypeLinkAccountFunctionType = &FunctionType{ } const AuthAccountTypeLinkAccountFunctionDocString = ` +**DEPRECATED**: Use ` + "`capabilities.account.issue`" + ` instead. + Creates a capability at the given public or private path which targets this account. Returns nil if a link for the given capability path already exists, or the newly created capability if not. @@ -488,6 +482,8 @@ var AuthAccountTypeGetCapabilityFunctionType = &FunctionType{ } const AuthAccountTypeGetCapabilityFunctionDocString = ` +**DEPRECATED**: Use ` + "`capabilities.get`" + ` instead. + Returns the capability at the given private or public path. ` @@ -509,6 +505,8 @@ var AuthAccountTypeGetLinkTargetFunctionType = &FunctionType{ } const AuthAccountTypeGetLinkTargetFunctionDocString = ` +**DEPRECATED** + Returns the target path of the capability at the given public or private path, or nil if there exists no capability at the given path. ` @@ -529,6 +527,8 @@ var AuthAccountTypeUnlinkFunctionType = &FunctionType{ } const AuthAccountTypeUnlinkFunctionDocString = ` +**DEPRECATED**: Use ` + "`capabilities.unpublish`" + ` instead if the path is public. + Removes the capability at the given public or private path. ` @@ -1194,18 +1194,38 @@ func init() { AuthAccountInboxType.Fields = MembersFieldNames(members) } -const AuthAccountStorageCapabilitiesTypeGetFunctionName = "get" +const AuthAccountCapabilitiesTypeStorageFieldName = "storage" + +var AuthAccountCapabilitiesTypeStorageFieldType = &ReferenceType{ + Type: AuthAccountStorageCapabilitiesType, +} + +const AuthAccountCapabilitiesTypeStorageFieldDocString = ` +The storage capabilities of the account. +` -var AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ +const AuthAccountCapabilitiesTypeAccountFieldName = "account" + +var AuthAccountCapabilitiesTypeAccountFieldType = &ReferenceType{ + Type: AuthAccountAccountCapabilitiesType, +} + +const AuthAccountCapabilitiesTypeAccountFieldDocString = ` +The account capabilities of the account. +` + +const AuthAccountCapabilitiesTypeGetFunctionName = "get" + +var AuthAccountCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ Name: "T", TypeBound: &ReferenceType{ Type: AnyType, }, } -var AuthAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ +var AuthAccountCapabilitiesTypeGetFunctionType = &FunctionType{ TypeParameters: []*TypeParameter{ - AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + AuthAccountCapabilitiesTypeGetFunctionTypeParameterT, }, Parameters: []Parameter{ { @@ -1219,29 +1239,31 @@ var AuthAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ Type: MustInstantiate( &CapabilityType{}, &GenericType{ - TypeParameter: AuthAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + TypeParameter: AuthAccountCapabilitiesTypeGetFunctionTypeParameterT, }, ), }, ), } -const AuthAccountStorageCapabilitiesTypeGetFunctionDocString = ` -get returns the storage capability at the given path, if one was stored there. +const AuthAccountCapabilitiesTypeGetFunctionDocString = ` +Returns the capability at the given public path. +Returns nil if the capability does not exist, +or if the given type is not a supertype of the capability's borrow type. ` -const AuthAccountStorageCapabilitiesTypeBorrowFunctionName = "borrow" +const AuthAccountCapabilitiesTypeBorrowFunctionName = "borrow" -var AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ +var AuthAccountCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ Name: "T", TypeBound: &ReferenceType{ Type: AnyType, }, } -var AuthAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ +var AuthAccountCapabilitiesTypeBorrowFunctionType = &FunctionType{ TypeParameters: []*TypeParameter{ - AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + AuthAccountCapabilitiesTypeBorrowFunctionTypeParameterT, }, Parameters: []Parameter{ { @@ -1253,20 +1275,126 @@ var AuthAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ ReturnTypeAnnotation: NewTypeAnnotation( &OptionalType{ Type: &GenericType{ - TypeParameter: AuthAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + TypeParameter: AuthAccountCapabilitiesTypeBorrowFunctionTypeParameterT, }, }, ), } -const AuthAccountStorageCapabilitiesTypeBorrowFunctionDocString = ` -borrow gets the storage capability at the given path, and borrows the capability if it exists. +const AuthAccountCapabilitiesTypeBorrowFunctionDocString = ` +Borrows the capability at the given public path. +Returns nil if the capability does not exist, or cannot be borrowed using the given type. +The function is equivalent to ` + "`get(path)?.borrow()`" + `. +` -Returns nil if the capability does not exist or cannot be borrowed using the given type. +const AuthAccountCapabilitiesTypePublishFunctionName = "publish" -The function is equivalent to ` + "`get(path)?.borrow()`" + `. +var AuthAccountCapabilitiesTypePublishFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "capability", + TypeAnnotation: NewTypeAnnotation(&CapabilityType{}), + }, + { + Identifier: "at", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountCapabilitiesTypePublishFunctionDocString = ` +Publish the capability at the given public path. + +If there is already a capability published under the given path, the program aborts. + +The path must be a public path, i.e., only the domain ` + "`public`" + ` is allowed. ` +const AuthAccountCapabilitiesTypeUnpublishFunctionName = "unpublish" + +var AuthAccountCapabilitiesTypeUnpublishFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &CapabilityType{}, + }, + ), +} + +const AuthAccountCapabilitiesTypeUnpublishFunctionDocString = ` +Unpublish the capability published at the given path. + +Returns the capability if one was published at the path. +Returns nil if no capability was published at the path. +` + +const AuthAccountCapabilitiesTypeName = "Capabilities" + +var AuthAccountCapabilitiesType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountCapabilitiesTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypeStorageFieldName, + AuthAccountCapabilitiesTypeStorageFieldType, + AuthAccountCapabilitiesTypeStorageFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypeAccountFieldName, + AuthAccountCapabilitiesTypeAccountFieldType, + AuthAccountCapabilitiesTypeAccountFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypeGetFunctionName, + AuthAccountCapabilitiesTypeGetFunctionType, + AuthAccountCapabilitiesTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypeBorrowFunctionName, + AuthAccountCapabilitiesTypeBorrowFunctionType, + AuthAccountCapabilitiesTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypePublishFunctionName, + AuthAccountCapabilitiesTypePublishFunctionType, + AuthAccountCapabilitiesTypePublishFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountCapabilitiesType, + AuthAccountCapabilitiesTypeUnpublishFunctionName, + AuthAccountCapabilitiesTypeUnpublishFunctionType, + AuthAccountCapabilitiesTypeUnpublishFunctionDocString, + ), + } + + AuthAccountCapabilitiesType.Members = MembersAsMap(members) + AuthAccountCapabilitiesType.Fields = MembersFieldNames(members) +} + const AuthAccountStorageCapabilitiesTypeGetControllerFunctionName = "getController" var AuthAccountStorageCapabilitiesTypeGetControllerFunctionType = &FunctionType{ @@ -1397,18 +1525,6 @@ var AuthAccountStorageCapabilitiesType = func() *CompositeType { func init() { var members = []*Member{ - NewUnmeteredPublicFunctionMember( - AuthAccountStorageCapabilitiesType, - AuthAccountStorageCapabilitiesTypeGetFunctionName, - AuthAccountStorageCapabilitiesTypeGetFunctionType, - AuthAccountStorageCapabilitiesTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - AuthAccountStorageCapabilitiesType, - AuthAccountStorageCapabilitiesTypeBorrowFunctionName, - AuthAccountStorageCapabilitiesTypeBorrowFunctionType, - AuthAccountStorageCapabilitiesTypeBorrowFunctionDocString, - ), NewUnmeteredPublicFunctionMember( AuthAccountStorageCapabilitiesType, AuthAccountStorageCapabilitiesTypeGetControllerFunctionName, @@ -1596,6 +1712,7 @@ var AuthAccountType = func() *CompositeType { t.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) t.SetNestedType(AuthAccountKeysTypeName, AuthAccountKeysType) t.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) + t.SetNestedType(AuthAccountCapabilitiesTypeName, AuthAccountCapabilitiesType) t.SetNestedType(AuthAccountStorageCapabilitiesTypeName, AuthAccountStorageCapabilitiesType) t.SetNestedType(AuthAccountAccountCapabilitiesTypeName, AuthAccountAccountCapabilitiesType) return t @@ -1653,15 +1770,9 @@ func init() { ), NewUnmeteredPublicConstantFieldMember( AuthAccountType, - AuthAccountTypeStorageCapabilitiesFieldName, - AuthAccountTypeStorageCapabilitiesFieldType, - AuthAccountTypeStorageCapabilitiesFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - AuthAccountType, - AuthAccountTypeAccountCapabilitiesFieldName, - AuthAccountTypeAccountCapabilitiesFieldType, - AuthAccountTypeAccountCapabilitiesFieldDocString, + AuthAccountTypeCapabilitiesFieldName, + AuthAccountTypeCapabilitiesFieldType, + AuthAccountTypeCapabilitiesFieldDocString, ), NewUnmeteredPublicConstantFieldMember( AuthAccountType, diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc index e64fa71328..5e436ea39f 100644 --- a/runtime/sema/publicaccount.cdc +++ b/runtime/sema/publicaccount.cdc @@ -22,15 +22,19 @@ pub struct PublicAccount { /// The keys assigned to the account. pub let keys: PublicAccount.Keys - /// The storage capabilities of the account. - pub let storageCapabilities: &PublicAccount.StorageCapabilities + /// The capabilities of the account. + pub let capabilities: &PublicAccount.Capabilities /// All public paths of this account. pub let publicPaths: [PublicPath] + /// **DEPRECATED**: Use `capabilities.get` instead. + /// /// Returns the capability at the given public path. pub fun getCapability(_ path: PublicPath): Capability + /// **DEPRECATED** + /// /// Returns the target path of the capability at the given public or private path, /// or nil if there exists no capability at the given path. pub fun getLinkTarget(_ path: CapabilityPath): Path? @@ -83,7 +87,7 @@ pub struct PublicAccount { pub let count: UInt64 } - pub struct StorageCapabilities { + pub struct Capabilities { /// get returns the storage capability at the given path, if one was stored there. fun get(_ path: PublicPath): Capability? diff --git a/runtime/sema/publicaccount.gen.go b/runtime/sema/publicaccount.gen.go index 991d006e94..25b53f8461 100644 --- a/runtime/sema/publicaccount.gen.go +++ b/runtime/sema/publicaccount.gen.go @@ -77,14 +77,14 @@ const PublicAccountTypeKeysFieldDocString = ` The keys assigned to the account. ` -const PublicAccountTypeStorageCapabilitiesFieldName = "storageCapabilities" +const PublicAccountTypeCapabilitiesFieldName = "capabilities" -var PublicAccountTypeStorageCapabilitiesFieldType = &ReferenceType{ - Type: PublicAccountStorageCapabilitiesType, +var PublicAccountTypeCapabilitiesFieldType = &ReferenceType{ + Type: PublicAccountCapabilitiesType, } -const PublicAccountTypeStorageCapabilitiesFieldDocString = ` -The storage capabilities of the account. +const PublicAccountTypeCapabilitiesFieldDocString = ` +The capabilities of the account. ` const PublicAccountTypePublicPathsFieldName = "publicPaths" @@ -128,6 +128,8 @@ var PublicAccountTypeGetCapabilityFunctionType = &FunctionType{ } const PublicAccountTypeGetCapabilityFunctionDocString = ` +**DEPRECATED**: Use ` + "`capabilities.get`" + ` instead. + Returns the capability at the given public path. ` @@ -149,6 +151,8 @@ var PublicAccountTypeGetLinkTargetFunctionType = &FunctionType{ } const PublicAccountTypeGetLinkTargetFunctionDocString = ` +**DEPRECATED** + Returns the target path of the capability at the given public or private path, or nil if there exists no capability at the given path. ` @@ -401,18 +405,18 @@ func init() { PublicAccountKeysType.Fields = MembersFieldNames(members) } -const PublicAccountStorageCapabilitiesTypeGetFunctionName = "get" +const PublicAccountCapabilitiesTypeGetFunctionName = "get" -var PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ +var PublicAccountCapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ Name: "T", TypeBound: &ReferenceType{ Type: AnyType, }, } -var PublicAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ +var PublicAccountCapabilitiesTypeGetFunctionType = &FunctionType{ TypeParameters: []*TypeParameter{ - PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + PublicAccountCapabilitiesTypeGetFunctionTypeParameterT, }, Parameters: []Parameter{ { @@ -426,29 +430,29 @@ var PublicAccountStorageCapabilitiesTypeGetFunctionType = &FunctionType{ Type: MustInstantiate( &CapabilityType{}, &GenericType{ - TypeParameter: PublicAccountStorageCapabilitiesTypeGetFunctionTypeParameterT, + TypeParameter: PublicAccountCapabilitiesTypeGetFunctionTypeParameterT, }, ), }, ), } -const PublicAccountStorageCapabilitiesTypeGetFunctionDocString = ` +const PublicAccountCapabilitiesTypeGetFunctionDocString = ` get returns the storage capability at the given path, if one was stored there. ` -const PublicAccountStorageCapabilitiesTypeBorrowFunctionName = "borrow" +const PublicAccountCapabilitiesTypeBorrowFunctionName = "borrow" -var PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ +var PublicAccountCapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ Name: "T", TypeBound: &ReferenceType{ Type: AnyType, }, } -var PublicAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ +var PublicAccountCapabilitiesTypeBorrowFunctionType = &FunctionType{ TypeParameters: []*TypeParameter{ - PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + PublicAccountCapabilitiesTypeBorrowFunctionTypeParameterT, }, Parameters: []Parameter{ { @@ -460,24 +464,24 @@ var PublicAccountStorageCapabilitiesTypeBorrowFunctionType = &FunctionType{ ReturnTypeAnnotation: NewTypeAnnotation( &OptionalType{ Type: &GenericType{ - TypeParameter: PublicAccountStorageCapabilitiesTypeBorrowFunctionTypeParameterT, + TypeParameter: PublicAccountCapabilitiesTypeBorrowFunctionTypeParameterT, }, }, ), } -const PublicAccountStorageCapabilitiesTypeBorrowFunctionDocString = ` +const PublicAccountCapabilitiesTypeBorrowFunctionDocString = ` borrow gets the storage capability at the given path, and borrows the capability if it exists. Returns nil if the capability does not exist or cannot be borrowed using the given type. The function is equivalent to ` + "`get(path)?.borrow()`" + `. ` -const PublicAccountStorageCapabilitiesTypeName = "StorageCapabilities" +const PublicAccountCapabilitiesTypeName = "Capabilities" -var PublicAccountStorageCapabilitiesType = func() *CompositeType { +var PublicAccountCapabilitiesType = func() *CompositeType { var t = &CompositeType{ - Identifier: PublicAccountStorageCapabilitiesTypeName, + Identifier: PublicAccountCapabilitiesTypeName, Kind: common.CompositeKindStructure, importable: false, hasComputedMembers: true, @@ -489,21 +493,21 @@ var PublicAccountStorageCapabilitiesType = func() *CompositeType { func init() { var members = []*Member{ NewUnmeteredPublicFunctionMember( - PublicAccountStorageCapabilitiesType, - PublicAccountStorageCapabilitiesTypeGetFunctionName, - PublicAccountStorageCapabilitiesTypeGetFunctionType, - PublicAccountStorageCapabilitiesTypeGetFunctionDocString, + PublicAccountCapabilitiesType, + PublicAccountCapabilitiesTypeGetFunctionName, + PublicAccountCapabilitiesTypeGetFunctionType, + PublicAccountCapabilitiesTypeGetFunctionDocString, ), NewUnmeteredPublicFunctionMember( - PublicAccountStorageCapabilitiesType, - PublicAccountStorageCapabilitiesTypeBorrowFunctionName, - PublicAccountStorageCapabilitiesTypeBorrowFunctionType, - PublicAccountStorageCapabilitiesTypeBorrowFunctionDocString, + PublicAccountCapabilitiesType, + PublicAccountCapabilitiesTypeBorrowFunctionName, + PublicAccountCapabilitiesTypeBorrowFunctionType, + PublicAccountCapabilitiesTypeBorrowFunctionDocString, ), } - PublicAccountStorageCapabilitiesType.Members = MembersAsMap(members) - PublicAccountStorageCapabilitiesType.Fields = MembersFieldNames(members) + PublicAccountCapabilitiesType.Members = MembersAsMap(members) + PublicAccountCapabilitiesType.Fields = MembersFieldNames(members) } const PublicAccountTypeName = "PublicAccount" @@ -518,7 +522,7 @@ var PublicAccountType = func() *CompositeType { t.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) t.SetNestedType(PublicAccountKeysTypeName, PublicAccountKeysType) - t.SetNestedType(PublicAccountStorageCapabilitiesTypeName, PublicAccountStorageCapabilitiesType) + t.SetNestedType(PublicAccountCapabilitiesTypeName, PublicAccountCapabilitiesType) return t }() @@ -568,9 +572,9 @@ func init() { ), NewUnmeteredPublicConstantFieldMember( PublicAccountType, - PublicAccountTypeStorageCapabilitiesFieldName, - PublicAccountTypeStorageCapabilitiesFieldType, - PublicAccountTypeStorageCapabilitiesFieldDocString, + PublicAccountTypeCapabilitiesFieldName, + PublicAccountTypeCapabilitiesFieldType, + PublicAccountTypeCapabilitiesFieldDocString, ), NewUnmeteredPublicConstantFieldMember( PublicAccountType, diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 84be260bf1..9445681c33 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -216,27 +216,15 @@ func NewAuthAccountValue( ) }, func() interpreter.Value { - storageCapabilities := newAuthAccountStorageCapabilitiesValue( + capabilities := newAuthAccountCapabilitiesValue( gauge, addressValue, ) return interpreter.NewEphemeralReferenceValue( gauge, false, - storageCapabilities, - sema.AuthAccountTypeStorageCapabilitiesFieldType, - ) - }, - func() interpreter.Value { - accountCapabilities := newAuthAccountAccountCapabilitiesValue( - gauge, - addressValue, - ) - return interpreter.NewEphemeralReferenceValue( - gauge, - false, - accountCapabilities, - sema.AuthAccountTypeAccountCapabilitiesFieldType, + capabilities, + sema.AuthAccountTypeCapabilitiesFieldType, ) }, ) @@ -2119,15 +2107,15 @@ func NewPublicAccountValue( ) }, func() interpreter.Value { - storageCapabilities := newPublicAccountStorageCapabilitiesValue( + capabilities := newPublicAccountCapabilitiesValue( gauge, addressValue, ) return interpreter.NewEphemeralReferenceValue( gauge, false, - storageCapabilities, - sema.PublicAccountTypeStorageCapabilitiesFieldType, + capabilities, + sema.PublicAccountTypeCapabilitiesFieldType, ) }, ) @@ -2203,8 +2191,6 @@ func newAuthAccountStorageCapabilitiesValue( nil, nil, nil, - nil, - nil, ) } @@ -2223,12 +2209,51 @@ func newAuthAccountAccountCapabilitiesValue( ) } -func newPublicAccountStorageCapabilitiesValue( +func newAuthAccountCapabilitiesValue( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.Value { + // TODO: + return interpreter.NewAuthAccountCapabilitiesValue( + gauge, + addressValue, + nil, + nil, + nil, + nil, + func() interpreter.Value { + storageCapabilities := newAuthAccountStorageCapabilitiesValue( + gauge, + addressValue, + ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.AuthAccountCapabilitiesTypeStorageFieldType, + ) + }, + func() interpreter.Value { + accountCapabilities := newAuthAccountAccountCapabilitiesValue( + gauge, + addressValue, + ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + accountCapabilities, + sema.AuthAccountCapabilitiesTypeAccountFieldType, + ) + }, + ) +} + +func newPublicAccountCapabilitiesValue( gauge common.MemoryGauge, addressValue interpreter.AddressValue, ) interpreter.Value { // TODO: - return interpreter.NewPublicAccountStorageCapabilitiesValue( + return interpreter.NewPublicAccountCapabilitiesValue( gauge, addressValue, nil, diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index 9ba068eb31..bafe1ecf37 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -2265,22 +2265,38 @@ func TestCheckAccountClaim(t *testing.T) { }) } -func TestCheckStorageCapabilities(t *testing.T) { +func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - t.Run("AuthAccount.storageCapabilities", func(t *testing.T) { + t.Run("AuthAccount.capabilities", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` fun test() { - let capabilities: &AuthAccount.StorageCapabilities = authAccount.storageCapabilities + let capabilities: &AuthAccount.Capabilities = authAccount.capabilities let cap: Capability<&Int>? = capabilities.get<&Int>(/public/foo) let ref: &Int? = capabilities.borrow<&Int>(/public/foo) + capabilities.publish(cap!, at: /public/bar) + + let cap2: Capability = capabilities.unpublish(/public/bar)! + } + `) + require.NoError(t, err) + }) + + t.Run("AuthAccount.capabilities.storage", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckAccount(t, ` + fun test() { + let capabilities: &AuthAccount.StorageCapabilities = authAccount.capabilities.storage + let controller: &StorageCapabilityController? = capabilities.getController(byCapabilityID: 1) let controllers: [&StorageCapabilityController] = capabilities.getControllers(forPath: /storage/foo) @@ -2298,13 +2314,13 @@ func TestCheckStorageCapabilities(t *testing.T) { require.NoError(t, err) }) - t.Run("AuthAccount.accountCapabilities", func(t *testing.T) { + t.Run("AuthAccount.capabilities.account", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` fun test() { - let capabilities: &AuthAccount.AccountCapabilities = authAccount.accountCapabilities + let capabilities: &AuthAccount.AccountCapabilities = authAccount.capabilities.account let controller: &AccountCapabilityController? = capabilities.getController(byCapabilityID: 1) @@ -2320,13 +2336,13 @@ func TestCheckStorageCapabilities(t *testing.T) { require.NoError(t, err) }) - t.Run("PublicAccount.storageCapabilities", func(t *testing.T) { + t.Run("PublicAccount.capabilities", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` fun test() { - let capabilities: &PublicAccount.StorageCapabilities = publicAccount.storageCapabilities + let capabilities: &PublicAccount.Capabilities = publicAccount.capabilities let cap: Capability<&Int>? = capabilities.get<&Int>(/public/foo) @@ -2336,54 +2352,25 @@ func TestCheckStorageCapabilities(t *testing.T) { require.NoError(t, err) }) - t.Run("PublicAccount.storageCapabilities: invalid", func(t *testing.T) { + t.Run("PublicAccount.capabilities.storage: invalid", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` - fun test() { - let capabilities: &PublicAccount.StorageCapabilities = publicAccount.storageCapabilities - - capabilities.getController(byCapabilityID: 1) - - capabilities.getControllers(forPath: /storage/foo) - - capabilities.forEachController( - forPath: /storage/bar, - function: fun (controller: &StorageCapabilityController): Bool { - return true - } - ) - - capabilities.issue<&String>(/storage/baz) - } + let capabilitiesRef: &PublicAccount.StorageCapabilities = publicAccount.capabilities.storage `) require.Error(t, err) - errors := RequireCheckerErrors(t, err, 4) - - var err1 *sema.NotDeclaredMemberError - require.ErrorAs(t, errors[0], &err1) - assert.Equal(t, "getController", err1.Name) - - var err2 *sema.NotDeclaredMemberError - require.ErrorAs(t, errors[1], &err2) - assert.Equal(t, "getControllers", err2.Name) - - var err3 *sema.NotDeclaredMemberError - require.ErrorAs(t, errors[2], &err3) - assert.Equal(t, "forEachController", err3.Name) - - var err4 *sema.NotDeclaredMemberError - require.ErrorAs(t, errors[3], &err4) - assert.Equal(t, "issue", err4.Name) + errors := RequireCheckerErrors(t, err, 2) + require.IsType(t, &sema.NotDeclaredError{}, errors[0]) + require.IsType(t, &sema.NotDeclaredMemberError{}, errors[1]) }) - t.Run("PublicAccount.accountCapabilities", func(t *testing.T) { + t.Run("PublicAccount.capabilities.account: invalid", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` - let capabilitiesRef: &PublicAccount.AccountCapabilities = publicAccount.accountCapabilities + let capabilitiesRef: &PublicAccount.AccountCapabilities = publicAccount.capabilities.account `) require.Error(t, err) errors := RequireCheckerErrors(t, err, 2) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 693745da37..fbbf7ad7c5 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9250,9 +9250,9 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. panicFunctionValue, panicFunctionValue, panicFunctionValue, - interpreter.AccountKeysCountGetter(func() interpreter.UInt64Value { + func() interpreter.UInt64Value { panic(errors.NewUnreachableError()) - }), + }, ) }, func() interpreter.Value { @@ -9265,37 +9265,51 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. ) }, func() interpreter.Value { - storageCapabilities := interpreter.NewAuthAccountStorageCapabilitiesValue( - gauge, - addressValue, - panicFunctionValue, - panicFunctionValue, - panicFunctionValue, - panicFunctionValue, - panicFunctionValue, - panicFunctionValue, - ) - return interpreter.NewEphemeralReferenceValue( - gauge, - false, - storageCapabilities, - sema.AuthAccountTypeStorageCapabilitiesFieldType, - ) - }, - func() interpreter.Value { - accountCapabilities := interpreter.NewAuthAccountAccountCapabilitiesValue( + capabilities := interpreter.NewAuthAccountCapabilitiesValue( gauge, addressValue, panicFunctionValue, panicFunctionValue, panicFunctionValue, panicFunctionValue, + func() interpreter.Value { + storageCapabilities := interpreter.NewAuthAccountStorageCapabilitiesValue( + gauge, + addressValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + storageCapabilities, + sema.AuthAccountCapabilitiesTypeStorageFieldType, + ) + }, + func() interpreter.Value { + accountCapabilities := interpreter.NewAuthAccountAccountCapabilitiesValue( + gauge, + addressValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + panicFunctionValue, + ) + return interpreter.NewEphemeralReferenceValue( + gauge, + false, + accountCapabilities, + sema.AuthAccountCapabilitiesTypeAccountFieldType, + ) + }, ) return interpreter.NewEphemeralReferenceValue( gauge, false, - accountCapabilities, - sema.AuthAccountTypeAccountCapabilitiesFieldType, + capabilities, + sema.AuthAccountTypeCapabilitiesFieldType, ) }, ) @@ -9345,7 +9359,7 @@ func newTestPublicAccountValue(gauge common.MemoryGauge, addressValue interprete ) }, func() interpreter.Value { - storageCapabilities := interpreter.NewPublicAccountStorageCapabilitiesValue( + capabilities := interpreter.NewPublicAccountCapabilitiesValue( gauge, addressValue, panicFunctionValue, @@ -9354,8 +9368,8 @@ func newTestPublicAccountValue(gauge common.MemoryGauge, addressValue interprete return interpreter.NewEphemeralReferenceValue( gauge, false, - storageCapabilities, - sema.PublicAccountTypeStorageCapabilitiesFieldType, + capabilities, + sema.PublicAccountTypeCapabilitiesFieldType, ) }, ) diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index fc0dcdf260..6b278addf2 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -709,7 +709,7 @@ func TestInterpretSimpleCompositeMetering(t *testing.T) { require.NoError(t, err) assert.Equal(t, uint64(1), meter.getMemory(common.MemoryKindSimpleCompositeValueBase)) - assert.Equal(t, uint64(4), meter.getMemory(common.MemoryKindSimpleCompositeValue)) + assert.Equal(t, uint64(3), meter.getMemory(common.MemoryKindSimpleCompositeValue)) }) t.Run("public account", func(t *testing.T) { @@ -728,7 +728,7 @@ func TestInterpretSimpleCompositeMetering(t *testing.T) { require.NoError(t, err) assert.Equal(t, uint64(1), meter.getMemory(common.MemoryKindSimpleCompositeValueBase)) - assert.Equal(t, uint64(2), meter.getMemory(common.MemoryKindSimpleCompositeValue)) + assert.Equal(t, uint64(1), meter.getMemory(common.MemoryKindSimpleCompositeValue)) }) } From dbe78db5f15a1461cc1de6111287aa8132e97d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Apr 2023 15:04:11 -0700 Subject: [PATCH 064/246] fix and generalize registration of all root and nested native composite types --- runtime/sema/type.go | 57 ++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index c9802bc9ab..1e2dc05abd 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6563,27 +6563,6 @@ func (t *CapabilityType) initializeMemberResolvers() { }) } -var NativeCompositeTypes = map[string]*CompositeType{} - -func init() { - types := []*CompositeType{ - AccountKeyType, - PublicKeyType, - HashAlgorithmType, - SignatureAlgorithmType, - AuthAccountType, - AuthAccountKeysType, - AuthAccountContractsType, - PublicAccountType, - PublicAccountKeysType, - PublicAccountContractsType, - } - - for _, semaType := range types { - NativeCompositeTypes[semaType.QualifiedIdentifier()] = semaType - } -} - const AccountKeyTypeName = "AccountKey" const AccountKeyKeyIndexFieldName = "keyIndex" const AccountKeyPublicKeyFieldName = "publicKey" @@ -6825,3 +6804,39 @@ func isNumericSuperType(typ Type) bool { return false } + +var NativeCompositeTypes = map[string]*CompositeType{} + +func init() { + compositeTypes := []*CompositeType{ + AccountKeyType, + PublicKeyType, + HashAlgorithmType, + SignatureAlgorithmType, + AuthAccountType, + PublicAccountType, + } + + for len(compositeTypes) > 0 { + lastIndex := len(compositeTypes) - 1 + compositeType := compositeTypes[lastIndex] + compositeTypes[lastIndex] = nil + compositeTypes = compositeTypes[:lastIndex] + + NativeCompositeTypes[compositeType.QualifiedIdentifier()] = compositeType + + nestedTypes := compositeType.NestedTypes + if nestedTypes == nil { + continue + } + + nestedTypes.Foreach(func(_ string, nestedType Type) { + nestedCompositeType, ok := nestedType.(*CompositeType) + if !ok { + return + } + + compositeTypes = append(compositeTypes, nestedCompositeType) + }) + } +} From 2cbf71393a2eb416ce48fa31172c8428584e0ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Apr 2023 15:17:15 -0700 Subject: [PATCH 065/246] fix reference value borrow types --- runtime/stdlib/account.go | 8 ++++---- runtime/tests/interpreter/interpreter_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 9445681c33..50dafb0de7 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -224,7 +224,7 @@ func NewAuthAccountValue( gauge, false, capabilities, - sema.AuthAccountTypeCapabilitiesFieldType, + sema.AuthAccountTypeCapabilitiesFieldType.Type, ) }, ) @@ -2115,7 +2115,7 @@ func NewPublicAccountValue( gauge, false, capabilities, - sema.PublicAccountTypeCapabilitiesFieldType, + sema.PublicAccountTypeCapabilitiesFieldType.Type, ) }, ) @@ -2230,7 +2230,7 @@ func newAuthAccountCapabilitiesValue( gauge, false, storageCapabilities, - sema.AuthAccountCapabilitiesTypeStorageFieldType, + sema.AuthAccountCapabilitiesTypeStorageFieldType.Type, ) }, func() interpreter.Value { @@ -2242,7 +2242,7 @@ func newAuthAccountCapabilitiesValue( gauge, false, accountCapabilities, - sema.AuthAccountCapabilitiesTypeAccountFieldType, + sema.AuthAccountCapabilitiesTypeAccountFieldType.Type, ) }, ) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index fbbf7ad7c5..1bd6780c2e 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9285,7 +9285,7 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. gauge, false, storageCapabilities, - sema.AuthAccountCapabilitiesTypeStorageFieldType, + sema.AuthAccountCapabilitiesTypeStorageFieldType.Type, ) }, func() interpreter.Value { @@ -9301,7 +9301,7 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. gauge, false, accountCapabilities, - sema.AuthAccountCapabilitiesTypeAccountFieldType, + sema.AuthAccountCapabilitiesTypeAccountFieldType.Type, ) }, ) @@ -9309,7 +9309,7 @@ func newTestAuthAccountValue(gauge common.MemoryGauge, addressValue interpreter. gauge, false, capabilities, - sema.AuthAccountTypeCapabilitiesFieldType, + sema.AuthAccountTypeCapabilitiesFieldType.Type, ) }, ) @@ -9369,7 +9369,7 @@ func newTestPublicAccountValue(gauge common.MemoryGauge, addressValue interprete gauge, false, capabilities, - sema.PublicAccountTypeCapabilitiesFieldType, + sema.PublicAccountTypeCapabilitiesFieldType.Type, ) }, ) From c84ea1cc162b6ed425e9ef3711bb7283cbfd3d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Apr 2023 17:14:29 -0700 Subject: [PATCH 066/246] export StoredValueExists so it can be used from stdlib --- runtime/interpreter/interpreter.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 8afac817ad..5a0a93b602 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2229,7 +2229,7 @@ func (interpreter *Interpreter) NewSubInterpreter( ) } -func (interpreter *Interpreter) storedValueExists( +func (interpreter *Interpreter) StoredValueExists( storageAddress common.Address, domain string, identifier string, @@ -3471,7 +3471,7 @@ func (interpreter *Interpreter) authAccountSaveFunction(addressValue AddressValu locationRange := invocation.LocationRange - if interpreter.storedValueExists( + if interpreter.StoredValueExists( address, domain, identifier, @@ -3708,7 +3708,7 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu newCapabilityDomain := newCapabilityPath.Domain.Identifier() newCapabilityIdentifier := newCapabilityPath.Identifier - if interpreter.storedValueExists( + if interpreter.StoredValueExists( address, newCapabilityDomain, newCapabilityIdentifier, @@ -3796,7 +3796,7 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr newCapabilityDomain := newCapabilityPath.Domain.Identifier() newCapabilityIdentifier := newCapabilityPath.Identifier - if interpreter.storedValueExists( + if interpreter.StoredValueExists( address, newCapabilityDomain, newCapabilityIdentifier, From 3b4042fcc85ac363d2b49a5df10c6c421851467e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Apr 2023 17:15:54 -0700 Subject: [PATCH 067/246] implement AuthAccount.Capabilities.publish function --- runtime/stdlib/account.go | 63 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 50dafb0de7..15370a958a 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2219,7 +2219,7 @@ func newAuthAccountCapabilitiesValue( addressValue, nil, nil, - nil, + newAuthAccountCapabilitiesPublishFunction(gauge, addressValue), nil, func() interpreter.Value { storageCapabilities := newAuthAccountStorageCapabilitiesValue( @@ -2248,6 +2248,67 @@ func newAuthAccountCapabilitiesValue( ) } +func newAuthAccountCapabilitiesPublishFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountCapabilitiesTypePublishFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + + capabilityValue, ok := invocation.Arguments[0].(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + pathValue, ok := invocation.Arguments[1].(interpreter.PathValue) + if !ok || pathValue.Domain != common.PathDomainPublic { + panic(errors.NewUnreachableError()) + } + + domain := pathValue.Domain.Identifier() + identifier := pathValue.Identifier + // Prevent an overwrite + + locationRange := invocation.LocationRange + + if inter.StoredValueExists( + address, + domain, + identifier, + ) { + panic( + interpreter.OverwriteError{ + Address: addressValue, + Path: pathValue, + LocationRange: locationRange, + }, + ) + } + + capabilityValue, ok = capabilityValue.Transfer( + inter, + locationRange, + atree.Address(address), + true, + nil, + ).(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Write new value + + inter.WriteStored(address, domain, identifier, capabilityValue) + + return interpreter.Void + }, + ) +} + func newPublicAccountCapabilitiesValue( gauge common.MemoryGauge, addressValue interpreter.AddressValue, From d69db31944e6405eb12abe449f93823eddb1a602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Apr 2023 17:37:43 -0700 Subject: [PATCH 068/246] implement AuthAccount.Capabilities.unpublish function --- runtime/stdlib/account.go | 52 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 15370a958a..34d06d10ea 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2220,7 +2220,7 @@ func newAuthAccountCapabilitiesValue( nil, nil, newAuthAccountCapabilitiesPublishFunction(gauge, addressValue), - nil, + newAuthAccountCapabilitiesUnpublishFunction(gauge, addressValue), func() interpreter.Value { storageCapabilities := newAuthAccountStorageCapabilitiesValue( gauge, @@ -2271,6 +2271,7 @@ func newAuthAccountCapabilitiesPublishFunction( domain := pathValue.Domain.Identifier() identifier := pathValue.Identifier + // Prevent an overwrite locationRange := invocation.LocationRange @@ -2309,6 +2310,55 @@ func newAuthAccountCapabilitiesPublishFunction( ) } +func newAuthAccountCapabilitiesUnpublishFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountCapabilitiesTypeUnpublishFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + pathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || pathValue.Domain != common.PathDomainPublic { + panic(errors.NewUnreachableError()) + } + + domain := pathValue.Domain.Identifier() + identifier := pathValue.Identifier + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + readValue := inter.ReadStored(address, domain, identifier) + if readValue == nil { + return interpreter.Nil + } + + capabilityValue := readValue.(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + capabilityValue, ok = capabilityValue.Transfer( + inter, + locationRange, + atree.Address{}, + true, + nil, + ).(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter.WriteStored(address, domain, identifier, nil) + + return interpreter.NewSomeValueNonCopying(inter, capabilityValue) + }, + ) +} + func newPublicAccountCapabilitiesValue( gauge common.MemoryGauge, addressValue interpreter.AddressValue, From 7fce35a7c89554b7b590223904e26d1a4fc24f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Apr 2023 16:25:12 -0700 Subject: [PATCH 069/246] implement Auth/PublicAccountCapabilities.get function --- runtime/stdlib/account.go | 93 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 34d06d10ea..98c68751f3 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2217,7 +2217,7 @@ func newAuthAccountCapabilitiesValue( return interpreter.NewAuthAccountCapabilitiesValue( gauge, addressValue, - nil, + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.AuthAccountCapabilitiesTypeGetFunctionType), nil, newAuthAccountCapabilitiesPublishFunction(gauge, addressValue), newAuthAccountCapabilitiesUnpublishFunction(gauge, addressValue), @@ -2367,7 +2367,96 @@ func newPublicAccountCapabilitiesValue( return interpreter.NewPublicAccountCapabilitiesValue( gauge, addressValue, + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.PublicAccountCapabilitiesTypeGetFunctionType), nil, - nil, + ) +} + +func newAccountCapabilitiesGetFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, + funcType *sema.FunctionType, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + + return interpreter.NewHostFunctionValue( + gauge, + funcType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get path argument + + pathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || pathValue.Domain != common.PathDomainPublic { + panic(errors.NewUnreachableError()) + } + + domain := pathValue.Domain.Identifier() + identifier := pathValue.Identifier + + // Get borrow type type argument + + typeParameterPair := invocation.TypeParameterTypes.Oldest() + wantedBorrowType, ok := typeParameterPair.Value.(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Read stored capability, if any + + readValue := inter.ReadStored(address, domain, identifier) + if readValue == nil { + return interpreter.Nil + } + + capabilityValue := readValue.(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + allowedBorrowType, ok := inter.MustConvertStaticToSemaType(capabilityValue.BorrowType).(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Ensure requested borrow type is not more permissive + + if !sema.IsSubType(allowedBorrowType, wantedBorrowType) { + return interpreter.Nil + } + + // Return capability value + + capabilityValue, ok = capabilityValue.Transfer( + inter, + locationRange, + atree.Address{}, + false, + nil, + ).(*interpreter.StorageCapabilityValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + wantedBorrowStaticType := + interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, wantedBorrowType) + if !ok { + panic(errors.NewUnreachableError()) + } + + return interpreter.NewSomeValueNonCopying( + inter, + interpreter.NewStorageCapabilityValue( + inter, + capabilityValue.ID, + capabilityValue.Address, + capabilityValue.Path, + wantedBorrowStaticType, + ), + ) + }, ) } From e3dd09ff8553e626e4b391195c22acabe021fb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Apr 2023 16:25:44 -0700 Subject: [PATCH 070/246] refer diretly to type parameter instead of through index --- runtime/sema/authaccount.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/sema/authaccount.go b/runtime/sema/authaccount.go index d2518c40b0..e1e880473c 100644 --- a/runtime/sema/authaccount.go +++ b/runtime/sema/authaccount.go @@ -24,6 +24,6 @@ var AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation = AuthAcco func init() { AuthAccountContractsTypeAddFunctionType.RequiredArgumentCount = RequiredArgumentCount(2) - AuthAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true - PublicAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true + AuthAccountTypeGetCapabilityFunctionTypeParameterT.Optional = true + PublicAccountTypeGetCapabilityFunctionTypeParameterT.Optional = true } From f94924790cd05cb7355ec70054988d8724fad3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Apr 2023 17:01:57 -0700 Subject: [PATCH 071/246] start implementation of AuthAccount.StorageCapabilities.issue function --- runtime/stdlib/account.go | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 98c68751f3..4fe8da0aef 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2190,7 +2190,7 @@ func newAuthAccountStorageCapabilitiesValue( nil, nil, nil, - nil, + newAuthAccountStorageCapabilitiesIssueFunction(gauge, addressValue), ) } @@ -2248,6 +2248,46 @@ func newAuthAccountCapabilitiesValue( ) } +func newAuthAccountStorageCapabilitiesIssueFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountStorageCapabilitiesTypeIssueFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + // Get path argument + + pathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || pathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + // Get borrow type type argument + + typeParameterPair := invocation.TypeParameterTypes.Oldest() + borrowType, ok := typeParameterPair.Value.(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + + // TODO: create and write StorageCapabilityController + + borrowStaticType := interpreter.ConvertSemaReferenceTypeToStaticReferenceType(gauge, borrowType) + + return interpreter.NewStorageCapabilityValue( + gauge, + // TODO: + interpreter.TodoCapabilityID, + addressValue, + pathValue, + borrowStaticType, + ) + }, + ) +} + func newAuthAccountCapabilitiesPublishFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, From eabe5a0c003b936102e31d0859d2dcbca4df4068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Apr 2023 18:56:20 -0700 Subject: [PATCH 072/246] make AccountReferenceValue a ReferenceValue --- runtime/interpreter/value.go | 1 + runtime/interpreter/value_accountreference.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 8791f15f2f..47d41c539c 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16976,6 +16976,7 @@ func (s SomeStorable) ChildStorables() []atree.Storable { } type ReferenceValue interface { + Value isReference() } diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index 51d8ec5489..77c7505a5b 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -39,6 +39,7 @@ var _ Value = &AccountReferenceValue{} var _ EquatableValue = &AccountReferenceValue{} var _ ValueIndexableValue = &AccountReferenceValue{} var _ MemberAccessibleValue = &AccountReferenceValue{} +var _ ReferenceValue = &AccountReferenceValue{} func NewUnmeteredAccountReferenceValue( address common.Address, @@ -68,6 +69,8 @@ func NewAccountReferenceValue( func (*AccountReferenceValue) IsValue() {} +func (*AccountReferenceValue) isReference() {} + func (v *AccountReferenceValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitAccountReferenceValue(interpreter, v) } From 3c53a9e9a0d9808bc746a41bb82637a4f7490b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Apr 2023 18:56:51 -0700 Subject: [PATCH 073/246] implement Auth/PublicAccount.Capabilities.borrow function --- runtime/interpreter/interpreter.go | 116 ++++++++++++++++------------- runtime/stdlib/account.go | 41 ++++++---- 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 5a0a93b602..9c7daee78c 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3911,6 +3911,66 @@ func (interpreter *Interpreter) authAccountUnlinkFunction(addressValue AddressVa ) } +func (interpreter *Interpreter) BorrowCapability( + address common.Address, + pathValue PathValue, + borrowType *sema.ReferenceType, + locationRange LocationRange, +) ReferenceValue { + target, authorized, err := + interpreter.GetStorageCapabilityFinalTarget( + address, + pathValue, + borrowType, + locationRange, + ) + if err != nil { + panic(err) + } + + if target == nil { + return nil + } + + switch target := target.(type) { + case AccountCapabilityTarget: + return NewAccountReferenceValue( + interpreter, + address, + pathValue, + borrowType.Type, + ) + + case PathCapabilityTarget: + targetPath := PathValue(target) + + reference := NewStorageReferenceValue( + interpreter, + authorized, + address, + targetPath, + borrowType.Type, + ) + + // Attempt to dereference, + // which reads the stored value + // and performs a dynamic type check + + value, err := reference.dereference(interpreter, locationRange) + if err != nil { + panic(err) + } + if value == nil { + return nil + } + + return reference + + default: + panic(errors.NewUnreachableError()) + } +} + func (interpreter *Interpreter) storageCapabilityBorrowFunction( addressValue AddressValue, pathValue PathValue, @@ -3944,61 +4004,11 @@ func (interpreter *Interpreter) storageCapabilityBorrowFunction( panic(errors.NewUnreachableError()) } - target, authorized, err := - interpreter.GetStorageCapabilityFinalTarget( - address, - pathValue, - borrowType, - invocation.LocationRange, - ) - if err != nil { - panic(err) - } - - if target == nil { + reference := interpreter.BorrowCapability(address, pathValue, borrowType, invocation.LocationRange) + if reference == nil { return Nil } - - switch target := target.(type) { - case AccountCapabilityTarget: - return NewSomeValueNonCopying( - interpreter, - NewAccountReferenceValue( - interpreter, - address, - pathValue, - borrowType.Type, - ), - ) - - case PathCapabilityTarget: - targetPath := PathValue(target) - - reference := NewStorageReferenceValue( - interpreter, - authorized, - address, - targetPath, - borrowType.Type, - ) - - // Attempt to dereference, - // which reads the stored value - // and performs a dynamic type check - - value, err := reference.dereference(interpreter, invocation.LocationRange) - if err != nil { - panic(err) - } - if value == nil { - return Nil - } - - return NewSomeValueNonCopying(interpreter, reference) - - default: - panic(errors.NewUnreachableError()) - } + return NewSomeValueNonCopying(interpreter, reference) }, ) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 4fe8da0aef..ad8ac598f8 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2217,8 +2217,8 @@ func newAuthAccountCapabilitiesValue( return interpreter.NewAuthAccountCapabilitiesValue( gauge, addressValue, - newAccountCapabilitiesGetFunction(gauge, addressValue, sema.AuthAccountCapabilitiesTypeGetFunctionType), - nil, + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.AuthAccountCapabilitiesTypeGetFunctionType, false), + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.AuthAccountCapabilitiesTypeBorrowFunctionType, true), newAuthAccountCapabilitiesPublishFunction(gauge, addressValue), newAuthAccountCapabilitiesUnpublishFunction(gauge, addressValue), func() interpreter.Value { @@ -2407,8 +2407,8 @@ func newPublicAccountCapabilitiesValue( return interpreter.NewPublicAccountCapabilitiesValue( gauge, addressValue, - newAccountCapabilitiesGetFunction(gauge, addressValue, sema.PublicAccountCapabilitiesTypeGetFunctionType), - nil, + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.PublicAccountCapabilitiesTypeGetFunctionType, false), + newAccountCapabilitiesGetFunction(gauge, addressValue, sema.PublicAccountCapabilitiesTypeBorrowFunctionType, true), ) } @@ -2416,6 +2416,7 @@ func newAccountCapabilitiesGetFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, funcType *sema.FunctionType, + borrow bool, ) *interpreter.HostFunctionValue { address := addressValue.ToAddress() @@ -2452,12 +2453,12 @@ func newAccountCapabilitiesGetFunction( return interpreter.Nil } - capabilityValue := readValue.(*interpreter.StorageCapabilityValue) + readCapabilityValue := readValue.(*interpreter.StorageCapabilityValue) if !ok { panic(errors.NewUnreachableError()) } - allowedBorrowType, ok := inter.MustConvertStaticToSemaType(capabilityValue.BorrowType).(*sema.ReferenceType) + allowedBorrowType, ok := inter.MustConvertStaticToSemaType(readCapabilityValue.BorrowType).(*sema.ReferenceType) if !ok { panic(errors.NewUnreachableError()) } @@ -2470,7 +2471,7 @@ func newAccountCapabilitiesGetFunction( // Return capability value - capabilityValue, ok = capabilityValue.Transfer( + readCapabilityValue, ok = readCapabilityValue.Transfer( inter, locationRange, atree.Address{}, @@ -2487,15 +2488,27 @@ func newAccountCapabilitiesGetFunction( panic(errors.NewUnreachableError()) } - return interpreter.NewSomeValueNonCopying( - inter, - interpreter.NewStorageCapabilityValue( + var resultValue interpreter.Value + if borrow { + resultValue = inter.BorrowCapability( + readCapabilityValue.Address.ToAddress(), + readCapabilityValue.Path, + wantedBorrowType, + locationRange, + ) + } else { + resultValue = interpreter.NewStorageCapabilityValue( inter, - capabilityValue.ID, - capabilityValue.Address, - capabilityValue.Path, + readCapabilityValue.ID, + readCapabilityValue.Address, + readCapabilityValue.Path, wantedBorrowStaticType, - ), + ) + } + + return interpreter.NewSomeValueNonCopying( + inter, + resultValue, ) }, ) From baf5be8aaf246211d7dab041c65dc9eea4ef42b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 13:10:05 -0700 Subject: [PATCH 074/246] refactor StorageCapabilityControllerValue to dedicated type so it can be stored --- runtime/common/memorykind.go | 1 + runtime/common/memorykind_string.go | 335 +++++----- runtime/common/metering.go | 97 +-- runtime/format/capability.go | 5 +- runtime/interpreter/decode.go | 68 +++ runtime/interpreter/encode.go | 52 +- runtime/interpreter/encoding_test.go | 577 ++++++++++++++++-- .../value_accountcapabilitycontroller.go | 2 +- .../value_storagecapabilitycontroller.go | 232 ++++--- runtime/interpreter/visitor.go | 95 +-- 10 files changed, 1055 insertions(+), 409 deletions(-) diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 04ea030df6..46ceb72352 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -51,6 +51,7 @@ const ( MemoryKindBigInt MemoryKindSimpleCompositeValue MemoryKindPublishedValue + MemoryKindStorageCapabilityControllerValue // Atree Nodes MemoryKindAtreeArrayDataSlab diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 922940626b..6ca38a1974 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -32,176 +32,177 @@ func _() { _ = x[MemoryKindBigInt-21] _ = x[MemoryKindSimpleCompositeValue-22] _ = x[MemoryKindPublishedValue-23] - _ = x[MemoryKindAtreeArrayDataSlab-24] - _ = x[MemoryKindAtreeArrayMetaDataSlab-25] - _ = x[MemoryKindAtreeArrayElementOverhead-26] - _ = x[MemoryKindAtreeMapDataSlab-27] - _ = x[MemoryKindAtreeMapMetaDataSlab-28] - _ = x[MemoryKindAtreeMapElementOverhead-29] - _ = x[MemoryKindAtreeMapPreAllocatedElement-30] - _ = x[MemoryKindAtreeEncodedSlab-31] - _ = x[MemoryKindPrimitiveStaticType-32] - _ = x[MemoryKindCompositeStaticType-33] - _ = x[MemoryKindInterfaceStaticType-34] - _ = x[MemoryKindVariableSizedStaticType-35] - _ = x[MemoryKindConstantSizedStaticType-36] - _ = x[MemoryKindDictionaryStaticType-37] - _ = x[MemoryKindOptionalStaticType-38] - _ = x[MemoryKindRestrictedStaticType-39] - _ = x[MemoryKindReferenceStaticType-40] - _ = x[MemoryKindCapabilityStaticType-41] - _ = x[MemoryKindFunctionStaticType-42] - _ = x[MemoryKindCadenceVoidValue-43] - _ = x[MemoryKindCadenceOptionalValue-44] - _ = x[MemoryKindCadenceBoolValue-45] - _ = x[MemoryKindCadenceStringValue-46] - _ = x[MemoryKindCadenceCharacterValue-47] - _ = x[MemoryKindCadenceAddressValue-48] - _ = x[MemoryKindCadenceIntValue-49] - _ = x[MemoryKindCadenceNumberValue-50] - _ = x[MemoryKindCadenceArrayValueBase-51] - _ = x[MemoryKindCadenceArrayValueLength-52] - _ = x[MemoryKindCadenceDictionaryValue-53] - _ = x[MemoryKindCadenceKeyValuePair-54] - _ = x[MemoryKindCadenceStructValueBase-55] - _ = x[MemoryKindCadenceStructValueSize-56] - _ = x[MemoryKindCadenceResourceValueBase-57] - _ = x[MemoryKindCadenceAttachmentValueBase-58] - _ = x[MemoryKindCadenceResourceValueSize-59] - _ = x[MemoryKindCadenceAttachmentValueSize-60] - _ = x[MemoryKindCadenceEventValueBase-61] - _ = x[MemoryKindCadenceEventValueSize-62] - _ = x[MemoryKindCadenceContractValueBase-63] - _ = x[MemoryKindCadenceContractValueSize-64] - _ = x[MemoryKindCadenceEnumValueBase-65] - _ = x[MemoryKindCadenceEnumValueSize-66] - _ = x[MemoryKindCadencePathLinkValue-67] - _ = x[MemoryKindCadenceAccountLinkValue-68] - _ = x[MemoryKindCadencePathValue-69] - _ = x[MemoryKindCadenceTypeValue-70] - _ = x[MemoryKindCadenceStorageCapabilityValue-71] - _ = x[MemoryKindCadenceFunctionValue-72] - _ = x[MemoryKindCadenceOptionalType-73] - _ = x[MemoryKindCadenceVariableSizedArrayType-74] - _ = x[MemoryKindCadenceConstantSizedArrayType-75] - _ = x[MemoryKindCadenceDictionaryType-76] - _ = x[MemoryKindCadenceField-77] - _ = x[MemoryKindCadenceParameter-78] - _ = x[MemoryKindCadenceTypeParameter-79] - _ = x[MemoryKindCadenceStructType-80] - _ = x[MemoryKindCadenceResourceType-81] - _ = x[MemoryKindCadenceAttachmentType-82] - _ = x[MemoryKindCadenceEventType-83] - _ = x[MemoryKindCadenceContractType-84] - _ = x[MemoryKindCadenceStructInterfaceType-85] - _ = x[MemoryKindCadenceResourceInterfaceType-86] - _ = x[MemoryKindCadenceContractInterfaceType-87] - _ = x[MemoryKindCadenceFunctionType-88] - _ = x[MemoryKindCadenceReferenceType-89] - _ = x[MemoryKindCadenceRestrictedType-90] - _ = x[MemoryKindCadenceCapabilityType-91] - _ = x[MemoryKindCadenceEnumType-92] - _ = x[MemoryKindRawString-93] - _ = x[MemoryKindAddressLocation-94] - _ = x[MemoryKindBytes-95] - _ = x[MemoryKindVariable-96] - _ = x[MemoryKindCompositeTypeInfo-97] - _ = x[MemoryKindCompositeField-98] - _ = x[MemoryKindInvocation-99] - _ = x[MemoryKindStorageMap-100] - _ = x[MemoryKindStorageKey-101] - _ = x[MemoryKindTypeToken-102] - _ = x[MemoryKindErrorToken-103] - _ = x[MemoryKindSpaceToken-104] - _ = x[MemoryKindProgram-105] - _ = x[MemoryKindIdentifier-106] - _ = x[MemoryKindArgument-107] - _ = x[MemoryKindBlock-108] - _ = x[MemoryKindFunctionBlock-109] - _ = x[MemoryKindParameter-110] - _ = x[MemoryKindParameterList-111] - _ = x[MemoryKindTypeParameter-112] - _ = x[MemoryKindTypeParameterList-113] - _ = x[MemoryKindTransfer-114] - _ = x[MemoryKindMembers-115] - _ = x[MemoryKindTypeAnnotation-116] - _ = x[MemoryKindDictionaryEntry-117] - _ = x[MemoryKindFunctionDeclaration-118] - _ = x[MemoryKindCompositeDeclaration-119] - _ = x[MemoryKindAttachmentDeclaration-120] - _ = x[MemoryKindInterfaceDeclaration-121] - _ = x[MemoryKindEnumCaseDeclaration-122] - _ = x[MemoryKindFieldDeclaration-123] - _ = x[MemoryKindTransactionDeclaration-124] - _ = x[MemoryKindImportDeclaration-125] - _ = x[MemoryKindVariableDeclaration-126] - _ = x[MemoryKindSpecialFunctionDeclaration-127] - _ = x[MemoryKindPragmaDeclaration-128] - _ = x[MemoryKindAssignmentStatement-129] - _ = x[MemoryKindBreakStatement-130] - _ = x[MemoryKindContinueStatement-131] - _ = x[MemoryKindEmitStatement-132] - _ = x[MemoryKindExpressionStatement-133] - _ = x[MemoryKindForStatement-134] - _ = x[MemoryKindIfStatement-135] - _ = x[MemoryKindReturnStatement-136] - _ = x[MemoryKindSwapStatement-137] - _ = x[MemoryKindSwitchStatement-138] - _ = x[MemoryKindWhileStatement-139] - _ = x[MemoryKindRemoveStatement-140] - _ = x[MemoryKindBooleanExpression-141] - _ = x[MemoryKindVoidExpression-142] - _ = x[MemoryKindNilExpression-143] - _ = x[MemoryKindStringExpression-144] - _ = x[MemoryKindIntegerExpression-145] - _ = x[MemoryKindFixedPointExpression-146] - _ = x[MemoryKindArrayExpression-147] - _ = x[MemoryKindDictionaryExpression-148] - _ = x[MemoryKindIdentifierExpression-149] - _ = x[MemoryKindInvocationExpression-150] - _ = x[MemoryKindMemberExpression-151] - _ = x[MemoryKindIndexExpression-152] - _ = x[MemoryKindConditionalExpression-153] - _ = x[MemoryKindUnaryExpression-154] - _ = x[MemoryKindBinaryExpression-155] - _ = x[MemoryKindFunctionExpression-156] - _ = x[MemoryKindCastingExpression-157] - _ = x[MemoryKindCreateExpression-158] - _ = x[MemoryKindDestroyExpression-159] - _ = x[MemoryKindReferenceExpression-160] - _ = x[MemoryKindForceExpression-161] - _ = x[MemoryKindPathExpression-162] - _ = x[MemoryKindAttachExpression-163] - _ = x[MemoryKindConstantSizedType-164] - _ = x[MemoryKindDictionaryType-165] - _ = x[MemoryKindFunctionType-166] - _ = x[MemoryKindInstantiationType-167] - _ = x[MemoryKindNominalType-168] - _ = x[MemoryKindOptionalType-169] - _ = x[MemoryKindReferenceType-170] - _ = x[MemoryKindRestrictedType-171] - _ = x[MemoryKindVariableSizedType-172] - _ = x[MemoryKindPosition-173] - _ = x[MemoryKindRange-174] - _ = x[MemoryKindElaboration-175] - _ = x[MemoryKindActivation-176] - _ = x[MemoryKindActivationEntries-177] - _ = x[MemoryKindVariableSizedSemaType-178] - _ = x[MemoryKindConstantSizedSemaType-179] - _ = x[MemoryKindDictionarySemaType-180] - _ = x[MemoryKindOptionalSemaType-181] - _ = x[MemoryKindRestrictedSemaType-182] - _ = x[MemoryKindReferenceSemaType-183] - _ = x[MemoryKindCapabilitySemaType-184] - _ = x[MemoryKindOrderedMap-185] - _ = x[MemoryKindOrderedMapEntryList-186] - _ = x[MemoryKindOrderedMapEntry-187] - _ = x[MemoryKindLast-188] + _ = x[MemoryKindStorageCapabilityControllerValue-24] + _ = x[MemoryKindAtreeArrayDataSlab-25] + _ = x[MemoryKindAtreeArrayMetaDataSlab-26] + _ = x[MemoryKindAtreeArrayElementOverhead-27] + _ = x[MemoryKindAtreeMapDataSlab-28] + _ = x[MemoryKindAtreeMapMetaDataSlab-29] + _ = x[MemoryKindAtreeMapElementOverhead-30] + _ = x[MemoryKindAtreeMapPreAllocatedElement-31] + _ = x[MemoryKindAtreeEncodedSlab-32] + _ = x[MemoryKindPrimitiveStaticType-33] + _ = x[MemoryKindCompositeStaticType-34] + _ = x[MemoryKindInterfaceStaticType-35] + _ = x[MemoryKindVariableSizedStaticType-36] + _ = x[MemoryKindConstantSizedStaticType-37] + _ = x[MemoryKindDictionaryStaticType-38] + _ = x[MemoryKindOptionalStaticType-39] + _ = x[MemoryKindRestrictedStaticType-40] + _ = x[MemoryKindReferenceStaticType-41] + _ = x[MemoryKindCapabilityStaticType-42] + _ = x[MemoryKindFunctionStaticType-43] + _ = x[MemoryKindCadenceVoidValue-44] + _ = x[MemoryKindCadenceOptionalValue-45] + _ = x[MemoryKindCadenceBoolValue-46] + _ = x[MemoryKindCadenceStringValue-47] + _ = x[MemoryKindCadenceCharacterValue-48] + _ = x[MemoryKindCadenceAddressValue-49] + _ = x[MemoryKindCadenceIntValue-50] + _ = x[MemoryKindCadenceNumberValue-51] + _ = x[MemoryKindCadenceArrayValueBase-52] + _ = x[MemoryKindCadenceArrayValueLength-53] + _ = x[MemoryKindCadenceDictionaryValue-54] + _ = x[MemoryKindCadenceKeyValuePair-55] + _ = x[MemoryKindCadenceStructValueBase-56] + _ = x[MemoryKindCadenceStructValueSize-57] + _ = x[MemoryKindCadenceResourceValueBase-58] + _ = x[MemoryKindCadenceAttachmentValueBase-59] + _ = x[MemoryKindCadenceResourceValueSize-60] + _ = x[MemoryKindCadenceAttachmentValueSize-61] + _ = x[MemoryKindCadenceEventValueBase-62] + _ = x[MemoryKindCadenceEventValueSize-63] + _ = x[MemoryKindCadenceContractValueBase-64] + _ = x[MemoryKindCadenceContractValueSize-65] + _ = x[MemoryKindCadenceEnumValueBase-66] + _ = x[MemoryKindCadenceEnumValueSize-67] + _ = x[MemoryKindCadencePathLinkValue-68] + _ = x[MemoryKindCadenceAccountLinkValue-69] + _ = x[MemoryKindCadencePathValue-70] + _ = x[MemoryKindCadenceTypeValue-71] + _ = x[MemoryKindCadenceStorageCapabilityValue-72] + _ = x[MemoryKindCadenceFunctionValue-73] + _ = x[MemoryKindCadenceOptionalType-74] + _ = x[MemoryKindCadenceVariableSizedArrayType-75] + _ = x[MemoryKindCadenceConstantSizedArrayType-76] + _ = x[MemoryKindCadenceDictionaryType-77] + _ = x[MemoryKindCadenceField-78] + _ = x[MemoryKindCadenceParameter-79] + _ = x[MemoryKindCadenceTypeParameter-80] + _ = x[MemoryKindCadenceStructType-81] + _ = x[MemoryKindCadenceResourceType-82] + _ = x[MemoryKindCadenceAttachmentType-83] + _ = x[MemoryKindCadenceEventType-84] + _ = x[MemoryKindCadenceContractType-85] + _ = x[MemoryKindCadenceStructInterfaceType-86] + _ = x[MemoryKindCadenceResourceInterfaceType-87] + _ = x[MemoryKindCadenceContractInterfaceType-88] + _ = x[MemoryKindCadenceFunctionType-89] + _ = x[MemoryKindCadenceReferenceType-90] + _ = x[MemoryKindCadenceRestrictedType-91] + _ = x[MemoryKindCadenceCapabilityType-92] + _ = x[MemoryKindCadenceEnumType-93] + _ = x[MemoryKindRawString-94] + _ = x[MemoryKindAddressLocation-95] + _ = x[MemoryKindBytes-96] + _ = x[MemoryKindVariable-97] + _ = x[MemoryKindCompositeTypeInfo-98] + _ = x[MemoryKindCompositeField-99] + _ = x[MemoryKindInvocation-100] + _ = x[MemoryKindStorageMap-101] + _ = x[MemoryKindStorageKey-102] + _ = x[MemoryKindTypeToken-103] + _ = x[MemoryKindErrorToken-104] + _ = x[MemoryKindSpaceToken-105] + _ = x[MemoryKindProgram-106] + _ = x[MemoryKindIdentifier-107] + _ = x[MemoryKindArgument-108] + _ = x[MemoryKindBlock-109] + _ = x[MemoryKindFunctionBlock-110] + _ = x[MemoryKindParameter-111] + _ = x[MemoryKindParameterList-112] + _ = x[MemoryKindTypeParameter-113] + _ = x[MemoryKindTypeParameterList-114] + _ = x[MemoryKindTransfer-115] + _ = x[MemoryKindMembers-116] + _ = x[MemoryKindTypeAnnotation-117] + _ = x[MemoryKindDictionaryEntry-118] + _ = x[MemoryKindFunctionDeclaration-119] + _ = x[MemoryKindCompositeDeclaration-120] + _ = x[MemoryKindAttachmentDeclaration-121] + _ = x[MemoryKindInterfaceDeclaration-122] + _ = x[MemoryKindEnumCaseDeclaration-123] + _ = x[MemoryKindFieldDeclaration-124] + _ = x[MemoryKindTransactionDeclaration-125] + _ = x[MemoryKindImportDeclaration-126] + _ = x[MemoryKindVariableDeclaration-127] + _ = x[MemoryKindSpecialFunctionDeclaration-128] + _ = x[MemoryKindPragmaDeclaration-129] + _ = x[MemoryKindAssignmentStatement-130] + _ = x[MemoryKindBreakStatement-131] + _ = x[MemoryKindContinueStatement-132] + _ = x[MemoryKindEmitStatement-133] + _ = x[MemoryKindExpressionStatement-134] + _ = x[MemoryKindForStatement-135] + _ = x[MemoryKindIfStatement-136] + _ = x[MemoryKindReturnStatement-137] + _ = x[MemoryKindSwapStatement-138] + _ = x[MemoryKindSwitchStatement-139] + _ = x[MemoryKindWhileStatement-140] + _ = x[MemoryKindRemoveStatement-141] + _ = x[MemoryKindBooleanExpression-142] + _ = x[MemoryKindVoidExpression-143] + _ = x[MemoryKindNilExpression-144] + _ = x[MemoryKindStringExpression-145] + _ = x[MemoryKindIntegerExpression-146] + _ = x[MemoryKindFixedPointExpression-147] + _ = x[MemoryKindArrayExpression-148] + _ = x[MemoryKindDictionaryExpression-149] + _ = x[MemoryKindIdentifierExpression-150] + _ = x[MemoryKindInvocationExpression-151] + _ = x[MemoryKindMemberExpression-152] + _ = x[MemoryKindIndexExpression-153] + _ = x[MemoryKindConditionalExpression-154] + _ = x[MemoryKindUnaryExpression-155] + _ = x[MemoryKindBinaryExpression-156] + _ = x[MemoryKindFunctionExpression-157] + _ = x[MemoryKindCastingExpression-158] + _ = x[MemoryKindCreateExpression-159] + _ = x[MemoryKindDestroyExpression-160] + _ = x[MemoryKindReferenceExpression-161] + _ = x[MemoryKindForceExpression-162] + _ = x[MemoryKindPathExpression-163] + _ = x[MemoryKindAttachExpression-164] + _ = x[MemoryKindConstantSizedType-165] + _ = x[MemoryKindDictionaryType-166] + _ = x[MemoryKindFunctionType-167] + _ = x[MemoryKindInstantiationType-168] + _ = x[MemoryKindNominalType-169] + _ = x[MemoryKindOptionalType-170] + _ = x[MemoryKindReferenceType-171] + _ = x[MemoryKindRestrictedType-172] + _ = x[MemoryKindVariableSizedType-173] + _ = x[MemoryKindPosition-174] + _ = x[MemoryKindRange-175] + _ = x[MemoryKindElaboration-176] + _ = x[MemoryKindActivation-177] + _ = x[MemoryKindActivationEntries-178] + _ = x[MemoryKindVariableSizedSemaType-179] + _ = x[MemoryKindConstantSizedSemaType-180] + _ = x[MemoryKindDictionarySemaType-181] + _ = x[MemoryKindOptionalSemaType-182] + _ = x[MemoryKindRestrictedSemaType-183] + _ = x[MemoryKindReferenceSemaType-184] + _ = x[MemoryKindCapabilitySemaType-185] + _ = x[MemoryKindOrderedMap-186] + _ = x[MemoryKindOrderedMapEntryList-187] + _ = x[MemoryKindOrderedMapEntry-188] + _ = x[MemoryKindLast-189] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 394, 416, 441, 457, 477, 500, 527, 543, 562, 581, 600, 623, 646, 666, 684, 704, 723, 743, 761, 777, 797, 813, 831, 852, 871, 886, 904, 925, 948, 970, 989, 1011, 1033, 1057, 1083, 1107, 1133, 1154, 1175, 1199, 1223, 1243, 1263, 1283, 1306, 1322, 1338, 1367, 1387, 1406, 1435, 1464, 1485, 1497, 1513, 1533, 1550, 1569, 1590, 1606, 1625, 1651, 1679, 1707, 1726, 1746, 1767, 1788, 1803, 1812, 1827, 1832, 1840, 1857, 1871, 1881, 1891, 1901, 1910, 1920, 1930, 1937, 1947, 1955, 1960, 1973, 1982, 1995, 2008, 2025, 2033, 2040, 2054, 2069, 2088, 2108, 2129, 2149, 2168, 2184, 2206, 2223, 2242, 2268, 2285, 2304, 2318, 2335, 2348, 2367, 2379, 2390, 2405, 2418, 2433, 2447, 2462, 2479, 2493, 2506, 2522, 2539, 2559, 2574, 2594, 2614, 2634, 2650, 2665, 2686, 2701, 2717, 2735, 2752, 2768, 2785, 2804, 2819, 2833, 2849, 2866, 2880, 2892, 2909, 2920, 2932, 2945, 2959, 2976, 2984, 2989, 3000, 3010, 3027, 3048, 3069, 3087, 3103, 3121, 3138, 3156, 3166, 3185, 3200, 3204} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 408, 426, 448, 473, 489, 509, 532, 559, 575, 594, 613, 632, 655, 678, 698, 716, 736, 755, 775, 793, 809, 829, 845, 863, 884, 903, 918, 936, 957, 980, 1002, 1021, 1043, 1065, 1089, 1115, 1139, 1165, 1186, 1207, 1231, 1255, 1275, 1295, 1315, 1338, 1354, 1370, 1399, 1419, 1438, 1467, 1496, 1517, 1529, 1545, 1565, 1582, 1601, 1622, 1638, 1657, 1683, 1711, 1739, 1758, 1778, 1799, 1820, 1835, 1844, 1859, 1864, 1872, 1889, 1903, 1913, 1923, 1933, 1942, 1952, 1962, 1969, 1979, 1987, 1992, 2005, 2014, 2027, 2040, 2057, 2065, 2072, 2086, 2101, 2120, 2140, 2161, 2181, 2200, 2216, 2238, 2255, 2274, 2300, 2317, 2336, 2350, 2367, 2380, 2399, 2411, 2422, 2437, 2450, 2465, 2479, 2494, 2511, 2525, 2538, 2554, 2571, 2591, 2606, 2626, 2646, 2666, 2682, 2697, 2718, 2733, 2749, 2767, 2784, 2800, 2817, 2836, 2851, 2865, 2881, 2898, 2912, 2924, 2941, 2952, 2964, 2977, 2991, 3008, 3016, 3021, 3032, 3042, 3059, 3080, 3101, 3119, 3135, 3153, 3170, 3188, 3198, 3217, 3232, 3236} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 196d1bfc2e..37e303b1fb 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -131,28 +131,29 @@ var ( // Interpreter values - SimpleCompositeValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindSimpleCompositeValueBase) - AtreeMapElementOverhead = NewConstantMemoryUsage(MemoryKindAtreeMapElementOverhead) - AtreeArrayElementOverhead = NewConstantMemoryUsage(MemoryKindAtreeArrayElementOverhead) - CompositeTypeInfoMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeTypeInfo) - CompositeFieldMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeField) - DictionaryValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryValueBase) - ArrayValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindArrayValueBase) - CompositeValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeValueBase) - AddressValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAddressValue) - BoundFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindBoundFunctionValue) - HostFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindHostFunctionValue) - InterpretedFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindInterpretedFunctionValue) - StorageCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageCapabilityValue) - EphemeralReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindEphemeralReferenceValue) - StorageReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageReferenceValue) - AccountReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountReferenceValue) - PathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPathLinkValue) - AccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountLinkValue) - PathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPathValue) - OptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalValue) - TypeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindTypeValue) - PublishedValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPublishedValue) + SimpleCompositeValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindSimpleCompositeValueBase) + AtreeMapElementOverhead = NewConstantMemoryUsage(MemoryKindAtreeMapElementOverhead) + AtreeArrayElementOverhead = NewConstantMemoryUsage(MemoryKindAtreeArrayElementOverhead) + CompositeTypeInfoMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeTypeInfo) + CompositeFieldMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeField) + DictionaryValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryValueBase) + ArrayValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindArrayValueBase) + CompositeValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeValueBase) + AddressValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAddressValue) + BoundFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindBoundFunctionValue) + HostFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindHostFunctionValue) + InterpretedFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindInterpretedFunctionValue) + StorageCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageCapabilityValue) + EphemeralReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindEphemeralReferenceValue) + StorageReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageReferenceValue) + AccountReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountReferenceValue) + PathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPathLinkValue) + AccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountLinkValue) + PathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPathValue) + OptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalValue) + TypeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindTypeValue) + PublishedValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPublishedValue) + StorageCapabilityControllerValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageCapabilityControllerValue) // Static Types @@ -230,32 +231,32 @@ var ( // Following are the known memory usage amounts for string representation of interpreter values. // Same as `len(format.X)`. However, values are hard-coded to avoid the circular dependency. - VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) - TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) - FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) - TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) - NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) - StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) - AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) - SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) - AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) - HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) - AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) - PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) - AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) - PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) - AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) - PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) - AuthAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.StorageCapabilities()")) - AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) - AuthAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Capabilities()")) - PublicAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Capabilities()")) - AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) - StorageCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: )")) - AccountCapabilityControllerStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) - PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) - PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) + VoidStringMemoryUsage = NewRawStringMemoryUsage(len("()")) + TrueStringMemoryUsage = NewRawStringMemoryUsage(len("true")) + FalseStringMemoryUsage = NewRawStringMemoryUsage(len("false")) + TypeValueStringMemoryUsage = NewRawStringMemoryUsage(len("Type<>()")) + NilValueStringMemoryUsage = NewRawStringMemoryUsage(len("nil")) + StorageReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageReference()")) + AccountReferenceValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountReference()")) + SeenReferenceStringMemoryUsage = NewRawStringMemoryUsage(3) // len(ellipsis) + AddressValueStringMemoryUsage = NewRawStringMemoryUsage(AddressLength*2 + 2) // len(bytes-to-hex + prefix) + HostFunctionValueStringMemoryUsage = NewRawStringMemoryUsage(len("Function(...)")) + AuthAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount()")) + PublicAccountValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount()")) + AuthAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Contracts()")) + PublicAccountContractsStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Contracts()")) + AuthAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Keys()")) + PublicAccountKeysStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Keys()")) + AuthAccountStorageCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.StorageCapabilities()")) + AuthAccountAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.AccountCapabilities()")) + AuthAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Capabilities()")) + PublicAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Capabilities()")) + AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) + StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) + StorageCapabilityControllerValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: , target: )")) + AccountCapabilityControllerValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) + PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) + PublishedValueStringMemoryUsage = NewRawStringMemoryUsage(len("PublishedValue<>()")) // Static types string representations diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 2b314ede16..de3d110eaf 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -37,11 +37,12 @@ func StorageCapability(borrowType string, id string, address string, path string ) } -func StorageCapabilityController(borrowType string, capabilityID string) string { +func StorageCapabilityController(borrowType string, capabilityID string, target string) string { return fmt.Sprintf( - "StorageCapabilityController(borrowType: %s, capabilityID: %s)", + "StorageCapabilityController(borrowType: %s, capabilityID: %s, target: %s)", borrowType, capabilityID, + target, ) } diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index b0df451fd5..93d535171c 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -311,6 +311,9 @@ func (d StorableDecoder) decodeStorable() (atree.Storable, error) { case CBORTagTypeValue: storable, err = d.decodeType() + case CBORTagStorageCapabilityControllerValue: + storable, err = d.decodeStorageCapabilityController() + default: return nil, UnsupportedTagDecodingError{ Tag: num, @@ -962,6 +965,71 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err ), nil } +func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapabilityControllerValue, error) { + + const expectedLength = encodedStorageCapabilityControllerValueLength + + size, err := d.decoder.DecodeArrayHead() + if err != nil { + if e, ok := err.(*cbor.WrongTypeError); ok { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller encoding: expected [%d]any, got %s", + expectedLength, + e.ActualType.String(), + ) + } + return nil, err + } + + if size != expectedLength { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller encoding: expected [%d]any, got [%d]any", + expectedLength, + size, + ) + } + + // Decode path at array index encodedStorageCapabilityControllerValueTargetPathFieldKey + num, err := d.decoder.DecodeTagNumber() + if err != nil { + return nil, errors.NewUnexpectedError("invalid storage capability controller target path encoding: %w", err) + } + if num != CBORTagPathValue { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller target path encoding: expected CBOR tag %d, got %d", + CBORTagPathValue, + num, + ) + } + pathValue, err := d.decodePath() + if err != nil { + return nil, errors.NewUnexpectedError("invalid storage capability controller target path encoding: %w", err) + } + + // Decode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey + borrowStaticType, err := d.DecodeStaticType() + if err != nil { + return nil, errors.NewUnexpectedError("invalid storage capability controller borrow type encoding: %w", err) + } + + // Decode capability ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey + + capabilityID, err := d.decoder.DecodeUint64() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller capability ID: %w", + err, + ) + } + + return NewStorageCapabilityControllerValue( + d.memoryGauge, + borrowStaticType, + pathValue, + UInt64Value(capabilityID), + ), nil +} + func (d StorableDecoder) decodePathLink() (PathLinkValue, error) { const expectedLength = encodedPathLinkValueLength diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 523e93ee00..921144758b 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -200,7 +200,7 @@ const ( CBORTagPathLinkValue CBORTagPublishedValue CBORTagAccountLinkValue - _ + CBORTagStorageCapabilityControllerValue _ _ _ @@ -1033,6 +1033,56 @@ func (v TypeValue) Encode(e *atree.Encoder) error { return EncodeStaticType(e.CBOR, v.Type) } +// NOTE: NEVER change, only add/increment; ensure uint64 +const ( + // encodedStorageCapabilityControllerValueTargetPathFieldKey uint64 = 0 + // encodedStorageCapabilityControllerValueBorrowTypeFieldKey uint64 = 1 + // encodedStorageCapabilityControllerValueCapabilityIDFieldKey uint64 = 2 + + // !!! *WARNING* !!! + // + // encodedStorageCapabilityControllerValueLength MUST be updated when new element is added. + // It is used to verify encoded storage capability controller length during decoding. + encodedStorageCapabilityControllerValueLength = 3 +) + +// Encode encodes StorageCapabilityControllerValue as +// +// cbor.Tag{ +// Number: CBORTagStorageCapabilityControllerValue, +// Content: []any{ +// encodedStorageCapabilityControllerValueTargetPathFieldKey: PathValue(v.TargetPath), +// encodedStorageCapabilityControllerValueBorrowTypeFieldKey: StaticType(v.BorrowType), +// encodedStorageCapabilityControllerValueCapabilityIDFieldKey: UInt64Value(v.CapabilityID), +// }, +// } +func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { + // Encode tag number and array head + err := e.CBOR.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagStorageCapabilityControllerValue, + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + // Encode target path at array index encodedStorageCapabilityControllerValueTargetPathFieldKey + err = v.TargetPath.Encode(e) + if err != nil { + return err + } + + // Encode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey + err = EncodeStaticType(e.CBOR, v.BorrowType) + if err != nil { + return err + } + + // Encode ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey + return e.CBOR.EncodeUint64(uint64(v.CapabilityID)) +} + func StaticTypeToBytes(t StaticType) (cbor.RawMessage, error) { var buf bytes.Buffer enc := CBOREncMode.NewStreamEncoder(&buf) diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index e78030c05d..2a74944d49 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3221,21 +3221,24 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { func TestEncodeDecodePathLinkValue(t *testing.T) { t.Parallel() - - expectedLinkEncodingPrefix := []byte{ - // tag - 0xd8, CBORTagPathLinkValue, - // array, 2 items follow - 0x82, - 0xd8, CBORTagPathValue, - // array, 2 items follow - 0x82, - // positive integer 3 - 0x3, - // UTF-8 string, length 3 - 0x63, - // b, a, r - 0x62, 0x61, 0x72, + assemble := func(bytes ...byte) []byte { + result := []byte{ + // tag + 0xd8, CBORTagPathLinkValue, + // array, 2 items follow + 0x82, + 0xd8, CBORTagPathValue, + // array, 2 items follow + 0x82, + // positive integer 3 + 0x3, + // UTF-8 string, length 3 + 0x63, + // b, a, r + 0x62, 0x61, 0x72, + } + result = append(result, bytes...) + return result } t.Run("primitive, Bool", func(t *testing.T) { @@ -3247,9 +3250,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { Type: ConvertSemaToPrimitiveStaticType(nil, sema.BoolType), } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagPrimitiveStaticType, 0x6, @@ -3274,18 +3275,12 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - encodedType := []byte{ + encoded := assemble( // tag 0xd8, CBORTagOptionalStaticType, // tag 0xd8, CBORTagPrimitiveStaticType, 0x6, - } - - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], - encodedType..., ) testEncodeDecode(t, @@ -3309,9 +3304,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { ), } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagCompositeStaticType, // array, 2 items follow @@ -3348,9 +3341,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagInterfaceStaticType, // array, 2 items follow @@ -3386,9 +3377,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagVariableSizedStaticType, // tag @@ -3416,9 +3405,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagConstantSizedStaticType, // array, 2 items follow @@ -3450,9 +3437,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagReferenceStaticType, // array, 2 items follow @@ -3484,9 +3469,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagReferenceStaticType, // array, 2 items follow @@ -3518,9 +3501,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagDictionaryStaticType, // array, 2 items follow @@ -3566,9 +3547,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagRestrictedStaticType, // array, 2 items follow @@ -3636,9 +3615,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { Type: CapabilityStaticType{}, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagCapabilityStaticType, // null @@ -3664,9 +3641,7 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { }, } - //nolint:gocritic - encoded := append( - expectedLinkEncodingPrefix[:], + encoded := assemble( // tag 0xd8, CBORTagCapabilityStaticType, // tag @@ -3896,3 +3871,495 @@ func TestCBORTagValue(t *testing.T) { require.Equal(t, byte(222), byte(CBORTag_Count)) }) } + +func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { + + t.Parallel() + + assemble := func(bytes ...byte) []byte { + result := []byte{ + // tag + 0xd8, CBORTagStorageCapabilityControllerValue, + // array, 3 items follow + 0x83, + 0xd8, CBORTagPathValue, + // array, 2 items follow + 0x82, + // positive integer 3 + 0x3, + // UTF-8 string, length 3 + 0x63, + // b, a, r + 0x62, 0x61, 0x72, + } + result = append(result, bytes...) + result = append(result, + // positive integer 42 + 0x18, 0x2A, + ) + return result + } + + const capabilityID = 42 + + t.Run("primitive, Bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: ConvertSemaToPrimitiveStaticType(nil, sema.BoolType), + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("optional, primitive, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: OptionalStaticType{ + Type: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagOptionalStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("composite, struct, qualified identifier", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: NewCompositeStaticTypeComputeTypeID( + nil, + utils.TestLocation, + "SimpleStruct", + ), + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagCompositeStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 12 + 0x6c, + // SimpleStruct + 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("interface, struct, qualified identifier", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: InterfaceStaticType{ + Location: utils.TestLocation, + QualifiedIdentifier: "SimpleInterface", + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagInterfaceStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 22 + 0x6F, + // SimpleInterface + 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("variable-sized, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: VariableSizedStaticType{ + Type: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagVariableSizedStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("constant-sized, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: ConstantSizedStaticType{ + Type: PrimitiveStaticTypeBool, + Size: 42, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagConstantSizedStaticType, + // array, 2 items follow + 0x82, + // positive integer 42 + 0x18, 0x2A, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("reference type, authorized, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: ReferenceStaticType{ + Authorized: true, + BorrowedType: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // true + 0xf5, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("reference type, unauthorized, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: ReferenceStaticType{ + Authorized: false, + BorrowedType: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("dictionary, bool, string", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: DictionaryStaticType{ + KeyType: PrimitiveStaticTypeBool, + ValueType: PrimitiveStaticTypeString, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagDictionaryStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x8, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("restricted", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: &RestrictedStaticType{ + Type: NewCompositeStaticTypeComputeTypeID( + nil, + utils.TestLocation, + "S", + ), + Restrictions: []InterfaceStaticType{ + { + Location: utils.TestLocation, + QualifiedIdentifier: "I1", + }, + { + Location: utils.TestLocation, + QualifiedIdentifier: "I2", + }, + }, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagRestrictedStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagCompositeStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 1 + 0x61, + // S + 0x53, + // array, length 2 + 0x82, + // tag + 0xd8, CBORTagInterfaceStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 2 + 0x62, + // I1 + 0x49, 0x31, + // tag + 0xd8, CBORTagInterfaceStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 2 + 0x62, + // I2 + 0x49, 0x32, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("capability, none", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: CapabilityStaticType{}, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagCapabilityStaticType, + // null + 0xf6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("capability, primitive, bool", func(t *testing.T) { + + t.Parallel() + + value := &StorageCapabilityControllerValue{ + TargetPath: publicPathValue, + BorrowType: CapabilityStaticType{ + BorrowType: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagCapabilityStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("larger than max inline size", func(t *testing.T) { + + t.Parallel() + + maxInlineElementSize := atree.MaxInlineArrayElementSize + identifier := strings.Repeat("x", int(maxInlineElementSize+1)) + + path := PathValue{ + Domain: common.PathDomainStorage, + Identifier: identifier, + } + + expected := &StorageCapabilityControllerValue{ + TargetPath: path, + BorrowType: PrimitiveStaticTypeNever, + CapabilityID: capabilityID, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + maxInlineElementSize: maxInlineElementSize, + encoded: []byte{ + // tag + 0xd8, atree.CBORTagStorageID, + + // storage ID + 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + }, + }, + ) + }) +} diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 0e3ce51fbb..6cc42c76a4 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -70,7 +70,7 @@ func NewAccountCapabilityControllerValue( var str string stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { if str == "" { - common.UseMemory(memoryGauge, common.AccountCapabilityControllerStringMemoryUsage) + common.UseMemory(memoryGauge, common.AccountCapabilityControllerValueStringMemoryUsage) borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 8b8474eb30..6c398f16f1 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -19,115 +19,163 @@ package interpreter import ( - "fmt" + "github.com/onflow/atree" "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/format" - "github.com/onflow/cadence/runtime/sema" ) -var storageCapabilityControllerFieldNames = []string{ - sema.StorageCapabilityControllerTypeBorrowTypeFieldName, - sema.StorageCapabilityControllerTypeCapabilityIDFieldName, +// StorageCapabilityControllerValue + +type StorageCapabilityControllerValue struct { + BorrowType StaticType + TargetPath PathValue + CapabilityID UInt64Value +} + +func NewUnmeteredStorageCapabilityControllerValue( + staticType StaticType, + targetPath PathValue, + capabilityID UInt64Value, +) *StorageCapabilityControllerValue { + return &StorageCapabilityControllerValue{ + BorrowType: staticType, + TargetPath: targetPath, + CapabilityID: capabilityID, + } } func NewStorageCapabilityControllerValue( - gauge common.MemoryGauge, - capabilityID uint64, - borrowType StaticType, - delete func() error, - getTarget func() (PathValue, error), - retarget func(newPath PathValue) error, -) Value { + memoryGauge common.MemoryGauge, + staticType StaticType, + targetPath PathValue, + capabilityID UInt64Value, +) *StorageCapabilityControllerValue { + // Constant because its constituents are already metered. + common.UseMemory(memoryGauge, common.StorageCapabilityControllerValueMemoryUsage) + return NewUnmeteredStorageCapabilityControllerValue( + staticType, + targetPath, + capabilityID, + ) +} - borrowTypeValue := NewTypeValue(gauge, borrowType) - fields := map[string]Value{ - sema.StorageCapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, - sema.StorageCapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { - return capabilityID - }), - } +var _ Value = &StorageCapabilityControllerValue{} +var _ atree.Value = &StorageCapabilityControllerValue{} +var _ EquatableValue = &StorageCapabilityControllerValue{} + +func (*StorageCapabilityControllerValue) IsValue() {} + +func (v *StorageCapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { + visitor.VisitStorageCapabilityControllerValue(interpreter, v) +} + +func (v *StorageCapabilityControllerValue) Walk(_ *Interpreter, walkChild func(Value)) { + walkChild(v.TargetPath) + walkChild(v.CapabilityID) +} + +func (v *StorageCapabilityControllerValue) StaticType(_ *Interpreter) StaticType { + return PrimitiveStaticTypeStorageCapabilityController +} + +func (*StorageCapabilityControllerValue) IsImportable(_ *Interpreter) bool { + return false +} + +func (v *StorageCapabilityControllerValue) String() string { + return v.RecursiveString(SeenReferences{}) +} + +func (v *StorageCapabilityControllerValue) RecursiveString(seenReferences SeenReferences) string { + return format.StorageCapabilityController( + v.BorrowType.String(), + v.TargetPath.RecursiveString(seenReferences), + v.CapabilityID.RecursiveString(seenReferences), + ) +} + +func (v *StorageCapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + common.UseMemory(memoryGauge, common.StorageCapabilityControllerValueStringMemoryUsage) - computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { - switch name { - case sema.StorageCapabilityControllerTypeTargetFunctionName: - return NewHostFunctionValue( - gauge, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation Invocation) Value { - target, err := getTarget() - if err != nil { - panic(err) - } - - return target - }, - ) - - case sema.StorageCapabilityControllerTypeDeleteFunctionName: - return NewHostFunctionValue( - gauge, - sema.StorageCapabilityControllerTypeDeleteFunctionType, - func(invocation Invocation) Value { - err := delete() - if err != nil { - panic(err) - } - - return Void - }, - ) - - case sema.StorageCapabilityControllerTypeRetargetFunctionName: - return NewHostFunctionValue( - gauge, - sema.StorageCapabilityControllerTypeRetargetFunctionType, - func(invocation Invocation) Value { - newTarget, ok := invocation.Arguments[0].(PathValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - err := retarget(newTarget) - if !ok { - panic(err) - } - - return Void - }, - ) - } - - return nil + return format.StorageCapabilityController( + v.BorrowType.MeteredString(memoryGauge), + v.CapabilityID.MeteredString(memoryGauge, seenReferences), + v.TargetPath.MeteredString(memoryGauge, seenReferences), + ) +} + +func (v *StorageCapabilityControllerValue) ConformsToStaticType( + _ *Interpreter, + _ LocationRange, + _ TypeConformanceResults, +) bool { + return true +} + +func (v *StorageCapabilityControllerValue) Equal(interpreter *Interpreter, locationRange LocationRange, other Value) bool { + otherController, ok := other.(*StorageCapabilityControllerValue) + if !ok { + return false } - var str string - stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { - if str == "" { - common.UseMemory(memoryGauge, common.StorageCapabilityControllerStringMemoryUsage) + return otherController.TargetPath.Equal(interpreter, locationRange, v.TargetPath) && + otherController.BorrowType.Equal(v.BorrowType) && + otherController.CapabilityID.Equal(interpreter, locationRange, v.CapabilityID) +} - borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) +func (*StorageCapabilityControllerValue) IsStorable() bool { + return true +} - memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) - common.UseMemory(memoryGauge, memoryUsage) +func (v *StorageCapabilityControllerValue) Storable(storage atree.SlabStorage, address atree.Address, maxInlineSize uint64) (atree.Storable, error) { + return maybeLargeImmutableStorable(v, storage, address, maxInlineSize) +} - idStr := fmt.Sprint(capabilityID) +func (*StorageCapabilityControllerValue) NeedsStoreTo(_ atree.Address) bool { + return false +} - str = format.StorageCapabilityController(borrowTypeStr, idStr) - } +func (*StorageCapabilityControllerValue) IsResourceKinded(_ *Interpreter) bool { + return false +} - return str +func (v *StorageCapabilityControllerValue) Transfer( + interpreter *Interpreter, + _ LocationRange, + _ atree.Address, + remove bool, + storable atree.Storable, +) Value { + if remove { + interpreter.RemoveReferencedSlab(storable) } + return v +} - return NewSimpleCompositeValue( - gauge, - sema.StorageCapabilityControllerType.ID(), - PrimitiveStaticTypeStorageCapabilityController, - storageCapabilityControllerFieldNames, - fields, - computeField, - nil, - stringer, - ) +func (v *StorageCapabilityControllerValue) Clone(interpreter *Interpreter) Value { + return &StorageCapabilityControllerValue{ + TargetPath: v.TargetPath.Clone(interpreter).(PathValue), + BorrowType: v.BorrowType, + CapabilityID: v.CapabilityID, + } +} + +func (v *StorageCapabilityControllerValue) DeepRemove(_ *Interpreter) { + // NO-OP +} + +func (v *StorageCapabilityControllerValue) ByteSize() uint32 { + return mustStorableSize(v) +} + +func (v *StorageCapabilityControllerValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { + return v, nil +} + +func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { + return []atree.Storable{ + v.TargetPath, + v.CapabilityID, + } } diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index 754b766d6f..9c60ab34fc 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -62,52 +62,54 @@ type Visitor interface { VisitInterpretedFunctionValue(interpreter *Interpreter, value *InterpretedFunctionValue) VisitHostFunctionValue(interpreter *Interpreter, value *HostFunctionValue) VisitBoundFunctionValue(interpreter *Interpreter, value BoundFunctionValue) + VisitStorageCapabilityControllerValue(interpreter *Interpreter, v *StorageCapabilityControllerValue) } type EmptyVisitor struct { - SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) - TypeValueVisitor func(interpreter *Interpreter, value TypeValue) - VoidValueVisitor func(interpreter *Interpreter, value VoidValue) - BoolValueVisitor func(interpreter *Interpreter, value BoolValue) - CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) - StringValueVisitor func(interpreter *Interpreter, value *StringValue) - ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool - IntValueVisitor func(interpreter *Interpreter, value IntValue) - Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) - Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) - Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) - Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) - Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) - Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) - UIntValueVisitor func(interpreter *Interpreter, value UIntValue) - UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) - UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) - UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) - UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) - UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) - UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) - Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) - Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) - Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) - Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) - Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) - UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) - CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool - DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool - NilValueVisitor func(interpreter *Interpreter, value NilValue) - SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool - StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) - AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) - EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) - AddressValueVisitor func(interpreter *Interpreter, value AddressValue) - PathValueVisitor func(interpreter *Interpreter, value PathValue) - StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) - PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) - AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) - PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) - InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) - HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) - BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) + SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) + TypeValueVisitor func(interpreter *Interpreter, value TypeValue) + VoidValueVisitor func(interpreter *Interpreter, value VoidValue) + BoolValueVisitor func(interpreter *Interpreter, value BoolValue) + CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) + StringValueVisitor func(interpreter *Interpreter, value *StringValue) + ArrayValueVisitor func(interpreter *Interpreter, value *ArrayValue) bool + IntValueVisitor func(interpreter *Interpreter, value IntValue) + Int8ValueVisitor func(interpreter *Interpreter, value Int8Value) + Int16ValueVisitor func(interpreter *Interpreter, value Int16Value) + Int32ValueVisitor func(interpreter *Interpreter, value Int32Value) + Int64ValueVisitor func(interpreter *Interpreter, value Int64Value) + Int128ValueVisitor func(interpreter *Interpreter, value Int128Value) + Int256ValueVisitor func(interpreter *Interpreter, value Int256Value) + UIntValueVisitor func(interpreter *Interpreter, value UIntValue) + UInt8ValueVisitor func(interpreter *Interpreter, value UInt8Value) + UInt16ValueVisitor func(interpreter *Interpreter, value UInt16Value) + UInt32ValueVisitor func(interpreter *Interpreter, value UInt32Value) + UInt64ValueVisitor func(interpreter *Interpreter, value UInt64Value) + UInt128ValueVisitor func(interpreter *Interpreter, value UInt128Value) + UInt256ValueVisitor func(interpreter *Interpreter, value UInt256Value) + Word8ValueVisitor func(interpreter *Interpreter, value Word8Value) + Word16ValueVisitor func(interpreter *Interpreter, value Word16Value) + Word32ValueVisitor func(interpreter *Interpreter, value Word32Value) + Word64ValueVisitor func(interpreter *Interpreter, value Word64Value) + Fix64ValueVisitor func(interpreter *Interpreter, value Fix64Value) + UFix64ValueVisitor func(interpreter *Interpreter, value UFix64Value) + CompositeValueVisitor func(interpreter *Interpreter, value *CompositeValue) bool + DictionaryValueVisitor func(interpreter *Interpreter, value *DictionaryValue) bool + NilValueVisitor func(interpreter *Interpreter, value NilValue) + SomeValueVisitor func(interpreter *Interpreter, value *SomeValue) bool + StorageReferenceValueVisitor func(interpreter *Interpreter, value *StorageReferenceValue) + AccountReferenceValueVisitor func(interpreter *Interpreter, value *AccountReferenceValue) + EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) + AddressValueVisitor func(interpreter *Interpreter, value AddressValue) + PathValueVisitor func(interpreter *Interpreter, value PathValue) + StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) + PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) + AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) + PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) + InterpretedFunctionValueVisitor func(interpreter *Interpreter, value *InterpretedFunctionValue) + HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) + BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) + StorageCapabilityControllerValueVisitor func(interpreter *Interpreter, value *StorageCapabilityControllerValue) } var _ Visitor = &EmptyVisitor{} @@ -412,3 +414,10 @@ func (v EmptyVisitor) VisitBoundFunctionValue(interpreter *Interpreter, value Bo } v.BoundFunctionValueVisitor(interpreter, value) } + +func (v EmptyVisitor) VisitStorageCapabilityControllerValue(interpreter *Interpreter, value *StorageCapabilityControllerValue) { + if v.StorageCapabilityControllerValueVisitor == nil { + return + } + v.StorageCapabilityControllerValueVisitor(interpreter, value) +} From 03a054ac89b7a423283a45b1bfe266c0d54bf076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 13:11:55 -0700 Subject: [PATCH 075/246] generate capability ID by introducing new account ID generation via runtime interface --- runtime/environment.go | 4 ++++ runtime/interface.go | 2 ++ runtime/runtime_test.go | 9 ++++++++ runtime/stdlib/account.go | 47 ++++++++++++++++++++++++++++++++------- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index 75374c8c8a..578f95fdc1 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -268,6 +268,10 @@ func (e *interpreterEnvironment) CreateAccount(payer common.Address) (address co return e.runtimeInterface.CreateAccount(payer) } +func (e *interpreterEnvironment) GenerateAccountID(address common.Address) (uint64, error) { + return e.runtimeInterface.GenerateAccountID(address) +} + func (e *interpreterEnvironment) EmitEvent( inter *interpreter.Interpreter, eventType *sema.CompositeType, diff --git a/runtime/interface.go b/runtime/interface.go index 9463b94620..ced5a17f1f 100644 --- a/runtime/interface.go +++ b/runtime/interface.go @@ -146,6 +146,8 @@ type Interface interface { oldOwner common.Address, newOwner common.Address, ) + // GenerateAccountID generates a new unique ID for the given account. + GenerateAccountID(address common.Address) (uint64, error) } type MeterInterface interface { diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 10305e1d9b..054d2ec2aa 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -222,6 +222,15 @@ type testRuntimeInterface struct { memoryUsed func() (uint64, error) interactionUsed func() (uint64, error) updatedContractCode bool + generateAccountID func(address common.Address) (uint64, error) +} + +func (i *testRuntimeInterface) GenerateAccountID(address common.Address) (uint64, error) { + if i.generateAccountID == nil { + return 0, nil + } + + return i.generateAccountID(address) } // testRuntimeInterface should implement Interface diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index ad8ac598f8..b95fb4dc74 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -59,7 +59,13 @@ type EventEmitter interface { ) } +type AccountIDGenerator interface { + // GenerateAccountID generates a new unique ID for the given account. + GenerateAccountID(address common.Address) (uint64, error) +} + type AuthAccountHandler interface { + AccountIDGenerator BalanceProvider AvailableBalanceProvider StorageUsedProvider @@ -218,6 +224,7 @@ func NewAuthAccountValue( func() interpreter.Value { capabilities := newAuthAccountCapabilitiesValue( gauge, + handler, addressValue, ) return interpreter.NewEphemeralReferenceValue( @@ -2181,6 +2188,7 @@ func CodeToHashValue(inter *interpreter.Interpreter, code []byte) *interpreter.A func newAuthAccountStorageCapabilitiesValue( gauge common.MemoryGauge, + accountIDGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) interpreter.Value { // TODO: @@ -2190,7 +2198,7 @@ func newAuthAccountStorageCapabilitiesValue( nil, nil, nil, - newAuthAccountStorageCapabilitiesIssueFunction(gauge, addressValue), + newAuthAccountStorageCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), ) } @@ -2211,9 +2219,9 @@ func newAuthAccountAccountCapabilitiesValue( func newAuthAccountCapabilitiesValue( gauge common.MemoryGauge, + idGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) interpreter.Value { - // TODO: return interpreter.NewAuthAccountCapabilitiesValue( gauge, addressValue, @@ -2224,6 +2232,7 @@ func newAuthAccountCapabilitiesValue( func() interpreter.Value { storageCapabilities := newAuthAccountStorageCapabilitiesValue( gauge, + idGenerator, addressValue, ) return interpreter.NewEphemeralReferenceValue( @@ -2250,8 +2259,10 @@ func newAuthAccountCapabilitiesValue( func newAuthAccountStorageCapabilitiesIssueFunction( gauge common.MemoryGauge, + idGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() return interpreter.NewHostFunctionValue( gauge, sema.AuthAccountStorageCapabilitiesTypeIssueFunctionType, @@ -2259,8 +2270,8 @@ func newAuthAccountStorageCapabilitiesIssueFunction( // Get path argument - pathValue, ok := invocation.Arguments[0].(interpreter.PathValue) - if !ok || pathValue.Domain != common.PathDomainStorage { + targetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || targetPathValue.Domain != common.PathDomainStorage { panic(errors.NewUnreachableError()) } @@ -2272,16 +2283,36 @@ func newAuthAccountStorageCapabilitiesIssueFunction( panic(errors.NewUnreachableError()) } - // TODO: create and write StorageCapabilityController + // Create and write StorageCapabilityController borrowStaticType := interpreter.ConvertSemaReferenceTypeToStaticReferenceType(gauge, borrowType) + var capabilityID uint64 + var err error + errors.WrapPanic(func() { + capabilityID, err = idGenerator.GenerateAccountID(address) + }) + if err != nil { + panic(err) + } + + capabilityIDValue := interpreter.UInt64Value(capabilityID) + + _ = interpreter.NewStorageCapabilityControllerValue( + gauge, + borrowStaticType, + targetPathValue, + capabilityIDValue, + ) + + // TODO: store controller in {ID -> controller} + // TODO: store controller in {path -> {controller ID -> void}} + return interpreter.NewStorageCapabilityValue( gauge, - // TODO: - interpreter.TodoCapabilityID, + capabilityIDValue, addressValue, - pathValue, + targetPathValue, borrowStaticType, ) }, From 9ea7db79d0f0c2cdba9695b17d7973a7578573e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 13:12:03 -0700 Subject: [PATCH 076/246] simplify --- runtime/interpreter/interpreter.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 9c7daee78c..37adeddafe 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2234,8 +2234,7 @@ func (interpreter *Interpreter) StoredValueExists( domain string, identifier string, ) bool { - config := interpreter.SharedState.Config - accountStorage := config.Storage.GetStorageMap(storageAddress, domain, false) + accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false) if accountStorage == nil { return false } @@ -2247,8 +2246,7 @@ func (interpreter *Interpreter) ReadStored( domain string, identifier string, ) Value { - config := interpreter.SharedState.Config - accountStorage := config.Storage.GetStorageMap(storageAddress, domain, false) + accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false) if accountStorage == nil { return nil } @@ -2261,8 +2259,7 @@ func (interpreter *Interpreter) WriteStored( identifier string, value Value, ) { - config := interpreter.SharedState.Config - accountStorage := config.Storage.GetStorageMap(storageAddress, domain, true) + accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, true) accountStorage.WriteValue(interpreter, identifier, value) interpreter.recordStorageMutation() } @@ -3292,8 +3289,7 @@ func (interpreter *Interpreter) IsSubTypeOfSemaType(subType StaticType, superTyp } func (interpreter *Interpreter) domainPaths(address common.Address, domain common.PathDomain) []Value { - config := interpreter.SharedState.Config - storageMap := config.Storage.GetStorageMap(address, domain.Identifier(), false) + storageMap := interpreter.Storage().GetStorageMap(address, domain.Identifier(), false) if storageMap == nil { return []Value{} } From 33ca0c8f7844a39d0765a0f21c9420793a4c4d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 13:12:24 -0700 Subject: [PATCH 077/246] call Clone recursively --- runtime/interpreter/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 47d41c539c..58c8bf911f 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18649,7 +18649,7 @@ func (v *PublishedValue) Transfer( func (v *PublishedValue) Clone(interpreter *Interpreter) Value { return &PublishedValue{ Recipient: v.Recipient, - Value: v.Value, + Value: v.Value.Clone(interpreter).(*StorageCapabilityValue), } } From 05a7208d9fd050d26e3dbc93352a4a6f1b1f99be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 13:12:31 -0700 Subject: [PATCH 078/246] improve comment --- runtime/sema/storage_capability_controller.cdc | 4 ++-- runtime/sema/storage_capability_controller.gen.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/sema/storage_capability_controller.cdc b/runtime/sema/storage_capability_controller.cdc index 4cab4a2457..b2e5fbf2d8 100644 --- a/runtime/sema/storage_capability_controller.cdc +++ b/runtime/sema/storage_capability_controller.cdc @@ -22,7 +22,7 @@ pub struct StorageCapabilityController { /// Returns the targeted storage path of the controlled capability. pub fun target(): StoragePath - /// Retarget the capability. - /// This moves the CapCon from one CapCon array to another. + /// Retarget the controlled capability to the given storage path. + /// The path may be different or the same as the current path. pub fun retarget(target: StoragePath) } diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go index bb5684cc22..3383971baf 100644 --- a/runtime/sema/storage_capability_controller.gen.go +++ b/runtime/sema/storage_capability_controller.gen.go @@ -84,8 +84,8 @@ var StorageCapabilityControllerTypeRetargetFunctionType = &FunctionType{ } const StorageCapabilityControllerTypeRetargetFunctionDocString = ` -Retarget the capability. -This moves the CapCon from one CapCon array to another. +Retarget the controlled capability to the given storage path. +The path may be different or the same as the current path. ` const StorageCapabilityControllerTypeName = "StorageCapabilityController" From f46d99e6967d54f455a54eb10df4d84473be50fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 18:15:38 -0700 Subject: [PATCH 079/246] generalize storage map keys, introduce uint64 storage map key --- runtime/contract_test.go | 2 +- runtime/environment.go | 2 +- runtime/interpreter/decode.go | 7 + runtime/interpreter/encode.go | 5 + runtime/interpreter/encoding_test.go | 148 ++++++++++++++++-- runtime/interpreter/interpreter.go | 140 +++++++++++------ runtime/interpreter/storage_test.go | 8 +- runtime/interpreter/storagemap.go | 80 +++++----- runtime/interpreter/storagemapkey.go | 75 +++++++++ runtime/interpreter/stringatreevalue.go | 12 +- runtime/interpreter/uint64atreevalue.go | 70 +++++++++ runtime/interpreter/value.go | 51 +++--- runtime/interpreter/value_accountreference.go | 4 +- runtime/interpreter/value_test.go | 2 +- runtime/runtime.go | 15 +- runtime/stdlib/account.go | 59 +++++-- runtime/storage.go | 5 +- runtime/storage_test.go | 6 +- runtime/tests/interpreter/account_test.go | 6 +- runtime/tests/interpreter/interpreter_test.go | 6 +- runtime/tests/interpreter/metatype_test.go | 6 +- 21 files changed, 541 insertions(+), 168 deletions(-) create mode 100644 runtime/interpreter/storagemapkey.go create mode 100644 runtime/interpreter/uint64atreevalue.go diff --git a/runtime/contract_test.go b/runtime/contract_test.go index 07ea41f7ea..21d2d08540 100644 --- a/runtime/contract_test.go +++ b/runtime/contract_test.go @@ -224,7 +224,7 @@ func TestRuntimeContract(t *testing.T) { if storageMap == nil { return false } - return storageMap.ValueExists("Test") + return storageMap.ValueExists(interpreter.StringStorageMapKey("Test")) } t.Run("add", func(t *testing.T) { diff --git a/runtime/environment.go b/runtime/environment.go index 578f95fdc1..4f42e95e8c 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -896,7 +896,7 @@ func (e *interpreterEnvironment) loadContract( false, ) if storageMap != nil { - storedValue = storageMap.ReadValue(inter, location.Name) + storedValue = storageMap.ReadValue(inter, interpreter.StringStorageMapKey(location.Name)) } } diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 93d535171c..7695cfcf3a 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -184,6 +184,13 @@ func (d StorableDecoder) decodeStorable() (atree.Storable, error) { // already metered by decodeString storable = StringAtreeValue(str) + case cbor.UintType: + n, err := decodeUint64(d.decoder, d.memoryGauge) + if err != nil { + return nil, err + } + storable = Uint64AtreeValue(n) + case cbor.TagType: var num uint64 num, err = d.decoder.DecodeTagNumber() diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 921144758b..3fef561fc9 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -280,6 +280,11 @@ func (v StringAtreeValue) Encode(e *atree.Encoder) error { return e.CBOR.EncodeString(string(v)) } +// Encode encodes the value as a CBOR unsigned integer +func (v Uint64AtreeValue) Encode(e *atree.Encoder) error { + return e.CBOR.EncodeUint64(uint64(v)) +} + // cborVoidValue represents the CBOR value: // // cbor.Tag{ diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 2a74944d49..ded5db589c 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -38,7 +38,7 @@ import ( ) type encodeDecodeTest struct { - value Value + value atree.Value storable atree.Storable encoded []byte invalid bool @@ -47,7 +47,6 @@ type encodeDecodeTest struct { deepEquality bool storage Storage slabStorageID atree.StorageID - check func(actual Value) maxInlineElementSize uint64 } @@ -90,7 +89,7 @@ func testEncodeDecode(t *testing.T, test encodeDecodeTest) { } decoder := CBORDecMode.NewByteStreamDecoder(encoded) - decoded, err := DecodeStorable(decoder, test.slabStorageID, nil) + decodedStorable, err := DecodeStorable(decoder, test.slabStorageID, nil) if test.invalid { require.Error(t, err) @@ -103,8 +102,10 @@ func testEncodeDecode(t *testing.T, test encodeDecodeTest) { Storage: test.storage, }, ) + require.NoError(t, err) - decodedValue := StoredValue(inter, decoded, test.storage) + decodedValue, err := decodedStorable.StoredValue(test.storage) + require.NoError(t, err) expectedValue := test.value if test.decodedValue != nil { @@ -114,12 +115,13 @@ func testEncodeDecode(t *testing.T, test encodeDecodeTest) { if test.deepEquality { assert.Equal(t, expectedValue, decodedValue) } else { - require.NoError(t, err) - AssertValuesEqual(t, inter, expectedValue, decodedValue) - } - - if test.check != nil { - test.check(decodedValue) + if expectedValue, ok := expectedValue.(Value); ok { + storedValue, err := ConvertStoredValue(nil, decodedValue) + require.NoError(t, err) + AssertValuesEqual(t, inter, expectedValue, storedValue) + return + } + assert.Equal(t, expectedValue, decodedValue) } } } @@ -259,6 +261,132 @@ func TestEncodeDecodeString(t *testing.T) { }) } +func TestEncodeDecodeStringAtreeValue(t *testing.T) { + + t.Parallel() + + t.Run("empty", func(t *testing.T) { + + t.Parallel() + + expected := StringAtreeValue("") + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + encoded: []byte{ + // UTF-8 string, 0 bytes follow + 0x60, + }, + }) + }) + + t.Run("non-empty", func(t *testing.T) { + + t.Parallel() + + expected := StringAtreeValue("foo") + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + encoded: []byte{ + // UTF-8 string, 3 bytes follow + 0x63, + // f, o, o + 0x66, 0x6f, 0x6f, + }, + }, + ) + }) + + t.Run("larger than max inline size", func(t *testing.T) { + + t.Parallel() + + maxInlineElementSize := atree.MaxInlineArrayElementSize + expected := StringAtreeValue(strings.Repeat("x", int(maxInlineElementSize+1))) + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + maxInlineElementSize: maxInlineElementSize, + encoded: []byte{ + // tag + 0xd8, atree.CBORTagStorageID, + + // storage ID + 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + }, + }, + ) + }) +} + +func TestEncodeDecodeUint64AtreeValue(t *testing.T) { + + t.Parallel() + + t.Run("zero", func(t *testing.T) { + t.Parallel() + + testEncodeDecode(t, + encodeDecodeTest{ + value: Uint64AtreeValue(0), + encoded: []byte{ + // integer 0 + 0x0, + }, + }, + ) + }) + + t.Run("negative", func(t *testing.T) { + t.Parallel() + + testEncodeDecode(t, + encodeDecodeTest{ + encoded: []byte{ + // negative integer 42 + 0x38, + 0x29, + }, + invalid: true, + }, + ) + }) + + t.Run("positive", func(t *testing.T) { + t.Parallel() + + testEncodeDecode(t, + encodeDecodeTest{ + value: Uint64AtreeValue(42), + encoded: []byte{ + // positive integer 42 + 0x18, + 0x2a, + }, + }, + ) + }) + + t.Run("max", func(t *testing.T) { + t.Parallel() + + testEncodeDecode(t, + encodeDecodeTest{ + value: Uint64AtreeValue(math.MaxUint64), + encoded: []byte{ + // positive integer 0xffffffffffffffff + 0x1b, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + ) + }) +} + func TestEncodeDecodeArray(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 37adeddafe..f8b64461b5 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2232,7 +2232,7 @@ func (interpreter *Interpreter) NewSubInterpreter( func (interpreter *Interpreter) StoredValueExists( storageAddress common.Address, domain string, - identifier string, + identifier StorageMapKey, ) bool { accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false) if accountStorage == nil { @@ -2244,7 +2244,7 @@ func (interpreter *Interpreter) StoredValueExists( func (interpreter *Interpreter) ReadStored( storageAddress common.Address, domain string, - identifier string, + identifier StorageMapKey, ) Value { accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false) if accountStorage == nil { @@ -2256,12 +2256,11 @@ func (interpreter *Interpreter) ReadStored( func (interpreter *Interpreter) WriteStored( storageAddress common.Address, domain string, - identifier string, + key StorageMapKey, value Value, -) { +) (existed bool) { accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, true) - accountStorage.WriteValue(interpreter, identifier, value) - interpreter.recordStorageMutation() + return accountStorage.WriteValue(interpreter, key, value) } type fromStringFunctionValue struct { @@ -3294,16 +3293,19 @@ func (interpreter *Interpreter) domainPaths(address common.Address, domain commo return []Value{} } iterator := storageMap.Iterator(interpreter) - var values []Value + var paths []Value count := storageMap.Count() if count > 0 { - values = make([]Value, 0, count) - for key := iterator.NextKey(); key != ""; key = iterator.NextKey() { - values = append(values, NewPathValue(interpreter, domain, key)) + paths = make([]Value, 0, count) + for key := iterator.NextKey(); key != nil; key = iterator.NextKey() { + // TODO: unfortunately, the iterator only returns an atree.Value, not a StorageMapKey + identifier := string(key.(StringAtreeValue)) + path := NewPathValue(interpreter, domain, identifier) + paths = append(paths, path) } } - return values + return paths } func (interpreter *Interpreter) accountPaths(addressValue AddressValue, locationRange LocationRange, domain common.PathDomain, pathType StaticType) *ArrayValue { @@ -3374,7 +3376,7 @@ func (interpreter *Interpreter) newStorageIterationFunction( inter.SharedState.inStorageIteration = inIteration }() - for key, value := storageIterator.Next(); key != "" && value != nil; key, value = storageIterator.Next() { + for key, value := storageIterator.Next(); key != nil && value != nil; key, value = storageIterator.Next() { staticType := value.StaticType(inter) // Perform a forced type loading to see if the underlying type is not broken. @@ -3384,7 +3386,9 @@ func (interpreter *Interpreter) newStorageIterationFunction( continue } - pathValue := NewPathValue(inter, domain, key) + // TODO: unfortunately, the iterator only returns an atree.Value, not a StorageMapKey + identifier := string(key.(StringAtreeValue)) + pathValue := NewPathValue(inter, domain, identifier) runtimeType := NewTypeValue(inter, staticType) subInvocation := NewInvocation( @@ -3467,11 +3471,9 @@ func (interpreter *Interpreter) authAccountSaveFunction(addressValue AddressValu locationRange := invocation.LocationRange - if interpreter.StoredValueExists( - address, - domain, - identifier, - ) { + storageMapKey := StringStorageMapKey(identifier) + + if interpreter.StoredValueExists(address, domain, storageMapKey) { panic( OverwriteError{ Address: addressValue, @@ -3491,7 +3493,12 @@ func (interpreter *Interpreter) authAccountSaveFunction(addressValue AddressValu // Write new value - interpreter.WriteStored(address, domain, identifier, value) + interpreter.WriteStored( + address, + domain, + storageMapKey, + value, + ) return Void }, @@ -3517,7 +3524,9 @@ func (interpreter *Interpreter) authAccountTypeFunction(addressValue AddressValu domain := path.Domain.Identifier() identifier := path.Identifier - value := interpreter.ReadStored(address, domain, identifier) + storageMapKey := StringStorageMapKey(identifier) + + value := interpreter.ReadStored(address, domain, storageMapKey) if value == nil { return Nil @@ -3562,7 +3571,9 @@ func (interpreter *Interpreter) authAccountReadFunction(addressValue AddressValu domain := path.Domain.Identifier() identifier := path.Identifier - value := interpreter.ReadStored(address, domain, identifier) + storageMapKey := StringStorageMapKey(identifier) + + value := interpreter.ReadStored(address, domain, storageMapKey) if value == nil { return Nil @@ -3606,7 +3617,12 @@ func (interpreter *Interpreter) authAccountReadFunction(addressValue AddressValu // Remove the value from storage, // but only if the type check succeeded. if clear { - interpreter.WriteStored(address, domain, identifier, nil) + interpreter.WriteStored( + address, + domain, + storageMapKey, + nil, + ) } return NewSomeValueNonCopying(invocation.Interpreter, transferredValue) @@ -3704,10 +3720,12 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu newCapabilityDomain := newCapabilityPath.Domain.Identifier() newCapabilityIdentifier := newCapabilityPath.Identifier + storageMapKey := StringStorageMapKey(newCapabilityIdentifier) + if interpreter.StoredValueExists( address, newCapabilityDomain, - newCapabilityIdentifier, + storageMapKey, ) { return Nil } @@ -3722,7 +3740,7 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu interpreter.WriteStored( address, newCapabilityDomain, - newCapabilityIdentifier, + storageMapKey, pathLink, ) @@ -3792,10 +3810,12 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr newCapabilityDomain := newCapabilityPath.Domain.Identifier() newCapabilityIdentifier := newCapabilityPath.Identifier + storageMapKey := StringStorageMapKey(newCapabilityIdentifier) + if interpreter.StoredValueExists( address, newCapabilityDomain, - newCapabilityIdentifier, + storageMapKey, ) { return Nil } @@ -3805,7 +3825,7 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr interpreter.WriteStored( address, newCapabilityDomain, - newCapabilityIdentifier, + storageMapKey, accountLinkValue, ) @@ -3860,7 +3880,9 @@ func (interpreter *Interpreter) accountGetLinkTargetFunction( domain := capabilityPath.Domain.Identifier() identifier := capabilityPath.Identifier - value := interpreter.ReadStored(address, domain, identifier) + storageMapKey := StringStorageMapKey(identifier) + + value := interpreter.ReadStored(address, domain, storageMapKey) if value == nil { return Nil @@ -3900,7 +3922,14 @@ func (interpreter *Interpreter) authAccountUnlinkFunction(addressValue AddressVa // Write new value - interpreter.WriteStored(address, domain, identifier, nil) + storageMapKey := StringStorageMapKey(identifier) + + interpreter.WriteStored( + address, + domain, + storageMapKey, + nil, + ) return Void }, @@ -4114,11 +4143,12 @@ func (interpreter *Interpreter) GetStorageCapabilityFinalTarget( seenPaths[path] = struct{}{} } - value := interpreter.ReadStored( - address, - path.Domain.Identifier(), - path.Identifier, - ) + domain := path.Domain.Identifier() + identifier := path.Identifier + + storageMapKey := StringStorageMapKey(identifier) + + value := interpreter.ReadStored(address, domain, storageMapKey) if value == nil { return nil, false, nil @@ -4455,10 +4485,8 @@ func (interpreter *Interpreter) RemoveReferencedSlab(storable atree.Storable) { return } - config := interpreter.SharedState.Config - storageID := atree.StorageID(storageIDStorable) - err := config.Storage.Remove(storageID) + err := interpreter.Storage().Remove(storageID) if err != nil { panic(errors.NewExternalError(err)) } @@ -4500,11 +4528,14 @@ func (interpreter *Interpreter) ValidateAtreeValue(value atree.Value) { defaultHIP := newHashInputProvider(interpreter, EmptyLocationRange) hip := func(value atree.Value, buffer []byte) ([]byte, error) { - if _, ok := value.(StringAtreeValue); ok { - return StringAtreeHashInput(value, buffer) + switch value := value.(type) { + case StringAtreeValue: + return StringAtreeValueHashInput(value, buffer) + case Uint64AtreeValue: + return Uint64AtreeValueHashInput(value, buffer) + default: + return defaultHIP(value, buffer) } - - return defaultHIP(value, buffer) } config := interpreter.SharedState.Config @@ -4516,8 +4547,9 @@ func (interpreter *Interpreter) ValidateAtreeValue(value atree.Value) { panic(err) } - if _, ok := value.(StringAtreeValue); ok { - equal, err := StringAtreeComparator( + switch value := value.(type) { + case StringAtreeValue: + equal, err := StringAtreeValueComparator( storage, value, otherStorable, @@ -4527,15 +4559,27 @@ func (interpreter *Interpreter) ValidateAtreeValue(value atree.Value) { } return equal - } - if equatableValue, ok := value.(EquatableValue); ok { + case Uint64AtreeValue: + equal, err := Uint64AtreeValueComparator( + storage, + value, + otherStorable, + ) + if err != nil { + panic(err) + } + + return equal + + case EquatableValue: otherValue := StoredValue(interpreter, otherStorable, storage) - return equatableValue.Equal(interpreter, EmptyLocationRange, otherValue) - } + return value.Equal(interpreter, EmptyLocationRange, otherValue) - // Not all values are comparable, assume valid for now - return true + default: + // Not all values are comparable, assume valid for now + return true + } } switch value := value.(type) { diff --git a/runtime/interpreter/storage_test.go b/runtime/interpreter/storage_test.go index 73c372e7fe..b97cbc784a 100644 --- a/runtime/interpreter/storage_test.go +++ b/runtime/interpreter/storage_test.go @@ -461,10 +461,10 @@ func TestInterpretStorageOverwriteAndRemove(t *testing.T) { NewUnmeteredStringValue("first"), ) - const identifier = "test" + const storageMapKey = StringStorageMapKey("test") storageMap := storage.GetStorageMap(address, "storage", true) - storageMap.WriteValue(inter, identifier, array1) + storageMap.WriteValue(inter, storageMapKey, array1) // Overwriting delete any existing child slabs @@ -478,7 +478,7 @@ func TestInterpretStorageOverwriteAndRemove(t *testing.T) { NewUnmeteredStringValue("second"), ) - storageMap.WriteValue(inter, identifier, array2) + storageMap.WriteValue(inter, storageMapKey, array2) // 2: // - storage map (atree ordered map) @@ -487,7 +487,7 @@ func TestInterpretStorageOverwriteAndRemove(t *testing.T) { // Writing nil is deletion and should delete any child slabs - storageMap.WriteValue(inter, identifier, nil) + storageMap.WriteValue(inter, storageMapKey, nil) // 1: // - storage map (atree ordered map) diff --git a/runtime/interpreter/storagemap.go b/runtime/interpreter/storagemap.go index 8c329ffb22..a9a2d08053 100644 --- a/runtime/interpreter/storagemap.go +++ b/runtime/interpreter/storagemap.go @@ -66,11 +66,11 @@ func NewStorageMapWithRootID(storage atree.SlabStorage, storageID atree.StorageI } // ValueExists returns true if the given key exists in the storage map. -func (s StorageMap) ValueExists(key string) bool { +func (s StorageMap) ValueExists(key StorageMapKey) bool { _, err := s.orderedMap.Get( - StringAtreeComparator, - StringAtreeHashInput, - StringAtreeValue(key), + key.AtreeValueCompare, + key.AtreeValueHashInput, + key.AtreeValue(), ) if err != nil { var keyNotFoundError *atree.KeyNotFoundError @@ -85,11 +85,11 @@ func (s StorageMap) ValueExists(key string) bool { // ReadValue returns the value for the given key. // Returns nil if the key does not exist. -func (s StorageMap) ReadValue(gauge common.MemoryGauge, key string) Value { +func (s StorageMap) ReadValue(gauge common.MemoryGauge, key StorageMapKey) Value { storable, err := s.orderedMap.Get( - StringAtreeComparator, - StringAtreeHashInput, - StringAtreeValue(key), + key.AtreeValueCompare, + key.AtreeValueHashInput, + key.AtreeValue(), ) if err != nil { var keyNotFoundError *atree.KeyNotFoundError @@ -105,21 +105,25 @@ func (s StorageMap) ReadValue(gauge common.MemoryGauge, key string) Value { // WriteValue sets or removes a value in the storage map. // If the given value is nil, the key is removed. // If the given value is non-nil, the key is added/updated. -func (s StorageMap) WriteValue(interpreter *Interpreter, key string, value atree.Value) { +// Returns true if a value previously existed at the given key. +func (s StorageMap) WriteValue(interpreter *Interpreter, key StorageMapKey, value atree.Value) (existed bool) { if value == nil { - s.RemoveValue(interpreter, key) + return s.RemoveValue(interpreter, key) } else { - s.SetValue(interpreter, key, value) + return s.SetValue(interpreter, key, value) } } // SetValue sets a value in the storage map. // If the given key already stores a value, it is overwritten. -func (s StorageMap) SetValue(interpreter *Interpreter, key string, value atree.Value) { +// Returns true if +func (s StorageMap) SetValue(interpreter *Interpreter, key StorageMapKey, value atree.Value) (existed bool) { + interpreter.recordStorageMutation() + existingStorable, err := s.orderedMap.Set( - StringAtreeComparator, - StringAtreeHashInput, - StringAtreeValue(key), + key.AtreeValueCompare, + key.AtreeValueHashInput, + key.AtreeValue(), value, ) if err != nil { @@ -127,20 +131,23 @@ func (s StorageMap) SetValue(interpreter *Interpreter, key string, value atree.V } interpreter.maybeValidateAtreeValue(s.orderedMap) - if existingStorable != nil { - config := interpreter.SharedState.Config - existingValue := StoredValue(interpreter, existingStorable, config.Storage) + existed = existingStorable != nil + if existed { + existingValue := StoredValue(interpreter, existingStorable, interpreter.Storage()) existingValue.DeepRemove(interpreter) interpreter.RemoveReferencedSlab(existingStorable) } + return } // RemoveValue removes a value in the storage map, if it exists. -func (s StorageMap) RemoveValue(interpreter *Interpreter, key string) { +func (s StorageMap) RemoveValue(interpreter *Interpreter, key StorageMapKey) (existed bool) { + interpreter.recordStorageMutation() + existingKeyStorable, existingValueStorable, err := s.orderedMap.Remove( - StringAtreeComparator, - StringAtreeHashInput, - StringAtreeValue(key), + key.AtreeValueCompare, + key.AtreeValueHashInput, + key.AtreeValue(), ) if err != nil { var keyNotFoundError *atree.KeyNotFoundError @@ -153,18 +160,19 @@ func (s StorageMap) RemoveValue(interpreter *Interpreter, key string) { // Key - // NOTE: key / field name is stringAtreeValue, - // and not a Value, so no need to deep remove + // NOTE: Key is just an atree.Value, not an interpreter.Value, + // so do not need (can) convert and not need to deep remove interpreter.RemoveReferencedSlab(existingKeyStorable) // Value - if existingValueStorable != nil { - config := interpreter.SharedState.Config - existingValue := StoredValue(interpreter, existingValueStorable, config.Storage) + existed = existingValueStorable != nil + if existed { + existingValue := StoredValue(interpreter, existingValueStorable, interpreter.Storage()) existingValue.DeepRemove(interpreter) interpreter.RemoveReferencedSlab(existingValueStorable) } + return } // Iterator returns an iterator (StorageMapIterator), @@ -199,35 +207,33 @@ type StorageMapIterator struct { // Next returns the next key and value of the storage map iterator. // If there is no further key-value pair, ("", nil) is returned. -func (i StorageMapIterator) Next() (string, Value) { +func (i StorageMapIterator) Next() (atree.Value, Value) { k, v, err := i.mapIterator.Next() if err != nil { panic(errors.NewExternalError(err)) } if k == nil || v == nil { - return "", nil + return nil, nil } - key := string(k.(StringAtreeValue)) + // NOTE: Key is just an atree.Value, not an interpreter.Value, + // so do not need (can) convert + value := MustConvertStoredValue(i.gauge, v) - return key, value + return k, value } // NextKey returns the next key of the storage map iterator. // If there is no further key, "" is returned. -func (i StorageMapIterator) NextKey() string { +func (i StorageMapIterator) NextKey() atree.Value { k, err := i.mapIterator.NextKey() if err != nil { panic(errors.NewExternalError(err)) } - if k == nil { - return "" - } - - return string(k.(StringAtreeValue)) + return k } // NextValue returns the next value in the storage map iterator. diff --git a/runtime/interpreter/storagemapkey.go b/runtime/interpreter/storagemapkey.go new file mode 100644 index 0000000000..e056a6bc24 --- /dev/null +++ b/runtime/interpreter/storagemapkey.go @@ -0,0 +1,75 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import "github.com/onflow/atree" + +type StorageMapKey interface { + isStorageMapKey() + AtreeValue() atree.Value + AtreeValueHashInput(v atree.Value, _ []byte) ([]byte, error) + AtreeValueCompare(storage atree.SlabStorage, value atree.Value, otherStorable atree.Storable) (bool, error) +} + +// StringStorageMapKey is a StorageMapKey backed by a simple StringAtreeValue +type StringStorageMapKey StringAtreeValue + +var _ StorageMapKey = StringStorageMapKey("") + +func (StringStorageMapKey) isStorageMapKey() {} + +func (StringStorageMapKey) AtreeValueHashInput(v atree.Value, scratch []byte) ([]byte, error) { + return StringAtreeValueHashInput(v, scratch) +} + +func (StringStorageMapKey) AtreeValueCompare( + slabStorage atree.SlabStorage, + value atree.Value, + otherStorable atree.Storable, +) (bool, error) { + return StringAtreeValueComparator(slabStorage, value, otherStorable) +} + +func (k StringStorageMapKey) AtreeValue() atree.Value { + return StringAtreeValue(k) +} + + +// Uint64StorageMapKey is a StorageMapKey backed by a simple Uint64AtreeValue +type Uint64StorageMapKey Uint64AtreeValue + +var _ StorageMapKey = Uint64StorageMapKey(0) + +func (Uint64StorageMapKey) isStorageMapKey() {} + +func (Uint64StorageMapKey) AtreeValueHashInput(v atree.Value, scratch []byte) ([]byte, error) { + return Uint64AtreeValueHashInput(v, scratch) +} + +func (Uint64StorageMapKey) AtreeValueCompare( + slabStorage atree.SlabStorage, + value atree.Value, + otherStorable atree.Storable, +) (bool, error) { + return Uint64AtreeValueComparator(slabStorage, value, otherStorable) +} + +func (k Uint64StorageMapKey) AtreeValue() atree.Value { + return Uint64AtreeValue(k) +} diff --git a/runtime/interpreter/stringatreevalue.go b/runtime/interpreter/stringatreevalue.go index fc62851121..98e7fcf2f9 100644 --- a/runtime/interpreter/stringatreevalue.go +++ b/runtime/interpreter/stringatreevalue.go @@ -57,17 +57,11 @@ func (StringAtreeValue) ChildStorables() []atree.Storable { return nil } -func StringAtreeHashInput(v atree.Value, _ []byte) ([]byte, error) { +func StringAtreeValueHashInput(v atree.Value, _ []byte) ([]byte, error) { return []byte(v.(StringAtreeValue)), nil } -func StringAtreeComparator(storage atree.SlabStorage, value atree.Value, otherStorable atree.Storable) (bool, error) { - otherValue, err := otherStorable.StoredValue(storage) - if err != nil { - return false, err - } - - result := value.(StringAtreeValue) == otherValue.(StringAtreeValue) - +func StringAtreeValueComparator(_ atree.SlabStorage, value atree.Value, otherStorable atree.Storable) (bool, error) { + result := value.(StringAtreeValue) == otherStorable.(StringAtreeValue) return result, nil } diff --git a/runtime/interpreter/uint64atreevalue.go b/runtime/interpreter/uint64atreevalue.go new file mode 100644 index 0000000000..785122428f --- /dev/null +++ b/runtime/interpreter/uint64atreevalue.go @@ -0,0 +1,70 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "encoding/binary" + + "github.com/onflow/atree" + + "github.com/onflow/cadence/runtime/common" +) + +type Uint64AtreeValue uint64 + +var _ atree.Value = Uint64AtreeValue(0) +var _ atree.Storable = Uint64AtreeValue(0) + +func (v Uint64AtreeValue) Storable( + _ atree.SlabStorage, + _ atree.Address, + _ uint64, +) ( + atree.Storable, + error, +) { + return v, nil +} + +func NewUint64AtreeValue(gauge common.MemoryGauge, s uint64) Uint64AtreeValue { + common.UseMemory(gauge, UInt64MemoryUsage) + return Uint64AtreeValue(s) +} + +func (v Uint64AtreeValue) ByteSize() uint32 { + return getUintCBORSize(uint64(v)) +} + +func (v Uint64AtreeValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { + return v, nil +} + +func (Uint64AtreeValue) ChildStorables() []atree.Storable { + return nil +} + +func Uint64AtreeValueHashInput(v atree.Value, scratch []byte) ([]byte, error) { + binary.BigEndian.PutUint64(scratch[:], uint64(v.(Uint64AtreeValue))) + return scratch[:8], nil +} + +func Uint64AtreeValueComparator(_ atree.SlabStorage, value atree.Value, otherStorable atree.Storable) (bool, error) { + result := value.(Uint64AtreeValue) == otherStorable.(Uint64AtreeValue) + return result, nil +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 58c8bf911f..99ebe5fba0 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1760,9 +1760,7 @@ func (v *ArrayValue) Get(interpreter *Interpreter, locationRange LocationRange, panic(errors.NewExternalError(err)) } - config := interpreter.SharedState.Config - - return StoredValue(interpreter, storable, config.Storage) + return StoredValue(interpreter, storable, interpreter.Storage()) } func (v *ArrayValue) SetKey(interpreter *Interpreter, locationRange LocationRange, key Value, value Value) { @@ -1809,8 +1807,7 @@ func (v *ArrayValue) Set(interpreter *Interpreter, locationRange LocationRange, } interpreter.maybeValidateAtreeValue(v.array) - config := interpreter.SharedState.Config - existingValue := StoredValue(interpreter, existingStorable, config.Storage) + existingValue := StoredValue(interpreter, existingStorable, interpreter.Storage()) existingValue.DeepRemove(interpreter) @@ -1969,8 +1966,7 @@ func (v *ArrayValue) Remove(interpreter *Interpreter, locationRange LocationRang } interpreter.maybeValidateAtreeValue(v.array) - config := interpreter.SharedState.Config - value := StoredValue(interpreter, storable, config.Storage) + value := StoredValue(interpreter, storable, interpreter.Storage()) return value.Transfer( interpreter, @@ -14185,8 +14181,8 @@ func (v *CompositeValue) GetMember(interpreter *Interpreter, locationRange Locat } storable, err := v.dictionary.Get( - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, StringAtreeValue(name), ) if err != nil { @@ -14329,8 +14325,8 @@ func (v *CompositeValue) RemoveMember( // No need to clean up storable for passed-in key value, // as atree never calls Storable() existingKeyStorable, existingValueStorable, err := v.dictionary.Remove( - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, StringAtreeValue(name), ) if err != nil { @@ -14403,8 +14399,8 @@ func (v *CompositeValue) SetMember( ) existingStorable, err := v.dictionary.Set( - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, NewStringAtreeValue(interpreter, name), value, ) @@ -14508,8 +14504,8 @@ func (v *CompositeValue) GetField(interpreter *Interpreter, locationRange Locati } storable, err := v.dictionary.Get( - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, StringAtreeValue(name), ) if err != nil { @@ -14807,8 +14803,8 @@ func (v *CompositeValue) Transfer( address, atree.NewDefaultDigesterBuilder(), v.dictionary.Type(), - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, v.dictionary.Seed(), func() (atree.Value, atree.Value, error) { @@ -14950,8 +14946,8 @@ func (v *CompositeValue) Clone(interpreter *Interpreter) Value { v.StorageID().Address, atree.NewDefaultDigesterBuilder(), v.dictionary.Type(), - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, v.dictionary.Seed(), func() (atree.Value, atree.Value, error) { @@ -15064,8 +15060,8 @@ func (v *CompositeValue) RemoveField( ) { existingKeyStorable, existingValueStorable, err := v.dictionary.Remove( - StringAtreeComparator, - StringAtreeHashInput, + StringAtreeValueComparator, + StringAtreeValueHashInput, StringAtreeValue(name), ) if err != nil { @@ -15084,8 +15080,7 @@ func (v *CompositeValue) RemoveField( interpreter.RemoveReferencedSlab(existingKeyStorable) // Value - config := interpreter.SharedState.Config - existingValue := StoredValue(interpreter, existingValueStorable, config.Storage) + existingValue := StoredValue(interpreter, existingValueStorable, interpreter.Storage()) existingValue.DeepRemove(interpreter) interpreter.RemoveReferencedSlab(existingValueStorable) } @@ -15937,8 +15932,7 @@ func (v *DictionaryValue) Remove( } interpreter.maybeValidateAtreeValue(v.dictionary) - config := interpreter.SharedState.Config - storage := config.Storage + storage := interpreter.Storage() // Key @@ -16021,8 +16015,7 @@ func (v *DictionaryValue) Insert( return NilOptionalValue } - config := interpreter.SharedState.Config - storage := config.Storage + storage := interpreter.Storage() existingValue := StoredValue( interpreter, @@ -17075,7 +17068,9 @@ func (v *StorageReferenceValue) dereference(interpreter *Interpreter, locationRa domain := v.TargetPath.Domain.Identifier() identifier := v.TargetPath.Identifier - referenced := interpreter.ReadStored(address, domain, identifier) + storageMapKey := StringStorageMapKey(identifier) + + referenced := interpreter.ReadStored(address, domain, storageMapKey) if referenced == nil { return nil, nil } diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index 77c7505a5b..db2f682661 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -111,7 +111,9 @@ func (v *AccountReferenceValue) checkLink(interpreter *Interpreter, locationRang domain := v.Path.Domain.Identifier() identifier := v.Path.Identifier - referenced := interpreter.ReadStored(address, domain, identifier) + storageMapKey := StringStorageMapKey(identifier) + + referenced := interpreter.ReadStored(address, domain, storageMapKey) if referenced == nil { panic(DereferenceError{ Cause: "no value is stored at this path", diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 510dc2c9e2..98228fb181 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -3784,7 +3784,7 @@ func TestValue_ConformsToStaticType(t *testing.T) { require.NoError(t, err) storageMap := storage.GetStorageMap(testAddress, "storage", true) - storageMap.WriteValue(inter, "test", TrueValue) + storageMap.WriteValue(inter, StringStorageMapKey("test"), TrueValue) value := valueFactory(inter) diff --git a/runtime/runtime.go b/runtime/runtime.go index 7ea7bff60e..aa08ee6f09 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -601,7 +601,9 @@ func (r *interpreterRuntime) ReadStored( domain := pathValue.Domain.Identifier() identifier := pathValue.Identifier - value := inter.ReadStored(address, domain, identifier) + storageMapKey := interpreter.StringStorageMapKey(identifier) + + value := inter.ReadStored(address, domain, storageMapKey) var exportedValue cadence.Value if value != nil { @@ -670,11 +672,12 @@ func (r *interpreterRuntime) ReadLinked( return nil, nil } - value := inter.ReadStored( - address, - targetPath.Domain.Identifier(), - targetPath.Identifier, - ) + domain := targetPath.Domain.Identifier() + identifier := targetPath.Identifier + + storageMapKey := interpreter.StringStorageMapKey(identifier) + + value := inter.ReadStored(address, domain, storageMapKey) var exportedValue cadence.Value if value != nil { diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index b95fb4dc74..7836d19d19 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -987,7 +987,7 @@ func newPublicAccountContractsValue( ) } -const inboxStorageDomain = "inbox" +const InboxStorageDomain = "inbox" func accountInboxPublishFunction( gauge common.MemoryGauge, @@ -1037,7 +1037,14 @@ func accountInboxPublishFunction( nil, ) - inter.WriteStored(address, inboxStorageDomain, nameValue.Str, publishedValue) + storageMapKey := interpreter.StringStorageMapKey(nameValue.Str) + + inter.WriteStored( + address, + InboxStorageDomain, + storageMapKey, + publishedValue, + ) return interpreter.Void }, @@ -1062,7 +1069,9 @@ func accountInboxUnpublishFunction( inter := invocation.Interpreter locationRange := invocation.LocationRange - readValue := inter.ReadStored(address, inboxStorageDomain, nameValue.Str) + storageMapKey := interpreter.StringStorageMapKey(nameValue.Str) + + readValue := inter.ReadStored(address, InboxStorageDomain, storageMapKey) if readValue == nil { return interpreter.Nil } @@ -1094,7 +1103,12 @@ func accountInboxUnpublishFunction( nil, ) - inter.WriteStored(address, inboxStorageDomain, nameValue.Str, nil) + inter.WriteStored( + address, + InboxStorageDomain, + storageMapKey, + nil, + ) handler.EmitEvent( inter, @@ -1135,7 +1149,9 @@ func accountInboxClaimFunction( providerAddress := providerValue.ToAddress() - readValue := inter.ReadStored(providerAddress, inboxStorageDomain, nameValue.Str) + storageMapKey := interpreter.StringStorageMapKey(nameValue.Str) + + readValue := inter.ReadStored(providerAddress, InboxStorageDomain, storageMapKey) if readValue == nil { return interpreter.Nil } @@ -1172,7 +1188,12 @@ func accountInboxClaimFunction( nil, ) - inter.WriteStored(providerAddress, inboxStorageDomain, nameValue.Str, nil) + inter.WriteStored( + providerAddress, + InboxStorageDomain, + storageMapKey, + nil, + ) handler.EmitEvent( inter, @@ -2347,10 +2368,12 @@ func newAuthAccountCapabilitiesPublishFunction( locationRange := invocation.LocationRange + storageMapKey := interpreter.StringStorageMapKey(identifier) + if inter.StoredValueExists( address, domain, - identifier, + storageMapKey, ) { panic( interpreter.OverwriteError{ @@ -2374,7 +2397,12 @@ func newAuthAccountCapabilitiesPublishFunction( // Write new value - inter.WriteStored(address, domain, identifier, capabilityValue) + inter.WriteStored( + address, + domain, + storageMapKey, + capabilityValue, + ) return interpreter.Void }, @@ -2402,7 +2430,9 @@ func newAuthAccountCapabilitiesUnpublishFunction( inter := invocation.Interpreter locationRange := invocation.LocationRange - readValue := inter.ReadStored(address, domain, identifier) + storageMapKey := interpreter.StringStorageMapKey(identifier) + + readValue := inter.ReadStored(address, domain, storageMapKey) if readValue == nil { return interpreter.Nil } @@ -2423,7 +2453,12 @@ func newAuthAccountCapabilitiesUnpublishFunction( panic(errors.NewUnreachableError()) } - inter.WriteStored(address, domain, identifier, nil) + inter.WriteStored( + address, + domain, + storageMapKey, + nil, + ) return interpreter.NewSomeValueNonCopying(inter, capabilityValue) }, @@ -2479,7 +2514,9 @@ func newAccountCapabilitiesGetFunction( // Read stored capability, if any - readValue := inter.ReadStored(address, domain, identifier) + storageMapKey := interpreter.StringStorageMapKey(identifier) + + readValue := inter.ReadStored(address, domain, storageMapKey) if readValue == nil { return interpreter.Nil } diff --git a/runtime/storage.go b/runtime/storage.go index c0a41147c9..53ee07c2e4 100644 --- a/runtime/storage.go +++ b/runtime/storage.go @@ -207,10 +207,11 @@ func (s *Storage) writeContractUpdate( ) { storageMap := s.GetStorageMap(key.Address, StorageDomainContract, true) // NOTE: pass nil instead of allocating a Value-typed interface that points to nil + storageMapKey := interpreter.StringStorageMapKey(key.Key) if contractValue == nil { - storageMap.WriteValue(inter, key.Key, nil) + storageMap.WriteValue(inter, storageMapKey, nil) } else { - storageMap.WriteValue(inter, key.Key, contractValue) + storageMap.WriteValue(inter, storageMapKey, contractValue) } } diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 607b19c326..9956a84070 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -3240,7 +3240,7 @@ func TestRuntimeStorageInternalAccess(t *testing.T) { // Read first - firstValue := storageMap.ReadValue(nil, "first") + firstValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("first")) RequireValuesEqual( t, inter, @@ -3250,7 +3250,7 @@ func TestRuntimeStorageInternalAccess(t *testing.T) { // Read second - secondValue := storageMap.ReadValue(nil, "second") + secondValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("second")) require.IsType(t, &interpreter.ArrayValue{}, secondValue) arrayValue := secondValue.(*interpreter.ArrayValue) @@ -3265,7 +3265,7 @@ func TestRuntimeStorageInternalAccess(t *testing.T) { // Read r - rValue := storageMap.ReadValue(nil, "r") + rValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("r")) require.IsType(t, &interpreter.CompositeValue{}, rValue) _, err = ExportValue(rValue, inter, interpreter.EmptyLocationRange) diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index 5a8cf2e18b..cbf7d117cd 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -22,6 +22,8 @@ import ( "fmt" "testing" + "github.com/onflow/atree" + "github.com/onflow/cadence/runtime/activations" "github.com/stretchr/testify/assert" @@ -39,7 +41,7 @@ import ( type storageKey struct { address common.Address domain string - key string + key atree.Value } func testAccount( @@ -123,7 +125,7 @@ func testAccount( iterator := accountStorage.Iterator(inter) for { key, value := iterator.Next() - if key == "" { + if key == nil { break } storageKey := storageKey{ diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 1bd6780c2e..482a64c593 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -4981,8 +4981,10 @@ func TestInterpretReferenceFailableDowncasting(t *testing.T) { nil, ) - storageMap := storage.GetStorageMap(storageAddress, storagePath.Domain.Identifier(), true) - storageMap.WriteValue(inter, storagePath.Identifier, r) + domain := storagePath.Domain.Identifier() + storageMap := storage.GetStorageMap(storageAddress, domain, true) + storageMapKey := interpreter.StringStorageMapKey(storagePath.Identifier) + storageMap.WriteValue(inter, storageMapKey, r) result, err := inter.Invoke("testInvalidUnauthorized") require.NoError(t, err) diff --git a/runtime/tests/interpreter/metatype_test.go b/runtime/tests/interpreter/metatype_test.go index 8a2f591f67..13a0721e9b 100644 --- a/runtime/tests/interpreter/metatype_test.go +++ b/runtime/tests/interpreter/metatype_test.go @@ -776,10 +776,12 @@ func TestInterpretGetType(t *testing.T) { ) require.NoError(t, err) - storageMap := storage.GetStorageMap(storageAddress, storagePath.Domain.Identifier(), true) + domain := storagePath.Domain.Identifier() + storageMap := storage.GetStorageMap(storageAddress, domain, true) + storageMapKey := interpreter.StringStorageMapKey(storagePath.Identifier) storageMap.WriteValue( inter, - storagePath.Identifier, + storageMapKey, interpreter.NewUnmeteredIntValueFromInt64(2), ) From ffb6b6235c11ecbe7cea526152c2793bcd8242ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 18:16:07 -0700 Subject: [PATCH 080/246] store storage capability controller --- runtime/interpreter/statictype.go | 4 + .../value_storagecapabilitycontroller.go | 8 ++ runtime/stdlib/account.go | 87 ++++++++++++++++++- 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index 434db60151..1ecf29eeac 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -387,6 +387,10 @@ func (t OptionalStaticType) Equal(other StaticType) bool { return t.Type.Equal(otherOptionalType.Type) } +var NilStaticType = OptionalStaticType{ + Type: PrimitiveStaticTypeNever, +} + // RestrictedStaticType type RestrictedStaticType struct { diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 6c398f16f1..bdc42f5f54 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -25,6 +25,11 @@ import ( "github.com/onflow/cadence/runtime/format" ) +type CapabilityControllerValue interface { + Value + isCapabilityControllerValue() +} + // StorageCapabilityControllerValue type StorageCapabilityControllerValue struct { @@ -63,9 +68,12 @@ func NewStorageCapabilityControllerValue( var _ Value = &StorageCapabilityControllerValue{} var _ atree.Value = &StorageCapabilityControllerValue{} var _ EquatableValue = &StorageCapabilityControllerValue{} +var _ CapabilityControllerValue = &StorageCapabilityControllerValue{} func (*StorageCapabilityControllerValue) IsValue() {} +func (*StorageCapabilityControllerValue) isCapabilityControllerValue() {} + func (v *StorageCapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitStorageCapabilityControllerValue(interpreter, v) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 7836d19d19..917f548f67 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2278,6 +2278,14 @@ func newAuthAccountCapabilitiesValue( ) } +// CapabilityControllerStorageDomain is the storage domain which stores +// capability controllers by capability ID +const CapabilityControllerStorageDomain = "cap_con" + +// PathCapabilityStorageDomain is the storage domain which stores +// capability ID dictionaries (sets) by storage path identifier +const PathCapabilityStorageDomain = "path_cap" + func newAuthAccountStorageCapabilitiesIssueFunction( gauge common.MemoryGauge, idGenerator AccountIDGenerator, @@ -2289,6 +2297,9 @@ func newAuthAccountStorageCapabilitiesIssueFunction( sema.AuthAccountStorageCapabilitiesTypeIssueFunctionType, func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + // Get path argument targetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) @@ -2319,15 +2330,15 @@ func newAuthAccountStorageCapabilitiesIssueFunction( capabilityIDValue := interpreter.UInt64Value(capabilityID) - _ = interpreter.NewStorageCapabilityControllerValue( + controller := interpreter.NewStorageCapabilityControllerValue( gauge, borrowStaticType, targetPathValue, capabilityIDValue, ) - // TODO: store controller in {ID -> controller} - // TODO: store controller in {path -> {controller ID -> void}} + storeCapabilityController(inter, address, capabilityIDValue, controller) + recordPathCapabilityController(inter, locationRange, address, targetPathValue, capabilityIDValue) return interpreter.NewStorageCapabilityValue( gauge, @@ -2340,6 +2351,75 @@ func newAuthAccountStorageCapabilitiesIssueFunction( ) } +// storeCapabilityController stores a capability controller in the account's capability ID to controller storage map +func storeCapabilityController( + inter *interpreter.Interpreter, + address common.Address, + capabilityIDValue interpreter.UInt64Value, + controller interpreter.CapabilityControllerValue, +) { + storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + + existed := inter.WriteStored( + address, + CapabilityControllerStorageDomain, + storageMapKey, + controller, + ) + if existed { + panic(errors.NewUnreachableError()) + } +} + +var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeUInt64, + ValueType: interpreter.NilStaticType, +} + +func recordPathCapabilityController( + inter *interpreter.Interpreter, + locatonRange interpreter.LocationRange, + address common.Address, + targetPathValue interpreter.PathValue, + capabilityIDValue interpreter.UInt64Value, +) { + if targetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + identifier := targetPathValue.Identifier + + storageMapKey := interpreter.StringStorageMapKey(identifier) + + storageMap := inter.Storage().GetStorageMap( + address, + PathCapabilityStorageDomain, + true, + ) + + setKey := capabilityIDValue + setValue := interpreter.Nil + + readValue := storageMap.ReadValue(inter, storageMapKey) + if readValue == nil { + capabilityIDSet := interpreter.NewDictionaryValueWithAddress( + inter, + locatonRange, + capabilityIDSetStaticType, + address, + setKey, + setValue, + ) + storageMap.SetValue(inter, storageMapKey, capabilityIDSet) + } else { + capabilityIDSet := readValue.(*interpreter.DictionaryValue) + existing := capabilityIDSet.Insert(inter, locatonRange, setKey, setValue) + if existing != interpreter.Nil { + panic(errors.NewUnreachableError()) + } + } +} + func newAuthAccountCapabilitiesPublishFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, @@ -2469,7 +2549,6 @@ func newPublicAccountCapabilitiesValue( gauge common.MemoryGauge, addressValue interpreter.AddressValue, ) interpreter.Value { - // TODO: return interpreter.NewPublicAccountCapabilitiesValue( gauge, addressValue, From 2a2bfa5c65886af51455c4a644e39d10b687bc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 18:39:29 -0700 Subject: [PATCH 081/246] implement AuthAccount.StorageCapabilities.getController --- runtime/interpreter/value.go | 3 +- runtime/stdlib/account.go | 77 ++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 99ebe5fba0..3b822f46f1 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17330,7 +17330,8 @@ func (*StorageReferenceValue) isReference() {} // EphemeralReferenceValue type EphemeralReferenceValue struct { - Value Value + Value Value + // BorrowedType is the T in &T BorrowedType sema.Type Authorized bool } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 917f548f67..70561745e0 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2216,7 +2216,7 @@ func newAuthAccountStorageCapabilitiesValue( return interpreter.NewAuthAccountStorageCapabilitiesValue( gauge, addressValue, - nil, + newAuthAccountStorageCapabilitiesGetControllerFunction(gauge, addressValue), nil, nil, newAuthAccountStorageCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), @@ -2278,13 +2278,42 @@ func newAuthAccountCapabilitiesValue( ) } -// CapabilityControllerStorageDomain is the storage domain which stores -// capability controllers by capability ID -const CapabilityControllerStorageDomain = "cap_con" +func newAuthAccountStorageCapabilitiesGetControllerFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.FunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountStorageCapabilitiesTypeGetControllerFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { -// PathCapabilityStorageDomain is the storage domain which stores -// capability ID dictionaries (sets) by storage path identifier -const PathCapabilityStorageDomain = "path_cap" + inter := invocation.Interpreter + + // Get capability ID argument + + capabilityIDValue, ok := invocation.Arguments[0].(interpreter.UInt64Value) + if !ok { + panic(errors.NewUnreachableError()) + } + + capabilityController := getCapabilityController(inter, address, capabilityIDValue) + storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) + if !ok { + return interpreter.Nil + } + + referenceValue := interpreter.NewEphemeralReferenceValue( + inter, + false, + storageCapabilityController, + sema.StorageCapabilityControllerType, + ) + + return interpreter.NewSomeValueNonCopying(inter, referenceValue) + }, + ) +} func newAuthAccountStorageCapabilitiesIssueFunction( gauge common.MemoryGauge, @@ -2351,6 +2380,10 @@ func newAuthAccountStorageCapabilitiesIssueFunction( ) } +// CapabilityControllerStorageDomain is the storage domain which stores +// capability controllers by capability ID +const CapabilityControllerStorageDomain = "cap_con" + // storeCapabilityController stores a capability controller in the account's capability ID to controller storage map func storeCapabilityController( inter *interpreter.Interpreter, @@ -2371,11 +2404,41 @@ func storeCapabilityController( } } +// getCapabilityController gets the capability controller for the given capability ID +func getCapabilityController( + inter *interpreter.Interpreter, + address common.Address, + capabilityIDValue interpreter.UInt64Value, +) interpreter.CapabilityControllerValue { + + storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + + readValue := inter.ReadStored( + address, + CapabilityControllerStorageDomain, + storageMapKey, + ) + if readValue == nil { + return nil + } + + capabilityController, ok := readValue.(interpreter.CapabilityControllerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return capabilityController +} + var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeUInt64, ValueType: interpreter.NilStaticType, } +// PathCapabilityStorageDomain is the storage domain which stores +// capability ID dictionaries (sets) by storage path identifier +const PathCapabilityStorageDomain = "path_cap" + func recordPathCapabilityController( inter *interpreter.Interpreter, locatonRange interpreter.LocationRange, From 7d11a597acf8bb190ff35155545f03260f5cab56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Apr 2023 18:50:38 -0700 Subject: [PATCH 082/246] implement StorageCapabilityController.capablityID field --- .../value_storagecapabilitycontroller.go | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index bdc42f5f54..1ef8698488 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -22,7 +22,9 @@ import ( "github.com/onflow/atree" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/format" + "github.com/onflow/cadence/runtime/sema" ) type CapabilityControllerValue interface { @@ -69,6 +71,7 @@ var _ Value = &StorageCapabilityControllerValue{} var _ atree.Value = &StorageCapabilityControllerValue{} var _ EquatableValue = &StorageCapabilityControllerValue{} var _ CapabilityControllerValue = &StorageCapabilityControllerValue{} +var _ MemberAccessibleValue = &StorageCapabilityControllerValue{} func (*StorageCapabilityControllerValue) IsValue() {} @@ -187,3 +190,23 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { v.CapabilityID, } } + +func (v *StorageCapabilityControllerValue) GetMember(_ *Interpreter, _ LocationRange, name string) Value { + switch name { + // TODO: + case sema.StorageCapabilityControllerTypeCapabilityIDFieldName: + return v.CapabilityID + } + + return nil +} + +func (*StorageCapabilityControllerValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { + // Storage capability controllers have no removable members (fields / functions) + panic(errors.NewUnreachableError()) +} + +func (*StorageCapabilityControllerValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { + // Storage capability controllers have no settable members (fields / functions) + panic(errors.NewUnreachableError()) +} From 8e59427f3abbc6dd910b55b24bc65ecb0c246a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Apr 2023 10:48:11 -0700 Subject: [PATCH 083/246] implement StorageCapabilityController.borrowType field --- runtime/interpreter/value_storagecapabilitycontroller.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 1ef8698488..dba9d157f5 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -191,11 +191,14 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { } } -func (v *StorageCapabilityControllerValue) GetMember(_ *Interpreter, _ LocationRange, name string) Value { +func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { switch name { // TODO: case sema.StorageCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID + + case sema.StorageCapabilityControllerTypeBorrowTypeFieldName: + return NewTypeValue(inter, v.BorrowType) } return nil From d758190ae2b17bf0627bc1185d2a83d66bea7359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Apr 2023 10:59:01 -0700 Subject: [PATCH 084/246] improve StorageCapabilityController.retarget function --- runtime/sema/storage_capability_controller.cdc | 2 +- runtime/sema/storage_capability_controller.gen.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/sema/storage_capability_controller.cdc b/runtime/sema/storage_capability_controller.cdc index b2e5fbf2d8..e2acd0bbd2 100644 --- a/runtime/sema/storage_capability_controller.cdc +++ b/runtime/sema/storage_capability_controller.cdc @@ -24,5 +24,5 @@ pub struct StorageCapabilityController { /// Retarget the controlled capability to the given storage path. /// The path may be different or the same as the current path. - pub fun retarget(target: StoragePath) + pub fun retarget(_ target: StoragePath) } diff --git a/runtime/sema/storage_capability_controller.gen.go b/runtime/sema/storage_capability_controller.gen.go index 3383971baf..40abb2fc10 100644 --- a/runtime/sema/storage_capability_controller.gen.go +++ b/runtime/sema/storage_capability_controller.gen.go @@ -74,6 +74,7 @@ const StorageCapabilityControllerTypeRetargetFunctionName = "retarget" var StorageCapabilityControllerTypeRetargetFunctionType = &FunctionType{ Parameters: []Parameter{ { + Label: ArgumentLabelNotRequired, Identifier: "target", TypeAnnotation: NewTypeAnnotation(StoragePathType), }, From 9cb1e08b6e4c77fedb7f6147196a58fd93495a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Apr 2023 10:59:24 -0700 Subject: [PATCH 085/246] start implementation of StorageCapabilityController.retarget function --- .../value_storagecapabilitycontroller.go | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index dba9d157f5..fc82a348a8 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -199,6 +199,40 @@ func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat case sema.StorageCapabilityControllerTypeBorrowTypeFieldName: return NewTypeValue(inter, v.BorrowType) + + case sema.StorageCapabilityControllerTypeTargetFunctionName: + return NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation Invocation) Value { + return v.TargetPath + }, + ) + + case sema.StorageCapabilityControllerTypeRetargetFunctionName: + return NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation Invocation) Value { + // Get path argument + + newTargetPathValue, ok := invocation.Arguments[0].(PathValue) + if !ok || newTargetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + oldTargetPathValue := v.TargetPath + + // TODO: remove from old path -> cap id set + _ = oldTargetPathValue + + // TODO: add to new path -> cap id set + + v.TargetPath = newTargetPathValue + + return Void + }, + ) } return nil From c651553683cb7768d85413ee03307a2fc57ae0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Apr 2023 15:16:28 -0700 Subject: [PATCH 086/246] move storage capability controller retarget function to stdlib and implement it --- .../value_storagecapabilitycontroller.go | 35 ++------ runtime/stdlib/account.go | 81 ++++++++++++++++++- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index fc82a348a8..97f0a61222 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -35,9 +35,10 @@ type CapabilityControllerValue interface { // StorageCapabilityControllerValue type StorageCapabilityControllerValue struct { - BorrowType StaticType - TargetPath PathValue - CapabilityID UInt64Value + BorrowType StaticType + TargetPath PathValue + CapabilityID UInt64Value + RetargetFunction FunctionValue } func NewUnmeteredStorageCapabilityControllerValue( @@ -192,8 +193,10 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { } func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { + + // TODO: sema.StorageCapabilityControllerTypeDeleteFunctionName + switch name { - // TODO: case sema.StorageCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID @@ -210,29 +213,7 @@ func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat ) case sema.StorageCapabilityControllerTypeRetargetFunctionName: - return NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation Invocation) Value { - // Get path argument - - newTargetPathValue, ok := invocation.Arguments[0].(PathValue) - if !ok || newTargetPathValue.Domain != common.PathDomainStorage { - panic(errors.NewUnreachableError()) - } - - oldTargetPathValue := v.TargetPath - - // TODO: remove from old path -> cap id set - _ = oldTargetPathValue - - // TODO: add to new path -> cap id set - - v.TargetPath = newTargetPathValue - - return Void - }, - ) + return v.RetargetFunction } return nil diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 70561745e0..e7afc1d71c 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2427,9 +2427,49 @@ func getCapabilityController( panic(errors.NewUnreachableError()) } + // Inject functions + switch capabilityController := capabilityController.(type) { + case *interpreter.StorageCapabilityControllerValue: + capabilityController.RetargetFunction = + newStorageCapabilityControllerRetargetFunction(inter, address, capabilityController) + + // TODO: inject delete function + } + return capabilityController } +func newStorageCapabilityControllerRetargetFunction( + inter *interpreter.Interpreter, + address common.Address, + controller *interpreter.StorageCapabilityControllerValue, +) interpreter.FunctionValue { + return interpreter.NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + locationRange := invocation.LocationRange + + // Get path argument + + newTargetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || newTargetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + oldTargetPathValue := controller.TargetPath + + capabilityID := controller.CapabilityID + unrecordPathCapabilityController(inter, locationRange, address, oldTargetPathValue, capabilityID) + recordPathCapabilityController(inter, locationRange, address, newTargetPathValue, capabilityID) + + controller.TargetPath = newTargetPathValue + + return interpreter.Void + }, + ) +} + var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeUInt64, ValueType: interpreter.NilStaticType, @@ -2441,7 +2481,7 @@ const PathCapabilityStorageDomain = "path_cap" func recordPathCapabilityController( inter *interpreter.Interpreter, - locatonRange interpreter.LocationRange, + locationRange interpreter.LocationRange, address common.Address, targetPathValue interpreter.PathValue, capabilityIDValue interpreter.UInt64Value, @@ -2467,7 +2507,7 @@ func recordPathCapabilityController( if readValue == nil { capabilityIDSet := interpreter.NewDictionaryValueWithAddress( inter, - locatonRange, + locationRange, capabilityIDSetStaticType, address, setKey, @@ -2476,13 +2516,48 @@ func recordPathCapabilityController( storageMap.SetValue(inter, storageMapKey, capabilityIDSet) } else { capabilityIDSet := readValue.(*interpreter.DictionaryValue) - existing := capabilityIDSet.Insert(inter, locatonRange, setKey, setValue) + existing := capabilityIDSet.Insert(inter, locationRange, setKey, setValue) if existing != interpreter.Nil { panic(errors.NewUnreachableError()) } } } +func unrecordPathCapabilityController( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + address common.Address, + targetPathValue interpreter.PathValue, + capabilityIDValue interpreter.UInt64Value, +) { + if targetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + identifier := targetPathValue.Identifier + + storageMapKey := interpreter.StringStorageMapKey(identifier) + + storageMap := inter.Storage().GetStorageMap( + address, + PathCapabilityStorageDomain, + true, + ) + + setKey := capabilityIDValue + + readValue := storageMap.ReadValue(inter, storageMapKey) + if readValue == nil { + panic(errors.NewUnreachableError()) + } + + capabilityIDSet := readValue.(*interpreter.DictionaryValue) + existing := capabilityIDSet.Remove(inter, locationRange, setKey) + if existing == interpreter.Nil { + panic(errors.NewUnreachableError()) + } +} + func newAuthAccountCapabilitiesPublishFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, From 10a92f64c39953998a798deb9f4a739ed503715a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Apr 2023 17:49:01 -0700 Subject: [PATCH 087/246] test Auth/PublicAccount.get/borrow --- runtime/capabilitycontrollers_test.go | 596 ++++++++++++++++++++++++++ 1 file changed, 596 insertions(+) create mode 100644 runtime/capabilitycontrollers_test.go diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go new file mode 100644 index 0000000000..eaa9ca7722 --- /dev/null +++ b/runtime/capabilitycontrollers_test.go @@ -0,0 +1,596 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +import ( + "fmt" + "testing" + + "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" +) + +func TestRuntimeCapabilityControllers(t *testing.T) { + t.Parallel() + + test := func( + testContract string, + testTransaction string, + ) ( + err error, + logs []string, + events []string, + ) { + + rt := newTestInterpreterRuntime() + + accountCodes := map[Location][]byte{} + accountIDs := map[common.Address]uint64{} + + var deployTx []byte + if len(testContract) > 0 { + deployTx = DeploymentTransaction("Test", []byte(testContract)) + } + + testTx := []byte(testTransaction) + + signer := common.MustBytesToAddress([]byte{0x1}) + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + log: func(message string) { + logs = append(logs, message) + }, + emitEvent: func(event cadence.Event) error { + events = append(events, event.String()) + return nil + }, + getSigningAccounts: func() ([]Address, error) { + return []Address{signer}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + updateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + getAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + generateAccountID: func(address common.Address) (uint64, error) { + accountID := accountIDs[address] + 1 + accountIDs[address] = accountID + return accountID, nil + }, + } + + nextTransactionLocation := newTransactionLocationGenerator() + + if deployTx != nil { + + // Deploy contract + + err = rt.ExecuteTransaction( + Script{ + Source: deployTx, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + } + + // Call contract + + err = rt.ExecuteTransaction( + Script{ + Source: testTx, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + + return + } + + testAccount := func(accountType sema.Type, accountExpression string) { + + testName := fmt.Sprintf( + "%s.Capabilities, storage capability", + accountType.String(), + ) + + t.Run(testName, func(t *testing.T) { + + t.Parallel() + + // language=cadence + contract := ` + pub contract Test { + + pub resource R { + + pub let id: Int + + init(id: Int) { + self.id = id + } + } + + pub resource S {} + + pub fun createAndSaveR(id: Int, storagePath: StoragePath) { + self.account.save( + <-create R(id: id), + to: storagePath + ) + } + } + ` + + t.Run("get non-existing", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + transaction { + prepare(signer: AuthAccount) { + // Act + let gotCap: Capability<&AnyStruct>? = + %s.capabilities.get<&AnyStruct>(/public/r) + + // Assert + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("get and borrow existing, with valid type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R> = %s.capabilities.get<&Test.R>(publicPath)! + let ref: &Test.R = gotCap.borrow()! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.id == expectedCapID) + assert(ref.id == resourceID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("get existing, with subtype", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R>? = %s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("get existing, with different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.S>? = %s.capabilities.get<&Test.S>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("get unpublished", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let gotCap: Capability<&Test.R>? = %s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("borrow non-existing", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + transaction { + prepare(signer: AuthAccount) { + // Act + let ref: &AnyStruct? = + %s.capabilities.borrow<&AnyStruct>(/public/r) + + // Assert + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("borrow existing, with valid type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.R = %s.capabilities.borrow<&Test.R>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref.id == resourceID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("borrow existing, with subtype", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.R? = %s.capabilities.borrow<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("borrow existing, with different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.S? = %s.capabilities.borrow<&Test.S>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("borrow unpublished", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let ref: &Test.R? = %s.capabilities.borrow<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + if accountType == sema.AuthAccountType { + + t.Run("publish, existing published", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + signer.capabilities.publish(issuedCap, at: publicPath) + } + } + `, + ) + RequireError(t, err) + + var overwriteErr interpreter.OverwriteError + require.ErrorAs(t, err, &overwriteErr) + }) + + t.Run("unpublish non-existing", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + contract, + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/r + + // Act + let cap = signer.capabilities.unpublish(publicPath) + + // Assert + assert(cap == nil) + } + } + `, + ) + require.NoError(t, err) + }) + } + }) + } + + for accountType, accountExpression := range map[sema.Type]string{ + sema.AuthAccountType: "signer", + sema.PublicAccountType: "getAccount(0x1)", + } { + testAccount(accountType, accountExpression) + } +} From 97ec304c5c4a2d9aa7d9d5462a1645bd17223aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 10:20:13 -0700 Subject: [PATCH 088/246] always use same test contract --- runtime/capabilitycontrollers_test.go | 118 +++++++++++--------------- 1 file changed, 48 insertions(+), 70 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index eaa9ca7722..a5a608bffd 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -34,10 +34,7 @@ import ( func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - test := func( - testContract string, - testTransaction string, - ) ( + test := func(tx string) ( err error, logs []string, events []string, @@ -48,12 +45,32 @@ func TestRuntimeCapabilityControllers(t *testing.T) { accountCodes := map[Location][]byte{} accountIDs := map[common.Address]uint64{} - var deployTx []byte - if len(testContract) > 0 { - deployTx = DeploymentTransaction("Test", []byte(testContract)) - } + deployTx := DeploymentTransaction( + "Test", + // language=cadence + []byte(` + pub contract Test { + + pub resource R { + + pub let id: Int + + init(id: Int) { + self.id = id + } + } + + pub resource S {} - testTx := []byte(testTransaction) + pub fun createAndSaveR(id: Int, storagePath: StoragePath) { + self.account.save( + <-create R(id: id), + to: storagePath + ) + } + } + `), + ) signer := common.MustBytesToAddress([]byte{0x1}) @@ -87,27 +104,24 @@ func TestRuntimeCapabilityControllers(t *testing.T) { nextTransactionLocation := newTransactionLocationGenerator() - if deployTx != nil { - - // Deploy contract + // Deploy contract - err = rt.ExecuteTransaction( - Script{ - Source: deployTx, - }, - Context{ - Interface: runtimeInterface, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) - } + err = rt.ExecuteTransaction( + Script{ + Source: deployTx, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) // Call contract err = rt.ExecuteTransaction( Script{ - Source: testTx, + Source: []byte(tx), }, Context{ Interface: runtimeInterface, @@ -129,50 +143,25 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - // language=cadence - contract := ` - pub contract Test { - - pub resource R { - - pub let id: Int - - init(id: Int) { - self.id = id - } - } - - pub resource S {} - - pub fun createAndSaveR(id: Int, storagePath: StoragePath) { - self.account.save( - <-create R(id: id), - to: storagePath - ) - } - } - ` - t.Run("get non-existing", func(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` - transaction { - prepare(signer: AuthAccount) { - // Act - let gotCap: Capability<&AnyStruct>? = - %s.capabilities.get<&AnyStruct>(/public/r) - - // Assert - assert(gotCap == nil) + transaction { + prepare(signer: AuthAccount) { + // Act + let gotCap: Capability<&AnyStruct>? = + %s.capabilities.get<&AnyStruct>(/public/r) + + // Assert + assert(gotCap == nil) + } } - } - `, + `, accountExpression, ), ) @@ -184,7 +173,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -225,7 +213,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -264,7 +251,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -303,7 +289,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -344,7 +329,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -370,7 +354,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -409,7 +392,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -448,7 +430,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -487,7 +468,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, fmt.Sprintf( // language=cadence ` @@ -530,7 +510,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, // language=cadence ` import Test from 0x1 @@ -563,7 +542,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() err, _, _ := test( - contract, // language=cadence ` import Test from 0x1 From 6c6f205c2b2cece05ff200a9f0de5daef9e206d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 11:22:28 -0700 Subject: [PATCH 089/246] test AuthAccount.StorageCapabilities.getController --- runtime/capabilitycontrollers_test.go | 102 ++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index a5a608bffd..94df94b9d0 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -132,6 +132,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { return } + // TODO: account capability testAccount := func(accountType sema.Type, accountExpression string) { testName := fmt.Sprintf( @@ -571,4 +572,105 @@ func TestRuntimeCapabilityControllers(t *testing.T) { } { testAccount(accountType, accountExpression) } + + t.Run("AuthAccount.StorageCapabilities", func(t *testing.T) { + + t.Parallel() + + t.Run("issue, multiple controllers to various paths, with same or different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Act + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let issuedCap4: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + + // Assert + assert(issuedCap1.id == 1) + assert(issuedCap2.id == 2) + assert(issuedCap3.id == 3) + assert(issuedCap4.id == 4) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let issuedCap4: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + + // Act + let controller1: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) + let controller2: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap2.id) + let controller3: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap3.id) + let controller4: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap4.id) + + // Assert + assert(controller1!.capabilityID == 1) + assert(controller1!.borrowType == Type<&Test.R>()) + assert(controller1!.target() == storagePath1) + + assert(controller2!.capabilityID == 2) + assert(controller2!.borrowType == Type<&Test.R>()) + assert(controller2!.target() == storagePath1) + + assert(controller3!.capabilityID == 3) + assert(controller3!.borrowType == Type<&Test.R{}>()) + assert(controller3!.target() == storagePath1) + + assert(controller4!.capabilityID == 4) + assert(controller4!.borrowType == Type<&Test.R>()) + assert(controller4!.target() == storagePath2) + } + } + `, + ) + require.NoError(t, err) + }) + + // TODO: getControllers, forEachController + }) + }) } From 493e2cb47803ce0bb46270c42a553a50e1c9cee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 11:22:39 -0700 Subject: [PATCH 090/246] test StorageCapabilityController.retarget --- runtime/capabilitycontrollers_test.go | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 94df94b9d0..158af7cba2 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -672,5 +672,73 @@ func TestRuntimeCapabilityControllers(t *testing.T) { // TODO: getControllers, forEachController }) + + // TODO: AuthAccount.AccountCapabilities + + t.Run("StorageCapabilityController", func(t *testing.T) { + + t.Parallel() + + t.Run("retarget", func(t *testing.T) { + + t.Parallel() + + // TODO: assert borrow + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller1: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) + + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller2: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap2.id) + + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let controller3: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap3.id) + + let issuedCap4: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + let controller4: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap4.id) + + // Act + controller1!.retarget(storagePath2) + + // Assert + assert(controller1!.target() == storagePath2) + let controller1After: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) + assert(controller1After!.target() == storagePath2) + + assert(controller2!.target() == storagePath1) + + assert(controller3!.target() == storagePath1) + + assert(controller4!.target() == storagePath2) + } + } + `, + ) + require.NoError(t, err) + }) + + // TODO: getControllers, forEachController }) + + // TODO: AccountCapabilityController } From e8e5802fa61165be7c7991cdc48aa98631e70526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 11:23:36 -0700 Subject: [PATCH 091/246] fix argument label --- runtime/tests/checker/capability_controller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/capability_controller_test.go b/runtime/tests/checker/capability_controller_test.go index 74812934f6..8d552faef4 100644 --- a/runtime/tests/checker/capability_controller_test.go +++ b/runtime/tests/checker/capability_controller_test.go @@ -76,7 +76,7 @@ func TestCheckStorageCapabilityController(t *testing.T) { let borrowType: Type = controller.borrowType let capabilityID: UInt64 = controller.capabilityID let target: StoragePath = controller.target() - let _: Void = controller.retarget(target: /storage/test) + let _: Void = controller.retarget(/storage/test) `) require.NoError(t, err) From 905de137884cba5db8611cab09d3c4a2804ebc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 12:27:09 -0700 Subject: [PATCH 092/246] test AuthAccount.StorageCapabilities.getController for non-existing controller --- runtime/capabilitycontrollers_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 158af7cba2..02466a6212 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -613,6 +613,33 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) + t.Run("getController, non-existing", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Act + let controller1: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: 0) + let controller2: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: 1) + + // Assert + assert(controller1 == nil) + assert(controller2 == nil) + } + } + `, + ) + require.NoError(t, err) + }) + t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { t.Parallel() From 0386fbaf99cef5634cc20a90e2562eb43b472d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 14:59:58 -0700 Subject: [PATCH 093/246] remove old examples --- runtime/examples/importing/import.cdc | 5 -- runtime/examples/importing/imported.cdc | 5 -- runtime/examples/vault.cdc | 61 ------------------------- 3 files changed, 71 deletions(-) delete mode 100644 runtime/examples/importing/import.cdc delete mode 100644 runtime/examples/importing/imported.cdc delete mode 100644 runtime/examples/vault.cdc diff --git a/runtime/examples/importing/import.cdc b/runtime/examples/importing/import.cdc deleted file mode 100644 index 59ed234828..0000000000 --- a/runtime/examples/importing/import.cdc +++ /dev/null @@ -1,5 +0,0 @@ -import x, crash from "imported.bpl" - -pub fun main() { - crash() -} diff --git a/runtime/examples/importing/imported.cdc b/runtime/examples/importing/imported.cdc deleted file mode 100644 index f1458db8fe..0000000000 --- a/runtime/examples/importing/imported.cdc +++ /dev/null @@ -1,5 +0,0 @@ -let x = 1 -let y = log("IMPORTED") -fun crash() { - panic("?!") -} diff --git a/runtime/examples/vault.cdc b/runtime/examples/vault.cdc deleted file mode 100644 index 48a3119c1e..0000000000 --- a/runtime/examples/vault.cdc +++ /dev/null @@ -1,61 +0,0 @@ - -pub struct interface Vault { - pub balance: Int - - init(balance: Int) { - post { - self.balance == balance: - "the balance must be initialized to the initial balance" - } - } - - pub fun withdraw(amount: Int): Vault { - pre { - amount > 0: - "withdrawal amount must be positive" - amount <= self.balance: - "insufficient funds: the amount must be smaller or equal to the balance" - } - post { - self.balance == before(self.balance) - amount: - "Incorrect amount removed" - result.balance == amount: "incorrect amount returned" - } - } - - pub fun deposit(vault: Vault) { - post { - self.balance == before(self.balance) + vault.balance: - "the amount must be added to the balance" - } - } -} - -pub struct ExampleVault: Vault { - pub var balance: Int - - init(balance: Int) { - self.balance = balance - } - - pub fun withdraw(amount: Int): Vault { - self.balance = self.balance - amount - return ExampleVault(balance: amount) - } - - pub fun deposit(vault: Vault) { - self.balance = self.balance + vault.balance - } -} - -pub fun main() { - let vaultA = ExampleVault(balance: 10) - let vaultB = ExampleVault(balance: 0) - - let vaultC = vaultA.withdraw(amount: 7) - vaultB.deposit(vault: vaultC) - - log(vaultA.balance) - log(vaultB.balance) - log(vaultC.balance) -} From e0a4b8ef463fc51526bc0f905e2601d2aca88e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 15:00:55 -0700 Subject: [PATCH 094/246] add QuickSort example --- examples/quicksort.cdc | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/quicksort.cdc diff --git a/examples/quicksort.cdc b/examples/quicksort.cdc new file mode 100644 index 0000000000..0332febcdc --- /dev/null +++ b/examples/quicksort.cdc @@ -0,0 +1,50 @@ + +/// quickSort is qsort from "The C Programming Language". +/// +/// > Our version of quicksort is not the fastest possible, +/// > but it's one of the simplest. +/// +pub fun quickSort(_ items: &[AnyStruct], isLess: ((Int, Int): Bool)) { + + fun quickSortPart(leftIndex: Int, rightIndex: Int) { + + if leftIndex >= rightIndex { + return + } + + let pivotIndex = (leftIndex + rightIndex) / 2 + + items[pivotIndex] <-> items[leftIndex] + + var lastIndex = leftIndex + var index = leftIndex + 1 + while index <= rightIndex { + if isLess(index, leftIndex) { + lastIndex = lastIndex + 1 + items[lastIndex] <-> items[index] + } + index = index + 1 + } + + items[leftIndex] <-> items[lastIndex] + + quickSortPart(leftIndex: leftIndex, rightIndex: lastIndex - 1) + quickSortPart(leftIndex: lastIndex + 1, rightIndex: rightIndex) + } + + quickSortPart( + leftIndex: 0, + rightIndex: items.length - 1 + ) +} + +pub fun main() { + let items = [5, 3, 7, 6, 2, 9] + quickSort( + &items as &[AnyStruct], + isLess: fun (i: Int, j: Int): Bool { + return items[i] < items[j] + } + ) + log(items) +} From dbd0156ba6bf56c17b8aca80df49c1c90bb1a2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 15:01:14 -0700 Subject: [PATCH 095/246] add dictionary iterator --- runtime/interpreter/value.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 3b822f46f1..2ff8a40f8f 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -15422,6 +15422,32 @@ func (v *DictionaryValue) Iterate(gauge common.MemoryGauge, f func(key, value Va } } +type DictionaryIterator struct { + mapIterator *atree.MapIterator +} + +func (i DictionaryIterator) NextKey(gauge common.MemoryGauge) Value { + atreeValue, err := i.mapIterator.NextKey() + if err != nil { + panic(errors.NewExternalError(err)) + } + if atreeValue == nil { + return nil + } + return MustConvertStoredValue(gauge, atreeValue) +} + +func (v *DictionaryValue) Iterator() DictionaryIterator { + mapIterator, err := v.dictionary.Iterator() + if err != nil { + panic(errors.NewExternalError(err)) + } + + return DictionaryIterator{ + mapIterator: mapIterator, + } +} + func (v *DictionaryValue) Walk(interpreter *Interpreter, walkChild func(Value)) { v.Iterate(interpreter, func(key, value Value) (resume bool) { walkChild(key) From b409dc195bd9b07538a28c2496081443d0830ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 15:01:57 -0700 Subject: [PATCH 096/246] implement AuthAccount.StorageCapabilities.getControllers --- runtime/capabilitycontrollers_test.go | 102 +++++++++++++++++- runtime/stdlib/account.go | 150 +++++++++++++++++++++++--- 2 files changed, 235 insertions(+), 17 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 02466a6212..ec4b90573d 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -68,6 +68,45 @@ func TestRuntimeCapabilityControllers(t *testing.T) { to: storagePath ) } + + /// quickSort is qsort from "The C Programming Language". + /// + /// > Our version of quicksort is not the fastest possible, + /// > but it's one of the simplest. + /// + pub fun quickSort(_ items: &[AnyStruct], isLess: ((Int, Int): Bool)) { + + fun quickSortPart(leftIndex: Int, rightIndex: Int) { + + if leftIndex >= rightIndex { + return + } + + let pivotIndex = (leftIndex + rightIndex) / 2 + + items[pivotIndex] <-> items[leftIndex] + + var lastIndex = leftIndex + var index = leftIndex + 1 + while index <= rightIndex { + if isLess(index, leftIndex) { + lastIndex = lastIndex + 1 + items[lastIndex] <-> items[index] + } + index = index + 1 + } + + items[leftIndex] <-> items[lastIndex] + + quickSortPart(leftIndex: leftIndex, rightIndex: lastIndex - 1) + quickSortPart(leftIndex: lastIndex + 1, rightIndex: rightIndex) + } + + quickSortPart( + leftIndex: 0, + rightIndex: items.length - 1 + ) + } } `), ) @@ -640,6 +679,8 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) + // TODO: getController, non-storage capability controller + t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { t.Parallel() @@ -697,7 +738,62 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - // TODO: getControllers, forEachController + t.Run("getControllers", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let issuedCap4: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + + // Act + let controllers1: [&StorageCapabilityController] = + signer.capabilities.storage.getControllers(forPath: storagePath1) + let controllers2: [&StorageCapabilityController] = + signer.capabilities.storage.getControllers(forPath: storagePath2) + + // Assert + assert(controllers1.length == 3) + + Test.quickSort( + &controllers1 as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1[i] + let b = controllers1[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers1[0].capabilityID == 1) + assert(controllers1[1].capabilityID == 2) + assert(controllers1[2].capabilityID == 3) + + assert(controllers2.length == 1) + assert(controllers2[0].capabilityID == 4) + } + } + `, + ) + require.NoError(t, err) + }) + + // TODO: forEachController }) // TODO: AuthAccount.AccountCapabilities @@ -710,7 +806,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - // TODO: assert borrow + // TODO: assert borrow, getControllers, and forEachController after retarget err, _, _ := test( // language=cadence @@ -763,8 +859,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { ) require.NoError(t, err) }) - - // TODO: getControllers, forEachController }) // TODO: AccountCapabilityController diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index e7afc1d71c..10ac2cfde9 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2217,7 +2217,7 @@ func newAuthAccountStorageCapabilitiesValue( gauge, addressValue, newAuthAccountStorageCapabilitiesGetControllerFunction(gauge, addressValue), - nil, + newAuthAccountStorageCapabilitiesGetControllersFunction(gauge, addressValue), nil, newAuthAccountStorageCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), ) @@ -2297,7 +2297,11 @@ func newAuthAccountStorageCapabilitiesGetControllerFunction( panic(errors.NewUnreachableError()) } - capabilityController := getCapabilityController(inter, address, capabilityIDValue) + capabilityController := getCapabilityController(inter, address, uint64(capabilityIDValue)) + if capabilityController == nil { + return interpreter.Nil + } + storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) if !ok { return interpreter.Nil @@ -2315,6 +2319,75 @@ func newAuthAccountStorageCapabilitiesGetControllerFunction( ) } +var storageCapabilityControllerReferencesArrayStaticType = interpreter.VariableSizedStaticType{ + Type: interpreter.ReferenceStaticType{ + BorrowedType: interpreter.PrimitiveStaticTypeStorageCapabilityController, + }, +} + +func newAuthAccountStorageCapabilitiesGetControllersFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.FunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountStorageCapabilitiesTypeGetControllersFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + + // Get path argument + + targetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || targetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + // Get capability controllers iterator + + nextCapabilityControllerID, count := getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) + + var capabilityControllerIndex uint64 = 0 + + return interpreter.NewArrayValueWithIterator( + inter, + storageCapabilityControllerReferencesArrayStaticType, + common.Address{}, + count, + func() interpreter.Value { + if capabilityControllerIndex >= count { + return nil + } + capabilityControllerIndex++ + + capabilityControllerID, ok := nextCapabilityControllerID() + if !ok { + return nil + } + + capabilityController := getCapabilityController(inter, address, capabilityControllerID) + if capabilityController == nil { + panic(errors.NewUnreachableError()) + } + + storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return interpreter.NewEphemeralReferenceValue( + inter, + false, + storageCapabilityController, + sema.StorageCapabilityControllerType, + ) + }, + ) + }, + ) +} + func newAuthAccountStorageCapabilitiesIssueFunction( gauge common.MemoryGauge, idGenerator AccountIDGenerator, @@ -2408,10 +2481,10 @@ func storeCapabilityController( func getCapabilityController( inter *interpreter.Interpreter, address common.Address, - capabilityIDValue interpreter.UInt64Value, + capabilityID uint64, ) interpreter.CapabilityControllerValue { - storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + storageMapKey := interpreter.Uint64StorageMapKey(capabilityID) readValue := inter.ReadStored( address, @@ -2523,13 +2596,11 @@ func recordPathCapabilityController( } } -func unrecordPathCapabilityController( +func getPathCapabilityIDSet( inter *interpreter.Interpreter, - locationRange interpreter.LocationRange, - address common.Address, targetPathValue interpreter.PathValue, - capabilityIDValue interpreter.UInt64Value, -) { + address common.Address, +) *interpreter.DictionaryValue { if targetPathValue.Domain != common.PathDomainStorage { panic(errors.NewUnreachableError()) } @@ -2544,18 +2615,71 @@ func unrecordPathCapabilityController( true, ) - setKey := capabilityIDValue - readValue := storageMap.ReadValue(inter, storageMapKey) if readValue == nil { + return nil + } + + capabilityIDSet, ok := readValue.(*interpreter.DictionaryValue) + if !ok { panic(errors.NewUnreachableError()) } - capabilityIDSet := readValue.(*interpreter.DictionaryValue) - existing := capabilityIDSet.Remove(inter, locationRange, setKey) + return capabilityIDSet +} + +func unrecordPathCapabilityController( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + address common.Address, + targetPathValue interpreter.PathValue, + capabilityIDValue interpreter.UInt64Value, +) { + capabilityIDSet := getPathCapabilityIDSet(inter, targetPathValue, address) + if capabilityIDSet == nil { + panic(errors.NewUnreachableError()) + } + + existing := capabilityIDSet.Remove(inter, locationRange, capabilityIDValue) if existing == interpreter.Nil { panic(errors.NewUnreachableError()) } + + // TODO: remove capability set if empty +} + +func getPathCapabilityControllerIDsIterator( + inter *interpreter.Interpreter, + address common.Address, + targetPathValue interpreter.PathValue, +) ( + nextCapabilityID func() (uint64, bool), + count uint64, +) { + capabilityIDSet := getPathCapabilityIDSet(inter, targetPathValue, address) + if capabilityIDSet == nil { + return func() (uint64, bool) { + return 0, false + }, 0 + } + + iterator := capabilityIDSet.Iterator() + + count = uint64(capabilityIDSet.Count()) + nextCapabilityID = func() (uint64, bool) { + keyValue := iterator.NextKey(inter) + if keyValue == nil { + return 0, false + } + + capabilityIDValue, ok := keyValue.(interpreter.UInt64Value) + if !ok { + panic(errors.NewUnreachableError()) + } + + return uint64(capabilityIDValue), true + } + return } func newAuthAccountCapabilitiesPublishFunction( From 5fc9131bc4f9e3629d12db8883c55112ffe69f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 15:39:54 -0700 Subject: [PATCH 097/246] remove argument label for function parameter of AuthAccount.StorageCapabilities.forEachController --- runtime/sema/authaccount.cdc | 2 +- runtime/sema/authaccount.gen.go | 1 + runtime/tests/checker/account_test.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index a71f964d0b..1e818c3a90 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -353,7 +353,7 @@ pub struct AuthAccount { /// Iterate through all storage capability controllers for capabilities that target this storage path. /// /// Returning false from the function stops the iteration. - pub fun forEachController(forPath: StoragePath, function: ((&StorageCapabilityController): Bool)) + pub fun forEachController(forPath: StoragePath, _ function: ((&StorageCapabilityController): Bool)) /// Issue/create a new storage capability. pub fun issue(_ path: StoragePath): Capability diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index 693d2b173a..99c48fda8e 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -1450,6 +1450,7 @@ var AuthAccountStorageCapabilitiesTypeForEachControllerFunctionType = &FunctionT TypeAnnotation: NewTypeAnnotation(StoragePathType), }, { + Label: ArgumentLabelNotRequired, Identifier: "function", TypeAnnotation: NewTypeAnnotation(&FunctionType{ Parameters: []Parameter{ diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index bafe1ecf37..7283fd455d 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -2303,7 +2303,7 @@ func TestCheckAccountCapabilities(t *testing.T) { capabilities.forEachController( forPath: /storage/bar, - function: fun (controller: &StorageCapabilityController): Bool { + fun (controller: &StorageCapabilityController): Bool { return true } ) From 304e4b0a42c80b0afb43c6ef7627f55686130987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 15:40:30 -0700 Subject: [PATCH 098/246] implement AuthAccount.StorageCapabilities.forEachController --- runtime/capabilitycontrollers_test.go | 107 +++++++++++++++++- runtime/stdlib/account.go | 149 ++++++++++++++++++++------ 2 files changed, 224 insertions(+), 32 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index ec4b90573d..2aec2831f7 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -793,7 +793,112 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - // TODO: forEachController + t.Run("forEachController, all", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let issuedCap4: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + + // Act + let controllers1: [&StorageCapabilityController] = [] + signer.capabilities.storage.forEachController( + forPath: storagePath1, + fun (controller: &StorageCapabilityController): Bool { + controllers1.append(controller) + return true + } + ) + + let controllers2: [&StorageCapabilityController] = [] + signer.capabilities.storage.forEachController( + forPath: storagePath2, + fun (controller: &StorageCapabilityController): Bool { + controllers2.append(controller) + return true + } + ) + + // Assert + assert(controllers1.length == 3) + + Test.quickSort( + &controllers1 as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1[i] + let b = controllers1[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers1[0].capabilityID == 1) + assert(controllers1[1].capabilityID == 2) + assert(controllers1[2].capabilityID == 3) + + assert(controllers2.length == 1) + assert(controllers2[0].capabilityID == 4) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("forEachController, stop immediately", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + + // Act + var stopped = false + signer.capabilities.storage.forEachController( + forPath: storagePath, + fun (controller: &StorageCapabilityController): Bool { + assert(!stopped) + stopped = true + return false + } + ) + + // Assert + assert(stopped) + } + } + `, + ) + require.NoError(t, err) + }) }) // TODO: AuthAccount.AccountCapabilities diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 10ac2cfde9..b17749fed0 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -723,7 +723,8 @@ func newAccountKeysGetFunction( ) } -// the AccountKey in `forEachKey(_ f: ((AccountKey): Bool)): Void` +// accountKeysForEachCallbackTypeParams are the parameter types of the callback function of +// `Auth/PublicAccount.Keys.forEachKey(_ f: ((AccountKey): Bool))` var accountKeysForEachCallbackTypeParams = []sema.Type{sema.AccountKeyType} func newAccountKeysForEachFunction( @@ -2212,13 +2213,12 @@ func newAuthAccountStorageCapabilitiesValue( accountIDGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) interpreter.Value { - // TODO: return interpreter.NewAuthAccountStorageCapabilitiesValue( gauge, addressValue, newAuthAccountStorageCapabilitiesGetControllerFunction(gauge, addressValue), newAuthAccountStorageCapabilitiesGetControllersFunction(gauge, addressValue), - nil, + newAuthAccountStorageCapabilitiesForEachControllerFunction(gauge, addressValue), newAuthAccountStorageCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), ) } @@ -2297,23 +2297,13 @@ func newAuthAccountStorageCapabilitiesGetControllerFunction( panic(errors.NewUnreachableError()) } - capabilityController := getCapabilityController(inter, address, uint64(capabilityIDValue)) - if capabilityController == nil { - return interpreter.Nil - } + capabilityID := uint64(capabilityIDValue) - storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) - if !ok { + referenceValue := getStorageCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { return interpreter.Nil } - referenceValue := interpreter.NewEphemeralReferenceValue( - inter, - false, - storageCapabilityController, - sema.StorageCapabilityControllerType, - ) - return interpreter.NewSomeValueNonCopying(inter, referenceValue) }, ) @@ -2346,7 +2336,8 @@ func newAuthAccountStorageCapabilitiesGetControllersFunction( // Get capability controllers iterator - nextCapabilityControllerID, count := getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) + nextCapabilityID, count := + getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) var capabilityControllerIndex uint64 = 0 @@ -2361,33 +2352,105 @@ func newAuthAccountStorageCapabilitiesGetControllersFunction( } capabilityControllerIndex++ - capabilityControllerID, ok := nextCapabilityControllerID() + capabilityID, ok := nextCapabilityID() if !ok { return nil } - capabilityController := getCapabilityController(inter, address, capabilityControllerID) - if capabilityController == nil { - panic(errors.NewUnreachableError()) - } - - storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) - if !ok { + referenceValue := getStorageCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { panic(errors.NewUnreachableError()) } - return interpreter.NewEphemeralReferenceValue( - inter, - false, - storageCapabilityController, - sema.StorageCapabilityControllerType, - ) + return referenceValue }, ) }, ) } +// the AccountKey in ` forEachController(forPath: StoragePath, _ function: ((&StorageCapabilityController): Bool))` +var authAccountStorageCapabilitiesForEachControllerCallbackTypeParams = []sema.Type{ + &sema.ReferenceType{ + Type: sema.StorageCapabilityControllerType, + }, +} + +func newAuthAccountStorageCapabilitiesForEachControllerFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountStorageCapabilitiesTypeForEachControllerFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get path argument + + targetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) + if !ok || targetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + // Get function argument + + functionValue, ok := invocation.Arguments[1].(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Get capability controllers iterator + + nextCapabilityID, _ := + getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) + + for { + capabilityID, ok := nextCapabilityID() + if !ok { + break + } + + referenceValue := getStorageCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { + panic(errors.NewUnreachableError()) + } + + subInvocation := interpreter.NewInvocation( + inter, + nil, + nil, + []interpreter.Value{referenceValue}, + authAccountStorageCapabilitiesForEachControllerCallbackTypeParams, + nil, + locationRange, + ) + + res, err := inter.InvokeFunction(functionValue, subInvocation) + if err != nil { + // interpreter panicked while invoking the inner function value + panic(err) + } + + shouldContinue, ok := res.(interpreter.BoolValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + if !shouldContinue { + break + } + } + + return interpreter.Void + }, + ) +} + func newAuthAccountStorageCapabilitiesIssueFunction( gauge common.MemoryGauge, idGenerator AccountIDGenerator, @@ -2512,6 +2575,30 @@ func getCapabilityController( return capabilityController } +func getStorageCapabilityControllerReference( + inter *interpreter.Interpreter, + address common.Address, + capabilityID uint64, +) *interpreter.EphemeralReferenceValue { + + capabilityController := getCapabilityController(inter, address, capabilityID) + if capabilityController == nil { + return nil + } + + storageCapabilityController, ok := capabilityController.(*interpreter.StorageCapabilityControllerValue) + if !ok { + return nil + } + + return interpreter.NewEphemeralReferenceValue( + inter, + false, + storageCapabilityController, + sema.StorageCapabilityControllerType, + ) +} + func newStorageCapabilityControllerRetargetFunction( inter *interpreter.Interpreter, address common.Address, From b755049c1e81e2bd150aa60f90857d38b6eff100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 16:54:55 -0700 Subject: [PATCH 099/246] swap order in StorageCapabilityController encoding so prefix is shared with AccountCapabilityController --- runtime/interpreter/decode.go | 32 ++++++++++++++-------------- runtime/interpreter/encode.go | 21 +++++++++--------- runtime/interpreter/encoding_test.go | 10 ++++----- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 7695cfcf3a..01adf9047c 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -996,6 +996,22 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability ) } + // Decode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey + borrowStaticType, err := d.DecodeStaticType() + if err != nil { + return nil, errors.NewUnexpectedError("invalid storage capability controller borrow type encoding: %w", err) + } + + // Decode capability ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey + + capabilityID, err := d.decoder.DecodeUint64() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller capability ID: %w", + err, + ) + } + // Decode path at array index encodedStorageCapabilityControllerValueTargetPathFieldKey num, err := d.decoder.DecodeTagNumber() if err != nil { @@ -1013,22 +1029,6 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability return nil, errors.NewUnexpectedError("invalid storage capability controller target path encoding: %w", err) } - // Decode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey - borrowStaticType, err := d.DecodeStaticType() - if err != nil { - return nil, errors.NewUnexpectedError("invalid storage capability controller borrow type encoding: %w", err) - } - - // Decode capability ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey - - capabilityID, err := d.decoder.DecodeUint64() - if err != nil { - return nil, errors.NewUnexpectedError( - "invalid storage capability controller capability ID: %w", - err, - ) - } - return NewStorageCapabilityControllerValue( d.memoryGauge, borrowStaticType, diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 3fef561fc9..d428f259c3 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -1040,9 +1040,9 @@ func (v TypeValue) Encode(e *atree.Encoder) error { // NOTE: NEVER change, only add/increment; ensure uint64 const ( - // encodedStorageCapabilityControllerValueTargetPathFieldKey uint64 = 0 - // encodedStorageCapabilityControllerValueBorrowTypeFieldKey uint64 = 1 - // encodedStorageCapabilityControllerValueCapabilityIDFieldKey uint64 = 2 + // encodedStorageCapabilityControllerValueBorrowTypeFieldKey uint64 = 0 + // encodedStorageCapabilityControllerValueCapabilityIDFieldKey uint64 = 1 + // encodedStorageCapabilityControllerValueTargetPathFieldKey uint64 = 2 // !!! *WARNING* !!! // @@ -1056,9 +1056,9 @@ const ( // cbor.Tag{ // Number: CBORTagStorageCapabilityControllerValue, // Content: []any{ -// encodedStorageCapabilityControllerValueTargetPathFieldKey: PathValue(v.TargetPath), // encodedStorageCapabilityControllerValueBorrowTypeFieldKey: StaticType(v.BorrowType), // encodedStorageCapabilityControllerValueCapabilityIDFieldKey: UInt64Value(v.CapabilityID), +// encodedStorageCapabilityControllerValueTargetPathFieldKey: PathValue(v.TargetPath), // }, // } func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { @@ -1072,11 +1072,6 @@ func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { if err != nil { return err } - // Encode target path at array index encodedStorageCapabilityControllerValueTargetPathFieldKey - err = v.TargetPath.Encode(e) - if err != nil { - return err - } // Encode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey err = EncodeStaticType(e.CBOR, v.BorrowType) @@ -1085,7 +1080,13 @@ func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { } // Encode ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey - return e.CBOR.EncodeUint64(uint64(v.CapabilityID)) + err = e.CBOR.EncodeUint64(uint64(v.CapabilityID)) + if err != nil { + return err + } + + // Encode target path at array index encodedStorageCapabilityControllerValueTargetPathFieldKey + return v.TargetPath.Encode(e) } func StaticTypeToBytes(t StaticType) (cbor.RawMessage, error) { diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index ded5db589c..0a57ca6f88 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -4010,6 +4010,11 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { 0xd8, CBORTagStorageCapabilityControllerValue, // array, 3 items follow 0x83, + } + result = append(result, bytes...) + result = append(result, + // positive integer 42 + 0x18, 0x2A, 0xd8, CBORTagPathValue, // array, 2 items follow 0x82, @@ -4019,11 +4024,6 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { 0x63, // b, a, r 0x62, 0x61, 0x72, - } - result = append(result, bytes...) - result = append(result, - // positive integer 42 - 0x18, 0x2A, ) return result } From 6958af6140a3b9f510f5ceda53ed34d03510fe57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 16:55:34 -0700 Subject: [PATCH 100/246] refactor AccpountCapabilityControllerValue to dedicated type --- runtime/common/memorykind.go | 1 + runtime/common/memorykind_string.go | 335 +++++++++--------- runtime/common/metering.go | 1 + runtime/interpreter/decode.go | 50 +++ runtime/interpreter/encode.go | 45 ++- runtime/interpreter/encoding_test.go | 88 +++++ .../value_accountcapabilitycontroller.go | 214 ++++++++--- runtime/interpreter/visitor.go | 9 + 8 files changed, 520 insertions(+), 223 deletions(-) diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 46ceb72352..9211280b38 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -52,6 +52,7 @@ const ( MemoryKindSimpleCompositeValue MemoryKindPublishedValue MemoryKindStorageCapabilityControllerValue + MemoryKindAccountCapabilityControllerValue // Atree Nodes MemoryKindAtreeArrayDataSlab diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 6ca38a1974..3729fd2ce3 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -33,176 +33,177 @@ func _() { _ = x[MemoryKindSimpleCompositeValue-22] _ = x[MemoryKindPublishedValue-23] _ = x[MemoryKindStorageCapabilityControllerValue-24] - _ = x[MemoryKindAtreeArrayDataSlab-25] - _ = x[MemoryKindAtreeArrayMetaDataSlab-26] - _ = x[MemoryKindAtreeArrayElementOverhead-27] - _ = x[MemoryKindAtreeMapDataSlab-28] - _ = x[MemoryKindAtreeMapMetaDataSlab-29] - _ = x[MemoryKindAtreeMapElementOverhead-30] - _ = x[MemoryKindAtreeMapPreAllocatedElement-31] - _ = x[MemoryKindAtreeEncodedSlab-32] - _ = x[MemoryKindPrimitiveStaticType-33] - _ = x[MemoryKindCompositeStaticType-34] - _ = x[MemoryKindInterfaceStaticType-35] - _ = x[MemoryKindVariableSizedStaticType-36] - _ = x[MemoryKindConstantSizedStaticType-37] - _ = x[MemoryKindDictionaryStaticType-38] - _ = x[MemoryKindOptionalStaticType-39] - _ = x[MemoryKindRestrictedStaticType-40] - _ = x[MemoryKindReferenceStaticType-41] - _ = x[MemoryKindCapabilityStaticType-42] - _ = x[MemoryKindFunctionStaticType-43] - _ = x[MemoryKindCadenceVoidValue-44] - _ = x[MemoryKindCadenceOptionalValue-45] - _ = x[MemoryKindCadenceBoolValue-46] - _ = x[MemoryKindCadenceStringValue-47] - _ = x[MemoryKindCadenceCharacterValue-48] - _ = x[MemoryKindCadenceAddressValue-49] - _ = x[MemoryKindCadenceIntValue-50] - _ = x[MemoryKindCadenceNumberValue-51] - _ = x[MemoryKindCadenceArrayValueBase-52] - _ = x[MemoryKindCadenceArrayValueLength-53] - _ = x[MemoryKindCadenceDictionaryValue-54] - _ = x[MemoryKindCadenceKeyValuePair-55] - _ = x[MemoryKindCadenceStructValueBase-56] - _ = x[MemoryKindCadenceStructValueSize-57] - _ = x[MemoryKindCadenceResourceValueBase-58] - _ = x[MemoryKindCadenceAttachmentValueBase-59] - _ = x[MemoryKindCadenceResourceValueSize-60] - _ = x[MemoryKindCadenceAttachmentValueSize-61] - _ = x[MemoryKindCadenceEventValueBase-62] - _ = x[MemoryKindCadenceEventValueSize-63] - _ = x[MemoryKindCadenceContractValueBase-64] - _ = x[MemoryKindCadenceContractValueSize-65] - _ = x[MemoryKindCadenceEnumValueBase-66] - _ = x[MemoryKindCadenceEnumValueSize-67] - _ = x[MemoryKindCadencePathLinkValue-68] - _ = x[MemoryKindCadenceAccountLinkValue-69] - _ = x[MemoryKindCadencePathValue-70] - _ = x[MemoryKindCadenceTypeValue-71] - _ = x[MemoryKindCadenceStorageCapabilityValue-72] - _ = x[MemoryKindCadenceFunctionValue-73] - _ = x[MemoryKindCadenceOptionalType-74] - _ = x[MemoryKindCadenceVariableSizedArrayType-75] - _ = x[MemoryKindCadenceConstantSizedArrayType-76] - _ = x[MemoryKindCadenceDictionaryType-77] - _ = x[MemoryKindCadenceField-78] - _ = x[MemoryKindCadenceParameter-79] - _ = x[MemoryKindCadenceTypeParameter-80] - _ = x[MemoryKindCadenceStructType-81] - _ = x[MemoryKindCadenceResourceType-82] - _ = x[MemoryKindCadenceAttachmentType-83] - _ = x[MemoryKindCadenceEventType-84] - _ = x[MemoryKindCadenceContractType-85] - _ = x[MemoryKindCadenceStructInterfaceType-86] - _ = x[MemoryKindCadenceResourceInterfaceType-87] - _ = x[MemoryKindCadenceContractInterfaceType-88] - _ = x[MemoryKindCadenceFunctionType-89] - _ = x[MemoryKindCadenceReferenceType-90] - _ = x[MemoryKindCadenceRestrictedType-91] - _ = x[MemoryKindCadenceCapabilityType-92] - _ = x[MemoryKindCadenceEnumType-93] - _ = x[MemoryKindRawString-94] - _ = x[MemoryKindAddressLocation-95] - _ = x[MemoryKindBytes-96] - _ = x[MemoryKindVariable-97] - _ = x[MemoryKindCompositeTypeInfo-98] - _ = x[MemoryKindCompositeField-99] - _ = x[MemoryKindInvocation-100] - _ = x[MemoryKindStorageMap-101] - _ = x[MemoryKindStorageKey-102] - _ = x[MemoryKindTypeToken-103] - _ = x[MemoryKindErrorToken-104] - _ = x[MemoryKindSpaceToken-105] - _ = x[MemoryKindProgram-106] - _ = x[MemoryKindIdentifier-107] - _ = x[MemoryKindArgument-108] - _ = x[MemoryKindBlock-109] - _ = x[MemoryKindFunctionBlock-110] - _ = x[MemoryKindParameter-111] - _ = x[MemoryKindParameterList-112] - _ = x[MemoryKindTypeParameter-113] - _ = x[MemoryKindTypeParameterList-114] - _ = x[MemoryKindTransfer-115] - _ = x[MemoryKindMembers-116] - _ = x[MemoryKindTypeAnnotation-117] - _ = x[MemoryKindDictionaryEntry-118] - _ = x[MemoryKindFunctionDeclaration-119] - _ = x[MemoryKindCompositeDeclaration-120] - _ = x[MemoryKindAttachmentDeclaration-121] - _ = x[MemoryKindInterfaceDeclaration-122] - _ = x[MemoryKindEnumCaseDeclaration-123] - _ = x[MemoryKindFieldDeclaration-124] - _ = x[MemoryKindTransactionDeclaration-125] - _ = x[MemoryKindImportDeclaration-126] - _ = x[MemoryKindVariableDeclaration-127] - _ = x[MemoryKindSpecialFunctionDeclaration-128] - _ = x[MemoryKindPragmaDeclaration-129] - _ = x[MemoryKindAssignmentStatement-130] - _ = x[MemoryKindBreakStatement-131] - _ = x[MemoryKindContinueStatement-132] - _ = x[MemoryKindEmitStatement-133] - _ = x[MemoryKindExpressionStatement-134] - _ = x[MemoryKindForStatement-135] - _ = x[MemoryKindIfStatement-136] - _ = x[MemoryKindReturnStatement-137] - _ = x[MemoryKindSwapStatement-138] - _ = x[MemoryKindSwitchStatement-139] - _ = x[MemoryKindWhileStatement-140] - _ = x[MemoryKindRemoveStatement-141] - _ = x[MemoryKindBooleanExpression-142] - _ = x[MemoryKindVoidExpression-143] - _ = x[MemoryKindNilExpression-144] - _ = x[MemoryKindStringExpression-145] - _ = x[MemoryKindIntegerExpression-146] - _ = x[MemoryKindFixedPointExpression-147] - _ = x[MemoryKindArrayExpression-148] - _ = x[MemoryKindDictionaryExpression-149] - _ = x[MemoryKindIdentifierExpression-150] - _ = x[MemoryKindInvocationExpression-151] - _ = x[MemoryKindMemberExpression-152] - _ = x[MemoryKindIndexExpression-153] - _ = x[MemoryKindConditionalExpression-154] - _ = x[MemoryKindUnaryExpression-155] - _ = x[MemoryKindBinaryExpression-156] - _ = x[MemoryKindFunctionExpression-157] - _ = x[MemoryKindCastingExpression-158] - _ = x[MemoryKindCreateExpression-159] - _ = x[MemoryKindDestroyExpression-160] - _ = x[MemoryKindReferenceExpression-161] - _ = x[MemoryKindForceExpression-162] - _ = x[MemoryKindPathExpression-163] - _ = x[MemoryKindAttachExpression-164] - _ = x[MemoryKindConstantSizedType-165] - _ = x[MemoryKindDictionaryType-166] - _ = x[MemoryKindFunctionType-167] - _ = x[MemoryKindInstantiationType-168] - _ = x[MemoryKindNominalType-169] - _ = x[MemoryKindOptionalType-170] - _ = x[MemoryKindReferenceType-171] - _ = x[MemoryKindRestrictedType-172] - _ = x[MemoryKindVariableSizedType-173] - _ = x[MemoryKindPosition-174] - _ = x[MemoryKindRange-175] - _ = x[MemoryKindElaboration-176] - _ = x[MemoryKindActivation-177] - _ = x[MemoryKindActivationEntries-178] - _ = x[MemoryKindVariableSizedSemaType-179] - _ = x[MemoryKindConstantSizedSemaType-180] - _ = x[MemoryKindDictionarySemaType-181] - _ = x[MemoryKindOptionalSemaType-182] - _ = x[MemoryKindRestrictedSemaType-183] - _ = x[MemoryKindReferenceSemaType-184] - _ = x[MemoryKindCapabilitySemaType-185] - _ = x[MemoryKindOrderedMap-186] - _ = x[MemoryKindOrderedMapEntryList-187] - _ = x[MemoryKindOrderedMapEntry-188] - _ = x[MemoryKindLast-189] + _ = x[MemoryKindAccountCapabilityControllerValue-25] + _ = x[MemoryKindAtreeArrayDataSlab-26] + _ = x[MemoryKindAtreeArrayMetaDataSlab-27] + _ = x[MemoryKindAtreeArrayElementOverhead-28] + _ = x[MemoryKindAtreeMapDataSlab-29] + _ = x[MemoryKindAtreeMapMetaDataSlab-30] + _ = x[MemoryKindAtreeMapElementOverhead-31] + _ = x[MemoryKindAtreeMapPreAllocatedElement-32] + _ = x[MemoryKindAtreeEncodedSlab-33] + _ = x[MemoryKindPrimitiveStaticType-34] + _ = x[MemoryKindCompositeStaticType-35] + _ = x[MemoryKindInterfaceStaticType-36] + _ = x[MemoryKindVariableSizedStaticType-37] + _ = x[MemoryKindConstantSizedStaticType-38] + _ = x[MemoryKindDictionaryStaticType-39] + _ = x[MemoryKindOptionalStaticType-40] + _ = x[MemoryKindRestrictedStaticType-41] + _ = x[MemoryKindReferenceStaticType-42] + _ = x[MemoryKindCapabilityStaticType-43] + _ = x[MemoryKindFunctionStaticType-44] + _ = x[MemoryKindCadenceVoidValue-45] + _ = x[MemoryKindCadenceOptionalValue-46] + _ = x[MemoryKindCadenceBoolValue-47] + _ = x[MemoryKindCadenceStringValue-48] + _ = x[MemoryKindCadenceCharacterValue-49] + _ = x[MemoryKindCadenceAddressValue-50] + _ = x[MemoryKindCadenceIntValue-51] + _ = x[MemoryKindCadenceNumberValue-52] + _ = x[MemoryKindCadenceArrayValueBase-53] + _ = x[MemoryKindCadenceArrayValueLength-54] + _ = x[MemoryKindCadenceDictionaryValue-55] + _ = x[MemoryKindCadenceKeyValuePair-56] + _ = x[MemoryKindCadenceStructValueBase-57] + _ = x[MemoryKindCadenceStructValueSize-58] + _ = x[MemoryKindCadenceResourceValueBase-59] + _ = x[MemoryKindCadenceAttachmentValueBase-60] + _ = x[MemoryKindCadenceResourceValueSize-61] + _ = x[MemoryKindCadenceAttachmentValueSize-62] + _ = x[MemoryKindCadenceEventValueBase-63] + _ = x[MemoryKindCadenceEventValueSize-64] + _ = x[MemoryKindCadenceContractValueBase-65] + _ = x[MemoryKindCadenceContractValueSize-66] + _ = x[MemoryKindCadenceEnumValueBase-67] + _ = x[MemoryKindCadenceEnumValueSize-68] + _ = x[MemoryKindCadencePathLinkValue-69] + _ = x[MemoryKindCadenceAccountLinkValue-70] + _ = x[MemoryKindCadencePathValue-71] + _ = x[MemoryKindCadenceTypeValue-72] + _ = x[MemoryKindCadenceStorageCapabilityValue-73] + _ = x[MemoryKindCadenceFunctionValue-74] + _ = x[MemoryKindCadenceOptionalType-75] + _ = x[MemoryKindCadenceVariableSizedArrayType-76] + _ = x[MemoryKindCadenceConstantSizedArrayType-77] + _ = x[MemoryKindCadenceDictionaryType-78] + _ = x[MemoryKindCadenceField-79] + _ = x[MemoryKindCadenceParameter-80] + _ = x[MemoryKindCadenceTypeParameter-81] + _ = x[MemoryKindCadenceStructType-82] + _ = x[MemoryKindCadenceResourceType-83] + _ = x[MemoryKindCadenceAttachmentType-84] + _ = x[MemoryKindCadenceEventType-85] + _ = x[MemoryKindCadenceContractType-86] + _ = x[MemoryKindCadenceStructInterfaceType-87] + _ = x[MemoryKindCadenceResourceInterfaceType-88] + _ = x[MemoryKindCadenceContractInterfaceType-89] + _ = x[MemoryKindCadenceFunctionType-90] + _ = x[MemoryKindCadenceReferenceType-91] + _ = x[MemoryKindCadenceRestrictedType-92] + _ = x[MemoryKindCadenceCapabilityType-93] + _ = x[MemoryKindCadenceEnumType-94] + _ = x[MemoryKindRawString-95] + _ = x[MemoryKindAddressLocation-96] + _ = x[MemoryKindBytes-97] + _ = x[MemoryKindVariable-98] + _ = x[MemoryKindCompositeTypeInfo-99] + _ = x[MemoryKindCompositeField-100] + _ = x[MemoryKindInvocation-101] + _ = x[MemoryKindStorageMap-102] + _ = x[MemoryKindStorageKey-103] + _ = x[MemoryKindTypeToken-104] + _ = x[MemoryKindErrorToken-105] + _ = x[MemoryKindSpaceToken-106] + _ = x[MemoryKindProgram-107] + _ = x[MemoryKindIdentifier-108] + _ = x[MemoryKindArgument-109] + _ = x[MemoryKindBlock-110] + _ = x[MemoryKindFunctionBlock-111] + _ = x[MemoryKindParameter-112] + _ = x[MemoryKindParameterList-113] + _ = x[MemoryKindTypeParameter-114] + _ = x[MemoryKindTypeParameterList-115] + _ = x[MemoryKindTransfer-116] + _ = x[MemoryKindMembers-117] + _ = x[MemoryKindTypeAnnotation-118] + _ = x[MemoryKindDictionaryEntry-119] + _ = x[MemoryKindFunctionDeclaration-120] + _ = x[MemoryKindCompositeDeclaration-121] + _ = x[MemoryKindAttachmentDeclaration-122] + _ = x[MemoryKindInterfaceDeclaration-123] + _ = x[MemoryKindEnumCaseDeclaration-124] + _ = x[MemoryKindFieldDeclaration-125] + _ = x[MemoryKindTransactionDeclaration-126] + _ = x[MemoryKindImportDeclaration-127] + _ = x[MemoryKindVariableDeclaration-128] + _ = x[MemoryKindSpecialFunctionDeclaration-129] + _ = x[MemoryKindPragmaDeclaration-130] + _ = x[MemoryKindAssignmentStatement-131] + _ = x[MemoryKindBreakStatement-132] + _ = x[MemoryKindContinueStatement-133] + _ = x[MemoryKindEmitStatement-134] + _ = x[MemoryKindExpressionStatement-135] + _ = x[MemoryKindForStatement-136] + _ = x[MemoryKindIfStatement-137] + _ = x[MemoryKindReturnStatement-138] + _ = x[MemoryKindSwapStatement-139] + _ = x[MemoryKindSwitchStatement-140] + _ = x[MemoryKindWhileStatement-141] + _ = x[MemoryKindRemoveStatement-142] + _ = x[MemoryKindBooleanExpression-143] + _ = x[MemoryKindVoidExpression-144] + _ = x[MemoryKindNilExpression-145] + _ = x[MemoryKindStringExpression-146] + _ = x[MemoryKindIntegerExpression-147] + _ = x[MemoryKindFixedPointExpression-148] + _ = x[MemoryKindArrayExpression-149] + _ = x[MemoryKindDictionaryExpression-150] + _ = x[MemoryKindIdentifierExpression-151] + _ = x[MemoryKindInvocationExpression-152] + _ = x[MemoryKindMemberExpression-153] + _ = x[MemoryKindIndexExpression-154] + _ = x[MemoryKindConditionalExpression-155] + _ = x[MemoryKindUnaryExpression-156] + _ = x[MemoryKindBinaryExpression-157] + _ = x[MemoryKindFunctionExpression-158] + _ = x[MemoryKindCastingExpression-159] + _ = x[MemoryKindCreateExpression-160] + _ = x[MemoryKindDestroyExpression-161] + _ = x[MemoryKindReferenceExpression-162] + _ = x[MemoryKindForceExpression-163] + _ = x[MemoryKindPathExpression-164] + _ = x[MemoryKindAttachExpression-165] + _ = x[MemoryKindConstantSizedType-166] + _ = x[MemoryKindDictionaryType-167] + _ = x[MemoryKindFunctionType-168] + _ = x[MemoryKindInstantiationType-169] + _ = x[MemoryKindNominalType-170] + _ = x[MemoryKindOptionalType-171] + _ = x[MemoryKindReferenceType-172] + _ = x[MemoryKindRestrictedType-173] + _ = x[MemoryKindVariableSizedType-174] + _ = x[MemoryKindPosition-175] + _ = x[MemoryKindRange-176] + _ = x[MemoryKindElaboration-177] + _ = x[MemoryKindActivation-178] + _ = x[MemoryKindActivationEntries-179] + _ = x[MemoryKindVariableSizedSemaType-180] + _ = x[MemoryKindConstantSizedSemaType-181] + _ = x[MemoryKindDictionarySemaType-182] + _ = x[MemoryKindOptionalSemaType-183] + _ = x[MemoryKindRestrictedSemaType-184] + _ = x[MemoryKindReferenceSemaType-185] + _ = x[MemoryKindCapabilitySemaType-186] + _ = x[MemoryKindOrderedMap-187] + _ = x[MemoryKindOrderedMapEntryList-188] + _ = x[MemoryKindOrderedMapEntry-189] + _ = x[MemoryKindLast-190] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 408, 426, 448, 473, 489, 509, 532, 559, 575, 594, 613, 632, 655, 678, 698, 716, 736, 755, 775, 793, 809, 829, 845, 863, 884, 903, 918, 936, 957, 980, 1002, 1021, 1043, 1065, 1089, 1115, 1139, 1165, 1186, 1207, 1231, 1255, 1275, 1295, 1315, 1338, 1354, 1370, 1399, 1419, 1438, 1467, 1496, 1517, 1529, 1545, 1565, 1582, 1601, 1622, 1638, 1657, 1683, 1711, 1739, 1758, 1778, 1799, 1820, 1835, 1844, 1859, 1864, 1872, 1889, 1903, 1913, 1923, 1933, 1942, 1952, 1962, 1969, 1979, 1987, 1992, 2005, 2014, 2027, 2040, 2057, 2065, 2072, 2086, 2101, 2120, 2140, 2161, 2181, 2200, 2216, 2238, 2255, 2274, 2300, 2317, 2336, 2350, 2367, 2380, 2399, 2411, 2422, 2437, 2450, 2465, 2479, 2494, 2511, 2525, 2538, 2554, 2571, 2591, 2606, 2626, 2646, 2666, 2682, 2697, 2718, 2733, 2749, 2767, 2784, 2800, 2817, 2836, 2851, 2865, 2881, 2898, 2912, 2924, 2941, 2952, 2964, 2977, 2991, 3008, 3016, 3021, 3032, 3042, 3059, 3080, 3101, 3119, 3135, 3153, 3170, 3188, 3198, 3217, 3232, 3236} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 408, 440, 458, 480, 505, 521, 541, 564, 591, 607, 626, 645, 664, 687, 710, 730, 748, 768, 787, 807, 825, 841, 861, 877, 895, 916, 935, 950, 968, 989, 1012, 1034, 1053, 1075, 1097, 1121, 1147, 1171, 1197, 1218, 1239, 1263, 1287, 1307, 1327, 1347, 1370, 1386, 1402, 1431, 1451, 1470, 1499, 1528, 1549, 1561, 1577, 1597, 1614, 1633, 1654, 1670, 1689, 1715, 1743, 1771, 1790, 1810, 1831, 1852, 1867, 1876, 1891, 1896, 1904, 1921, 1935, 1945, 1955, 1965, 1974, 1984, 1994, 2001, 2011, 2019, 2024, 2037, 2046, 2059, 2072, 2089, 2097, 2104, 2118, 2133, 2152, 2172, 2193, 2213, 2232, 2248, 2270, 2287, 2306, 2332, 2349, 2368, 2382, 2399, 2412, 2431, 2443, 2454, 2469, 2482, 2497, 2511, 2526, 2543, 2557, 2570, 2586, 2603, 2623, 2638, 2658, 2678, 2698, 2714, 2729, 2750, 2765, 2781, 2799, 2816, 2832, 2849, 2868, 2883, 2897, 2913, 2930, 2944, 2956, 2973, 2984, 2996, 3009, 3023, 3040, 3048, 3053, 3064, 3074, 3091, 3112, 3133, 3151, 3167, 3185, 3202, 3220, 3230, 3249, 3264, 3268} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 37e303b1fb..549e75c65a 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -154,6 +154,7 @@ var ( TypeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindTypeValue) PublishedValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPublishedValue) StorageCapabilityControllerValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageCapabilityControllerValue) + AccountCapabilityControllerValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountCapabilityControllerValue) // Static Types diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 01adf9047c..0ae572215a 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -321,6 +321,9 @@ func (d StorableDecoder) decodeStorable() (atree.Storable, error) { case CBORTagStorageCapabilityControllerValue: storable, err = d.decodeStorageCapabilityController() + case CBORTagAccountCapabilityControllerValue: + storable, err = d.decodeAccountCapabilityController() + default: return nil, UnsupportedTagDecodingError{ Tag: num, @@ -1037,6 +1040,53 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability ), nil } +func (d StorableDecoder) decodeAccountCapabilityController() (*AccountCapabilityControllerValue, error) { + + const expectedLength = encodedAccountCapabilityControllerValueLength + + size, err := d.decoder.DecodeArrayHead() + if err != nil { + if e, ok := err.(*cbor.WrongTypeError); ok { + return nil, errors.NewUnexpectedError( + "invalid account capability controller encoding: expected [%d]any, got %s", + expectedLength, + e.ActualType.String(), + ) + } + return nil, err + } + + if size != expectedLength { + return nil, errors.NewUnexpectedError( + "invalid account capability controller encoding: expected [%d]any, got [%d]any", + expectedLength, + size, + ) + } + + // Decode borrow type at array index encodedAccountCapabilityControllerValueBorrowTypeFieldKey + borrowStaticType, err := d.DecodeStaticType() + if err != nil { + return nil, errors.NewUnexpectedError("invalid account capability controller borrow type encoding: %w", err) + } + + // Decode capability ID at array index encodedAccountCapabilityControllerValueCapabilityIDFieldKey + + capabilityID, err := d.decoder.DecodeUint64() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid account capability controller capability ID: %w", + err, + ) + } + + return NewAccountCapabilityControllerValue( + d.memoryGauge, + borrowStaticType, + UInt64Value(capabilityID), + ), nil +} + func (d StorableDecoder) decodePathLink() (PathLinkValue, error) { const expectedLength = encodedPathLinkValueLength diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index d428f259c3..6d9995ddb1 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -201,7 +201,7 @@ const ( CBORTagPublishedValue CBORTagAccountLinkValue CBORTagStorageCapabilityControllerValue - _ + CBORTagAccountCapabilityControllerValue _ _ _ @@ -1089,6 +1089,49 @@ func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { return v.TargetPath.Encode(e) } +// NOTE: NEVER change, only add/increment; ensure uint64 +const ( + // encodedAccountCapabilityControllerValueBorrowTypeFieldKey uint64 = 0 + // encodedAccountCapabilityControllerValueCapabilityIDFieldKey uint64 = 1 + + // !!! *WARNING* !!! + // + // encodedAccountCapabilityControllerValueLength MUST be updated when new element is added. + // It is used to verify encoded account capability controller length during decoding. + encodedAccountCapabilityControllerValueLength = 2 +) + +// Encode encodes AccountCapabilityControllerValue as +// +// cbor.Tag{ +// Number: CBORTagAccountCapabilityControllerValue, +// Content: []any{ +// encodedAccountCapabilityControllerValueBorrowTypeFieldKey: StaticType(v.BorrowType), +// encodedAccountCapabilityControllerValueCapabilityIDFieldKey: UInt64Value(v.CapabilityID), +// }, +// } +func (v *AccountCapabilityControllerValue) Encode(e *atree.Encoder) error { + // Encode tag number and array head + err := e.CBOR.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagAccountCapabilityControllerValue, + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // Encode borrow type at array index encodedAccountCapabilityControllerValueBorrowTypeFieldKey + err = EncodeStaticType(e.CBOR, v.BorrowType) + if err != nil { + return err + } + + // Encode ID at array index encodedAccountCapabilityControllerValueCapabilityIDFieldKey + return e.CBOR.EncodeUint64(uint64(v.CapabilityID)) +} + func StaticTypeToBytes(t StaticType) (cbor.RawMessage, error) { var buf bytes.Buffer enc := CBOREncMode.NewStreamEncoder(&buf) diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 0a57ca6f88..53eb4c02eb 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -4491,3 +4491,91 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) } + +func TestEncodeDecodeAccountCapabilityControllerValue(t *testing.T) { + + t.Parallel() + + assemble := func(bytes ...byte) []byte { + result := []byte{ + // tag + 0xd8, CBORTagAccountCapabilityControllerValue, + // array, 2 items follow + 0x82, + } + result = append(result, bytes...) + result = append(result, + // positive integer 42 + 0x18, 0x2A, + ) + return result + } + + const capabilityID = 42 + + t.Run("AuthAccount reference, unauthorized, ", func(t *testing.T) { + + t.Parallel() + + value := &AccountCapabilityControllerValue{ + BorrowType: ReferenceStaticType{ + Authorized: false, + BorrowedType: PrimitiveStaticTypeBool, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("larger than max inline size", func(t *testing.T) { + + t.Parallel() + + // Generate an arbitrary, large static type + maxInlineElementSize := atree.MaxInlineArrayElementSize + var borrowType StaticType = PrimitiveStaticTypeNever + + for i := uint64(0); i < maxInlineElementSize; i++ { + borrowType = OptionalStaticType{ + Type: borrowType, + } + } + + expected := &AccountCapabilityControllerValue{ + BorrowType: borrowType, + CapabilityID: capabilityID, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + maxInlineElementSize: maxInlineElementSize, + encoded: []byte{ + // tag + 0xd8, atree.CBORTagStorageID, + + // storage ID + 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + }, + }, + ) + }) +} diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 6cc42c76a4..3f8dd6eb22 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -19,80 +19,184 @@ package interpreter import ( - "fmt" + "github.com/onflow/atree" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/format" "github.com/onflow/cadence/runtime/sema" ) -var AccountCapabilityControllerFieldNames = []string{ - sema.AccountCapabilityControllerTypeBorrowTypeFieldName, - sema.AccountCapabilityControllerTypeCapabilityIDFieldName, +// AccountCapabilityControllerValue + +type AccountCapabilityControllerValue struct { + BorrowType StaticType + CapabilityID UInt64Value +} + +func NewUnmeteredAccountCapabilityControllerValue( + staticType StaticType, + capabilityID UInt64Value, +) *AccountCapabilityControllerValue { + return &AccountCapabilityControllerValue{ + BorrowType: staticType, + CapabilityID: capabilityID, + } } func NewAccountCapabilityControllerValue( - gauge common.MemoryGauge, - capabilityID uint64, - borrowType StaticType, - delete func() error, -) Value { + memoryGauge common.MemoryGauge, + staticType StaticType, + capabilityID UInt64Value, +) *AccountCapabilityControllerValue { + // Constant because its constituents are already metered. + common.UseMemory(memoryGauge, common.AccountCapabilityControllerValueMemoryUsage) + return NewUnmeteredAccountCapabilityControllerValue( + staticType, + capabilityID, + ) +} + +var _ Value = &AccountCapabilityControllerValue{} +var _ atree.Value = &AccountCapabilityControllerValue{} +var _ EquatableValue = &AccountCapabilityControllerValue{} +var _ CapabilityControllerValue = &AccountCapabilityControllerValue{} +var _ MemberAccessibleValue = &AccountCapabilityControllerValue{} + +func (*AccountCapabilityControllerValue) IsValue() {} + +func (*AccountCapabilityControllerValue) isCapabilityControllerValue() {} + +func (v *AccountCapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { + visitor.VisitAccountCapabilityControllerValue(interpreter, v) +} + +func (v *AccountCapabilityControllerValue) Walk(_ *Interpreter, walkChild func(Value)) { + walkChild(v.CapabilityID) +} + +func (v *AccountCapabilityControllerValue) StaticType(_ *Interpreter) StaticType { + return PrimitiveStaticTypeAccountCapabilityController +} + +func (*AccountCapabilityControllerValue) IsImportable(_ *Interpreter) bool { + return false +} + +func (v *AccountCapabilityControllerValue) String() string { + return v.RecursiveString(SeenReferences{}) +} + +func (v *AccountCapabilityControllerValue) RecursiveString(seenReferences SeenReferences) string { + return format.AccountCapabilityController( + v.BorrowType.String(), + v.CapabilityID.RecursiveString(seenReferences), + ) +} + +func (v *AccountCapabilityControllerValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + common.UseMemory(memoryGauge, common.AccountCapabilityControllerValueStringMemoryUsage) + + return format.AccountCapabilityController( + v.BorrowType.MeteredString(memoryGauge), + v.CapabilityID.MeteredString(memoryGauge, seenReferences), + ) +} + +func (v *AccountCapabilityControllerValue) ConformsToStaticType( + _ *Interpreter, + _ LocationRange, + _ TypeConformanceResults, +) bool { + return true +} - borrowTypeValue := NewTypeValue(gauge, borrowType) - fields := map[string]Value{ - sema.AccountCapabilityControllerTypeBorrowTypeFieldName: borrowTypeValue, - sema.AccountCapabilityControllerTypeCapabilityIDFieldName: NewUInt64Value(gauge, func() uint64 { - return capabilityID - }), +func (v *AccountCapabilityControllerValue) Equal(interpreter *Interpreter, locationRange LocationRange, other Value) bool { + otherController, ok := other.(*AccountCapabilityControllerValue) + if !ok { + return false } - computeField := func(name string, inter *Interpreter, locationRange LocationRange) Value { - switch name { - case sema.AccountCapabilityControllerTypeDeleteFunctionName: - return NewHostFunctionValue( - gauge, - sema.AccountCapabilityControllerTypeDeleteFunctionType, - func(invocation Invocation) Value { - err := delete() - if err != nil { - panic(err) - } - - return Void - }, - ) - - } - - return nil + return otherController.BorrowType.Equal(v.BorrowType) && + otherController.CapabilityID.Equal(interpreter, locationRange, v.CapabilityID) +} + +func (*AccountCapabilityControllerValue) IsStorable() bool { + return true +} + +func (v *AccountCapabilityControllerValue) Storable(storage atree.SlabStorage, address atree.Address, maxInlineSize uint64) (atree.Storable, error) { + return maybeLargeImmutableStorable(v, storage, address, maxInlineSize) +} + +func (*AccountCapabilityControllerValue) NeedsStoreTo(_ atree.Address) bool { + return false +} + +func (*AccountCapabilityControllerValue) IsResourceKinded(_ *Interpreter) bool { + return false +} + +func (v *AccountCapabilityControllerValue) Transfer( + interpreter *Interpreter, + _ LocationRange, + _ atree.Address, + remove bool, + storable atree.Storable, +) Value { + if remove { + interpreter.RemoveReferencedSlab(storable) + } + return v +} + +func (v *AccountCapabilityControllerValue) Clone(interpreter *Interpreter) Value { + return &AccountCapabilityControllerValue{ + BorrowType: v.BorrowType, + CapabilityID: v.CapabilityID, } +} + +func (v *AccountCapabilityControllerValue) DeepRemove(_ *Interpreter) { + // NO-OP +} + +func (v *AccountCapabilityControllerValue) ByteSize() uint32 { + return mustStorableSize(v) +} + +func (v *AccountCapabilityControllerValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { + return v, nil +} - var str string - stringer := func(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { - if str == "" { - common.UseMemory(memoryGauge, common.AccountCapabilityControllerValueStringMemoryUsage) +func (v *AccountCapabilityControllerValue) ChildStorables() []atree.Storable { + return []atree.Storable{ + v.CapabilityID, + } +} - borrowTypeStr := borrowTypeValue.MeteredString(gauge, seenReferences) +func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { - memoryUsage := common.NewStringMemoryUsage(OverEstimateUintStringLength(uint(capabilityID))) - common.UseMemory(memoryGauge, memoryUsage) + // TODO: sema.AccountCapabilityControllerTypeDeleteFunctionName - idStr := fmt.Sprint(capabilityID) + switch name { + case sema.AccountCapabilityControllerTypeCapabilityIDFieldName: + return v.CapabilityID - str = format.AccountCapabilityController(borrowTypeStr, idStr) - } + case sema.AccountCapabilityControllerTypeBorrowTypeFieldName: + return NewTypeValue(inter, v.BorrowType) - return str } - return NewSimpleCompositeValue( - gauge, - sema.AccountCapabilityControllerType.ID(), - PrimitiveStaticTypeAccountCapabilityController, - AccountCapabilityControllerFieldNames, - fields, - computeField, - nil, - stringer, - ) + return nil +} + +func (*AccountCapabilityControllerValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { + // Storage capability controllers have no removable members (fields / functions) + panic(errors.NewUnreachableError()) +} + +func (*AccountCapabilityControllerValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { + // Storage capability controllers have no settable members (fields / functions) + panic(errors.NewUnreachableError()) } diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index 9c60ab34fc..b45b99ff2d 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -63,6 +63,7 @@ type Visitor interface { VisitHostFunctionValue(interpreter *Interpreter, value *HostFunctionValue) VisitBoundFunctionValue(interpreter *Interpreter, value BoundFunctionValue) VisitStorageCapabilityControllerValue(interpreter *Interpreter, v *StorageCapabilityControllerValue) + VisitAccountCapabilityControllerValue(interpreter *Interpreter, v *AccountCapabilityControllerValue) } type EmptyVisitor struct { @@ -110,6 +111,7 @@ type EmptyVisitor struct { HostFunctionValueVisitor func(interpreter *Interpreter, value *HostFunctionValue) BoundFunctionValueVisitor func(interpreter *Interpreter, value BoundFunctionValue) StorageCapabilityControllerValueVisitor func(interpreter *Interpreter, value *StorageCapabilityControllerValue) + AccountCapabilityControllerValueVisitor func(interpreter *Interpreter, value *AccountCapabilityControllerValue) } var _ Visitor = &EmptyVisitor{} @@ -421,3 +423,10 @@ func (v EmptyVisitor) VisitStorageCapabilityControllerValue(interpreter *Interpr } v.StorageCapabilityControllerValueVisitor(interpreter, value) } + +func (v EmptyVisitor) VisitAccountCapabilityControllerValue(interpreter *Interpreter, value *AccountCapabilityControllerValue) { + if v.AccountCapabilityControllerValueVisitor == nil { + return + } + v.AccountCapabilityControllerValueVisitor(interpreter, value) +} From 6ed030c1af27e6c0dea866d0b406acba1515af25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 16:55:45 -0700 Subject: [PATCH 101/246] remove unused test files --- .../value_accountcapabilitycontroller_test.go | 27 ------------------- .../value_storagecapabilitycontroller_test.go | 27 ------------------- 2 files changed, 54 deletions(-) delete mode 100644 runtime/interpreter/value_accountcapabilitycontroller_test.go delete mode 100644 runtime/interpreter/value_storagecapabilitycontroller_test.go diff --git a/runtime/interpreter/value_accountcapabilitycontroller_test.go b/runtime/interpreter/value_accountcapabilitycontroller_test.go deleted file mode 100644 index 2251031d44..0000000000 --- a/runtime/interpreter/value_accountcapabilitycontroller_test.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package interpreter - -import ( - "testing" -) - -func TestAccountCapabilityControllerValue(t *testing.T) { - // TODO add tests when capcons are integrated with accounts -} diff --git a/runtime/interpreter/value_storagecapabilitycontroller_test.go b/runtime/interpreter/value_storagecapabilitycontroller_test.go deleted file mode 100644 index 4dafd83ad5..0000000000 --- a/runtime/interpreter/value_storagecapabilitycontroller_test.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package interpreter - -import ( - "testing" -) - -func TestStorageCapabilityControllerValue(t *testing.T) { - // TODO add tests when capcons are integrated with accounts -} From 9f72101a10c2d56c0d6e0fa7ef1d91726cf633be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 18:08:41 -0700 Subject: [PATCH 102/246] change parameter order to make it consistent with AccountCapabilityController --- runtime/interpreter/decode.go | 2 +- runtime/interpreter/value_storagecapabilitycontroller.go | 8 ++++---- runtime/stdlib/account.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 0ae572215a..039df4fe3b 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1035,8 +1035,8 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability return NewStorageCapabilityControllerValue( d.memoryGauge, borrowStaticType, - pathValue, UInt64Value(capabilityID), + pathValue, ), nil } diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 97f0a61222..77f2b64e52 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -37,14 +37,14 @@ type CapabilityControllerValue interface { type StorageCapabilityControllerValue struct { BorrowType StaticType TargetPath PathValue - CapabilityID UInt64Value RetargetFunction FunctionValue + CapabilityID UInt64Value } func NewUnmeteredStorageCapabilityControllerValue( staticType StaticType, - targetPath PathValue, capabilityID UInt64Value, + targetPath PathValue, ) *StorageCapabilityControllerValue { return &StorageCapabilityControllerValue{ BorrowType: staticType, @@ -56,15 +56,15 @@ func NewUnmeteredStorageCapabilityControllerValue( func NewStorageCapabilityControllerValue( memoryGauge common.MemoryGauge, staticType StaticType, - targetPath PathValue, capabilityID UInt64Value, + targetPath PathValue, ) *StorageCapabilityControllerValue { // Constant because its constituents are already metered. common.UseMemory(memoryGauge, common.StorageCapabilityControllerValueMemoryUsage) return NewUnmeteredStorageCapabilityControllerValue( staticType, - targetPath, capabilityID, + targetPath, ) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index b17749fed0..7786b6bb1f 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2498,8 +2498,8 @@ func newAuthAccountStorageCapabilitiesIssueFunction( controller := interpreter.NewStorageCapabilityControllerValue( gauge, borrowStaticType, - targetPathValue, capabilityIDValue, + targetPathValue, ) storeCapabilityController(inter, address, capabilityIDValue, controller) From 8cef63aecab7e009bf62c6026a92c4d5b0726b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 21 Apr 2023 18:09:43 -0700 Subject: [PATCH 103/246] implement AuthAccount.AccountCapabilities.issue --- runtime/stdlib/account.go | 87 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 7786b6bb1f..fc232098e9 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2225,6 +2225,7 @@ func newAuthAccountStorageCapabilitiesValue( func newAuthAccountAccountCapabilitiesValue( gauge common.MemoryGauge, + accountIDGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) interpreter.Value { // TODO: @@ -2234,7 +2235,7 @@ func newAuthAccountAccountCapabilitiesValue( nil, nil, nil, - nil, + newAuthAccountAccountCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), ) } @@ -2266,6 +2267,7 @@ func newAuthAccountCapabilitiesValue( func() interpreter.Value { accountCapabilities := newAuthAccountAccountCapabilitiesValue( gauge, + idGenerator, addressValue, ) return interpreter.NewEphemeralReferenceValue( @@ -2509,7 +2511,65 @@ func newAuthAccountStorageCapabilitiesIssueFunction( gauge, capabilityIDValue, addressValue, - targetPathValue, + // TODO: remove + interpreter.EmptyPathValue, + borrowStaticType, + ) + }, + ) +} + +func newAuthAccountAccountCapabilitiesIssueFunction( + gauge common.MemoryGauge, + idGenerator AccountIDGenerator, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountAccountCapabilitiesTypeIssueFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + + // Get borrow type type argument + + typeParameterPair := invocation.TypeParameterTypes.Oldest() + borrowType, ok := typeParameterPair.Value.(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Create and write AccountCapabilityController + + borrowStaticType := interpreter.ConvertSemaReferenceTypeToStaticReferenceType(gauge, borrowType) + + var capabilityID uint64 + var err error + errors.WrapPanic(func() { + capabilityID, err = idGenerator.GenerateAccountID(address) + }) + if err != nil { + panic(err) + } + + capabilityIDValue := interpreter.UInt64Value(capabilityID) + + controller := interpreter.NewAccountCapabilityControllerValue( + gauge, + borrowStaticType, + capabilityIDValue, + ) + + storeCapabilityController(inter, address, capabilityIDValue, controller) + recordAccountCapabilityController(inter, address, capabilityIDValue) + + return interpreter.NewStorageCapabilityValue( + gauge, + capabilityIDValue, + addressValue, + // TODO: remove + interpreter.EmptyPathValue, borrowStaticType, ) }, @@ -2769,6 +2829,29 @@ func getPathCapabilityControllerIDsIterator( return } +// AccountCapabilityStorageDomain is the storage domain which +// records active account capability controller IDs +const AccountCapabilityStorageDomain = "acc_cap" + +func recordAccountCapabilityController( + inter *interpreter.Interpreter, + address common.Address, + capabilityIDValue interpreter.UInt64Value, +) { + storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + + storageMap := inter.Storage().GetStorageMap( + address, + AccountCapabilityStorageDomain, + true, + ) + + existed := storageMap.SetValue(inter, storageMapKey, interpreter.NilValue{}) + if existed { + panic(errors.NewUnreachableError()) + } +} + func newAuthAccountCapabilitiesPublishFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, From 831b4b99e4e47adc63aa6038d6022e27034255fd Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 25 Apr 2023 14:12:45 +0300 Subject: [PATCH 104/246] Initialize stdlib values of Test contract lazily --- runtime/stdlib/test.go | 1060 ++++++++++++++++++++--------------- runtime/stdlib/test_test.go | 4 +- 2 files changed, 600 insertions(+), 464 deletions(-) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index cb97039ba6..923f11f6fc 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -20,6 +20,7 @@ package stdlib import ( "fmt" + "sync" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" @@ -58,199 +59,122 @@ const matcherTestFunctionName = "test" const addressesFieldName = "addresses" -var TestContractLocation = common.IdentifierLocation(testContractTypeName) +const TestContractLocation = common.IdentifierLocation(testContractTypeName) -var TestContractChecker = func() *sema.Checker { +var testOnce sync.Once - program, err := parser.ParseProgram(nil, contracts.TestContract, parser.Config{}) - if err != nil { - panic(err) - } +// Deprecated: Use TestContractChecker instead +var testContractChecker *sema.Checker - activation := sema.NewVariableActivation(sema.BaseValueActivation) - activation.DeclareValue(AssertFunction) - activation.DeclareValue(PanicFunction) +// Deprecated: Use TestContractType instead +var testContractType *sema.CompositeType - var checker *sema.Checker - checker, err = sema.NewChecker( - program, - TestContractLocation, - nil, - &sema.Config{ - BaseValueActivation: activation, - AccessCheckMode: sema.AccessCheckModeStrict, - }, - ) - if err != nil { - panic(err) - } +// Deprecated: Use TestContractInitializerTypes +var testContractInitializerTypes []sema.Type - err = checker.Check() - if err != nil { - panic(err) - } +var testExpectFunction *interpreter.HostFunctionValue - return checker -}() +var equalMatcherFunction *interpreter.HostFunctionValue -func NewTestContract( - inter *interpreter.Interpreter, - testFramework TestFramework, - constructor interpreter.FunctionValue, - invocationRange ast.Range, -) ( - *interpreter.CompositeValue, - error, -) { - value, err := inter.InvokeFunctionValue( - constructor, - nil, - testContractInitializerTypes, - testContractInitializerTypes, - invocationRange, - ) - if err != nil { - return nil, err - } +var beEmptyMatcherFunction *interpreter.HostFunctionValue - compositeValue := value.(*interpreter.CompositeValue) +var haveElementCountMatcherFunction *interpreter.HostFunctionValue - // Inject natively implemented function values - compositeValue.Functions[testAssertFunctionName] = testAssertFunction - compositeValue.Functions[testFailFunctionName] = testFailFunction - compositeValue.Functions[testExpectFunctionName] = testExpectFunction - compositeValue.Functions[testNewEmulatorBlockchainFunctionName] = testNewEmulatorBlockchainFunction(testFramework) - compositeValue.Functions[testReadFileFunctionName] = testReadFileFunction(testFramework) +var containMatcherFunction *interpreter.HostFunctionValue - // Inject natively implemented matchers - compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction - compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction - compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction - compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction - compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction - compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction - compositeValue.Functions[beLessThanMatcherFunctionName] = beLessThanMatcherFunction +var beGreaterThanMatcherFunction *interpreter.HostFunctionValue - return compositeValue, nil -} +var beLessThanMatcherFunction *interpreter.HostFunctionValue -var testContractType = func() *sema.CompositeType { - variable, ok := TestContractChecker.Elaboration.GetGlobalType(testContractTypeName) - if !ok { - panic(errors.NewUnreachableError()) - } - return variable.Type.(*sema.CompositeType) -}() +var newMatcherFunction *interpreter.HostFunctionValue -var testContractInitializerTypes = func() (result []sema.Type) { - result = make([]sema.Type, len(testContractType.ConstructorParameters)) - for i, parameter := range testContractType.ConstructorParameters { - result[i] = parameter.TypeAnnotation.Type - } - return result -}() +var testNewEmulatorBlockchainFunctionType *sema.FunctionType -func typeNotFoundError(parentType, nestedType string) error { - return errors.NewUnexpectedError("cannot find type '%s.%s'", parentType, nestedType) -} +var emulatorBackendExecuteScriptFunctionType *sema.FunctionType -func memberNotFoundError(parentType, member string) error { - return errors.NewUnexpectedError("cannot find member '%s.%s'", parentType, member) -} +var emulatorBackendCreateAccountFunctionType *sema.FunctionType -var blockchainBackendInterfaceType = func() *sema.InterfaceType { - typ, ok := testContractType.NestedTypes.Get(blockchainBackendTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, blockchainBackendTypeName)) - } +var emulatorBackendAddTransactionFunctionType *sema.FunctionType - interfaceType, ok := typ.(*sema.InterfaceType) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected interface", - blockchainBackendTypeName, - )) - } +var emulatorBackendExecuteNextTransactionFunctionType *sema.FunctionType - return interfaceType -}() +var emulatorBackendCommitBlockFunctionType *sema.FunctionType -var matcherType = func() *sema.CompositeType { - typ, ok := testContractType.NestedTypes.Get(matcherTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, matcherTypeName)) - } +var emulatorBackendDeployContractFunctionType *sema.FunctionType - compositeType, ok := typ.(*sema.CompositeType) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected struct type", - matcherTypeName, - )) - } +var emulatorBackendUseConfigFunctionType *sema.FunctionType - return compositeType -}() +var emulatorBackendType *sema.CompositeType -var matcherTestFunctionType = compositeFunctionType(matcherType, matcherTestFunctionName) +func TestContractChecker() *sema.Checker { + testOnce.Do(initTest) + return testContractChecker +} -func compositeFunctionType(parent *sema.CompositeType, funcName string) *sema.FunctionType { - testFunc, ok := parent.Members.Get(funcName) - if !ok { - panic(memberNotFoundError(parent.Identifier, funcName)) - } +func TestContractType() *sema.CompositeType { + testOnce.Do(initTest) + return testContractType +} - return getFunctionTypeFromMember(testFunc, funcName) +func TestContractInitializerTypes() []sema.Type { + testOnce.Do(initTest) + return testContractInitializerTypes } -func interfaceFunctionType(parent *sema.InterfaceType, funcName string) *sema.FunctionType { - testFunc, ok := parent.Members.Get(funcName) - if !ok { - panic(memberNotFoundError(parent.Identifier, funcName)) +func initTest() { + program, err := parser.ParseProgram( + nil, + contracts.TestContract, + parser.Config{}, + ) + if err != nil { + panic(err) } - return getFunctionTypeFromMember(testFunc, funcName) -} + activation := sema.NewVariableActivation(sema.BaseValueActivation) + activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) -func getFunctionTypeFromMember(funcMember *sema.Member, funcName string) *sema.FunctionType { - functionType, ok := funcMember.TypeAnnotation.Type.(*sema.FunctionType) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected function type", - funcName, - )) + testContractChecker, err = sema.NewChecker( + program, + TestContractLocation, + nil, + &sema.Config{ + BaseValueActivation: activation, + AccessCheckMode: sema.AccessCheckModeStrict, + }, + ) + if err != nil { + panic(err) } - return functionType -} + err = testContractChecker.Check() + if err != nil { + panic(err) + } -func init() { + variable, ok := testContractChecker.Elaboration.GetGlobalType(testContractTypeName) + if !ok { + panic(errors.NewUnreachableError()) + } + testContractType = variable.Type.(*sema.CompositeType) - // Enrich 'Test' contract with natively implemented functions + testContractInitializerTypes = make([]sema.Type, len(testContractType.ConstructorParameters)) + for i, parameter := range testContractType.ConstructorParameters { + testContractInitializerTypes[i] = parameter.TypeAnnotation.Type + } - // Test.assert() - testContractType.Members.Set( - testAssertFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testAssertFunctionName, - testAssertFunctionType, - testAssertFunctionDocString, - ), - ) + blockchainBackendInterfaceType := initBlockchainBackendInterfaceType() - // Test.fail() - testContractType.Members.Set( - testFailFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testFailFunctionName, - testFailFunctionType, - testFailFunctionDocString, - ), - ) + matcherType := initMatcherType() + matcherTestFunctionType := compositeFunctionType(matcherType, matcherTestFunctionName) + + initEmulatorBackendFunctions(blockchainBackendInterfaceType) + initEmulatorBackendType(blockchainBackendInterfaceType) // Test.expect() + testExpectFunctionType := initTestExpectFunctionType(matcherType) + initTestExpectFunction(testExpectFunctionType) testContractType.Members.Set( testExpectFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -261,18 +185,9 @@ func init() { ), ) - // Test.newEmulatorBlockchain() - testContractType.Members.Set( - testNewEmulatorBlockchainFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testNewEmulatorBlockchainFunctionName, - testNewEmulatorBlockchainFunctionType, - testNewEmulatorBlockchainFunctionDocString, - ), - ) - // Test.newMatcher() + newMatcherFunctionType := initNewMatcherFunctionType(matcherType) + initNewMatcherFunction(newMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( newMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -284,6 +199,8 @@ func init() { ) // Test.equal() + equalMatcherFunctionType := initEqualMatcherFunctionType(matcherType) + initEqualMatcherFunction(equalMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( equalMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -295,6 +212,8 @@ func init() { ) // Test.beEmpty() + beEmptyMatcherFunctionType := initBeEmptyMatcherFunctionType(matcherType) + initBeEmptyMatcherFunction(beEmptyMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( beEmptyMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -306,6 +225,8 @@ func init() { ) // Test.haveElementCount() + haveElementCountMatcherFunctionType := initHaveElementCountMatcherFunctionType(matcherType) + initHaveElementCountMatcherFunction(haveElementCountMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( haveElementCountMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -317,6 +238,8 @@ func init() { ) // Test.contain() + containMatcherFunctionType := initContainMatcherFunctionType(matcherType) + initContainMatcherFunction(containMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( containMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -328,6 +251,8 @@ func init() { ) // Test.beGreaterThan() + beGreaterThanMatcherFunctionType := initBeGreaterThanMatcherFunctionType(matcherType) + initBeGreaterThanMatcherFunction(beGreaterThanMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( beGreaterThanMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -339,6 +264,8 @@ func init() { ) // Test.beLessThan() + beLessThanMatcherFunctionType := initBeLessThanMatcherFunctionType(matcherType) + initBeLessThanMatcherFunction(beLessThanMatcherFunctionType, matcherTestFunctionType) testContractType.Members.Set( beLessThanMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -349,33 +276,186 @@ func init() { ), ) - // Test.readFile() - testContractType.Members.Set( - testReadFileFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testReadFileFunctionName, - testReadFileFunctionType, - testReadFileFunctionDocString, - ), - ) + blockchainType, ok := testContractType.NestedTypes.Get(blockchainTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, blockchainTypeName)) + } + + testNewEmulatorBlockchainFunctionType = &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + blockchainType, + ), + } + + initTestContractTypeFunctions() + + // Enrich 'Test' contract elaboration with natively implemented composite types. + // e.g: 'EmulatorBackend' type. + testContractChecker.Elaboration.SetCompositeType( + emulatorBackendType.ID(), + emulatorBackendType, + ) +} + +func initBlockchainBackendInterfaceType() *sema.InterfaceType { + typ, ok := testContractType.NestedTypes.Get(blockchainBackendTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, blockchainBackendTypeName)) + } + + blockchainBackendInterfaceType, ok := typ.(*sema.InterfaceType) + if !ok { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected interface", + blockchainBackendTypeName, + )) + } + return blockchainBackendInterfaceType +} + +func initMatcherType() *sema.CompositeType { + typ, ok := testContractType.NestedTypes.Get(matcherTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, matcherTypeName)) + } + + matcherType, ok := typ.(*sema.CompositeType) + if !ok { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected struct type", + matcherTypeName, + )) + } + return matcherType +} + +func initTestContractTypeFunctions() { + // Enrich 'Test' contract with natively implemented functions + + // Test.assert() + testContractType.Members.Set( + testAssertFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + testAssertFunctionName, + testAssertFunctionType, + testAssertFunctionDocString, + ), + ) + + // Test.fail() + testContractType.Members.Set( + testFailFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + testFailFunctionName, + testFailFunctionType, + testFailFunctionDocString, + ), + ) + + // Test.newEmulatorBlockchain() + testContractType.Members.Set( + testNewEmulatorBlockchainFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + testNewEmulatorBlockchainFunctionName, + testNewEmulatorBlockchainFunctionType, + testNewEmulatorBlockchainFunctionDocString, + ), + ) + + // Test.readFile() + testContractType.Members.Set( + testReadFileFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + testReadFileFunctionName, + testReadFileFunctionType, + testReadFileFunctionDocString, + ), + ) +} + +func NewTestContract( + inter *interpreter.Interpreter, + testFramework TestFramework, + constructor interpreter.FunctionValue, + invocationRange ast.Range, +) ( + *interpreter.CompositeValue, + error, +) { + initializerTypes := TestContractInitializerTypes() + value, err := inter.InvokeFunctionValue( + constructor, + nil, + initializerTypes, + initializerTypes, + invocationRange, + ) + if err != nil { + return nil, err + } + + compositeValue := value.(*interpreter.CompositeValue) + + // Inject natively implemented function values + compositeValue.Functions[testAssertFunctionName] = testAssertFunction + compositeValue.Functions[testFailFunctionName] = testFailFunction + compositeValue.Functions[testExpectFunctionName] = testExpectFunction + compositeValue.Functions[testNewEmulatorBlockchainFunctionName] = testNewEmulatorBlockchainFunction(testFramework) + compositeValue.Functions[testReadFileFunctionName] = testReadFileFunction(testFramework) + + // Inject natively implemented matchers + compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction + compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction + compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction + compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction + compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction + compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction + compositeValue.Functions[beLessThanMatcherFunctionName] = beLessThanMatcherFunction + + return compositeValue, nil +} + +func typeNotFoundError(parentType, nestedType string) error { + return errors.NewUnexpectedError("cannot find type '%s.%s'", parentType, nestedType) +} + +func memberNotFoundError(parentType, member string) error { + return errors.NewUnexpectedError("cannot find member '%s.%s'", parentType, member) +} + +func compositeFunctionType(parent *sema.CompositeType, funcName string) *sema.FunctionType { + testFunc, ok := parent.Members.Get(funcName) + if !ok { + panic(memberNotFoundError(parent.Identifier, funcName)) + } - // Enrich 'Test' contract elaboration with natively implemented composite types. - // e.g: 'EmulatorBackend' type. - TestContractChecker.Elaboration.SetCompositeType( - EmulatorBackendType.ID(), - EmulatorBackendType, - ) + return getFunctionTypeFromMember(testFunc, funcName) } -var blockchainType = func() sema.Type { - typ, ok := testContractType.NestedTypes.Get(blockchainTypeName) +func interfaceFunctionType(parent *sema.InterfaceType, funcName string) *sema.FunctionType { + testFunc, ok := parent.Members.Get(funcName) if !ok { - panic(typeNotFoundError(testContractTypeName, blockchainTypeName)) + panic(memberNotFoundError(parent.Identifier, funcName)) + } + + return getFunctionTypeFromMember(testFunc, funcName) +} + +func getFunctionTypeFromMember(funcMember *sema.Member, funcName string) *sema.FunctionType { + functionType, ok := funcMember.TypeAnnotation.Type.(*sema.FunctionType) + if !ok { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected function type", + funcName, + )) } - return typ -}() + return functionType +} // Functions belonging to the 'Test' contract @@ -487,8 +567,7 @@ Expect function tests a value against a matcher, and fails the test if it's not const testExpectFunctionName = "expect" -var testExpectFunctionType = func() *sema.FunctionType { - +func initTestExpectFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ TypeBound: sema.AnyStructType, Name: "T", @@ -519,35 +598,37 @@ var testExpectFunctionType = func() *sema.FunctionType { sema.VoidType, ), } -}() +} -var testExpectFunction = interpreter.NewUnmeteredHostFunctionValue( - testExpectFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - value := invocation.Arguments[0] +func initTestExpectFunction(testExpectFunctionType *sema.FunctionType) { + testExpectFunction = interpreter.NewUnmeteredHostFunctionValue( + testExpectFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + value := invocation.Arguments[0] - matcher, ok := invocation.Arguments[1].(*interpreter.CompositeValue) - if !ok { - panic(errors.NewUnreachableError()) - } + matcher, ok := invocation.Arguments[1].(*interpreter.CompositeValue) + if !ok { + panic(errors.NewUnreachableError()) + } - inter := invocation.Interpreter - locationRange := invocation.LocationRange + inter := invocation.Interpreter + locationRange := invocation.LocationRange - result := invokeMatcherTest( - inter, - matcher, - value, - locationRange, - ) + result := invokeMatcherTest( + inter, + matcher, + value, + locationRange, + ) - if !result { - panic(AssertionError{}) - } + if !result { + panic(AssertionError{}) + } - return interpreter.Void - }, -) + return interpreter.Void + }, + ) +} func invokeMatcherTest( inter *interpreter.Interpreter, @@ -641,12 +722,6 @@ Creates a blockchain which is backed by a new emulator instance. const testNewEmulatorBlockchainFunctionName = "newEmulatorBlockchain" -var testNewEmulatorBlockchainFunctionType = &sema.FunctionType{ - ReturnTypeAnnotation: sema.NewTypeAnnotation( - blockchainType, - ), -} - func testNewEmulatorBlockchainFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( testNewEmulatorBlockchainFunctionType, @@ -718,8 +793,7 @@ The test function is of type '((T): Bool)', where 'T' is bound to 'AnyStruct'. const newMatcherFunctionName = "newMatcher" -var newMatcherFunctionType = func() *sema.FunctionType { - +func initNewMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ TypeBound: sema.AnyStructType, Name: "T", @@ -758,19 +832,28 @@ var newMatcherFunctionType = func() *sema.FunctionType { typeParameter, }, } -}() +} -var newMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - newMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - test, ok := invocation.Arguments[0].(interpreter.FunctionValue) - if !ok { - panic(errors.NewUnreachableError()) - } +func initNewMatcherFunction( + newMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + newMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + newMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + test, ok := invocation.Arguments[0].(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnreachableError()) + } - return newMatcherWithGenericTestFunction(invocation, test) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + test, + matcherTestFunctionType, + ) + }, + ) +} // 'EmulatorBackend' struct. // @@ -779,9 +862,45 @@ var newMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( const emulatorBackendTypeName = "EmulatorBackend" -var EmulatorBackendType = func() *sema.CompositeType { +func initEmulatorBackendFunctions(blockchainBackendInterfaceType *sema.InterfaceType) { + emulatorBackendExecuteScriptFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendExecuteScriptFunctionName, + ) + + emulatorBackendCreateAccountFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendCreateAccountFunctionName, + ) + + emulatorBackendAddTransactionFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendAddTransactionFunctionName, + ) + + emulatorBackendExecuteNextTransactionFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendExecuteNextTransactionFunctionName, + ) + + emulatorBackendCommitBlockFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendCommitBlockFunctionName, + ) + + emulatorBackendDeployContractFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendDeployContractFunctionName, + ) + + emulatorBackendUseConfigFunctionType = interfaceFunctionType( + blockchainBackendInterfaceType, + emulatorBackendUseConfigFunctionName, + ) +} - ty := &sema.CompositeType{ +func initEmulatorBackendType(blockchainBackendInterfaceType *sema.InterfaceType) { + emulatorBackendType = &sema.CompositeType{ Identifier: emulatorBackendTypeName, Kind: common.CompositeKindStructure, Location: TestContractLocation, @@ -792,54 +911,52 @@ var EmulatorBackendType = func() *sema.CompositeType { var members = []*sema.Member{ sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendExecuteScriptFunctionName, emulatorBackendExecuteScriptFunctionType, emulatorBackendExecuteScriptFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendCreateAccountFunctionName, emulatorBackendCreateAccountFunctionType, emulatorBackendCreateAccountFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendAddTransactionFunctionName, emulatorBackendAddTransactionFunctionType, emulatorBackendAddTransactionFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendExecuteNextTransactionFunctionName, emulatorBackendExecuteNextTransactionFunctionType, emulatorBackendExecuteNextTransactionFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendCommitBlockFunctionName, emulatorBackendCommitBlockFunctionType, emulatorBackendCommitBlockFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendDeployContractFunctionName, emulatorBackendDeployContractFunctionType, emulatorBackendDeployContractFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( - ty, + emulatorBackendType, emulatorBackendUseConfigFunctionName, emulatorBackendUseConfigFunctionType, emulatorBackendUseConfigFunctionDocString, ), } - ty.Members = sema.MembersAsMap(members) - ty.Fields = sema.MembersFieldNames(members) - - return ty -}() + emulatorBackendType.Members = sema.MembersAsMap(members) + emulatorBackendType.Fields = sema.MembersFieldNames(members) +} func newEmulatorBackend( inter *interpreter.Interpreter, @@ -879,7 +996,7 @@ func newEmulatorBackend( return interpreter.NewCompositeValue( inter, locationRange, - EmulatorBackendType.Location, + emulatorBackendType.Location, emulatorBackendTypeName, common.CompositeKindStructure, fields, @@ -896,11 +1013,6 @@ Executes a script and returns the script return value and the status. The 'returnValue' field of the result will be nil if the script failed. ` -var emulatorBackendExecuteScriptFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendExecuteScriptFunctionName, -) - func emulatorBackendExecuteScriptFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendExecuteScriptFunctionType, @@ -1003,11 +1115,6 @@ The transaction is paid by the service account. The returned account can be used to sign and authorize transactions. ` -var emulatorBackendCreateAccountFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendCreateAccountFunctionName, -) - func emulatorBackendCreateAccountFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendCreateAccountFunctionType, @@ -1076,11 +1183,6 @@ const emulatorBackendAddTransactionFunctionDocString = ` Add a transaction to the current block. ` -var emulatorBackendAddTransactionFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendAddTransactionFunctionName, -) - func emulatorBackendAddTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendAddTransactionFunctionType, @@ -1253,11 +1355,6 @@ Executes the next transaction in the block, if any. Returns the result of the transaction, or nil if no transaction was scheduled. ` -var emulatorBackendExecuteNextTransactionFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendExecuteNextTransactionFunctionName, -) - func emulatorBackendExecuteNextTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendExecuteNextTransactionFunctionType, @@ -1339,11 +1436,6 @@ const emulatorBackendCommitBlockFunctionDocString = ` Commit the current block. Committing will fail if there are un-executed transactions in the block. ` -var emulatorBackendCommitBlockFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendCommitBlockFunctionName, -) - func emulatorBackendCommitBlockFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendCommitBlockFunctionType, @@ -1366,8 +1458,7 @@ const equalMatcherFunctionDocString = ` Returns a matcher that succeeds if the tested value is equal to the given value. ` -var equalMatcherFunctionType = func() *sema.FunctionType { - +func initEqualMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ TypeBound: sema.AnyStructType, Name: "T", @@ -1392,41 +1483,50 @@ var equalMatcherFunctionType = func() *sema.FunctionType { }, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() +} -var equalMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - equalMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } +func initEqualMatcherFunction( + equalMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + equalMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + equalMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } - inter := invocation.Interpreter + inter := invocation.Interpreter - equalTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { + equalTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { - thisValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } + thisValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } - equal := thisValue.Equal( - inter, - invocation.LocationRange, - otherValue, - ) + equal := thisValue.Equal( + inter, + invocation.LocationRange, + otherValue, + ) - return interpreter.AsBoolValue(equal) - }, - ) + return interpreter.AsBoolValue(equal) + }, + ) - return newMatcherWithGenericTestFunction(invocation, equalTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + equalTestFunc, + matcherTestFunctionType, + ) + }, + ) +} const beEmptyMatcherFunctionName = "beEmpty" @@ -1435,39 +1535,48 @@ Returns a matcher that succeeds if the tested value is an array or dictionary, and the tested value contains no elements. ` -var beEmptyMatcherFunctionType = func() *sema.FunctionType { +func initBeEmptyMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { return &sema.FunctionType{ IsConstructor: false, TypeParameters: []*sema.TypeParameter{}, Parameters: []sema.Parameter{}, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() - -var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beEmptyMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - beEmptyTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var isEmpty bool - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - isEmpty = value.Count() == 0 - case *interpreter.DictionaryValue: - isEmpty = value.Count() == 0 - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } +} - return interpreter.AsBoolValue(isEmpty) - }, - ) +func initBeEmptyMatcherFunction( + beEmptyMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beEmptyMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + beEmptyTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var isEmpty bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + isEmpty = value.Count() == 0 + case *interpreter.DictionaryValue: + isEmpty = value.Count() == 0 + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(isEmpty) + }, + ) - return newMatcherWithGenericTestFunction(invocation, beEmptyTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + beEmptyTestFunc, + matcherTestFunctionType, + ) + }, + ) +} const haveElementCountMatcherFunctionName = "haveElementCount" @@ -1476,7 +1585,7 @@ Returns a matcher that succeeds if the tested value is an array or dictionary, and has the given number of elements. ` -var haveElementCountMatcherFunctionType = func() *sema.FunctionType { +func initHaveElementCountMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { return &sema.FunctionType{ IsConstructor: false, TypeParameters: []*sema.TypeParameter{}, @@ -1491,37 +1600,46 @@ var haveElementCountMatcherFunctionType = func() *sema.FunctionType { }, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() - -var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - haveElementCountMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - count, ok := invocation.Arguments[0].(interpreter.IntValue) - if !ok { - panic(errors.NewUnreachableError()) - } +} - haveElementCountTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var matchingCount bool - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - matchingCount = value.Count() == count.ToInt(invocation.LocationRange) - case *interpreter.DictionaryValue: - matchingCount = value.Count() == count.ToInt(invocation.LocationRange) - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } +func initHaveElementCountMatcherFunction( + haveElementCountMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + haveElementCountMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + count, ok := invocation.Arguments[0].(interpreter.IntValue) + if !ok { + panic(errors.NewUnreachableError()) + } - return interpreter.AsBoolValue(matchingCount) - }, - ) + haveElementCountTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var matchingCount bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + case *interpreter.DictionaryValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(matchingCount) + }, + ) - return newMatcherWithGenericTestFunction(invocation, haveElementCountTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + haveElementCountTestFunc, + matcherTestFunctionType, + ) + }, + ) +} const containMatcherFunctionName = "contain" @@ -1531,7 +1649,7 @@ a value that is equal to the given value, or the tested value is a dictionary that contains an entry where the key is equal to the given value. ` -var containMatcherFunctionType = func() *sema.FunctionType { +func initContainMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { return &sema.FunctionType{ IsConstructor: false, TypeParameters: []*sema.TypeParameter{}, @@ -1546,47 +1664,56 @@ var containMatcherFunctionType = func() *sema.FunctionType { }, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() - -var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - containMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - element, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } +} - inter := invocation.Interpreter +func initContainMatcherFunction( + containMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + containMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + element, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } - containTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var elementFound interpreter.BoolValue - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - elementFound = value.Contains( - inter, - invocation.LocationRange, - element, - ) - case *interpreter.DictionaryValue: - elementFound = value.ContainsKey( - inter, - invocation.LocationRange, - element, - ) - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } + inter := invocation.Interpreter - return elementFound - }, - ) + containTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var elementFound interpreter.BoolValue + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + elementFound = value.Contains( + inter, + invocation.LocationRange, + element, + ) + case *interpreter.DictionaryValue: + elementFound = value.ContainsKey( + inter, + invocation.LocationRange, + element, + ) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return elementFound + }, + ) - return newMatcherWithGenericTestFunction(invocation, containTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + containTestFunc, + matcherTestFunctionType, + ) + }, + ) +} const beGreaterThanMatcherFunctionName = "beGreaterThan" @@ -1595,7 +1722,7 @@ Returns a matcher that succeeds if the tested value is a number and greater than the given number. ` -var beGreaterThanMatcherFunctionType = func() *sema.FunctionType { +func initBeGreaterThanMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { return &sema.FunctionType{ IsConstructor: false, TypeParameters: []*sema.TypeParameter{}, @@ -1610,40 +1737,49 @@ var beGreaterThanMatcherFunctionType = func() *sema.FunctionType { }, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() +} -var beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beGreaterThanMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } +func initBeGreaterThanMatcherFunction( + beGreaterThanMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beGreaterThanMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } - inter := invocation.Interpreter + inter := invocation.Interpreter - beGreaterThanTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } + beGreaterThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } - isGreaterThan := thisValue.Greater( - inter, - otherValue, - invocation.LocationRange, - ) + isGreaterThan := thisValue.Greater( + inter, + otherValue, + invocation.LocationRange, + ) - return isGreaterThan - }, - ) + return isGreaterThan + }, + ) - return newMatcherWithGenericTestFunction(invocation, beGreaterThanTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + beGreaterThanTestFunc, + matcherTestFunctionType, + ) + }, + ) +} const beLessThanMatcherFunctionName = "beLessThan" @@ -1652,7 +1788,7 @@ Returns a matcher that succeeds if the tested value is a number and less than the given number. ` -var beLessThanMatcherFunctionType = func() *sema.FunctionType { +func initBeLessThanMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { return &sema.FunctionType{ IsConstructor: false, TypeParameters: []*sema.TypeParameter{}, @@ -1667,40 +1803,49 @@ var beLessThanMatcherFunctionType = func() *sema.FunctionType { }, ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), } -}() +} -var beLessThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beLessThanMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } +func initBeLessThanMatcherFunction( + beLessThanMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) { + beLessThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beLessThanMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } - inter := invocation.Interpreter + inter := invocation.Interpreter - beLessThanTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } + beLessThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } - isLessThan := thisValue.Less( - inter, - otherValue, - invocation.LocationRange, - ) + isLessThan := thisValue.Less( + inter, + otherValue, + invocation.LocationRange, + ) - return isLessThan - }, - ) + return isLessThan + }, + ) - return newMatcherWithGenericTestFunction(invocation, beLessThanTestFunc) - }, -) + return newMatcherWithGenericTestFunction( + invocation, + beLessThanTestFunc, + matcherTestFunctionType, + ) + }, + ) +} // 'EmulatorBackend.deployContract' function @@ -1710,11 +1855,6 @@ const emulatorBackendDeployContractFunctionDocString = ` Deploys a given contract, and initializes it with the provided arguments. ` -var emulatorBackendDeployContractFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendDeployContractFunctionName, -) - func emulatorBackendDeployContractFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendDeployContractFunctionType, @@ -1766,11 +1906,6 @@ const emulatorBackendUseConfigFunctionName = "useConfiguration" const emulatorBackendUseConfigFunctionDocString = `Use configurations function` -var emulatorBackendUseConfigFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendUseConfigFunctionName, -) - func emulatorBackendUseConfigFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( emulatorBackendUseConfigFunctionType, @@ -1840,6 +1975,7 @@ func (e TestFailedError) Error() string { func newMatcherWithGenericTestFunction( invocation interpreter.Invocation, testFunc interpreter.FunctionValue, + matcherTestFunctionType *sema.FunctionType, ) interpreter.Value { inter := invocation.Interpreter diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 393d4c1825..b968eea395 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -64,7 +64,7 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr ) { if importedLocation == TestContractLocation { return sema.ElaborationImport{ - Elaboration: TestContractChecker.Elaboration, + Elaboration: TestContractChecker().Elaboration, }, nil } @@ -91,7 +91,7 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr Storage: storage, ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { if location == TestContractLocation { - program := interpreter.ProgramFromChecker(TestContractChecker) + program := interpreter.ProgramFromChecker(TestContractChecker()) subInterpreter, err := inter.NewSubInterpreter(program, location) if err != nil { panic(err) From 0e664c1190ee44754427e489eb31852d2a6f15d8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Apr 2023 11:40:47 -0700 Subject: [PATCH 105/246] Fix conformance check for attachments --- runtime/sema/check_composite_declaration.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index fd262dc9f9..94d8bd8ded 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1305,11 +1305,17 @@ func (checker *Checker) checkCompositeLikeConformance( func (checker *Checker) checkConformanceKindMatch( compositeDeclaration ast.CompositeLikeDeclaration, - compositeType CompositeKindedType, + compositeKindedType CompositeKindedType, interfaceConformance *InterfaceType, ) { - if interfaceConformance.CompositeKind == compositeType.GetCompositeKind() { + if interfaceConformance.CompositeKind == compositeKindedType.GetCompositeKind() { + return + } + + // For attachments + if compositeType, ok := compositeKindedType.(*CompositeType); ok && + interfaceConformance.CompositeKind == compositeType.getBaseCompositeKind() { return } @@ -1342,7 +1348,7 @@ func (checker *Checker) checkConformanceKindMatch( checker.report( &CompositeKindMismatchError{ - ExpectedKind: compositeType.GetCompositeKind(), + ExpectedKind: compositeKindedType.GetCompositeKind(), ActualKind: interfaceConformance.CompositeKind, Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeKindMismatchIdentifier), }, From 14ed211c9ac0fcc71565aa628a17929b5c5bf787 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Apr 2023 16:04:43 -0700 Subject: [PATCH 106/246] Fix interface declaration order constraint --- runtime/interpreter/interpreter.go | 2 +- runtime/sema/check_casting_expression.go | 8 +- runtime/sema/check_composite_declaration.go | 8 +- runtime/sema/check_interface_declaration.go | 6 +- runtime/sema/checker.go | 19 ++++- runtime/sema/type.go | 84 ++++++++++----------- runtime/sema/type_tags.go | 2 +- runtime/tests/checker/interface_test.go | 30 ++++++++ 8 files changed, 98 insertions(+), 61 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 4ebab8bd62..e09dd332e7 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1134,7 +1134,7 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( // in reverse order: first the conformances, then the type requirements; // each conformances and type requirements in reverse order as well. - conformances := compositeType.InterfaceConformances() + conformances := compositeType.EffectiveInterfaceConformances() for i := len(conformances) - 1; i >= 0; i-- { conformance := conformances[i].InterfaceType wrapFunctions(interpreter.SharedState.typeCodes.InterfaceCodes[conformance.ID()]) diff --git a/runtime/sema/check_casting_expression.go b/runtime/sema/check_casting_expression.go index 73939a104f..3825efe9ac 100644 --- a/runtime/sema/check_casting_expression.go +++ b/runtime/sema/check_casting_expression.go @@ -234,7 +234,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { return IsSubType(typedInnerSubType, restrictedSuperType) && typedSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -255,7 +255,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { return IsSubType(typedInnerSubType, restrictedSuperType) && typedSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -278,7 +278,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { return IsSubType(typedInnerSubType, restrictedSuperType) && typedSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -317,7 +317,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { return IsSubType(typedSubType, typedSuperType.Type) && typedSuperType.RestrictionSet(). - IsSubsetOf(typedSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedSubType.EffectiveInterfaceConformanceSet()) default: diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 94d8bd8ded..d0493cdbef 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -214,7 +214,7 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL inheritedMembers := map[string]struct{}{} typeRequirementsInheritedMembers := map[string]map[string]struct{}{} - for _, conformance := range compositeType.InterfaceConformances() { + for _, conformance := range compositeType.EffectiveInterfaceConformances() { checker.checkCompositeLikeConformance( declaration, compositeType, @@ -697,7 +697,7 @@ func (checker *Checker) declareCompositeLikeMembersAndValue( var inheritedMembers StringMemberOrderedMap - for _, compositeTypeConformance := range compositeType.InterfaceConformances() { + for _, compositeTypeConformance := range compositeType.EffectiveInterfaceConformances() { conformanceNestedTypes := compositeTypeConformance.InterfaceType.GetNestedTypes() nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) @@ -1561,10 +1561,10 @@ func (checker *Checker) checkTypeRequirement( // Check that the composite declaration declares at least the conformances // that the type requirement stated - for _, requiredConformance := range requiredCompositeType.InterfaceConformances() { + for _, requiredConformance := range requiredCompositeType.EffectiveInterfaceConformances() { found := false - for _, conformance := range declaredCompositeType.InterfaceConformances() { + for _, conformance := range declaredCompositeType.EffectiveInterfaceConformances() { if conformance.InterfaceType == requiredConformance.InterfaceType { found = true break diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index adbc8446b4..80b4388910 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -54,7 +54,7 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl inheritedMembers := map[string]*Member{} inheritedTypes := map[string]Type{} - for _, conformance := range interfaceType.InterfaceConformances() { + for _, conformance := range interfaceType.EffectiveInterfaceConformances() { checker.checkInterfaceConformance( declaration, interfaceType, @@ -267,10 +267,6 @@ func (checker *Checker) declareInterfaceType(declaration *ast.InterfaceDeclarati ) } - // Resolve conformances - interfaceType.ExplicitInterfaceConformances = - checker.explicitInterfaceConformances(declaration, interfaceType) - checker.Elaboration.SetInterfaceDeclarationType(declaration, interfaceType) checker.Elaboration.SetInterfaceTypeDeclaration(interfaceType, declaration) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 6ed5cab3a6..34e529f718 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -286,11 +286,22 @@ func (checker *Checker) CheckProgram(program *ast.Program) { } } + // NOTE: Resolving interface conformances and registering types in elaboration + // must be done *after* the full container chain is fully set up for *all* interfaces types. + // This is because initializing the explicit interface conformances (`explicitInterfaceConformances()`) + // requires the other interfaces to be already defined. + // Therefore, this is done in two steps. + for _, declaration := range program.InterfaceDeclarations() { - interfaceType := checker.declareInterfaceType(declaration) + checker.declareInterfaceType(declaration) + } - // NOTE: register types in elaboration - // *after* the full container chain is fully set up + for _, declaration := range program.InterfaceDeclarations() { + interfaceType := checker.Elaboration.InterfaceDeclarationType(declaration) + + // Resolve conformances + interfaceType.ExplicitInterfaceConformances = + checker.explicitInterfaceConformances(declaration, interfaceType) VisitThisAndNested(interfaceType, registerInElaboration) } @@ -958,7 +969,7 @@ func CheckRestrictedType( // Prepare a set of all the conformances - conformances := compositeType.ExplicitInterfaceConformanceSet() + conformances := compositeType.EffectiveInterfaceConformanceSet() for _, restriction := range restrictions { // The restriction must be an explicit or implicit conformance diff --git a/runtime/sema/type.go b/runtime/sema/type.go index abbd3f643a..9d5d6fdc99 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3540,16 +3540,16 @@ type CompositeType struct { Fields []string ConstructorParameters []Parameter ImplicitTypeRequirementConformances []*CompositeType - interfaceConformances []Conformance - // an internal set of field `ExplicitInterfaceConformances` - explicitInterfaceConformanceSet *InterfaceSet - ExplicitInterfaceConformances []*InterfaceType - Kind common.CompositeKind - cachedIdentifiersLock sync.RWMutex - explicitInterfaceConformanceSetOnce sync.Once - memberResolversOnce sync.Once - interfaceConformancesOnce sync.Once - hasComputedMembers bool + // an internal set of field `effectiveInterfaceConformances` + effectiveInterfaceConformanceSet *InterfaceSet + effectiveInterfaceConformances []Conformance + ExplicitInterfaceConformances []*InterfaceType + Kind common.CompositeKind + cachedIdentifiersLock sync.RWMutex + effectiveInterfaceConformanceSetOnce sync.Once + effectiveInterfaceConformancesOnce sync.Once + memberResolversOnce sync.Once + hasComputedMembers bool // Only applicable for native composite types importable bool } @@ -3565,31 +3565,31 @@ func (t *CompositeType) Tag() TypeTag { return CompositeTypeTag } -func (t *CompositeType) ExplicitInterfaceConformanceSet() *InterfaceSet { - t.initializeExplicitInterfaceConformanceSet() - return t.explicitInterfaceConformanceSet +func (t *CompositeType) EffectiveInterfaceConformanceSet() *InterfaceSet { + t.initializeEffectiveInterfaceConformanceSet() + return t.effectiveInterfaceConformanceSet } -func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { - t.explicitInterfaceConformanceSetOnce.Do(func() { - t.explicitInterfaceConformanceSet = NewInterfaceSet() +func (t *CompositeType) initializeEffectiveInterfaceConformanceSet() { + t.effectiveInterfaceConformanceSetOnce.Do(func() { + t.effectiveInterfaceConformanceSet = NewInterfaceSet() - for _, conformance := range t.InterfaceConformances() { - t.explicitInterfaceConformanceSet.Add(conformance.InterfaceType) + for _, conformance := range t.EffectiveInterfaceConformances() { + t.effectiveInterfaceConformanceSet.Add(conformance.InterfaceType) } }) } -func (t *CompositeType) InterfaceConformances() []Conformance { - t.interfaceConformancesOnce.Do(func() { - t.interfaceConformances = distinctConformances( +func (t *CompositeType) EffectiveInterfaceConformances() []Conformance { + t.effectiveInterfaceConformancesOnce.Do(func() { + t.effectiveInterfaceConformances = distinctConformances( t.ExplicitInterfaceConformances, nil, map[*InterfaceType]struct{}{}, ) }) - return t.interfaceConformances + return t.effectiveInterfaceConformances } func (t *CompositeType) addImplicitTypeRequirementConformance(typeRequirement *CompositeType) { @@ -3860,7 +3860,7 @@ func (t *CompositeType) TypeRequirements() []*CompositeType { var typeRequirements []*CompositeType if containerComposite, ok := t.containerType.(*CompositeType); ok { - for _, conformance := range containerComposite.InterfaceConformances() { + for _, conformance := range containerComposite.EffectiveInterfaceConformances() { ty, ok := conformance.InterfaceType.NestedTypes.Get(t.Identifier) if !ok { continue @@ -3930,7 +3930,7 @@ func (t *CompositeType) initializeMemberResolvers() { // However, if this composite type is a type requirement, // it acts like an interface and does not have to declare members. - t.ExplicitInterfaceConformanceSet(). + t.EffectiveInterfaceConformanceSet(). ForEach(func(conformance *InterfaceType) { for name, resolver := range conformance.GetMembers() { //nolint:maprange if _, ok := memberResolvers[name]; !ok { @@ -4153,9 +4153,9 @@ type InterfaceType struct { cachedIdentifiersLock sync.RWMutex memberResolversOnce sync.Once - ExplicitInterfaceConformances []*InterfaceType - interfaceConformancesOnce sync.Once - interfaceConformances []Conformance + ExplicitInterfaceConformances []*InterfaceType + effectiveInterfaceConformancesOnce sync.Once + effectiveInterfaceConformances []Conformance } var _ Type = &InterfaceType{} @@ -4377,16 +4377,16 @@ func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDec return declaration.Members.FieldPosition(name, declaration.CompositeKind) } -func (t *InterfaceType) InterfaceConformances() []Conformance { - t.interfaceConformancesOnce.Do(func() { - t.interfaceConformances = distinctConformances( +func (t *InterfaceType) EffectiveInterfaceConformances() []Conformance { + t.effectiveInterfaceConformancesOnce.Do(func() { + t.effectiveInterfaceConformances = distinctConformances( t.ExplicitInterfaceConformances, nil, map[*InterfaceType]struct{}{}, ) }) - return t.interfaceConformances + return t.effectiveInterfaceConformances } // distinctConformances recursively visit conformances and their conformances, @@ -4399,7 +4399,7 @@ func distinctConformances( collectedConformances := make([]Conformance, 0) - var origin *InterfaceType + var conformanceChainRoot *InterfaceType for _, conformance := range conformances { if _, ok := seenConformances[conformance]; ok { @@ -4407,24 +4407,24 @@ func distinctConformances( } seenConformances[conformance] = struct{}{} - if parent != nil { - origin = parent + if parent == nil { + conformanceChainRoot = conformance } else { - origin = conformance + conformanceChainRoot = parent } collectedConformances = append( collectedConformances, Conformance{ InterfaceType: conformance, - ConformanceChainRoot: origin, + ConformanceChainRoot: conformanceChainRoot, }, ) // Recursively collect conformances nestedConformances := distinctConformances( conformance.ExplicitInterfaceConformances, - origin, + conformanceChainRoot, seenConformances, ) @@ -5468,7 +5468,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // TODO: once interfaces can conform to interfaces, include return IsSubType(typedInnerSubType, restrictedSuperType) && typedInnerSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } switch typedSubType.Type { @@ -5564,7 +5564,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // This is equivalent in principle to the check we would perform to check // if `&T <: &{U}`, the singleton restricted set containing only `U`. case *CompositeType: - return typedInnerSubType.ExplicitInterfaceConformanceSet().Contains(typedInnerSuperType) + return typedInnerSubType.EffectiveInterfaceConformanceSet().Contains(typedInnerSuperType) // An unauthorized reference to an interface type `&T` // is a supertype of a reference to a restricted type `&{U}`: @@ -5742,7 +5742,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // TODO: once interfaces can conform to interfaces, include return IsSubType(restrictedSubtype, restrictedSuperType) && typedSuperType.RestrictionSet(). - IsSubsetOf(restrictedSubtype.ExplicitInterfaceConformanceSet()) + IsSubsetOf(restrictedSubtype.EffectiveInterfaceConformanceSet()) } case *CompositeType: @@ -5753,7 +5753,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return IsSubType(typedSubType, typedSuperType.Type) && typedSuperType.RestrictionSet(). - IsSubsetOf(typedSubType.ExplicitInterfaceConformanceSet()) + IsSubsetOf(typedSubType.EffectiveInterfaceConformanceSet()) } default: @@ -5848,7 +5848,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { } // TODO: once interfaces can conform to interfaces, include - return typedSubType.ExplicitInterfaceConformanceSet(). + return typedSubType.EffectiveInterfaceConformanceSet(). Contains(typedSuperType) // An interface type is a supertype of a restricted type if the restricted set contains diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 339bc5db18..5ed6d9901d 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -867,7 +867,7 @@ func commonSuperTypeOfComposites(types []Type) Type { panic(errors.NewUnreachableError()) } - conformances := compositeType.InterfaceConformances() + conformances := compositeType.EffectiveInterfaceConformances() if len(conformances) > 0 { diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index e4a3c36e8c..cc285641fc 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2723,6 +2723,36 @@ func TestCheckInterfaceInheritance(t *testing.T) { require.NoError(t, err) }) + t.Run("interface declaration order", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct Baz: Bar { + let x: Int + + init() { + self.x = 3 + } + + fun test(): Int { + return self.x + } + } + + // 'Foo' is defined after later in the program. + struct interface Bar: Foo {} + + struct interface Foo { + let x: Int + + fun test(): Int + } + `) + + require.NoError(t, err) + }) + t.Run("resource interface", func(t *testing.T) { t.Parallel() From d3683cad2886b5dc8b66c1c6ba980dc90f90c5b8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Apr 2023 16:22:37 -0700 Subject: [PATCH 107/246] Add validation for cyclic conformances --- runtime/sema/check_interface_declaration.go | 7 +++++ runtime/sema/errors.go | 20 ++++++++++++++ runtime/tests/checker/interface_test.go | 29 +++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 80b4388910..2f130da9ee 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -55,6 +55,13 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl inheritedTypes := map[string]Type{} for _, conformance := range interfaceType.EffectiveInterfaceConformances() { + if conformance.InterfaceType == interfaceType { + checker.report(CyclicConformanceError{ + InterfaceType: interfaceType, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, declaration.Identifier), + }) + } + checker.checkInterfaceConformance( declaration, interfaceType, diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index f04ff42f99..b2d8bd2518 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1434,6 +1434,26 @@ func (e *DuplicateConformanceError) Error() string { ) } +// CyclicConformanceError +type CyclicConformanceError struct { + InterfaceType *InterfaceType + ast.Range +} + +var _ SemanticError = CyclicConformanceError{} +var _ errors.UserError = CyclicConformanceError{} + +func (CyclicConformanceError) isSemanticError() {} + +func (CyclicConformanceError) IsUserError() {} + +func (e CyclicConformanceError) Error() string { + return fmt.Sprintf( + "`%s` has a cyclic conformance to itself", + e.InterfaceType.QualifiedString(), + ) +} + // MultipleInterfaceDefaultImplementationsError type MultipleInterfaceDefaultImplementationsError struct { CompositeType CompositeKindedType diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index cc285641fc..1cbcc3cc79 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3187,6 +3187,35 @@ func TestCheckInterfaceInheritance(t *testing.T) { require.NoError(t, err) }) + + t.Run("cyclic conformance", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo: Baz { + let x: Int + + fun test(): Int + } + + struct interface Bar: Foo {} + + struct interface Baz: Bar {} + `) + + errs := RequireCheckerErrors(t, err, 3) + + conformanceError := sema.CyclicConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + assert.Equal(t, "Foo", conformanceError.InterfaceType.QualifiedIdentifier()) + + require.ErrorAs(t, errs[1], &conformanceError) + assert.Equal(t, "Bar", conformanceError.InterfaceType.QualifiedIdentifier()) + + require.ErrorAs(t, errs[2], &conformanceError) + assert.Equal(t, "Baz", conformanceError.InterfaceType.QualifiedIdentifier()) + }) } func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { From 7ee3a5e8a8a73e3cb22a2027a22a2afbe48453d3 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 25 Apr 2023 16:44:13 -0700 Subject: [PATCH 108/246] Improve code comments --- runtime/sema/check_composite_declaration.go | 7 ++++++- runtime/sema/check_interface_declaration.go | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index d0493cdbef..ad729bc806 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1303,22 +1303,27 @@ func (checker *Checker) checkCompositeLikeConformance( } +// checkConformanceKindMatch ensures the composite kinds match. +// e.g. a structure shouldn't be able to conform to a resource interface. func (checker *Checker) checkConformanceKindMatch( compositeDeclaration ast.CompositeLikeDeclaration, compositeKindedType CompositeKindedType, interfaceConformance *InterfaceType, ) { + // Check if the conformance kind matches the declaration type's kind. if interfaceConformance.CompositeKind == compositeKindedType.GetCompositeKind() { return } - // For attachments + // For attachments, check if the conformance kind matches the base type's kind. if compositeType, ok := compositeKindedType.(*CompositeType); ok && interfaceConformance.CompositeKind == compositeType.getBaseCompositeKind() { return } + // If not a match, then report an error. + var compositeKindMismatchIdentifier *ast.Identifier conformances := compositeDeclaration.ConformanceList() diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 2f130da9ee..c94de11c57 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -55,6 +55,8 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl inheritedTypes := map[string]Type{} for _, conformance := range interfaceType.EffectiveInterfaceConformances() { + // If the currently checking type is also in its own conformance list, + // then this is a direct/indirect cyclic conformance. if conformance.InterfaceType == interfaceType { checker.report(CyclicConformanceError{ InterfaceType: interfaceType, @@ -385,6 +387,10 @@ func (checker *Checker) declareInterfaceMembers(declaration *ast.InterfaceDeclar } } +// checkInterfaceConformance checks the validity of an interface-conformance of an interface declaration. +// It checks for: +// - Duplicate conformances +// - Conflicting members (functions, fields, and type definitions) func (checker *Checker) checkInterfaceConformance( interfaceDeclaration *ast.InterfaceDeclaration, interfaceType *InterfaceType, From 67d25eb01f39d575fab0501350a17227af1e884a Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 26 Apr 2023 08:32:15 -0700 Subject: [PATCH 109/246] Fix lint --- runtime/ast/interface_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/ast/interface_test.go b/runtime/ast/interface_test.go index 02e8c69f3d..19039f3ba1 100644 --- a/runtime/ast/interface_test.go +++ b/runtime/ast/interface_test.go @@ -53,9 +53,9 @@ func TestInterfaceDeclaration_MarshalJSON(t *testing.T) { actual, err := json.Marshal(decl) require.NoError(t, err) - assert.JSONEq(t, - // language=json - ` + assert.JSONEq(t, + // language=json + ` { "Type": "InterfaceDeclaration", "Access": "AccessPublic", From 8d74c307882f8ecc43799c7704efff63c2b5b462 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 26 Apr 2023 08:44:31 -0700 Subject: [PATCH 110/246] Refactor code --- runtime/ast/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/ast/interface.go b/runtime/ast/interface.go index 2723e7e717..d5ea3b997e 100644 --- a/runtime/ast/interface.go +++ b/runtime/ast/interface.go @@ -41,6 +41,7 @@ type InterfaceDeclaration struct { var _ Element = &InterfaceDeclaration{} var _ Declaration = &InterfaceDeclaration{} var _ Statement = &InterfaceDeclaration{} +var _ CompositeLikeDeclaration = &InterfaceDeclaration{} func NewInterfaceDeclaration( gauge common.MemoryGauge, From 3f8bea936b3b43c40b8c2301b910db097123f0c0 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 26 Apr 2023 11:03:41 -0700 Subject: [PATCH 111/246] Add test for pre/post conditions execution order --- runtime/tests/interpreter/interface_test.go | 195 ++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 79f7ab2589..a5a808b30a 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -24,7 +24,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/activations" "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" "github.com/onflow/cadence/runtime/tests/utils" ) @@ -663,4 +666,196 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { utils.RequireError(t, err) assert.ErrorAs(t, err, &interpreter.ConditionError{}) }) + + t.Run("pre conditions order", func(t *testing.T) { + + t.Parallel() + + var logs []string + valueDeclaration := stdlib.NewStandardLibraryFunction( + "log", + stdlib.LogFunctionType, + "", + func(invocation interpreter.Invocation) interpreter.Value { + msg := invocation.Arguments[0].(*interpreter.StringValue).Str + logs = append(logs, msg) + return interpreter.Void + }, + ) + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(valueDeclaration) + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, valueDeclaration) + + // Inheritance hierarchy is as follows: + // + // F (concrete type) + // | + // E (interface) + // / \ + // C D + // / \ + // A B + + inter, err := parseCheckAndInterpretWithOptions(t, ` + struct interface A { + pub fun test() { + pre { print("A") } + } + } + + struct interface B { + pub fun test() { + pre { print("B") } + } + } + + struct interface C: A, B { + pub fun test() { + pre { print("C") } + } + } + + struct interface D { + pub fun test() { + pre { print("D") } + } + } + + struct interface E: C, D { + pub fun test() { + pre { print("E") } + } + } + + struct F: E { + pub fun test() { + pre { print("F") } + } + } + + pub fun print(_ msg: String): Bool { + log(msg) + return true + } + + pub fun main() { + let f = F() + f.test() + }`, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + // The pre-conditions of the interfaces are executed first, with depth-first pre-order traversal. + // The pre-condition of the concrete type is executed at the end, after the interfaces. + assert.Equal(t, []string{"E", "C", "A", "B", "D", "F"}, logs) + }) + + t.Run("post conditions order", func(t *testing.T) { + + t.Parallel() + + var logs []string + valueDeclaration := stdlib.NewStandardLibraryFunction( + "log", + stdlib.LogFunctionType, + "", + func(invocation interpreter.Invocation) interpreter.Value { + msg := invocation.Arguments[0].(*interpreter.StringValue).Str + logs = append(logs, msg) + return interpreter.Void + }, + ) + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(valueDeclaration) + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, valueDeclaration) + + // Inheritance hierarchy is as follows: + // + // F (concrete type) + // | + // E (interface) + // / \ + // C D + // / \ + // A B + + inter, err := parseCheckAndInterpretWithOptions(t, ` + struct interface A { + pub fun test() { + post { print("A") } + } + } + + struct interface B { + pub fun test() { + post { print("B") } + } + } + + struct interface C: A, B { + pub fun test() { + post { print("C") } + } + } + + struct interface D { + pub fun test() { + post { print("D") } + } + } + + struct interface E: C, D { + pub fun test() { + post { print("E") } + } + } + + struct F: E { + pub fun test() { + post { print("F") } + } + } + + pub fun print(_ msg: String): Bool { + log(msg) + return true + } + + pub fun main() { + let f = F() + f.test() + }`, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + // The post-condition of the concrete type is executed first, before the interfaces. + // The post-conditions of the interfaces are executed after that, with the reversed depth-first pre-order. + assert.Equal(t, []string{"F", "D", "B", "A", "C", "E"}, logs) + }) } From 25279e6aa725203f1c82c17fb55be5b4bcb75e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 14:04:25 -0700 Subject: [PATCH 112/246] refactor Test.EmulatorBackend type/value code and move to separate file, avoid global variables --- runtime/stdlib/test.go | 513 +---------------------- runtime/stdlib/test_emulatorbackend.go | 537 +++++++++++++++++++++++++ 2 files changed, 544 insertions(+), 506 deletions(-) create mode 100644 runtime/stdlib/test_emulatorbackend.go diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 923f11f6fc..e473b97784 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -90,21 +90,9 @@ var newMatcherFunction *interpreter.HostFunctionValue var testNewEmulatorBlockchainFunctionType *sema.FunctionType -var emulatorBackendExecuteScriptFunctionType *sema.FunctionType - -var emulatorBackendCreateAccountFunctionType *sema.FunctionType - -var emulatorBackendAddTransactionFunctionType *sema.FunctionType - -var emulatorBackendExecuteNextTransactionFunctionType *sema.FunctionType - -var emulatorBackendCommitBlockFunctionType *sema.FunctionType - -var emulatorBackendDeployContractFunctionType *sema.FunctionType - -var emulatorBackendUseConfigFunctionType *sema.FunctionType - -var emulatorBackendType *sema.CompositeType +// TODO: nest in future testType +// Deprecated +var testEmulatorBackend *testEmulatorBackendType func TestContractChecker() *sema.Checker { testOnce.Do(initTest) @@ -169,8 +157,7 @@ func initTest() { matcherType := initMatcherType() matcherTestFunctionType := compositeFunctionType(matcherType, matcherTestFunctionName) - initEmulatorBackendFunctions(blockchainBackendInterfaceType) - initEmulatorBackendType(blockchainBackendInterfaceType) + testEmulatorBackend = newTestEmulatorBackendType(blockchainBackendInterfaceType) // Test.expect() testExpectFunctionType := initTestExpectFunctionType(matcherType) @@ -292,8 +279,8 @@ func initTest() { // Enrich 'Test' contract elaboration with natively implemented composite types. // e.g: 'EmulatorBackend' type. testContractChecker.Elaboration.SetCompositeType( - emulatorBackendType.ID(), - emulatorBackendType, + testEmulatorBackend.compositeType.ID(), + testEmulatorBackend.compositeType, ) } @@ -730,7 +717,7 @@ func testNewEmulatorBlockchainFunction(testFramework TestFramework) *interpreter locationRange := invocation.LocationRange // Create an `EmulatorBackend` - emulatorBackend := newEmulatorBackend( + emulatorBackend := testEmulatorBackend.new( inter, testFramework, locationRange, @@ -855,187 +842,6 @@ func initNewMatcherFunction( ) } -// 'EmulatorBackend' struct. -// -// 'EmulatorBackend' is the native implementation of the 'Test.BlockchainBackend' interface. -// It provides a blockchain backed by the emulator. - -const emulatorBackendTypeName = "EmulatorBackend" - -func initEmulatorBackendFunctions(blockchainBackendInterfaceType *sema.InterfaceType) { - emulatorBackendExecuteScriptFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendExecuteScriptFunctionName, - ) - - emulatorBackendCreateAccountFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendCreateAccountFunctionName, - ) - - emulatorBackendAddTransactionFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendAddTransactionFunctionName, - ) - - emulatorBackendExecuteNextTransactionFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendExecuteNextTransactionFunctionName, - ) - - emulatorBackendCommitBlockFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendCommitBlockFunctionName, - ) - - emulatorBackendDeployContractFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendDeployContractFunctionName, - ) - - emulatorBackendUseConfigFunctionType = interfaceFunctionType( - blockchainBackendInterfaceType, - emulatorBackendUseConfigFunctionName, - ) -} - -func initEmulatorBackendType(blockchainBackendInterfaceType *sema.InterfaceType) { - emulatorBackendType = &sema.CompositeType{ - Identifier: emulatorBackendTypeName, - Kind: common.CompositeKindStructure, - Location: TestContractLocation, - ExplicitInterfaceConformances: []*sema.InterfaceType{ - blockchainBackendInterfaceType, - }, - } - - var members = []*sema.Member{ - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendExecuteScriptFunctionName, - emulatorBackendExecuteScriptFunctionType, - emulatorBackendExecuteScriptFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendCreateAccountFunctionName, - emulatorBackendCreateAccountFunctionType, - emulatorBackendCreateAccountFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendAddTransactionFunctionName, - emulatorBackendAddTransactionFunctionType, - emulatorBackendAddTransactionFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendExecuteNextTransactionFunctionName, - emulatorBackendExecuteNextTransactionFunctionType, - emulatorBackendExecuteNextTransactionFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendCommitBlockFunctionName, - emulatorBackendCommitBlockFunctionType, - emulatorBackendCommitBlockFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendDeployContractFunctionName, - emulatorBackendDeployContractFunctionType, - emulatorBackendDeployContractFunctionDocString, - ), - sema.NewUnmeteredPublicFunctionMember( - emulatorBackendType, - emulatorBackendUseConfigFunctionName, - emulatorBackendUseConfigFunctionType, - emulatorBackendUseConfigFunctionDocString, - ), - } - - emulatorBackendType.Members = sema.MembersAsMap(members) - emulatorBackendType.Fields = sema.MembersFieldNames(members) -} - -func newEmulatorBackend( - inter *interpreter.Interpreter, - testFramework TestFramework, - locationRange interpreter.LocationRange, -) *interpreter.CompositeValue { - var fields = []interpreter.CompositeField{ - { - Name: emulatorBackendExecuteScriptFunctionName, - Value: emulatorBackendExecuteScriptFunction(testFramework), - }, - { - Name: emulatorBackendCreateAccountFunctionName, - Value: emulatorBackendCreateAccountFunction(testFramework), - }, { - Name: emulatorBackendAddTransactionFunctionName, - Value: emulatorBackendAddTransactionFunction(testFramework), - }, - { - Name: emulatorBackendExecuteNextTransactionFunctionName, - Value: emulatorBackendExecuteNextTransactionFunction(testFramework), - }, - { - Name: emulatorBackendCommitBlockFunctionName, - Value: emulatorBackendCommitBlockFunction(testFramework), - }, - { - Name: emulatorBackendDeployContractFunctionName, - Value: emulatorBackendDeployContractFunction(testFramework), - }, - { - Name: emulatorBackendUseConfigFunctionName, - Value: emulatorBackendUseConfigFunction(testFramework), - }, - } - - return interpreter.NewCompositeValue( - inter, - locationRange, - emulatorBackendType.Location, - emulatorBackendTypeName, - common.CompositeKindStructure, - fields, - common.ZeroAddress, - ) -} - -// 'EmulatorBackend.executeScript' function - -const emulatorBackendExecuteScriptFunctionName = "executeScript" - -const emulatorBackendExecuteScriptFunctionDocString = ` -Executes a script and returns the script return value and the status. -The 'returnValue' field of the result will be nil if the script failed. -` - -func emulatorBackendExecuteScriptFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendExecuteScriptFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - script, ok := invocation.Arguments[0].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - args, err := arrayValueToSlice(invocation.Arguments[1]) - if err != nil { - panic(errors.NewUnexpectedErrorFromCause(err)) - } - - inter := invocation.Interpreter - - result := testFramework.RunScript(inter, script.Str, args) - - return newScriptResult(inter, result.Value, result) - }, - ) -} - func arrayValueToSlice(value interpreter.Value) ([]interpreter.Value, error) { array, ok := value.(*interpreter.ArrayValue) if !ok { @@ -1105,157 +911,6 @@ func getConstructor(inter *interpreter.Interpreter, typeName string) *interprete return resultStatusConstructor } -// 'EmulatorBackend.createAccount' function - -const emulatorBackendCreateAccountFunctionName = "createAccount" - -const emulatorBackendCreateAccountFunctionDocString = ` -Creates an account by submitting an account creation transaction. -The transaction is paid by the service account. -The returned account can be used to sign and authorize transactions. -` - -func emulatorBackendCreateAccountFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendCreateAccountFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - account, err := testFramework.CreateAccount() - if err != nil { - panic(err) - } - - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - return newAccountValue( - testFramework, - inter, - locationRange, - account, - ) - }, - ) -} - -func newAccountValue( - framework TestFramework, - inter *interpreter.Interpreter, - locationRange interpreter.LocationRange, - account *Account, -) interpreter.Value { - - // Create address value - address := interpreter.NewAddressValue(nil, account.Address) - - standardLibraryHandler := framework.StandardLibraryHandler() - - publicKey := NewPublicKeyValue( - inter, - locationRange, - account.PublicKey, - standardLibraryHandler, - standardLibraryHandler, - ) - - // Create an 'Account' by calling its constructor. - accountConstructor := getConstructor(inter, accountTypeName) - accountValue, err := inter.InvokeExternally( - accountConstructor, - accountConstructor.Type, - []interpreter.Value{ - address, - publicKey, - }, - ) - - if err != nil { - panic(err) - } - - return accountValue -} - -// 'EmulatorBackend.addTransaction' function - -const emulatorBackendAddTransactionFunctionName = "addTransaction" - -const emulatorBackendAddTransactionFunctionDocString = ` -Add a transaction to the current block. -` - -func emulatorBackendAddTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendAddTransactionFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - transactionValue, ok := invocation.Arguments[0].(interpreter.MemberAccessibleValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - // Get transaction code - codeValue := transactionValue.GetMember( - inter, - locationRange, - transactionCodeFieldName, - ) - code, ok := codeValue.(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - // Get authorizers - authorizerValue := transactionValue.GetMember( - inter, - locationRange, - transactionAuthorizerFieldName, - ) - - authorizers := addressesFromValue(authorizerValue) - - // Get signers - signersValue := transactionValue.GetMember( - inter, - locationRange, - transactionSignersFieldName, - ) - - signerAccounts := accountsFromValue( - inter, - signersValue, - locationRange, - ) - - // Get arguments - argsValue := transactionValue.GetMember( - inter, - locationRange, - transactionArgsFieldName, - ) - args, err := arrayValueToSlice(argsValue) - if err != nil { - panic(errors.NewUnexpectedErrorFromCause(err)) - } - - err = testFramework.AddTransaction( - invocation.Interpreter, - code.Str, - authorizers, - signerAccounts, - args, - ) - - if err != nil { - panic(err) - } - - return interpreter.Void - }, - ) -} - func addressesFromValue(accountsValue interpreter.Value) []common.Address { accountsArray, ok := accountsValue.(*interpreter.ArrayValue) if !ok { @@ -1346,31 +1001,6 @@ func accountFromValue( } } -// 'EmulatorBackend.executeNextTransaction' function - -const emulatorBackendExecuteNextTransactionFunctionName = "executeNextTransaction" - -const emulatorBackendExecuteNextTransactionFunctionDocString = ` -Executes the next transaction in the block, if any. -Returns the result of the transaction, or nil if no transaction was scheduled. -` - -func emulatorBackendExecuteNextTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendExecuteNextTransactionFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - result := testFramework.ExecuteNextTransaction() - - // If there are no transactions to run, then return `nil`. - if result == nil { - return interpreter.Nil - } - - return newTransactionResult(invocation.Interpreter, result) - }, - ) -} - // newTransactionResult Creates a "TransactionResult" indicating the status of the transaction execution. func newTransactionResult(inter *interpreter.Interpreter, result *TransactionResult) interpreter.Value { // Lookup and get 'ResultStatus' enum value. @@ -1428,28 +1058,6 @@ func newErrorValue(inter *interpreter.Interpreter, err error) interpreter.Value return errorValue } -// 'EmulatorBackend.commitBlock' function - -const emulatorBackendCommitBlockFunctionName = "commitBlock" - -const emulatorBackendCommitBlockFunctionDocString = ` -Commit the current block. Committing will fail if there are un-executed transactions in the block. -` - -func emulatorBackendCommitBlockFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendCommitBlockFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - err := testFramework.CommitBlock() - if err != nil { - panic(err) - } - - return interpreter.Void - }, - ) -} - // Built-in matchers const equalMatcherFunctionName = "equal" @@ -1847,113 +1455,6 @@ func initBeLessThanMatcherFunction( ) } -// 'EmulatorBackend.deployContract' function - -const emulatorBackendDeployContractFunctionName = "deployContract" - -const emulatorBackendDeployContractFunctionDocString = ` -Deploys a given contract, and initializes it with the provided arguments. -` - -func emulatorBackendDeployContractFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendDeployContractFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - - // Contract name - name, ok := invocation.Arguments[0].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - // Contract code - code, ok := invocation.Arguments[1].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - // authorizer - accountValue, ok := invocation.Arguments[2].(interpreter.MemberAccessibleValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - account := accountFromValue(inter, accountValue, invocation.LocationRange) - - // Contract init arguments - args, err := arrayValueToSlice(invocation.Arguments[3]) - if err != nil { - panic(err) - } - - err = testFramework.DeployContract( - inter, - name.Str, - code.Str, - account, - args, - ) - - return newErrorValue(inter, err) - }, - ) -} - -// 'EmulatorBackend.useConfiguration' function - -const emulatorBackendUseConfigFunctionName = "useConfiguration" - -const emulatorBackendUseConfigFunctionDocString = `Use configurations function` - -func emulatorBackendUseConfigFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - emulatorBackendUseConfigFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - - // configurations - configsValue, ok := invocation.Arguments[0].(*interpreter.CompositeValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - addresses, ok := configsValue.GetMember( - inter, - invocation.LocationRange, - addressesFieldName, - ).(*interpreter.DictionaryValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - mapping := make(map[string]common.Address, addresses.Count()) - - addresses.Iterate(nil, func(locationValue, addressValue interpreter.Value) bool { - location, ok := locationValue.(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - address, ok := addressValue.(interpreter.AddressValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - mapping[location.Str] = common.Address(address) - - return true - }) - - testFramework.UseConfiguration(&Configuration{ - Addresses: mapping, - }) - - return interpreter.Void - }, - ) -} - // TestFailedError type TestFailedError struct { diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go new file mode 100644 index 0000000000..5dba4c9d4b --- /dev/null +++ b/runtime/stdlib/test_emulatorbackend.go @@ -0,0 +1,537 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package stdlib + +import ( + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" +) + +// 'EmulatorBackend' struct. +// +// 'EmulatorBackend' is the native implementation of the 'Test.BlockchainBackend' interface. +// It provides a blockchain backed by the emulator. + +const testEmulatorBackendTypeName = "EmulatorBackend" + +type testEmulatorBackendType struct { + compositeType *sema.CompositeType + executeScriptFunctionType *sema.FunctionType + createAccountFunctionType *sema.FunctionType + addTransactionFunctionType *sema.FunctionType + executeNextTransactionFunctionType *sema.FunctionType + commitBlockFunctionType *sema.FunctionType + deployContractFunctionType *sema.FunctionType + useConfigFunctionType *sema.FunctionType +} + +func newTestEmulatorBackendType(blockchainBackendInterfaceType *sema.InterfaceType) *testEmulatorBackendType { + executeScriptFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeExecuteScriptFunctionName, + ) + + createAccountFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeCreateAccountFunctionName, + ) + + addTransactionFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeAddTransactionFunctionName, + ) + + executeNextTransactionFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeExecuteNextTransactionFunctionName, + ) + + commitBlockFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeCommitBlockFunctionName, + ) + + deployContractFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeDeployContractFunctionName, + ) + + useConfigFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeUseConfigFunctionName, + ) + + compositeType := &sema.CompositeType{ + Identifier: testEmulatorBackendTypeName, + Kind: common.CompositeKindStructure, + Location: TestContractLocation, + ExplicitInterfaceConformances: []*sema.InterfaceType{ + blockchainBackendInterfaceType, + }, + } + + var members = []*sema.Member{ + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeExecuteScriptFunctionName, + executeScriptFunctionType, + testEmulatorBackendTypeExecuteScriptFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeCreateAccountFunctionName, + createAccountFunctionType, + testEmulatorBackendTypeCreateAccountFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeAddTransactionFunctionName, + addTransactionFunctionType, + testEmulatorBackendTypeAddTransactionFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeExecuteNextTransactionFunctionName, + executeNextTransactionFunctionType, + testEmulatorBackendTypeExecuteNextTransactionFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeCommitBlockFunctionName, + commitBlockFunctionType, + testEmulatorBackendTypeCommitBlockFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeDeployContractFunctionName, + deployContractFunctionType, + newEmulatorBackendTypeDeployContractFunctionDocString, + ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeUseConfigFunctionName, + useConfigFunctionType, + emulatorBackendUseConfigFunctionDocString, + ), + } + + compositeType.Members = sema.MembersAsMap(members) + compositeType.Fields = sema.MembersFieldNames(members) + + return &testEmulatorBackendType{ + compositeType: compositeType, + executeScriptFunctionType: executeScriptFunctionType, + createAccountFunctionType: createAccountFunctionType, + addTransactionFunctionType: addTransactionFunctionType, + executeNextTransactionFunctionType: executeNextTransactionFunctionType, + commitBlockFunctionType: commitBlockFunctionType, + deployContractFunctionType: deployContractFunctionType, + useConfigFunctionType: useConfigFunctionType, + } +} + +// 'EmulatorBackend.executeScript' function + +const testEmulatorBackendTypeExecuteScriptFunctionName = "executeScript" + +const testEmulatorBackendTypeExecuteScriptFunctionDocString = ` +Executes a script and returns the script return value and the status. +The 'returnValue' field of the result will be nil if the script failed. +` + +func (t *testEmulatorBackendType) newExecuteScriptFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.executeScriptFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + script, ok := invocation.Arguments[0].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + args, err := arrayValueToSlice(invocation.Arguments[1]) + if err != nil { + panic(errors.NewUnexpectedErrorFromCause(err)) + } + + inter := invocation.Interpreter + + result := testFramework.RunScript(inter, script.Str, args) + + return newScriptResult(inter, result.Value, result) + }, + ) +} + +// 'EmulatorBackend.createAccount' function + +const testEmulatorBackendTypeCreateAccountFunctionName = "createAccount" + +const testEmulatorBackendTypeCreateAccountFunctionDocString = ` +Creates an account by submitting an account creation transaction. +The transaction is paid by the service account. +The returned account can be used to sign and authorize transactions. +` + +func (t *testEmulatorBackendType) newCreateAccountFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.createAccountFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + account, err := testFramework.CreateAccount() + if err != nil { + panic(err) + } + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + return newTestAccountValue( + testFramework, + inter, + locationRange, + account, + ) + }, + ) +} + +func newTestAccountValue( + framework TestFramework, + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + account *Account, +) interpreter.Value { + + // Create address value + address := interpreter.NewAddressValue(nil, account.Address) + + standardLibraryHandler := framework.StandardLibraryHandler() + + publicKey := NewPublicKeyValue( + inter, + locationRange, + account.PublicKey, + standardLibraryHandler, + standardLibraryHandler, + ) + + // Create an 'Account' by calling its constructor. + accountConstructor := getConstructor(inter, accountTypeName) + accountValue, err := inter.InvokeExternally( + accountConstructor, + accountConstructor.Type, + []interpreter.Value{ + address, + publicKey, + }, + ) + + if err != nil { + panic(err) + } + + return accountValue +} + +// 'EmulatorBackend.addTransaction' function + +const testEmulatorBackendTypeAddTransactionFunctionName = "addTransaction" + +const testEmulatorBackendTypeAddTransactionFunctionDocString = ` +Add a transaction to the current block. +` + +func (t *testEmulatorBackendType) newAddTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.addTransactionFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + transactionValue, ok := invocation.Arguments[0].(interpreter.MemberAccessibleValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Get transaction code + codeValue := transactionValue.GetMember( + inter, + locationRange, + transactionCodeFieldName, + ) + code, ok := codeValue.(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Get authorizers + authorizerValue := transactionValue.GetMember( + inter, + locationRange, + transactionAuthorizerFieldName, + ) + + authorizers := addressesFromValue(authorizerValue) + + // Get signers + signersValue := transactionValue.GetMember( + inter, + locationRange, + transactionSignersFieldName, + ) + + signerAccounts := accountsFromValue( + inter, + signersValue, + locationRange, + ) + + // Get arguments + argsValue := transactionValue.GetMember( + inter, + locationRange, + transactionArgsFieldName, + ) + args, err := arrayValueToSlice(argsValue) + if err != nil { + panic(errors.NewUnexpectedErrorFromCause(err)) + } + + err = testFramework.AddTransaction( + invocation.Interpreter, + code.Str, + authorizers, + signerAccounts, + args, + ) + + if err != nil { + panic(err) + } + + return interpreter.Void + }, + ) +} + +// 'EmulatorBackend.executeNextTransaction' function + +const testEmulatorBackendTypeExecuteNextTransactionFunctionName = "executeNextTransaction" + +const testEmulatorBackendTypeExecuteNextTransactionFunctionDocString = ` +Executes the next transaction in the block, if any. +Returns the result of the transaction, or nil if no transaction was scheduled. +` + +func (t *testEmulatorBackendType) newExecuteNextTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.executeNextTransactionFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + result := testFramework.ExecuteNextTransaction() + + // If there are no transactions to run, then return `nil`. + if result == nil { + return interpreter.Nil + } + + return newTransactionResult(invocation.Interpreter, result) + }, + ) +} + +// 'EmulatorBackend.commitBlock' function + +const testEmulatorBackendTypeCommitBlockFunctionName = "commitBlock" + +const testEmulatorBackendTypeCommitBlockFunctionDocString = ` +Commit the current block. Committing will fail if there are un-executed transactions in the block. +` + +func (t *testEmulatorBackendType) newCommitBlockFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.commitBlockFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + err := testFramework.CommitBlock() + if err != nil { + panic(err) + } + + return interpreter.Void + }, + ) +} + +// 'EmulatorBackend.deployContract' function + +const testEmulatorBackendTypeDeployContractFunctionName = "deployContract" + +const newEmulatorBackendTypeDeployContractFunctionDocString = ` +Deploys a given contract, and initializes it with the provided arguments. +` + +func (t *testEmulatorBackendType) newDeployContractFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.deployContractFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + + // Contract name + name, ok := invocation.Arguments[0].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Contract code + code, ok := invocation.Arguments[1].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // authorizer + accountValue, ok := invocation.Arguments[2].(interpreter.MemberAccessibleValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + account := accountFromValue(inter, accountValue, invocation.LocationRange) + + // Contract init arguments + args, err := arrayValueToSlice(invocation.Arguments[3]) + if err != nil { + panic(err) + } + + err = testFramework.DeployContract( + inter, + name.Str, + code.Str, + account, + args, + ) + + return newErrorValue(inter, err) + }, + ) +} + +// 'EmulatorBackend.useConfiguration' function + +const testEmulatorBackendTypeUseConfigFunctionName = "useConfiguration" + +const emulatorBackendUseConfigFunctionDocString = ` +Set the configuration to be used by the blockchain. +Overrides any existing configuration. +` + +func (t *testEmulatorBackendType) newUseConfigFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.useConfigFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + + // configurations + configsValue, ok := invocation.Arguments[0].(*interpreter.CompositeValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + addresses, ok := configsValue.GetMember( + inter, + invocation.LocationRange, + addressesFieldName, + ).(*interpreter.DictionaryValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + mapping := make(map[string]common.Address, addresses.Count()) + + addresses.Iterate(nil, func(locationValue, addressValue interpreter.Value) bool { + location, ok := locationValue.(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + address, ok := addressValue.(interpreter.AddressValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + mapping[location.Str] = common.Address(address) + + return true + }) + + testFramework.UseConfiguration(&Configuration{ + Addresses: mapping, + }) + + return interpreter.Void + }, + ) +} + +func (t *testEmulatorBackendType) new( + inter *interpreter.Interpreter, + testFramework TestFramework, + locationRange interpreter.LocationRange, +) *interpreter.CompositeValue { + var fields = []interpreter.CompositeField{ + { + Name: testEmulatorBackendTypeExecuteScriptFunctionName, + Value: t.newExecuteScriptFunction(testFramework), + }, + { + Name: testEmulatorBackendTypeCreateAccountFunctionName, + Value: t.newCreateAccountFunction(testFramework), + }, { + Name: testEmulatorBackendTypeAddTransactionFunctionName, + Value: t.newAddTransactionFunction(testFramework), + }, + { + Name: testEmulatorBackendTypeExecuteNextTransactionFunctionName, + Value: t.newExecuteNextTransactionFunction(testFramework), + }, + { + Name: testEmulatorBackendTypeCommitBlockFunctionName, + Value: t.newCommitBlockFunction(testFramework), + }, + { + Name: testEmulatorBackendTypeDeployContractFunctionName, + Value: t.newDeployContractFunction(testFramework), + }, + { + Name: testEmulatorBackendTypeUseConfigFunctionName, + Value: t.newUseConfigFunction(testFramework), + }, + } + + // TODO: Use SimpleCompositeValue + return interpreter.NewCompositeValue( + inter, + locationRange, + t.compositeType.Location, + testEmulatorBackendTypeName, + common.CompositeKindStructure, + fields, + common.ZeroAddress, + ) +} From 666a1f925c583c56b5fe0a7d9fe3b7812b07d032 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 26 Apr 2023 11:55:30 -0700 Subject: [PATCH 113/246] Improve tests --- runtime/tests/checker/interface_test.go | 62 ++++++++++++++++++++- runtime/tests/interpreter/interface_test.go | 60 ++++++++++---------- 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 1cbcc3cc79..5ebdd7dc8c 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3286,6 +3286,37 @@ func TestCheckInterfaceDefaultMethodsInheritance(t *testing.T) { assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) + t.Run("default impl in ancestor", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A { + pub fun hello() { + var a = 1 + } + } + + struct interface B: A { + pub fun hello() + } + + struct interface C: B { + pub fun hello() { + var a = 2 + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, "C", memberConflictError.InterfaceType.QualifiedIdentifier()) + assert.Equal(t, "hello", memberConflictError.MemberName) + assert.Equal(t, "A", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) + }) + t.Run("default impl from two paths", func(t *testing.T) { t.Parallel() @@ -3540,8 +3571,17 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { `) errs := RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - assert.IsType(t, &sema.ConformanceError{}, errs[1]) + + conformanceError := &sema.ConformanceError{} + require.ErrorAs(t, errs[0], &conformanceError) + assert.Empty(t, conformanceError.MissingMembers) + assert.Len(t, conformanceError.MissingNestedCompositeTypes, 1) + assert.Equal(t, conformanceError.MissingNestedCompositeTypes[0].Identifier, "BNested") + + require.ErrorAs(t, errs[1], &conformanceError) + assert.Empty(t, conformanceError.MissingMembers) + assert.Len(t, conformanceError.MissingNestedCompositeTypes, 1) + assert.Equal(t, conformanceError.MissingNestedCompositeTypes[0].Identifier, "ANested") }) t.Run("nested struct conflicting", func(t *testing.T) { @@ -3693,6 +3733,24 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { require.ErrorAs(t, errs[0], &conformanceError) require.ErrorAs(t, errs[1], &conformanceError) }) + + t.Run("nested interface inheritance", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A { + resource interface X: B.Y {} + } + + contract interface B: A { + resource interface Y {} + } + `) + + require.NoError(t, err) + }) + } func TestCheckInterfaceEventsInheritance(t *testing.T) { diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index a5a808b30a..0e0ab17534 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -690,46 +690,46 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { // Inheritance hierarchy is as follows: // - // F (concrete type) + // A (concrete type) // | - // E (interface) - // / \ - // C D - // / \ - // A B + // B (interface) + // / \ + // C D + // / \ / + // E F inter, err := parseCheckAndInterpretWithOptions(t, ` - struct interface A { + struct A: B { pub fun test() { pre { print("A") } } } - struct interface B { + struct interface B: C, D { pub fun test() { pre { print("B") } } } - struct interface C: A, B { + struct interface C: E, F { pub fun test() { pre { print("C") } } } - struct interface D { + struct interface D: F { pub fun test() { pre { print("D") } } } - struct interface E: C, D { + struct interface E { pub fun test() { pre { print("E") } } } - struct F: E { + struct interface F { pub fun test() { pre { print("F") } } @@ -741,8 +741,8 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { } pub fun main() { - let f = F() - f.test() + let a = A() + a.test() }`, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ @@ -760,7 +760,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { // The pre-conditions of the interfaces are executed first, with depth-first pre-order traversal. // The pre-condition of the concrete type is executed at the end, after the interfaces. - assert.Equal(t, []string{"E", "C", "A", "B", "D", "F"}, logs) + assert.Equal(t, []string{"B", "C", "E", "F", "D", "A"}, logs) }) t.Run("post conditions order", func(t *testing.T) { @@ -786,46 +786,46 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { // Inheritance hierarchy is as follows: // - // F (concrete type) + // A (concrete type) // | - // E (interface) - // / \ - // C D - // / \ - // A B + // B (interface) + // / \ + // C D + // / \ / + // E F inter, err := parseCheckAndInterpretWithOptions(t, ` - struct interface A { + struct A: B { pub fun test() { post { print("A") } } } - struct interface B { + struct interface B: C, D { pub fun test() { post { print("B") } } } - struct interface C: A, B { + struct interface C: E, F { pub fun test() { post { print("C") } } } - struct interface D { + struct interface D: F { pub fun test() { post { print("D") } } } - struct interface E: C, D { + struct interface E { pub fun test() { post { print("E") } } } - struct F: E { + struct interface F { pub fun test() { post { print("F") } } @@ -837,8 +837,8 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { } pub fun main() { - let f = F() - f.test() + let a = A() + a.test() }`, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ @@ -856,6 +856,6 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { // The post-condition of the concrete type is executed first, before the interfaces. // The post-conditions of the interfaces are executed after that, with the reversed depth-first pre-order. - assert.Equal(t, []string{"F", "D", "B", "A", "C", "E"}, logs) + assert.Equal(t, []string{"A", "D", "F", "E", "C", "B"}, logs) }) } From ae9016e7aadf8a4757df7a2060c7dc27c4d05bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 15:44:50 -0700 Subject: [PATCH 114/246] refactor Test contract code into Go type, avoid global variables --- runtime/stdlib/test.go | 1179 +----------------------- runtime/stdlib/test_contract.go | 1177 +++++++++++++++++++++++ runtime/stdlib/test_emulatorbackend.go | 29 +- runtime/stdlib/test_test.go | 4 +- 4 files changed, 1224 insertions(+), 1165 deletions(-) create mode 100644 runtime/stdlib/test_contract.go diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index e473b97784..60052a6282 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -26,9 +26,7 @@ import ( "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib/contracts" ) // This is the Cadence standard library for writing tests. @@ -36,22 +34,15 @@ import ( // write tests in Cadence. const testContractTypeName = "Test" -const blockchainTypeName = "Blockchain" -const blockchainBackendTypeName = "BlockchainBackend" -const scriptResultTypeName = "ScriptResult" -const transactionResultTypeName = "TransactionResult" -const resultStatusTypeName = "ResultStatus" -const accountTypeName = "Account" -const errorTypeName = "Error" -const matcherTypeName = "Matcher" - -const succeededCaseName = "succeeded" -const failedCaseName = "failed" - -const transactionCodeFieldName = "code" -const transactionAuthorizerFieldName = "authorizers" -const transactionSignersFieldName = "signers" -const transactionArgsFieldName = "arguments" + +const testScriptResultTypeName = "ScriptResult" +const testTransactionResultTypeName = "TransactionResult" +const testResultStatusTypeName = "ResultStatus" +const testResultStatusTypeSucceededCaseName = "succeeded" +const testResultStatusTypeFailedCaseName = "failed" +const testAccountTypeName = "Account" +const testErrorTypeName = "Error" +const testMatcherTypeName = "Matcher" const accountAddressFieldName = "address" @@ -63,349 +54,16 @@ const TestContractLocation = common.IdentifierLocation(testContractTypeName) var testOnce sync.Once -// Deprecated: Use TestContractChecker instead -var testContractChecker *sema.Checker - -// Deprecated: Use TestContractType instead -var testContractType *sema.CompositeType - -// Deprecated: Use TestContractInitializerTypes -var testContractInitializerTypes []sema.Type - -var testExpectFunction *interpreter.HostFunctionValue - -var equalMatcherFunction *interpreter.HostFunctionValue - -var beEmptyMatcherFunction *interpreter.HostFunctionValue - -var haveElementCountMatcherFunction *interpreter.HostFunctionValue - -var containMatcherFunction *interpreter.HostFunctionValue - -var beGreaterThanMatcherFunction *interpreter.HostFunctionValue - -var beLessThanMatcherFunction *interpreter.HostFunctionValue - -var newMatcherFunction *interpreter.HostFunctionValue +// Deprecated: Use GetTestContract instead +var testContractType *TestContractType -var testNewEmulatorBlockchainFunctionType *sema.FunctionType - -// TODO: nest in future testType -// Deprecated -var testEmulatorBackend *testEmulatorBackendType - -func TestContractChecker() *sema.Checker { - testOnce.Do(initTest) - return testContractChecker -} - -func TestContractType() *sema.CompositeType { - testOnce.Do(initTest) +func GetTestContractType() *TestContractType { + testOnce.Do(func() { + testContractType = newTestContractType() + }) return testContractType } -func TestContractInitializerTypes() []sema.Type { - testOnce.Do(initTest) - return testContractInitializerTypes -} - -func initTest() { - program, err := parser.ParseProgram( - nil, - contracts.TestContract, - parser.Config{}, - ) - if err != nil { - panic(err) - } - - activation := sema.NewVariableActivation(sema.BaseValueActivation) - activation.DeclareValue(AssertFunction) - activation.DeclareValue(PanicFunction) - - testContractChecker, err = sema.NewChecker( - program, - TestContractLocation, - nil, - &sema.Config{ - BaseValueActivation: activation, - AccessCheckMode: sema.AccessCheckModeStrict, - }, - ) - if err != nil { - panic(err) - } - - err = testContractChecker.Check() - if err != nil { - panic(err) - } - - variable, ok := testContractChecker.Elaboration.GetGlobalType(testContractTypeName) - if !ok { - panic(errors.NewUnreachableError()) - } - testContractType = variable.Type.(*sema.CompositeType) - - testContractInitializerTypes = make([]sema.Type, len(testContractType.ConstructorParameters)) - for i, parameter := range testContractType.ConstructorParameters { - testContractInitializerTypes[i] = parameter.TypeAnnotation.Type - } - - blockchainBackendInterfaceType := initBlockchainBackendInterfaceType() - - matcherType := initMatcherType() - matcherTestFunctionType := compositeFunctionType(matcherType, matcherTestFunctionName) - - testEmulatorBackend = newTestEmulatorBackendType(blockchainBackendInterfaceType) - - // Test.expect() - testExpectFunctionType := initTestExpectFunctionType(matcherType) - initTestExpectFunction(testExpectFunctionType) - testContractType.Members.Set( - testExpectFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testExpectFunctionName, - testExpectFunctionType, - testExpectFunctionDocString, - ), - ) - - // Test.newMatcher() - newMatcherFunctionType := initNewMatcherFunctionType(matcherType) - initNewMatcherFunction(newMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - newMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - newMatcherFunctionName, - newMatcherFunctionType, - newMatcherFunctionDocString, - ), - ) - - // Test.equal() - equalMatcherFunctionType := initEqualMatcherFunctionType(matcherType) - initEqualMatcherFunction(equalMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - equalMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - equalMatcherFunctionName, - equalMatcherFunctionType, - equalMatcherFunctionDocString, - ), - ) - - // Test.beEmpty() - beEmptyMatcherFunctionType := initBeEmptyMatcherFunctionType(matcherType) - initBeEmptyMatcherFunction(beEmptyMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - beEmptyMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - beEmptyMatcherFunctionName, - beEmptyMatcherFunctionType, - beEmptyMatcherFunctionDocString, - ), - ) - - // Test.haveElementCount() - haveElementCountMatcherFunctionType := initHaveElementCountMatcherFunctionType(matcherType) - initHaveElementCountMatcherFunction(haveElementCountMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - haveElementCountMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - haveElementCountMatcherFunctionName, - haveElementCountMatcherFunctionType, - haveElementCountMatcherFunctionDocString, - ), - ) - - // Test.contain() - containMatcherFunctionType := initContainMatcherFunctionType(matcherType) - initContainMatcherFunction(containMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - containMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - containMatcherFunctionName, - containMatcherFunctionType, - containMatcherFunctionDocString, - ), - ) - - // Test.beGreaterThan() - beGreaterThanMatcherFunctionType := initBeGreaterThanMatcherFunctionType(matcherType) - initBeGreaterThanMatcherFunction(beGreaterThanMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - beGreaterThanMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - beGreaterThanMatcherFunctionName, - beGreaterThanMatcherFunctionType, - beGreaterThanMatcherFunctionDocString, - ), - ) - - // Test.beLessThan() - beLessThanMatcherFunctionType := initBeLessThanMatcherFunctionType(matcherType) - initBeLessThanMatcherFunction(beLessThanMatcherFunctionType, matcherTestFunctionType) - testContractType.Members.Set( - beLessThanMatcherFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - beLessThanMatcherFunctionName, - beLessThanMatcherFunctionType, - beLessThanMatcherFunctionDocString, - ), - ) - - blockchainType, ok := testContractType.NestedTypes.Get(blockchainTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, blockchainTypeName)) - } - - testNewEmulatorBlockchainFunctionType = &sema.FunctionType{ - ReturnTypeAnnotation: sema.NewTypeAnnotation( - blockchainType, - ), - } - - initTestContractTypeFunctions() - - // Enrich 'Test' contract elaboration with natively implemented composite types. - // e.g: 'EmulatorBackend' type. - testContractChecker.Elaboration.SetCompositeType( - testEmulatorBackend.compositeType.ID(), - testEmulatorBackend.compositeType, - ) -} - -func initBlockchainBackendInterfaceType() *sema.InterfaceType { - typ, ok := testContractType.NestedTypes.Get(blockchainBackendTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, blockchainBackendTypeName)) - } - - blockchainBackendInterfaceType, ok := typ.(*sema.InterfaceType) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected interface", - blockchainBackendTypeName, - )) - } - return blockchainBackendInterfaceType -} - -func initMatcherType() *sema.CompositeType { - typ, ok := testContractType.NestedTypes.Get(matcherTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, matcherTypeName)) - } - - matcherType, ok := typ.(*sema.CompositeType) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected struct type", - matcherTypeName, - )) - } - return matcherType -} - -func initTestContractTypeFunctions() { - // Enrich 'Test' contract with natively implemented functions - - // Test.assert() - testContractType.Members.Set( - testAssertFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testAssertFunctionName, - testAssertFunctionType, - testAssertFunctionDocString, - ), - ) - - // Test.fail() - testContractType.Members.Set( - testFailFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testFailFunctionName, - testFailFunctionType, - testFailFunctionDocString, - ), - ) - - // Test.newEmulatorBlockchain() - testContractType.Members.Set( - testNewEmulatorBlockchainFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testNewEmulatorBlockchainFunctionName, - testNewEmulatorBlockchainFunctionType, - testNewEmulatorBlockchainFunctionDocString, - ), - ) - - // Test.readFile() - testContractType.Members.Set( - testReadFileFunctionName, - sema.NewUnmeteredPublicFunctionMember( - testContractType, - testReadFileFunctionName, - testReadFileFunctionType, - testReadFileFunctionDocString, - ), - ) -} - -func NewTestContract( - inter *interpreter.Interpreter, - testFramework TestFramework, - constructor interpreter.FunctionValue, - invocationRange ast.Range, -) ( - *interpreter.CompositeValue, - error, -) { - initializerTypes := TestContractInitializerTypes() - value, err := inter.InvokeFunctionValue( - constructor, - nil, - initializerTypes, - initializerTypes, - invocationRange, - ) - if err != nil { - return nil, err - } - - compositeValue := value.(*interpreter.CompositeValue) - - // Inject natively implemented function values - compositeValue.Functions[testAssertFunctionName] = testAssertFunction - compositeValue.Functions[testFailFunctionName] = testFailFunction - compositeValue.Functions[testExpectFunctionName] = testExpectFunction - compositeValue.Functions[testNewEmulatorBlockchainFunctionName] = testNewEmulatorBlockchainFunction(testFramework) - compositeValue.Functions[testReadFileFunctionName] = testReadFileFunction(testFramework) - - // Inject natively implemented matchers - compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction - compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction - compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction - compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction - compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction - compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction - compositeValue.Functions[beLessThanMatcherFunctionName] = beLessThanMatcherFunction - - return compositeValue, nil -} - func typeNotFoundError(parentType, nestedType string) error { return errors.NewUnexpectedError("cannot find type '%s.%s'", parentType, nestedType) } @@ -444,310 +102,6 @@ func getFunctionTypeFromMember(funcMember *sema.Member, funcName string) *sema.F return functionType } -// Functions belonging to the 'Test' contract - -// 'Test.assert' function - -const testAssertFunctionDocString = ` -Fails the test-case if the given condition is false, and reports a message which explains how the condition is false. -` - -const testAssertFunctionName = "assert" - -var testAssertFunctionType = &sema.FunctionType{ - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "condition", - TypeAnnotation: sema.NewTypeAnnotation( - sema.BoolType, - ), - }, - { - Identifier: "message", - TypeAnnotation: sema.NewTypeAnnotation( - sema.StringType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation( - sema.VoidType, - ), - RequiredArgumentCount: sema.RequiredArgumentCount(1), -} - -var testAssertFunction = interpreter.NewUnmeteredHostFunctionValue( - testAssertFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - condition, ok := invocation.Arguments[0].(interpreter.BoolValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - var message string - if len(invocation.Arguments) > 1 { - messageValue, ok := invocation.Arguments[1].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - message = messageValue.Str - } - - if !condition { - panic(AssertionError{ - Message: message, - LocationRange: invocation.LocationRange, - }) - } - - return interpreter.Void - }, -) - -// 'Test.fail' function - -const testFailFunctionDocString = ` -Fails the test-case with a message. -` - -const testFailFunctionName = "fail" - -var testFailFunctionType = &sema.FunctionType{ - Parameters: []sema.Parameter{ - { - Identifier: "message", - TypeAnnotation: sema.NewTypeAnnotation( - sema.StringType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation( - sema.VoidType, - ), - RequiredArgumentCount: sema.RequiredArgumentCount(0), -} - -var testFailFunction = interpreter.NewUnmeteredHostFunctionValue( - testFailFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var message string - if len(invocation.Arguments) > 0 { - messageValue, ok := invocation.Arguments[0].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - message = messageValue.Str - } - - panic(AssertionError{ - Message: message, - LocationRange: invocation.LocationRange, - }) - }, -) - -// 'Test.expect' function - -const testExpectFunctionDocString = ` -Expect function tests a value against a matcher, and fails the test if it's not a match. -` - -const testExpectFunctionName = "expect" - -func initTestExpectFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, - } - - return &sema.FunctionType{ - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: sema.NewTypeAnnotation( - &sema.GenericType{ - TypeParameter: typeParameter, - }, - ), - }, - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "matcher", - TypeAnnotation: sema.NewTypeAnnotation(matcherType), - }, - }, - TypeParameters: []*sema.TypeParameter{ - typeParameter, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation( - sema.VoidType, - ), - } -} - -func initTestExpectFunction(testExpectFunctionType *sema.FunctionType) { - testExpectFunction = interpreter.NewUnmeteredHostFunctionValue( - testExpectFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - value := invocation.Arguments[0] - - matcher, ok := invocation.Arguments[1].(*interpreter.CompositeValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - result := invokeMatcherTest( - inter, - matcher, - value, - locationRange, - ) - - if !result { - panic(AssertionError{}) - } - - return interpreter.Void - }, - ) -} - -func invokeMatcherTest( - inter *interpreter.Interpreter, - matcher interpreter.MemberAccessibleValue, - value interpreter.Value, - locationRange interpreter.LocationRange, -) bool { - testFunc := matcher.GetMember( - inter, - locationRange, - matcherTestFunctionName, - ) - - funcValue, ok := testFunc.(interpreter.FunctionValue) - if !ok { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected function", - matcherTestFunctionName, - )) - } - - functionType := funcValue.FunctionType() - - testResult, err := inter.InvokeExternally( - funcValue, - functionType, - []interpreter.Value{ - value, - }, - ) - - if err != nil { - panic(err) - } - - result, ok := testResult.(interpreter.BoolValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - return bool(result) -} - -// 'Test.readFile' function - -const testReadFileFunctionDocString = ` -Read a local file, and return the content as a string. -` - -const testReadFileFunctionName = "readFile" - -var testReadFileFunctionType = &sema.FunctionType{ - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "path", - TypeAnnotation: sema.NewTypeAnnotation( - sema.StringType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation( - sema.StringType, - ), -} - -func testReadFileFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - testReadFileFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - pathString, ok := invocation.Arguments[0].(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - content, err := testFramework.ReadFile(pathString.Str) - if err != nil { - panic(err) - } - - return interpreter.NewUnmeteredStringValue(content) - }, - ) -} - -// 'Test.newEmulatorBlockchain' function - -const testNewEmulatorBlockchainFunctionDocString = ` -Creates a blockchain which is backed by a new emulator instance. -` - -const testNewEmulatorBlockchainFunctionName = "newEmulatorBlockchain" - -func testNewEmulatorBlockchainFunction(testFramework TestFramework) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - testNewEmulatorBlockchainFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - // Create an `EmulatorBackend` - emulatorBackend := testEmulatorBackend.new( - inter, - testFramework, - locationRange, - ) - - // Create a 'Blockchain' struct value, that wraps the emulator backend, - // by calling the constructor of 'Blockchain'. - - blockchainConstructor := getNestedTypeConstructorValue( - *invocation.Self, - blockchainTypeName, - ) - - blockchain, err := inter.InvokeExternally( - blockchainConstructor, - blockchainConstructor.Type, - []interpreter.Value{ - emulatorBackend, - }, - ) - - if err != nil { - panic(err) - } - - return blockchain - }, - ) -} - func getNestedTypeConstructorValue(parent interpreter.Value, typeName string) *interpreter.HostFunctionValue { compositeValue, ok := parent.(*interpreter.CompositeValue) if !ok { @@ -762,86 +116,6 @@ func getNestedTypeConstructorValue(parent interpreter.Value, typeName string) *i return constructor } -// 'Test.NewMatcher' function. -// Constructs a matcher that test only 'AnyStruct'. -// Accepts test function that accepts subtype of 'AnyStruct'. -// -// Signature: -// fun newMatcher(test: ((T): Bool)): Test.Matcher -// -// where `T` is optional, and bound to `AnyStruct`. -// -// Sample usage: `Test.newMatcher(fun (_ value: Int: Bool) { return true })` - -const newMatcherFunctionDocString = ` -Creates a matcher with a test function. -The test function is of type '((T): Bool)', where 'T' is bound to 'AnyStruct'. -` - -const newMatcherFunctionName = "newMatcher" - -func initNewMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, - } - - return &sema.FunctionType{ - IsConstructor: true, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "test", - TypeAnnotation: sema.NewTypeAnnotation( - // Type of the 'test' function: ((T): Bool) - &sema.FunctionType{ - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: sema.NewTypeAnnotation( - &sema.GenericType{ - TypeParameter: typeParameter, - }, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation( - sema.BoolType, - ), - }, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - TypeParameters: []*sema.TypeParameter{ - typeParameter, - }, - } -} - -func initNewMatcherFunction( - newMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - newMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - newMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - test, ok := invocation.Arguments[0].(interpreter.FunctionValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - return newMatcherWithGenericTestFunction( - invocation, - test, - matcherTestFunctionType, - ) - }, - ) -} - func arrayValueToSlice(value interpreter.Value) ([]interpreter.Value, error) { array, ok := value.(*interpreter.ArrayValue) if !ok { @@ -870,20 +144,20 @@ func newScriptResult( } // Lookup and get 'ResultStatus' enum value. - resultStatusConstructor := getConstructor(inter, resultStatusTypeName) + resultStatusConstructor := getConstructor(inter, testResultStatusTypeName) var status interpreter.Value if result.Error == nil { - succeededVar := resultStatusConstructor.NestedVariables[succeededCaseName] + succeededVar := resultStatusConstructor.NestedVariables[testResultStatusTypeSucceededCaseName] status = succeededVar.GetValue() } else { - failedVar := resultStatusConstructor.NestedVariables[failedCaseName] + failedVar := resultStatusConstructor.NestedVariables[testResultStatusTypeFailedCaseName] status = failedVar.GetValue() } errValue := newErrorValue(inter, result.Error) // Create a 'ScriptResult' by calling its constructor. - scriptResultConstructor := getConstructor(inter, scriptResultTypeName) + scriptResultConstructor := getConstructor(inter, testScriptResultTypeName) scriptResult, err := inter.InvokeExternally( scriptResultConstructor, scriptResultConstructor.Type, @@ -911,7 +185,7 @@ func getConstructor(inter *interpreter.Interpreter, typeName string) *interprete return resultStatusConstructor } -func addressesFromValue(accountsValue interpreter.Value) []common.Address { +func addressArrayValueToSlice(accountsValue interpreter.Value) []common.Address { accountsArray, ok := accountsValue.(*interpreter.ArrayValue) if !ok { panic(errors.NewUnreachableError()) @@ -933,7 +207,7 @@ func addressesFromValue(accountsValue interpreter.Value) []common.Address { return addresses } -func accountsFromValue( +func accountsArrayValueToSlice( inter *interpreter.Interpreter, accountsValue interpreter.Value, locationRange interpreter.LocationRange, @@ -1004,18 +278,18 @@ func accountFromValue( // newTransactionResult Creates a "TransactionResult" indicating the status of the transaction execution. func newTransactionResult(inter *interpreter.Interpreter, result *TransactionResult) interpreter.Value { // Lookup and get 'ResultStatus' enum value. - resultStatusConstructor := getConstructor(inter, resultStatusTypeName) + resultStatusConstructor := getConstructor(inter, testResultStatusTypeName) var status interpreter.Value if result.Error == nil { - succeededVar := resultStatusConstructor.NestedVariables[succeededCaseName] + succeededVar := resultStatusConstructor.NestedVariables[testResultStatusTypeSucceededCaseName] status = succeededVar.GetValue() } else { - failedVar := resultStatusConstructor.NestedVariables[failedCaseName] + failedVar := resultStatusConstructor.NestedVariables[testResultStatusTypeFailedCaseName] status = failedVar.GetValue() } // Create a 'TransactionResult' by calling its constructor. - transactionResultConstructor := getConstructor(inter, transactionResultTypeName) + transactionResultConstructor := getConstructor(inter, testTransactionResultTypeName) errValue := newErrorValue(inter, result.Error) @@ -1041,7 +315,7 @@ func newErrorValue(inter *interpreter.Interpreter, err error) interpreter.Value } // Create a 'Error' by calling its constructor. - errorConstructor := getConstructor(inter, errorTypeName) + errorConstructor := getConstructor(inter, testErrorTypeName) errorValue, invocationErr := inter.InvokeExternally( errorConstructor, @@ -1058,403 +332,6 @@ func newErrorValue(inter *interpreter.Interpreter, err error) interpreter.Value return errorValue } -// Built-in matchers - -const equalMatcherFunctionName = "equal" - -const equalMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is equal to the given value. -` - -func initEqualMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, - } - - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{ - typeParameter, - }, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: sema.NewTypeAnnotation( - &sema.GenericType{ - TypeParameter: typeParameter, - }, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initEqualMatcherFunction( - equalMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - equalMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - equalMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - inter := invocation.Interpreter - - equalTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - - thisValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - equal := thisValue.Equal( - inter, - invocation.LocationRange, - otherValue, - ) - - return interpreter.AsBoolValue(equal) - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - equalTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - -const beEmptyMatcherFunctionName = "beEmpty" - -const beEmptyMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is an array or dictionary, -and the tested value contains no elements. -` - -func initBeEmptyMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{}, - Parameters: []sema.Parameter{}, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initBeEmptyMatcherFunction( - beEmptyMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beEmptyMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - beEmptyTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var isEmpty bool - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - isEmpty = value.Count() == 0 - case *interpreter.DictionaryValue: - isEmpty = value.Count() == 0 - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } - - return interpreter.AsBoolValue(isEmpty) - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - beEmptyTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - -const haveElementCountMatcherFunctionName = "haveElementCount" - -const haveElementCountMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is an array or dictionary, -and has the given number of elements. -` - -func initHaveElementCountMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{}, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "count", - TypeAnnotation: sema.NewTypeAnnotation( - sema.IntType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initHaveElementCountMatcherFunction( - haveElementCountMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - haveElementCountMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - count, ok := invocation.Arguments[0].(interpreter.IntValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - haveElementCountTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var matchingCount bool - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - matchingCount = value.Count() == count.ToInt(invocation.LocationRange) - case *interpreter.DictionaryValue: - matchingCount = value.Count() == count.ToInt(invocation.LocationRange) - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } - - return interpreter.AsBoolValue(matchingCount) - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - haveElementCountTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - -const containMatcherFunctionName = "contain" - -const containMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is an array that contains -a value that is equal to the given value, or the tested value is a dictionary -that contains an entry where the key is equal to the given value. -` - -func initContainMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{}, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "element", - TypeAnnotation: sema.NewTypeAnnotation( - sema.AnyStructType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initContainMatcherFunction( - containMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - containMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - element, ok := invocation.Arguments[0].(interpreter.EquatableValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - inter := invocation.Interpreter - - containTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - var elementFound interpreter.BoolValue - switch value := invocation.Arguments[0].(type) { - case *interpreter.ArrayValue: - elementFound = value.Contains( - inter, - invocation.LocationRange, - element, - ) - case *interpreter.DictionaryValue: - elementFound = value.ContainsKey( - inter, - invocation.LocationRange, - element, - ) - default: - panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) - } - - return elementFound - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - containTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - -const beGreaterThanMatcherFunctionName = "beGreaterThan" - -const beGreaterThanMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is a number and -greater than the given number. -` - -func initBeGreaterThanMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{}, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: sema.NewTypeAnnotation( - sema.NumberType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initBeGreaterThanMatcherFunction( - beGreaterThanMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beGreaterThanMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - inter := invocation.Interpreter - - beGreaterThanTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - isGreaterThan := thisValue.Greater( - inter, - otherValue, - invocation.LocationRange, - ) - - return isGreaterThan - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - beGreaterThanTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - -const beLessThanMatcherFunctionName = "beLessThan" - -const beLessThanMatcherFunctionDocString = ` -Returns a matcher that succeeds if the tested value is a number and -less than the given number. -` - -func initBeLessThanMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - IsConstructor: false, - TypeParameters: []*sema.TypeParameter{}, - Parameters: []sema.Parameter{ - { - Label: sema.ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: sema.NewTypeAnnotation( - sema.NumberType, - ), - }, - }, - ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), - } -} - -func initBeLessThanMatcherFunction( - beLessThanMatcherFunctionType *sema.FunctionType, - matcherTestFunctionType *sema.FunctionType, -) { - beLessThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( - beLessThanMatcherFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - inter := invocation.Interpreter - - beLessThanTestFunc := interpreter.NewHostFunctionValue( - nil, - matcherTestFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - isLessThan := thisValue.Less( - inter, - otherValue, - invocation.LocationRange, - ) - - return isLessThan - }, - ) - - return newMatcherWithGenericTestFunction( - invocation, - beLessThanTestFunc, - matcherTestFunctionType, - ) - }, - ) -} - // TestFailedError type TestFailedError struct { @@ -1531,7 +408,7 @@ func newMatcherWithGenericTestFunction( matcherConstructor := getNestedTypeConstructorValue( *invocation.Self, - matcherTypeName, + testMatcherTypeName, ) matcher, err := inter.InvokeExternally( matcherConstructor, @@ -1592,7 +469,7 @@ func NewTestInterpreterContractValueHandler( return contract case TestContractLocation: - contract, err := NewTestContract( + contract, err := GetTestContractType().NewTestContract( inter, testFramework, constructorGenerator(common.ZeroAddress), diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go new file mode 100644 index 0000000000..56e697d11f --- /dev/null +++ b/runtime/stdlib/test_contract.go @@ -0,0 +1,1177 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stdlib + +import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/parser" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib/contracts" +) + +type TestContractType struct { + Checker *sema.Checker + CompositeType *sema.CompositeType + InitializerTypes []sema.Type + emulatorBackendType *testEmulatorBackendType + newEmulatorBlockchainFunctionType *sema.FunctionType + expectFunction interpreter.FunctionValue + newMatcherFunction interpreter.FunctionValue + haveElementCountFunction interpreter.FunctionValue + beEmptyFunction interpreter.FunctionValue + equalFunction interpreter.FunctionValue + beGreaterThanFunction interpreter.FunctionValue + containFunction interpreter.FunctionValue + beLessThanFunction interpreter.FunctionValue +} + +// 'Test.assert' function + +const testTypeAssertFunctionDocString = ` +Fails the test-case if the given condition is false, and reports a message which explains how the condition is false. +` + +const testTypeAssertFunctionName = "assert" + +var testTypeAssertFunctionType = &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "condition", + TypeAnnotation: sema.NewTypeAnnotation( + sema.BoolType, + ), + }, + { + Identifier: "message", + TypeAnnotation: sema.NewTypeAnnotation( + sema.StringType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.VoidType, + ), + RequiredArgumentCount: sema.RequiredArgumentCount(1), +} + +var testTypeAssertFunction = interpreter.NewUnmeteredHostFunctionValue( + testTypeAssertFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + condition, ok := invocation.Arguments[0].(interpreter.BoolValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + var message string + if len(invocation.Arguments) > 1 { + messageValue, ok := invocation.Arguments[1].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + message = messageValue.Str + } + + if !condition { + panic(AssertionError{ + Message: message, + LocationRange: invocation.LocationRange, + }) + } + + return interpreter.Void + }, +) + +// 'Test.fail' function + +const testTypeFailFunctionDocString = ` +Fails the test-case with a message. +` + +const testTypeFailFunctionName = "fail" + +var testTypeFailFunctionType = &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Identifier: "message", + TypeAnnotation: sema.NewTypeAnnotation( + sema.StringType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.VoidType, + ), + RequiredArgumentCount: sema.RequiredArgumentCount(0), +} + +var testTypeFailFunction = interpreter.NewUnmeteredHostFunctionValue( + testTypeFailFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var message string + if len(invocation.Arguments) > 0 { + messageValue, ok := invocation.Arguments[0].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + message = messageValue.Str + } + + panic(AssertionError{ + Message: message, + LocationRange: invocation.LocationRange, + }) + }, +) + +// 'Test.expect' function + +const testTypeExpectFunctionDocString = ` +Expect function tests a value against a matcher, and fails the test if it's not a match. +` + +const testTypeExpectFunctionName = "expect" + +func newTestTypeExpectFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + typeParameter := &sema.TypeParameter{ + TypeBound: sema.AnyStructType, + Name: "T", + Optional: true, + } + + return &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "matcher", + TypeAnnotation: sema.NewTypeAnnotation(matcherType), + }, + }, + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.VoidType, + ), + } +} + +func newTestTypeExpectFunction(functionType *sema.FunctionType) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + functionType, + func(invocation interpreter.Invocation) interpreter.Value { + value := invocation.Arguments[0] + + matcher, ok := invocation.Arguments[1].(*interpreter.CompositeValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + result := invokeMatcherTest( + inter, + matcher, + value, + locationRange, + ) + + if !result { + panic(AssertionError{}) + } + + return interpreter.Void + }, + ) +} + +func invokeMatcherTest( + inter *interpreter.Interpreter, + matcher interpreter.MemberAccessibleValue, + value interpreter.Value, + locationRange interpreter.LocationRange, +) bool { + testFunc := matcher.GetMember( + inter, + locationRange, + matcherTestFunctionName, + ) + + funcValue, ok := testFunc.(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected function", + matcherTestFunctionName, + )) + } + + functionType := funcValue.FunctionType() + + testResult, err := inter.InvokeExternally( + funcValue, + functionType, + []interpreter.Value{ + value, + }, + ) + + if err != nil { + panic(err) + } + + result, ok := testResult.(interpreter.BoolValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return bool(result) +} + +// 'Test.readFile' function + +const testTypeReadFileFunctionDocString = ` +Read a local file, and return the content as a string. +` + +const testTypeReadFileFunctionName = "readFile" + +var testTypeReadFileFunctionType = &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: sema.NewTypeAnnotation( + sema.StringType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.StringType, + ), +} + +func newTestTypeReadFileFunction(testFramework TestFramework) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + testTypeReadFileFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + pathString, ok := invocation.Arguments[0].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + content, err := testFramework.ReadFile(pathString.Str) + if err != nil { + panic(err) + } + + return interpreter.NewUnmeteredStringValue(content) + }, + ) +} + +// 'Test.newEmulatorBlockchain' function + +const testTypeNewEmulatorBlockchainFunctionDocString = ` +Creates a blockchain which is backed by a new emulator instance. +` + +const testTypeNewEmulatorBlockchainFunctionName = "newEmulatorBlockchain" + +const testBlockchainTypeName = "Blockchain" + +func newTestTypeNewEmulatorBlockchainFunctionType(blockchainType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + blockchainType, + ), + } +} + +func (t *TestContractType) newNewEmulatorBlockchainFunction( + testFramework TestFramework, +) *interpreter.HostFunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + t.newEmulatorBlockchainFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Create an `EmulatorBackend` + emulatorBackend := t.emulatorBackendType.newEmulatorBackend( + inter, + testFramework, + locationRange, + ) + + // Create a 'Blockchain' struct value, that wraps the emulator backend, + // by calling the constructor of 'Blockchain'. + + blockchainConstructor := getNestedTypeConstructorValue( + *invocation.Self, + testBlockchainTypeName, + ) + + blockchain, err := inter.InvokeExternally( + blockchainConstructor, + blockchainConstructor.Type, + []interpreter.Value{ + emulatorBackend, + }, + ) + + if err != nil { + panic(err) + } + + return blockchain + }, + ) +} + +// 'Test.NewMatcher' function. +// Constructs a matcher that test only 'AnyStruct'. +// Accepts test function that accepts subtype of 'AnyStruct'. +// +// Signature: +// fun newMatcher(test: ((T): Bool)): Test.Matcher +// +// where `T` is optional, and bound to `AnyStruct`. +// +// Sample usage: `Test.newMatcher(fun (_ value: Int: Bool) { return true })` + +const testTypeNewMatcherFunctionDocString = ` +Creates a matcher with a test function. +The test function is of type '((T): Bool)', where 'T' is bound to 'AnyStruct'. +` + +const testTypeNewMatcherFunctionName = "newMatcher" + +func newTestTypeNewMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + typeParameter := &sema.TypeParameter{ + TypeBound: sema.AnyStructType, + Name: "T", + Optional: true, + } + + return &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "test", + TypeAnnotation: sema.NewTypeAnnotation( + // Type of the 'test' function: ((T): Bool) + &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.BoolType, + ), + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + } +} + +func newTestTypeNewMatcherFunction( + newMatcherFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + newMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + test, ok := invocation.Arguments[0].(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return newMatcherWithGenericTestFunction( + invocation, + test, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.equal` + +const testTypeEqualFunctionName = "equal" + +const testTypeEqualFunctionDocString = ` +Returns a matcher that succeeds if the tested value is equal to the given value. +` + +func newTestTypeEqualFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + typeParameter := &sema.TypeParameter{ + TypeBound: sema.AnyStructType, + Name: "T", + Optional: true, + } + + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeEqualFunction( + equalFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + equalFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + equalTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + thisValue, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + equal := thisValue.Equal( + inter, + invocation.LocationRange, + otherValue, + ) + + return interpreter.AsBoolValue(equal) + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + equalTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.beEmpty` + +const testTypeBeEmptyFunctionName = "beEmpty" + +const testTypeBeEmptyFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array or dictionary, +and the tested value contains no elements. +` + +func newTestTypeBeEmptyFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeBeEmptyFunction( + beEmptyFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + beEmptyFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + beEmptyTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var isEmpty bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + isEmpty = value.Count() == 0 + case *interpreter.DictionaryValue: + isEmpty = value.Count() == 0 + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(isEmpty) + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + beEmptyTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.haveElementCount` + +const testTypeHaveElementCountFunctionName = "haveElementCount" + +const testTypeHaveElementCountFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array or dictionary, +and has the given number of elements. +` + +func newTestTypeHaveElementCountFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "count", + TypeAnnotation: sema.NewTypeAnnotation( + sema.IntType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeHaveElementCountFunction( + haveElementCountFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + haveElementCountFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + count, ok := invocation.Arguments[0].(interpreter.IntValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + haveElementCountTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var matchingCount bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + case *interpreter.DictionaryValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(matchingCount) + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + haveElementCountTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.contain` + +const testTypeContainFunctionName = "contain" + +const testTypeContainFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array that contains +a value that is equal to the given value, or the tested value is a dictionary +that contains an entry where the key is equal to the given value. +` + +func newTestTypeContainFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "element", + TypeAnnotation: sema.NewTypeAnnotation( + sema.AnyStructType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeContainFunction( + containFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + containFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + element, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + containTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var elementFound interpreter.BoolValue + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + elementFound = value.Contains( + inter, + invocation.LocationRange, + element, + ) + case *interpreter.DictionaryValue: + elementFound = value.ContainsKey( + inter, + invocation.LocationRange, + element, + ) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return elementFound + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + containTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.beGreaterThan` + +const testTypeBeGreaterThanFunctionName = "beGreaterThan" + +const testTypeBeGreaterThanFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +greater than the given number. +` + +func newTestTypeBeGreaterThanFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + sema.NumberType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeBeGreaterThanFunction( + beGreaterThanFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + beGreaterThanFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + beGreaterThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + isGreaterThan := thisValue.Greater( + inter, + otherValue, + invocation.LocationRange, + ) + + return isGreaterThan + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + beGreaterThanTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +// `Test.beLessThan` + +const testTypeBeLessThanFunctionName = "beLessThan" + +const testTypeBeLessThanFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +less than the given number. +` + +func newTestTypeBeLessThanFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + sema.NumberType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +} + +func newTestTypeBeLessThanFunction( + beLessThanFunctionType *sema.FunctionType, + matcherTestFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + beLessThanFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + beLessThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + isLessThan := thisValue.Less( + inter, + otherValue, + invocation.LocationRange, + ) + + return isLessThan + }, + ) + + return newMatcherWithGenericTestFunction( + invocation, + beLessThanTestFunc, + matcherTestFunctionType, + ) + }, + ) +} + +func newTestContractType() *TestContractType { + + program, err := parser.ParseProgram( + nil, + contracts.TestContract, + parser.Config{}, + ) + if err != nil { + panic(err) + } + + activation := sema.NewVariableActivation(sema.BaseValueActivation) + activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) + + checker, err := sema.NewChecker( + program, + TestContractLocation, + nil, + &sema.Config{ + BaseValueActivation: activation, + AccessCheckMode: sema.AccessCheckModeStrict, + }, + ) + if err != nil { + panic(err) + } + + err = checker.Check() + if err != nil { + panic(err) + } + + variable, ok := checker.Elaboration.GetGlobalType(testContractTypeName) + if !ok { + panic(errors.NewUnreachableError()) + } + compositeType := variable.Type.(*sema.CompositeType) + + initializerTypes := make([]sema.Type, len(compositeType.ConstructorParameters)) + for i, parameter := range compositeType.ConstructorParameters { + initializerTypes[i] = parameter.TypeAnnotation.Type + } + + ty := &TestContractType{ + Checker: checker, + CompositeType: compositeType, + InitializerTypes: initializerTypes, + } + + blockchainBackendInterfaceType := ty.blockchainBackendInterfaceType() + + emulatorBackendType := newTestEmulatorBackendType(blockchainBackendInterfaceType) + ty.emulatorBackendType = emulatorBackendType + + // Enrich 'Test' contract elaboration with natively implemented composite types. + // e.g: 'EmulatorBackend' type. + checker.Elaboration.SetCompositeType( + emulatorBackendType.compositeType.ID(), + emulatorBackendType.compositeType, + ) + + matcherType := ty.matcherType() + matcherTestFunctionType := compositeFunctionType(matcherType, matcherTestFunctionName) + + blockchainType := ty.blockchainType() + + // Test.assert() + compositeType.Members.Set( + testTypeAssertFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeAssertFunctionName, + testTypeAssertFunctionType, + testTypeAssertFunctionDocString, + ), + ) + + // Test.fail() + compositeType.Members.Set( + testTypeFailFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeFailFunctionName, + testTypeFailFunctionType, + testTypeFailFunctionDocString, + ), + ) + + // Test.newEmulatorBlockchain() + newEmulatorBlockchainFunctionType := newTestTypeNewEmulatorBlockchainFunctionType(blockchainType) + compositeType.Members.Set( + testTypeNewEmulatorBlockchainFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeNewEmulatorBlockchainFunctionName, + newEmulatorBlockchainFunctionType, + testTypeNewEmulatorBlockchainFunctionDocString, + ), + ) + ty.newEmulatorBlockchainFunctionType = newEmulatorBlockchainFunctionType + + // Test.readFile() + compositeType.Members.Set( + testTypeReadFileFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeReadFileFunctionName, + testTypeReadFileFunctionType, + testTypeReadFileFunctionDocString, + ), + ) + + // Test.expect() + testExpectFunctionType := newTestTypeExpectFunctionType(matcherType) + compositeType.Members.Set( + testTypeExpectFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeExpectFunctionName, + testExpectFunctionType, + testTypeExpectFunctionDocString, + ), + ) + ty.expectFunction = newTestTypeExpectFunction(testExpectFunctionType) + + // Test.newMatcher() + newMatcherFunctionType := newTestTypeNewMatcherFunctionType(matcherType) + compositeType.Members.Set( + testTypeNewMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeNewMatcherFunctionName, + newMatcherFunctionType, + testTypeNewMatcherFunctionDocString, + ), + ) + ty.newMatcherFunction = newTestTypeNewMatcherFunction( + newMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.equal() + equalMatcherFunctionType := newTestTypeEqualFunctionType(matcherType) + compositeType.Members.Set( + testTypeEqualFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeEqualFunctionName, + equalMatcherFunctionType, + testTypeEqualFunctionDocString, + ), + ) + ty.equalFunction = newTestTypeEqualFunction( + equalMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.beEmpty() + beEmptyMatcherFunctionType := newTestTypeBeEmptyFunctionType(matcherType) + compositeType.Members.Set( + testTypeBeEmptyFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeBeEmptyFunctionName, + beEmptyMatcherFunctionType, + testTypeBeEmptyFunctionDocString, + ), + ) + ty.beEmptyFunction = newTestTypeBeEmptyFunction( + beEmptyMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.haveElementCount() + haveElementCountMatcherFunctionType := newTestTypeHaveElementCountFunctionType(matcherType) + compositeType.Members.Set( + testTypeHaveElementCountFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeHaveElementCountFunctionName, + haveElementCountMatcherFunctionType, + testTypeHaveElementCountFunctionDocString, + ), + ) + ty.haveElementCountFunction = newTestTypeHaveElementCountFunction( + haveElementCountMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.contain() + containMatcherFunctionType := newTestTypeContainFunctionType(matcherType) + compositeType.Members.Set( + testTypeContainFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeContainFunctionName, + containMatcherFunctionType, + testTypeContainFunctionDocString, + ), + ) + ty.containFunction = newTestTypeContainFunction( + containMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.beGreaterThan() + beGreaterThanMatcherFunctionType := newTestTypeBeGreaterThanFunctionType(matcherType) + compositeType.Members.Set( + testTypeBeGreaterThanFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeBeGreaterThanFunctionName, + beGreaterThanMatcherFunctionType, + testTypeBeGreaterThanFunctionDocString, + ), + ) + ty.beGreaterThanFunction = newTestTypeBeGreaterThanFunction( + beGreaterThanMatcherFunctionType, + matcherTestFunctionType, + ) + + // Test.beLessThan() + beLessThanMatcherFunctionType := newTestTypeBeLessThanFunctionType(matcherType) + compositeType.Members.Set( + testTypeBeLessThanFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testTypeBeLessThanFunctionName, + beLessThanMatcherFunctionType, + testTypeBeLessThanFunctionDocString, + ), + ) + ty.beLessThanFunction = newTestTypeBeLessThanFunction( + beLessThanMatcherFunctionType, + matcherTestFunctionType, + ) + + return ty +} + +const testBlockchainBackendTypeName = "BlockchainBackend" + +func (t *TestContractType) blockchainBackendInterfaceType() *sema.InterfaceType { + typ, ok := t.CompositeType.NestedTypes.Get(testBlockchainBackendTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, testBlockchainBackendTypeName)) + } + + blockchainBackendInterfaceType, ok := typ.(*sema.InterfaceType) + if !ok { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected interface", + testBlockchainBackendTypeName, + )) + } + + return blockchainBackendInterfaceType +} + +func (t *TestContractType) matcherType() *sema.CompositeType { + typ, ok := t.CompositeType.NestedTypes.Get(testMatcherTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, testMatcherTypeName)) + } + + matcherType, ok := typ.(*sema.CompositeType) + if !ok || matcherType.Kind != common.CompositeKindStructure { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected struct type", + testMatcherTypeName, + )) + } + + return matcherType +} + +func (t *TestContractType) blockchainType() *sema.CompositeType { + typ, ok := t.CompositeType.NestedTypes.Get(testBlockchainTypeName) + if !ok { + panic(typeNotFoundError(testContractTypeName, testBlockchainTypeName)) + } + + matcherType, ok := typ.(*sema.CompositeType) + if !ok || matcherType.Kind != common.CompositeKindStructure { + panic(errors.NewUnexpectedError( + "invalid type for '%s'. expected struct type", + testMatcherTypeName, + )) + } + + return matcherType +} + +func (t *TestContractType) NewTestContract( + inter *interpreter.Interpreter, + testFramework TestFramework, + constructor interpreter.FunctionValue, + invocationRange ast.Range, +) ( + *interpreter.CompositeValue, + error, +) { + initializerTypes := t.InitializerTypes + value, err := inter.InvokeFunctionValue( + constructor, + nil, + initializerTypes, + initializerTypes, + invocationRange, + ) + if err != nil { + return nil, err + } + + compositeValue := value.(*interpreter.CompositeValue) + + // Inject natively implemented function values + compositeValue.Functions[testTypeAssertFunctionName] = testTypeAssertFunction + compositeValue.Functions[testTypeFailFunctionName] = testTypeFailFunction + compositeValue.Functions[testTypeExpectFunctionName] = t.expectFunction + compositeValue.Functions[testTypeNewEmulatorBlockchainFunctionName] = + t.newNewEmulatorBlockchainFunction(testFramework) + compositeValue.Functions[testTypeReadFileFunctionName] = + newTestTypeReadFileFunction(testFramework) + + // Inject natively implemented matchers + compositeValue.Functions[testTypeNewMatcherFunctionName] = t.newMatcherFunction + compositeValue.Functions[testTypeEqualFunctionName] = t.equalFunction + compositeValue.Functions[testTypeBeEmptyFunctionName] = t.beEmptyFunction + compositeValue.Functions[testTypeHaveElementCountFunctionName] = t.haveElementCountFunction + compositeValue.Functions[testTypeContainFunctionName] = t.containFunction + compositeValue.Functions[testTypeBeGreaterThanFunctionName] = t.beGreaterThanFunction + compositeValue.Functions[testTypeBeLessThanFunctionName] = t.beLessThanFunction + + return compositeValue, nil +} diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go index 5dba4c9d4b..7760c79ebe 100644 --- a/runtime/stdlib/test_emulatorbackend.go +++ b/runtime/stdlib/test_emulatorbackend.go @@ -124,13 +124,13 @@ func newTestEmulatorBackendType(blockchainBackendInterfaceType *sema.InterfaceTy compositeType, testEmulatorBackendTypeDeployContractFunctionName, deployContractFunctionType, - newEmulatorBackendTypeDeployContractFunctionDocString, + testEmulatorBackendTypeDeployContractFunctionDocString, ), sema.NewUnmeteredPublicFunctionMember( compositeType, testEmulatorBackendTypeUseConfigFunctionName, useConfigFunctionType, - emulatorBackendUseConfigFunctionDocString, + testEmulatorBackendTypeUseConfigFunctionDocString, ), } @@ -234,7 +234,7 @@ func newTestAccountValue( ) // Create an 'Account' by calling its constructor. - accountConstructor := getConstructor(inter, accountTypeName) + accountConstructor := getConstructor(inter, testAccountTypeName) accountValue, err := inter.InvokeExternally( accountConstructor, accountConstructor.Type, @@ -259,6 +259,11 @@ const testEmulatorBackendTypeAddTransactionFunctionDocString = ` Add a transaction to the current block. ` +const testTransactionTypeCodeFieldName = "code" +const testTransactionTypeAuthorizersFieldName = "authorizers" +const testTransactionTypeSignersFieldName = "signers" +const testTransactionTypeArgumentsFieldName = "arguments" + func (t *testEmulatorBackendType) newAddTransactionFunction(testFramework TestFramework) *interpreter.HostFunctionValue { return interpreter.NewUnmeteredHostFunctionValue( t.addTransactionFunctionType, @@ -275,7 +280,7 @@ func (t *testEmulatorBackendType) newAddTransactionFunction(testFramework TestFr codeValue := transactionValue.GetMember( inter, locationRange, - transactionCodeFieldName, + testTransactionTypeCodeFieldName, ) code, ok := codeValue.(*interpreter.StringValue) if !ok { @@ -286,19 +291,19 @@ func (t *testEmulatorBackendType) newAddTransactionFunction(testFramework TestFr authorizerValue := transactionValue.GetMember( inter, locationRange, - transactionAuthorizerFieldName, + testTransactionTypeAuthorizersFieldName, ) - authorizers := addressesFromValue(authorizerValue) + authorizers := addressArrayValueToSlice(authorizerValue) // Get signers signersValue := transactionValue.GetMember( inter, locationRange, - transactionSignersFieldName, + testTransactionTypeSignersFieldName, ) - signerAccounts := accountsFromValue( + signerAccounts := accountsArrayValueToSlice( inter, signersValue, locationRange, @@ -308,7 +313,7 @@ func (t *testEmulatorBackendType) newAddTransactionFunction(testFramework TestFr argsValue := transactionValue.GetMember( inter, locationRange, - transactionArgsFieldName, + testTransactionTypeArgumentsFieldName, ) args, err := arrayValueToSlice(argsValue) if err != nil { @@ -383,7 +388,7 @@ func (t *testEmulatorBackendType) newCommitBlockFunction(testFramework TestFrame const testEmulatorBackendTypeDeployContractFunctionName = "deployContract" -const newEmulatorBackendTypeDeployContractFunctionDocString = ` +const testEmulatorBackendTypeDeployContractFunctionDocString = ` Deploys a given contract, and initializes it with the provided arguments. ` @@ -436,7 +441,7 @@ func (t *testEmulatorBackendType) newDeployContractFunction(testFramework TestFr const testEmulatorBackendTypeUseConfigFunctionName = "useConfiguration" -const emulatorBackendUseConfigFunctionDocString = ` +const testEmulatorBackendTypeUseConfigFunctionDocString = ` Set the configuration to be used by the blockchain. Overrides any existing configuration. ` @@ -489,7 +494,7 @@ func (t *testEmulatorBackendType) newUseConfigFunction(testFramework TestFramewo ) } -func (t *testEmulatorBackendType) new( +func (t *testEmulatorBackendType) newEmulatorBackend( inter *interpreter.Interpreter, testFramework TestFramework, locationRange interpreter.LocationRange, diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index b968eea395..acb18a8e7e 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -64,7 +64,7 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr ) { if importedLocation == TestContractLocation { return sema.ElaborationImport{ - Elaboration: TestContractChecker().Elaboration, + Elaboration: GetTestContractType().Checker.Elaboration, }, nil } @@ -91,7 +91,7 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr Storage: storage, ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { if location == TestContractLocation { - program := interpreter.ProgramFromChecker(TestContractChecker()) + program := interpreter.ProgramFromChecker(GetTestContractType().Checker) subInterpreter, err := inter.NewSubInterpreter(program, location) if err != nil { panic(err) From c809441000e44e7de3a6a0b63f8c3c7d17702cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 16:23:28 -0700 Subject: [PATCH 115/246] separate capability value types into two: path capability and ID capability --- encoding/json/decode.go | 32 +- encoding/json/encode.go | 103 ++-- encoding/json/encoding_test.go | 86 ++- runtime/common/memorykind.go | 6 +- runtime/common/memorykind_string.go | 364 ++++++------- runtime/common/metering.go | 45 +- runtime/convertValues.go | 84 ++- runtime/convertValues_test.go | 334 ++++++++++-- runtime/format/capability.go | 19 +- .../imported_values_memory_metering_test.go | 33 +- runtime/interpreter/decode.go | 129 ++++- runtime/interpreter/encode.go | 91 +++- runtime/interpreter/encoding_test.go | 179 +++++-- runtime/interpreter/interpreter.go | 33 +- runtime/interpreter/simplecompositevalue.go | 2 +- runtime/interpreter/value.go | 462 ++++++++++++---- runtime/interpreter/value_account.go | 4 +- .../value_accountcapabilitycontroller.go | 2 +- runtime/interpreter/value_accountreference.go | 2 +- runtime/interpreter/value_function.go | 6 +- .../value_storagecapabilitycontroller.go | 2 +- runtime/interpreter/value_test.go | 504 +++++++++++------- runtime/interpreter/visitor.go | 19 +- runtime/runtime.go | 4 +- runtime/runtime_test.go | 44 +- runtime/sema/type.go | 4 +- runtime/stdlib/account.go | 82 ++- runtime/storage_test.go | 6 +- runtime/tests/interpreter/account_test.go | 34 +- .../tests/interpreter/dynamic_casting_test.go | 253 +++++---- runtime/tests/interpreter/equality_test.go | 59 +- .../tests/interpreter/memory_metering_test.go | 41 +- runtime/tests/interpreter/values_test.go | 19 +- values.go | 109 +++- values_test.go | 35 +- 35 files changed, 2232 insertions(+), 999 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 2c0f7c240e..56c3659958 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -1269,21 +1269,29 @@ func (d *Decoder) decodeTypeValue(valueJSON any) cadence.TypeValue { ) } -func (d *Decoder) decodeCapability(valueJSON any) cadence.StorageCapability { +func (d *Decoder) decodeCapability(valueJSON any) cadence.Capability { obj := toObject(valueJSON) - path, ok := d.decodeJSON(obj.Get(pathKey)).(cadence.Path) - if !ok { - panic(errors.NewDefaultUserError("invalid capability: missing or invalid path")) - } + if id, ok := obj[idKey]; ok { + return cadence.NewMeteredIDCapability( + d.gauge, + d.decodeUInt64(id), + d.decodeAddress(obj.Get(addressKey)), + d.decodeType(obj.Get(borrowTypeKey), typeDecodingResults{}), + ) + } else { + path, ok := d.decodeJSON(obj.Get(pathKey)).(cadence.Path) + if !ok { + panic(errors.NewDefaultUserError("invalid capability: missing or invalid path")) + } - return cadence.NewMeteredStorageCapability( - d.gauge, - d.decodeUInt64(obj.Get(idKey)), - d.decodeAddress(obj.Get(addressKey)), - path, - d.decodeType(obj.Get(borrowTypeKey), typeDecodingResults{}), - ) + return cadence.NewMeteredPathCapability( + d.gauge, + d.decodeAddress(obj.Get(addressKey)), + path, + d.decodeType(obj.Get(borrowTypeKey), typeDecodingResults{}), + ) + } } // JSON types diff --git a/encoding/json/encode.go b/encoding/json/encode.go index c45bc347d2..bd2bc9e4ec 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -204,10 +204,15 @@ type jsonTypeValue struct { StaticType jsonValue `json:"staticType"` } -type jsonStorageCapabilityValue struct { +type jsonPathCapabilityValue struct { Path jsonValue `json:"path"` BorrowType jsonValue `json:"borrowType"` Address string `json:"address"` +} + +type jsonIDCapabilityValue struct { + BorrowType jsonValue `json:"borrowType"` + Address string `json:"address"` ID string `json:"id"` } @@ -260,85 +265,87 @@ const ( // Prepare traverses the object graph of the provided value and constructs // a struct representation that can be marshalled to JSON. func Prepare(v cadence.Value) jsonValue { - switch x := v.(type) { + switch v := v.(type) { case cadence.Void: return prepareVoid() case cadence.Optional: - return prepareOptional(x) + return prepareOptional(v) case cadence.Bool: - return prepareBool(x) + return prepareBool(v) case cadence.Character: - return prepareCharacter(x) + return prepareCharacter(v) case cadence.String: - return prepareString(x) + return prepareString(v) case cadence.Address: - return prepareAddress(x) + return prepareAddress(v) case cadence.Int: - return prepareInt(x) + return prepareInt(v) case cadence.Int8: - return prepareInt8(x) + return prepareInt8(v) case cadence.Int16: - return prepareInt16(x) + return prepareInt16(v) case cadence.Int32: - return prepareInt32(x) + return prepareInt32(v) case cadence.Int64: - return prepareInt64(x) + return prepareInt64(v) case cadence.Int128: - return prepareInt128(x) + return prepareInt128(v) case cadence.Int256: - return prepareInt256(x) + return prepareInt256(v) case cadence.UInt: - return prepareUInt(x) + return prepareUInt(v) case cadence.UInt8: - return prepareUInt8(x) + return prepareUInt8(v) case cadence.UInt16: - return prepareUInt16(x) + return prepareUInt16(v) case cadence.UInt32: - return prepareUInt32(x) + return prepareUInt32(v) case cadence.UInt64: - return prepareUInt64(x) + return prepareUInt64(v) case cadence.UInt128: - return prepareUInt128(x) + return prepareUInt128(v) case cadence.UInt256: - return prepareUInt256(x) + return prepareUInt256(v) case cadence.Word8: - return prepareWord8(x) + return prepareWord8(v) case cadence.Word16: - return prepareWord16(x) + return prepareWord16(v) case cadence.Word32: - return prepareWord32(x) + return prepareWord32(v) case cadence.Word64: - return prepareWord64(x) + return prepareWord64(v) case cadence.Fix64: - return prepareFix64(x) + return prepareFix64(v) case cadence.UFix64: - return prepareUFix64(x) + return prepareUFix64(v) case cadence.Array: - return prepareArray(x) + return prepareArray(v) case cadence.Dictionary: - return prepareDictionary(x) + return prepareDictionary(v) case cadence.Struct: - return prepareStruct(x) + return prepareStruct(v) case cadence.Resource: - return prepareResource(x) + return prepareResource(v) case cadence.Event: - return prepareEvent(x) + return prepareEvent(v) case cadence.Contract: - return prepareContract(x) + return prepareContract(v) case cadence.PathLink: - return preparePathLink(x) + return preparePathLink(v) case cadence.AccountLink: return prepareAccountLink() case cadence.Path: - return preparePath(x) + return preparePath(v) case cadence.TypeValue: - return prepareTypeValue(x) - case cadence.StorageCapability: - return prepareCapability(x) + return prepareTypeValue(v) + case cadence.PathCapability: + return preparePathCapability(v) + case cadence.IDCapability: + return prepareIDCapability(v) case cadence.Enum: - return prepareEnum(x) + return prepareEnum(v) case cadence.Function: - return prepareFunction(x) + return prepareFunction(v) default: panic(fmt.Errorf("unsupported value: %T, %v", v, v)) } @@ -902,11 +909,10 @@ func prepareTypeValue(typeValue cadence.TypeValue) jsonValue { } } -func prepareCapability(capability cadence.StorageCapability) jsonValue { +func preparePathCapability(capability cadence.PathCapability) jsonValue { return jsonValueObject{ Type: capabilityTypeStr, - Value: jsonStorageCapabilityValue{ - ID: encodeUInt(uint64(capability.ID)), + Value: jsonPathCapabilityValue{ Path: preparePath(capability.Path), Address: encodeBytes(capability.Address.Bytes()), BorrowType: prepareType(capability.BorrowType, typePreparationResults{}), @@ -914,6 +920,17 @@ func prepareCapability(capability cadence.StorageCapability) jsonValue { } } +func prepareIDCapability(capability cadence.IDCapability) jsonValue { + return jsonValueObject{ + Type: capabilityTypeStr, + Value: jsonIDCapabilityValue{ + ID: encodeUInt(uint64(capability.ID)), + Address: encodeBytes(capability.Address.Bytes()), + BorrowType: prepareType(capability.BorrowType, typePreparationResults{}), + }, + } +} + func prepareFunction(function cadence.Function) jsonValue { return jsonValueObject{ Type: functionTypeStr, diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index cceffce57a..8e24e6f7d2 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2527,38 +2527,66 @@ func TestEncodeCapability(t *testing.T) { t.Parallel() - path, err := cadence.NewPath(common.PathDomainStorage, "foo") - require.NoError(t, err) + t.Run("path", func(t *testing.T) { - testEncodeAndDecode( - t, - cadence.NewStorageCapability( - 6, - cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), - path, - cadence.IntType{}, - ), - // language=json - ` - { - "type": "Capability", - "value": { - "path": { - "type": "Path", + path, err := cadence.NewPath(common.PathDomainPublic, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + cadence.NewPathCapability( + cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + path, + cadence.IntType{}, + ), + // language=json + ` + { + "type": "Capability", "value": { - "domain": "storage", - "identifier": "foo" + "path": { + "type": "Path", + "value": { + "domain": "public", + "identifier": "foo" + } + }, + "borrowType": { + "kind": "Int" + }, + "address": "0x0000000102030405" } - }, - "borrowType": { - "kind": "Int" - }, - "address": "0x0000000102030405", - "id": "6" - } - } - `, - ) + } + `, + ) + }) + + t.Run("ID", func(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.NewIDCapability( + 6, + cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + cadence.IntType{}, + ), + // language=json + ` + { + "type": "Capability", + "value": { + "borrowType": { + "kind": "Int" + }, + "address": "0x0000000102030405", + "id": "6" + } + } + `, + ) + }) } func TestDecodeFixedPoints(t *testing.T) { diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 9211280b38..bf1888009f 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -39,7 +39,8 @@ const ( MemoryKindOptionalValue MemoryKindTypeValue MemoryKindPathValue - MemoryKindStorageCapabilityValue + MemoryKindIDCapabilityValue + MemoryKindPathCapabilityValue MemoryKindPathLinkValue MemoryKindAccountLinkValue MemoryKindStorageReferenceValue @@ -106,7 +107,8 @@ const ( MemoryKindCadenceAccountLinkValue MemoryKindCadencePathValue MemoryKindCadenceTypeValue - MemoryKindCadenceStorageCapabilityValue + MemoryKindCadenceIDCapabilityValue + MemoryKindCadencePathCapabilityValue MemoryKindCadenceFunctionValue // Cadence Types diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 3729fd2ce3..2f5fddc90e 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -20,190 +20,192 @@ func _() { _ = x[MemoryKindOptionalValue-9] _ = x[MemoryKindTypeValue-10] _ = x[MemoryKindPathValue-11] - _ = x[MemoryKindStorageCapabilityValue-12] - _ = x[MemoryKindPathLinkValue-13] - _ = x[MemoryKindAccountLinkValue-14] - _ = x[MemoryKindStorageReferenceValue-15] - _ = x[MemoryKindAccountReferenceValue-16] - _ = x[MemoryKindEphemeralReferenceValue-17] - _ = x[MemoryKindInterpretedFunctionValue-18] - _ = x[MemoryKindHostFunctionValue-19] - _ = x[MemoryKindBoundFunctionValue-20] - _ = x[MemoryKindBigInt-21] - _ = x[MemoryKindSimpleCompositeValue-22] - _ = x[MemoryKindPublishedValue-23] - _ = x[MemoryKindStorageCapabilityControllerValue-24] - _ = x[MemoryKindAccountCapabilityControllerValue-25] - _ = x[MemoryKindAtreeArrayDataSlab-26] - _ = x[MemoryKindAtreeArrayMetaDataSlab-27] - _ = x[MemoryKindAtreeArrayElementOverhead-28] - _ = x[MemoryKindAtreeMapDataSlab-29] - _ = x[MemoryKindAtreeMapMetaDataSlab-30] - _ = x[MemoryKindAtreeMapElementOverhead-31] - _ = x[MemoryKindAtreeMapPreAllocatedElement-32] - _ = x[MemoryKindAtreeEncodedSlab-33] - _ = x[MemoryKindPrimitiveStaticType-34] - _ = x[MemoryKindCompositeStaticType-35] - _ = x[MemoryKindInterfaceStaticType-36] - _ = x[MemoryKindVariableSizedStaticType-37] - _ = x[MemoryKindConstantSizedStaticType-38] - _ = x[MemoryKindDictionaryStaticType-39] - _ = x[MemoryKindOptionalStaticType-40] - _ = x[MemoryKindRestrictedStaticType-41] - _ = x[MemoryKindReferenceStaticType-42] - _ = x[MemoryKindCapabilityStaticType-43] - _ = x[MemoryKindFunctionStaticType-44] - _ = x[MemoryKindCadenceVoidValue-45] - _ = x[MemoryKindCadenceOptionalValue-46] - _ = x[MemoryKindCadenceBoolValue-47] - _ = x[MemoryKindCadenceStringValue-48] - _ = x[MemoryKindCadenceCharacterValue-49] - _ = x[MemoryKindCadenceAddressValue-50] - _ = x[MemoryKindCadenceIntValue-51] - _ = x[MemoryKindCadenceNumberValue-52] - _ = x[MemoryKindCadenceArrayValueBase-53] - _ = x[MemoryKindCadenceArrayValueLength-54] - _ = x[MemoryKindCadenceDictionaryValue-55] - _ = x[MemoryKindCadenceKeyValuePair-56] - _ = x[MemoryKindCadenceStructValueBase-57] - _ = x[MemoryKindCadenceStructValueSize-58] - _ = x[MemoryKindCadenceResourceValueBase-59] - _ = x[MemoryKindCadenceAttachmentValueBase-60] - _ = x[MemoryKindCadenceResourceValueSize-61] - _ = x[MemoryKindCadenceAttachmentValueSize-62] - _ = x[MemoryKindCadenceEventValueBase-63] - _ = x[MemoryKindCadenceEventValueSize-64] - _ = x[MemoryKindCadenceContractValueBase-65] - _ = x[MemoryKindCadenceContractValueSize-66] - _ = x[MemoryKindCadenceEnumValueBase-67] - _ = x[MemoryKindCadenceEnumValueSize-68] - _ = x[MemoryKindCadencePathLinkValue-69] - _ = x[MemoryKindCadenceAccountLinkValue-70] - _ = x[MemoryKindCadencePathValue-71] - _ = x[MemoryKindCadenceTypeValue-72] - _ = x[MemoryKindCadenceStorageCapabilityValue-73] - _ = x[MemoryKindCadenceFunctionValue-74] - _ = x[MemoryKindCadenceOptionalType-75] - _ = x[MemoryKindCadenceVariableSizedArrayType-76] - _ = x[MemoryKindCadenceConstantSizedArrayType-77] - _ = x[MemoryKindCadenceDictionaryType-78] - _ = x[MemoryKindCadenceField-79] - _ = x[MemoryKindCadenceParameter-80] - _ = x[MemoryKindCadenceTypeParameter-81] - _ = x[MemoryKindCadenceStructType-82] - _ = x[MemoryKindCadenceResourceType-83] - _ = x[MemoryKindCadenceAttachmentType-84] - _ = x[MemoryKindCadenceEventType-85] - _ = x[MemoryKindCadenceContractType-86] - _ = x[MemoryKindCadenceStructInterfaceType-87] - _ = x[MemoryKindCadenceResourceInterfaceType-88] - _ = x[MemoryKindCadenceContractInterfaceType-89] - _ = x[MemoryKindCadenceFunctionType-90] - _ = x[MemoryKindCadenceReferenceType-91] - _ = x[MemoryKindCadenceRestrictedType-92] - _ = x[MemoryKindCadenceCapabilityType-93] - _ = x[MemoryKindCadenceEnumType-94] - _ = x[MemoryKindRawString-95] - _ = x[MemoryKindAddressLocation-96] - _ = x[MemoryKindBytes-97] - _ = x[MemoryKindVariable-98] - _ = x[MemoryKindCompositeTypeInfo-99] - _ = x[MemoryKindCompositeField-100] - _ = x[MemoryKindInvocation-101] - _ = x[MemoryKindStorageMap-102] - _ = x[MemoryKindStorageKey-103] - _ = x[MemoryKindTypeToken-104] - _ = x[MemoryKindErrorToken-105] - _ = x[MemoryKindSpaceToken-106] - _ = x[MemoryKindProgram-107] - _ = x[MemoryKindIdentifier-108] - _ = x[MemoryKindArgument-109] - _ = x[MemoryKindBlock-110] - _ = x[MemoryKindFunctionBlock-111] - _ = x[MemoryKindParameter-112] - _ = x[MemoryKindParameterList-113] - _ = x[MemoryKindTypeParameter-114] - _ = x[MemoryKindTypeParameterList-115] - _ = x[MemoryKindTransfer-116] - _ = x[MemoryKindMembers-117] - _ = x[MemoryKindTypeAnnotation-118] - _ = x[MemoryKindDictionaryEntry-119] - _ = x[MemoryKindFunctionDeclaration-120] - _ = x[MemoryKindCompositeDeclaration-121] - _ = x[MemoryKindAttachmentDeclaration-122] - _ = x[MemoryKindInterfaceDeclaration-123] - _ = x[MemoryKindEnumCaseDeclaration-124] - _ = x[MemoryKindFieldDeclaration-125] - _ = x[MemoryKindTransactionDeclaration-126] - _ = x[MemoryKindImportDeclaration-127] - _ = x[MemoryKindVariableDeclaration-128] - _ = x[MemoryKindSpecialFunctionDeclaration-129] - _ = x[MemoryKindPragmaDeclaration-130] - _ = x[MemoryKindAssignmentStatement-131] - _ = x[MemoryKindBreakStatement-132] - _ = x[MemoryKindContinueStatement-133] - _ = x[MemoryKindEmitStatement-134] - _ = x[MemoryKindExpressionStatement-135] - _ = x[MemoryKindForStatement-136] - _ = x[MemoryKindIfStatement-137] - _ = x[MemoryKindReturnStatement-138] - _ = x[MemoryKindSwapStatement-139] - _ = x[MemoryKindSwitchStatement-140] - _ = x[MemoryKindWhileStatement-141] - _ = x[MemoryKindRemoveStatement-142] - _ = x[MemoryKindBooleanExpression-143] - _ = x[MemoryKindVoidExpression-144] - _ = x[MemoryKindNilExpression-145] - _ = x[MemoryKindStringExpression-146] - _ = x[MemoryKindIntegerExpression-147] - _ = x[MemoryKindFixedPointExpression-148] - _ = x[MemoryKindArrayExpression-149] - _ = x[MemoryKindDictionaryExpression-150] - _ = x[MemoryKindIdentifierExpression-151] - _ = x[MemoryKindInvocationExpression-152] - _ = x[MemoryKindMemberExpression-153] - _ = x[MemoryKindIndexExpression-154] - _ = x[MemoryKindConditionalExpression-155] - _ = x[MemoryKindUnaryExpression-156] - _ = x[MemoryKindBinaryExpression-157] - _ = x[MemoryKindFunctionExpression-158] - _ = x[MemoryKindCastingExpression-159] - _ = x[MemoryKindCreateExpression-160] - _ = x[MemoryKindDestroyExpression-161] - _ = x[MemoryKindReferenceExpression-162] - _ = x[MemoryKindForceExpression-163] - _ = x[MemoryKindPathExpression-164] - _ = x[MemoryKindAttachExpression-165] - _ = x[MemoryKindConstantSizedType-166] - _ = x[MemoryKindDictionaryType-167] - _ = x[MemoryKindFunctionType-168] - _ = x[MemoryKindInstantiationType-169] - _ = x[MemoryKindNominalType-170] - _ = x[MemoryKindOptionalType-171] - _ = x[MemoryKindReferenceType-172] - _ = x[MemoryKindRestrictedType-173] - _ = x[MemoryKindVariableSizedType-174] - _ = x[MemoryKindPosition-175] - _ = x[MemoryKindRange-176] - _ = x[MemoryKindElaboration-177] - _ = x[MemoryKindActivation-178] - _ = x[MemoryKindActivationEntries-179] - _ = x[MemoryKindVariableSizedSemaType-180] - _ = x[MemoryKindConstantSizedSemaType-181] - _ = x[MemoryKindDictionarySemaType-182] - _ = x[MemoryKindOptionalSemaType-183] - _ = x[MemoryKindRestrictedSemaType-184] - _ = x[MemoryKindReferenceSemaType-185] - _ = x[MemoryKindCapabilitySemaType-186] - _ = x[MemoryKindOrderedMap-187] - _ = x[MemoryKindOrderedMapEntryList-188] - _ = x[MemoryKindOrderedMapEntry-189] - _ = x[MemoryKindLast-190] + _ = x[MemoryKindIDCapabilityValue-12] + _ = x[MemoryKindPathCapabilityValue-13] + _ = x[MemoryKindPathLinkValue-14] + _ = x[MemoryKindAccountLinkValue-15] + _ = x[MemoryKindStorageReferenceValue-16] + _ = x[MemoryKindAccountReferenceValue-17] + _ = x[MemoryKindEphemeralReferenceValue-18] + _ = x[MemoryKindInterpretedFunctionValue-19] + _ = x[MemoryKindHostFunctionValue-20] + _ = x[MemoryKindBoundFunctionValue-21] + _ = x[MemoryKindBigInt-22] + _ = x[MemoryKindSimpleCompositeValue-23] + _ = x[MemoryKindPublishedValue-24] + _ = x[MemoryKindStorageCapabilityControllerValue-25] + _ = x[MemoryKindAccountCapabilityControllerValue-26] + _ = x[MemoryKindAtreeArrayDataSlab-27] + _ = x[MemoryKindAtreeArrayMetaDataSlab-28] + _ = x[MemoryKindAtreeArrayElementOverhead-29] + _ = x[MemoryKindAtreeMapDataSlab-30] + _ = x[MemoryKindAtreeMapMetaDataSlab-31] + _ = x[MemoryKindAtreeMapElementOverhead-32] + _ = x[MemoryKindAtreeMapPreAllocatedElement-33] + _ = x[MemoryKindAtreeEncodedSlab-34] + _ = x[MemoryKindPrimitiveStaticType-35] + _ = x[MemoryKindCompositeStaticType-36] + _ = x[MemoryKindInterfaceStaticType-37] + _ = x[MemoryKindVariableSizedStaticType-38] + _ = x[MemoryKindConstantSizedStaticType-39] + _ = x[MemoryKindDictionaryStaticType-40] + _ = x[MemoryKindOptionalStaticType-41] + _ = x[MemoryKindRestrictedStaticType-42] + _ = x[MemoryKindReferenceStaticType-43] + _ = x[MemoryKindCapabilityStaticType-44] + _ = x[MemoryKindFunctionStaticType-45] + _ = x[MemoryKindCadenceVoidValue-46] + _ = x[MemoryKindCadenceOptionalValue-47] + _ = x[MemoryKindCadenceBoolValue-48] + _ = x[MemoryKindCadenceStringValue-49] + _ = x[MemoryKindCadenceCharacterValue-50] + _ = x[MemoryKindCadenceAddressValue-51] + _ = x[MemoryKindCadenceIntValue-52] + _ = x[MemoryKindCadenceNumberValue-53] + _ = x[MemoryKindCadenceArrayValueBase-54] + _ = x[MemoryKindCadenceArrayValueLength-55] + _ = x[MemoryKindCadenceDictionaryValue-56] + _ = x[MemoryKindCadenceKeyValuePair-57] + _ = x[MemoryKindCadenceStructValueBase-58] + _ = x[MemoryKindCadenceStructValueSize-59] + _ = x[MemoryKindCadenceResourceValueBase-60] + _ = x[MemoryKindCadenceAttachmentValueBase-61] + _ = x[MemoryKindCadenceResourceValueSize-62] + _ = x[MemoryKindCadenceAttachmentValueSize-63] + _ = x[MemoryKindCadenceEventValueBase-64] + _ = x[MemoryKindCadenceEventValueSize-65] + _ = x[MemoryKindCadenceContractValueBase-66] + _ = x[MemoryKindCadenceContractValueSize-67] + _ = x[MemoryKindCadenceEnumValueBase-68] + _ = x[MemoryKindCadenceEnumValueSize-69] + _ = x[MemoryKindCadencePathLinkValue-70] + _ = x[MemoryKindCadenceAccountLinkValue-71] + _ = x[MemoryKindCadencePathValue-72] + _ = x[MemoryKindCadenceTypeValue-73] + _ = x[MemoryKindCadenceIDCapabilityValue-74] + _ = x[MemoryKindCadencePathCapabilityValue-75] + _ = x[MemoryKindCadenceFunctionValue-76] + _ = x[MemoryKindCadenceOptionalType-77] + _ = x[MemoryKindCadenceVariableSizedArrayType-78] + _ = x[MemoryKindCadenceConstantSizedArrayType-79] + _ = x[MemoryKindCadenceDictionaryType-80] + _ = x[MemoryKindCadenceField-81] + _ = x[MemoryKindCadenceParameter-82] + _ = x[MemoryKindCadenceTypeParameter-83] + _ = x[MemoryKindCadenceStructType-84] + _ = x[MemoryKindCadenceResourceType-85] + _ = x[MemoryKindCadenceAttachmentType-86] + _ = x[MemoryKindCadenceEventType-87] + _ = x[MemoryKindCadenceContractType-88] + _ = x[MemoryKindCadenceStructInterfaceType-89] + _ = x[MemoryKindCadenceResourceInterfaceType-90] + _ = x[MemoryKindCadenceContractInterfaceType-91] + _ = x[MemoryKindCadenceFunctionType-92] + _ = x[MemoryKindCadenceReferenceType-93] + _ = x[MemoryKindCadenceRestrictedType-94] + _ = x[MemoryKindCadenceCapabilityType-95] + _ = x[MemoryKindCadenceEnumType-96] + _ = x[MemoryKindRawString-97] + _ = x[MemoryKindAddressLocation-98] + _ = x[MemoryKindBytes-99] + _ = x[MemoryKindVariable-100] + _ = x[MemoryKindCompositeTypeInfo-101] + _ = x[MemoryKindCompositeField-102] + _ = x[MemoryKindInvocation-103] + _ = x[MemoryKindStorageMap-104] + _ = x[MemoryKindStorageKey-105] + _ = x[MemoryKindTypeToken-106] + _ = x[MemoryKindErrorToken-107] + _ = x[MemoryKindSpaceToken-108] + _ = x[MemoryKindProgram-109] + _ = x[MemoryKindIdentifier-110] + _ = x[MemoryKindArgument-111] + _ = x[MemoryKindBlock-112] + _ = x[MemoryKindFunctionBlock-113] + _ = x[MemoryKindParameter-114] + _ = x[MemoryKindParameterList-115] + _ = x[MemoryKindTypeParameter-116] + _ = x[MemoryKindTypeParameterList-117] + _ = x[MemoryKindTransfer-118] + _ = x[MemoryKindMembers-119] + _ = x[MemoryKindTypeAnnotation-120] + _ = x[MemoryKindDictionaryEntry-121] + _ = x[MemoryKindFunctionDeclaration-122] + _ = x[MemoryKindCompositeDeclaration-123] + _ = x[MemoryKindAttachmentDeclaration-124] + _ = x[MemoryKindInterfaceDeclaration-125] + _ = x[MemoryKindEnumCaseDeclaration-126] + _ = x[MemoryKindFieldDeclaration-127] + _ = x[MemoryKindTransactionDeclaration-128] + _ = x[MemoryKindImportDeclaration-129] + _ = x[MemoryKindVariableDeclaration-130] + _ = x[MemoryKindSpecialFunctionDeclaration-131] + _ = x[MemoryKindPragmaDeclaration-132] + _ = x[MemoryKindAssignmentStatement-133] + _ = x[MemoryKindBreakStatement-134] + _ = x[MemoryKindContinueStatement-135] + _ = x[MemoryKindEmitStatement-136] + _ = x[MemoryKindExpressionStatement-137] + _ = x[MemoryKindForStatement-138] + _ = x[MemoryKindIfStatement-139] + _ = x[MemoryKindReturnStatement-140] + _ = x[MemoryKindSwapStatement-141] + _ = x[MemoryKindSwitchStatement-142] + _ = x[MemoryKindWhileStatement-143] + _ = x[MemoryKindRemoveStatement-144] + _ = x[MemoryKindBooleanExpression-145] + _ = x[MemoryKindVoidExpression-146] + _ = x[MemoryKindNilExpression-147] + _ = x[MemoryKindStringExpression-148] + _ = x[MemoryKindIntegerExpression-149] + _ = x[MemoryKindFixedPointExpression-150] + _ = x[MemoryKindArrayExpression-151] + _ = x[MemoryKindDictionaryExpression-152] + _ = x[MemoryKindIdentifierExpression-153] + _ = x[MemoryKindInvocationExpression-154] + _ = x[MemoryKindMemberExpression-155] + _ = x[MemoryKindIndexExpression-156] + _ = x[MemoryKindConditionalExpression-157] + _ = x[MemoryKindUnaryExpression-158] + _ = x[MemoryKindBinaryExpression-159] + _ = x[MemoryKindFunctionExpression-160] + _ = x[MemoryKindCastingExpression-161] + _ = x[MemoryKindCreateExpression-162] + _ = x[MemoryKindDestroyExpression-163] + _ = x[MemoryKindReferenceExpression-164] + _ = x[MemoryKindForceExpression-165] + _ = x[MemoryKindPathExpression-166] + _ = x[MemoryKindAttachExpression-167] + _ = x[MemoryKindConstantSizedType-168] + _ = x[MemoryKindDictionaryType-169] + _ = x[MemoryKindFunctionType-170] + _ = x[MemoryKindInstantiationType-171] + _ = x[MemoryKindNominalType-172] + _ = x[MemoryKindOptionalType-173] + _ = x[MemoryKindReferenceType-174] + _ = x[MemoryKindRestrictedType-175] + _ = x[MemoryKindVariableSizedType-176] + _ = x[MemoryKindPosition-177] + _ = x[MemoryKindRange-178] + _ = x[MemoryKindElaboration-179] + _ = x[MemoryKindActivation-180] + _ = x[MemoryKindActivationEntries-181] + _ = x[MemoryKindVariableSizedSemaType-182] + _ = x[MemoryKindConstantSizedSemaType-183] + _ = x[MemoryKindDictionarySemaType-184] + _ = x[MemoryKindOptionalSemaType-185] + _ = x[MemoryKindRestrictedSemaType-186] + _ = x[MemoryKindReferenceSemaType-187] + _ = x[MemoryKindCapabilitySemaType-188] + _ = x[MemoryKindOrderedMap-189] + _ = x[MemoryKindOrderedMapEntryList-190] + _ = x[MemoryKindOrderedMapEntry-191] + _ = x[MemoryKindLast-192] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueIDCapabilityValuePathCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceIDCapabilityValueCadencePathCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 408, 440, 458, 480, 505, 521, 541, 564, 591, 607, 626, 645, 664, 687, 710, 730, 748, 768, 787, 807, 825, 841, 861, 877, 895, 916, 935, 950, 968, 989, 1012, 1034, 1053, 1075, 1097, 1121, 1147, 1171, 1197, 1218, 1239, 1263, 1287, 1307, 1327, 1347, 1370, 1386, 1402, 1431, 1451, 1470, 1499, 1528, 1549, 1561, 1577, 1597, 1614, 1633, 1654, 1670, 1689, 1715, 1743, 1771, 1790, 1810, 1831, 1852, 1867, 1876, 1891, 1896, 1904, 1921, 1935, 1945, 1955, 1965, 1974, 1984, 1994, 2001, 2011, 2019, 2024, 2037, 2046, 2059, 2072, 2089, 2097, 2104, 2118, 2133, 2152, 2172, 2193, 2213, 2232, 2248, 2270, 2287, 2306, 2332, 2349, 2368, 2382, 2399, 2412, 2431, 2443, 2454, 2469, 2482, 2497, 2511, 2526, 2543, 2557, 2570, 2586, 2603, 2623, 2638, 2658, 2678, 2698, 2714, 2729, 2750, 2765, 2781, 2799, 2816, 2832, 2849, 2868, 2883, 2897, 2913, 2930, 2944, 2956, 2973, 2984, 2996, 3009, 3023, 3040, 3048, 3053, 3064, 3074, 3091, 3112, 3133, 3151, 3167, 3185, 3202, 3220, 3230, 3249, 3264, 3268} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 178, 197, 210, 226, 247, 268, 291, 315, 332, 350, 356, 376, 390, 422, 454, 472, 494, 519, 535, 555, 578, 605, 621, 640, 659, 678, 701, 724, 744, 762, 782, 801, 821, 839, 855, 875, 891, 909, 930, 949, 964, 982, 1003, 1026, 1048, 1067, 1089, 1111, 1135, 1161, 1185, 1211, 1232, 1253, 1277, 1301, 1321, 1341, 1361, 1384, 1400, 1416, 1440, 1466, 1486, 1505, 1534, 1563, 1584, 1596, 1612, 1632, 1649, 1668, 1689, 1705, 1724, 1750, 1778, 1806, 1825, 1845, 1866, 1887, 1902, 1911, 1926, 1931, 1939, 1956, 1970, 1980, 1990, 2000, 2009, 2019, 2029, 2036, 2046, 2054, 2059, 2072, 2081, 2094, 2107, 2124, 2132, 2139, 2153, 2168, 2187, 2207, 2228, 2248, 2267, 2283, 2305, 2322, 2341, 2367, 2384, 2403, 2417, 2434, 2447, 2466, 2478, 2489, 2504, 2517, 2532, 2546, 2561, 2578, 2592, 2605, 2621, 2638, 2658, 2673, 2693, 2713, 2733, 2749, 2764, 2785, 2800, 2816, 2834, 2851, 2867, 2884, 2903, 2918, 2932, 2948, 2965, 2979, 2991, 3008, 3019, 3031, 3044, 3058, 3075, 3083, 3088, 3099, 3109, 3126, 3147, 3168, 3186, 3202, 3220, 3237, 3255, 3265, 3284, 3299, 3303} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 549e75c65a..9135d75b01 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -143,7 +143,8 @@ var ( BoundFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindBoundFunctionValue) HostFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindHostFunctionValue) InterpretedFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindInterpretedFunctionValue) - StorageCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageCapabilityValue) + PathCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindPathCapabilityValue) + IDCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindIDCapabilityValue) EphemeralReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindEphemeralReferenceValue) StorageReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindStorageReferenceValue) AccountReferenceValueMemoryUsage = NewConstantMemoryUsage(MemoryKindAccountReferenceValue) @@ -189,25 +190,26 @@ var ( // Cadence external values - CadenceDictionaryValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceDictionaryValue) - CadenceArrayValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceArrayValueBase) - CadenceStructValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceStructValueBase) - CadenceResourceValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceResourceValueBase) - CadenceAttachmentValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceAttachmentValueBase) - CadenceEventValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEventValueBase) - CadenceContractValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceContractValueBase) - CadenceEnumValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEnumValueBase) - CadenceAddressValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceAddressValue) - CadenceBoolValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceBoolValue) - CadenceStorageCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceStorageCapabilityValue) - CadenceFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionValue) - CadenceKeyValuePairMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceKeyValuePair) - CadencePathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) - CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) - CadenceOptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceOptionalValue) - CadencePathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathValue) - CadenceVoidValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceVoidValue) - CadenceTypeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceTypeValue) + CadenceDictionaryValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceDictionaryValue) + CadenceArrayValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceArrayValueBase) + CadenceStructValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceStructValueBase) + CadenceResourceValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceResourceValueBase) + CadenceAttachmentValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceAttachmentValueBase) + CadenceEventValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEventValueBase) + CadenceContractValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceContractValueBase) + CadenceEnumValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEnumValueBase) + CadenceAddressValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceAddressValue) + CadenceBoolValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceBoolValue) + CadenceIDCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceIDCapabilityValue) + CadencePathCapabilityValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathCapabilityValue) + CadenceFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionValue) + CadenceKeyValuePairMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceKeyValuePair) + CadencePathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) + CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) + CadenceOptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceOptionalValue) + CadencePathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathValue) + CadenceVoidValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceVoidValue) + CadenceTypeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceTypeValue) // Cadence external types @@ -253,7 +255,8 @@ var ( AuthAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Capabilities()")) PublicAccountCapabilitiesStringMemoryUsage = NewRawStringMemoryUsage(len("PublicAccount.Capabilities()")) AuthAccountInboxStringMemoryUsage = NewRawStringMemoryUsage(len("AuthAccount.Inbox()")) - StorageCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(id: , address: , path: )")) + IDCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , id: )")) + PathCapabilityValueStringMemoryUsage = NewRawStringMemoryUsage(len("Capability<>(address: , path: )")) StorageCapabilityControllerValueStringMemoryUsage = NewRawStringMemoryUsage(len("StorageCapabilityController(borrowType: , capabilityID: , target: )")) AccountCapabilityControllerValueStringMemoryUsage = NewRawStringMemoryUsage(len("AccountCapabilityController(borrowType: , capabilityID: )")) PathLinkValueStringMemoryUsage = NewRawStringMemoryUsage(len("PathLink<>()")) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 9270d79e44..17e48b5f14 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -219,8 +219,10 @@ func exportValueWithInterpreter( return exportPathValue(inter, v) case interpreter.TypeValue: return exportTypeValue(v, inter), nil - case *interpreter.StorageCapabilityValue: - return exportStorageCapabilityValue(v, inter) + case *interpreter.IDCapabilityValue: + return exportIDCapabilityValue(v, inter) + case *interpreter.PathCapabilityValue: + return exportPathCapabilityValue(v, inter) case *interpreter.EphemeralReferenceValue: // Break recursion through references if _, ok := seenReferences[v]; ok { @@ -626,10 +628,10 @@ func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) ca ) } -func exportStorageCapabilityValue( - v *interpreter.StorageCapabilityValue, +func exportPathCapabilityValue( + v *interpreter.PathCapabilityValue, inter *interpreter.Interpreter, -) (cadence.StorageCapability, error) { +) (cadence.PathCapability, error) { var borrowType sema.Type if v.BorrowType != nil { borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) @@ -637,18 +639,34 @@ func exportStorageCapabilityValue( path, err := exportPathValue(inter, v.Path) if err != nil { - return cadence.StorageCapability{}, err + return cadence.PathCapability{}, err } - return cadence.NewMeteredStorageCapability( + return cadence.NewMeteredPathCapability( inter, - cadence.NewMeteredUInt64(inter, uint64(v.ID)), cadence.NewMeteredAddress(inter, v.Address), path, ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), ), nil } +func exportIDCapabilityValue( + v *interpreter.IDCapabilityValue, + inter *interpreter.Interpreter, +) (cadence.IDCapability, error) { + var borrowType sema.Type + if v.BorrowType != nil { + borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) + } + + return cadence.NewMeteredIDCapability( + inter, + cadence.NewMeteredUInt64(inter, uint64(v.ID)), + cadence.NewMeteredAddress(inter, v.Address), + ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), + ), nil +} + // exportEvent converts a runtime event to its native Go representation. func exportEvent( gauge common.MemoryGauge, @@ -818,13 +836,18 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) ) case cadence.TypeValue: return i.importTypeValue(v.StaticType) - case cadence.StorageCapability: - return i.importStorageCapability( - v.ID, + case cadence.PathCapability: + return i.importPathCapability( v.Address, v.Path, v.BorrowType, ) + case cadence.IDCapability: + return i.importIDCapability( + v.ID, + v.Address, + v.BorrowType, + ) case cadence.Contract: return nil, errors.NewDefaultUserError("cannot import contract") case cadence.Function: @@ -1090,13 +1113,12 @@ func (i valueImporter) importTypeValue(v cadence.Type) (interpreter.TypeValue, e return interpreter.NewTypeValue(inter, typ), nil } -func (i valueImporter) importStorageCapability( - id cadence.UInt64, +func (i valueImporter) importPathCapability( address cadence.Address, path cadence.Path, borrowType cadence.Type, ) ( - *interpreter.StorageCapabilityValue, + *interpreter.PathCapabilityValue, error, ) { _, ok := borrowType.(*cadence.ReferenceType) @@ -1109,9 +1131,8 @@ func (i valueImporter) importStorageCapability( inter := i.inter - return interpreter.NewStorageCapabilityValue( + return interpreter.NewPathCapabilityValue( inter, - i.importUInt64(id), interpreter.NewAddressValue( inter, common.Address(address), @@ -1121,6 +1142,37 @@ func (i valueImporter) importStorageCapability( ), nil } +func (i valueImporter) importIDCapability( + id cadence.UInt64, + address cadence.Address, + borrowType cadence.Type, +) ( + *interpreter.IDCapabilityValue, + error, +) { + _, ok := borrowType.(*cadence.ReferenceType) + if !ok { + return nil, errors.NewDefaultUserError( + "cannot import capability: expected reference, got '%s'", + borrowType.ID(), + ) + } + + inter := i.inter + + addressValue := interpreter.NewAddressValue( + inter, + common.Address(address), + ) + + return interpreter.NewIDCapabilityValue( + inter, + i.importUInt64(id), + addressValue, + ImportType(inter, borrowType), + ), nil +} + func (i valueImporter) importOptionalValue( v cadence.Optional, expectedType sema.Type, diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 9bf87b1f4b..8fd03ae131 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -838,9 +838,8 @@ func TestImportValue(t *testing.T) { expected: nil, }, { - label: "Capability (invalid)", - value: cadence.NewStorageCapability( - 3, + label: "path Capability (invalid)", + value: cadence.NewPathCapability( cadence.Address{0x1}, cadence.Path{ Domain: common.PathDomainPublic, @@ -850,6 +849,15 @@ func TestImportValue(t *testing.T) { ), expected: nil, }, + { + label: "ID Capability (invalid)", + value: cadence.NewIDCapability( + 4, + cadence.Address{0x1}, + cadence.IntType{}, + ), + expected: nil, + }, { label: "Function (invalid)", value: cadence.Function{}, @@ -2058,14 +2066,13 @@ func TestExportTypeValue(t *testing.T) { } -func TestExportStorageCapabilityValue(t *testing.T) { +func TestExportPathCapabilityValue(t *testing.T) { t.Parallel() t.Run("Int", func(t *testing.T) { - capability := interpreter.NewUnmeteredStorageCapabilityValue( - 3, + capability := interpreter.NewUnmeteredPathCapabilityValue( interpreter.AddressValue{0x1}, interpreter.PathValue{ Domain: common.PathDomainStorage, @@ -2082,8 +2089,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.NewStorageCapability( - 3, + expected := cadence.NewPathCapability( cadence.Address{0x1}, cadence.Path{ Domain: common.PathDomainStorage, @@ -2120,8 +2126,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { inter := newTestInterpreter(t) inter.Program = interpreter.ProgramFromChecker(checker) - capability := interpreter.NewUnmeteredStorageCapabilityValue( - 3, + capability := interpreter.NewUnmeteredPathCapabilityValue( interpreter.AddressValue{0x1}, interpreter.PathValue{ Domain: common.PathDomainStorage, @@ -2138,8 +2143,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.NewStorageCapability( - 3, + expected := cadence.NewPathCapability( cadence.Address{0x1}, cadence.Path{ Domain: common.PathDomainStorage, @@ -2157,8 +2161,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { t.Run("no borrow type", func(t *testing.T) { - capability := interpreter.NewUnmeteredStorageCapabilityValue( - 3, + capability := interpreter.NewUnmeteredPathCapabilityValue( interpreter.AddressValue{0x1}, interpreter.PathValue{ Domain: common.PathDomainStorage, @@ -2175,8 +2178,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { ) require.NoError(t, err) - expected := cadence.NewStorageCapability( - 3, + expected := cadence.NewPathCapability( cadence.Address{0x1}, cadence.Path{ Domain: common.PathDomainStorage, @@ -2189,6 +2191,113 @@ func TestExportStorageCapabilityValue(t *testing.T) { }) } +func TestExportIDCapabilityValue(t *testing.T) { + + t.Parallel() + + t.Run("Int", func(t *testing.T) { + + capability := interpreter.NewUnmeteredIDCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + interpreter.PrimitiveStaticTypeInt, + ) + + actual, err := exportValueWithInterpreter( + capability, + newTestInterpreter(t), + interpreter.EmptyLocationRange, + seenReferences{}, + ) + require.NoError(t, err) + + expected := cadence.NewIDCapability( + 3, + cadence.Address{0x1}, + cadence.IntType{}, + ) + + assert.Equal(t, expected, actual) + + }) + + t.Run("Struct", func(t *testing.T) { + + const code = ` + struct S {} + ` + program, err := parser.ParseProgram(nil, []byte(code), parser.Config{}) + require.NoError(t, err) + + checker, err := sema.NewChecker( + program, + TestLocation, + nil, + &sema.Config{ + AccessCheckMode: sema.AccessCheckModeNotSpecifiedUnrestricted, + }, + ) + require.NoError(t, err) + + err = checker.Check() + require.NoError(t, err) + + inter := newTestInterpreter(t) + inter.Program = interpreter.ProgramFromChecker(checker) + + capability := interpreter.NewUnmeteredIDCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + interpreter.NewCompositeStaticTypeComputeTypeID(inter, TestLocation, "S"), + ) + + actual, err := exportValueWithInterpreter( + capability, + inter, + interpreter.EmptyLocationRange, + seenReferences{}, + ) + require.NoError(t, err) + + expected := cadence.NewIDCapability( + 3, + cadence.Address{0x1}, + &cadence.StructType{ + QualifiedIdentifier: "S", + Location: TestLocation, + Fields: []cadence.Field{}, + }, + ) + + assert.Equal(t, expected, actual) + }) + + t.Run("no borrow type", func(t *testing.T) { + + capability := interpreter.NewUnmeteredIDCapabilityValue( + 3, + interpreter.AddressValue{0x1}, + nil, + ) + + actual, err := exportValueWithInterpreter( + capability, + newTestInterpreter(t), + interpreter.EmptyLocationRange, + seenReferences{}, + ) + require.NoError(t, err) + + expected := cadence.NewIDCapability( + 3, + cadence.Address{0x1}, + nil, + ) + + assert.Equal(t, expected, actual) + }) +} + func TestExportPathLinkValue(t *testing.T) { t.Parallel() @@ -4013,7 +4122,7 @@ func TestTypeValueImport(t *testing.T) { }) } -func TestStorageCapabilityValueImport(t *testing.T) { +func TestPathCapabilityValueImport(t *testing.T) { t.Parallel() @@ -4021,14 +4130,14 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() - capabilityValue := cadence.StorageCapability{ - BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, - Address: cadence.Address{0x1}, - Path: cadence.Path{ + capabilityValue := cadence.NewPathCapability( + cadence.Address{0x1}, + cadence.Path{ Domain: common.PathDomainPublic, Identifier: "foo", }, - } + &cadence.ReferenceType{Type: cadence.IntType{}}, + ) script := ` pub fun main(s: Capability<&Int>) { @@ -4045,7 +4154,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { runtimeInterface := &testRuntimeInterface{ log: func(s string) { - assert.Equal(t, "Capability<&Int>(id: 0, address: 0x0100000000000000, path: /public/foo)", s) + assert.Equal(t, "Capability<&Int>(address: 0x0100000000000000, path: /public/foo)", s) ok = true }, meterMemory: func(_ common.MemoryUsage) error { @@ -4075,8 +4184,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() - capabilityValue := cadence.NewStorageCapability( - 3, + capabilityValue := cadence.NewPathCapability( cadence.Address{0x1}, cadence.Path{ Domain: common.PathDomainPublic, @@ -4123,16 +4231,16 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() - capabilityValue := cadence.StorageCapability{ - Address: cadence.Address{0x1}, - Path: cadence.Path{ + capabilityValue := cadence.NewPathCapability( + cadence.Address{0x1}, + cadence.Path{ Domain: common.PathDomainPrivate, Identifier: "foo", }, - BorrowType: &cadence.ReferenceType{ + &cadence.ReferenceType{ Type: cadence.IntType{}, }, - } + ) script := ` pub fun main(s: Capability<&Int>) { @@ -4172,14 +4280,14 @@ func TestStorageCapabilityValueImport(t *testing.T) { t.Parallel() - capabilityValue := cadence.StorageCapability{ - BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, - Address: cadence.Address{0x1}, - Path: cadence.Path{ + capabilityValue := cadence.NewPathCapability( + cadence.Address{0x1}, + cadence.Path{ Domain: common.PathDomainStorage, Identifier: "foo", }, - } + &cadence.ReferenceType{Type: cadence.IntType{}}, + ) script := ` pub fun main(s: Capability<&Int>) { @@ -4192,8 +4300,6 @@ func TestStorageCapabilityValueImport(t *testing.T) { rt := newTestInterpreterRuntime() runtimeInterface := &testRuntimeInterface{ - log: func(s string) { - }, meterMemory: func(_ common.MemoryUsage) error { return nil }, @@ -4228,14 +4334,14 @@ func TestStorageCapabilityValueImport(t *testing.T) { Initializers: [][]cadence.Parameter{}, } - capabilityValue := cadence.StorageCapability{ - BorrowType: borrowType, - Address: cadence.Address{0x1}, - Path: cadence.Path{ + capabilityValue := cadence.NewPathCapability( + cadence.Address{0x1}, + cadence.Path{ Domain: common.PathDomainPublic, Identifier: "foo", }, - } + borrowType, + ) script := ` pub fun main(s: Capability) { @@ -4248,8 +4354,150 @@ func TestStorageCapabilityValueImport(t *testing.T) { rt := newTestInterpreterRuntime() runtimeInterface := &testRuntimeInterface{ - log: func(s string) { + meterMemory: func(_ common.MemoryUsage) error { + return nil }, + } + runtimeInterface.decodeArgument = func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(runtimeInterface, b) + } + + _, err = rt.ExecuteScript( + Script{ + Source: []byte(script), + Arguments: [][]byte{encodedArg}, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + RequireError(t, err) + assertUserError(t, err) + }) +} + +func TestIDCapabilityValueImport(t *testing.T) { + + t.Parallel() + + t.Run("Capability<&Int>", func(t *testing.T) { + + t.Parallel() + + capabilityValue := cadence.NewIDCapability( + 42, + cadence.Address{0x1}, + &cadence.ReferenceType{Type: cadence.IntType{}}, + ) + + script := ` + pub fun main(s: Capability<&Int>) { + } + ` + + encodedArg, err := json.Encode(capabilityValue) + require.NoError(t, err) + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + meterMemory: func(_ common.MemoryUsage) error { + return nil + }, + } + runtimeInterface.decodeArgument = func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(runtimeInterface, b) + } + + _, err = rt.ExecuteScript( + Script{ + Source: []byte(script), + Arguments: [][]byte{encodedArg}, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + RequireError(t, err) + assertUserError(t, err) + }) + + t.Run("Capability", func(t *testing.T) { + + t.Parallel() + + capabilityValue := cadence.NewIDCapability( + 3, + cadence.Address{0x1}, + cadence.IntType{}, + ) + + script := ` + pub fun main(s: Capability) { + } + ` + + encodedArg, err := json.Encode(capabilityValue) + require.NoError(t, err) + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + meterMemory: func(_ common.MemoryUsage) error { + return nil + }, + } + runtimeInterface.decodeArgument = func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(runtimeInterface, b) + } + + _, err = rt.ExecuteScript( + Script{ + Source: []byte(script), + Arguments: [][]byte{encodedArg}, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + RequireError(t, err) + assertUserError(t, err) + }) + + t.Run("missing struct", func(t *testing.T) { + + t.Parallel() + + borrowType := &cadence.StructType{ + QualifiedIdentifier: "S", + Location: TestLocation, + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + capabilityValue := cadence.NewIDCapability( + 42, + cadence.Address{0x1}, + borrowType, + ) + + script := ` + pub fun main(s: Capability) { + } + ` + + encodedArg, err := json.Encode(capabilityValue) + require.NoError(t, err) + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ meterMemory: func(_ common.MemoryUsage) error { return nil }, diff --git a/runtime/format/capability.go b/runtime/format/capability.go index de3d110eaf..31923f00cc 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -22,21 +22,34 @@ import ( "fmt" ) -func StorageCapability(borrowType string, id string, address string, path string) string { +func PathCapability(borrowType string, address string, path string) string { var typeArgument string if borrowType != "" { typeArgument = fmt.Sprintf("<%s>", borrowType) } return fmt.Sprintf( - "Capability%s(id: %s, address: %s, path: %s)", + "Capability%s(address: %s, path: %s)", typeArgument, - id, address, path, ) } +func IDCapability(borrowType string, address string, id string) string { + var typeArgument string + if borrowType != "" { + typeArgument = fmt.Sprintf("<%s>", borrowType) + } + + return fmt.Sprintf( + "Capability%s(address: %s, id: %s)", + typeArgument, + address, + id, + ) +} + func StorageCapabilityController(borrowType string, capabilityID string, target string) string { return fmt.Sprintf( "StorageCapabilityController(borrowType: %s, capabilityID: %s, target: %s)", diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index 7349b3c08f..3fe52be2ac 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -434,53 +434,54 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { }, // Verify Capability and its composing values, Path and Type. + // NOTE: ID Capability is not importable, so no test necessary. { TypeName: "Capability", - MemoryKind: common.MemoryKindStorageCapabilityValue, + MemoryKind: common.MemoryKindPathCapabilityValue, Weight: 1, - TypeInstance: cadence.StorageCapability{ - Path: cadence.Path{ + TypeInstance: cadence.NewPathCapability( + cadence.Address{}, + cadence.Path{ Domain: common.PathDomainPublic, Identifier: "foobarrington", }, - Address: cadence.Address{}, - BorrowType: &cadence.ReferenceType{ + &cadence.ReferenceType{ Authorized: true, Type: cadence.AnyType{}, }, - }, + ), }, { TypeName: "Capability", MemoryKind: common.MemoryKindPathValue, Weight: 1, - TypeInstance: cadence.StorageCapability{ - Path: cadence.Path{ + TypeInstance: cadence.NewPathCapability( + cadence.Address{}, + cadence.Path{ Domain: common.PathDomainPublic, Identifier: "foobarrington", }, - Address: cadence.Address{}, - BorrowType: &cadence.ReferenceType{ + &cadence.ReferenceType{ Authorized: true, Type: cadence.AnyType{}, }, - }, + ), }, { TypeName: "Capability", MemoryKind: common.MemoryKindRawString, Weight: (1 + 13) + (1 + 13), - TypeInstance: cadence.StorageCapability{ - Path: cadence.Path{ + TypeInstance: cadence.NewPathCapability( + cadence.Address{}, + cadence.Path{ Domain: common.PathDomainPublic, Identifier: "foobarrington", }, - Address: cadence.Address{}, - BorrowType: &cadence.ReferenceType{ + &cadence.ReferenceType{ Authorized: true, Type: cadence.AnyType{}, }, - }, + ), }, // Verify Optional and its composing type diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 039df4fe3b..b28215794e 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -303,8 +303,11 @@ func (d StorableDecoder) decodeStorable() (atree.Storable, error) { case CBORTagPathValue: storable, err = d.decodePath() - case CBORTagStorageCapabilityValue: - storable, err = d.decodeStorageCapability() + case CBORTagPathCapabilityValue: + storable, err = d.decodePathCapability() + + case CBORTagIDCapabilityValue: + storable, err = d.decodeIDCapability() case CBORTagPathLinkValue: storable, err = d.decodePathLink() @@ -873,9 +876,9 @@ func (d StorableDecoder) decodePath() (PathValue, error) { ), nil } -func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, error) { +func (d StorableDecoder) decodePathCapability() (*PathCapabilityValue, error) { - const expectedLength = encodedStorageCapabilityValueLength + const expectedLength = encodedPathCapabilityValueLength size, err := d.decoder.DecodeArrayHead() if err != nil { @@ -899,7 +902,7 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err // address - // Decode address at array index encodedStorageCapabilityValueAddressFieldKey + // Decode address at array index encodedPathCapabilityValueAddressFieldKey var num uint64 num, err = d.decoder.DecodeTagNumber() if err != nil { @@ -924,7 +927,7 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err // path - // Decode path at array index encodedStorageCapabilityValuePathFieldKey + // Decode path at array index encodedPathCapabilityValuePathFieldKey pathStorable, err := d.decodeStorable() if err != nil { return nil, errors.NewUnexpectedError("invalid capability path: %w", err) @@ -934,7 +937,7 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err return nil, errors.NewUnexpectedError("invalid capability path: invalid type %T", pathValue) } - // Decode borrow type at array index encodedStorageCapabilityValueBorrowTypeFieldKey + // Decode borrow type at array index encodedPathCapabilityValueBorrowTypeFieldKey // borrow type (optional, for backwards compatibility) // Capabilities used to be untyped, i.e. they didn't have a borrow type. @@ -951,12 +954,68 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err if _, ok := err.(*cbor.WrongTypeError); ok { borrowType, err = d.DecodeStaticType() } - if err != nil { return nil, errors.NewUnexpectedError("invalid capability borrow type encoding: %w", err) } - // Decode ID at array index encodedStorageCapabilityValueIDFieldKey + return NewPathCapabilityValue( + d.memoryGauge, + address, + pathValue, + borrowType, + ), nil +} + +func (d StorableDecoder) decodeIDCapability() (*IDCapabilityValue, error) { + + const expectedLength = encodedIDCapabilityValueLength + + size, err := d.decoder.DecodeArrayHead() + if err != nil { + if e, ok := err.(*cbor.WrongTypeError); ok { + return nil, errors.NewUnexpectedError( + "invalid capability encoding: expected [%d]any, got %s", + expectedLength, + e.ActualType.String(), + ) + } + return nil, err + } + + if size != expectedLength { + return nil, errors.NewUnexpectedError( + "invalid capability encoding: expected [%d]any, got [%d]any", + expectedLength, + size, + ) + } + + // address + + // Decode address at array index encodedIDCapabilityValueAddressFieldKey + var num uint64 + num, err = d.decoder.DecodeTagNumber() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid capability address: %w", + err, + ) + } + if num != CBORTagAddressValue { + return nil, errors.NewUnexpectedError( + "invalid capability address: wrong tag %d", + num, + ) + } + address, err := d.decodeAddress() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid capability address: %w", + err, + ) + } + + // Decode ID at array index encodedIDCapabilityValueIDFieldKey id, err := d.decoder.DecodeUint64() if err != nil { @@ -966,11 +1025,32 @@ func (d StorableDecoder) decodeStorageCapability() (*StorageCapabilityValue, err ) } - return NewStorageCapabilityValue( + // Decode borrow type at array index encodedIDCapabilityValueBorrowTypeFieldKey + + // borrow type (optional, for backwards compatibility) + // Capabilities used to be untyped, i.e. they didn't have a borrow type. + // Later an optional type parameter, the borrow type, was added to it, + // which specifies as what type the capability should be borrowed. + // + // The decoding must be backwards-compatible and support both capability values + // with a borrow type and ones without + + var borrowType StaticType + + // Optional borrow type can be CBOR nil. + err = d.decoder.DecodeNil() + if _, ok := err.(*cbor.WrongTypeError); ok { + borrowType, err = d.DecodeStaticType() + } + + if err != nil { + return nil, errors.NewUnexpectedError("invalid capability borrow type encoding: %w", err) + } + + return NewIDCapabilityValue( d.memoryGauge, UInt64Value(id), address, - pathValue, borrowType, ), nil } @@ -1173,27 +1253,32 @@ func (d StorableDecoder) decodePublishedValue() (*PublishedValue, error) { return nil, errors.NewUnexpectedError("invalid published value recipient encoding: %w", err) } if num != CBORTagAddressValue { - return nil, errors.NewUnexpectedError("invalid published value recipient encoding: expected CBOR tag %d, got %d", CBORTagAddressValue, num) + return nil, errors.NewUnexpectedError( + "invalid published value recipient encoding: expected CBOR tag %d, got %d", + CBORTagAddressValue, + num, + ) } addressValue, err := d.decodeAddress() if err != nil { return nil, errors.NewUnexpectedError("invalid published value recipient encoding: %w", err) } - // Decode address at array index encodedPublishedValueValueFieldKey - num, err = d.decoder.DecodeTagNumber() + // Decode value at array index encodedPublishedValueValueFieldKey + value, err := d.decodeStorable() if err != nil { - return nil, errors.NewUnexpectedError("invalid published value recipient encoding: %w", err) - } - if num != CBORTagStorageCapabilityValue { - return nil, errors.NewUnexpectedError("invalid published value recipient encoding: expected CBOR tag %d, got %d", CBORTagStorageCapabilityValue, num) + return nil, errors.NewUnexpectedError("invalid published value value encoding: %w", err) } - value, err := d.decodeStorageCapability() - if err != nil { - return nil, errors.NewUnexpectedError("invalid published value encoding: %w", err) + + capabilityValue, ok := value.(CapabilityValue) + if !ok { + return nil, errors.NewUnexpectedError( + "invalid published value value encoding: expected capability, got %T", + value, + ) } - return NewPublishedValue(d.memoryGauge, addressValue, value), nil + return NewPublishedValue(d.memoryGauge, addressValue, capabilityValue), nil } func (d StorableDecoder) decodeType() (TypeValue, error) { diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 6d9995ddb1..efdd8e6fb1 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -195,14 +195,14 @@ const ( // Storage CBORTagPathValue - CBORTagStorageCapabilityValue + CBORTagPathCapabilityValue _ // DO NOT REPLACE! used to be used for storage references CBORTagPathLinkValue CBORTagPublishedValue CBORTagAccountLinkValue CBORTagStorageCapabilityControllerValue CBORTagAccountCapabilityControllerValue - _ + CBORTagIDCapabilityValue _ _ _ @@ -727,61 +727,104 @@ func (v PathValue) Encode(e *atree.Encoder) error { // NOTE: NEVER change, only add/increment; ensure uint64 const ( - // encodedStorageCapabilityValueAddressFieldKey uint64 = 0 - // encodedStorageCapabilityValuePathFieldKey uint64 = 1 - // encodedStorageCapabilityValueBorrowTypeFieldKey uint64 = 2 - // encodedStorageCapabilityValueIDFieldKey uint64 = 3 + // encodedPathCapabilityValueAddressFieldKey uint64 = 0 + // encodedPathCapabilityValuePathFieldKey uint64 = 1 + // encodedPathCapabilityValueBorrowTypeFieldKey uint64 = 2 // !!! *WARNING* !!! // - // encodedStorageCapabilityValueLength MUST be updated when new element is added. + // encodedPathCapabilityValueLength MUST be updated when new element is added. // It is used to verify encoded capability length during decoding. - encodedStorageCapabilityValueLength = 4 + encodedPathCapabilityValueLength = 3 ) -// Encode encodes CapabilityStorable as +// Encode encodes PathCapabilityValue as // // cbor.Tag{ -// Number: CBORTagStorageCapabilityValue, +// Number: CBORTagPathCapabilityValue, // Content: []any{ -// encodedStorageCapabilityValueAddressFieldKey: AddressValue(v.Address), -// encodedStorageCapabilityValuePathFieldKey: PathValue(v.Path), -// encodedStorageCapabilityValueBorrowTypeFieldKey: StaticType(v.BorrowType), -// encodedStorageCapabilityValueIDFieldKey: v.ID, +// encodedPathCapabilityValueAddressFieldKey: AddressValue(v.Address), +// encodedPathCapabilityValuePathFieldKey: PathValue(v.Path), +// encodedPathCapabilityValueBorrowTypeFieldKey: StaticType(v.BorrowType), // }, // } -func (v *StorageCapabilityValue) Encode(e *atree.Encoder) error { +func (v *PathCapabilityValue) Encode(e *atree.Encoder) error { // Encode tag number and array head err := e.CBOR.EncodeRawBytes([]byte{ // tag number - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, }) if err != nil { return err } - // Encode address at array index encodedStorageCapabilityValueAddressFieldKey + // Encode address at array index encodedPathCapabilityValueAddressFieldKey err = v.Address.Encode(e) if err != nil { return err } - // Encode path at array index encodedStorageCapabilityValuePathFieldKey + // Encode path at array index encodedPathCapabilityValuePathFieldKey err = v.Path.Encode(e) if err != nil { return err } - // Encode borrow type at array index encodedStorageCapabilityValueBorrowTypeFieldKey - err = EncodeStaticType(e.CBOR, v.BorrowType) + // Encode borrow type at array index encodedPathCapabilityValueBorrowTypeFieldKey + return EncodeStaticType(e.CBOR, v.BorrowType) +} + +// NOTE: NEVER change, only add/increment; ensure uint64 +const ( + // encodedIDCapabilityValueAddressFieldKey uint64 = 0 + // encodedIDCapabilityValueIDFieldKey uint64 = 1 + // encodedIDCapabilityValueBorrowTypeFieldKey uint64 = 2 + + // !!! *WARNING* !!! + // + // encodedIDCapabilityValueLength MUST be updated when new element is added. + // It is used to verify encoded capability length during decoding. + encodedIDCapabilityValueLength = 3 +) + +// Encode encodes IDCapabilityValue as +// +// cbor.Tag{ +// Number: CBORTagIDCapabilityValue, +// Content: []any{ +// encodedIDCapabilityValueAddressFieldKey: AddressValue(v.Address), +// encodedIDCapabilityValueIDFieldKey: v.ID, +// encodedIDCapabilityValueBorrowTypeFieldKey: StaticType(v.BorrowType), +// }, +// } +func (v *IDCapabilityValue) Encode(e *atree.Encoder) error { + // Encode tag number and array head + err := e.CBOR.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagIDCapabilityValue, + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + + // Encode address at array index encodedIDCapabilityValueAddressFieldKey + err = v.Address.Encode(e) + if err != nil { + return err + } + + // Encode ID at array index encodedIDCapabilityValueIDFieldKey + err = e.CBOR.EncodeUint64(uint64(v.ID)) if err != nil { return err } - // Encode ID at array index encodedStorageCapabilityValueIDFieldKey - return e.CBOR.EncodeUint64(uint64(v.ID)) + // Encode borrow type at array index encodedIDCapabilityValueBorrowTypeFieldKey + return EncodeStaticType(e.CBOR, v.BorrowType) } // NOTE: NEVER change, only add/increment; ensure uint64 diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 53eb4c02eb..c6d5e6b49b 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3009,16 +3009,15 @@ func TestEncodeDecodePathValue(t *testing.T) { }) } -func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { +func TestEncodeDecodePathCapabilityValue(t *testing.T) { t.Parallel() - t.Run("private path, untyped capability, new format", func(t *testing.T) { + t.Run("private path, untyped capability", func(t *testing.T) { t.Parallel() - value := NewUnmeteredStorageCapabilityValue( - 4, + value := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x2}), privatePathValue, nil, @@ -3026,9 +3025,9 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { encoded := []byte{ // tag - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3047,8 +3046,6 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x66, 0x6f, 0x6f, // nil 0xf6, - // positive integer 4 - 0x4, } testEncodeDecode(t, @@ -3063,8 +3060,7 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := NewUnmeteredStorageCapabilityValue( - 4, + value := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x2}), privatePathValue, PrimitiveStaticTypeBool, @@ -3072,9 +3068,9 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { encoded := []byte{ // tag - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3095,8 +3091,6 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0xd8, CBORTagPrimitiveStaticType, // bool 0x6, - // positive integer 4 - 0x4, } testEncodeDecode(t, @@ -3107,12 +3101,11 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { ) }) - t.Run("public path, untyped capability, new format", func(t *testing.T) { + t.Run("public path, untyped capability", func(t *testing.T) { t.Parallel() - value := NewUnmeteredStorageCapabilityValue( - 4, + value := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x3}), publicPathValue, nil, @@ -3120,9 +3113,9 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { encoded := []byte{ // tag - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3141,8 +3134,6 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x62, 0x61, 0x72, // nil 0xf6, - // positive integer 4 - 0x4, } testEncodeDecode(t, @@ -3158,8 +3149,7 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - value := NewUnmeteredStorageCapabilityValue( - 4, + value := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x3}), publicPathValue, PrimitiveStaticTypeBool, @@ -3167,9 +3157,9 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { encoded := []byte{ // tag - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3190,8 +3180,6 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0xd8, CBORTagPrimitiveStaticType, // bool 0x6, - // positive integer 4 - 0x4, } testEncodeDecode(t, @@ -3207,8 +3195,7 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { t.Parallel() - capabilityValue := NewUnmeteredStorageCapabilityValue( - 4, + capabilityValue := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x3}), publicPathValue, PrimitiveStaticTypePublicAccount, @@ -3216,9 +3203,9 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { encoded := []byte{ // tag - 0xd8, CBORTagStorageCapabilityValue, - // array, 4 items follow - 0x84, + 0xd8, CBORTagPathCapabilityValue, + // array, 3 items follow + 0x83, // tag for address 0xd8, CBORTagAddressValue, // byte sequence, length 1 @@ -3241,8 +3228,6 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { 0x18, // public account (tag) 0x5b, - // positive integer 4 - 0x4, } testEncodeDecode(t, @@ -3278,8 +3263,7 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { } } - expected := NewUnmeteredStorageCapabilityValue( - 4, + expected := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x3}), path, nil, @@ -3325,8 +3309,7 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { } } - expected := NewUnmeteredStorageCapabilityValue( - 4, + expected := NewUnmeteredPathCapabilityValue( NewUnmeteredAddressValueFromBytes([]byte{0x3}), path, nil, @@ -3346,6 +3329,118 @@ func TestEncodeDecodeStorageCapabilityValue(t *testing.T) { }) } +func TestEncodeDecodeIDCapabilityValue(t *testing.T) { + + t.Parallel() + + t.Run("untyped capability", func(t *testing.T) { + + t.Parallel() + + value := NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), + nil, + ) + + encoded := []byte{ + // tag + 0xd8, CBORTagIDCapabilityValue, + // array, 3 items follow + 0x83, + // tag for address + 0xd8, CBORTagAddressValue, + // byte sequence, length 1 + 0x41, + // address + 0x02, + // positive integer 4 + 0x4, + // nil + 0xf6, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("typed capability", func(t *testing.T) { + + t.Parallel() + + value := NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), + PrimitiveStaticTypeBool, + ) + + encoded := []byte{ + // tag + 0xd8, CBORTagIDCapabilityValue, + // array, 3 items follow + 0x83, + // tag for address + 0xd8, CBORTagAddressValue, + // byte sequence, length 1 + 0x41, + // address + 0x02, + // positive integer 4 + 0x4, + // tag for borrow type + 0xd8, CBORTagPrimitiveStaticType, + // bool + 0x6, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("larger than max inline size", func(t *testing.T) { + + t.Parallel() + + // Generate an arbitrary, large static type + maxInlineElementSize := atree.MaxInlineArrayElementSize + var borrowType StaticType = PrimitiveStaticTypeNever + + for i := uint64(0); i < maxInlineElementSize; i++ { + borrowType = OptionalStaticType{ + Type: borrowType, + } + } + + expected := NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x3}), + borrowType, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: expected, + maxInlineElementSize: maxInlineElementSize, + encoded: []byte{ + // tag + 0xd8, atree.CBORTagStorageID, + + // storage ID + 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + }, + }, + ) + }) +} + func TestEncodeDecodePathLinkValue(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index f8b64461b5..036e3dc9c7 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -30,9 +30,8 @@ import ( "github.com/onflow/atree" "go.opentelemetry.io/otel/attribute" - "github.com/onflow/cadence/runtime/activations" - "github.com/onflow/cadence/fixedpoint" + "github.com/onflow/cadence/runtime/activations" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" @@ -3746,10 +3745,8 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu return NewSomeValueNonCopying( interpreter, - NewStorageCapabilityValue( + NewPathCapabilityValue( interpreter, - // TODO: - TodoCapabilityID, addressValue, newCapabilityPath, borrowStaticType, @@ -3760,7 +3757,7 @@ func (interpreter *Interpreter) authAccountLinkFunction(addressValue AddressValu ) } -var authAccountReferenceStaticType = ReferenceStaticType{ +var AuthAccountReferenceStaticType = ReferenceStaticType{ BorrowedType: PrimitiveStaticTypeAuthAccount, ReferencedType: PrimitiveStaticTypeAuthAccount, } @@ -3774,7 +3771,7 @@ var authAccountReferenceStaticType = ReferenceStaticType{ // an interpreter.AccountLink is stored in the account. // // In both cases, when acquiring a capability, e.g. using getCapability, -// a StorageCapabilityValue is returned. +// a PathCapabilityValue is returned. // This is because in both cases, we are looking up a path in an account. // Depending on what is stored in the path, PathLink or AccountLink, // we return a respective reference value, a StorageReferenceValue for PathLink @@ -3844,13 +3841,11 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr return NewSomeValueNonCopying( interpreter, - NewStorageCapabilityValue( + NewPathCapabilityValue( interpreter, - // TODO: - TodoCapabilityID, addressValue, newCapabilityPath, - authAccountReferenceStaticType, + AuthAccountReferenceStaticType, ), ) @@ -3936,14 +3931,14 @@ func (interpreter *Interpreter) authAccountUnlinkFunction(addressValue AddressVa ) } -func (interpreter *Interpreter) BorrowCapability( +func (interpreter *Interpreter) BorrowPathCapability( address common.Address, pathValue PathValue, borrowType *sema.ReferenceType, locationRange LocationRange, ) ReferenceValue { target, authorized, err := - interpreter.GetStorageCapabilityFinalTarget( + interpreter.GetPathCapabilityFinalTarget( address, pathValue, borrowType, @@ -3996,7 +3991,7 @@ func (interpreter *Interpreter) BorrowCapability( } } -func (interpreter *Interpreter) storageCapabilityBorrowFunction( +func (interpreter *Interpreter) pathCapabilityBorrowFunction( addressValue AddressValue, pathValue PathValue, borrowType *sema.ReferenceType, @@ -4029,7 +4024,7 @@ func (interpreter *Interpreter) storageCapabilityBorrowFunction( panic(errors.NewUnreachableError()) } - reference := interpreter.BorrowCapability(address, pathValue, borrowType, invocation.LocationRange) + reference := interpreter.BorrowPathCapability(address, pathValue, borrowType, invocation.LocationRange) if reference == nil { return Nil } @@ -4038,7 +4033,7 @@ func (interpreter *Interpreter) storageCapabilityBorrowFunction( ) } -func (interpreter *Interpreter) storageCapabilityCheckFunction( +func (interpreter *Interpreter) pathCapabilityCheckFunction( addressValue AddressValue, pathValue PathValue, borrowType *sema.ReferenceType, @@ -4071,7 +4066,7 @@ func (interpreter *Interpreter) storageCapabilityCheckFunction( } target, authorized, err := - interpreter.GetStorageCapabilityFinalTarget( + interpreter.GetPathCapabilityFinalTarget( address, pathValue, borrowType, @@ -4115,7 +4110,7 @@ func (interpreter *Interpreter) storageCapabilityCheckFunction( ) } -func (interpreter *Interpreter) GetStorageCapabilityFinalTarget( +func (interpreter *Interpreter) GetPathCapabilityFinalTarget( address common.Address, path PathValue, wantedBorrowType *sema.ReferenceType, @@ -4168,7 +4163,7 @@ func (interpreter *Interpreter) GetStorageCapabilityFinalTarget( case AccountLinkValue: if !interpreter.IsSubTypeOfSemaType( - authAccountReferenceStaticType, + AuthAccountReferenceStaticType, wantedBorrowType, ) { return nil, false, nil diff --git a/runtime/interpreter/simplecompositevalue.go b/runtime/interpreter/simplecompositevalue.go index 9f3476928b..0a4b637a1e 100644 --- a/runtime/interpreter/simplecompositevalue.go +++ b/runtime/interpreter/simplecompositevalue.go @@ -70,7 +70,7 @@ func NewSimpleCompositeValue( } } -func (*SimpleCompositeValue) IsValue() {} +func (*SimpleCompositeValue) isValue() {} func (v *SimpleCompositeValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitSimpleCompositeValue(interpreter, v) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 2ff8a40f8f..ae3fe5444a 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -106,7 +106,7 @@ type Value interface { // Stringer provides `func String() string` // NOTE: important, error messages rely on values to implement String fmt.Stringer - IsValue() + isValue() Accept(interpreter *Interpreter, visitor Visitor) Walk(interpreter *Interpreter, walkChild func(Value)) StaticType(interpreter *Interpreter) StaticType @@ -211,6 +211,28 @@ type ReferenceTrackedResourceKindedValue interface { StorageID() atree.StorageID } +// ContractValue is the value of a contract. +// Under normal circumstances, a contract value is always a CompositeValue. +// However, in the test framework, an imported contract is constructed via a constructor function. +// Hence, during tests, the value is a HostFunctionValue. +type ContractValue interface { + Value + SetNestedVariables(variables map[string]*Variable) +} + +// CapabilityValue +type CapabilityValue interface { + atree.Storable + EquatableValue + isCapabilityValue() +} + +// LinkValue +type LinkValue interface { + Value + isLinkValue() +} + // IterableValue is a value which can be iterated over, e.g. with a for-loop type IterableValue interface { Value @@ -288,7 +310,7 @@ func NewTypeValue( return NewUnmeteredTypeValue(staticType) } -func (TypeValue) IsValue() {} +func (TypeValue) isValue() {} func (v TypeValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitTypeValue(interpreter, v) @@ -498,7 +520,7 @@ var _ Value = VoidValue{} var _ atree.Storable = VoidValue{} var _ EquatableValue = VoidValue{} -func (VoidValue) IsValue() {} +func (VoidValue) isValue() {} func (v VoidValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitVoidValue(interpreter, v) @@ -606,7 +628,7 @@ func AsBoolValue(v bool) BoolValue { return FalseValue } -func (BoolValue) IsValue() {} +func (BoolValue) isValue() {} func (v BoolValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitBoolValue(interpreter, v) @@ -751,7 +773,7 @@ var _ EquatableValue = CharacterValue("a") var _ HashableValue = CharacterValue("a") var _ MemberAccessibleValue = CharacterValue("a") -func (CharacterValue) IsValue() {} +func (CharacterValue) isValue() {} func (v CharacterValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitCharacterValue(interpreter, v) @@ -944,7 +966,7 @@ func (v *StringValue) prepareGraphemes() { } } -func (*StringValue) IsValue() {} +func (*StringValue) isValue() {} func (v *StringValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitStringValue(interpreter, v) @@ -1530,7 +1552,7 @@ var _ MemberAccessibleValue = &ArrayValue{} var _ ReferenceTrackedResourceKindedValue = &ArrayValue{} var _ IterableValue = &ArrayValue{} -func (*ArrayValue) IsValue() {} +func (*ArrayValue) isValue() {} func (v *ArrayValue) Accept(interpreter *Interpreter, visitor Visitor) { descend := visitor.VisitArrayValue(interpreter, v) @@ -2912,7 +2934,7 @@ var _ EquatableValue = IntValue{} var _ HashableValue = IntValue{} var _ MemberAccessibleValue = IntValue{} -func (IntValue) IsValue() {} +func (IntValue) isValue() {} func (v IntValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitIntValue(interpreter, v) @@ -3439,7 +3461,7 @@ var _ IntegerValue = Int8Value(0) var _ EquatableValue = Int8Value(0) var _ HashableValue = Int8Value(0) -func (Int8Value) IsValue() {} +func (Int8Value) isValue() {} func (v Int8Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt8Value(interpreter, v) @@ -4026,7 +4048,7 @@ var _ EquatableValue = Int16Value(0) var _ HashableValue = Int16Value(0) var _ MemberAccessibleValue = Int16Value(0) -func (Int16Value) IsValue() {} +func (Int16Value) isValue() {} func (v Int16Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt16Value(interpreter, v) @@ -4614,7 +4636,7 @@ var _ EquatableValue = Int32Value(0) var _ HashableValue = Int32Value(0) var _ MemberAccessibleValue = Int32Value(0) -func (Int32Value) IsValue() {} +func (Int32Value) isValue() {} func (v Int32Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt32Value(interpreter, v) @@ -5200,7 +5222,7 @@ var _ EquatableValue = Int64Value(0) var _ HashableValue = Int64Value(0) var _ MemberAccessibleValue = Int64Value(0) -func (Int64Value) IsValue() {} +func (Int64Value) isValue() {} func (v Int64Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt64Value(interpreter, v) @@ -5801,7 +5823,7 @@ var _ EquatableValue = Int128Value{} var _ HashableValue = Int128Value{} var _ MemberAccessibleValue = Int128Value{} -func (Int128Value) IsValue() {} +func (Int128Value) isValue() {} func (v Int128Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt128Value(interpreter, v) @@ -6489,7 +6511,7 @@ var _ EquatableValue = Int256Value{} var _ HashableValue = Int256Value{} var _ MemberAccessibleValue = Int256Value{} -func (Int256Value) IsValue() {} +func (Int256Value) isValue() {} func (v Int256Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitInt256Value(interpreter, v) @@ -7211,7 +7233,7 @@ var _ EquatableValue = UIntValue{} var _ HashableValue = UIntValue{} var _ MemberAccessibleValue = UIntValue{} -func (UIntValue) IsValue() {} +func (UIntValue) isValue() {} func (v UIntValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUIntValue(interpreter, v) @@ -7746,7 +7768,7 @@ func NewUnmeteredUInt8Value(value uint8) UInt8Value { return UInt8Value(value) } -func (UInt8Value) IsValue() {} +func (UInt8Value) isValue() {} func (v UInt8Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt8Value(interpreter, v) @@ -8294,7 +8316,7 @@ func NewUnmeteredUInt16Value(value uint16) UInt16Value { return UInt16Value(value) } -func (UInt16Value) IsValue() {} +func (UInt16Value) isValue() {} func (v UInt16Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt16Value(interpreter, v) @@ -8805,7 +8827,7 @@ var _ EquatableValue = UInt32Value(0) var _ HashableValue = UInt32Value(0) var _ MemberAccessibleValue = UInt32Value(0) -func (UInt32Value) IsValue() {} +func (UInt32Value) isValue() {} func (v UInt32Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt32Value(interpreter, v) @@ -9323,7 +9345,7 @@ func NewUnmeteredUInt64Value(value uint64) UInt64Value { return UInt64Value(value) } -func (UInt64Value) IsValue() {} +func (UInt64Value) isValue() {} func (v UInt64Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt64Value(interpreter, v) @@ -9873,7 +9895,7 @@ var _ EquatableValue = UInt128Value{} var _ HashableValue = UInt128Value{} var _ MemberAccessibleValue = UInt128Value{} -func (UInt128Value) IsValue() {} +func (UInt128Value) isValue() {} func (v UInt128Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt128Value(interpreter, v) @@ -10504,7 +10526,7 @@ var _ EquatableValue = UInt256Value{} var _ HashableValue = UInt256Value{} var _ MemberAccessibleValue = UInt256Value{} -func (UInt256Value) IsValue() {} +func (UInt256Value) isValue() {} func (v UInt256Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUInt256Value(interpreter, v) @@ -11119,7 +11141,7 @@ func NewUnmeteredWord8Value(value uint8) Word8Value { return Word8Value(value) } -func (Word8Value) IsValue() {} +func (Word8Value) isValue() {} func (v Word8Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitWord8Value(interpreter, v) @@ -11536,7 +11558,7 @@ func NewUnmeteredWord16Value(value uint16) Word16Value { return Word16Value(value) } -func (Word16Value) IsValue() {} +func (Word16Value) isValue() {} func (v Word16Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitWord16Value(interpreter, v) @@ -11954,7 +11976,7 @@ func NewUnmeteredWord32Value(value uint32) Word32Value { return Word32Value(value) } -func (Word32Value) IsValue() {} +func (Word32Value) isValue() {} func (v Word32Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitWord32Value(interpreter, v) @@ -12379,7 +12401,7 @@ func NewUnmeteredWord64Value(value uint64) Word64Value { // call ToBigInt instead of ToInt. var _ BigNumberValue = Word64Value(0) -func (Word64Value) IsValue() {} +func (Word64Value) isValue() {} func (v Word64Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitWord64Value(interpreter, v) @@ -12841,7 +12863,7 @@ var _ EquatableValue = Fix64Value(0) var _ HashableValue = Fix64Value(0) var _ MemberAccessibleValue = Fix64Value(0) -func (Fix64Value) IsValue() {} +func (Fix64Value) isValue() {} func (v Fix64Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitFix64Value(interpreter, v) @@ -13376,7 +13398,7 @@ var _ EquatableValue = UFix64Value(0) var _ HashableValue = UFix64Value(0) var _ MemberAccessibleValue = UFix64Value(0) -func (UFix64Value) IsValue() {} +func (UFix64Value) isValue() {} func (v UFix64Value) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitUFix64Value(interpreter, v) @@ -14000,7 +14022,7 @@ var _ MemberAccessibleValue = &CompositeValue{} var _ ReferenceTrackedResourceKindedValue = &CompositeValue{} var _ ContractValue = &CompositeValue{} -func (*CompositeValue) IsValue() {} +func (*CompositeValue) isValue() {} func (v *CompositeValue) Accept(interpreter *Interpreter, visitor Visitor) { descend := visitor.VisitCompositeValue(interpreter, v) @@ -15392,7 +15414,7 @@ var _ ValueIndexableValue = &DictionaryValue{} var _ MemberAccessibleValue = &DictionaryValue{} var _ ReferenceTrackedResourceKindedValue = &DictionaryValue{} -func (*DictionaryValue) IsValue() {} +func (*DictionaryValue) isValue() {} func (v *DictionaryValue) Accept(interpreter *Interpreter, visitor Visitor) { descend := visitor.VisitDictionaryValue(interpreter, v) @@ -16495,7 +16517,7 @@ var _ EquatableValue = NilValue{} var _ MemberAccessibleValue = NilValue{} var _ OptionalValue = NilValue{} -func (NilValue) IsValue() {} +func (NilValue) isValue() {} func (v NilValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitNilValue(interpreter, v) @@ -16520,8 +16542,8 @@ func (NilValue) isOptionalValue() {} func (NilValue) forEach(_ func(Value)) {} -func (n NilValue) fmap(inter *Interpreter, f func(Value) Value) OptionalValue { - return n +func (v NilValue) fmap(_ *Interpreter, _ func(Value) Value) OptionalValue { + return v } func (NilValue) IsDestroyed() bool { @@ -16665,7 +16687,7 @@ var _ EquatableValue = &SomeValue{} var _ MemberAccessibleValue = &SomeValue{} var _ OptionalValue = &SomeValue{} -func (*SomeValue) IsValue() {} +func (*SomeValue) isValue() {} func (v *SomeValue) Accept(interpreter *Interpreter, visitor Visitor) { descend := visitor.VisitSomeValue(interpreter, v) @@ -17045,7 +17067,7 @@ func NewStorageReferenceValue( ) } -func (*StorageReferenceValue) IsValue() {} +func (*StorageReferenceValue) isValue() {} func (v *StorageReferenceValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitStorageReferenceValue(interpreter, v) @@ -17391,7 +17413,7 @@ func NewEphemeralReferenceValue( return NewUnmeteredEphemeralReferenceValue(authorized, value, borrowedType) } -func (*EphemeralReferenceValue) IsValue() {} +func (*EphemeralReferenceValue) isValue() {} func (v *EphemeralReferenceValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitEphemeralReferenceValue(interpreter, v) @@ -17750,7 +17772,7 @@ var _ EquatableValue = AddressValue{} var _ HashableValue = AddressValue{} var _ MemberAccessibleValue = AddressValue{} -func (AddressValue) IsValue() {} +func (AddressValue) isValue() {} func (v AddressValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitAddressValue(interpreter, v) @@ -17946,7 +17968,7 @@ var _ EquatableValue = PathValue{} var _ HashableValue = PathValue{} var _ MemberAccessibleValue = PathValue{} -func (PathValue) IsValue() {} +func (PathValue) isValue() {} func (v PathValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitPathValue(interpreter, v) @@ -18173,103 +18195,99 @@ func (PathValue) ChildStorables() []atree.Storable { return nil } -// StorageCapabilityValue +// PathCapabilityValue -type StorageCapabilityValue struct { +type PathCapabilityValue struct { BorrowType StaticType Path PathValue Address AddressValue - ID UInt64Value } -func NewUnmeteredStorageCapabilityValue( - id UInt64Value, +func NewUnmeteredPathCapabilityValue( address AddressValue, path PathValue, borrowType StaticType, -) *StorageCapabilityValue { - return &StorageCapabilityValue{ - ID: id, +) *PathCapabilityValue { + return &PathCapabilityValue{ Address: address, Path: path, BorrowType: borrowType, } } -func NewStorageCapabilityValue( +func NewPathCapabilityValue( memoryGauge common.MemoryGauge, - id UInt64Value, address AddressValue, path PathValue, borrowType StaticType, -) *StorageCapabilityValue { +) *PathCapabilityValue { // Constant because its constituents are already metered. - common.UseMemory(memoryGauge, common.StorageCapabilityValueMemoryUsage) - return NewUnmeteredStorageCapabilityValue(id, address, path, borrowType) + common.UseMemory(memoryGauge, common.PathCapabilityValueMemoryUsage) + return NewUnmeteredPathCapabilityValue(address, path, borrowType) } -var _ Value = &StorageCapabilityValue{} -var _ atree.Storable = &StorageCapabilityValue{} -var _ EquatableValue = &StorageCapabilityValue{} -var _ MemberAccessibleValue = &StorageCapabilityValue{} +var _ Value = &PathCapabilityValue{} +var _ atree.Storable = &PathCapabilityValue{} +var _ EquatableValue = &PathCapabilityValue{} +var _ CapabilityValue = &PathCapabilityValue{} +var _ MemberAccessibleValue = &PathCapabilityValue{} + +func (*PathCapabilityValue) isValue() {} -func (*StorageCapabilityValue) IsValue() {} +func (*PathCapabilityValue) isCapabilityValue() {} -func (v *StorageCapabilityValue) Accept(interpreter *Interpreter, visitor Visitor) { - visitor.VisitStorageCapabilityValue(interpreter, v) +func (v *PathCapabilityValue) Accept(interpreter *Interpreter, visitor Visitor) { + visitor.VisitPathCapabilityValue(interpreter, v) } -func (v *StorageCapabilityValue) Walk(_ *Interpreter, walkChild func(Value)) { - walkChild(v.ID) +func (v *PathCapabilityValue) Walk(_ *Interpreter, walkChild func(Value)) { walkChild(v.Address) walkChild(v.Path) } -func (v *StorageCapabilityValue) StaticType(inter *Interpreter) StaticType { +func (v *PathCapabilityValue) StaticType(inter *Interpreter) StaticType { return NewCapabilityStaticType( inter, v.BorrowType, ) } -func (v *StorageCapabilityValue) IsImportable(_ *Interpreter) bool { +func (v *PathCapabilityValue) IsImportable(_ *Interpreter) bool { return v.Path.Domain == common.PathDomainPublic } -func (v *StorageCapabilityValue) String() string { +func (v *PathCapabilityValue) String() string { return v.RecursiveString(SeenReferences{}) } -func (v *StorageCapabilityValue) RecursiveString(seenReferences SeenReferences) string { +func (v *PathCapabilityValue) RecursiveString(seenReferences SeenReferences) string { var borrowType string if v.BorrowType != nil { borrowType = v.BorrowType.String() } - return format.StorageCapability( + return format.PathCapability( borrowType, - v.ID.RecursiveString(seenReferences), v.Address.RecursiveString(seenReferences), v.Path.RecursiveString(seenReferences), ) } -func (v *StorageCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { - common.UseMemory(memoryGauge, common.StorageCapabilityValueStringMemoryUsage) +func (v *PathCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + common.UseMemory(memoryGauge, common.PathCapabilityValueStringMemoryUsage) var borrowType string if v.BorrowType != nil { borrowType = v.BorrowType.MeteredString(memoryGauge) } - return format.StorageCapability( + return format.PathCapability( borrowType, - v.ID.MeteredString(memoryGauge, seenReferences), v.Address.MeteredString(memoryGauge, seenReferences), v.Path.MeteredString(memoryGauge, seenReferences), ) } -func (v *StorageCapabilityValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { +func (v *PathCapabilityValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { case sema.CapabilityTypeBorrowFunctionName: var borrowType *sema.ReferenceType @@ -18277,7 +18295,7 @@ func (v *StorageCapabilityValue) GetMember(interpreter *Interpreter, _ LocationR // this function will panic already if this conversion fails borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) } - return interpreter.storageCapabilityBorrowFunction(v.Address, v.Path, borrowType) + return interpreter.pathCapabilityBorrowFunction(v.Address, v.Path, borrowType) case sema.CapabilityTypeCheckFunctionName: var borrowType *sema.ReferenceType @@ -18285,29 +18303,30 @@ func (v *StorageCapabilityValue) GetMember(interpreter *Interpreter, _ LocationR // this function will panic already if this conversion fails borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) } - return interpreter.storageCapabilityCheckFunction(v.Address, v.Path, borrowType) + return interpreter.pathCapabilityCheckFunction(v.Address, v.Path, borrowType) case sema.CapabilityTypeAddressFieldName: return v.Address case sema.CapabilityTypeIDFieldName: - return v.ID + // TODO: + return UInt64Value(TodoCapabilityID) } return nil } -func (*StorageCapabilityValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { +func (*PathCapabilityValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { // Capabilities have no removable members (fields / functions) panic(errors.NewUnreachableError()) } -func (*StorageCapabilityValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { +func (*PathCapabilityValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { // Capabilities have no settable members (fields / functions) panic(errors.NewUnreachableError()) } -func (v *StorageCapabilityValue) ConformsToStaticType( +func (v *PathCapabilityValue) ConformsToStaticType( _ *Interpreter, _ LocationRange, _ TypeConformanceResults, @@ -18315,8 +18334,8 @@ func (v *StorageCapabilityValue) ConformsToStaticType( return true } -func (v *StorageCapabilityValue) Equal(interpreter *Interpreter, locationRange LocationRange, other Value) bool { - otherCapability, ok := other.(*StorageCapabilityValue) +func (v *PathCapabilityValue) Equal(interpreter *Interpreter, locationRange LocationRange, other Value) bool { + otherCapability, ok := other.(*PathCapabilityValue) if !ok { return false } @@ -18331,16 +18350,15 @@ func (v *StorageCapabilityValue) Equal(interpreter *Interpreter, locationRange L return false } - return otherCapability.ID == v.ID && - otherCapability.Address.Equal(interpreter, locationRange, v.Address) && + return otherCapability.Address.Equal(interpreter, locationRange, v.Address) && otherCapability.Path.Equal(interpreter, locationRange, v.Path) } -func (*StorageCapabilityValue) IsStorable() bool { +func (*PathCapabilityValue) IsStorable() bool { return true } -func (v *StorageCapabilityValue) Storable( +func (v *PathCapabilityValue) Storable( storage atree.SlabStorage, address atree.Address, maxInlineSize uint64, @@ -18353,15 +18371,15 @@ func (v *StorageCapabilityValue) Storable( ) } -func (*StorageCapabilityValue) NeedsStoreTo(_ atree.Address) bool { +func (*PathCapabilityValue) NeedsStoreTo(_ atree.Address) bool { return false } -func (*StorageCapabilityValue) IsResourceKinded(_ *Interpreter) bool { +func (*PathCapabilityValue) IsResourceKinded(_ *Interpreter) bool { return false } -func (v *StorageCapabilityValue) Transfer( +func (v *PathCapabilityValue) Transfer( interpreter *Interpreter, _ LocationRange, _ atree.Address, @@ -18375,35 +18393,261 @@ func (v *StorageCapabilityValue) Transfer( return v } -func (v *StorageCapabilityValue) Clone(interpreter *Interpreter) Value { - return NewUnmeteredStorageCapabilityValue( - v.ID, +func (v *PathCapabilityValue) Clone(interpreter *Interpreter) Value { + return NewUnmeteredPathCapabilityValue( v.Address.Clone(interpreter).(AddressValue), v.Path.Clone(interpreter).(PathValue), v.BorrowType, ) } -func (v *StorageCapabilityValue) DeepRemove(interpreter *Interpreter) { +func (v *PathCapabilityValue) DeepRemove(interpreter *Interpreter) { v.Address.DeepRemove(interpreter) v.Path.DeepRemove(interpreter) } -func (v *StorageCapabilityValue) ByteSize() uint32 { +func (v *PathCapabilityValue) ByteSize() uint32 { return mustStorableSize(v) } -func (v *StorageCapabilityValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { +func (v *PathCapabilityValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { return v, nil } -func (v *StorageCapabilityValue) ChildStorables() []atree.Storable { +func (v *PathCapabilityValue) ChildStorables() []atree.Storable { return []atree.Storable{ v.Address, v.Path, } } +// IDCapabilityValue + +type IDCapabilityValue struct { + BorrowType StaticType + Address AddressValue + ID UInt64Value +} + +func NewUnmeteredIDCapabilityValue( + id UInt64Value, + address AddressValue, + borrowType StaticType, +) *IDCapabilityValue { + return &IDCapabilityValue{ + ID: id, + Address: address, + BorrowType: borrowType, + } +} + +func NewIDCapabilityValue( + memoryGauge common.MemoryGauge, + id UInt64Value, + address AddressValue, + borrowType StaticType, +) *IDCapabilityValue { + // Constant because its constituents are already metered. + common.UseMemory(memoryGauge, common.IDCapabilityValueMemoryUsage) + return NewUnmeteredIDCapabilityValue(id, address, borrowType) +} + +var _ Value = &IDCapabilityValue{} +var _ atree.Storable = &IDCapabilityValue{} +var _ CapabilityValue = &IDCapabilityValue{} +var _ EquatableValue = &IDCapabilityValue{} +var _ MemberAccessibleValue = &IDCapabilityValue{} + +func (*IDCapabilityValue) isValue() {} + +func (*IDCapabilityValue) isCapabilityValue() {} + +func (v *IDCapabilityValue) Accept(interpreter *Interpreter, visitor Visitor) { + visitor.VisitIDCapabilityValue(interpreter, v) +} + +func (v *IDCapabilityValue) Walk(_ *Interpreter, walkChild func(Value)) { + walkChild(v.ID) + walkChild(v.Address) +} + +func (v *IDCapabilityValue) StaticType(inter *Interpreter) StaticType { + return NewCapabilityStaticType( + inter, + v.BorrowType, + ) +} + +func (v *IDCapabilityValue) IsImportable(_ *Interpreter) bool { + return false +} + +func (v *IDCapabilityValue) String() string { + return v.RecursiveString(SeenReferences{}) +} + +func (v *IDCapabilityValue) RecursiveString(seenReferences SeenReferences) string { + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.String() + } + return format.IDCapability( + borrowType, + v.Address.RecursiveString(seenReferences), + v.ID.RecursiveString(seenReferences), + ) +} + +func (v *IDCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { + common.UseMemory(memoryGauge, common.IDCapabilityValueStringMemoryUsage) + + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.MeteredString(memoryGauge) + } + + return format.IDCapability( + borrowType, + v.Address.MeteredString(memoryGauge, seenReferences), + v.ID.MeteredString(memoryGauge, seenReferences), + ) +} + +func (v *IDCapabilityValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { + switch name { + case sema.CapabilityTypeBorrowFunctionName: + var borrowType *sema.ReferenceType + if v.BorrowType != nil { + // this function will panic already if this conversion fails + borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) + } + // TODO: + _ = borrowType + panic("TODO") + + case sema.CapabilityTypeCheckFunctionName: + var borrowType *sema.ReferenceType + if v.BorrowType != nil { + // this function will panic already if this conversion fails + borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) + } + // TODO: + _ = borrowType + panic("TODO") + + case sema.CapabilityTypeAddressFieldName: + return v.Address + + case sema.CapabilityTypeIDFieldName: + return v.ID + } + + return nil +} + +func (*IDCapabilityValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { + // Capabilities have no removable members (fields / functions) + panic(errors.NewUnreachableError()) +} + +func (*IDCapabilityValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { + // Capabilities have no settable members (fields / functions) + panic(errors.NewUnreachableError()) +} + +func (v *IDCapabilityValue) ConformsToStaticType( + _ *Interpreter, + _ LocationRange, + _ TypeConformanceResults, +) bool { + return true +} + +func (v *IDCapabilityValue) Equal(interpreter *Interpreter, locationRange LocationRange, other Value) bool { + otherCapability, ok := other.(*IDCapabilityValue) + if !ok { + return false + } + + // BorrowType is optional + + if v.BorrowType == nil { + if otherCapability.BorrowType != nil { + return false + } + } else if !v.BorrowType.Equal(otherCapability.BorrowType) { + return false + } + + return otherCapability.ID == v.ID && + otherCapability.Address.Equal(interpreter, locationRange, v.Address) +} + +func (*IDCapabilityValue) IsStorable() bool { + return true +} + +func (v *IDCapabilityValue) Storable( + storage atree.SlabStorage, + address atree.Address, + maxInlineSize uint64, +) (atree.Storable, error) { + return maybeLargeImmutableStorable( + v, + storage, + address, + maxInlineSize, + ) +} + +func (*IDCapabilityValue) NeedsStoreTo(_ atree.Address) bool { + return false +} + +func (*IDCapabilityValue) IsResourceKinded(_ *Interpreter) bool { + return false +} + +func (v *IDCapabilityValue) Transfer( + interpreter *Interpreter, + _ LocationRange, + _ atree.Address, + remove bool, + storable atree.Storable, +) Value { + if remove { + v.DeepRemove(interpreter) + interpreter.RemoveReferencedSlab(storable) + } + return v +} + +func (v *IDCapabilityValue) Clone(interpreter *Interpreter) Value { + return NewUnmeteredIDCapabilityValue( + v.ID, + v.Address.Clone(interpreter).(AddressValue), + v.BorrowType, + ) +} + +func (v *IDCapabilityValue) DeepRemove(interpreter *Interpreter) { + v.Address.DeepRemove(interpreter) +} + +func (v *IDCapabilityValue) ByteSize() uint32 { + return mustStorableSize(v) +} + +func (v *IDCapabilityValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { + return v, nil +} + +func (v *IDCapabilityValue) ChildStorables() []atree.Storable { + return []atree.Storable{ + v.Address, + } +} + // PathLinkValue type PathLinkValue struct { @@ -18429,8 +18673,11 @@ var EmptyPathLinkValue = PathLinkValue{} var _ Value = PathLinkValue{} var _ atree.Value = PathLinkValue{} var _ EquatableValue = PathLinkValue{} +var _ LinkValue = PathLinkValue{} -func (PathLinkValue) IsValue() {} +func (PathLinkValue) isValue() {} + +func (PathLinkValue) isLinkValue() {} func (v PathLinkValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitPathLinkValue(interpreter, v) @@ -18552,20 +18799,23 @@ func (v PathLinkValue) ChildStorables() []atree.Storable { type PublishedValue struct { // NB: If `publish` and `claim` are ever extended to support arbitrary values, rather than just capabilities, // this will need to be changed to `Value`, and more storage-related operations must be implemented for `PublishedValue` - Value *StorageCapabilityValue + Value CapabilityValue Recipient AddressValue } -func NewPublishedValue(memoryGauge common.MemoryGauge, recipient AddressValue, value *StorageCapabilityValue) *PublishedValue { +func NewPublishedValue(memoryGauge common.MemoryGauge, recipient AddressValue, value CapabilityValue) *PublishedValue { common.UseMemory(memoryGauge, common.PublishedValueMemoryUsage) - return &PublishedValue{Recipient: recipient, Value: value} + return &PublishedValue{ + Recipient: recipient, + Value: value, + } } var _ Value = &PublishedValue{} var _ atree.Value = &PublishedValue{} var _ EquatableValue = &PublishedValue{} -func (*PublishedValue) IsValue() {} +func (*PublishedValue) isValue() {} func (v *PublishedValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitPublishedValue(interpreter, v) @@ -18654,7 +18904,7 @@ func (v *PublishedValue) Transfer( if v.NeedsStoreTo(address) { - innerValue := v.Value.Transfer(interpreter, locationRange, address, remove, nil).(*StorageCapabilityValue) + innerValue := v.Value.Transfer(interpreter, locationRange, address, remove, nil).(CapabilityValue) addressValue := v.Recipient.Transfer(interpreter, locationRange, address, remove, nil).(AddressValue) if remove { @@ -18671,7 +18921,7 @@ func (v *PublishedValue) Transfer( func (v *PublishedValue) Clone(interpreter *Interpreter) Value { return &PublishedValue{ Recipient: v.Recipient, - Value: v.Value.Clone(interpreter).(*StorageCapabilityValue), + Value: v.Value.Clone(interpreter).(CapabilityValue), } } @@ -18694,15 +18944,6 @@ func (v *PublishedValue) ChildStorables() []atree.Storable { } } -// ContractValue is the value of a contract. -// Under normal circumstances, a contract value is always a CompositeValue. -// However, in the test framework, an imported contract is constructed via a constructor function. -// Hence, during tests, the value is a HostFunctionValue. -type ContractValue interface { - Value - SetNestedVariables(variables map[string]*Variable) -} - // AccountLinkValue type AccountLinkValue struct{} @@ -18721,8 +18962,11 @@ var EmptyAccountLinkValue = AccountLinkValue{} var _ Value = AccountLinkValue{} var _ atree.Value = AccountLinkValue{} var _ EquatableValue = AccountLinkValue{} +var _ LinkValue = AccountLinkValue{} + +func (AccountLinkValue) isValue() {} -func (AccountLinkValue) IsValue() {} +func (AccountLinkValue) isLinkValue() {} func (v AccountLinkValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitAccountLinkValue(interpreter, v) diff --git a/runtime/interpreter/value_account.go b/runtime/interpreter/value_account.go index 748b41be2a..20e255e065 100644 --- a/runtime/interpreter/value_account.go +++ b/runtime/interpreter/value_account.go @@ -425,10 +425,8 @@ func accountGetCapabilityFunction( borrowStaticType = ConvertSemaToStaticType(interpreter, borrowType) } - return NewStorageCapabilityValue( + return NewPathCapabilityValue( gauge, - // TODO: - TodoCapabilityID, addressValue, path, borrowStaticType, diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 3f8dd6eb22..f2c5e4ae5a 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -63,7 +63,7 @@ var _ EquatableValue = &AccountCapabilityControllerValue{} var _ CapabilityControllerValue = &AccountCapabilityControllerValue{} var _ MemberAccessibleValue = &AccountCapabilityControllerValue{} -func (*AccountCapabilityControllerValue) IsValue() {} +func (*AccountCapabilityControllerValue) isValue() {} func (*AccountCapabilityControllerValue) isCapabilityControllerValue() {} diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index db2f682661..9d9b16d79e 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -67,7 +67,7 @@ func NewAccountReferenceValue( ) } -func (*AccountReferenceValue) IsValue() {} +func (*AccountReferenceValue) isValue() {} func (*AccountReferenceValue) isReference() {} diff --git a/runtime/interpreter/value_function.go b/runtime/interpreter/value_function.go index 20f5f4ba12..624115f8a1 100644 --- a/runtime/interpreter/value_function.go +++ b/runtime/interpreter/value_function.go @@ -79,7 +79,7 @@ func NewInterpretedFunctionValue( var _ Value = &InterpretedFunctionValue{} var _ FunctionValue = &InterpretedFunctionValue{} -func (*InterpretedFunctionValue) IsValue() {} +func (*InterpretedFunctionValue) isValue() {} func (f *InterpretedFunctionValue) String() string { return format.Function(f.Type.String()) @@ -223,7 +223,7 @@ var _ FunctionValue = &HostFunctionValue{} var _ MemberAccessibleValue = &HostFunctionValue{} var _ ContractValue = &HostFunctionValue{} -func (*HostFunctionValue) IsValue() {} +func (*HostFunctionValue) isValue() {} func (f *HostFunctionValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitHostFunctionValue(interpreter, f) @@ -346,7 +346,7 @@ func NewBoundFunctionValue( } } -func (BoundFunctionValue) IsValue() {} +func (BoundFunctionValue) isValue() {} func (f BoundFunctionValue) String() string { return f.RecursiveString(SeenReferences{}) diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 77f2b64e52..75724f5bf2 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -74,7 +74,7 @@ var _ EquatableValue = &StorageCapabilityControllerValue{} var _ CapabilityControllerValue = &StorageCapabilityControllerValue{} var _ MemberAccessibleValue = &StorageCapabilityControllerValue{} -func (*StorageCapabilityControllerValue) IsValue() {} +func (*StorageCapabilityControllerValue) isValue() {} func (*StorageCapabilityControllerValue) isCapabilityControllerValue() {} diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 98228fb181..bac4ecfb80 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -1077,10 +1077,10 @@ func TestStringer(t *testing.T) { }, "PathLink": { value: PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "foo", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "foo", + ), Type: PrimitiveStaticTypeInt, }, expected: "PathLink(/storage/foo)", @@ -1090,39 +1090,53 @@ func TestStringer(t *testing.T) { expected: "AccountLink()", }, "Path": { - value: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "foo", - }, + value: NewUnmeteredPathValue( + common.PathDomainStorage, + "foo", + ), expected: "/storage/foo", }, "Type": { - value: TypeValue{Type: PrimitiveStaticTypeInt}, + value: NewUnmeteredTypeValue(PrimitiveStaticTypeInt), expected: "Type()", }, - "Capability with borrow type": { - value: NewUnmeteredStorageCapabilityValue( + "path Capability with borrow type": { + value: NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "foo", + ), + PrimitiveStaticTypeInt, + ), + expected: "Capability(address: 0x0000000102030405, path: /public/foo)", + }, + "path Capability without borrow type": { + value: NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "foo", + ), + nil, + ), + expected: "Capability(address: 0x0000000102030405, path: /public/foo)", + }, + "ID Capability with borrow type": { + value: NewUnmeteredIDCapabilityValue( 6, NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "foo", - }, PrimitiveStaticTypeInt, ), - expected: "Capability(id: 6, address: 0x0000000102030405, path: /storage/foo)", + expected: "Capability(address: 0x0000000102030405, id: 6)", }, - "Capability without borrow type": { - value: NewUnmeteredStorageCapabilityValue( + "ID Capability without borrow type": { + value: NewUnmeteredIDCapabilityValue( 6, NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "foo", - }, nil, ), - expected: "Capability(id: 6, address: 0x0000000102030405, path: /storage/foo)", + expected: "Capability(address: 0x0000000102030405, id: 6)", }, "Recursive ephemeral reference (array)": { value: func() Value { @@ -1566,10 +1580,10 @@ func TestGetHashInput(t *testing.T) { ), }, "Path": { - value: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "foo", - }, + value: NewUnmeteredPathValue( + common.PathDomainStorage, + "foo", + ), expected: []byte{ byte(HashInputTypePath), // domain: storage @@ -1579,10 +1593,10 @@ func TestGetHashInput(t *testing.T) { }, }, "Path long identifier": { - value: PathValue{ - Domain: common.PathDomainStorage, - Identifier: strings.Repeat("a", 32), - }, + value: NewUnmeteredPathValue( + common.PathDomainStorage, + strings.Repeat("a", 32), + ), expected: append( []byte{byte(HashInputTypePath), // domain: storage @@ -1701,7 +1715,7 @@ func TestEphemeralReferenceTypeConformance(t *testing.T) { assert.True(t, conforms) } -func TestStorageCapabilityValue_Equal(t *testing.T) { +func TestPathCapabilityValue_Equal(t *testing.T) { t.Parallel() @@ -1712,24 +1726,22 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - NewUnmeteredStorageCapabilityValue( - 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( - 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), PrimitiveStaticTypeInt, ), ), @@ -1743,24 +1755,22 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - NewUnmeteredStorageCapabilityValue( - 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), nil, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( - 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), nil, ), ), @@ -1774,30 +1784,163 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test1", + ), + PrimitiveStaticTypeInt, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test2", + ), + PrimitiveStaticTypeInt, + ), + ), + ) + }) + + t.Run("different addresses", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.False(t, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeInt, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x2}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeInt, + ), + ), + ) + }) + + t.Run("different borrow types", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.False(t, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeInt, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeString, + ), + ), + ) + }) + + t.Run("different kind", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.False(t, + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeInt, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + PrimitiveStaticTypeInt, + ), + ), + ) + }) +} + +func TestIDCapabilityValue_Equal(t *testing.T) { + + t.Parallel() + + t.Run("equal, borrow type", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.True(t, + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test1", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test2", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ), ), ) }) + t.Run("equal, no borrow type", func(t *testing.T) { + + t.Parallel() + + inter := newTestInterpreter(t) + + require.True(t, + NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + nil, + ).Equal( + inter, + EmptyLocationRange, + NewUnmeteredIDCapabilityValue( + 4, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + nil, + ), + ), + ) + }) + t.Run("different addresses", func(t *testing.T) { t.Parallel() @@ -1805,24 +1948,16 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x2}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), PrimitiveStaticTypeInt, ), ), @@ -1836,24 +1971,16 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeString, ), ), @@ -1867,24 +1994,16 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 5, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ), ), @@ -1898,18 +2017,21 @@ func TestStorageCapabilityValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - NewUnmeteredStorageCapabilityValue( + NewUnmeteredIDCapabilityValue( 4, - AddressValue{0x1}, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), PrimitiveStaticTypeInt, ).Equal( inter, EmptyLocationRange, - NewUnmeteredStringValue("test"), + NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes([]byte{0x1}), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + PrimitiveStaticTypeInt, + ), ), ) }) @@ -1926,10 +2048,10 @@ func TestAddressValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - AddressValue{0x1}.Equal( + NewUnmeteredAddressValueFromBytes([]byte{0x1}).Equal( inter, EmptyLocationRange, - AddressValue{0x1}, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), ), ) }) @@ -1941,10 +2063,10 @@ func TestAddressValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - AddressValue{0x1}.Equal( + NewUnmeteredAddressValueFromBytes([]byte{0x1}).Equal( inter, EmptyLocationRange, - AddressValue{0x2}, + NewUnmeteredAddressValueFromBytes([]byte{0x2}), ), ) }) @@ -1956,7 +2078,7 @@ func TestAddressValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - AddressValue{0x1}.Equal( + NewUnmeteredAddressValueFromBytes([]byte{0x1}).Equal( inter, EmptyLocationRange, NewUnmeteredUInt8Value(1), @@ -2250,16 +2372,16 @@ func TestPathValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - PathValue{ - Domain: domain, - Identifier: "test", - }.Equal( + NewUnmeteredPathValue( + domain, + "test", + ).Equal( inter, EmptyLocationRange, - PathValue{ - Domain: domain, - Identifier: "test", - }, + NewUnmeteredPathValue( + domain, + "test", + ), ), ) }) @@ -2277,16 +2399,16 @@ func TestPathValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - PathValue{ - Domain: domain, - Identifier: "test", - }.Equal( + NewUnmeteredPathValue( + domain, + "test", + ).Equal( inter, EmptyLocationRange, - PathValue{ - Domain: otherDomain, - Identifier: "test", - }, + NewUnmeteredPathValue( + otherDomain, + "test", + ), ), ) }) @@ -2300,16 +2422,16 @@ func TestPathValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - PathValue{ - Domain: domain, - Identifier: "test1", - }.Equal( + NewUnmeteredPathValue( + domain, + "test1", + ).Equal( inter, EmptyLocationRange, - PathValue{ - Domain: domain, - Identifier: "test2", - }, + NewUnmeteredPathValue( + domain, + "test2", + ), ), ) }) @@ -2322,10 +2444,10 @@ func TestPathValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }.Equal( + NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ).Equal( inter, EmptyLocationRange, NewUnmeteredStringValue("/storage/test"), @@ -2346,19 +2468,19 @@ func TestPathLinkValue_Equal(t *testing.T) { require.True(t, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ), Type: PrimitiveStaticTypeInt, }.Equal( inter, EmptyLocationRange, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ), Type: PrimitiveStaticTypeInt, }, ), @@ -2373,19 +2495,19 @@ func TestPathLinkValue_Equal(t *testing.T) { require.False(t, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test1", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test1", + ), Type: PrimitiveStaticTypeInt, }.Equal( inter, EmptyLocationRange, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test2", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test2", + ), Type: PrimitiveStaticTypeInt, }, ), @@ -2400,19 +2522,19 @@ func TestPathLinkValue_Equal(t *testing.T) { require.False(t, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ), Type: PrimitiveStaticTypeInt, }.Equal( inter, EmptyLocationRange, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ), Type: PrimitiveStaticTypeString, }, ), @@ -2427,10 +2549,10 @@ func TestPathLinkValue_Equal(t *testing.T) { require.False(t, PathLinkValue{ - TargetPath: PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, + TargetPath: NewUnmeteredPathValue( + common.PathDomainStorage, + "test", + ), Type: PrimitiveStaticTypeInt, }.Equal( inter, @@ -3340,7 +3462,7 @@ func TestNumberValue_Equal(t *testing.T) { value.Equal( inter, EmptyLocationRange, - AddressValue{0x1}, + NewUnmeteredAddressValueFromBytes([]byte{0x1}), ), ) }) @@ -4087,16 +4209,38 @@ func TestValue_ConformsToStaticType(t *testing.T) { ) }) - t.Run("StorageCapabilityValue", func(t *testing.T) { + t.Run("PathCapabilityValue", func(t *testing.T) { + + t.Parallel() + + test( + func(_ *Interpreter) Value { + return NewUnmeteredPathCapabilityValue( + NewUnmeteredAddressValueFromBytes(testAddress.Bytes()), + NewUnmeteredPathValue( + common.PathDomainPublic, + "test", + ), + ReferenceStaticType{ + Authorized: false, + BorrowedType: PrimitiveStaticTypeBool, + ReferencedType: PrimitiveStaticTypeBool, + }, + ) + }, + true, + ) + }) + + t.Run("IDCapabilityValue", func(t *testing.T) { t.Parallel() test( func(_ *Interpreter) Value { - return NewUnmeteredStorageCapabilityValue( + return NewUnmeteredIDCapabilityValue( NewUnmeteredUInt64Value(4), NewUnmeteredAddressValueFromBytes(testAddress.Bytes()), - NewUnmeteredPathValue(common.PathDomainStorage, "test"), ReferenceStaticType{ Authorized: false, BorrowedType: PrimitiveStaticTypeBool, diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index b45b99ff2d..96a5e8efdd 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -55,7 +55,8 @@ type Visitor interface { VisitEphemeralReferenceValue(interpreter *Interpreter, value *EphemeralReferenceValue) VisitAddressValue(interpreter *Interpreter, value AddressValue) VisitPathValue(interpreter *Interpreter, value PathValue) - VisitStorageCapabilityValue(interpreter *Interpreter, value *StorageCapabilityValue) + VisitPathCapabilityValue(interpreter *Interpreter, value *PathCapabilityValue) + VisitIDCapabilityValue(interpreter *Interpreter, value *IDCapabilityValue) VisitPathLinkValue(interpreter *Interpreter, value PathLinkValue) VisitAccountLinkValue(interpreter *Interpreter, value AccountLinkValue) VisitPublishedValue(interpreter *Interpreter, value *PublishedValue) @@ -103,7 +104,8 @@ type EmptyVisitor struct { EphemeralReferenceValueVisitor func(interpreter *Interpreter, value *EphemeralReferenceValue) AddressValueVisitor func(interpreter *Interpreter, value AddressValue) PathValueVisitor func(interpreter *Interpreter, value PathValue) - StorageCapabilityValueVisitor func(interpreter *Interpreter, value *StorageCapabilityValue) + PathCapabilityValueVisitor func(interpreter *Interpreter, value *PathCapabilityValue) + IDCapabilityValueVisitor func(interpreter *Interpreter, value *IDCapabilityValue) PathLinkValueVisitor func(interpreter *Interpreter, value PathLinkValue) AccountLinkValueVisitor func(interpreter *Interpreter, value AccountLinkValue) PublishedValueVisitor func(interpreter *Interpreter, value *PublishedValue) @@ -368,11 +370,18 @@ func (v EmptyVisitor) VisitPathValue(interpreter *Interpreter, value PathValue) v.PathValueVisitor(interpreter, value) } -func (v EmptyVisitor) VisitStorageCapabilityValue(interpreter *Interpreter, value *StorageCapabilityValue) { - if v.StorageCapabilityValueVisitor == nil { +func (v EmptyVisitor) VisitPathCapabilityValue(interpreter *Interpreter, value *PathCapabilityValue) { + if v.PathCapabilityValueVisitor == nil { return } - v.StorageCapabilityValueVisitor(interpreter, value) + v.PathCapabilityValueVisitor(interpreter, value) +} + +func (v EmptyVisitor) VisitIDCapabilityValue(interpreter *Interpreter, value *IDCapabilityValue) { + if v.IDCapabilityValueVisitor == nil { + return + } + v.IDCapabilityValueVisitor(interpreter, value) } func (v EmptyVisitor) VisitPathLinkValue(interpreter *Interpreter, value PathLinkValue) { diff --git a/runtime/runtime.go b/runtime/runtime.go index aa08ee6f09..3fa7e46a23 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -146,7 +146,7 @@ type Runtime interface { // ReadStored(address common.Address, path cadence.Path, context Context) (cadence.Value, error) - // ReadLinked dereferences the path and returns the value stored at the target + // Deprecated: ReadLinked dereferences the path and returns the value stored at the target. // ReadLinked(address common.Address, path cadence.Path, context Context) (cadence.Value, error) @@ -644,7 +644,7 @@ func (r *interpreterRuntime) ReadLinked( pathValue := valueImporter{inter: inter}.importPathValue(path) - target, _, err := inter.GetStorageCapabilityFinalTarget( + target, _, err := inter.GetPathCapabilityFinalTarget( address, pathValue, &sema.ReferenceType{ diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 054d2ec2aa..8c2f5d1ef5 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -3601,7 +3601,7 @@ func TestRuntimeInvokeContractFunction(t *testing.T) { require.ErrorAs(t, err, &interpreter.ValueTransferTypeError{}) }) - t.Run("function with un-importable argument errors and error propagates", func(t *testing.T) { + t.Run("function with un-importable argument errors and error propagates (path capability)", func(t *testing.T) { _, err = runtime.InvokeContractFunction( common.AddressLocation{ Address: addressValue, @@ -3609,9 +3609,41 @@ func TestRuntimeInvokeContractFunction(t *testing.T) { }, "helloArg", []cadence.Value{ - cadence.StorageCapability{ - BorrowType: cadence.AddressType{}, // this will error during `importValue` - }, + cadence.NewPathCapability( + cadence.Address{}, + cadence.Path{ + Domain: common.PathDomainPublic, + Identifier: "test", + }, + cadence.AddressType{}, // this will error during `importValue` + ), + }, + []sema.Type{ + &sema.CapabilityType{}, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + RequireError(t, err) + + require.ErrorContains(t, err, "cannot import capability") + }) + + t.Run("function with un-importable argument errors and error propagates (ID capability)", func(t *testing.T) { + _, err = runtime.InvokeContractFunction( + common.AddressLocation{ + Address: addressValue, + Name: "Test", + }, + "helloArg", + []cadence.Value{ + cadence.NewIDCapability( + 42, + cadence.Address{}, + cadence.AddressType{}, // this will error during `importValue` + ), }, []sema.Type{ &sema.CapabilityType{}, @@ -7048,9 +7080,7 @@ func TestRuntimeGetCapability(t *testing.T) { require.NoError(t, err) require.Equal(t, - cadence.NewStorageCapability( - // TODO: - interpreter.TodoCapabilityID, + cadence.NewPathCapability( cadence.BytesToAddress([]byte{0x1}), cadence.Path{ Domain: common.PathDomainPublic, diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 1e2dc05abd..bf7c7fe374 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2170,8 +2170,8 @@ func (t *VariableSizedType) IsImportable(results map[*Member]bool) bool { return t.Type.IsImportable(results) } -func (v *VariableSizedType) IsEquatable() bool { - return v.Type.IsEquatable() +func (t *VariableSizedType) IsEquatable() bool { + return t.Type.IsEquatable() } func (t *VariableSizedType) TypeAnnotationState() TypeAnnotationState { diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index fc232098e9..2b97319e70 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -1000,7 +1000,7 @@ func accountInboxPublishFunction( gauge, sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { - value, ok := invocation.Arguments[0].(*interpreter.StorageCapabilityValue) + value, ok := invocation.Arguments[0].(interpreter.CapabilityValue) if !ok { panic(errors.NewUnreachableError()) } @@ -2507,12 +2507,10 @@ func newAuthAccountStorageCapabilitiesIssueFunction( storeCapabilityController(inter, address, capabilityIDValue, controller) recordPathCapabilityController(inter, locationRange, address, targetPathValue, capabilityIDValue) - return interpreter.NewStorageCapabilityValue( + return interpreter.NewIDCapabilityValue( gauge, capabilityIDValue, addressValue, - // TODO: remove - interpreter.EmptyPathValue, borrowStaticType, ) }, @@ -2564,12 +2562,10 @@ func newAuthAccountAccountCapabilitiesIssueFunction( storeCapabilityController(inter, address, capabilityIDValue, controller) recordAccountCapabilityController(inter, address, capabilityIDValue) - return interpreter.NewStorageCapabilityValue( + return interpreter.NewIDCapabilityValue( gauge, capabilityIDValue, addressValue, - // TODO: remove - interpreter.EmptyPathValue, borrowStaticType, ) }, @@ -2863,11 +2859,24 @@ func newAuthAccountCapabilitiesPublishFunction( func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter - capabilityValue, ok := invocation.Arguments[0].(*interpreter.StorageCapabilityValue) - if !ok { + // Get capability argument + + var capabilityValue *interpreter.IDCapabilityValue + + firstValue := invocation.Arguments[0] + switch firstValue := firstValue.(type) { + case *interpreter.IDCapabilityValue: + capabilityValue = firstValue + + case *interpreter.PathCapabilityValue: + panic(errors.NewDefaultUserError("cannot publish linked capability")) + + default: panic(errors.NewUnreachableError()) } + // Get path argument + pathValue, ok := invocation.Arguments[1].(interpreter.PathValue) if !ok || pathValue.Domain != common.PathDomainPublic { panic(errors.NewUnreachableError()) @@ -2902,7 +2911,7 @@ func newAuthAccountCapabilitiesPublishFunction( atree.Address(address), true, nil, - ).(*interpreter.StorageCapabilityValue) + ).(*interpreter.IDCapabilityValue) if !ok { panic(errors.NewUnreachableError()) } @@ -2931,6 +2940,11 @@ func newAuthAccountCapabilitiesUnpublishFunction( sema.AuthAccountCapabilitiesTypeUnpublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get path argument + pathValue, ok := invocation.Arguments[0].(interpreter.PathValue) if !ok || pathValue.Domain != common.PathDomainPublic { panic(errors.NewUnreachableError()) @@ -2939,8 +2953,7 @@ func newAuthAccountCapabilitiesUnpublishFunction( domain := pathValue.Domain.Identifier() identifier := pathValue.Identifier - inter := invocation.Interpreter - locationRange := invocation.LocationRange + // Read/remove capability storageMapKey := interpreter.StringStorageMapKey(identifier) @@ -2949,8 +2962,15 @@ func newAuthAccountCapabilitiesUnpublishFunction( return interpreter.Nil } - capabilityValue := readValue.(*interpreter.StorageCapabilityValue) - if !ok { + var capabilityValue *interpreter.IDCapabilityValue + switch readValue := readValue.(type) { + case *interpreter.IDCapabilityValue: + capabilityValue = readValue + + case interpreter.LinkValue: + panic(errors.NewDefaultUserError("cannot unpublish linked capability")) + + default: panic(errors.NewUnreachableError()) } @@ -2960,7 +2980,7 @@ func newAuthAccountCapabilitiesUnpublishFunction( atree.Address{}, true, nil, - ).(*interpreter.StorageCapabilityValue) + ).(*interpreter.IDCapabilityValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3032,8 +3052,17 @@ func newAccountCapabilitiesGetFunction( return interpreter.Nil } - readCapabilityValue := readValue.(*interpreter.StorageCapabilityValue) - if !ok { + var readCapabilityValue *interpreter.IDCapabilityValue + + switch readValue := readValue.(type) { + case *interpreter.IDCapabilityValue: + readCapabilityValue = readValue + + case interpreter.LinkValue: + // TODO: return PathCapabilityValue? + return interpreter.Nil + + default: panic(errors.NewUnreachableError()) } @@ -3056,7 +3085,7 @@ func newAccountCapabilitiesGetFunction( atree.Address{}, false, nil, - ).(*interpreter.StorageCapabilityValue) + ).(*interpreter.IDCapabilityValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3069,18 +3098,19 @@ func newAccountCapabilitiesGetFunction( var resultValue interpreter.Value if borrow { - resultValue = inter.BorrowCapability( - readCapabilityValue.Address.ToAddress(), - readCapabilityValue.Path, - wantedBorrowType, - locationRange, - ) + // TODO: + panic("TODO") + //resultValue = inter.BorrowCapability( + // readCapabilityValue.Address.ToAddress(), + // readCapabilityValue.ID, + // wantedBorrowType, + // locationRange, + //) } else { - resultValue = interpreter.NewStorageCapabilityValue( + resultValue = interpreter.NewIDCapabilityValue( inter, readCapabilityValue.ID, readCapabilityValue.Address, - readCapabilityValue.Path, wantedBorrowStaticType, ) } diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 9956a84070..8000843663 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -1368,7 +1368,7 @@ func TestRuntimeStorageUnlink(t *testing.T) { require.NoError(t, err) } -func TestRuntimeStorageSaveStorageCapability(t *testing.T) { +func TestRuntimeStorageSavePathCapability(t *testing.T) { t.Parallel() @@ -1443,9 +1443,7 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { value, err := runtime.ReadStored(signer, storagePath, context) require.NoError(t, err) - expected := cadence.NewStorageCapability( - // TODO: - interpreter.TodoCapabilityID, + expected := cadence.NewPathCapability( cadence.Address(signer), cadence.Path{ Domain: domain, diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index cbf7d117cd..35530c8172 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -968,9 +968,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1019,9 +1017,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1125,9 +1121,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1162,7 +1156,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { require.IsType(t, &interpreter.SomeValue{}, value) capability := value.(*interpreter.SomeValue).InnerValue(inter, interpreter.EmptyLocationRange) - require.IsType(t, &interpreter.StorageCapabilityValue{}, capability) + require.IsType(t, &interpreter.PathCapabilityValue{}, capability) s2Type := checker.RequireGlobalType(t, inter.Program.Elaboration, "S2") @@ -1177,9 +1171,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1286,9 +1278,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1370,9 +1360,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1401,9 +1389,7 @@ func TestInterpretAuthAccount_link(t *testing.T) { RequireValuesEqual( t, inter, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + interpreter.NewUnmeteredPathCapabilityValue( address, interpreter.PathValue{ Domain: capabilityDomain, @@ -1821,9 +1807,9 @@ func TestInterpretAccount_getCapability(t *testing.T) { require.NoError(t, err) - require.IsType(t, &interpreter.StorageCapabilityValue{}, value) + require.IsType(t, &interpreter.PathCapabilityValue{}, value) - actualBorrowType := value.(*interpreter.StorageCapabilityValue).BorrowType + actualBorrowType := value.(*interpreter.PathCapabilityValue).BorrowType if typed { expectedBorrowType := interpreter.ConvertSemaToStaticType( diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index 13ac2ee999..b9638076f5 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -3486,119 +3486,122 @@ func TestInterpretDynamicCastingCapability(t *testing.T) { t.Parallel() - structType := &sema.CompositeType{ - Location: TestLocation, - Identifier: "S", - Kind: common.CompositeKindStructure, - } + test := func( + name string, + newCapabilityValue func(borrowType interpreter.StaticType) interpreter.CapabilityValue, + ) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + structType := &sema.CompositeType{ + Location: TestLocation, + Identifier: "S", + Kind: common.CompositeKindStructure, + } - types := []sema.Type{ - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: structType, - }, - }, - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: sema.AnyStructType, - }, - }, - &sema.CapabilityType{}, - sema.AnyStructType, - } + capabilityValue := newCapabilityValue( + interpreter.ConvertSemaToStaticType( + nil, + &sema.ReferenceType{ + Type: structType, + }, + ), + ) - capabilityValue := interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, - interpreter.AddressValue{}, - interpreter.EmptyPathValue, - interpreter.ConvertSemaToStaticType( - nil, - &sema.ReferenceType{ - Type: structType, - }, - ), - ) + types := []sema.Type{ + &sema.CapabilityType{ + BorrowType: &sema.ReferenceType{ + Type: structType, + }, + }, + &sema.CapabilityType{ + BorrowType: &sema.ReferenceType{ + Type: sema.AnyStructType, + }, + }, + &sema.CapabilityType{}, + sema.AnyStructType, + } - capabilityValueDeclaration := stdlib.StandardLibraryValue{ - Name: "cap", - Type: &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: structType, - }, - }, - Value: capabilityValue, - Kind: common.DeclarationKindConstant, - } + capabilityValueDeclaration := stdlib.StandardLibraryValue{ + Name: "cap", + Type: &sema.CapabilityType{ + BorrowType: &sema.ReferenceType{ + Type: structType, + }, + }, + Value: capabilityValue, + Kind: common.DeclarationKindConstant, + } - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(capabilityValueDeclaration) + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(capabilityValueDeclaration) - baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) - interpreter.Declare(baseActivation, capabilityValueDeclaration) + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, capabilityValueDeclaration) - options := ParseCheckAndInterpretOptions{ - CheckerConfig: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, - Config: &interpreter.Config{ - BaseActivation: baseActivation, - }, - } + options := ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + } - for operation, returnsOptional := range dynamicCastingOperations { + for operation, returnsOptional := range dynamicCastingOperations { - t.Run(operation.Symbol(), func(t *testing.T) { + t.Run(operation.Symbol(), func(t *testing.T) { - for _, fromType := range types { - for _, targetType := range types { + for _, fromType := range types { + for _, targetType := range types { - t.Run(fmt.Sprintf("valid: from %s to %s", fromType, targetType), func(t *testing.T) { + t.Run(fmt.Sprintf("valid: from %s to %s", fromType, targetType), func(t *testing.T) { - inter, err := parseCheckAndInterpretWithOptions(t, - fmt.Sprintf( - ` + inter, err := parseCheckAndInterpretWithOptions(t, + fmt.Sprintf( + ` struct S {} let x: %[1]s = cap let y: %[2]s? = x %[3]s %[2]s `, - fromType, - targetType, - operation.Symbol(), - ), - options, - ) - require.NoError(t, err) + fromType, + targetType, + operation.Symbol(), + ), + options, + ) + require.NoError(t, err) - AssertValuesEqual( - t, - inter, - capabilityValue, - inter.Globals.Get("x").GetValue(), - ) + AssertValuesEqual( + t, + inter, + capabilityValue, + inter.Globals.Get("x").GetValue(), + ) - AssertValuesEqual( - t, - inter, - interpreter.NewUnmeteredSomeValueNonCopying( - capabilityValue, - ), - inter.Globals.Get("y").GetValue(), - ) - }) - } + AssertValuesEqual( + t, + inter, + interpreter.NewUnmeteredSomeValueNonCopying( + capabilityValue, + ), + inter.Globals.Get("y").GetValue(), + ) + }) + } - for _, otherType := range []sema.Type{ - sema.StringType, - sema.VoidType, - sema.BoolType, - } { + for _, otherType := range []sema.Type{ + sema.StringType, + sema.VoidType, + sema.BoolType, + } { - t.Run(fmt.Sprintf("invalid: from %s to Capability<&%s>", fromType, otherType), func(t *testing.T) { + t.Run(fmt.Sprintf("invalid: from %s to Capability<&%s>", fromType, otherType), func(t *testing.T) { - inter, err := parseCheckAndInterpretWithOptions(t, - fmt.Sprintf( - ` + inter, err := parseCheckAndInterpretWithOptions(t, + fmt.Sprintf( + ` struct S {} fun test(): Capability<&%[2]s>? { @@ -3606,34 +3609,56 @@ func TestInterpretDynamicCastingCapability(t *testing.T) { return x %[3]s Capability<&%[2]s> } `, - fromType, - otherType, - operation.Symbol(), - ), - options, - ) - require.NoError(t, err) + fromType, + otherType, + operation.Symbol(), + ), + options, + ) + require.NoError(t, err) - result, err := inter.Invoke("test") + result, err := inter.Invoke("test") - if returnsOptional { - require.NoError(t, err) - AssertValuesEqual( - t, - inter, - interpreter.Nil, - result, - ) - } else { - RequireError(t, err) + if returnsOptional { + require.NoError(t, err) + AssertValuesEqual( + t, + inter, + interpreter.Nil, + result, + ) + } else { + RequireError(t, err) - require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{}) + require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{}) + } + }) } - }) - } + } + }) } }) } + + test( + "path capability", + func(borrowType interpreter.StaticType) interpreter.CapabilityValue { + return interpreter.NewUnmeteredPathCapabilityValue( + interpreter.AddressValue{}, + interpreter.EmptyPathValue, + borrowType, + ) + }, + ) + test("path capability", + func(borrowType interpreter.StaticType) interpreter.CapabilityValue { + return interpreter.NewUnmeteredIDCapabilityValue( + 4, + interpreter.AddressValue{}, + borrowType, + ) + }, + ) } func TestInterpretResourceConstructorCast(t *testing.T) { diff --git a/runtime/tests/interpreter/equality_test.go b/runtime/tests/interpreter/equality_test.go index 83e80ae7ee..558a105023 100644 --- a/runtime/tests/interpreter/equality_test.go +++ b/runtime/tests/interpreter/equality_test.go @@ -40,15 +40,14 @@ func TestInterpretEquality(t *testing.T) { t.Parallel() - t.Run("capability", func(t *testing.T) { + t.Run("capability (path)", func(t *testing.T) { t.Parallel() capabilityValueDeclaration := stdlib.StandardLibraryValue{ Name: "cap", Type: &sema.CapabilityType{}, - Value: interpreter.NewUnmeteredStorageCapabilityValue( - 4, + Value: interpreter.NewUnmeteredPathCapabilityValue( interpreter.NewUnmeteredAddressValueFromBytes([]byte{0x1}), interpreter.PathValue{ Domain: common.PathDomainStorage, @@ -98,6 +97,60 @@ func TestInterpretEquality(t *testing.T) { ) }) + t.Run("capability (ID)", func(t *testing.T) { + + t.Parallel() + + capabilityValueDeclaration := stdlib.StandardLibraryValue{ + Name: "cap", + Type: &sema.CapabilityType{}, + Value: interpreter.NewUnmeteredIDCapabilityValue( + 4, + interpreter.NewUnmeteredAddressValueFromBytes([]byte{0x1}), + nil, + ), + Kind: common.DeclarationKindConstant, + } + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(capabilityValueDeclaration) + + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, capabilityValueDeclaration) + + inter, err := parseCheckAndInterpretWithOptions(t, + ` + let maybeCapNonNil: Capability? = cap + let maybeCapNil: Capability? = nil + let res1 = maybeCapNonNil != nil + let res2 = maybeCapNil == nil + `, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.TrueValue, + inter.Globals.Get("res1").GetValue(), + ) + + AssertValuesEqual( + t, + inter, + interpreter.TrueValue, + inter.Globals.Get("res2").GetValue(), + ) + }) + t.Run("function", func(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index 6b278addf2..0aa95c8a4c 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -6964,7 +6964,7 @@ func TestInterpretPathValueMetering(t *testing.T) { }) } -func TestInterpretStorageCapabilityValueMetering(t *testing.T) { +func TestInterpretPathCapabilityValueMetering(t *testing.T) { t.Parallel() t.Run("creation", func(t *testing.T) { @@ -6986,7 +6986,7 @@ func TestInterpretStorageCapabilityValueMetering(t *testing.T) { _, err := inter.Invoke("main", account) require.NoError(t, err) - assert.Equal(t, uint64(1), meter.getMemory(common.MemoryKindStorageCapabilityValue)) + assert.Equal(t, uint64(1), meter.getMemory(common.MemoryKindPathCapabilityValue)) assert.Equal(t, uint64(4), meter.getMemory(common.MemoryKindPathValue)) assert.Equal(t, uint64(2), meter.getMemory(common.MemoryKindReferenceStaticType)) }) @@ -7016,6 +7016,8 @@ func TestInterpretStorageCapabilityValueMetering(t *testing.T) { }) } +// TODO: IDCapability + func TestInterpretPathLinkValueMetering(t *testing.T) { t.Parallel() @@ -9025,7 +9027,7 @@ func TestInterpretValueStringConversion(t *testing.T) { testValueStringConversion(t, script) }) - t.Run("Capability", func(t *testing.T) { + t.Run("path Capability", func(t *testing.T) { t.Parallel() script := ` @@ -9037,10 +9039,9 @@ func TestInterpretValueStringConversion(t *testing.T) { struct Bar: Foo {} ` - testValueStringConversion(t, script, - interpreter.NewUnmeteredStorageCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + testValueStringConversion(t, + script, + interpreter.NewUnmeteredPathCapabilityValue( interpreter.AddressValue{1}, interpreter.PathValue{ Domain: common.PathDomainPublic, @@ -9054,6 +9055,32 @@ func TestInterpretValueStringConversion(t *testing.T) { )) }) + t.Run("ID Capability", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(a: Capability<&{Foo}>) { + log(a) + } + + struct interface Foo {} + struct Bar: Foo {} + ` + + testValueStringConversion(t, + script, + interpreter.NewUnmeteredIDCapabilityValue( + // TODO: + interpreter.TodoCapabilityID, + interpreter.AddressValue{1}, + interpreter.CompositeStaticType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + TypeID: "S.test.Bar", + }, + )) + }) + t.Run("Type", func(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/values_test.go b/runtime/tests/interpreter/values_test.go index 83242c6f81..4e8b6cc3f3 100644 --- a/runtime/tests/interpreter/values_test.go +++ b/runtime/tests/interpreter/values_test.go @@ -1126,7 +1126,7 @@ func randomStorableValue(inter *interpreter.Interpreter, currentDepth int) inter if currentDepth < containerMaxDepth { n = randomInt(Composite) } else { - n = randomInt(Capability) + n = randomInt(IDCapability) } switch n { @@ -1142,9 +1142,8 @@ func randomStorableValue(inter *interpreter.Interpreter, currentDepth int) inter return randomArrayValue(inter, currentDepth) case Composite: return randomCompositeValue(inter, common.CompositeKindStructure, currentDepth) - case Capability: - return interpreter.NewUnmeteredStorageCapabilityValue( - interpreter.UInt64Value(randomInt(math.MaxInt-1)), + case PathCapability: + return interpreter.NewUnmeteredPathCapabilityValue( randomAddressValue(), randomPathValue(), interpreter.ReferenceStaticType{ @@ -1152,6 +1151,15 @@ func randomStorableValue(inter *interpreter.Interpreter, currentDepth int) inter BorrowedType: interpreter.PrimitiveStaticTypeAnyStruct, }, ) + case IDCapability: + return interpreter.NewUnmeteredIDCapabilityValue( + interpreter.UInt64Value(randomInt(math.MaxInt-1)), + randomAddressValue(), + interpreter.ReferenceStaticType{ + Authorized: false, + BorrowedType: interpreter.PrimitiveStaticTypeAnyStruct, + }, + ) case Some: return interpreter.NewUnmeteredSomeValueNonCopying( randomStorableValue(inter, currentDepth+1), @@ -1529,7 +1537,8 @@ const ( Void Nil // `Never?` - Capability + PathCapability + IDCapability // Containers Some diff --git a/values.go b/values.go index 531e0f9ce4..bd4e1cb599 100644 --- a/values.go +++ b/values.go @@ -2013,70 +2013,135 @@ func (v TypeValue) String() string { return format.TypeValue(v.StaticType.ID()) } -// StorageCapability +// Capability -type StorageCapability struct { +type Capability interface { + Value + isCapability() +} + +// PathCapability + +type PathCapability struct { BorrowType Type Path Path Address Address - ID UInt64 } -var _ Value = StorageCapability{} +var _ Value = PathCapability{} +var _ Capability = PathCapability{} -func NewStorageCapability( - id UInt64, +func NewPathCapability( address Address, path Path, borrowType Type, -) StorageCapability { - return StorageCapability{ - ID: id, +) PathCapability { + return PathCapability{ Path: path, Address: address, BorrowType: borrowType, } } -func NewMeteredStorageCapability( +func NewMeteredPathCapability( gauge common.MemoryGauge, - id UInt64, address Address, path Path, borrowType Type, -) StorageCapability { - common.UseMemory(gauge, common.CadenceStorageCapabilityValueMemoryUsage) - return NewStorageCapability( - id, +) PathCapability { + common.UseMemory(gauge, common.CadencePathCapabilityValueMemoryUsage) + return NewPathCapability( address, path, borrowType, ) } -func (StorageCapability) isValue() {} +func (PathCapability) isValue() {} -func (v StorageCapability) Type() Type { +func (PathCapability) isCapability() {} + +func (v PathCapability) Type() Type { return NewCapabilityType(v.BorrowType) } -func (v StorageCapability) MeteredType(gauge common.MemoryGauge) Type { +func (v PathCapability) MeteredType(gauge common.MemoryGauge) Type { return NewMeteredCapabilityType(gauge, v.BorrowType) } -func (StorageCapability) ToGoValue() any { +func (PathCapability) ToGoValue() any { return nil } -func (v StorageCapability) String() string { - return format.StorageCapability( +func (v PathCapability) String() string { + return format.PathCapability( v.BorrowType.ID(), - v.ID.String(), v.Address.String(), v.Path.String(), ) } +// IDCapability + +type IDCapability struct { + BorrowType Type + Address Address + ID UInt64 +} + +var _ Value = IDCapability{} +var _ Capability = IDCapability{} + +func NewIDCapability( + id UInt64, + address Address, + borrowType Type, +) IDCapability { + return IDCapability{ + ID: id, + Address: address, + BorrowType: borrowType, + } +} + +func NewMeteredIDCapability( + gauge common.MemoryGauge, + id UInt64, + address Address, + borrowType Type, +) IDCapability { + common.UseMemory(gauge, common.CadenceIDCapabilityValueMemoryUsage) + return NewIDCapability( + id, + address, + borrowType, + ) +} + +func (IDCapability) isValue() {} + +func (IDCapability) isCapability() {} + +func (v IDCapability) Type() Type { + return NewCapabilityType(v.BorrowType) +} + +func (v IDCapability) MeteredType(gauge common.MemoryGauge) Type { + return NewMeteredCapabilityType(gauge, v.BorrowType) +} + +func (IDCapability) ToGoValue() any { + return nil +} + +func (v IDCapability) String() string { + return format.IDCapability( + v.BorrowType.ID(), + v.Address.String(), + v.ID.String(), + ) +} + // Enum type Enum struct { EnumType *EnumType diff --git a/values_test.go b/values_test.go index 696d8eb7d1..4e77e952ec 100644 --- a/values_test.go +++ b/values_test.go @@ -376,18 +376,26 @@ func newValueTestCases() map[string]valueTestCase { expectedType: NewMetaType(), string: "Type()", }, - "Capability": { - value: NewStorageCapability( - 3, + "Capability (Path)": { + value: NewPathCapability( BytesToAddress([]byte{1, 2, 3, 4, 5}), Path{ - Domain: common.PathDomainStorage, + Domain: common.PathDomainPublic, Identifier: "foo", }, IntType{}, ), expectedType: NewCapabilityType(IntType{}), - string: "Capability(id: 3, address: 0x0000000102030405, path: /storage/foo)", + string: "Capability(address: 0x0000000102030405, path: /public/foo)", + }, + "Capability (ID)": { + value: NewIDCapability( + 3, + BytesToAddress([]byte{1, 2, 3, 4, 5}), + IntType{}, + ), + expectedType: NewCapabilityType(IntType{}), + string: "Capability(address: 0x0000000102030405, id: 3)", }, "Function": { value: NewFunction( @@ -793,7 +801,22 @@ func TestValue_Type(t *testing.T) { if !testCase.noType { // Check if the type is not a duplicate of some other type // i.e: two values can't return the same type. - require.NotContains(t, checkedTypes, returnedType) + // + // Current known exceptions: + // - Capability: PathCapabilityValue | IDCapabilityValue + + var ignoreDuplicateType bool + + if _, ok := returnedType.(*CapabilityType); ok { + switch value.(type) { + case IDCapability, PathCapability: + ignoreDuplicateType = true + } + } + + if !ignoreDuplicateType { + require.NotContains(t, checkedTypes, returnedType) + } checkedTypes[returnedType] = struct{}{} } }) From c937a7458ee6bb48dc678595bf6ff318dbd3a15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 16:57:40 -0700 Subject: [PATCH 116/246] fix error message --- runtime/convertTypes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 1c277d45a2..c2a4c830aa 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -680,6 +680,6 @@ func ImportType(memoryGauge common.MemoryGauge, t cadence.Type) interpreter.Stat case cadence.DeployedContractType: return interpreter.NewPrimitiveStaticType(memoryGauge, interpreter.PrimitiveStaticTypeDeployedContract) default: - panic(fmt.Sprintf("cannot export type of type %T", t)) + panic(fmt.Sprintf("cannot import type of type %T", t)) } } From 70a95606a311060af59f48c9f069497f28e9c281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 16:58:50 -0700 Subject: [PATCH 117/246] make ID capability borrow type non-optional --- runtime/convertValues.go | 8 ++-- runtime/convertValues_test.go | 25 ------------- runtime/format/capability.go | 9 +---- runtime/interpreter/decode.go | 17 +-------- runtime/interpreter/encode.go | 56 ++++++++++++++++------------ runtime/interpreter/encoding_test.go | 11 ++---- runtime/interpreter/value.go | 40 ++++---------------- runtime/interpreter/value_test.go | 31 --------------- values.go | 7 +++- values_test.go | 12 ++++++ 10 files changed, 68 insertions(+), 148 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 17e48b5f14..6b6b3fd14f 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -654,16 +654,14 @@ func exportIDCapabilityValue( v *interpreter.IDCapabilityValue, inter *interpreter.Interpreter, ) (cadence.IDCapability, error) { - var borrowType sema.Type - if v.BorrowType != nil { - borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) - } + borrowType := inter.MustConvertStaticToSemaType(v.BorrowType) + exportedBorrowType := ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}) return cadence.NewMeteredIDCapability( inter, cadence.NewMeteredUInt64(inter, uint64(v.ID)), cadence.NewMeteredAddress(inter, v.Address), - ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), + exportedBorrowType, ), nil } diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 8fd03ae131..4a2abf1247 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -2271,31 +2271,6 @@ func TestExportIDCapabilityValue(t *testing.T) { assert.Equal(t, expected, actual) }) - - t.Run("no borrow type", func(t *testing.T) { - - capability := interpreter.NewUnmeteredIDCapabilityValue( - 3, - interpreter.AddressValue{0x1}, - nil, - ) - - actual, err := exportValueWithInterpreter( - capability, - newTestInterpreter(t), - interpreter.EmptyLocationRange, - seenReferences{}, - ) - require.NoError(t, err) - - expected := cadence.NewIDCapability( - 3, - cadence.Address{0x1}, - nil, - ) - - assert.Equal(t, expected, actual) - }) } func TestExportPathLinkValue(t *testing.T) { diff --git a/runtime/format/capability.go b/runtime/format/capability.go index 31923f00cc..3403a70ca7 100644 --- a/runtime/format/capability.go +++ b/runtime/format/capability.go @@ -37,14 +37,9 @@ func PathCapability(borrowType string, address string, path string) string { } func IDCapability(borrowType string, address string, id string) string { - var typeArgument string - if borrowType != "" { - typeArgument = fmt.Sprintf("<%s>", borrowType) - } - return fmt.Sprintf( - "Capability%s(address: %s, id: %s)", - typeArgument, + "Capability<%s>(address: %s, id: %s)", + borrowType, address, id, ) diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index b28215794e..e517336587 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1027,22 +1027,7 @@ func (d StorableDecoder) decodeIDCapability() (*IDCapabilityValue, error) { // Decode borrow type at array index encodedIDCapabilityValueBorrowTypeFieldKey - // borrow type (optional, for backwards compatibility) - // Capabilities used to be untyped, i.e. they didn't have a borrow type. - // Later an optional type parameter, the borrow type, was added to it, - // which specifies as what type the capability should be borrowed. - // - // The decoding must be backwards-compatible and support both capability values - // with a borrow type and ones without - - var borrowType StaticType - - // Optional borrow type can be CBOR nil. - err = d.decoder.DecodeNil() - if _, ok := err.(*cbor.WrongTypeError); ok { - borrowType, err = d.DecodeStaticType() - } - + borrowType, err := d.DecodeStaticType() if err != nil { return nil, errors.NewUnexpectedError("invalid capability borrow type encoding: %w", err) } diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index efdd8e6fb1..f57ec245ad 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -773,7 +773,12 @@ func (v *PathCapabilityValue) Encode(e *atree.Encoder) error { } // Encode borrow type at array index encodedPathCapabilityValueBorrowTypeFieldKey - return EncodeStaticType(e.CBOR, v.BorrowType) + + if v.BorrowType == nil { + return e.CBOR.EncodeNil() + } else { + return v.BorrowType.Encode(e.CBOR) + } } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -824,7 +829,7 @@ func (v *IDCapabilityValue) Encode(e *atree.Encoder) error { } // Encode borrow type at array index encodedIDCapabilityValueBorrowTypeFieldKey - return EncodeStaticType(e.CBOR, v.BorrowType) + return v.BorrowType.Encode(e.CBOR) } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -984,7 +989,7 @@ func (v PathLinkValue) Encode(e *atree.Encoder) error { return err } // Encode type at array index encodedPathLinkValueTypeFieldKey - return EncodeStaticType(e.CBOR, v.Type) + return v.Type.Encode(e.CBOR) } // cborAccountLinkValue represents the CBOR value: @@ -1078,7 +1083,11 @@ func (v TypeValue) Encode(e *atree.Encoder) error { } // Encode type at array index encodedTypeValueTypeFieldKey - return EncodeStaticType(e.CBOR, v.Type) + if v.Type == nil { + return e.CBOR.EncodeNil() + } else { + return v.Type.Encode(e.CBOR) + } } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1117,7 +1126,7 @@ func (v *StorageCapabilityControllerValue) Encode(e *atree.Encoder) error { } // Encode borrow type at array index encodedStorageCapabilityControllerValueBorrowTypeFieldKey - err = EncodeStaticType(e.CBOR, v.BorrowType) + err = v.BorrowType.Encode(e.CBOR) if err != nil { return err } @@ -1166,7 +1175,7 @@ func (v *AccountCapabilityControllerValue) Encode(e *atree.Encoder) error { } // Encode borrow type at array index encodedAccountCapabilityControllerValueBorrowTypeFieldKey - err = EncodeStaticType(e.CBOR, v.BorrowType) + err = v.BorrowType.Encode(e.CBOR) if err != nil { return err } @@ -1179,7 +1188,7 @@ func StaticTypeToBytes(t StaticType) (cbor.RawMessage, error) { var buf bytes.Buffer enc := CBOREncMode.NewStreamEncoder(&buf) - err := EncodeStaticType(enc, t) + err := t.Encode(enc) if err != nil { return nil, err } @@ -1192,14 +1201,6 @@ func StaticTypeToBytes(t StaticType) (cbor.RawMessage, error) { return buf.Bytes(), nil } -func EncodeStaticType(e *cbor.StreamEncoder, t StaticType) error { - if t == nil { - return e.EncodeNil() - } - - return t.Encode(e) -} - // Encode encodes PrimitiveStaticType as // // cbor.Tag{ @@ -1231,7 +1232,12 @@ func (t OptionalStaticType) Encode(e *cbor.StreamEncoder) error { if err != nil { return err } - return EncodeStaticType(e, t.Type) + + if t.Type == nil { + return e.EncodeNil() + } else { + return t.Type.Encode(e) + } } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1334,7 +1340,7 @@ func (t VariableSizedStaticType) Encode(e *cbor.StreamEncoder) error { if err != nil { return err } - return EncodeStaticType(e, t.Type) + return t.Type.Encode(e) } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1375,7 +1381,7 @@ func (t ConstantSizedStaticType) Encode(e *cbor.StreamEncoder) error { return err } // Encode type at array index encodedConstantSizedStaticTypeTypeFieldKey - return EncodeStaticType(e, t.Type) + return t.Type.Encode(e) } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1416,7 +1422,7 @@ func (t ReferenceStaticType) Encode(e *cbor.StreamEncoder) error { return err } // Encode type at array index encodedReferenceStaticTypeTypeFieldKey - return EncodeStaticType(e, t.BorrowedType) + return t.BorrowedType.Encode(e) } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1452,12 +1458,12 @@ func (t DictionaryStaticType) Encode(e *cbor.StreamEncoder) error { return err } // Encode key type at array index encodedDictionaryStaticTypeKeyTypeFieldKey - err = EncodeStaticType(e, t.KeyType) + err = t.KeyType.Encode(e) if err != nil { return err } // Encode value type at array index encodedDictionaryStaticTypeValueTypeFieldKey - return EncodeStaticType(e, t.ValueType) + return t.ValueType.Encode(e) } // NOTE: NEVER change, only add/increment; ensure uint64 @@ -1493,7 +1499,7 @@ func (t *RestrictedStaticType) Encode(e *cbor.StreamEncoder) error { return err } // Encode type at array index encodedRestrictedStaticTypeTypeFieldKey - err = EncodeStaticType(e, t.Type) + err = t.Type.Encode(e) if err != nil { return err } @@ -1526,7 +1532,11 @@ func (t CapabilityStaticType) Encode(e *cbor.StreamEncoder) error { if err != nil { return err } - return EncodeStaticType(e, t.BorrowType) + if t.BorrowType == nil { + return e.EncodeNil() + } else { + return t.BorrowType.Encode(e) + } } func (t FunctionStaticType) Encode(_ *cbor.StreamEncoder) error { diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index c6d5e6b49b..7cb648c4c6 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3337,12 +3337,6 @@ func TestEncodeDecodeIDCapabilityValue(t *testing.T) { t.Parallel() - value := NewUnmeteredIDCapabilityValue( - 4, - NewUnmeteredAddressValueFromBytes([]byte{0x2}), - nil, - ) - encoded := []byte{ // tag 0xd8, CBORTagIDCapabilityValue, @@ -3362,8 +3356,9 @@ func TestEncodeDecodeIDCapabilityValue(t *testing.T) { testEncodeDecode(t, encodeDecodeTest{ - value: value, - encoded: encoded, + encoded: encoded, + decodeOnly: true, + invalid: true, }, ) }) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index ae3fe5444a..4ae25243d0 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18487,12 +18487,8 @@ func (v *IDCapabilityValue) String() string { } func (v *IDCapabilityValue) RecursiveString(seenReferences SeenReferences) string { - var borrowType string - if v.BorrowType != nil { - borrowType = v.BorrowType.String() - } return format.IDCapability( - borrowType, + v.BorrowType.String(), v.Address.RecursiveString(seenReferences), v.ID.RecursiveString(seenReferences), ) @@ -18501,13 +18497,8 @@ func (v *IDCapabilityValue) RecursiveString(seenReferences SeenReferences) strin func (v *IDCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, seenReferences SeenReferences) string { common.UseMemory(memoryGauge, common.IDCapabilityValueStringMemoryUsage) - var borrowType string - if v.BorrowType != nil { - borrowType = v.BorrowType.MeteredString(memoryGauge) - } - return format.IDCapability( - borrowType, + v.BorrowType.MeteredString(memoryGauge), v.Address.MeteredString(memoryGauge, seenReferences), v.ID.MeteredString(memoryGauge, seenReferences), ) @@ -18516,21 +18507,15 @@ func (v *IDCapabilityValue) MeteredString(memoryGauge common.MemoryGauge, seenRe func (v *IDCapabilityValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { case sema.CapabilityTypeBorrowFunctionName: - var borrowType *sema.ReferenceType - if v.BorrowType != nil { - // this function will panic already if this conversion fails - borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) - } + // this function will panic already if this conversion fails + borrowType, _ := interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) // TODO: _ = borrowType panic("TODO") case sema.CapabilityTypeCheckFunctionName: - var borrowType *sema.ReferenceType - if v.BorrowType != nil { - // this function will panic already if this conversion fails - borrowType, _ = interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) - } + // this function will panic already if this conversion fails + borrowType, _ := interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) // TODO: _ = borrowType panic("TODO") @@ -18569,18 +18554,9 @@ func (v *IDCapabilityValue) Equal(interpreter *Interpreter, locationRange Locati return false } - // BorrowType is optional - - if v.BorrowType == nil { - if otherCapability.BorrowType != nil { - return false - } - } else if !v.BorrowType.Equal(otherCapability.BorrowType) { - return false - } - return otherCapability.ID == v.ID && - otherCapability.Address.Equal(interpreter, locationRange, v.Address) + otherCapability.Address.Equal(interpreter, locationRange, v.Address) && + otherCapability.BorrowType.Equal(v.BorrowType) } func (*IDCapabilityValue) IsStorable() bool { diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index bac4ecfb80..f19be940d5 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -1130,14 +1130,6 @@ func TestStringer(t *testing.T) { ), expected: "Capability(address: 0x0000000102030405, id: 6)", }, - "ID Capability without borrow type": { - value: NewUnmeteredIDCapabilityValue( - 6, - NewUnmeteredAddressValueFromBytes([]byte{1, 2, 3, 4, 5}), - nil, - ), - expected: "Capability(address: 0x0000000102030405, id: 6)", - }, "Recursive ephemeral reference (array)": { value: func() Value { array := NewArrayValue( @@ -1918,29 +1910,6 @@ func TestIDCapabilityValue_Equal(t *testing.T) { ) }) - t.Run("equal, no borrow type", func(t *testing.T) { - - t.Parallel() - - inter := newTestInterpreter(t) - - require.True(t, - NewUnmeteredIDCapabilityValue( - 4, - NewUnmeteredAddressValueFromBytes([]byte{0x1}), - nil, - ).Equal( - inter, - EmptyLocationRange, - NewUnmeteredIDCapabilityValue( - 4, - NewUnmeteredAddressValueFromBytes([]byte{0x1}), - nil, - ), - ), - ) - }) - t.Run("different addresses", func(t *testing.T) { t.Parallel() diff --git a/values.go b/values.go index bd4e1cb599..83f6a3e764 100644 --- a/values.go +++ b/values.go @@ -2074,8 +2074,13 @@ func (PathCapability) ToGoValue() any { } func (v PathCapability) String() string { + var borrowType string + if v.BorrowType != nil { + borrowType = v.BorrowType.ID() + } + return format.PathCapability( - v.BorrowType.ID(), + borrowType, v.Address.String(), v.Path.String(), ) diff --git a/values_test.go b/values_test.go index 4e77e952ec..fc37ab7736 100644 --- a/values_test.go +++ b/values_test.go @@ -388,6 +388,18 @@ func newValueTestCases() map[string]valueTestCase { expectedType: NewCapabilityType(IntType{}), string: "Capability(address: 0x0000000102030405, path: /public/foo)", }, + "Capability (Path, no borrow type)": { + value: NewPathCapability( + BytesToAddress([]byte{1, 2, 3, 4, 5}), + Path{ + Domain: common.PathDomainPublic, + Identifier: "foo", + }, + nil, + ), + expectedType: NewCapabilityType(nil), + string: "Capability(address: 0x0000000102030405, path: /public/foo)", + }, "Capability (ID)": { value: NewIDCapability( 3, From 6a704015a2251bde2c4b64de08fc969d05b85da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Apr 2023 17:43:39 -0700 Subject: [PATCH 118/246] revert refactor --- runtime/interpreter/interpreter.go | 118 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 036e3dc9c7..6bad208fd6 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3931,66 +3931,6 @@ func (interpreter *Interpreter) authAccountUnlinkFunction(addressValue AddressVa ) } -func (interpreter *Interpreter) BorrowPathCapability( - address common.Address, - pathValue PathValue, - borrowType *sema.ReferenceType, - locationRange LocationRange, -) ReferenceValue { - target, authorized, err := - interpreter.GetPathCapabilityFinalTarget( - address, - pathValue, - borrowType, - locationRange, - ) - if err != nil { - panic(err) - } - - if target == nil { - return nil - } - - switch target := target.(type) { - case AccountCapabilityTarget: - return NewAccountReferenceValue( - interpreter, - address, - pathValue, - borrowType.Type, - ) - - case PathCapabilityTarget: - targetPath := PathValue(target) - - reference := NewStorageReferenceValue( - interpreter, - authorized, - address, - targetPath, - borrowType.Type, - ) - - // Attempt to dereference, - // which reads the stored value - // and performs a dynamic type check - - value, err := reference.dereference(interpreter, locationRange) - if err != nil { - panic(err) - } - if value == nil { - return nil - } - - return reference - - default: - panic(errors.NewUnreachableError()) - } -} - func (interpreter *Interpreter) pathCapabilityBorrowFunction( addressValue AddressValue, pathValue PathValue, @@ -4006,6 +3946,7 @@ func (interpreter *Interpreter) pathCapabilityBorrowFunction( func(invocation Invocation) Value { interpreter := invocation.Interpreter + locationRange := invocation.LocationRange // NOTE: if a type argument is provided for the function, // use it *instead* of the type of the value (if any) @@ -4024,7 +3965,58 @@ func (interpreter *Interpreter) pathCapabilityBorrowFunction( panic(errors.NewUnreachableError()) } - reference := interpreter.BorrowPathCapability(address, pathValue, borrowType, invocation.LocationRange) + target, authorized, err := + interpreter.GetPathCapabilityFinalTarget( + address, + pathValue, + borrowType, + locationRange, + ) + if err != nil { + panic(err) + } + + var reference ReferenceValue + + switch target := target.(type) { + case nil: + reference = nil + + case AccountCapabilityTarget: + reference = NewAccountReferenceValue( + interpreter, + address, + pathValue, + borrowType.Type, + ) + + case PathCapabilityTarget: + targetPath := PathValue(target) + + storageReference := NewStorageReferenceValue( + interpreter, + authorized, + address, + targetPath, + borrowType.Type, + ) + + // Attempt to dereference, + // which reads the stored value + // and performs a dynamic type check + + value, err := storageReference.dereference(interpreter, locationRange) + if err != nil { + panic(err) + } + if value != nil { + reference = storageReference + } + + default: + panic(errors.NewUnreachableError()) + } + if reference == nil { return Nil } @@ -4173,6 +4165,10 @@ func (interpreter *Interpreter) GetPathCapabilityFinalTarget( false, nil + case *IDCapabilityValue: + // TODO: follow target? + return nil, false, nil + default: return PathCapabilityTarget(path), wantedReferenceType.Authorized, From a9c3caa0c49985ea779f930afb509b1771476c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 27 Apr 2023 15:18:22 -0700 Subject: [PATCH 119/246] make capability controller borrow types reference static types --- runtime/interpreter/decode.go | 18 +- runtime/interpreter/encoding_test.go | 382 +++++++++++------- .../value_accountcapabilitycontroller.go | 10 +- .../value_storagecapabilitycontroller.go | 10 +- 4 files changed, 264 insertions(+), 156 deletions(-) diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index e517336587..6ae72f1789 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1069,6 +1069,13 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability if err != nil { return nil, errors.NewUnexpectedError("invalid storage capability controller borrow type encoding: %w", err) } + borrowReferenceStaticType, ok := borrowStaticType.(ReferenceStaticType) + if !ok { + return nil, errors.NewUnexpectedError( + "invalid storage capability controller borrow type encoding: expected reference static type, got %T", + borrowStaticType, + ) + } // Decode capability ID at array index encodedStorageCapabilityControllerValueCapabilityIDFieldKey @@ -1099,7 +1106,7 @@ func (d StorableDecoder) decodeStorageCapabilityController() (*StorageCapability return NewStorageCapabilityControllerValue( d.memoryGauge, - borrowStaticType, + borrowReferenceStaticType, UInt64Value(capabilityID), pathValue, ), nil @@ -1134,6 +1141,13 @@ func (d StorableDecoder) decodeAccountCapabilityController() (*AccountCapability if err != nil { return nil, errors.NewUnexpectedError("invalid account capability controller borrow type encoding: %w", err) } + borrowReferenceStaticType, ok := borrowStaticType.(ReferenceStaticType) + if !ok { + return nil, errors.NewUnexpectedError( + "invalid account capability controller borrow type encoding: expected reference static type, got %T", + borrowStaticType, + ) + } // Decode capability ID at array index encodedAccountCapabilityControllerValueCapabilityIDFieldKey @@ -1147,7 +1161,7 @@ func (d StorableDecoder) decodeAccountCapabilityController() (*AccountCapability return NewAccountCapabilityControllerValue( d.memoryGauge, - borrowStaticType, + borrowReferenceStaticType, UInt64Value(capabilityID), ), nil } diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 7cb648c4c6..3157c96c4c 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -4120,16 +4120,10 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { const capabilityID = 42 - t.Run("primitive, Bool", func(t *testing.T) { + t.Run("non-reference, primitive, Bool", func(t *testing.T) { t.Parallel() - value := &StorageCapabilityControllerValue{ - TargetPath: publicPathValue, - BorrowType: ConvertSemaToPrimitiveStaticType(nil, sema.BoolType), - CapabilityID: capabilityID, - } - encoded := assemble( // tag 0xd8, CBORTagPrimitiveStaticType, @@ -4138,27 +4132,33 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { testEncodeDecode(t, encodeDecodeTest{ - value: value, - encoded: encoded, + decodeOnly: true, + invalid: true, + encoded: encoded, }, ) }) - t.Run("optional, primitive, bool", func(t *testing.T) { + t.Run("unauthorized reference, primitive, Bool", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: OptionalStaticType{ - Type: PrimitiveStaticTypeBool, + BorrowType: ReferenceStaticType{ + BorrowedType: PrimitiveStaticTypeBool, + Authorized: false, }, CapabilityID: capabilityID, } encoded := assemble( // tag - 0xd8, CBORTagOptionalStaticType, + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, // tag 0xd8, CBORTagPrimitiveStaticType, 0x6, @@ -4172,35 +4172,29 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("composite, struct, qualified identifier", func(t *testing.T) { + t.Run("authorized reference, primitive, Bool", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: NewCompositeStaticTypeComputeTypeID( - nil, - utils.TestLocation, - "SimpleStruct", - ), + BorrowType: ReferenceStaticType{ + BorrowedType: PrimitiveStaticTypeBool, + Authorized: true, + }, CapabilityID: capabilityID, } encoded := assemble( // tag - 0xd8, CBORTagCompositeStaticType, + 0xd8, CBORTagReferenceStaticType, // array, 2 items follow 0x82, + // true + 0xf5, // tag - 0xd8, CBORTagStringLocation, - // UTF-8 string, length 4 - 0x64, - // t, e, s, t - 0x74, 0x65, 0x73, 0x74, - // UTF-8 string, length 12 - 0x6c, - // SimpleStruct - 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0xd8, CBORTagPrimitiveStaticType, + 0x6, ) testEncodeDecode(t, @@ -4211,34 +4205,32 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("interface, struct, qualified identifier", func(t *testing.T) { + t.Run("unauthorized reference, optional, primitive, Bool", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: InterfaceStaticType{ - Location: utils.TestLocation, - QualifiedIdentifier: "SimpleInterface", + BorrowType: ReferenceStaticType{ + BorrowedType: OptionalStaticType{ + Type: PrimitiveStaticTypeBool, + }, }, CapabilityID: capabilityID, } encoded := assemble( // tag - 0xd8, CBORTagInterfaceStaticType, + 0xd8, CBORTagReferenceStaticType, // array, 2 items follow 0x82, + // false + 0xf4, // tag - 0xd8, CBORTagStringLocation, - // UTF-8 string, length 4 - 0x64, - // t, e, s, t - 0x74, 0x65, 0x73, 0x74, - // UTF-8 string, length 22 - 0x6F, - // SimpleInterface - 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0xd8, CBORTagOptionalStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, ) testEncodeDecode(t, @@ -4249,24 +4241,43 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("variable-sized, bool", func(t *testing.T) { + t.Run("unauthorized reference, composite, struct, qualified identifier", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: VariableSizedStaticType{ - Type: PrimitiveStaticTypeBool, + BorrowType: ReferenceStaticType{ + BorrowedType: NewCompositeStaticTypeComputeTypeID( + nil, + utils.TestLocation, + "SimpleStruct", + ), }, CapabilityID: capabilityID, } encoded := assemble( // tag - 0xd8, CBORTagVariableSizedStaticType, + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, // tag - 0xd8, CBORTagPrimitiveStaticType, - 0x6, + 0xd8, CBORTagCompositeStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 12 + 0x6c, + // SimpleStruct + 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, ) testEncodeDecode(t, @@ -4277,29 +4288,42 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("constant-sized, bool", func(t *testing.T) { + t.Run("unauthorized reference, interface, struct, qualified identifier", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: ConstantSizedStaticType{ - Type: PrimitiveStaticTypeBool, - Size: 42, + BorrowType: ReferenceStaticType{ + BorrowedType: InterfaceStaticType{ + Location: utils.TestLocation, + QualifiedIdentifier: "SimpleInterface", + }, }, CapabilityID: capabilityID, } encoded := assemble( // tag - 0xd8, CBORTagConstantSizedStaticType, + 0xd8, CBORTagReferenceStaticType, // array, 2 items follow 0x82, - // positive integer 42 - 0x18, 0x2A, + // false + 0xf4, // tag - 0xd8, CBORTagPrimitiveStaticType, - 0x6, + 0xd8, CBORTagInterfaceStaticType, + // array, 2 items follow + 0x82, + // tag + 0xd8, CBORTagStringLocation, + // UTF-8 string, length 4 + 0x64, + // t, e, s, t + 0x74, 0x65, 0x73, 0x74, + // UTF-8 string, length 22 + 0x6F, + // SimpleInterface + 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, ) testEncodeDecode(t, @@ -4310,15 +4334,16 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("reference type, authorized, bool", func(t *testing.T) { + t.Run("unauthorized reference, variable-sized, bool", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, BorrowType: ReferenceStaticType{ - Authorized: true, - BorrowedType: PrimitiveStaticTypeBool, + BorrowedType: VariableSizedStaticType{ + Type: PrimitiveStaticTypeBool, + }, }, CapabilityID: capabilityID, } @@ -4328,8 +4353,10 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { 0xd8, CBORTagReferenceStaticType, // array, 2 items follow 0x82, - // true - 0xf5, + // false + 0xf4, + // tag + 0xd8, CBORTagVariableSizedStaticType, // tag 0xd8, CBORTagPrimitiveStaticType, 0x6, @@ -4343,15 +4370,17 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("reference type, unauthorized, bool", func(t *testing.T) { + t.Run("unauthorized reference, constant-sized, bool", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, BorrowType: ReferenceStaticType{ - Authorized: false, - BorrowedType: PrimitiveStaticTypeBool, + BorrowedType: ConstantSizedStaticType{ + Type: PrimitiveStaticTypeBool, + Size: 42, + }, }, CapabilityID: capabilityID, } @@ -4364,6 +4393,12 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { // false 0xf4, // tag + 0xd8, CBORTagConstantSizedStaticType, + // array, 2 items follow + 0x82, + // positive integer 42 + 0x18, 0x2A, + // tag 0xd8, CBORTagPrimitiveStaticType, 0x6, ) @@ -4376,20 +4411,28 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("dictionary, bool, string", func(t *testing.T) { + t.Run("unauthorized reference, dictionary, bool, string", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: DictionaryStaticType{ - KeyType: PrimitiveStaticTypeBool, - ValueType: PrimitiveStaticTypeString, + BorrowType: ReferenceStaticType{ + BorrowedType: DictionaryStaticType{ + KeyType: PrimitiveStaticTypeBool, + ValueType: PrimitiveStaticTypeString, + }, }, CapabilityID: capabilityID, } encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, // tag 0xd8, CBORTagDictionaryStaticType, // array, 2 items follow @@ -4410,26 +4453,28 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("restricted", func(t *testing.T) { + t.Run("unauthorized reference, restricted", func(t *testing.T) { t.Parallel() value := &StorageCapabilityControllerValue{ TargetPath: publicPathValue, - BorrowType: &RestrictedStaticType{ - Type: NewCompositeStaticTypeComputeTypeID( - nil, - utils.TestLocation, - "S", - ), - Restrictions: []InterfaceStaticType{ - { - Location: utils.TestLocation, - QualifiedIdentifier: "I1", - }, - { - Location: utils.TestLocation, - QualifiedIdentifier: "I2", + BorrowType: ReferenceStaticType{ + BorrowedType: &RestrictedStaticType{ + Type: NewCompositeStaticTypeComputeTypeID( + nil, + utils.TestLocation, + "S", + ), + Restrictions: []InterfaceStaticType{ + { + Location: utils.TestLocation, + QualifiedIdentifier: "I1", + }, + { + Location: utils.TestLocation, + QualifiedIdentifier: "I2", + }, }, }, }, @@ -4437,6 +4482,12 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { } encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, // tag 0xd8, CBORTagRestrictedStaticType, // array, 2 items follow @@ -4495,59 +4546,6 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { ) }) - t.Run("capability, none", func(t *testing.T) { - - t.Parallel() - - value := &StorageCapabilityControllerValue{ - TargetPath: publicPathValue, - BorrowType: CapabilityStaticType{}, - CapabilityID: capabilityID, - } - - encoded := assemble( - // tag - 0xd8, CBORTagCapabilityStaticType, - // null - 0xf6, - ) - - testEncodeDecode(t, - encodeDecodeTest{ - value: value, - encoded: encoded, - }, - ) - }) - - t.Run("capability, primitive, bool", func(t *testing.T) { - - t.Parallel() - - value := &StorageCapabilityControllerValue{ - TargetPath: publicPathValue, - BorrowType: CapabilityStaticType{ - BorrowType: PrimitiveStaticTypeBool, - }, - CapabilityID: capabilityID, - } - - encoded := assemble( - // tag - 0xd8, CBORTagCapabilityStaticType, - // tag - 0xd8, CBORTagPrimitiveStaticType, - 0x6, - ) - - testEncodeDecode(t, - encodeDecodeTest{ - value: value, - encoded: encoded, - }, - ) - }) - t.Run("larger than max inline size", func(t *testing.T) { t.Parallel() @@ -4561,8 +4559,10 @@ func TestEncodeDecodeStorageCapabilityControllerValue(t *testing.T) { } expected := &StorageCapabilityControllerValue{ - TargetPath: path, - BorrowType: PrimitiveStaticTypeNever, + TargetPath: path, + BorrowType: ReferenceStaticType{ + BorrowedType: PrimitiveStaticTypeNever, + }, CapabilityID: capabilityID, } @@ -4603,14 +4603,33 @@ func TestEncodeDecodeAccountCapabilityControllerValue(t *testing.T) { const capabilityID = 42 - t.Run("AuthAccount reference, unauthorized, ", func(t *testing.T) { + t.Run("non-reference, primitive, Bool", func(t *testing.T) { + + t.Parallel() + + encoded := assemble( + // tag + 0xd8, CBORTagPrimitiveStaticType, + 0x6, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + decodeOnly: true, + invalid: true, + encoded: encoded, + }, + ) + }) + + t.Run("unauthorized reference, AuthAccount", func(t *testing.T) { t.Parallel() value := &AccountCapabilityControllerValue{ BorrowType: ReferenceStaticType{ Authorized: false, - BorrowedType: PrimitiveStaticTypeBool, + BorrowedType: PrimitiveStaticTypeAuthAccount, }, CapabilityID: capabilityID, } @@ -4622,9 +4641,82 @@ func TestEncodeDecodeAccountCapabilityControllerValue(t *testing.T) { 0x82, // false 0xf4, + 0xd8, CBORTagPrimitiveStaticType, + // unsigned 90 + 0x18, 0x5a, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("unauthorized reference, restricted AuthAccount", func(t *testing.T) { + + t.Parallel() + + value := &AccountCapabilityControllerValue{ + BorrowType: ReferenceStaticType{ + BorrowedType: &RestrictedStaticType{ + Type: PrimitiveStaticTypeAuthAccount, + }, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, CBORTagRestrictedStaticType, + // array, 2 items follow + 0x82, // tag 0xd8, CBORTagPrimitiveStaticType, - 0x6, + // unsigned 90 + 0x18, 0x5a, + // array, length 0 + 0x80, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("authorized reference, AuthAccount", func(t *testing.T) { + + t.Parallel() + + value := &AccountCapabilityControllerValue{ + BorrowType: ReferenceStaticType{ + Authorized: true, + BorrowedType: PrimitiveStaticTypeAuthAccount, + }, + CapabilityID: capabilityID, + } + + encoded := assemble( + // tag + 0xd8, CBORTagReferenceStaticType, + // array, 2 items follow + 0x82, + // true + 0xf5, + // tag + 0xd8, CBORTagPrimitiveStaticType, + // unsigned 90 + 0x18, 0x5a, ) testEncodeDecode(t, @@ -4650,7 +4742,9 @@ func TestEncodeDecodeAccountCapabilityControllerValue(t *testing.T) { } expected := &AccountCapabilityControllerValue{ - BorrowType: borrowType, + BorrowType: ReferenceStaticType{ + BorrowedType: borrowType, + }, CapabilityID: capabilityID, } diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index f2c5e4ae5a..26f2cbfe7d 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -30,29 +30,29 @@ import ( // AccountCapabilityControllerValue type AccountCapabilityControllerValue struct { - BorrowType StaticType + BorrowType ReferenceStaticType CapabilityID UInt64Value } func NewUnmeteredAccountCapabilityControllerValue( - staticType StaticType, + borrowType ReferenceStaticType, capabilityID UInt64Value, ) *AccountCapabilityControllerValue { return &AccountCapabilityControllerValue{ - BorrowType: staticType, + BorrowType: borrowType, CapabilityID: capabilityID, } } func NewAccountCapabilityControllerValue( memoryGauge common.MemoryGauge, - staticType StaticType, + borrowType ReferenceStaticType, capabilityID UInt64Value, ) *AccountCapabilityControllerValue { // Constant because its constituents are already metered. common.UseMemory(memoryGauge, common.AccountCapabilityControllerValueMemoryUsage) return NewUnmeteredAccountCapabilityControllerValue( - staticType, + borrowType, capabilityID, ) } diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 75724f5bf2..4d0cf4448c 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -35,19 +35,19 @@ type CapabilityControllerValue interface { // StorageCapabilityControllerValue type StorageCapabilityControllerValue struct { - BorrowType StaticType + BorrowType ReferenceStaticType TargetPath PathValue RetargetFunction FunctionValue CapabilityID UInt64Value } func NewUnmeteredStorageCapabilityControllerValue( - staticType StaticType, + borrowType ReferenceStaticType, capabilityID UInt64Value, targetPath PathValue, ) *StorageCapabilityControllerValue { return &StorageCapabilityControllerValue{ - BorrowType: staticType, + BorrowType: borrowType, TargetPath: targetPath, CapabilityID: capabilityID, } @@ -55,14 +55,14 @@ func NewUnmeteredStorageCapabilityControllerValue( func NewStorageCapabilityControllerValue( memoryGauge common.MemoryGauge, - staticType StaticType, + borrowType ReferenceStaticType, capabilityID UInt64Value, targetPath PathValue, ) *StorageCapabilityControllerValue { // Constant because its constituents are already metered. common.UseMemory(memoryGauge, common.StorageCapabilityControllerValueMemoryUsage) return NewUnmeteredStorageCapabilityControllerValue( - staticType, + borrowType, capabilityID, targetPath, ) From 1feaf943662de6d82f1b1cc62a06d34817e6fdce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 27 Apr 2023 15:19:45 -0700 Subject: [PATCH 120/246] implement Auth/PublicAccount.Capabilities.get/borrow --- runtime/capabilitycontrollers_test.go | 38 +++++ .../value_accountcapabilitycontroller.go | 4 + .../value_storagecapabilitycontroller.go | 5 + runtime/stdlib/account.go | 141 +++++++++++++----- 4 files changed, 148 insertions(+), 40 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 2aec2831f7..87988cc91d 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -208,6 +208,44 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) + t.Run("get existing, with valid type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R> = %s.capabilities.get<&Test.R>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.id == expectedCapID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + t.Run("get and borrow existing, with valid type", func(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 26f2cbfe7d..ae6b2298d9 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -67,6 +67,10 @@ func (*AccountCapabilityControllerValue) isValue() {} func (*AccountCapabilityControllerValue) isCapabilityControllerValue() {} +func (v *AccountCapabilityControllerValue) CapabilityControllerBorrowType() ReferenceStaticType { + return v.BorrowType +} + func (v *AccountCapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitAccountCapabilityControllerValue(interpreter, v) } diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 4d0cf4448c..b8480c89d0 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -30,6 +30,7 @@ import ( type CapabilityControllerValue interface { Value isCapabilityControllerValue() + CapabilityControllerBorrowType() ReferenceStaticType } // StorageCapabilityControllerValue @@ -78,6 +79,10 @@ func (*StorageCapabilityControllerValue) isValue() {} func (*StorageCapabilityControllerValue) isCapabilityControllerValue() {} +func (v *StorageCapabilityControllerValue) CapabilityControllerBorrowType() ReferenceStaticType { + return v.BorrowType +} + func (v *StorageCapabilityControllerValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitStorageCapabilityControllerValue(interpreter, v) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 2b97319e70..3b4104fb5e 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -3009,6 +3009,94 @@ func newPublicAccountCapabilitiesValue( ) } +func borrowIDCapability( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + capabilityAddressValue interpreter.AddressValue, + capabilityIDValue interpreter.UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, + borrow bool, +) interpreter.Value { + + if wantedBorrowType == nil { + wantedBorrowType = capabilityBorrowType + } else { + // Ensure requested borrow type is not more permissive + // than the capability's borrow type: + // The requested type must be a supertype + + if !sema.IsSubType(capabilityBorrowType, wantedBorrowType) { + return nil + } + } + + capabilityAddress := capabilityAddressValue.ToAddress() + capabilityID := uint64(capabilityIDValue) + + controller := getCapabilityController(inter, capabilityAddress, capabilityID) + if controller == nil { + return nil + } + + // Ensure requested borrow type is not more permissive + // than the controller's borrow type: + // The requested type must be a supertype + + controllerBorrowStaticType := controller.CapabilityControllerBorrowType() + + controllerBorrowType, ok := + inter.MustConvertStaticToSemaType(controllerBorrowStaticType).(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + + if !sema.IsSubType(controllerBorrowType, wantedBorrowType) { + return nil + } + + // Return reference / capability value + + if borrow { + switch controller := controller.(type) { + case *interpreter.AccountCapabilityControllerValue: + return interpreter.NewAccountReferenceValue( + inter, + capabilityAddress, + // TODO: + interpreter.EmptyPathValue, + wantedBorrowType, + ) + + case *interpreter.StorageCapabilityControllerValue: + return interpreter.NewStorageReferenceValue( + inter, + wantedBorrowType.Authorized, + capabilityAddress, + controller.TargetPath, + wantedBorrowType.Type, + ) + + default: + panic(errors.NewUnreachableError()) + } + + } else { + wantedBorrowStaticType := + interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, wantedBorrowType) + if !ok { + panic(errors.NewUnreachableError()) + } + + return interpreter.NewIDCapabilityValue( + inter, + capabilityIDValue, + capabilityAddressValue, + wantedBorrowStaticType, + ) + } +} + func newAccountCapabilitiesGetFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, @@ -3066,53 +3154,26 @@ func newAccountCapabilitiesGetFunction( panic(errors.NewUnreachableError()) } - allowedBorrowType, ok := inter.MustConvertStaticToSemaType(readCapabilityValue.BorrowType).(*sema.ReferenceType) + capabilityBorrowType, ok := + inter.MustConvertStaticToSemaType(readCapabilityValue.BorrowType).(*sema.ReferenceType) if !ok { panic(errors.NewUnreachableError()) } - // Ensure requested borrow type is not more permissive - - if !sema.IsSubType(allowedBorrowType, wantedBorrowType) { - return interpreter.Nil - } - - // Return capability value + capabilityID := readCapabilityValue.ID + capabilityAddress := readCapabilityValue.Address - readCapabilityValue, ok = readCapabilityValue.Transfer( + resultValue := borrowIDCapability( inter, locationRange, - atree.Address{}, - false, - nil, - ).(*interpreter.IDCapabilityValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - wantedBorrowStaticType := - interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, wantedBorrowType) - if !ok { - panic(errors.NewUnreachableError()) - } - - var resultValue interpreter.Value - if borrow { - // TODO: - panic("TODO") - //resultValue = inter.BorrowCapability( - // readCapabilityValue.Address.ToAddress(), - // readCapabilityValue.ID, - // wantedBorrowType, - // locationRange, - //) - } else { - resultValue = interpreter.NewIDCapabilityValue( - inter, - readCapabilityValue.ID, - readCapabilityValue.Address, - wantedBorrowStaticType, - ) + capabilityAddress, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + borrow, + ) + if resultValue == nil { + return interpreter.Nil } return interpreter.NewSomeValueNonCopying( From a0343d2d1d916660a31587a67df82cccf95a5c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 27 Apr 2023 18:52:09 -0700 Subject: [PATCH 121/246] implement ID capability borrow/check --- runtime/environment.go | 37 +++++ runtime/interpreter/config.go | 4 + runtime/interpreter/interpreter.go | 97 +++++++++++++ runtime/interpreter/value.go | 8 +- runtime/stdlib/account.go | 130 ++++++++++++------ .../tests/interpreter/memory_metering_test.go | 3 +- 6 files changed, 227 insertions(+), 52 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index 4f42e95e8c..ff36e806c7 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -148,6 +148,8 @@ func (e *interpreterEnvironment) newInterpreterConfig() *interpreter.Config { OnMeterComputation: e.newOnMeterComputation(), OnFunctionInvocation: e.newOnFunctionInvocationHandler(), OnInvokedFunctionReturn: e.newOnInvokedFunctionReturnHandler(), + IDCapabilityBorrowHandler: e.newIDCapabilityBorrowHandler(), + IDCapabilityCheckHandler: e.newIDCapabilityCheckHandler(), } } @@ -651,6 +653,41 @@ func (e *interpreterEnvironment) newAuthAccountHandler() interpreter.AuthAccount } } +func (e *interpreterEnvironment) newIDCapabilityBorrowHandler() interpreter.IDCapabilityBorrowHandlerFunc { + return func( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + address interpreter.AddressValue, + capabilityID interpreter.UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, + ) interpreter.ReferenceValue { + + return stdlib.BorrowCapabilityController( + inter, + address, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + } +} + +func (e *interpreterEnvironment) newIDCapabilityCheckHandler() interpreter.IDCapabilityCheckHandlerFunc { + return func( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + address interpreter.AddressValue, + capabilityID interpreter.UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, + ) interpreter.BoolValue { + + // TODO: + panic("TODO") + } +} + func (e *interpreterEnvironment) ValidatePublicKey(publicKey *stdlib.PublicKey) error { return e.runtimeInterface.ValidatePublicKey(publicKey) } diff --git a/runtime/interpreter/config.go b/runtime/interpreter/config.go index f2886ef8a5..88092a729f 100644 --- a/runtime/interpreter/config.go +++ b/runtime/interpreter/config.go @@ -68,4 +68,8 @@ type Config struct { AccountLinkingAllowed bool // OnAccountLinked is triggered when an account is linked by the program OnAccountLinked OnAccountLinkedFunc + // IDCapabilityCheckHandler is used to check ID capabilities + IDCapabilityCheckHandler IDCapabilityCheckHandlerFunc + // IDCapabilityBorrowHandler is used to borrow ID capabilities + IDCapabilityBorrowHandler IDCapabilityBorrowHandlerFunc } diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 6bad208fd6..caafc5c838 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -119,6 +119,26 @@ type OnAccountLinkedFunc func( path PathValue, ) error +// IDCapabilityBorrowHandlerFunc is a function that is used to borrow ID capabilities. +type IDCapabilityBorrowHandlerFunc func( + inter *Interpreter, + locationRange LocationRange, + address AddressValue, + capabilityID UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, +) ReferenceValue + +// IDCapabilityCheckHandlerFunc is a function that is used to check ID capabilities. +type IDCapabilityCheckHandlerFunc func( + inter *Interpreter, + locationRange LocationRange, + address AddressValue, + capabilityID UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, +) BoolValue + // InjectedCompositeFieldsHandlerFunc is a function that handles storage reads. type InjectedCompositeFieldsHandlerFunc func( inter *Interpreter, @@ -4819,3 +4839,80 @@ func (interpreter *Interpreter) ConfigureAccountLinkingAllowed() { config.AccountLinkingAllowed = true } + +func (interpreter *Interpreter) idCapabilityBorrowFunction( + addressValue AddressValue, + capabilityID UInt64Value, + capabilityBorrowType *sema.ReferenceType, +) *HostFunctionValue { + + return NewHostFunctionValue( + interpreter, + sema.CapabilityTypeBorrowFunctionType(capabilityBorrowType), + func(invocation Invocation) Value { + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + var wantedBorrowType *sema.ReferenceType + typeParameterPair := invocation.TypeParameterTypes.Oldest() + if typeParameterPair != nil { + ty := typeParameterPair.Value + var ok bool + wantedBorrowType, ok = ty.(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + } + + return inter.SharedState.Config.IDCapabilityBorrowHandler( + inter, + locationRange, + addressValue, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + }, + ) +} + +func (interpreter *Interpreter) idCapabilityCheckFunction( + addressValue AddressValue, + capabilityID UInt64Value, + capabilityBorrowType *sema.ReferenceType, +) *HostFunctionValue { + + return NewHostFunctionValue( + interpreter, + sema.CapabilityTypeCheckFunctionType(capabilityBorrowType), + func(invocation Invocation) Value { + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // NOTE: if a type argument is provided for the function, + // use it *instead* of the type of the value (if any) + + var wantedBorrowType *sema.ReferenceType + typeParameterPair := invocation.TypeParameterTypes.Oldest() + if typeParameterPair != nil { + ty := typeParameterPair.Value + var ok bool + wantedBorrowType, ok = ty.(*sema.ReferenceType) + if !ok { + panic(errors.NewUnreachableError()) + } + } + + return inter.SharedState.Config.IDCapabilityCheckHandler( + inter, + locationRange, + addressValue, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + }, + ) +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 4ae25243d0..dafd560831 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18509,16 +18509,12 @@ func (v *IDCapabilityValue) GetMember(interpreter *Interpreter, _ LocationRange, case sema.CapabilityTypeBorrowFunctionName: // this function will panic already if this conversion fails borrowType, _ := interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) - // TODO: - _ = borrowType - panic("TODO") + return interpreter.idCapabilityBorrowFunction(v.Address, v.ID, borrowType) case sema.CapabilityTypeCheckFunctionName: // this function will panic already if this conversion fails borrowType, _ := interpreter.MustConvertStaticToSemaType(v.BorrowType).(*sema.ReferenceType) - // TODO: - _ = borrowType - panic("TODO") + return interpreter.idCapabilityCheckFunction(v.Address, v.ID, borrowType) case sema.CapabilityTypeAddressFieldName: return v.Address diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 3b4104fb5e..fc50041f2a 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -3009,15 +3009,16 @@ func newPublicAccountCapabilitiesValue( ) } -func borrowIDCapability( +func getCheckedCapabilityController( inter *interpreter.Interpreter, - locationRange interpreter.LocationRange, capabilityAddressValue interpreter.AddressValue, capabilityIDValue interpreter.UInt64Value, wantedBorrowType *sema.ReferenceType, capabilityBorrowType *sema.ReferenceType, - borrow bool, -) interpreter.Value { +) ( + interpreter.CapabilityControllerValue, + *sema.ReferenceType, +) { if wantedBorrowType == nil { wantedBorrowType = capabilityBorrowType @@ -3027,7 +3028,7 @@ func borrowIDCapability( // The requested type must be a supertype if !sema.IsSubType(capabilityBorrowType, wantedBorrowType) { - return nil + return nil, nil } } @@ -3036,7 +3037,7 @@ func borrowIDCapability( controller := getCapabilityController(inter, capabilityAddress, capabilityID) if controller == nil { - return nil + return nil, nil } // Ensure requested borrow type is not more permissive @@ -3052,51 +3053,70 @@ func borrowIDCapability( } if !sema.IsSubType(controllerBorrowType, wantedBorrowType) { - return nil + return nil, nil } - // Return reference / capability value - - if borrow { - switch controller := controller.(type) { - case *interpreter.AccountCapabilityControllerValue: - return interpreter.NewAccountReferenceValue( - inter, - capabilityAddress, - // TODO: - interpreter.EmptyPathValue, - wantedBorrowType, - ) + return controller, wantedBorrowType +} - case *interpreter.StorageCapabilityControllerValue: - return interpreter.NewStorageReferenceValue( - inter, - wantedBorrowType.Authorized, - capabilityAddress, - controller.TargetPath, - wantedBorrowType.Type, - ) +func uncheckedBorrowCapabilityController( + inter *interpreter.Interpreter, + controller interpreter.CapabilityControllerValue, + capabilityAddress common.Address, + wantedBorrowType *sema.ReferenceType, +) interpreter.ReferenceValue { - default: - panic(errors.NewUnreachableError()) - } + // TODO: dereference? - } else { - wantedBorrowStaticType := - interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, wantedBorrowType) - if !ok { - panic(errors.NewUnreachableError()) - } + switch controller := controller.(type) { + case *interpreter.AccountCapabilityControllerValue: + return interpreter.NewAccountReferenceValue( + inter, + capabilityAddress, + // TODO: + interpreter.EmptyPathValue, + wantedBorrowType, + ) - return interpreter.NewIDCapabilityValue( + case *interpreter.StorageCapabilityControllerValue: + return interpreter.NewStorageReferenceValue( inter, - capabilityIDValue, - capabilityAddressValue, - wantedBorrowStaticType, + wantedBorrowType.Authorized, + capabilityAddress, + controller.TargetPath, + wantedBorrowType.Type, ) + + default: + panic(errors.NewUnreachableError()) } } +func BorrowCapabilityController( + inter *interpreter.Interpreter, + capabilityAddress interpreter.AddressValue, + capabilityID interpreter.UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, +) interpreter.ReferenceValue { + controller, resultBorrowType := getCheckedCapabilityController( + inter, + capabilityAddress, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + if controller == nil { + return nil + } + return uncheckedBorrowCapabilityController( + inter, + controller, + capabilityAddress.ToAddress(), + resultBorrowType, + ) +} + func newAccountCapabilitiesGetFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, @@ -3111,7 +3131,6 @@ func newAccountCapabilitiesGetFunction( func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter - locationRange := invocation.LocationRange // Get path argument @@ -3163,15 +3182,38 @@ func newAccountCapabilitiesGetFunction( capabilityID := readCapabilityValue.ID capabilityAddress := readCapabilityValue.Address - resultValue := borrowIDCapability( + var resultValue interpreter.Value + controller, resultBorrowType := getCheckedCapabilityController( inter, - locationRange, capabilityAddress, capabilityID, wantedBorrowType, capabilityBorrowType, - borrow, ) + if controller != nil { + if borrow { + resultValue = uncheckedBorrowCapabilityController( + inter, + controller, + capabilityAddress.ToAddress(), + resultBorrowType, + ) + } else { + resultBorrowStaticType := + interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, resultBorrowType) + if !ok { + panic(errors.NewUnreachableError()) + } + + resultValue = interpreter.NewIDCapabilityValue( + inter, + capabilityID, + capabilityAddress, + resultBorrowStaticType, + ) + } + } + if resultValue == nil { return interpreter.Nil } diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index 0aa95c8a4c..ab51805ed2 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -9070,8 +9070,7 @@ func TestInterpretValueStringConversion(t *testing.T) { testValueStringConversion(t, script, interpreter.NewUnmeteredIDCapabilityValue( - // TODO: - interpreter.TodoCapabilityID, + 4, interpreter.AddressValue{1}, interpreter.CompositeStaticType{ Location: utils.TestLocation, From 6d38ab55932b9635f8f102d592a2c0884f0f235d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 12:01:23 -0700 Subject: [PATCH 122/246] implement Capability.check for ID capability --- runtime/environment.go | 11 +- runtime/interpreter/value.go | 10 +- .../value_accountcapabilitycontroller.go | 14 ++ runtime/interpreter/value_accountreference.go | 5 + .../value_storagecapabilitycontroller.go | 19 +++ runtime/stdlib/account.go | 144 ++++++++++++------ 6 files changed, 149 insertions(+), 54 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index ff36e806c7..da9e3cfdb2 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -665,6 +665,7 @@ func (e *interpreterEnvironment) newIDCapabilityBorrowHandler() interpreter.IDCa return stdlib.BorrowCapabilityController( inter, + locationRange, address, capabilityID, wantedBorrowType, @@ -683,8 +684,14 @@ func (e *interpreterEnvironment) newIDCapabilityCheckHandler() interpreter.IDCap capabilityBorrowType *sema.ReferenceType, ) interpreter.BoolValue { - // TODO: - panic("TODO") + return stdlib.CheckCapabilityController( + inter, + locationRange, + address, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) } } diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index dafd560831..8a832d06cf 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17019,6 +17019,7 @@ func (s SomeStorable) ChildStorables() []atree.Storable { type ReferenceValue interface { Value isReference() + ReferencedValue(interpreter *Interpreter, locationRange LocationRange, errorOnFailedDereference bool) *Value } // StorageReferenceValue @@ -17141,7 +17142,7 @@ func (v *StorageReferenceValue) dereference(interpreter *Interpreter, locationRa } func (v *StorageReferenceValue) ReferencedValue(interpreter *Interpreter, locationRange LocationRange, errorOnFailedDereference bool) *Value { - referencedValue, err := v.dereference(interpreter, EmptyLocationRange) + referencedValue, err := v.dereference(interpreter, locationRange) if err == nil { return referencedValue } @@ -17445,7 +17446,7 @@ func (v *EphemeralReferenceValue) MeteredString(memoryGauge common.MemoryGauge, } func (v *EphemeralReferenceValue) StaticType(inter *Interpreter) StaticType { - referencedValue := v.ReferencedValue(inter, EmptyLocationRange) + referencedValue := v.ReferencedValue(inter, EmptyLocationRange, true) if referencedValue == nil { panic(DereferenceError{ Cause: "the value being referenced has been destroyed or moved", @@ -17469,6 +17470,7 @@ func (*EphemeralReferenceValue) IsImportable(_ *Interpreter) bool { func (v *EphemeralReferenceValue) ReferencedValue( interpreter *Interpreter, locationRange LocationRange, + _ bool, ) *Value { // Just like for storage references, references to optionals are unwrapped, // i.e. a reference to `nil` aborts when dereferenced. @@ -17488,7 +17490,7 @@ func (v *EphemeralReferenceValue) mustReferencedValue( interpreter *Interpreter, locationRange LocationRange, ) Value { - referencedValue := v.ReferencedValue(interpreter, locationRange) + referencedValue := v.ReferencedValue(interpreter, locationRange, true) if referencedValue == nil { panic(DereferenceError{ Cause: "the value being referenced has been destroyed or moved", @@ -17638,7 +17640,7 @@ func (v *EphemeralReferenceValue) ConformsToStaticType( locationRange LocationRange, results TypeConformanceResults, ) bool { - referencedValue := v.ReferencedValue(interpreter, locationRange) + referencedValue := v.ReferencedValue(interpreter, locationRange, true) if referencedValue == nil { return false } diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index ae6b2298d9..35e28fb611 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -204,3 +204,17 @@ func (*AccountCapabilityControllerValue) SetMember(_ *Interpreter, _ LocationRan // Storage capability controllers have no settable members (fields / functions) panic(errors.NewUnreachableError()) } + +func (v *AccountCapabilityControllerValue) ReferenceValue( + interpreter *Interpreter, + capabilityAddress common.Address, + resultBorrowType *sema.ReferenceType, +) ReferenceValue { + return NewAccountReferenceValue( + interpreter, + capabilityAddress, + // TODO: + EmptyPathValue, + resultBorrowType, + ) +} diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index 9d9b16d79e..a9eefa495d 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -296,3 +296,8 @@ func (v *AccountReferenceValue) authAccount(interpreter *Interpreter) Value { } return v._authAccount } + +func (v *AccountReferenceValue) ReferencedValue(interpreter *Interpreter, _ LocationRange, _ bool) *Value { + authAccount := v.authAccount(interpreter) + return &authAccount +} diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index b8480c89d0..6f9a6f794b 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -31,6 +31,11 @@ type CapabilityControllerValue interface { Value isCapabilityControllerValue() CapabilityControllerBorrowType() ReferenceStaticType + ReferenceValue( + interpreter *Interpreter, + capabilityAddress common.Address, + resultBorrowType *sema.ReferenceType, + ) ReferenceValue } // StorageCapabilityControllerValue @@ -233,3 +238,17 @@ func (*StorageCapabilityControllerValue) SetMember(_ *Interpreter, _ LocationRan // Storage capability controllers have no settable members (fields / functions) panic(errors.NewUnreachableError()) } + +func (v *StorageCapabilityControllerValue) ReferenceValue( + interpreter *Interpreter, + capabilityAddress common.Address, + resultBorrowType *sema.ReferenceType, +) ReferenceValue { + return NewStorageReferenceValue( + interpreter, + resultBorrowType.Authorized, + capabilityAddress, + v.TargetPath, + resultBorrowType.Type, + ) +} diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index fc50041f2a..31f9c8aa46 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -3059,62 +3059,99 @@ func getCheckedCapabilityController( return controller, wantedBorrowType } -func uncheckedBorrowCapabilityController( +func getCheckedCapabilityControllerReference( inter *interpreter.Interpreter, - controller interpreter.CapabilityControllerValue, - capabilityAddress common.Address, + capabilityAddressValue interpreter.AddressValue, + capabilityIDValue interpreter.UInt64Value, wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, ) interpreter.ReferenceValue { + controller, resultBorrowType := getCheckedCapabilityController( + inter, + capabilityAddressValue, + capabilityIDValue, + wantedBorrowType, + capabilityBorrowType, + ) + if controller == nil { + return nil + } - // TODO: dereference? - - switch controller := controller.(type) { - case *interpreter.AccountCapabilityControllerValue: - return interpreter.NewAccountReferenceValue( - inter, - capabilityAddress, - // TODO: - interpreter.EmptyPathValue, - wantedBorrowType, - ) - - case *interpreter.StorageCapabilityControllerValue: - return interpreter.NewStorageReferenceValue( - inter, - wantedBorrowType.Authorized, - capabilityAddress, - controller.TargetPath, - wantedBorrowType.Type, - ) + capabilityAddress := capabilityAddressValue.ToAddress() - default: - panic(errors.NewUnreachableError()) - } + return controller.ReferenceValue( + inter, + capabilityAddress, + resultBorrowType, + ) } func BorrowCapabilityController( inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, capabilityAddress interpreter.AddressValue, capabilityID interpreter.UInt64Value, wantedBorrowType *sema.ReferenceType, capabilityBorrowType *sema.ReferenceType, ) interpreter.ReferenceValue { - controller, resultBorrowType := getCheckedCapabilityController( + referenceValue := getCheckedCapabilityControllerReference( inter, capabilityAddress, capabilityID, wantedBorrowType, capabilityBorrowType, ) - if controller == nil { + if referenceValue == nil { return nil } - return uncheckedBorrowCapabilityController( + + // Attempt to dereference, + // which reads the stored value + // and performs a dynamic type check + + referencedValue := referenceValue.ReferencedValue( inter, - controller, - capabilityAddress.ToAddress(), - resultBorrowType, + locationRange, + true, + ) + + if referencedValue == nil { + return nil + } + + return referenceValue +} + +func CheckCapabilityController( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + capabilityAddress interpreter.AddressValue, + capabilityID interpreter.UInt64Value, + wantedBorrowType *sema.ReferenceType, + capabilityBorrowType *sema.ReferenceType, +) interpreter.BoolValue { + referenceValue := getCheckedCapabilityControllerReference( + inter, + capabilityAddress, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + if referenceValue == nil { + return interpreter.FalseValue + } + + // Attempt to dereference, + // which reads the stored value + // and performs a dynamic type check + + referencedValue := referenceValue.ReferencedValue( + inter, + locationRange, + false, ) + + return interpreter.AsBoolValue(referencedValue != nil) } func newAccountCapabilitiesGetFunction( @@ -3131,6 +3168,7 @@ func newAccountCapabilitiesGetFunction( func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter + locationRange := invocation.LocationRange // Get path argument @@ -3183,22 +3221,32 @@ func newAccountCapabilitiesGetFunction( capabilityAddress := readCapabilityValue.Address var resultValue interpreter.Value - controller, resultBorrowType := getCheckedCapabilityController( - inter, - capabilityAddress, - capabilityID, - wantedBorrowType, - capabilityBorrowType, - ) - if controller != nil { - if borrow { - resultValue = uncheckedBorrowCapabilityController( - inter, - controller, - capabilityAddress.ToAddress(), - resultBorrowType, - ) - } else { + if borrow { + // When borrowing, + // check the controller and types, + // and return a checked reference + + resultValue = BorrowCapabilityController( + inter, + locationRange, + capabilityAddress, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + } else { + // When not borrowing, + // check the controller and types, + // and return a capability + + controller, resultBorrowType := getCheckedCapabilityController( + inter, + capabilityAddress, + capabilityID, + wantedBorrowType, + capabilityBorrowType, + ) + if controller != nil { resultBorrowStaticType := interpreter.ConvertSemaReferenceTypeToStaticReferenceType(inter, resultBorrowType) if !ok { From 0720d46021c2acb37c2346513b48a16c4cf8c511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 15:24:49 -0700 Subject: [PATCH 123/246] add support for restricted types --- runtime/sema/gen/main.go | 52 +++++++++++++++++++-- runtime/sema/gen/testdata/fields.cdc | 13 +++++- runtime/sema/gen/testdata/fields.golden.go | 53 +++++++++++++++++++++- 3 files changed, 110 insertions(+), 8 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 50e9ee975f..760f1625b1 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -630,45 +630,49 @@ func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { return typeVarIdent(identifier) case *ast.OptionalType: + innerType := typeExpr(t.Type, typeParams) return &dst.UnaryExpr{ Op: token.AND, X: &dst.CompositeLit{ Type: dst.NewIdent("OptionalType"), Elts: []dst.Expr{ - goKeyValue("Type", typeExpr(t.Type, typeParams)), + goKeyValue("Type", innerType), }, }, } case *ast.ReferenceType: + borrowType := typeExpr(t.Type, typeParams) return &dst.UnaryExpr{ Op: token.AND, X: &dst.CompositeLit{ Type: dst.NewIdent("ReferenceType"), Elts: []dst.Expr{ - goKeyValue("Type", typeExpr(t.Type, typeParams)), + goKeyValue("Type", borrowType), }, }, } case *ast.VariableSizedType: + elementType := typeExpr(t.Type, typeParams) return &dst.UnaryExpr{ Op: token.AND, X: &dst.CompositeLit{ Type: dst.NewIdent("VariableSizedType"), Elts: []dst.Expr{ - goKeyValue("Type", typeExpr(t.Type, typeParams)), + goKeyValue("Type", elementType), }, }, } case *ast.ConstantSizedType: + elementType := typeExpr(t.Type, typeParams) return &dst.UnaryExpr{ Op: token.AND, X: &dst.CompositeLit{ Type: dst.NewIdent("ConstantSizedType"), Elts: []dst.Expr{ - goKeyValue("Type", typeExpr(t.Type, typeParams)), + goKeyValue("Type", elementType), goKeyValue( "Size", &dst.BasicLit{ @@ -706,6 +710,46 @@ func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { Args: argumentExprs, } + case *ast.RestrictedType: + var elements []dst.Expr + if t.Type != nil { + restrictedType := typeExpr(t.Type, typeParams) + elements = append(elements, + goKeyValue("Type", restrictedType), + ) + } + + if len(t.Restrictions) > 0 { + restrictions := make([]dst.Expr, 0, len(t.Restrictions)) + for _, restriction := range t.Restrictions { + restrictions = append( + restrictions, + typeExpr(restriction, typeParams), + ) + } + elements = append( + elements, + goKeyValue("Restrictions", + &dst.CompositeLit{ + Type: &dst.ArrayType{ + Elt: &dst.StarExpr{ + X: dst.NewIdent("InterfaceType"), + }, + }, + Elts: restrictions, + }, + ), + ) + } + + return &dst.UnaryExpr{ + Op: token.AND, + X: &dst.CompositeLit{ + Type: dst.NewIdent("RestrictedType"), + Elts: elements, + }, + } + default: panic(fmt.Errorf("%T types are not supported", t)) } diff --git a/runtime/sema/gen/testdata/fields.cdc b/runtime/sema/gen/testdata/fields.cdc index 5755eb6181..d9a41974e5 100644 --- a/runtime/sema/gen/testdata/fields.cdc +++ b/runtime/sema/gen/testdata/fields.cdc @@ -23,9 +23,18 @@ pub struct Test { /// This is a test type field. let testType: Type - /// This is a test capability field. + /// This is a test unparameterized capability field. let testCap: Capability - /// This is a test specific capability field. + /// This is a test parameterized capability field. let testCapInt: Capability + + /// This is a test restricted type (without type) field. + let testRestrictedWithoutType: {Bar, Baz} + + /// This is a test restricted type (with type) field. + let testRestrictedWithType: Foo{Bar, Baz} + + /// This is a test restricted type (without restrictions) field. + let testRestrictedWithoutRestrictions: Foo{} } diff --git a/runtime/sema/gen/testdata/fields.golden.go b/runtime/sema/gen/testdata/fields.golden.go index 9c8a357ae5..196964c9e5 100644 --- a/runtime/sema/gen/testdata/fields.golden.go +++ b/runtime/sema/gen/testdata/fields.golden.go @@ -100,7 +100,7 @@ const TestTypeTestCapFieldName = "testCap" var TestTypeTestCapFieldType = &CapabilityType{} const TestTypeTestCapFieldDocString = ` -This is a test capability field. +This is a test unparameterized capability field. ` const TestTypeTestCapIntFieldName = "testCapInt" @@ -111,7 +111,38 @@ var TestTypeTestCapIntFieldType = MustInstantiate( ) const TestTypeTestCapIntFieldDocString = ` -This is a test specific capability field. +This is a test parameterized capability field. +` + +const TestTypeTestRestrictedWithoutTypeFieldName = "testRestrictedWithoutType" + +var TestTypeTestRestrictedWithoutTypeFieldType = &RestrictedType{ + Restrictions: []*InterfaceType{BarType, BazType}, +} + +const TestTypeTestRestrictedWithoutTypeFieldDocString = ` +This is a test restricted type (without type) field. +` + +const TestTypeTestRestrictedWithTypeFieldName = "testRestrictedWithType" + +var TestTypeTestRestrictedWithTypeFieldType = &RestrictedType{ + Type: FooType, + Restrictions: []*InterfaceType{BarType, BazType}, +} + +const TestTypeTestRestrictedWithTypeFieldDocString = ` +This is a test restricted type (with type) field. +` + +const TestTypeTestRestrictedWithoutRestrictionsFieldName = "testRestrictedWithoutRestrictions" + +var TestTypeTestRestrictedWithoutRestrictionsFieldType = &RestrictedType{ + Type: FooType, +} + +const TestTypeTestRestrictedWithoutRestrictionsFieldDocString = ` +This is a test restricted type (without restrictions) field. ` const TestTypeName = "Test" @@ -192,6 +223,24 @@ func init() { TestTypeTestCapIntFieldType, TestTypeTestCapIntFieldDocString, ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestRestrictedWithoutTypeFieldName, + TestTypeTestRestrictedWithoutTypeFieldType, + TestTypeTestRestrictedWithoutTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestRestrictedWithTypeFieldName, + TestTypeTestRestrictedWithTypeFieldType, + TestTypeTestRestrictedWithTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestRestrictedWithoutRestrictionsFieldName, + TestTypeTestRestrictedWithoutRestrictionsFieldType, + TestTypeTestRestrictedWithoutRestrictionsFieldDocString, + ), }) } } From fba6f0ab3f3ed576ac095ab19f272af636076a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 15:33:53 -0700 Subject: [PATCH 124/246] improve type bound for type parameter of AuthAccount.AccountCapabilities.issue --- runtime/sema/authaccount.cdc | 2 +- runtime/sema/authaccount.gen.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index 1e818c3a90..8773d91584 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -374,6 +374,6 @@ pub struct AuthAccount { pub fun forEachController(_ function: ((&AccountCapabilityController): Bool)) /// Issue/create a new account capability. - pub fun issue(): Capability + pub fun issue(): Capability } } diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index 99c48fda8e..299c0dd340 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -1633,7 +1633,9 @@ const AuthAccountAccountCapabilitiesTypeIssueFunctionName = "issue" var AuthAccountAccountCapabilitiesTypeIssueFunctionTypeParameterT = &TypeParameter{ Name: "T", TypeBound: &ReferenceType{ - Type: AuthAccountType, + Type: &RestrictedType{ + Type: AuthAccountType, + }, }, } From 9ae82716ec583a97219df64eb60b16dc3703f503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 16:17:59 -0700 Subject: [PATCH 125/246] implement AuthAccount.AccountCapabilities.getController/getControllers/forEachController --- runtime/capabilitycontrollers_test.go | 239 +++++++++++++++++++++++++- runtime/stdlib/account.go | 231 ++++++++++++++++++++++++- 2 files changed, 464 insertions(+), 6 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 87988cc91d..a9d6ddb26c 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -939,7 +939,244 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) }) - // TODO: AuthAccount.AccountCapabilities + t.Run("AuthAccount.AccountCapabilities", func(t *testing.T) { + + t.Parallel() + + t.Run("issue, multiple controllers, with same or different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Act + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Assert + assert(issuedCap1.id == 1) + assert(issuedCap2.id == 2) + assert(issuedCap3.id == 3) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("getController, non-existing", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Act + let controller1: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: 0) + let controller2: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: 1) + + // Assert + assert(controller1 == nil) + assert(controller2 == nil) + } + } + `, + ) + require.NoError(t, err) + }) + + // TODO: getController, non-account capability controller + + t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controller1: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap1.id) + let controller2: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap2.id) + let controller3: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap3.id) + + // Assert + assert(controller1!.capabilityID == 1) + assert(controller1!.borrowType == Type<&AuthAccount>()) + + assert(controller2!.capabilityID == 2) + assert(controller2!.borrowType == Type<&AuthAccount>()) + + assert(controller3!.capabilityID == 3) + assert(controller3!.borrowType == Type<&AuthAccount{}>()) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("getControllers", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controllers: [&AccountCapabilityController] = + signer.capabilities.account.getControllers() + + // Assert + assert(controllers.length == 3) + + Test.quickSort( + &controllers as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers[i] + let b = controllers[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers[0].capabilityID == 1) + assert(controllers[1].capabilityID == 2) + assert(controllers[2].capabilityID == 3) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("forEachController, all", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controllers: [&AccountCapabilityController] = [] + signer.capabilities.account.forEachController( + fun (controller: &AccountCapabilityController): Bool { + controllers.append(controller) + return true + } + ) + + // Assert + assert(controllers.length == 3) + + Test.quickSort( + &controllers as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers[i] + let b = controllers[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers[0].capabilityID == 1) + assert(controllers[1].capabilityID == 2) + assert(controllers[2].capabilityID == 3) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("forEachController, stop immediately", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + + // Act + var stopped = false + signer.capabilities.account.forEachController( + fun (controller: &AccountCapabilityController): Bool { + assert(!stopped) + stopped = true + return false + } + ) + + // Assert + assert(stopped) + } + } + `, + ) + require.NoError(t, err) + }) + }) t.Run("StorageCapabilityController", func(t *testing.T) { diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 8809222cc2..d906b02718 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2229,13 +2229,12 @@ func newAuthAccountAccountCapabilitiesValue( accountIDGenerator AccountIDGenerator, addressValue interpreter.AddressValue, ) interpreter.Value { - // TODO: return interpreter.NewAuthAccountAccountCapabilitiesValue( gauge, addressValue, - nil, - nil, - nil, + newAuthAccountAccountCapabilitiesGetControllerFunction(gauge, addressValue), + newAuthAccountAccountCapabilitiesGetControllersFunction(gauge, addressValue), + newAuthAccountAccountCapabilitiesForEachControllerFunction(gauge, addressValue), newAuthAccountAccountCapabilitiesIssueFunction(gauge, accountIDGenerator, addressValue), ) } @@ -2372,7 +2371,8 @@ func newAuthAccountStorageCapabilitiesGetControllersFunction( ) } -// the AccountKey in ` forEachController(forPath: StoragePath, _ function: ((&StorageCapabilityController): Bool))` +// `(&StorageCapabilityController)` in +// `forEachController(forPath: StoragePath, _ function: ((&StorageCapabilityController): Bool))` var authAccountStorageCapabilitiesForEachControllerCallbackTypeParams = []sema.Type{ &sema.ReferenceType{ Type: sema.StorageCapabilityControllerType, @@ -2849,6 +2849,43 @@ func recordAccountCapabilityController( } } +func getAccountCapabilityControllerIDsIterator( + inter *interpreter.Interpreter, + address common.Address, +) ( + nextCapabilityID func() (uint64, bool), + count uint64, +) { + storageMap := inter.Storage().GetStorageMap( + address, + AccountCapabilityStorageDomain, + false, + ) + if storageMap == nil { + return func() (uint64, bool) { + return 0, false + }, 0 + } + + iterator := storageMap.Iterator(inter) + + count = uint64(storageMap.Count()) + nextCapabilityID = func() (uint64, bool) { + keyValue := iterator.NextKey() + if keyValue == nil { + return 0, false + } + + capabilityIDValue, ok := keyValue.(interpreter.Uint64AtreeValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return uint64(capabilityIDValue), true + } + return +} + func newAuthAccountCapabilitiesPublishFunction( gauge common.MemoryGauge, addressValue interpreter.AddressValue, @@ -3274,3 +3311,187 @@ func newAccountCapabilitiesGetFunction( }, ) } + +func getAccountCapabilityControllerReference( + inter *interpreter.Interpreter, + address common.Address, + capabilityID uint64, +) *interpreter.EphemeralReferenceValue { + + capabilityController := getCapabilityController(inter, address, capabilityID) + if capabilityController == nil { + return nil + } + + accountCapabilityController, ok := capabilityController.(*interpreter.AccountCapabilityControllerValue) + if !ok { + return nil + } + + return interpreter.NewEphemeralReferenceValue( + inter, + false, + accountCapabilityController, + sema.AccountCapabilityControllerType, + ) +} + +func newAuthAccountAccountCapabilitiesGetControllerFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.FunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountAccountCapabilitiesTypeGetControllerFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + + // Get capability ID argument + + capabilityIDValue, ok := invocation.Arguments[0].(interpreter.UInt64Value) + if !ok { + panic(errors.NewUnreachableError()) + } + + capabilityID := uint64(capabilityIDValue) + + referenceValue := getAccountCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { + return interpreter.Nil + } + + return interpreter.NewSomeValueNonCopying(inter, referenceValue) + }, + ) +} + +var accountCapabilityControllerReferencesArrayStaticType = interpreter.VariableSizedStaticType{ + Type: interpreter.ReferenceStaticType{ + BorrowedType: interpreter.PrimitiveStaticTypeAccountCapabilityController, + }, +} + +func newAuthAccountAccountCapabilitiesGetControllersFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) interpreter.FunctionValue { + address := addressValue.ToAddress() + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountAccountCapabilitiesTypeGetControllersFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + + // Get capability controllers iterator + + nextCapabilityID, count := + getAccountCapabilityControllerIDsIterator(inter, address) + + var capabilityControllerIndex uint64 = 0 + + return interpreter.NewArrayValueWithIterator( + inter, + accountCapabilityControllerReferencesArrayStaticType, + common.Address{}, + count, + func() interpreter.Value { + if capabilityControllerIndex >= count { + return nil + } + capabilityControllerIndex++ + + capabilityID, ok := nextCapabilityID() + if !ok { + return nil + } + + referenceValue := getAccountCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { + panic(errors.NewUnreachableError()) + } + + return referenceValue + }, + ) + }, + ) +} + +// `(&AccountCapabilityController)` in +// `forEachController(_ function: ((&AccountCapabilityController): Bool))` +var authAccountAccountCapabilitiesForEachControllerCallbackTypeParams = []sema.Type{ + &sema.ReferenceType{ + Type: sema.AccountCapabilityControllerType, + }, +} + +func newAuthAccountAccountCapabilitiesForEachControllerFunction( + gauge common.MemoryGauge, + addressValue interpreter.AddressValue, +) *interpreter.HostFunctionValue { + address := addressValue.ToAddress() + + return interpreter.NewHostFunctionValue( + gauge, + sema.AuthAccountAccountCapabilitiesTypeForEachControllerFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get function argument + + functionValue, ok := invocation.Arguments[0].(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // Get capability controllers iterator + + nextCapabilityID, _ := + getAccountCapabilityControllerIDsIterator(inter, address) + + for { + capabilityID, ok := nextCapabilityID() + if !ok { + break + } + + referenceValue := getAccountCapabilityControllerReference(inter, address, capabilityID) + if referenceValue == nil { + panic(errors.NewUnreachableError()) + } + + subInvocation := interpreter.NewInvocation( + inter, + nil, + nil, + []interpreter.Value{referenceValue}, + authAccountAccountCapabilitiesForEachControllerCallbackTypeParams, + nil, + locationRange, + ) + + res, err := inter.InvokeFunction(functionValue, subInvocation) + if err != nil { + // interpreter panicked while invoking the inner function value + panic(err) + } + + shouldContinue, ok := res.(interpreter.BoolValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + if !shouldContinue { + break + } + } + + return interpreter.Void + }, + ) +} From c57df48ccb374b48be4515687e9bde8619df1a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 16:19:07 -0700 Subject: [PATCH 126/246] test Capability.check for ID capability value --- runtime/capabilitycontrollers_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index a9d6ddb26c..3100dc4749 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -208,7 +208,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - t.Run("get existing, with valid type", func(t *testing.T) { + t.Run("get and check existing, with valid type", func(t *testing.T) { t.Parallel() @@ -236,6 +236,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { // Assert assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) assert(gotCap.id == expectedCapID) } } @@ -246,7 +247,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - t.Run("get and borrow existing, with valid type", func(t *testing.T) { + t.Run("get, borrow, and check existing, with valid type", func(t *testing.T) { t.Parallel() @@ -275,6 +276,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { // Assert assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) assert(gotCap.id == expectedCapID) assert(ref.id == resourceID) } From f9616aa01bdcc95b4fa2918be47b0d4c018c01b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 28 Apr 2023 16:28:34 -0700 Subject: [PATCH 127/246] update TODOs --- runtime/capabilitycontrollers_test.go | 4 +++- runtime/interpreter/value_accountreference.go | 2 ++ runtime/stdlib/account.go | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 3100dc4749..96e05989a5 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -1241,7 +1241,9 @@ func TestRuntimeCapabilityControllers(t *testing.T) { ) require.NoError(t, err) }) + + // TODO: delete }) - // TODO: AccountCapabilityController + // TODO: AccountCapabilityController: delete } diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index a9eefa495d..fa97eb5c13 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -107,6 +107,8 @@ func (*AccountReferenceValue) IsImportable(_ *Interpreter) bool { } func (v *AccountReferenceValue) checkLink(interpreter *Interpreter, locationRange LocationRange) { + // TODO: do not check for account capability controller capability, no link path + address := v.Address domain := v.Path.Domain.Identifier() identifier := v.Path.Identifier diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index d906b02718..55ffce3ef0 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2626,7 +2626,8 @@ func getCapabilityController( capabilityController.RetargetFunction = newStorageCapabilityControllerRetargetFunction(inter, address, capabilityController) - // TODO: inject delete function + // TODO: inject delete function in *interpreter.StorageCapabilityControllerValue + // TODO: inject delete function in *interpreter.AccountCapabilityControllerValue } return capabilityController From a1fcdabc6d39ecdf47119de1898a892cf8cbc661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 13:29:21 -0700 Subject: [PATCH 128/246] fix borrow type of account reference value returned from account capability controller --- runtime/interpreter/value_accountcapabilitycontroller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 35e28fb611..04458a9a87 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -215,6 +215,6 @@ func (v *AccountCapabilityControllerValue) ReferenceValue( capabilityAddress, // TODO: EmptyPathValue, - resultBorrowType, + resultBorrowType.Type, ) } From 435c29c47b280c088a3a5f58f0a981a19454d8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 13:33:18 -0700 Subject: [PATCH 129/246] do not check account reference source path when borrowed capability is ID capability --- runtime/interpreter/interpreter.go | 2 +- .../value_accountcapabilitycontroller.go | 2 +- runtime/interpreter/value_accountreference.go | 23 +++++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 2548a683a8..60312762e5 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1869,7 +1869,7 @@ func (interpreter *Interpreter) convert(value Value, valueType, targetType sema. return NewAccountReferenceValue( interpreter, ref.Address, - ref.Path, + ref.SourcePath, unwrappedTargetType.Type, ) diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 04458a9a87..7bdc69c90a 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -213,7 +213,7 @@ func (v *AccountCapabilityControllerValue) ReferenceValue( return NewAccountReferenceValue( interpreter, capabilityAddress, - // TODO: + // NOTE: no source path, not a path capability (linking API) EmptyPathValue, resultBorrowType.Type, ) diff --git a/runtime/interpreter/value_accountreference.go b/runtime/interpreter/value_accountreference.go index fa97eb5c13..164dd19e96 100644 --- a/runtime/interpreter/value_accountreference.go +++ b/runtime/interpreter/value_accountreference.go @@ -31,7 +31,7 @@ import ( type AccountReferenceValue struct { BorrowedType sema.Type _authAccount Value - Path PathValue + SourcePath PathValue Address common.Address } @@ -43,12 +43,12 @@ var _ ReferenceValue = &AccountReferenceValue{} func NewUnmeteredAccountReferenceValue( address common.Address, - path PathValue, + sourcePath PathValue, borrowedType sema.Type, ) *AccountReferenceValue { return &AccountReferenceValue{ Address: address, - Path: path, + SourcePath: sourcePath, BorrowedType: borrowedType, } } @@ -56,13 +56,13 @@ func NewUnmeteredAccountReferenceValue( func NewAccountReferenceValue( memoryGauge common.MemoryGauge, address common.Address, - path PathValue, + sourcePath PathValue, borrowedType sema.Type, ) *AccountReferenceValue { common.UseMemory(memoryGauge, common.AccountReferenceValueMemoryUsage) return NewUnmeteredAccountReferenceValue( address, - path, + sourcePath, borrowedType, ) } @@ -107,11 +107,14 @@ func (*AccountReferenceValue) IsImportable(_ *Interpreter) bool { } func (v *AccountReferenceValue) checkLink(interpreter *Interpreter, locationRange LocationRange) { - // TODO: do not check for account capability controller capability, no link path + // Do not check source for ID capability, no link path + if v.SourcePath == EmptyPathValue { + return + } address := v.Address - domain := v.Path.Domain.Identifier() - identifier := v.Path.Identifier + domain := v.SourcePath.Domain.Identifier() + identifier := v.SourcePath.Identifier storageMapKey := StringStorageMapKey(identifier) @@ -218,7 +221,7 @@ func (v *AccountReferenceValue) Equal(_ *Interpreter, _ LocationRange, other Val otherReference, ok := other.(*AccountReferenceValue) if !ok || v.Address != otherReference.Address || - v.Path != otherReference.Path { + v.SourcePath != otherReference.SourcePath { return false } @@ -283,7 +286,7 @@ func (v *AccountReferenceValue) Transfer( func (v *AccountReferenceValue) Clone(_ *Interpreter) Value { return NewUnmeteredAccountReferenceValue( v.Address, - v.Path, + v.SourcePath, v.BorrowedType, ) } From f13a5138f4446b74b10a9de9237bf920a7842ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 13:33:47 -0700 Subject: [PATCH 130/246] the Auth/Public.Capabilities for account capabilities --- runtime/capabilitycontrollers_test.go | 1257 ++++++++++++++++--------- 1 file changed, 815 insertions(+), 442 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 96e05989a5..b8c7db67b0 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -171,7 +171,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { return } - // TODO: account capability testAccount := func(accountType sema.Type, accountExpression string) { testName := fmt.Sprintf( @@ -195,7 +194,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { // Act let gotCap: Capability<&AnyStruct>? = - %s.capabilities.get<&AnyStruct>(/public/r) + %s.capabilities.get<&AnyStruct>(/public/x) // Assert assert(gotCap == nil) @@ -212,196 +211,390 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) - - // Act - let gotCap: Capability<&Test.R> = %s.capabilities.get<&Test.R>(publicPath)! + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R> = + %s.capabilities.get<&Test.R>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) + assert(gotCap.id == expectedCapID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Assert - assert(issuedCap.id == expectedCapID) - assert(gotCap.check()) - assert(gotCap.id == expectedCapID) + t.Run("account capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&AuthAccount> = + %s.capabilities.get<&AuthAccount>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) + assert(gotCap.id == expectedCapID) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + }) t.Run("get, borrow, and check existing, with valid type", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 + t.Run("storage capability", func(t *testing.T) { - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R> = + %s.capabilities.get<&Test.R>(publicPath)! + let ref: &Test.R = gotCap.borrow()! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) + assert(gotCap.id == expectedCapID) + assert(ref.id == resourceID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Act - let gotCap: Capability<&Test.R> = %s.capabilities.get<&Test.R>(publicPath)! - let ref: &Test.R = gotCap.borrow()! + t.Run("account capability", func(t *testing.T) { - // Assert - assert(issuedCap.id == expectedCapID) - assert(gotCap.check()) - assert(gotCap.id == expectedCapID) - assert(ref.id == resourceID) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&AuthAccount> = + %s.capabilities.get<&AuthAccount>(publicPath)! + let ref: &AuthAccount = gotCap.borrow()! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap.check()) + assert(gotCap.id == expectedCapID) + assert(ref.address == 0x1) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("get existing, with subtype", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 + t.Run("storage capability", func(t *testing.T) { - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R{}> = - signer.capabilities.storage.issue<&Test.R{}>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R>? = + %s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Act - let gotCap: Capability<&Test.R>? = %s.capabilities.get<&Test.R>(publicPath) + t.Run("account capability", func(t *testing.T) { - // Assert - assert(issuedCap.id == expectedCapID) - assert(gotCap == nil) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.R>? = + %s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("get existing, with different type", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 + t.Run("storage capability", func(t *testing.T) { - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&Test.S>? = + %s.capabilities.get<&Test.S>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Act - let gotCap: Capability<&Test.S>? = %s.capabilities.get<&Test.S>(publicPath) + t.Run("account capability", func(t *testing.T) { - // Assert - assert(issuedCap.id == expectedCapID) - assert(gotCap == nil) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap: Capability<&AnyResource>? = + %s.capabilities.get<&AnyResource>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(gotCap == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("get unpublished", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) - let unpublishedcap = signer.capabilities.unpublish(publicPath) - - // Act - let gotCap: Capability<&Test.R>? = %s.capabilities.get<&Test.R>(publicPath) + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let gotCap: Capability<&Test.R>? = + %s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(gotCap == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Assert - assert(issuedCap.id == expectedCapID) - assert(unpublishedcap!.id == expectedCapID) - assert(gotCap == nil) + t.Run("account capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let gotCap: Capability<&AuthAccount>? = + %s.capabilities.get<&AuthAccount>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(gotCap == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + }) t.Run("borrow non-existing", func(t *testing.T) { @@ -416,7 +609,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { // Act let ref: &AnyStruct? = - %s.capabilities.borrow<&AnyStruct>(/public/r) + %s.capabilities.borrow<&AnyStruct>(/public/x) // Assert assert(ref == nil) @@ -433,154 +626,304 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) - - // Act - let ref: &Test.R = %s.capabilities.borrow<&Test.R>(publicPath)! + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.R = + %s.capabilities.borrow<&Test.R>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref.id == resourceID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Assert - assert(issuedCap.id == expectedCapID) - assert(ref.id == resourceID) + t.Run("account capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &AuthAccount = + %s.capabilities.borrow<&AuthAccount>(publicPath)! + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref.address == 0x1) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("borrow existing, with subtype", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 + t.Run("storage capability", func(t *testing.T) { - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R{}> = - signer.capabilities.storage.issue<&Test.R{}>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.R? = + %s.capabilities.borrow<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Act - let ref: &Test.R? = %s.capabilities.borrow<&Test.R>(publicPath) + t.Run("account capability", func(t *testing.T) { - // Assert - assert(issuedCap.id == expectedCapID) - assert(ref == nil) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &AuthAccount? = + %s.capabilities.borrow<&AuthAccount>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("borrow existing, with different type", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 + t.Run("storage capability", func(t *testing.T) { - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &Test.S? = + %s.capabilities.borrow<&Test.S>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Act - let ref: &Test.S? = %s.capabilities.borrow<&Test.S>(publicPath) + t.Run("account capability", func(t *testing.T) { - // Assert - assert(issuedCap.id == expectedCapID) - assert(ref == nil) + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let ref: &AnyResource? = + %s.capabilities.borrow<&AnyResource>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(ref == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) t.Run("borrow unpublished", func(t *testing.T) { t.Parallel() - err, _, _ := test( - fmt.Sprintf( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - let resourceID = 42 - - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath) - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) - let unpublishedcap = signer.capabilities.unpublish(publicPath) - - // Act - let ref: &Test.R? = %s.capabilities.borrow<&Test.R>(publicPath) + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let ref: &Test.R? = + %s.capabilities.borrow<&Test.R>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(ref == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) - // Assert - assert(issuedCap.id == expectedCapID) - assert(unpublishedcap!.id == expectedCapID) - assert(ref == nil) + t.Run("account capability", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + let unpublishedcap = signer.capabilities.unpublish(publicPath) + + // Act + let ref: &AuthAccount? = + %s.capabilities.borrow<&AuthAccount>(publicPath) + + // Assert + assert(issuedCap.id == expectedCapID) + assert(unpublishedcap!.id == expectedCapID) + assert(ref == nil) + } } - } - `, - accountExpression, - ), - ) - require.NoError(t, err) + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) }) if accountType == sema.AuthAccountType { @@ -589,32 +932,62 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - err, _, _ := test( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath = /storage/r - let publicPath = /public/r - let expectedCapID: UInt64 = 1 - - // Arrange - let issuedCap: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath) - signer.capabilities.publish(issuedCap, at: publicPath) - - // Act - signer.capabilities.publish(issuedCap, at: publicPath) + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + signer.capabilities.publish(issuedCap, at: publicPath) + } } - } - `, - ) - RequireError(t, err) + `, + ) + RequireError(t, err) + + var overwriteErr interpreter.OverwriteError + require.ErrorAs(t, err, &overwriteErr) + }) + + t.Run("storage capability", func(t *testing.T) { + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + let expectedCapID: UInt64 = 1 + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + signer.capabilities.publish(issuedCap, at: publicPath) + } + } + `, + ) + RequireError(t, err) - var overwriteErr interpreter.OverwriteError - require.ErrorAs(t, err, &overwriteErr) + var overwriteErr interpreter.OverwriteError + require.ErrorAs(t, err, &overwriteErr) + }) }) t.Run("unpublish non-existing", func(t *testing.T) { @@ -982,22 +1355,22 @@ func TestRuntimeCapabilityControllers(t *testing.T) { err, _, _ := test( // language=cadence ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - // Act - let controller1: &AccountCapabilityController? = - signer.capabilities.account.getController(byCapabilityID: 0) - let controller2: &AccountCapabilityController? = - signer.capabilities.account.getController(byCapabilityID: 1) - - // Assert - assert(controller1 == nil) - assert(controller2 == nil) - } - } - `, + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Act + let controller1: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: 0) + let controller2: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: 1) + + // Assert + assert(controller1 == nil) + assert(controller2 == nil) + } + } + `, ) require.NoError(t, err) }) @@ -1011,38 +1384,38 @@ func TestRuntimeCapabilityControllers(t *testing.T) { err, _, _ := test( // language=cadence ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - // Arrange - let issuedCap1: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap2: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap3: Capability<&AuthAccount{}> = - signer.capabilities.account.issue<&AuthAccount{}>() - - // Act - let controller1: &AccountCapabilityController? = - signer.capabilities.account.getController(byCapabilityID: issuedCap1.id) - let controller2: &AccountCapabilityController? = - signer.capabilities.account.getController(byCapabilityID: issuedCap2.id) - let controller3: &AccountCapabilityController? = - signer.capabilities.account.getController(byCapabilityID: issuedCap3.id) - - // Assert - assert(controller1!.capabilityID == 1) - assert(controller1!.borrowType == Type<&AuthAccount>()) - - assert(controller2!.capabilityID == 2) - assert(controller2!.borrowType == Type<&AuthAccount>()) - - assert(controller3!.capabilityID == 3) - assert(controller3!.borrowType == Type<&AuthAccount{}>()) - } - } - `, + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controller1: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap1.id) + let controller2: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap2.id) + let controller3: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap3.id) + + // Assert + assert(controller1!.capabilityID == 1) + assert(controller1!.borrowType == Type<&AuthAccount>()) + + assert(controller2!.capabilityID == 2) + assert(controller2!.borrowType == Type<&AuthAccount>()) + + assert(controller3!.capabilityID == 3) + assert(controller3!.borrowType == Type<&AuthAccount{}>()) + } + } + `, ) require.NoError(t, err) }) @@ -1054,41 +1427,41 @@ func TestRuntimeCapabilityControllers(t *testing.T) { err, _, _ := test( // language=cadence ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - - // Arrange - let issuedCap1: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap2: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap3: Capability<&AuthAccount{}> = - signer.capabilities.account.issue<&AuthAccount{}>() - - // Act - let controllers: [&AccountCapabilityController] = - signer.capabilities.account.getControllers() - - // Assert - assert(controllers.length == 3) - - Test.quickSort( - &controllers as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers[i] - let b = controllers[j] - return a.capabilityID < b.capabilityID - } - ) - - assert(controllers[0].capabilityID == 1) - assert(controllers[1].capabilityID == 2) - assert(controllers[2].capabilityID == 3) - } - } - `, + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controllers: [&AccountCapabilityController] = + signer.capabilities.account.getControllers() + + // Assert + assert(controllers.length == 3) + + Test.quickSort( + &controllers as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers[i] + let b = controllers[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers[0].capabilityID == 1) + assert(controllers[1].capabilityID == 2) + assert(controllers[2].capabilityID == 3) + } + } + `, ) require.NoError(t, err) }) @@ -1100,45 +1473,45 @@ func TestRuntimeCapabilityControllers(t *testing.T) { err, _, _ := test( // language=cadence ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - // Arrange - let issuedCap1: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap2: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap3: Capability<&AuthAccount{}> = - signer.capabilities.account.issue<&AuthAccount{}>() - - // Act - let controllers: [&AccountCapabilityController] = [] - signer.capabilities.account.forEachController( - fun (controller: &AccountCapabilityController): Bool { - controllers.append(controller) - return true - } - ) - - // Assert - assert(controllers.length == 3) - - Test.quickSort( - &controllers as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers[i] - let b = controllers[j] - return a.capabilityID < b.capabilityID - } - ) - - assert(controllers[0].capabilityID == 1) - assert(controllers[1].capabilityID == 2) - assert(controllers[2].capabilityID == 3) - } - } - `, + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap3: Capability<&AuthAccount{}> = + signer.capabilities.account.issue<&AuthAccount{}>() + + // Act + let controllers: [&AccountCapabilityController] = [] + signer.capabilities.account.forEachController( + fun (controller: &AccountCapabilityController): Bool { + controllers.append(controller) + return true + } + ) + + // Assert + assert(controllers.length == 3) + + Test.quickSort( + &controllers as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers[i] + let b = controllers[j] + return a.capabilityID < b.capabilityID + } + ) + + assert(controllers[0].capabilityID == 1) + assert(controllers[1].capabilityID == 2) + assert(controllers[2].capabilityID == 3) + } + } + `, ) require.NoError(t, err) }) @@ -1150,31 +1523,31 @@ func TestRuntimeCapabilityControllers(t *testing.T) { err, _, _ := test( // language=cadence ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - // Arrange - let issuedCap1: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - let issuedCap2: Capability<&AuthAccount> = - signer.capabilities.account.issue<&AuthAccount>() - - // Act - var stopped = false - signer.capabilities.account.forEachController( - fun (controller: &AccountCapabilityController): Bool { - assert(!stopped) - stopped = true - return false - } - ) - - // Assert - assert(stopped) - } - } - `, + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap1: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let issuedCap2: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + + // Act + var stopped = false + signer.capabilities.account.forEachController( + fun (controller: &AccountCapabilityController): Bool { + assert(!stopped) + stopped = true + return false + } + ) + + // Assert + assert(stopped) + } + } + `, ) require.NoError(t, err) }) From 893dd6719daf06458381c71f658fb06f5b43946a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 13:42:46 -0700 Subject: [PATCH 131/246] test getController for different controller kind --- runtime/capabilitycontrollers_test.go | 56 ++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index b8c7db67b0..970b50dcaa 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -1092,7 +1092,33 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - // TODO: getController, non-storage capability controller + t.Run("getController, account capability controller", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + + // Act + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + // Assert + assert(controller == nil) + } + } + `, + ) + require.NoError(t, err) + }) t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { @@ -1375,7 +1401,33 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) - // TODO: getController, non-account capability controller + t.Run("getController, storage capability controller", func(t *testing.T) { + + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap: Capability<&AnyStruct> = + signer.capabilities.storage.issue<&AnyStruct>(/storage/x) + + // Act + let controller: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap.id) + + // Assert + assert(controller == nil) + } + } + `, + ) + require.NoError(t, err) + }) t.Run("getController, multiple controllers to various paths, with same or different type", func(t *testing.T) { From 98253a85f9386b513f1029801230b3e95bd9337c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 13:54:54 -0700 Subject: [PATCH 132/246] test getControllers after StorageCapabilityController retarget --- runtime/capabilitycontrollers_test.go | 57 +++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 970b50dcaa..32a4dc7688 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -1613,7 +1613,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - // TODO: assert borrow, getControllers, and forEachController after retarget + // TODO: assert borrow after retarget err, _, _ := test( // language=cadence @@ -1646,6 +1646,32 @@ func TestRuntimeCapabilityControllers(t *testing.T) { let controller4: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap4.id) + let controllers1Before = signer.capabilities.storage.getControllers(forPath: storagePath1) + Test.quickSort( + &controllers1Before as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1Before[i] + let b = controllers1Before[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers1Before.length == 3) + assert(controllers1Before[0].capabilityID == 1) + assert(controllers1Before[1].capabilityID == 2) + assert(controllers1Before[2].capabilityID == 3) + + let controllers2Before = signer.capabilities.storage.getControllers(forPath: storagePath2) + Test.quickSort( + &controllers2Before as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers2Before[i] + let b = controllers2Before[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers2Before.length == 1) + assert(controllers2Before[0].capabilityID == 4) + // Act controller1!.retarget(storagePath2) @@ -1654,12 +1680,35 @@ func TestRuntimeCapabilityControllers(t *testing.T) { let controller1After: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) assert(controller1After!.target() == storagePath2) - assert(controller2!.target() == storagePath1) - assert(controller3!.target() == storagePath1) - assert(controller4!.target() == storagePath2) + + let controllers1After = signer.capabilities.storage.getControllers(forPath: storagePath1) + Test.quickSort( + &controllers1After as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1After[i] + let b = controllers1After[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers1After.length == 2) + assert(controllers1After[0].capabilityID == 2) + assert(controllers1After[1].capabilityID == 3) + + let controllers2After = signer.capabilities.storage.getControllers(forPath: storagePath2) + Test.quickSort( + &controllers2After as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers2After[i] + let b = controllers2After[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers2After.length == 2) + assert(controllers2After[0].capabilityID == 1) + assert(controllers2After[1].capabilityID == 4) } } `, From a3322b87d48cbefa251612d21d1a3c159343ef4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 15:25:51 -0700 Subject: [PATCH 133/246] test Auth/PublicAccount.capabilities.get for linked storage capability --- runtime/capabilitycontrollers_test.go | 45 ++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 32a4dc7688..31dee785b4 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -207,6 +207,50 @@ func TestRuntimeCapabilityControllers(t *testing.T) { require.NoError(t, err) }) + t.Run("get linked capability", func(t *testing.T) { + + t.Parallel() + + t.Run("storage link", func(t *testing.T) { + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let expectedCapID: UInt64 = 1 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + signer.link<&Test.R>(publicPath, target: storagePath)! + + // Act + let gotCap1: Capability<&Test.R>? = + %[1]s.getCapability<&Test.R>(publicPath) + let gotCap2: Capability<&Test.R>? = + %[1]s.capabilities.get<&Test.R>(publicPath) + + // Assert + assert(gotCap1!.check()) + + assert(gotCap2 == nil) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + // NOTE: account link cannot be tested + }) + t.Run("get and check existing, with valid type", func(t *testing.T) { t.Parallel() @@ -281,7 +325,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { ) require.NoError(t, err) }) - }) t.Run("get, borrow, and check existing, with valid type", func(t *testing.T) { From b63dfa55ae4b64bad590e4cc6a825e28a861d251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 15:45:32 -0700 Subject: [PATCH 134/246] test publishing linked capability --- runtime/capabilitycontrollers_test.go | 61 ++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 31dee785b4..74a742b6f6 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -41,6 +41,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { ) { rt := newTestInterpreterRuntime() + rt.defaultConfig.AccountLinkingEnabled = true accountCodes := map[Location][]byte{} accountIDs := map[common.Address]uint64{} @@ -222,7 +223,6 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath = /storage/r let publicPath = /public/r - let expectedCapID: UInt64 = 1 let resourceID = 42 // Arrange @@ -1057,6 +1057,65 @@ func TestRuntimeCapabilityControllers(t *testing.T) { ) require.NoError(t, err) }) + + t.Run("publish linked capability", func(t *testing.T) { + + t.Parallel() + + t.Run("storage link", func(t *testing.T) { + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let publicPath2 = /public/r2 + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + let linkedCap: Capability<&Test.R> = + signer.link<&Test.R>(publicPath, target: storagePath)! + + // Act + signer.capabilities.publish(linkedCap, at: publicPath2) + } + } + `, + ) + require.ErrorContains(t, err, "cannot publish linked capability") + }) + + t.Run("account link", func(t *testing.T) { + err, _, _ := test( + // language=cadence + ` + #allowAccountLinking + + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let privatePath = /private/acct + let publicPath = /public/acct + + // Arrange + let linkedCap: Capability<&AuthAccount> = + signer.linkAccount(privatePath)! + + // Act + signer.capabilities.publish(linkedCap, at: publicPath) + } + } + `, + ) + require.ErrorContains(t, err, "cannot publish linked capability") + }) + }) } }) } From 08e9919e5366bcd1b5b618a3aa016fd1bf19822d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 15:46:10 -0700 Subject: [PATCH 135/246] ensure newTestInterpreterRuntime is always used --- runtime/account_test.go | 36 ++++++++++++------------------------ runtime/coverage_test.go | 12 ++++++------ runtime/debugger_test.go | 12 ++++-------- runtime/runtime.go | 2 +- runtime/runtime_test.go | 10 ++-------- runtime/storage_test.go | 5 ++--- 6 files changed, 27 insertions(+), 50 deletions(-) diff --git a/runtime/account_test.go b/runtime/account_test.go index 0273f8636e..2dc3226c60 100644 --- a/runtime/account_test.go +++ b/runtime/account_test.go @@ -2441,10 +2441,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: false, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = false address := common.MustBytesToAddress([]byte{0x1}) @@ -2504,10 +2502,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = true address := common.MustBytesToAddress([]byte{0x1}) @@ -2569,10 +2565,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = true address1 := common.MustBytesToAddress([]byte{0x1}) address2 := common.MustBytesToAddress([]byte{0x2}) @@ -2693,10 +2687,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = true address := common.MustBytesToAddress([]byte{0x1}) @@ -2790,10 +2782,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = true address := common.MustBytesToAddress([]byte{0x1}) @@ -2902,10 +2892,8 @@ func TestRuntimeAccountLink(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - AccountLinkingEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.AccountLinkingEnabled = true address := common.MustBytesToAddress([]byte{0x1}) diff --git a/runtime/coverage_test.go b/runtime/coverage_test.go index 59fe97cb7b..c891bbd14e 100644 --- a/runtime/coverage_test.go +++ b/runtime/coverage_test.go @@ -1241,9 +1241,9 @@ func TestRuntimeCoverage(t *testing.T) { } }, } - runtime := NewInterpreterRuntime(Config{ - CoverageReport: coverageReport, - }) + + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.CoverageReport = coverageReport value, err := runtime.ExecuteScript( Script{ @@ -1398,9 +1398,9 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { } }, } - runtime := NewInterpreterRuntime(Config{ - CoverageReport: coverageReport, - }) + + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.CoverageReport = coverageReport value, err := runtime.ExecuteScript( Script{ diff --git a/runtime/debugger_test.go b/runtime/debugger_test.go index eb370c98a9..4e914a8c89 100644 --- a/runtime/debugger_test.go +++ b/runtime/debugger_test.go @@ -52,10 +52,8 @@ func TestRuntimeDebugger(t *testing.T) { go func() { defer wg.Done() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - Debugger: debugger, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.Debugger = debugger address := common.MustBytesToAddress([]byte{0x1}) @@ -146,10 +144,8 @@ func TestRuntimeDebuggerBreakpoints(t *testing.T) { go func() { defer wg.Done() - runtime := NewInterpreterRuntime(Config{ - AtreeValidationEnabled: true, - Debugger: debugger, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.Debugger = debugger address := common.MustBytesToAddress([]byte{0x1}) diff --git a/runtime/runtime.go b/runtime/runtime.go index 3fa7e46a23..b4a3509086 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -212,7 +212,7 @@ type interpreterRuntime struct { defaultConfig Config } -// NewInterpreterRuntime returns a interpreter-based version of the Flow runtime. +// NewInterpreterRuntime returns an interpreter-based version of the Flow runtime. func NewInterpreterRuntime(defaultConfig Config) Runtime { return &interpreterRuntime{ defaultConfig: defaultConfig, diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 3846c211dd..2b38f9c66c 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -8025,14 +8025,8 @@ func TestRuntimeAccountTypeEquality(t *testing.T) { t.Parallel() - rt := testInterpreterRuntime{ - interpreterRuntime: NewInterpreterRuntime( - Config{ - AccountLinkingEnabled: true, - }, - ).(*interpreterRuntime), - } - rt.Config() + rt := newTestInterpreterRuntime() + rt.defaultConfig.AccountLinkingEnabled = true script := []byte(` #allowAccountLinking diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 8000843663..197f4c9dd6 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -1747,9 +1747,8 @@ func TestRuntimeResourceOwnerChange(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - ResourceOwnerChangeHandlerEnabled: true, - }) + runtime := newTestInterpreterRuntime() + runtime.defaultConfig.ResourceOwnerChangeHandlerEnabled = true address1 := common.MustBytesToAddress([]byte{0x1}) address2 := common.MustBytesToAddress([]byte{0x2}) From 373354a6fb978d8588c99fb117e6666ce204e1d6 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 1 May 2023 16:13:57 -0700 Subject: [PATCH 136/246] Add more tests for field conflicts --- runtime/tests/checker/interface_test.go | 46 ++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 5ebdd7dc8c..fba9a1942f 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3003,7 +3003,7 @@ func TestCheckInterfaceInheritance(t *testing.T) { require.NoError(t, err) }) - t.Run("duplicate fields mismatching", func(t *testing.T) { + t.Run("duplicate fields, mismatching type", func(t *testing.T) { t.Parallel() @@ -3025,6 +3025,50 @@ func TestCheckInterfaceInheritance(t *testing.T) { assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) }) + t.Run("duplicate fields, mismatching kind", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub var x: String + } + + struct interface Bar: Foo { + pub let x: String + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, "x", memberConflictError.MemberName) + assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) + }) + + t.Run("duplicate fields, mismatching access modifier", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface Foo { + pub(set) var x: String + } + + struct interface Bar: Foo { + pub var x: String + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + memberConflictError := &sema.InterfaceMemberConflictError{} + require.ErrorAs(t, errs[0], &memberConflictError) + assert.Equal(t, "x", memberConflictError.MemberName) + assert.Equal(t, "Foo", memberConflictError.ConflictingInterfaceType.QualifiedIdentifier()) + }) + t.Run("duplicate members mixed type", func(t *testing.T) { t.Parallel() From 18772a0533cd3f5c5130bd3a0a9525e332067a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 16:36:41 -0700 Subject: [PATCH 137/246] add support for ID capabilities to CCF codec --- encoding/ccf/ccf_test.go | 310 +++++++++++++++++++++++++++++++++++++-- encoding/ccf/decode.go | 53 ++++++- encoding/ccf/encode.go | 44 +++++- 3 files changed, 378 insertions(+), 29 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 9006cb49c7..040ce352cd 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -9461,7 +9461,7 @@ func TestEncodeType(t *testing.T) { }) } -func TestEncodeCapability(t *testing.T) { +func TestEncodePathCapability(t *testing.T) { t.Parallel() @@ -9473,7 +9473,7 @@ func TestEncodeCapability(t *testing.T) { testEncodeAndDecode( t, - cadence.StorageCapability{ + cadence.PathCapability{ Path: path, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), }, @@ -9498,7 +9498,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9534,13 +9534,13 @@ func TestEncodeCapability(t *testing.T) { path2, err := cadence.NewPath(1, "bar") require.NoError(t, err) - capability1 := cadence.StorageCapability{ + capability1 := cadence.PathCapability{ Path: path1, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, } - capability2 := cadence.StorageCapability{ + capability2 := cadence.PathCapability{ Path: path2, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: simpleStructType, @@ -9625,7 +9625,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9653,7 +9653,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9677,7 +9677,7 @@ func TestEncodeCapability(t *testing.T) { testEncodeAndDecode( t, - cadence.StorageCapability{ + cadence.PathCapability{ Path: path, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, @@ -9705,7 +9705,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9730,12 +9730,12 @@ func TestEncodeCapability(t *testing.T) { path2, err := cadence.NewPath(1, "bar") require.NoError(t, err) - capability1 := cadence.StorageCapability{ + capability1 := cadence.PathCapability{ Path: path1, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, } - capability2 := cadence.StorageCapability{ + capability2 := cadence.PathCapability{ Path: path2, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, @@ -9774,7 +9774,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9789,7 +9789,7 @@ func TestEncodeCapability(t *testing.T) { // array, 2 elements follow 0x82, // address - // bytes, 8 bytes folloow + // bytes, 8 bytes follow 0x48, // {1,2,3,4,5} 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, @@ -9806,6 +9806,285 @@ func TestEncodeCapability(t *testing.T) { }) } +func TestEncodeCapability(t *testing.T) { + + t.Parallel() + + t.Run("unparameterized Capability", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.IDCapability{ + ID: 42, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + }, + []byte{ + // language=edn, format=ccf + // 130([144([null]), [h'0000000102030405', 42]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // null + 0xf6, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 42 + 0x18, 0x2a, + }, + ) + }) + + t.Run("array of unparameterized Capability", func(t *testing.T) { + t.Parallel() + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, + } + + capability1 := cadence.IDCapability{ + ID: 42, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + + capability2 := cadence.IDCapability{ + ID: 43, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: simpleStructType, + } + + testEncodeAndDecode( + t, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(nil))), + []byte{ + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["bar", 137(4)]]])], [139(144([null])), [130([144([137(4)]), [h'0000000102030405', 42]]), 130([144([136(h'')]), [h'0000000102030405', 43]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["bar", IntType]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // null + 0xf6, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 42 + 0x18, 0x2a, + + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow + 0x81, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 43 + 0x18, 0x2b, + }, + ) + }) + + t.Run("Capability", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.IDCapability{ + ID: 42, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + }, + []byte{ + // language=edn, format=ccf + // 130([144([137(4)]), [h'0000000102030405', 42]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 42 + 0x18, 0x2a, + }, + ) + }) + + t.Run("array of Capability", func(t *testing.T) { + t.Parallel() + + capability1 := cadence.IDCapability{ + ID: 42, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + capability2 := cadence.IDCapability{ + ID: 43, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + + testEncodeAndDecode( + t, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(cadence.NewIntType()))), + []byte{ + // language=edn, format=ccf + // 130([139(144([137(4)])), [[h'0000000102030405', 42], [h'0000000102030405', 43]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 42 + 0x18, 0x2a, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes follow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // 43 + 0x18, 0x2b, + }, + ) + }) +} + func TestDecodeFix64(t *testing.T) { t.Parallel() @@ -11227,7 +11506,8 @@ func testEncodeAndDecodeEx(t *testing.T, val cadence.Value, expectedCBOR []byte, func testEncode(t *testing.T, val cadence.Value, expectedCBOR []byte) (actualCBOR []byte) { actualCBOR, err := ccf.Encode(val) require.NoError(t, err) - assert.True(t, bytes.Equal(expectedCBOR, actualCBOR), fmt.Sprintf("actual: 0x%x", actualCBOR)) + + utils.AssertEqualWithDiff(t, expectedCBOR, actualCBOR) return actualCBOR } @@ -12509,7 +12789,7 @@ func TestDeployedEvents(t *testing.T) { // Encode Cadence value to CCF actualCBOR, err := ccf.Encode(tc.event) require.NoError(t, err) - require.Equal(t, tc.expectedCBOR, actualCBOR) + utils.AssertEqualWithDiff(t, tc.expectedCBOR, actualCBOR) // Decode CCF to Cadence value decodedEvent, err := ccf.Decode(nil, actualCBOR) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 9dc9ef22ef..4ba477cd70 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -227,7 +227,8 @@ func (d *Decoder) decodeTypeAndValue(types *cadenceTypeByCCFTypeID) (cadence.Val // / dict-value // / composite-value // / path-value -// / capability-value +// / path-capability-value +// / id-capability-value // / function-value // / type-value // @@ -1162,7 +1163,20 @@ func (d *Decoder) decodePath() (cadence.Value, error) { // decodeCapability decodes encoded capability-value as // language=CDDL -// capability-value = [ +// +// capability-value = +// +// id-capability-value +// / path-capability-value +// +// id-capability-value = [ +// +// address: address-value, +// id: uint64-value +// +// ] +// +// path-capability-value = [ // // address: address-value, // path: path-value @@ -1200,18 +1214,41 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTy return nil, err } - // Decode path. - path, err := d.decodePath() + // Decode ID or path. + nextType, err = d.dec.NextType() if err != nil { return nil, err } - return cadence.NewMeteredStorageCapability( + if nextType == cbor.UintType { + // Decode ID. + + id, err := d.decodeUInt64() + if err != nil { + return nil, err + } + + return cadence.NewMeteredIDCapability( + d.gauge, + id.(cadence.UInt64), + address.(cadence.Address), + typ.BorrowType, + ), nil + } else { + + // Decode path. + path, err := d.decodePath() + if err != nil { + return nil, err + } + + return cadence.NewMeteredPathCapability( d.gauge, - path.(cadence.Path), address.(cadence.Address), - typ.BorrowType), - nil + path.(cadence.Path), + typ.BorrowType, + ), nil + } } // decodeTypeValue decodes encoded type-value as diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 9f71212e3d..5b94d3da57 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -284,7 +284,8 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy // / dict-value // / composite-value // / path-value -// / capability-value +// / path-capability-value +// / id-capability-value // / function-value // / type-value // @@ -488,8 +489,11 @@ func (e *Encoder) encodeValue( // If x.StaticType is nil, type value is encoded as nil. return e.encodeNullableTypeValue(v.StaticType, ccfTypeIDByCadenceType{}) - case cadence.StorageCapability: - return e.encodeCapability(v) + case cadence.PathCapability: + return e.encodePathCapability(v) + + case cadence.IDCapability: + return e.encodeIDCapability(v) case cadence.Enum: return e.encodeEnum(v, tids) @@ -969,15 +973,15 @@ func (e *Encoder) encodePath(x cadence.Path) error { return e.enc.EncodeString(x.Identifier) } -// encodeCapability encodes cadence.StorageCapability as +// encodePathCapability encodes cadence.PathCapability as // language=CDDL -// capability-value = [ +// path-capability-value = [ // // address: address-value, // path: path-value // // ] -func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { +func (e *Encoder) encodePathCapability(capability cadence.PathCapability) error { // Encode array head with length 2. err := e.enc.EncodeRawBytes([]byte{ // array, 2 items follow @@ -997,6 +1001,34 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { return e.encodePath(capability.Path) } +// encodeIDCapability encodes cadence.IDCapability as +// language=CDDL +// id-capability-value = [ +// +// address: address-value, +// id: uint64-value +// +// ] +func (e *Encoder) encodeIDCapability(capability cadence.IDCapability) error { + // Encode array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: address + err = e.encodeAddress(capability.Address) + if err != nil { + return err + } + + // element 1: id + return e.encodeUInt64(capability.ID) +} + // encodeFunction encodes cadence.FunctionType as // language=CDDL // function-value = [ From 0073170aa70e756d71bde2171ec711c19e2e14b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 1 May 2023 17:33:54 -0700 Subject: [PATCH 138/246] fix and test retarget --- runtime/capabilitycontrollers_test.go | 318 ++++++++++++++++++-------- runtime/interpreter/interpreter.go | 6 +- runtime/stdlib/account.go | 3 +- 3 files changed, 230 insertions(+), 97 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 74a742b6f6..dfe2456070 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -64,12 +64,19 @@ func TestRuntimeCapabilityControllers(t *testing.T) { pub resource S {} pub fun createAndSaveR(id: Int, storagePath: StoragePath) { - self.account.save( + self.account.save( <-create R(id: id), to: storagePath ) } + pub fun createAndSaveS(storagePath: StoragePath) { + self.account.save( + <-create S(), + to: storagePath + ) + } + /// quickSort is qsort from "The C Programming Language". /// /// > Our version of quicksort is not the fastest possible, @@ -1715,107 +1722,230 @@ func TestRuntimeCapabilityControllers(t *testing.T) { t.Parallel() - // TODO: assert borrow after retarget - - err, _, _ := test( - // language=cadence - ` - import Test from 0x1 - - transaction { - prepare(signer: AuthAccount) { - let storagePath1 = /storage/r - let storagePath2 = /storage/r2 - - // Arrange - let issuedCap1: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath1) - let controller1: &StorageCapabilityController? = - signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) - - let issuedCap2: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath1) - let controller2: &StorageCapabilityController? = - signer.capabilities.storage.getController(byCapabilityID: issuedCap2.id) - - let issuedCap3: Capability<&Test.R{}> = - signer.capabilities.storage.issue<&Test.R{}>(storagePath1) - let controller3: &StorageCapabilityController? = - signer.capabilities.storage.getController(byCapabilityID: issuedCap3.id) + t.Run("target, getControllers", func(t *testing.T) { + t.Parallel() - let issuedCap4: Capability<&Test.R> = - signer.capabilities.storage.issue<&Test.R>(storagePath2) - let controller4: &StorageCapabilityController? = - signer.capabilities.storage.getController(byCapabilityID: issuedCap4.id) + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + + // Arrange + let issuedCap1: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller1: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) + + let issuedCap2: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller2: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap2.id) + + let issuedCap3: Capability<&Test.R{}> = + signer.capabilities.storage.issue<&Test.R{}>(storagePath1) + let controller3: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap3.id) + + let issuedCap4: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath2) + let controller4: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap4.id) + + let controllers1Before = signer.capabilities.storage.getControllers(forPath: storagePath1) + Test.quickSort( + &controllers1Before as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1Before[i] + let b = controllers1Before[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers1Before.length == 3) + assert(controllers1Before[0].capabilityID == 1) + assert(controllers1Before[1].capabilityID == 2) + assert(controllers1Before[2].capabilityID == 3) + + let controllers2Before = signer.capabilities.storage.getControllers(forPath: storagePath2) + Test.quickSort( + &controllers2Before as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers2Before[i] + let b = controllers2Before[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers2Before.length == 1) + assert(controllers2Before[0].capabilityID == 4) + + // Act + controller1!.retarget(storagePath2) + + // Assert + assert(controller1!.target() == storagePath2) + let controller1After: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) + assert(controller1After!.target() == storagePath2) + assert(controller2!.target() == storagePath1) + assert(controller3!.target() == storagePath1) + assert(controller4!.target() == storagePath2) + + let controllers1After = signer.capabilities.storage.getControllers(forPath: storagePath1) + Test.quickSort( + &controllers1After as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers1After[i] + let b = controllers1After[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers1After.length == 2) + assert(controllers1After[0].capabilityID == 2) + assert(controllers1After[1].capabilityID == 3) + + let controllers2After = signer.capabilities.storage.getControllers(forPath: storagePath2) + Test.quickSort( + &controllers2After as &[AnyStruct], + isLess: fun(i: Int, j: Int): Bool { + let a = controllers2After[i] + let b = controllers2After[j] + return a.capabilityID < b.capabilityID + } + ) + assert(controllers2After.length == 2) + assert(controllers2After[0].capabilityID == 1) + assert(controllers2After[1].capabilityID == 4) + } + } + `, + ) + require.NoError(t, err) + }) - let controllers1Before = signer.capabilities.storage.getControllers(forPath: storagePath1) - Test.quickSort( - &controllers1Before as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers1Before[i] - let b = controllers1Before[j] - return a.capabilityID < b.capabilityID - } - ) - assert(controllers1Before.length == 3) - assert(controllers1Before[0].capabilityID == 1) - assert(controllers1Before[1].capabilityID == 2) - assert(controllers1Before[2].capabilityID == 3) + t.Run("retarget empty, borrow", func(t *testing.T) { + t.Parallel() - let controllers2Before = signer.capabilities.storage.getControllers(forPath: storagePath2) - Test.quickSort( - &controllers2Before as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers2Before[i] - let b = controllers2Before[j] - return a.capabilityID < b.capabilityID - } - ) - assert(controllers2Before.length == 1) - assert(controllers2Before[0].capabilityID == 4) + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/empty + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath1) + + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + assert(issuedCap.borrow() != nil) + assert(issuedCap.check()) + assert(issuedCap.borrow()!.id == resourceID) + + // Act + controller!.retarget(storagePath2) + + // Assert + assert(issuedCap.borrow() == nil) + assert(!issuedCap.check()) + } + } + `, + ) + require.NoError(t, err) + }) - // Act - controller1!.retarget(storagePath2) + t.Run("retarget to value with same type, borrow", func(t *testing.T) { + t.Parallel() - // Assert - assert(controller1!.target() == storagePath2) - let controller1After: &StorageCapabilityController? = - signer.capabilities.storage.getController(byCapabilityID: issuedCap1.id) - assert(controller1After!.target() == storagePath2) - assert(controller2!.target() == storagePath1) - assert(controller3!.target() == storagePath1) - assert(controller4!.target() == storagePath2) + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/r2 + let resourceID1 = 42 + let resourceID2 = 43 + + // Arrange + Test.createAndSaveR(id: resourceID1, storagePath: storagePath1) + Test.createAndSaveR(id: resourceID2, storagePath: storagePath2) + + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + assert(issuedCap.borrow() != nil) + assert(issuedCap.check()) + assert(issuedCap.borrow()!.id == resourceID1) + + // Act + controller!.retarget(storagePath2) + + // Assert + assert(issuedCap.borrow() != nil) + assert(issuedCap.check()) + assert(issuedCap.borrow()!.id == resourceID2) + } + } + `, + ) + require.NoError(t, err) + }) - let controllers1After = signer.capabilities.storage.getControllers(forPath: storagePath1) - Test.quickSort( - &controllers1After as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers1After[i] - let b = controllers1After[j] - return a.capabilityID < b.capabilityID - } - ) - assert(controllers1After.length == 2) - assert(controllers1After[0].capabilityID == 2) - assert(controllers1After[1].capabilityID == 3) + t.Run("retarget to value with different type, borrow", func(t *testing.T) { + t.Parallel() - let controllers2After = signer.capabilities.storage.getControllers(forPath: storagePath2) - Test.quickSort( - &controllers2After as &[AnyStruct], - isLess: fun(i: Int, j: Int): Bool { - let a = controllers2After[i] - let b = controllers2After[j] - return a.capabilityID < b.capabilityID - } - ) - assert(controllers2After.length == 2) - assert(controllers2After[0].capabilityID == 1) - assert(controllers2After[1].capabilityID == 4) + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath1 = /storage/r + let storagePath2 = /storage/s + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath1) + Test.createAndSaveS(storagePath: storagePath2) + + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath1) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + assert(issuedCap.borrow() != nil) + assert(issuedCap.check()) + assert(issuedCap.borrow()!.id == resourceID) + + // Act + controller!.retarget(storagePath2) + + // Assert + assert(issuedCap.borrow() == nil) + assert(!issuedCap.check()) + } } - } - `, - ) - require.NoError(t, err) + `, + ) + require.NoError(t, err) + }) }) // TODO: delete diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 60312762e5..081012c868 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -4919,7 +4919,7 @@ func (interpreter *Interpreter) idCapabilityBorrowFunction( } } - return inter.SharedState.Config.IDCapabilityBorrowHandler( + referenceValue := inter.SharedState.Config.IDCapabilityBorrowHandler( inter, locationRange, addressValue, @@ -4927,6 +4927,10 @@ func (interpreter *Interpreter) idCapabilityBorrowFunction( wantedBorrowType, capabilityBorrowType, ) + if referenceValue == nil { + return Nil + } + return NewSomeValueNonCopying(inter, referenceValue) }, ) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 55ffce3ef0..a328f4dcb2 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -3151,9 +3151,8 @@ func BorrowCapabilityController( referencedValue := referenceValue.ReferencedValue( inter, locationRange, - true, + false, ) - if referencedValue == nil { return nil } From a78f897eb397c05889b269530660ffc8f4575944 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 2 May 2023 11:08:08 -0700 Subject: [PATCH 139/246] Adjust tests to match view function restrictions --- runtime/tests/interpreter/interface_test.go | 32 ++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 0e0ab17534..37d0fafe61 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -671,10 +671,22 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { t.Parallel() + logFunctionType := sema.NewSimpleFunctionType( + sema.FunctionPurityView, + []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.AnyStructTypeAnnotation, + }, + }, + sema.VoidTypeAnnotation, + ) + var logs []string valueDeclaration := stdlib.NewStandardLibraryFunction( "log", - stdlib.LogFunctionType, + logFunctionType, "", func(invocation interpreter.Invocation) interpreter.Value { msg := invocation.Arguments[0].(*interpreter.StringValue).Str @@ -735,7 +747,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { } } - pub fun print(_ msg: String): Bool { + pub view fun print(_ msg: String): Bool { log(msg) return true } @@ -767,10 +779,22 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { t.Parallel() + logFunctionType := sema.NewSimpleFunctionType( + sema.FunctionPurityView, + []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.AnyStructTypeAnnotation, + }, + }, + sema.VoidTypeAnnotation, + ) + var logs []string valueDeclaration := stdlib.NewStandardLibraryFunction( "log", - stdlib.LogFunctionType, + logFunctionType, "", func(invocation interpreter.Invocation) interpreter.Value { msg := invocation.Arguments[0].(*interpreter.StringValue).Str @@ -831,7 +855,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { } } - pub fun print(_ msg: String): Bool { + pub view fun print(_ msg: String): Bool { log(msg) return true } From f70b36059469f44190ab3b9179872df8bc6fd21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 2 May 2023 11:29:31 -0700 Subject: [PATCH 140/246] implement StorageCapabilityController.delete --- runtime/capabilitycontrollers_test.go | 136 +++++++++++++++++- .../value_storagecapabilitycontroller.go | 47 ++++-- runtime/stdlib/account.go | 86 +++++++++-- 3 files changed, 248 insertions(+), 21 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index dfe2456070..d24abe5077 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -1948,7 +1948,141 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) }) - // TODO: delete + // TODO: + t.Run("delete", func(t *testing.T) { + + t.Parallel() + + // TODO: + t.Run("getController, getControllers", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + let controllersBefore = signer.capabilities.storage.getControllers(forPath: storagePath) + assert(controllersBefore.length == 1) + assert(controllersBefore[0].capabilityID == 1) + + // Act + controller!.delete() + + // Assert + let controllerAfter: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + assert(controllerAfter == nil) + + let controllersAfter = signer.capabilities.storage.getControllers(forPath: storagePath) + assert(controllersAfter.length == 0) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("target", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + // Act + controller!.delete() + + // Assert + controller!.target() + } + } + `, + ) + require.ErrorContains(t, err, "controller is deleted") + }) + + t.Run("retarget", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + // Act + controller!.delete() + + // Assert + controller!.retarget(/storage/r2) + } + } + `, + ) + require.ErrorContains(t, err, "controller is deleted") + }) + + t.Run("delete", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + let controller: &StorageCapabilityController? = + signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) + + // Act + controller!.delete() + + // Assert + controller!.delete() + } + } + `, + ) + require.ErrorContains(t, err, "controller is deleted") + }) + }) }) // TODO: AccountCapabilityController: delete diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 6f9a6f794b..4165ab49a2 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -41,10 +41,14 @@ type CapabilityControllerValue interface { // StorageCapabilityControllerValue type StorageCapabilityControllerValue struct { - BorrowType ReferenceStaticType - TargetPath PathValue + BorrowType ReferenceStaticType + CapabilityID UInt64Value + TargetPath PathValue + + // Injected functions + TargetFunction FunctionValue RetargetFunction FunctionValue - CapabilityID UInt64Value + DeleteFunction FunctionValue } func NewUnmeteredStorageCapabilityControllerValue( @@ -204,8 +208,6 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { - // TODO: sema.StorageCapabilityControllerTypeDeleteFunctionName - switch name { case sema.StorageCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID @@ -214,16 +216,13 @@ func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat return NewTypeValue(inter, v.BorrowType) case sema.StorageCapabilityControllerTypeTargetFunctionName: - return NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation Invocation) Value { - return v.TargetPath - }, - ) + return v.TargetFunction case sema.StorageCapabilityControllerTypeRetargetFunctionName: return v.RetargetFunction + + case sema.StorageCapabilityControllerTypeDeleteFunctionName: + return v.DeleteFunction } return nil @@ -252,3 +251,27 @@ func (v *StorageCapabilityControllerValue) ReferenceValue( resultBorrowType.Type, ) } + +// SetDeleted sets the controller as deleted, i.e. functions panic from now on +func (v *StorageCapabilityControllerValue) SetDeleted(gauge common.MemoryGauge) { + + panicFunction := func(invocation Invocation) Value { + panic(errors.NewDefaultUserError("controller is deleted")) + } + + v.TargetFunction = NewHostFunctionValue( + gauge, + sema.StorageCapabilityControllerTypeTargetFunctionType, + panicFunction, + ) + v.RetargetFunction = NewHostFunctionValue( + gauge, + sema.StorageCapabilityControllerTypeRetargetFunctionType, + panicFunction, + ) + v.DeleteFunction = NewHostFunctionValue( + gauge, + sema.StorageCapabilityControllerTypeDeleteFunctionType, + panicFunction, + ) +} diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index a328f4dcb2..ccec6d6455 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2597,6 +2597,25 @@ func storeCapabilityController( } } +// removeCapabilityController removes a capability controller from the account's capability ID to controller storage map +func removeCapabilityController( + inter *interpreter.Interpreter, + address common.Address, + capabilityIDValue interpreter.UInt64Value, +) { + storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + + existed := inter.WriteStored( + address, + CapabilityControllerStorageDomain, + storageMapKey, + nil, + ) + if !existed { + panic(errors.NewUnreachableError()) + } +} + // getCapabilityController gets the capability controller for the given capability ID func getCapabilityController( inter *interpreter.Interpreter, @@ -2615,22 +2634,26 @@ func getCapabilityController( return nil } - capabilityController, ok := readValue.(interpreter.CapabilityControllerValue) + controller, ok := readValue.(interpreter.CapabilityControllerValue) if !ok { panic(errors.NewUnreachableError()) } // Inject functions - switch capabilityController := capabilityController.(type) { + switch controller := controller.(type) { case *interpreter.StorageCapabilityControllerValue: - capabilityController.RetargetFunction = - newStorageCapabilityControllerRetargetFunction(inter, address, capabilityController) - - // TODO: inject delete function in *interpreter.StorageCapabilityControllerValue - // TODO: inject delete function in *interpreter.AccountCapabilityControllerValue + controller.TargetFunction = + newStorageCapabilityControllerTargetFunction(inter, controller) + controller.RetargetFunction = + newStorageCapabilityControllerRetargetFunction(inter, address, controller) + controller.DeleteFunction = + newStorageCapabilityControllerDeleteFunction(inter, address, controller) + + case *interpreter.AccountCapabilityControllerValue: + // TODO: inject delete function } - return capabilityController + return controller } func getStorageCapabilityControllerReference( @@ -2657,6 +2680,19 @@ func getStorageCapabilityControllerReference( ) } +func newStorageCapabilityControllerTargetFunction( + inter *interpreter.Interpreter, + controller *interpreter.StorageCapabilityControllerValue, +) interpreter.FunctionValue { + return interpreter.NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + return controller.TargetPath + }, + ) +} + func newStorageCapabilityControllerRetargetFunction( inter *interpreter.Interpreter, address common.Address, @@ -2688,6 +2724,40 @@ func newStorageCapabilityControllerRetargetFunction( ) } +func newStorageCapabilityControllerDeleteFunction( + inter *interpreter.Interpreter, + address common.Address, + controller *interpreter.StorageCapabilityControllerValue, +) interpreter.FunctionValue { + return interpreter.NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + capabilityID := controller.CapabilityID + + unrecordPathCapabilityController( + inter, + locationRange, + address, + controller.TargetPath, + capabilityID, + ) + removeCapabilityController( + inter, + address, + capabilityID, + ) + + controller.SetDeleted(inter) + + return interpreter.Void + }, + ) +} + var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeUInt64, ValueType: interpreter.NilStaticType, From 6d963ed3ec806039525adfb84a66538d7b58eeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 2 May 2023 11:31:10 -0700 Subject: [PATCH 141/246] improve naming --- runtime/stdlib/account.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index ccec6d6455..1a09ff1ad0 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2339,7 +2339,7 @@ func newAuthAccountStorageCapabilitiesGetControllersFunction( // Get capability controllers iterator nextCapabilityID, count := - getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) + getStorageCapabilityControllerIDsIterator(inter, address, targetPathValue) var capabilityControllerIndex uint64 = 0 @@ -2410,7 +2410,7 @@ func newAuthAccountStorageCapabilitiesForEachControllerFunction( // Get capability controllers iterator nextCapabilityID, _ := - getPathCapabilityControllerIDsIterator(inter, address, targetPathValue) + getStorageCapabilityControllerIDsIterator(inter, address, targetPathValue) for { capabilityID, ok := nextCapabilityID() @@ -2506,7 +2506,7 @@ func newAuthAccountStorageCapabilitiesIssueFunction( ) storeCapabilityController(inter, address, capabilityIDValue, controller) - recordPathCapabilityController(inter, locationRange, address, targetPathValue, capabilityIDValue) + recordStorageCapabilityController(inter, locationRange, address, targetPathValue, capabilityIDValue) return interpreter.NewIDCapabilityValue( gauge, @@ -2714,8 +2714,8 @@ func newStorageCapabilityControllerRetargetFunction( oldTargetPathValue := controller.TargetPath capabilityID := controller.CapabilityID - unrecordPathCapabilityController(inter, locationRange, address, oldTargetPathValue, capabilityID) - recordPathCapabilityController(inter, locationRange, address, newTargetPathValue, capabilityID) + unrecordStorageCapabilityController(inter, locationRange, address, oldTargetPathValue, capabilityID) + recordStorageCapabilityController(inter, locationRange, address, newTargetPathValue, capabilityID) controller.TargetPath = newTargetPathValue @@ -2738,7 +2738,7 @@ func newStorageCapabilityControllerDeleteFunction( capabilityID := controller.CapabilityID - unrecordPathCapabilityController( + unrecordStorageCapabilityController( inter, locationRange, address, @@ -2767,7 +2767,7 @@ var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ // capability ID dictionaries (sets) by storage path identifier const PathCapabilityStorageDomain = "path_cap" -func recordPathCapabilityController( +func recordStorageCapabilityController( inter *interpreter.Interpreter, locationRange interpreter.LocationRange, address common.Address, @@ -2843,7 +2843,7 @@ func getPathCapabilityIDSet( return capabilityIDSet } -func unrecordPathCapabilityController( +func unrecordStorageCapabilityController( inter *interpreter.Interpreter, locationRange interpreter.LocationRange, address common.Address, @@ -2863,7 +2863,7 @@ func unrecordPathCapabilityController( // TODO: remove capability set if empty } -func getPathCapabilityControllerIDsIterator( +func getStorageCapabilityControllerIDsIterator( inter *interpreter.Interpreter, address common.Address, targetPathValue interpreter.PathValue, From 025c81bf936fa28e5c017a5f8d85a16da34cb9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 2 May 2023 11:51:20 -0700 Subject: [PATCH 142/246] implement AccountCapabilityController.delete --- runtime/capabilitycontrollers_test.go | 78 ++++++++++++++++++- .../value_accountcapabilitycontroller.go | 21 ++++- runtime/stdlib/account.go | 53 ++++++++++++- 3 files changed, 146 insertions(+), 6 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index d24abe5077..c1da0afbb7 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -1948,12 +1948,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) }) - // TODO: t.Run("delete", func(t *testing.T) { t.Parallel() - // TODO: t.Run("getController, getControllers", func(t *testing.T) { t.Parallel() @@ -2085,5 +2083,79 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) }) - // TODO: AccountCapabilityController: delete + t.Run("AccountCapabilityController", func(t *testing.T) { + + t.Parallel() + + t.Run("delete", func(t *testing.T) { + + t.Parallel() + + t.Run("getController, getControllers", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let controller: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap.id) + + let controllersBefore = signer.capabilities.account.getControllers() + assert(controllersBefore.length == 1) + assert(controllersBefore[0].capabilityID == 1) + + // Act + controller!.delete() + + // Assert + let controllerAfter: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap.id) + assert(controllerAfter == nil) + + let controllersAfter = signer.capabilities.account.getControllers() + assert(controllersAfter.length == 0) + } + } + `, + ) + require.NoError(t, err) + }) + + t.Run("delete", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + let controller: &AccountCapabilityController? = + signer.capabilities.account.getController(byCapabilityID: issuedCap.id) + + // Act + controller!.delete() + + // Assert + controller!.delete() + } + } + `, + ) + require.ErrorContains(t, err, "controller is deleted") + }) + }) + + }) } diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 7bdc69c90a..abefe258ab 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -32,6 +32,9 @@ import ( type AccountCapabilityControllerValue struct { BorrowType ReferenceStaticType CapabilityID UInt64Value + + // Injected functions + DeleteFunction FunctionValue } func NewUnmeteredAccountCapabilityControllerValue( @@ -181,8 +184,6 @@ func (v *AccountCapabilityControllerValue) ChildStorables() []atree.Storable { func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { - // TODO: sema.AccountCapabilityControllerTypeDeleteFunctionName - switch name { case sema.AccountCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID @@ -190,6 +191,8 @@ func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat case sema.AccountCapabilityControllerTypeBorrowTypeFieldName: return NewTypeValue(inter, v.BorrowType) + case sema.AccountCapabilityControllerTypeDeleteFunctionName: + return v.DeleteFunction } return nil @@ -218,3 +221,17 @@ func (v *AccountCapabilityControllerValue) ReferenceValue( resultBorrowType.Type, ) } + +// SetDeleted sets the controller as deleted, i.e. functions panic from now on +func (v *AccountCapabilityControllerValue) SetDeleted(gauge common.MemoryGauge) { + + panicFunction := func(invocation Invocation) Value { + panic(errors.NewDefaultUserError("controller is deleted")) + } + + v.DeleteFunction = NewHostFunctionValue( + gauge, + sema.AccountCapabilityControllerTypeDeleteFunctionType, + panicFunction, + ) +} diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 1a09ff1ad0..940f415458 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2650,7 +2650,8 @@ func getCapabilityController( newStorageCapabilityControllerDeleteFunction(inter, address, controller) case *interpreter.AccountCapabilityControllerValue: - // TODO: inject delete function + controller.DeleteFunction = + newAccountCapabilityControllerDeleteFunction(inter, address, controller) } return controller @@ -2920,6 +2921,25 @@ func recordAccountCapabilityController( } } +func unrecordAccountCapabilityController( + inter *interpreter.Interpreter, + address common.Address, + capabilityIDValue interpreter.UInt64Value, +) { + storageMapKey := interpreter.Uint64StorageMapKey(capabilityIDValue) + + storageMap := inter.Storage().GetStorageMap( + address, + AccountCapabilityStorageDomain, + true, + ) + + existed := storageMap.RemoveValue(inter, storageMapKey) + if !existed { + panic(errors.NewUnreachableError()) + } +} + func getAccountCapabilityControllerIDsIterator( inter *interpreter.Interpreter, address common.Address, @@ -3565,3 +3585,34 @@ func newAuthAccountAccountCapabilitiesForEachControllerFunction( }, ) } + +func newAccountCapabilityControllerDeleteFunction( + inter *interpreter.Interpreter, + address common.Address, + controller *interpreter.AccountCapabilityControllerValue, +) interpreter.FunctionValue { + return interpreter.NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeTargetFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + + capabilityID := controller.CapabilityID + + unrecordAccountCapabilityController( + inter, + address, + capabilityID, + ) + removeCapabilityController( + inter, + address, + capabilityID, + ) + + controller.SetDeleted(inter) + + return interpreter.Void + }, + ) +} From 54e0ce34a714f0aef4d02408d7a8cd01b48108fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 2 May 2023 13:40:55 -0700 Subject: [PATCH 143/246] introduce ConformingDeclaration, avoid InterfaceDeclaration conforming to CompositeLikeDeclaration --- runtime/ast/attachment.go | 24 ++++++++++----------- runtime/ast/composite.go | 19 ++++++++-------- runtime/ast/interface.go | 5 ----- runtime/sema/check_composite_declaration.go | 12 +++++------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/runtime/ast/attachment.go b/runtime/ast/attachment.go index 445f60940f..221742085d 100644 --- a/runtime/ast/attachment.go +++ b/runtime/ast/attachment.go @@ -80,6 +80,8 @@ func (*AttachmentDeclaration) isDeclaration() {} // but will be rejected in semantic analysis func (*AttachmentDeclaration) isStatement() {} +func (*AttachmentDeclaration) isCompositeLikeDeclaration() {} + func (d *AttachmentDeclaration) DeclarationIdentifier() *Identifier { return &d.Identifier } @@ -117,13 +119,13 @@ var attachmentConformanceSeparatorDoc prettier.Doc = prettier.Concat{ prettier.Line{}, } -func (e *AttachmentDeclaration) Doc() prettier.Doc { +func (d *AttachmentDeclaration) Doc() prettier.Doc { var doc prettier.Concat - if e.Access != AccessNotSpecified { + if d.Access != AccessNotSpecified { doc = append( doc, - prettier.Text(e.Access.Keyword()), + prettier.Text(d.Access.Keyword()), prettier.Space, ) } @@ -132,19 +134,19 @@ func (e *AttachmentDeclaration) Doc() prettier.Doc { doc, attachmentStatementDoc, prettier.Space, - prettier.Text(e.Identifier.Identifier), + prettier.Text(d.Identifier.Identifier), prettier.Space, attachmentStatementForDoc, prettier.Space, - e.BaseType.Doc(), + d.BaseType.Doc(), ) - if len(e.Conformances) > 0 { + if len(d.Conformances) > 0 { conformancesDoc := prettier.Concat{ prettier.Line{}, } - for i, conformance := range e.Conformances { + for i, conformance := range d.Conformances { if i > 0 { conformancesDoc = append( conformancesDoc, @@ -163,7 +165,7 @@ func (e *AttachmentDeclaration) Doc() prettier.Doc { prettier.Dedent{ Doc: prettier.Concat{ prettier.Line{}, - e.Members.Doc(), + d.Members.Doc(), }, }, ) @@ -182,7 +184,7 @@ func (e *AttachmentDeclaration) Doc() prettier.Doc { doc = append( doc, prettier.Space, - e.Members.Doc(), + d.Members.Doc(), ) } @@ -204,10 +206,6 @@ func (d *AttachmentDeclaration) String() string { return Prettier(d) } -func (d *AttachmentDeclaration) IsInterface() bool { - return false -} - // AttachExpression type AttachExpression struct { Base Expression diff --git a/runtime/ast/composite.go b/runtime/ast/composite.go index 782abea78e..eb13674841 100644 --- a/runtime/ast/composite.go +++ b/runtime/ast/composite.go @@ -27,17 +27,20 @@ import ( "github.com/onflow/cadence/runtime/errors" ) +// ConformingDeclaration +type ConformingDeclaration interface { + Declaration + ConformanceList() []*NominalType +} + // CompositeDeclaration // NOTE: For events, only an empty initializer is declared type CompositeLikeDeclaration interface { - Element - Declaration - Statement + ConformingDeclaration + isCompositeLikeDeclaration() Kind() common.CompositeKind - ConformanceList() []*NominalType - IsInterface() bool } type CompositeDeclaration struct { @@ -92,6 +95,8 @@ func (*CompositeDeclaration) isDeclaration() {} // but will be rejected in semantic analysis func (*CompositeDeclaration) isStatement() {} +func (*CompositeDeclaration) isCompositeLikeDeclaration() {} + func (d *CompositeDeclaration) DeclarationIdentifier() *Identifier { return &d.Identifier } @@ -275,10 +280,6 @@ func (d *CompositeDeclaration) ConformanceList() []*NominalType { return d.Conformances } -func (d *CompositeDeclaration) IsInterface() bool { - return false -} - // FieldDeclarationFlags type FieldDeclarationFlags uint8 diff --git a/runtime/ast/interface.go b/runtime/ast/interface.go index d5ea3b997e..cf19d4a03b 100644 --- a/runtime/ast/interface.go +++ b/runtime/ast/interface.go @@ -41,7 +41,6 @@ type InterfaceDeclaration struct { var _ Element = &InterfaceDeclaration{} var _ Declaration = &InterfaceDeclaration{} var _ Statement = &InterfaceDeclaration{} -var _ CompositeLikeDeclaration = &InterfaceDeclaration{} func NewInterfaceDeclaration( gauge common.MemoryGauge, @@ -133,7 +132,3 @@ func (d *InterfaceDeclaration) ConformanceList() []*NominalType { func (d *InterfaceDeclaration) Kind() common.CompositeKind { return d.CompositeKind } - -func (d *InterfaceDeclaration) IsInterface() bool { - return true -} diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 4e756dd78d..0b029bd83c 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -311,7 +311,7 @@ func (checker *Checker) declareCompositeLikeNestedTypes( nestedCompositeDeclaration, isCompositeDeclaration := nestedDeclaration.(ast.CompositeLikeDeclaration) - if isCompositeDeclaration && !nestedCompositeDeclaration.IsInterface() { + if isCompositeDeclaration { nestedCompositeType, ok := nestedType.(*CompositeType) if !ok { @@ -1050,14 +1050,14 @@ func (checker *Checker) initializerParameters(initializers []*ast.SpecialFunctio } func (checker *Checker) explicitInterfaceConformances( - declaration ast.CompositeLikeDeclaration, + conformingDeclaration ast.ConformingDeclaration, compositeType CompositeKindedType, ) []*InterfaceType { var interfaceTypes []*InterfaceType seenConformances := map[*InterfaceType]bool{} - for _, conformance := range declaration.ConformanceList() { + for _, conformance := range conformingDeclaration.ConformanceList() { convertedType := checker.ConvertType(conformance) if interfaceType, ok := convertedType.(*InterfaceType); ok { @@ -1325,7 +1325,7 @@ func (checker *Checker) checkCompositeLikeConformance( // checkConformanceKindMatch ensures the composite kinds match. // e.g. a structure shouldn't be able to conform to a resource interface. func (checker *Checker) checkConformanceKindMatch( - compositeDeclaration ast.CompositeLikeDeclaration, + conformingDeclaration ast.ConformingDeclaration, compositeKindedType CompositeKindedType, interfaceConformance *InterfaceType, ) { @@ -1345,12 +1345,12 @@ func (checker *Checker) checkConformanceKindMatch( var compositeKindMismatchIdentifier *ast.Identifier - conformances := compositeDeclaration.ConformanceList() + conformances := conformingDeclaration.ConformanceList() if len(conformances) == 0 { // For type requirements, there is no explicit conformance. // Hence, log the error at the type requirement (i.e: declaration identifier) - compositeKindMismatchIdentifier = compositeDeclaration.DeclarationIdentifier() + compositeKindMismatchIdentifier = conformingDeclaration.DeclarationIdentifier() } else { // Otherwise, find the conformance which resulted in the mismatch, // and log the error there. From c6117d12ced0daa87e5097bc275c9c8e50744d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 2 May 2023 16:30:19 -0700 Subject: [PATCH 144/246] add meeting notes for Apr 13, 2023 --- meetings/2023-04-13.md | 291 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 meetings/2023-04-13.md diff --git a/meetings/2023-04-13.md b/meetings/2023-04-13.md new file mode 100644 index 0000000000..80a5fad37b --- /dev/null +++ b/meetings/2023-04-13.md @@ -0,0 +1,291 @@ + +# Apr 13, 2023 + +* First fully open/public meeting 🎉 + +* Code coverage feature shipping in CLI 🎉 + + * Huge shoutout to Ardit Marku from Build Squad + +* New contributor: darkdrag00nv2 🎉 + +* Contribute: + * FLIPs + + * Cadence: [https://github.com/onflow/cadence/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Good+First+Issue%22](https://github.com/onflow/cadence/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Good+First+Issue%22) + + * Cadence tools: [https://github.com/onflow/cadence-tools/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Good+First+Issue%22](https://github.com/onflow/cadence-tools/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Good+First+Issue%22) + +## FLIPs + +### AuthAccount capabilities + +* [https://github.com/onflow/flips/pull/53](https://github.com/onflow/flips/pull/53) +* Status + * Approved and implemented 🎉 + +### Capability Controllers + +* FLIP: [https://github.com/onflow/flow/pull/798](https://github.com/onflow/flow/pull/798) + +* Status: + + * Had many breakout sessions + + * Final API discussions + + * Implementation in progress + + * No remaining concerns + +* Open problems: + + * API: Capability publishing + +* Next steps: + + * One more breakout session + + * **Vote!** + +### Entitlements and Safe Downcasting + +* FLIP: [https://github.com/onflow/flips/pull/54](https://github.com/onflow/flips/pull/54) + +* Status: + + * Had breakout session for nested entitlements. Consensus on solution + + * Implementation in progress + + * No remaining concerns + +* Open problems: + + * Attachments + +* Next steps: + + * Address remaining technical issues async + + * **Vote!** + +### Attachments + +* Attachments FLIP: [https://github.com/onflow/flips/pull/11](https://github.com/onflow/flips/pull/11) + +* Forum discussion: [https://forum.onflow.org/t/flip-cadence-extensions-attachments/3645/2](https://forum.onflow.org/t/flip-cadence-extensions-attachments/3645/2) + +* Status: + + * Approved and merged 🎉 + + * Deployed to Testnet + + * Receiving feedback + + * Discovering use-cases + + * Need mitigation for trolling attack + + * Mainnet blocked on solution for trolling attack + + * Had breakout session for trolling attack + + * Have idea for solution, but might depend on breaking change (Stable Cadence) + +* Open problem: + + * Trolling attack + +* Next steps: + + * Address trolling attack + + * Need breakout session + +### Interface Inheritance + +* FLIP: [https://github.com/onflow/flips/pull/40](https://github.com/onflow/flips/pull/40) + +* Forum discussion: [https://forum.onflow.org/t/flip-interface-inheritance-in-cadence/3750](https://forum.onflow.org/t/flip-interface-inheritance-in-cadence/3750) + +* Status: + + * Was on hold due to prioritization, planning to continue now, given other FLIPs done + + * Had breakout session + + * Implementation available + +* Open questions: + + * Multiple interface default functions (diamond problem) + + * Meeting notes: [https://docs.google.com/document/d/1LUvRg0T09mdE2Ndgti6xI1AY_KPYGJr2ZRLVuqzGxFg/edit#heading=h.d4wc1pr484vi](https://docs.google.com/document/d/1LUvRg0T09mdE2Ndgti6xI1AY_KPYGJr2ZRLVuqzGxFg/edit#heading=h.d4wc1pr484vi) + +* Next steps: + + * Schedule another breakout session + + * Is this still a blocker for NFT/FT v2? + + * Still being used, expected to be available + + * Not 100% required, but preferred. Less prone to errors + +### Extend transaction format + +* FLIP: [https://github.com/onflow/flips/pull/41](https://github.com/onflow/flips/pull/41) + +* Status: + + * No updates at the moment + + * Last breakout session was a while back + + * Planning to restart working group + +* Open problems: + + * Unexplored corner: + + * Useful for multi-sign use-cases + + * Does model also make sense for single-sign use-cases? + + * Signer assignment + + * Mapping signatures to roles + + * Currently Flow transaction signatures are sequential + + * Action-based model? + +* Next steps: + + * Have another breakout session + +### External Mutability + +* [https://github.com/onflow/flips/pull/58](https://github.com/onflow/flips/pull/58) + +* [https://github.com/onflow/flips/pull/59](https://github.com/onflow/flips/pull/59) + +* Status + + * Had breakout session + + * Multiple alternatives / FLIPs + + * FLIP 58: + + * Add additional static checks + + * Deemed overkill + + * FLIP 59: + + * Remove/restrict let fields + + * Deemed better than 58 + + * Impact analysis: + + * Inner mutability used as a feature + + * E.g. MetadataViews standard + + * Need delegation functions for nested members + + * More boilerplate + + * Moves problem, but does not solve it + + * Investigating another idea (entitlements, references to fields) + +* Open problems: + + * Usability + + * FLIPs have different compromises + +* Next steps: + + * Breakout session to determine how to proceed + +## Related FLIPs + +### Account linking standard + +* FLIP: [https://github.com/onflow/flips/pull/72](https://github.com/onflow/flips/pull/72) + +* Status: + + * Had breakout session + + * Great feedback + + * Implementation in progress: + + * [https://github.com/Flowtyio/restricted-child-account](https://github.com/Flowtyio/restricted-child-account) + + * Positive sentiment, thumbs up from e.g. Flowty, Niftory, Dapper, etc + + * Wallets like it, even though it is more work for them + +* Open problem: + + * Regulatory risk for application developers + + * Discussions with legal council, technical solution + + * Discovery of linked assets + +* Next steps: + + * Work out how to restrict access + + * Continue implementation + +### FT/NFT v2 standards + +* FLIP: [https://github.com/onflow/flips/pull/56](https://github.com/onflow/flips/pull/56) + +* Status: + + * Multiple breakout sessions + + * Several major changes: + + * [https://github.com/onflow/flow-nft/pull/126#issuecomment-1505864702](https://github.com/onflow/flow-nft/pull/126#issuecomment-1505864702) + +* Open problems: + + * Waiting on FLIP for emitting events from interfaces (would allow nested type requirements) + + * [https://github.com/onflow/cadence/issues/2069](https://github.com/onflow/cadence/issues/2069) + + * Need to open FLIP for removal of nested type requirements + + * Upgrade constraints + +* Next steps: + + * Breakout session for upgrade + + * Maybe needs storage migration + + * Open FLIP for removal of nested type requirements + +## Questions + +* Changelog for Stable Cadence? + + * Announcement of breaking changes Stable Cadence in forum + + * [https://forum.onflow.org/t/another-update-on-stable-cadence/3715](https://forum.onflow.org/t/another-update-on-stable-cadence/3715) + + * Preview release available, needs to be updated + + * Will create TN instance with changes From 9eb2facdbafbc03e623f2a4c301a4296b4b0ed80 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 3 May 2023 13:23:47 +0300 Subject: [PATCH 145/246] Introduce the Test.expectFailure function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bastian Müller --- runtime/stdlib/test_contract.go | 107 +++++++++++++++++ runtime/stdlib/test_test.go | 200 +++++++++++++++++++++++++++++++- 2 files changed, 306 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index 56e697d11f..2f8bc1679d 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -19,6 +19,9 @@ package stdlib import ( + "fmt" + "strings" + "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" @@ -42,6 +45,7 @@ type TestContractType struct { beGreaterThanFunction interpreter.FunctionValue containFunction interpreter.FunctionValue beLessThanFunction interpreter.FunctionValue + expectFailureFunction interpreter.FunctionValue } // 'Test.assert' function @@ -796,6 +800,93 @@ func newTestTypeBeLessThanFunctionType(matcherType *sema.CompositeType) *sema.Fu } } +// Test.expectFailure function + +const testExpectFailureFunctionName = "expectFailure" + +const testExpectFailureFunctionDocString = ` +Wraps a function call in a closure, and expects it to fail with +an error message that contains the given error message portion. +` + +func newTestTypeExpectFailureFunctionType() *sema.FunctionType { + return &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "functionWrapper", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.FunctionType{ + Parameters: nil, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.VoidType, + ), + }, + ), + }, + { + Identifier: "errorMessageSubstring", + TypeAnnotation: sema.NewTypeAnnotation( + sema.StringType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + sema.VoidType, + ), + RequiredArgumentCount: sema.RequiredArgumentCount(2), + } +} + +func newTestTypeExpectFailureFunction( + testExpectFailureFunctionType *sema.FunctionType, +) interpreter.FunctionValue { + return interpreter.NewUnmeteredHostFunctionValue( + testExpectFailureFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + functionValue, ok := invocation.Arguments[0].(interpreter.FunctionValue) + if !ok { + panic(errors.NewUnreachableError()) + } + functionType := functionValue.FunctionType() + + errorMessage, ok := invocation.Arguments[1].(*interpreter.StringValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + failedAsExpected := true + + defer inter.RecoverErrors(func(internalErr error) { + if !failedAsExpected { + panic(internalErr) + } else if !strings.Contains(internalErr.Error(), errorMessage.Str) { + msg := fmt.Sprintf( + "Expected error message to include: %s.", + errorMessage, + ) + panic( + errors.NewDefaultUserError(msg), + ) + } + }) + + _, err := inter.InvokeExternally( + functionValue, + functionType, + nil, + ) + if err == nil { + failedAsExpected = false + panic(errors.NewDefaultUserError("Expected a failure, but found none.")) + } + + return interpreter.Void + }, + ) +} + func newTestTypeBeLessThanFunction( beLessThanFunctionType *sema.FunctionType, matcherTestFunctionType *sema.FunctionType, @@ -1076,6 +1167,21 @@ func newTestContractType() *TestContractType { matcherTestFunctionType, ) + // Test.expectFailure() + expectFailureFunctionType := newTestTypeExpectFailureFunctionType() + compositeType.Members.Set( + testExpectFailureFunctionName, + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testExpectFailureFunctionName, + expectFailureFunctionType, + testExpectFailureFunctionDocString, + ), + ) + ty.expectFailureFunction = newTestTypeExpectFailureFunction( + expectFailureFunctionType, + ) + return ty } @@ -1172,6 +1278,7 @@ func (t *TestContractType) NewTestContract( compositeValue.Functions[testTypeContainFunctionName] = t.containFunction compositeValue.Functions[testTypeBeGreaterThanFunctionName] = t.beGreaterThanFunction compositeValue.Functions[testTypeBeLessThanFunctionName] = t.beLessThanFunction + compositeValue.Functions[testExpectFailureFunctionName] = t.expectFailureFunction return compositeValue, nil } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index acb18a8e7e..0397b77ea5 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/activations" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" cdcErrors "github.com/onflow/cadence/runtime/errors" @@ -84,11 +85,16 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr var uuid uint64 = 0 + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, AssertFunction) + interpreter.Declare(baseActivation, PanicFunction) + inter, err := interpreter.NewInterpreter( interpreter.ProgramFromChecker(checker), checker.Location, &interpreter.Config{ - Storage: storage, + Storage: storage, + BaseActivation: baseActivation, ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { if location == TestContractLocation { program := interpreter.ProgramFromChecker(GetTestContractType().Checker) @@ -1438,3 +1444,195 @@ func TestTestExpect(t *testing.T) { assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) } + +func TestTestExpectFailure(t *testing.T) { + + t.Parallel() + + t.Run("expect failure with no failure found", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test() { + let foo = Foo(answer: 42) + Test.expectFailure(fun(): Void { + foo.correctAnswer(42) + }, errorMessageSubstring: "wrong answer!") + } + + pub struct Foo { + priv let answer: UInt8 + + init(answer: UInt8) { + self.answer = answer + } + + pub fun correctAnswer(_ input: UInt8): Bool { + if self.answer != input { + panic("wrong answer!") + } + return true + } + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorContains( + t, + err, + "Expected a failure, but found none.", + ) + }) + + t.Run("expect failure with matching error message", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test() { + let foo = Foo(answer: 42) + Test.expectFailure(fun(): Void { + foo.correctAnswer(43) + }, errorMessageSubstring: "wrong answer!") + } + + pub struct Foo { + priv let answer: UInt8 + + init(answer: UInt8) { + self.answer = answer + } + + pub fun correctAnswer(_ input: UInt8): Bool { + if self.answer != input { + panic("wrong answer!") + } + return true + } + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.NoError(t, err) + }) + + t.Run("expect failure with mismatching error message", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test() { + let foo = Foo(answer: 42) + Test.expectFailure(fun(): Void { + foo.correctAnswer(43) + }, errorMessageSubstring: "what is wrong?") + } + + pub struct Foo { + priv let answer: UInt8 + + init(answer: UInt8) { + self.answer = answer + } + + pub fun correctAnswer(_ input: UInt8): Bool { + if self.answer != input { + panic("wrong answer!") + } + return true + } + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorContains( + t, + err, + "Expected error message to include: \"what is wrong?\".", + ) + }) + + t.Run("expect failure with wrong function signature", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test() { + let foo = Foo(answer: 42) + Test.expectFailure(fun(answer: UInt64): Foo { + foo.correctAnswer(42) + return foo + }, errorMessageSubstring: "wrong answer") + } + + pub struct Foo { + priv let answer: UInt8 + + init(answer: UInt8) { + self.answer = answer + } + + pub fun correctAnswer(_ input: UInt8): Bool { + if self.answer != input { + panic("wrong answer!") + } + return true + } + } + ` + + _, err := newTestContractInterpreter(t, script) + errs := checker.RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + }) + + t.Run("expect failure with wrong error message type", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test() { + let foo = Foo(answer: 42) + Test.expectFailure(fun(): Void { + foo.correctAnswer(42) + }, errorMessageSubstring: ["wrong answer"]) + } + + pub struct Foo { + priv let answer: UInt8 + + init(answer: UInt8) { + self.answer = answer + } + + pub fun correctAnswer(_ input: UInt8): Bool { + if self.answer != input { + panic("wrong answer!") + } + return true + } + } + ` + + _, err := newTestContractInterpreter(t, script) + errs := checker.RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + }) +} From ea0b0e1b002250cd82248a3a625cbdfae12e8358 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 3 May 2023 08:13:26 -0700 Subject: [PATCH 146/246] Address TODO's --- runtime/sema/type.go | 67 +++++++---- runtime/tests/checker/attachments_test.go | 20 +++- runtime/tests/checker/interface_test.go | 132 ++++++++++++++++++++++ 3 files changed, 194 insertions(+), 25 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index d23da5e3c7..0fd0c0d9c7 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4327,17 +4327,19 @@ type InterfaceType struct { TypeID TypeID QualifiedIdentifier string } - Identifier string - Fields []string - InitializerParameters []Parameter - CompositeKind common.CompositeKind - cachedIdentifiersLock sync.RWMutex - memberResolversOnce sync.Once - InitializerPurity FunctionPurity + Identifier string + Fields []string + InitializerParameters []Parameter + CompositeKind common.CompositeKind + cachedIdentifiersLock sync.RWMutex + memberResolversOnce sync.Once + effectiveInterfaceConformancesOnce sync.Once + effectiveInterfaceConformanceSetOnce sync.Once + InitializerPurity FunctionPurity - ExplicitInterfaceConformances []*InterfaceType - effectiveInterfaceConformancesOnce sync.Once - effectiveInterfaceConformances []Conformance + ExplicitInterfaceConformances []*InterfaceType + effectiveInterfaceConformances []Conformance + effectiveInterfaceConformanceSet *InterfaceSet } var _ Type = &InterfaceType{} @@ -4575,6 +4577,21 @@ func (t *InterfaceType) EffectiveInterfaceConformances() []Conformance { return t.effectiveInterfaceConformances } +func (t *InterfaceType) EffectiveInterfaceConformanceSet() *InterfaceSet { + t.initializeEffectiveInterfaceConformanceSet() + return t.effectiveInterfaceConformanceSet +} + +func (t *InterfaceType) initializeEffectiveInterfaceConformanceSet() { + t.effectiveInterfaceConformanceSetOnce.Do(func() { + t.effectiveInterfaceConformanceSet = NewInterfaceSet() + + for _, conformance := range t.EffectiveInterfaceConformances() { + t.effectiveInterfaceConformanceSet.Add(conformance.InterfaceType) + } + }) +} + // distinctConformances recursively visit conformances and their conformances, // and return all the distinct conformances as an array. func distinctConformances( @@ -5656,7 +5673,6 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // // The holder of the reference may only restrict the reference. - // TODO: once interfaces can conform to interfaces, include return IsSubType(typedInnerSubType, restrictedSuperType) && typedInnerSuperType.RestrictionSet(). IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) @@ -5759,7 +5775,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // An unauthorized reference to an interface type `&T` // is a supertype of a reference to a restricted type `&{U}`: - // if the restriction set contains that explicit interface type. + // if at least one value in the restriction set is a subtype of the interface supertype. // This particular case comes up when checking attachment access; enabling the following expression to typechecking: // resource interface I { /* ... */ } @@ -5773,11 +5789,8 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // when checking whether an attachment declared for `I` is accessible on a value of type `&R{X}`, // even if `R <: I`, we only want to allow access if `X <: I` - // Once interfaces can conform to interfaces, - // this should instead check that at least one value in the restriction set - // is a subtype of the interface supertype case *RestrictedType: - return typedInnerSubType.RestrictionSet().Contains(typedInnerSuperType) + return isRestrictedTypeASubTypeOfInterface(typedInnerSubType, typedInnerSuperType) } return false @@ -5935,7 +5948,6 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // and `T` conforms to `Vs`. // `Us` and `Vs` do *not* have to be subsets. - // TODO: once interfaces can conform to interfaces, include return IsSubType(restrictedSubtype, restrictedSuperType) && typedSuperType.RestrictionSet(). IsSubsetOf(restrictedSubtype.EffectiveInterfaceConformanceSet()) @@ -6043,13 +6055,11 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return false } - // TODO: once interfaces can conform to interfaces, include return typedSubType.EffectiveInterfaceConformanceSet(). Contains(typedSuperType) - // An interface type is a supertype of a restricted type if the restricted set contains - // that explicit interface type. Once interfaces can conform to interfaces, this should instead - // check that at least one value in the restriction set is a subtype of the interface supertype + // An interface type is a supertype of a restricted type if at least one value + // in the restriction set is a subtype of the interface supertype. // This particular case comes up when checking attachment access; enabling the following expression to typechecking: // resource interface I { /* ... */ } @@ -6058,11 +6068,11 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // let i : {I} = ... // some operation constructing `i` // let a = i[A] // must here check that `i`'s type is a subtype of `A`'s base type, or that {I} <: I case *RestrictedType: - return typedSubType.RestrictionSet().Contains(typedSuperType) + return isRestrictedTypeASubTypeOfInterface(typedSubType, typedSuperType) case *InterfaceType: - // TODO: Once interfaces can conform to interfaces, check conformances here - return false + return typedSubType.EffectiveInterfaceConformanceSet(). + Contains(typedSuperType) } case ParameterizedType: @@ -6118,6 +6128,15 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return false } +func isRestrictedTypeASubTypeOfInterface(restrictedType *RestrictedType, interfaceType *InterfaceType) bool { + for _, restriction := range restrictedType.Restrictions { + if IsSubType(restriction, interfaceType) { + return true + } + } + return false +} + // UnwrapOptionalType returns the type if it is not an optional type, // or the inner-most type if it is (optional types are repeatedly unwrapped) func UnwrapOptionalType(ty Type) Type { diff --git a/runtime/tests/checker/attachments_test.go b/runtime/tests/checker/attachments_test.go index 25ce8a64e0..dd5fafc960 100644 --- a/runtime/tests/checker/attachments_test.go +++ b/runtime/tests/checker/attachments_test.go @@ -2699,7 +2699,25 @@ func TestCheckAttachToRestrictedType(t *testing.T) { require.NoError(t, err) }) - // TODO: once interfaces can conform to interfaces, add more tests here for interface hierarchy + t.Run("attach to super interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, + ` + resource interface I {} + resource interface I2: I {} + resource R: I2 {} + attachment A for I {} + pub fun foo() { + let r: @{I2} <- create R() + destroy attach A() to <-r + } + `, + ) + + require.NoError(t, err) + }) } func TestCheckAttachWithArguments(t *testing.T) { diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 248bf3d645..191507259f 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3857,3 +3857,135 @@ func TestCheckInterfaceEventsInheritance(t *testing.T) { require.ErrorAs(t, errs[1], &conformanceError) }) } + +func TestCheckInheritedInterfacesSubtyping(t *testing.T) { + + t.Parallel() + + t.Run("restricted type subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct S: B {} + + + fun foo(): {A} { + var s: S{B} = S() + return s + } + `) + + require.NoError(t, err) + }) + + t.Run("reference type subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct S: B {} + + + fun foo(): &{A} { + var s = S() + return &s as &S + } + `) + + require.NoError(t, err) + }) + + t.Run("attachment on restricted type", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface A {} + + resource interface B: A {} + + resource R: B {} + + attachment X for A {} + + fun foo() { + var r: @{B} <- create R() + let x = r[X] + destroy r + } + `) + + require.NoError(t, err) + }) + + t.Run("attachment on reference type", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface A {} + + resource interface B: A {} + + resource R: B {} + + attachment X for A {} + + fun foo() { + var r <- create R() + let b = &r as &{B} + let x = b[X] + destroy r + } + `) + + require.NoError(t, err) + }) + + t.Run("concrete type subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A {} + + contract interface B: A {} + + contract S: B {} + + fun foo(a: [S]): [A] { + return a // must be covariant + } + `) + + require.NoError(t, err) + }) + + t.Run("inheriting interface subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface A {} + + contract interface B: A {} + + contract S: B {} + + fun foo(a: [B]): [A] { + return a // must be covariant + } + `) + + require.NoError(t, err) + }) +} From c1486dbc47b13a5181c1a94e44767ad0a6a61afa Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 3 May 2023 10:36:05 -0700 Subject: [PATCH 147/246] Refactor code --- runtime/sema/check_composite_declaration.go | 8 ++++---- runtime/sema/errors.go | 8 ++++---- runtime/sema/type.go | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 0b029bd83c..3e17d835f5 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1051,7 +1051,7 @@ func (checker *Checker) initializerParameters(initializers []*ast.SpecialFunctio func (checker *Checker) explicitInterfaceConformances( conformingDeclaration ast.ConformingDeclaration, - compositeType CompositeKindedType, + compositeKindedType CompositeKindedType, ) []*InterfaceType { var interfaceTypes []*InterfaceType @@ -1066,9 +1066,9 @@ func (checker *Checker) explicitInterfaceConformances( if seenConformances[interfaceType] { checker.report( &DuplicateConformanceError{ - CompositeType: compositeType, - InterfaceType: interfaceType, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, conformance.Identifier), + CompositeKindedType: compositeKindedType, + InterfaceType: interfaceType, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, conformance.Identifier), }, ) } diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 2d12716e25..81d921776b 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1414,8 +1414,8 @@ func (n NestedConformanceMismatchNote) Message() string { // // TODO: just make this a warning? type DuplicateConformanceError struct { - CompositeType CompositeKindedType - InterfaceType *InterfaceType + CompositeKindedType CompositeKindedType + InterfaceType *InterfaceType ast.Range } @@ -1429,8 +1429,8 @@ func (*DuplicateConformanceError) IsUserError() {} func (e *DuplicateConformanceError) Error() string { return fmt.Sprintf( "%s `%s` repeats conformance to %s `%s`", - e.CompositeType.GetCompositeKind().Name(), - e.CompositeType.QualifiedString(), + e.CompositeKindedType.GetCompositeKind().Name(), + e.CompositeKindedType.QualifiedString(), e.InterfaceType.CompositeKind.DeclarationKind(true).Name(), e.InterfaceType.QualifiedString(), ) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 0fd0c0d9c7..34ec066517 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4600,6 +4600,10 @@ func distinctConformances( seenConformances map[*InterfaceType]struct{}, ) []Conformance { + if len(conformances) == 0 { + return nil + } + collectedConformances := make([]Conformance, 0) var conformanceChainRoot *InterfaceType From 653caeb4e70316c65377fc6e33b899d8c1862ddd Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 3 May 2023 15:16:12 -0700 Subject: [PATCH 148/246] Add restricted type subsytping to inheriting interfaces --- runtime/sema/check_casting_expression.go | 8 +- runtime/sema/type.go | 66 ++++++------ runtime/sema/type_test.go | 6 +- runtime/tests/checker/interface_test.go | 131 ++++++++++++++++++++++- 4 files changed, 167 insertions(+), 44 deletions(-) diff --git a/runtime/sema/check_casting_expression.go b/runtime/sema/check_casting_expression.go index 08aea89e25..2cf8d3b364 100644 --- a/runtime/sema/check_casting_expression.go +++ b/runtime/sema/check_casting_expression.go @@ -241,7 +241,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { if typedInnerSubType, ok := typedSubType.Type.(*CompositeType); ok { return IsSubType(typedInnerSubType, restrictedSuperType) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -262,7 +262,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { if typedInnerSubType, ok := typedSubType.Type.(*CompositeType); ok { return IsSubType(typedInnerSubType, restrictedSuperType) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -285,7 +285,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { if typedInnerSubType, ok := typedSubType.Type.(*CompositeType); ok { return IsSubType(typedInnerSubType, restrictedSuperType) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } } @@ -324,7 +324,7 @@ func FailableCastCanSucceed(subType, superType Type) bool { // and `T` conforms to `Us`. return IsSubType(typedSubType, typedSuperType.Type) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedSubType.EffectiveInterfaceConformanceSet()) default: diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 34ec066517..8ea85a9696 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5665,8 +5665,8 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // The requirement for `T` to conform to `Vs` is implied by the subset requirement. return IsSubType(typedInnerSubType.Type, restrictedSuperType) && - typedInnerSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.RestrictionSet()) + typedInnerSuperType.EffectiveRestrictionSet(). + IsSubsetOf(typedInnerSubType.EffectiveRestrictionSet()) case *CompositeType: // An unauthorized reference to an unrestricted type `&T` @@ -5678,7 +5678,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // The holder of the reference may only restrict the reference. return IsSubType(typedInnerSubType, restrictedSuperType) && - typedInnerSuperType.RestrictionSet(). + typedInnerSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedInnerSubType.EffectiveInterfaceConformanceSet()) } @@ -5710,8 +5710,8 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // and may only further restrict the reference to the composite. return typedInnerSubType.Type == typedInnerSuperType.Type && - typedInnerSuperType.RestrictionSet(). - IsSubsetOf(typedInnerSubType.RestrictionSet()) + typedInnerSuperType.EffectiveRestrictionSet(). + IsSubsetOf(typedInnerSubType.EffectiveRestrictionSet()) } switch typedInnerSubType.Type { @@ -5794,7 +5794,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // even if `R <: I`, we only want to allow access if `X <: I` case *RestrictedType: - return isRestrictedTypeASubTypeOfInterface(typedInnerSubType, typedInnerSuperType) + return typedInnerSubType.EffectiveRestrictionSet().Contains(typedInnerSuperType) } return false @@ -5941,8 +5941,8 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // and `Vs` is a subset of `Us`. return IsSubType(restrictedSubtype, restrictedSuperType) && - typedSuperType.RestrictionSet(). - IsSubsetOf(typedSubType.RestrictionSet()) + typedSuperType.EffectiveRestrictionSet(). + IsSubsetOf(typedSubType.EffectiveRestrictionSet()) } if restrictedSubtype, ok := restrictedSubtype.(*CompositeType); ok { @@ -5953,7 +5953,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // `Us` and `Vs` do *not* have to be subsets. return IsSubType(restrictedSubtype, restrictedSuperType) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(restrictedSubtype.EffectiveInterfaceConformanceSet()) } @@ -5964,7 +5964,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // and `T` conforms to `Us`. return IsSubType(typedSubType, typedSuperType.Type) && - typedSuperType.RestrictionSet(). + typedSuperType.EffectiveRestrictionSet(). IsSubsetOf(typedSubType.EffectiveInterfaceConformanceSet()) } @@ -6072,7 +6072,7 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // let i : {I} = ... // some operation constructing `i` // let a = i[A] // must here check that `i`'s type is a subtype of `A`'s base type, or that {I} <: I case *RestrictedType: - return isRestrictedTypeASubTypeOfInterface(typedSubType, typedSuperType) + return typedSubType.EffectiveRestrictionSet().Contains(typedSuperType) case *InterfaceType: return typedSubType.EffectiveInterfaceConformanceSet(). @@ -6132,15 +6132,6 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return false } -func isRestrictedTypeASubTypeOfInterface(restrictedType *RestrictedType, interfaceType *InterfaceType) bool { - for _, restriction := range restrictedType.Restrictions { - if IsSubType(restriction, interfaceType) { - return true - } - } - return false -} - // UnwrapOptionalType returns the type if it is not an optional type, // or the inner-most type if it is (optional types are repeatedly unwrapped) func UnwrapOptionalType(ty Type) Type { @@ -6317,11 +6308,11 @@ func (t *TransactionType) Resolve(_ *TypeParameterTypeOrderedMap) Type { type RestrictedType struct { Type Type // an internal set of field `Restrictions` - restrictionSet *InterfaceSet - Restrictions []*InterfaceType - restrictionSetOnce sync.Once - memberResolvers map[string]MemberResolver - memberResolversOnce sync.Once + effectiveRestrictionSet *InterfaceSet + Restrictions []*InterfaceType + effectiveRestrictionSetOnce sync.Once + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ Type = &RestrictedType{} @@ -6329,7 +6320,7 @@ var _ Type = &RestrictedType{} func NewRestrictedType(memoryGauge common.MemoryGauge, typ Type, restrictions []*InterfaceType) *RestrictedType { common.UseMemory(memoryGauge, common.RestrictedSemaTypeMemoryUsage) - // Also meter the cost for the `restrictionSet` here, since ordered maps are not separately metered. + // Also meter the cost for the `effectiveRestrictionSet` here, since ordered maps are not separately metered. wrapperUsage, entryListUsage, entriesUsage := common.NewOrderedMapMemoryUsages(uint64(len(restrictions))) common.UseMemory(memoryGauge, wrapperUsage) common.UseMemory(memoryGauge, entryListUsage) @@ -6341,16 +6332,21 @@ func NewRestrictedType(memoryGauge common.MemoryGauge, typ Type, restrictions [] } } -func (t *RestrictedType) RestrictionSet() *InterfaceSet { - t.initializeRestrictionSet() - return t.restrictionSet +func (t *RestrictedType) EffectiveRestrictionSet() *InterfaceSet { + t.initializeEffectiveRestrictionSet() + return t.effectiveRestrictionSet } -func (t *RestrictedType) initializeRestrictionSet() { - t.restrictionSetOnce.Do(func() { - t.restrictionSet = NewInterfaceSet() +func (t *RestrictedType) initializeEffectiveRestrictionSet() { + t.effectiveRestrictionSetOnce.Do(func() { + t.effectiveRestrictionSet = NewInterfaceSet() for _, restriction := range t.Restrictions { - t.restrictionSet.Add(restriction) + t.effectiveRestrictionSet.Add(restriction) + + // Also add the interfaces to which this restricting interface conforms. + for _, conformance := range restriction.EffectiveInterfaceConformances() { + t.effectiveRestrictionSet.Add(conformance.InterfaceType) + } } }) } @@ -6424,8 +6420,8 @@ func (t *RestrictedType) Equal(other Type) bool { // Check that the set of restrictions are equal; order does not matter - restrictionSet := t.RestrictionSet() - otherRestrictionSet := otherRestrictedType.RestrictionSet() + restrictionSet := t.EffectiveRestrictionSet() + otherRestrictionSet := otherRestrictedType.EffectiveRestrictionSet() if restrictionSet.Len() != otherRestrictionSet.Len() { return false diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 41f402dfd4..135851486c 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -1059,7 +1059,7 @@ func TestCommonSuperType(t *testing.T) { Restrictions: []*InterfaceType{interfaceType2}, } // just initialize for equality - typ.initializeRestrictionSet() + typ.initializeEffectiveRestrictionSet() return typ }(), }, @@ -1075,7 +1075,7 @@ func TestCommonSuperType(t *testing.T) { Restrictions: []*InterfaceType{interfaceType1, interfaceType2}, } // just initialize for equality - typ.initializeRestrictionSet() + typ.initializeEffectiveRestrictionSet() return typ }(), }, @@ -1101,7 +1101,7 @@ func TestCommonSuperType(t *testing.T) { } // just initialize for equality - typ.initializeRestrictionSet() + typ.initializeEffectiveRestrictionSet() return typ }(), }, diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 191507259f..b302fae0ba 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -3862,7 +3862,7 @@ func TestCheckInheritedInterfacesSubtyping(t *testing.T) { t.Parallel() - t.Run("restricted type subtyping", func(t *testing.T) { + t.Run("restricted composite type subtyping", func(t *testing.T) { t.Parallel() @@ -3883,7 +3883,7 @@ func TestCheckInheritedInterfacesSubtyping(t *testing.T) { require.NoError(t, err) }) - t.Run("reference type subtyping", func(t *testing.T) { + t.Run("restricted anystruct type subtyping", func(t *testing.T) { t.Parallel() @@ -3895,6 +3895,46 @@ func TestCheckInheritedInterfacesSubtyping(t *testing.T) { struct S: B {} + fun foo(): {A} { + var s: {B} = S() + return s + } + `) + + require.NoError(t, err) + }) + + t.Run("composite type subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct S: B {} + + fun foo(): {A} { + var s = S() + return s + } + `) + + require.NoError(t, err) + }) + + t.Run("reference type subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct S: B {} + fun foo(): &{A} { var s = S() return &s as &S @@ -3988,4 +4028,91 @@ func TestCheckInheritedInterfacesSubtyping(t *testing.T) { require.NoError(t, err) }) + + t.Run("restricted anystruct reference subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct interface C {} + + struct S: B, C {} + + // Case I: &{B, C} is a subtype of &{B} + fun foo(): &{B} { + var s: S{B, C} = S() + return &s as &{B, C} + } + + // Case II: &{B} is a subtype of &{A} + fun bar(): &{A} { + var s: S{B} = S() + return &s as &{B} + } + `) + + require.NoError(t, err) + }) + + t.Run("restricted composite type reference subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct interface C {} + + struct S: B, C {} + + // Case I: &S{B, C} is a subtype of &S{B} + fun foo(): &S{B} { + var s: S{B, C} = S() + return &s as &S{B, C} + } + + // Case II: &S{B} is a subtype of &S{A} + fun bar(): &S{A} { + var s: S{B} = S() + return &s as &S{B} + } + `) + + require.NoError(t, err) + }) + + t.Run("multi-restricted composite type reference subtyping", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface A {} + + struct interface B: A {} + + struct interface C {} + + struct S: B, C {} + + // Case I: &S{B, C} is a subtype of &S{B} + fun foo(): &S{B} { + var s: S{B, C} = S() + return &s as &S{B, C} + } + + // Case II: &S{B, C} is also a subtype of &S{A} + fun bar(): &S{A} { + var s: S{B, C} = S() + return &s as &S{B, C} + } + `) + + require.NoError(t, err) + }) } From c51fe38fe5bb77ade20d0ab044e927e32f88efbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 3 May 2023 15:51:53 -0700 Subject: [PATCH 149/246] put capability controllers API behind a feature flag --- runtime/capabilitycontrollers_test.go | 1 + runtime/config.go | 2 + runtime/environment.go | 1 + runtime/sema/checker.go | 17 ++++++-- runtime/sema/config.go | 2 + runtime/tests/checker/account_test.go | 57 +++++++++++++++++++++------ 6 files changed, 66 insertions(+), 14 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index c1da0afbb7..22ff7b2ddf 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -42,6 +42,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { rt := newTestInterpreterRuntime() rt.defaultConfig.AccountLinkingEnabled = true + rt.defaultConfig.CapabilityControllersEnabled = true accountCodes := map[Location][]byte{} accountIDs := map[common.Address]uint64{} diff --git a/runtime/config.go b/runtime/config.go index 3b904f4887..2421605942 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -39,4 +39,6 @@ type Config struct { AccountLinkingEnabled bool // AttachmentsEnabled specifies if attachments are enabled AttachmentsEnabled bool + // CapabilityControllersEnabled specifies if capability controllers are enabled + CapabilityControllersEnabled bool } diff --git a/runtime/environment.go b/runtime/environment.go index da9e3cfdb2..1d10f7819e 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -163,6 +163,7 @@ func (e *interpreterEnvironment) newCheckerConfig() *sema.Config { CheckHandler: e.newCheckHandler(), AccountLinkingEnabled: e.config.AccountLinkingEnabled, AttachmentsEnabled: e.config.AttachmentsEnabled, + CapabilityControllersEnabled: e.config.CapabilityControllersEnabled, } } diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 6ed5cab3a6..8706de2f69 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2411,10 +2411,21 @@ func (checker *Checker) checkNativeModifier(isNative bool, position ast.HasPosit } func (checker *Checker) isAvailableMember(expressionType Type, identifier string) bool { - if expressionType == AuthAccountType && - identifier == AuthAccountTypeLinkAccountFunctionName { + switch expressionType { + case AuthAccountType: + switch identifier { + case AuthAccountTypeLinkAccountFunctionName: + return checker.Config.AccountLinkingEnabled + + case AuthAccountTypeCapabilitiesFieldName: + return checker.Config.CapabilityControllersEnabled + } - return checker.Config.AccountLinkingEnabled + case PublicAccountType: + switch identifier { + case PublicAccountTypeCapabilitiesFieldName: + return checker.Config.CapabilityControllersEnabled + } } return true diff --git a/runtime/sema/config.go b/runtime/sema/config.go index 9b68873a18..d651c0d884 100644 --- a/runtime/sema/config.go +++ b/runtime/sema/config.go @@ -57,4 +57,6 @@ type Config struct { AccountLinkingEnabled bool // AttachmentsEnabled determines if attachments are enabled AttachmentsEnabled bool + // CapabilityControllersEnabled determines if capability controllers are enabled + CapabilityControllersEnabled bool } diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index 7283fd455d..0f5b7571ac 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -30,7 +30,7 @@ import ( "github.com/onflow/cadence/runtime/stdlib" ) -func ParseAndCheckAccount(t *testing.T, code string) (*sema.Checker, error) { +func ParseAndCheckAccountWithConfig(t *testing.T, code string, config sema.Config) (*sema.Checker, error) { constantDeclaration := func(name string, ty sema.Type) stdlib.StandardLibraryValue { return stdlib.StandardLibraryValue{ @@ -39,20 +39,29 @@ func ParseAndCheckAccount(t *testing.T, code string) (*sema.Checker, error) { Kind: common.DeclarationKindConstant, } } - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + + baseValueActivation := config.BaseValueActivation + if baseValueActivation == nil { + baseValueActivation = sema.BaseValueActivation + } + + baseValueActivation = sema.NewVariableActivation(baseValueActivation) baseValueActivation.DeclareValue(constantDeclaration("authAccount", sema.AuthAccountType)) baseValueActivation.DeclareValue(constantDeclaration("publicAccount", sema.PublicAccountType)) + config.BaseValueActivation = baseValueActivation return ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, + Config: &config, }, ) } +func ParseAndCheckAccount(t *testing.T, code string) (*sema.Checker, error) { + return ParseAndCheckAccountWithConfig(t, code, sema.Config{}) +} + func TestCheckAccount_save(t *testing.T) { t.Parallel() @@ -2269,11 +2278,37 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - t.Run("AuthAccount.capabilities", func(t *testing.T) { + parseAndCheckAccountWithCapabilityControllers := func(t *testing.T, code string) (*sema.Checker, error) { + return ParseAndCheckAccountWithConfig( + t, + code, + sema.Config{ + CapabilityControllersEnabled: true, + }, + ) + } + + t.Run("AuthAccount.capabilities, disabled", func(t *testing.T) { t.Parallel() _, err := ParseAndCheckAccount(t, ` + fun test() { + authAccount.capabilities + } + `) + require.Error(t, err) + + errors := RequireCheckerErrors(t, err, 1) + require.IsType(t, &sema.NotDeclaredMemberError{}, errors[0]) + + }) + + t.Run("AuthAccount.capabilities, enabled", func(t *testing.T) { + + t.Parallel() + + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` fun test() { let capabilities: &AuthAccount.Capabilities = authAccount.capabilities @@ -2293,7 +2328,7 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - _, err := ParseAndCheckAccount(t, ` + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` fun test() { let capabilities: &AuthAccount.StorageCapabilities = authAccount.capabilities.storage @@ -2318,7 +2353,7 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - _, err := ParseAndCheckAccount(t, ` + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` fun test() { let capabilities: &AuthAccount.AccountCapabilities = authAccount.capabilities.account @@ -2340,7 +2375,7 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - _, err := ParseAndCheckAccount(t, ` + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` fun test() { let capabilities: &PublicAccount.Capabilities = publicAccount.capabilities @@ -2356,7 +2391,7 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - _, err := ParseAndCheckAccount(t, ` + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` let capabilitiesRef: &PublicAccount.StorageCapabilities = publicAccount.capabilities.storage `) require.Error(t, err) @@ -2369,7 +2404,7 @@ func TestCheckAccountCapabilities(t *testing.T) { t.Parallel() - _, err := ParseAndCheckAccount(t, ` + _, err := parseAndCheckAccountWithCapabilityControllers(t, ` let capabilitiesRef: &PublicAccount.AccountCapabilities = publicAccount.capabilities.account `) require.Error(t, err) From 6aa013343df88049c8e546246362439116f84a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 3 May 2023 17:38:56 -0700 Subject: [PATCH 150/246] return ID Capability for getCapability if it is publishd at given path --- runtime/capabilitycontrollers_test.go | 131 ++++++++++++++++++++------ runtime/interpreter/value_account.go | 20 ++++ runtime/runtime_test.go | 4 +- 3 files changed, 126 insertions(+), 29 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index 22ff7b2ddf..67b66f39a3 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -183,7 +183,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { testAccount := func(accountType sema.Type, accountExpression string) { testName := fmt.Sprintf( - "%s.Capabilities, storage capability", + "%s.Capabilities", accountType.String(), ) @@ -1125,6 +1125,81 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) }) } + + t.Run("issue, publish, getCapability, borrow", func(t *testing.T) { + t.Parallel() + + t.Run("AuthAccount.AccountCapabilities", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let publicPath = /public/acct + + // Arrange + let issuedCap: Capability<&AuthAccount> = + signer.capabilities.account.issue<&AuthAccount>() + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap = %s.getCapability<&AuthAccount>(publicPath) + let ref: &AuthAccount = gotCap!.borrow()! + + // Assert + assert(ref.address == 0x1) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + + t.Run("AuthAccount.StorageCapabilities", func(t *testing.T) { + t.Parallel() + + err, _, _ := test( + fmt.Sprintf( + // language=cadence + ` + import Test from 0x1 + + transaction { + prepare(signer: AuthAccount) { + let storagePath = /storage/r + let publicPath = /public/r + let resourceID = 42 + + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath) + + // Arrange + let issuedCap: Capability<&Test.R> = + signer.capabilities.storage.issue<&Test.R>(storagePath) + signer.capabilities.publish(issuedCap, at: publicPath) + + // Act + let gotCap = %s.getCapability<&Test.R>(publicPath) + let ref: &Test.R = gotCap!.borrow()! + + // Assert + assert(ref.id == resourceID) + } + } + `, + accountExpression, + ), + ) + require.NoError(t, err) + }) + }) }) } @@ -1736,7 +1811,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { let storagePath1 = /storage/r let storagePath2 = /storage/r2 - // Arrange + // Arrange let issuedCap1: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath1) let controller1: &StorageCapabilityController? = @@ -1839,10 +1914,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath1 = /storage/r let storagePath2 = /storage/empty - let resourceID = 42 + let resourceID = 42 - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath1) + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath1) let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath1) @@ -1857,7 +1932,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { controller!.retarget(storagePath2) // Assert - assert(issuedCap.borrow() == nil) + assert(issuedCap.borrow() == nil) assert(!issuedCap.check()) } } @@ -1878,12 +1953,12 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath1 = /storage/r let storagePath2 = /storage/r2 - let resourceID1 = 42 - let resourceID2 = 43 + let resourceID1 = 42 + let resourceID2 = 43 - // Arrange - Test.createAndSaveR(id: resourceID1, storagePath: storagePath1) - Test.createAndSaveR(id: resourceID2, storagePath: storagePath2) + // Arrange + Test.createAndSaveR(id: resourceID1, storagePath: storagePath1) + Test.createAndSaveR(id: resourceID2, storagePath: storagePath2) let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath1) @@ -1920,11 +1995,11 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath1 = /storage/r let storagePath2 = /storage/s - let resourceID = 42 + let resourceID = 42 - // Arrange - Test.createAndSaveR(id: resourceID, storagePath: storagePath1) - Test.createAndSaveS(storagePath: storagePath2) + // Arrange + Test.createAndSaveR(id: resourceID, storagePath: storagePath1) + Test.createAndSaveS(storagePath: storagePath2) let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath1) @@ -1939,7 +2014,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) { controller!.retarget(storagePath2) // Assert - assert(issuedCap.borrow() == nil) + assert(issuedCap.borrow() == nil) assert(!issuedCap.check()) } } @@ -1965,10 +2040,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath = /storage/r - // Arrange + // Arrange let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath) - let controller: &StorageCapabilityController? = + let controller: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) let controllersBefore = signer.capabilities.storage.getControllers(forPath: storagePath) @@ -2004,10 +2079,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath = /storage/r - // Arrange + // Arrange let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath) - let controller: &StorageCapabilityController? = + let controller: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) // Act @@ -2034,10 +2109,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath = /storage/r - // Arrange + // Arrange let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath) - let controller: &StorageCapabilityController? = + let controller: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) // Act @@ -2064,10 +2139,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { prepare(signer: AuthAccount) { let storagePath = /storage/r - // Arrange + // Arrange let issuedCap: Capability<&Test.R> = signer.capabilities.storage.issue<&Test.R>(storagePath) - let controller: &StorageCapabilityController? = + let controller: &StorageCapabilityController? = signer.capabilities.storage.getController(byCapabilityID: issuedCap.id) // Act @@ -2102,10 +2177,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { transaction { prepare(signer: AuthAccount) { - // Arrange + // Arrange let issuedCap: Capability<&AuthAccount> = signer.capabilities.account.issue<&AuthAccount>() - let controller: &AccountCapabilityController? = + let controller: &AccountCapabilityController? = signer.capabilities.account.getController(byCapabilityID: issuedCap.id) let controllersBefore = signer.capabilities.account.getControllers() @@ -2139,10 +2214,10 @@ func TestRuntimeCapabilityControllers(t *testing.T) { transaction { prepare(signer: AuthAccount) { - // Arrange + // Arrange let issuedCap: Capability<&AuthAccount> = signer.capabilities.account.issue<&AuthAccount>() - let controller: &AccountCapabilityController? = + let controller: &AccountCapabilityController? = signer.capabilities.account.getController(byCapabilityID: issuedCap.id) // Act diff --git a/runtime/interpreter/value_account.go b/runtime/interpreter/value_account.go index 20e255e065..5448a966fc 100644 --- a/runtime/interpreter/value_account.go +++ b/runtime/interpreter/value_account.go @@ -386,6 +386,8 @@ func accountGetCapabilityFunction( funcType *sema.FunctionType, ) *HostFunctionValue { + address := addressValue.ToAddress() + return NewHostFunctionValue( gauge, funcType, @@ -425,6 +427,24 @@ func accountGetCapabilityFunction( borrowStaticType = ConvertSemaToStaticType(interpreter, borrowType) } + // Read stored capability, if any + + domain := path.Domain.Identifier() + identifier := path.Identifier + + storageMapKey := StringStorageMapKey(identifier) + + readValue := interpreter.ReadStored(address, domain, storageMapKey) + if capabilityValue, ok := readValue.(*IDCapabilityValue); ok { + // TODO: only if interpreter.IsSubType(capabilityValue.BorrowType, borrowStaticType) ? + return NewIDCapabilityValue( + gauge, + capabilityValue.ID, + addressValue, + borrowStaticType, + ) + } + return NewPathCapabilityValue( gauge, addressValue, diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 2b38f9c66c..56571c20aa 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -7066,7 +7066,9 @@ func TestRuntimeGetCapability(t *testing.T) { } `) - runtimeInterface := &testRuntimeInterface{} + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + } res, err := runtime.ExecuteScript( Script{ From 6cc2b6d1f64e177bfeadac0a76bba8c9c8efd93b Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Thu, 4 May 2023 12:45:53 +0300 Subject: [PATCH 151/246] Add integration tests (in Cadence) for Crypto contract and generate code coverage --- runtime/stdlib/contracts/crypto_test.cdc | 84 +++++++++++++++++++ runtime/stdlib/contracts/flow.json | 24 ++++++ .../scripts/crypto_get_key_from_list.cdc | 21 +++++ .../stdlib/contracts/scripts/crypto_hash.cdc | 6 ++ .../scripts/crypto_hash_with_tag.cdc | 10 +++ .../contracts/scripts/crypto_key_list_add.cdc | 18 ++++ .../scripts/crypto_key_list_verify.cdc | 51 +++++++++++ ...to_key_list_verify_duplicate_signature.cdc | 39 +++++++++ ...o_key_list_verify_insufficient_weights.cdc | 51 +++++++++++ ...ypto_key_list_verify_invalid_signature.cdc | 34 ++++++++ ...ypto_key_list_verify_missing_signature.cdc | 34 ++++++++ .../crypto_key_list_verify_revoked.cdc | 36 ++++++++ .../scripts/crypto_revoke_key_from_list.cdc | 24 ++++++ 13 files changed, 432 insertions(+) create mode 100644 runtime/stdlib/contracts/crypto_test.cdc create mode 100644 runtime/stdlib/contracts/flow.json create mode 100644 runtime/stdlib/contracts/scripts/crypto_get_key_from_list.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_hash.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_hash_with_tag.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_add.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify_duplicate_signature.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify_insufficient_weights.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify_invalid_signature.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify_missing_signature.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_key_list_verify_revoked.cdc create mode 100644 runtime/stdlib/contracts/scripts/crypto_revoke_key_from_list.cdc diff --git a/runtime/stdlib/contracts/crypto_test.cdc b/runtime/stdlib/contracts/crypto_test.cdc new file mode 100644 index 0000000000..7c2dcfd9da --- /dev/null +++ b/runtime/stdlib/contracts/crypto_test.cdc @@ -0,0 +1,84 @@ +import Test + +pub var blockchain = Test.newEmulatorBlockchain() +pub var account = blockchain.createAccount() + +pub fun setup() { + blockchain.useConfiguration(Test.Configuration({ + "Crypto": account.address + })) + + var crypto = Test.readFile("crypto.cdc") + var err = blockchain.deployContract( + name: "Crypto", + code: crypto, + account: account, + arguments: [] + ) + + Test.assert(err == nil) +} + +pub fun testCryptoHash() { + let returnedValue = executeScript("./scripts/crypto_hash.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testCryptoHashWithTag() { + let returnedValue = executeScript("./scripts/crypto_hash_with_tag.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testAddKeyToKeyList() { + let returnedValue = executeScript("./scripts/crypto_key_list_add.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testGetKeyFromList() { + let returnedValue = executeScript("./scripts/crypto_get_key_from_list.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testRevokeKeyFromList() { + let returnedValue = executeScript("./scripts/crypto_revoke_key_from_list.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerify() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerifyInsufficientWeights() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify_insufficient_weights.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerifyWithRevokedKey() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify_revoked.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerifyWithMissingSignature() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify_missing_signature.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerifyDuplicateSignature() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify_duplicate_signature.cdc") + Test.assert(returnedValue, message: "found: false") +} + +pub fun testKeyListVerifyInvalidSignature() { + let returnedValue = executeScript("./scripts/crypto_key_list_verify_invalid_signature.cdc") + Test.assert(returnedValue, message: "found: false") +} + +priv fun executeScript(_ scriptPath: String): Bool { + var script = Test.readFile(scriptPath) + let value = blockchain.executeScript(script, []) + + Test.assert(value.status == Test.ResultStatus.succeeded) + + return value.returnValue! as! Bool +} diff --git a/runtime/stdlib/contracts/flow.json b/runtime/stdlib/contracts/flow.json new file mode 100644 index 0000000000..5ac314e31f --- /dev/null +++ b/runtime/stdlib/contracts/flow.json @@ -0,0 +1,24 @@ +{ + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "sandboxnet": "access.sandboxnet.nodes.onflow.org:9000", + "testnet": "access.devnet.nodes.onflow.org:9000" + }, + "contracts": { + "Crypto": "crypto.cdc" + }, + "deployments": { + "emulator": { + "emulator-account": [ + "Crypto" + ] + } + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "key": "b775d6da04a0b4819903d89a25395bfd90eb8559182255690c7c141edaeae923" + } + } +} \ No newline at end of file diff --git a/runtime/stdlib/contracts/scripts/crypto_get_key_from_list.cdc b/runtime/stdlib/contracts/scripts/crypto_get_key_from_list.cdc new file mode 100644 index 0000000000..cc2c3ef18a --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_get_key_from_list.cdc @@ -0,0 +1,21 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 1.0 + ) + + assert(keyList.get(keyIndex: 0) != nil) + assert(keyList.get(keyIndex: 2) == nil) + + return true +} diff --git a/runtime/stdlib/contracts/scripts/crypto_hash.cdc b/runtime/stdlib/contracts/scripts/crypto_hash.cdc new file mode 100644 index 0000000000..3848a29f2f --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_hash.cdc @@ -0,0 +1,6 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let hash = Crypto.hash([1, 2, 3], algorithm: HashAlgorithm.SHA3_256) + return hash.length == 32 +} diff --git a/runtime/stdlib/contracts/scripts/crypto_hash_with_tag.cdc b/runtime/stdlib/contracts/scripts/crypto_hash_with_tag.cdc new file mode 100644 index 0000000000..b38712efb5 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_hash_with_tag.cdc @@ -0,0 +1,10 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let hash = Crypto.hashWithTag( + [1, 2, 3], + tag: "v0.1.tag", + algorithm: HashAlgorithm.SHA3_256 + ) + return hash.length == 32 +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_add.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_add.cdc new file mode 100644 index 0000000000..31319ab87e --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_add.cdc @@ -0,0 +1,18 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 1.0 + ) + + return keyList.get(keyIndex: 0) != nil +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify.cdc new file mode 100644 index 0000000000..8f91f993ae --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify.cdc @@ -0,0 +1,51 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKeyA = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + + keyList.add( + publicKeyA, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let publicKeyB = PublicKey( + publicKey: + "df9609ee588dd4a6f7789df8d56f03f545d4516f0c99b200d73b9a3afafc14de5d21a4fc7a2a2015719dc95c9e756cfa44f2a445151aaf42479e7120d83df956".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + + keyList.add( + publicKeyB, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ), + Crypto.KeyListSignature( + keyIndex: 1, + signature: + "bbdc5591c3f937a730d4f6c0a6fde61a0a6ceaa531ccb367c3559335ab9734f4f2b9da8adbe371f1f7da913b5a3fdd96a871e04f078928ca89a83d841c72fadf".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + let isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + return isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify_duplicate_signature.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_duplicate_signature.cdc new file mode 100644 index 0000000000..ea42acde84 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_duplicate_signature.cdc @@ -0,0 +1,39 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ), + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + var isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + + return !isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify_insufficient_weights.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_insufficient_weights.cdc new file mode 100644 index 0000000000..43beb5ea19 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_insufficient_weights.cdc @@ -0,0 +1,51 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKeyA = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + + keyList.add( + publicKeyA, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.4 + ) + + let publicKeyB = PublicKey( + publicKey: + "df9609ee588dd4a6f7789df8d56f03f545d4516f0c99b200d73b9a3afafc14de5d21a4fc7a2a2015719dc95c9e756cfa44f2a445151aaf42479e7120d83df956".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + + keyList.add( + publicKeyB, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ), + Crypto.KeyListSignature( + keyIndex: 1, + signature: + "bbdc5591c3f937a730d4f6c0a6fde61a0a6ceaa531ccb367c3559335ab9734f4f2b9da8adbe371f1f7da913b5a3fdd96a871e04f078928ca89a83d841c72fadf".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + let isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + return !isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify_invalid_signature.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_invalid_signature.cdc new file mode 100644 index 0000000000..0e55f644e9 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_invalid_signature.cdc @@ -0,0 +1,34 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "db70a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + var isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + + return !isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify_missing_signature.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_missing_signature.cdc new file mode 100644 index 0000000000..5a57840443 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_missing_signature.cdc @@ -0,0 +1,34 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 1, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + var isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + + return !isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_key_list_verify_revoked.cdc b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_revoked.cdc new file mode 100644 index 0000000000..43d6dba9c4 --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_key_list_verify_revoked.cdc @@ -0,0 +1,36 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + let signatureSet = [ + Crypto.KeyListSignature( + keyIndex: 0, + signature: + "8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex() + ) + ] + + // "foo", encoded as UTF-8, in hex representation + let signedData = "666f6f".decodeHex() + + keyList.revoke(keyIndex: 0) + + var isValid = keyList.verify( + signatureSet: signatureSet, + signedData: signedData + ) + + return !isValid +} diff --git a/runtime/stdlib/contracts/scripts/crypto_revoke_key_from_list.cdc b/runtime/stdlib/contracts/scripts/crypto_revoke_key_from_list.cdc new file mode 100644 index 0000000000..ee8b6d875b --- /dev/null +++ b/runtime/stdlib/contracts/scripts/crypto_revoke_key_from_list.cdc @@ -0,0 +1,24 @@ +import Crypto from "Crypto" + +pub fun main(): Bool { + let keyList = Crypto.KeyList() + + let publicKey = PublicKey( + publicKey: + "db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ) + keyList.add( + publicKey, + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: 0.5 + ) + + keyList.revoke(keyIndex: 0) + keyList.revoke(keyIndex: 2) + + assert(keyList.get(keyIndex: 0)!.isRevoked) + assert(keyList.get(keyIndex: 2) == nil) + + return true +} From abddf429768c1b8201e2ed11d685df3e3492ecc0 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 4 May 2023 12:47:06 -0400 Subject: [PATCH 152/246] don't truncate multiline nested errors early --- runtime/error_test.go | 60 +++++++++++++++++++++++++++++++++++++++++ runtime/pretty/print.go | 3 +++ 2 files changed, 63 insertions(+) diff --git a/runtime/error_test.go b/runtime/error_test.go index 02998ec546..15861c898f 100644 --- a/runtime/error_test.go +++ b/runtime/error_test.go @@ -178,6 +178,66 @@ func TestRuntimeError(t *testing.T) { ) }) + t.Run("execution multiline nested error", func(t *testing.T) { + + t.Parallel() + + runtime := newTestInterpreterRuntime() + + script := []byte(` + pub resource R { + init(s:String){ + panic("42") + } + } + + pub fun createR(): @R{ + return <- create R( + s: "argument" + ) + } + + pub fun main() { + destroy createR() + } + `) + + runtimeInterface := &testRuntimeInterface{} + + location := common.ScriptLocation{0x1} + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: location, + }, + ) + + require.EqualError(t, err, + "Execution failed:\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:15:12\n"+ + " |\n"+ + "15 | destroy createR()\n"+ + " | ^^^^^^^^^\n"+ + "\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:9:21\n"+ + " |\n"+ + " 9 | return <- create R(\n"+ + "10 | s: \"argument\"\n"+ + "11 | )\n"+ + " | ^\n"+ + "\n"+ + "error: panic: 42\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:4:5\n"+ + " |\n"+ + "4 | panic(\"42\")\n"+ + " | ^^^^^^^^^^^\n", + ) + }) + t.Run("parse error in import", func(t *testing.T) { t.Parallel() diff --git a/runtime/pretty/print.go b/runtime/pretty/print.go index 33fc731ee5..5585948bd3 100644 --- a/runtime/pretty/print.go +++ b/runtime/pretty/print.go @@ -337,6 +337,9 @@ func (p ErrorPrettyPrinter) writeCodeExcerpts( columns := 1 if excerpt.endPos != nil { endColumn := excerpt.endPos.Column + if excerpt.startPos.Column > endColumn { + endColumn = excerpt.startPos.Column + } if endColumn >= maxLineLength { endColumn = maxLineLength - 1 } From 28538276de8462ef7745218ac454b8d33a02fc45 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 4 May 2023 12:52:00 -0400 Subject: [PATCH 153/246] highlight entire line --- runtime/error_test.go | 2 +- runtime/pretty/print.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/error_test.go b/runtime/error_test.go index 15861c898f..5066700ed7 100644 --- a/runtime/error_test.go +++ b/runtime/error_test.go @@ -228,7 +228,7 @@ func TestRuntimeError(t *testing.T) { " 9 | return <- create R(\n"+ "10 | s: \"argument\"\n"+ "11 | )\n"+ - " | ^\n"+ + " | ^^^^^^^^^^^^^^^^^^\n"+ "\n"+ "error: panic: 42\n"+ " --> 0100000000000000000000000000000000000000000000000000000000000000:4:5\n"+ diff --git a/runtime/pretty/print.go b/runtime/pretty/print.go index 5585948bd3..9175d22314 100644 --- a/runtime/pretty/print.go +++ b/runtime/pretty/print.go @@ -343,7 +343,11 @@ func (p ErrorPrettyPrinter) writeCodeExcerpts( if endColumn >= maxLineLength { endColumn = maxLineLength - 1 } - columns = endColumn - excerpt.startPos.Column + 1 + startColumn := excerpt.endPos.Column + if excerpt.startPos.Column < startColumn { + startColumn = excerpt.startPos.Column + } + columns = endColumn - startColumn + 1 } indicator := "-" From e92f83c439eb57b55f868e4d6a9a8c5c8e0335e8 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 4 May 2023 14:15:29 -0400 Subject: [PATCH 154/246] fix indicator misalignment --- runtime/error_test.go | 66 +++++++++++++++++++++++++++++++++++++++-- runtime/pretty/print.go | 10 +++---- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/runtime/error_test.go b/runtime/error_test.go index 5066700ed7..31277fea72 100644 --- a/runtime/error_test.go +++ b/runtime/error_test.go @@ -184,6 +184,66 @@ func TestRuntimeError(t *testing.T) { runtime := newTestInterpreterRuntime() + script := []byte(` + pub resource Resource { + init(s:String){ + panic("42") + } + } + + pub fun createResource(): @Resource{ + return <- create Resource( + s: "argument" + ) + } + + pub fun main() { + destroy createResource() + } + `) + + runtimeInterface := &testRuntimeInterface{} + + location := common.ScriptLocation{0x1} + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: location, + }, + ) + + require.EqualError(t, err, + "Execution failed:\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:15:12\n"+ + " |\n"+ + "15 | destroy createResource()\n"+ + " | ^^^^^^^^^^^^^^^^\n"+ + "\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:9:21\n"+ + " |\n"+ + " 9 | return <- create Resource(\n"+ + "10 | s: \"argument\"\n"+ + "11 | )\n"+ + " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"+ + "\n"+ + "error: panic: 42\n"+ + " --> 0100000000000000000000000000000000000000000000000000000000000000:4:5\n"+ + " |\n"+ + "4 | panic(\"42\")\n"+ + " | ^^^^^^^^^^^\n", + ) + }) + + t.Run("execution multiline nested error endline longer", func(t *testing.T) { + + t.Parallel() + + runtime := newTestInterpreterRuntime() + script := []byte(` pub resource R { init(s:String){ @@ -194,7 +254,7 @@ func TestRuntimeError(t *testing.T) { pub fun createR(): @R{ return <- create R( s: "argument" - ) + /*comment here to fill line */) } pub fun main() { @@ -227,8 +287,8 @@ func TestRuntimeError(t *testing.T) { " |\n"+ " 9 | return <- create R(\n"+ "10 | s: \"argument\"\n"+ - "11 | )\n"+ - " | ^^^^^^^^^^^^^^^^^^\n"+ + "11 | /*comment here to fill line */)\n"+ + " | ^^^^^^^^^^^^^^\n"+ "\n"+ "error: panic: 42\n"+ " --> 0100000000000000000000000000000000000000000000000000000000000000:4:5\n"+ diff --git a/runtime/pretty/print.go b/runtime/pretty/print.go index 9175d22314..87bbdde016 100644 --- a/runtime/pretty/print.go +++ b/runtime/pretty/print.go @@ -337,16 +337,14 @@ func (p ErrorPrettyPrinter) writeCodeExcerpts( columns := 1 if excerpt.endPos != nil { endColumn := excerpt.endPos.Column - if excerpt.startPos.Column > endColumn { - endColumn = excerpt.startPos.Column + startColumn := excerpt.startPos.Column + if excerpt.endPos.Column < startColumn { + startColumn = excerpt.endPos.Column + endColumn = len(lines[excerpt.startPos.Line-1]) } if endColumn >= maxLineLength { endColumn = maxLineLength - 1 } - startColumn := excerpt.endPos.Column - if excerpt.startPos.Column < startColumn { - startColumn = excerpt.startPos.Column - } columns = endColumn - startColumn + 1 } From f787a122e4c4f6080aef890122c8b53cc773e346 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 4 May 2023 14:15:46 -0400 Subject: [PATCH 155/246] remove extra test --- runtime/error_test.go | 60 ------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/runtime/error_test.go b/runtime/error_test.go index 31277fea72..92690ff572 100644 --- a/runtime/error_test.go +++ b/runtime/error_test.go @@ -238,66 +238,6 @@ func TestRuntimeError(t *testing.T) { ) }) - t.Run("execution multiline nested error endline longer", func(t *testing.T) { - - t.Parallel() - - runtime := newTestInterpreterRuntime() - - script := []byte(` - pub resource R { - init(s:String){ - panic("42") - } - } - - pub fun createR(): @R{ - return <- create R( - s: "argument" - /*comment here to fill line */) - } - - pub fun main() { - destroy createR() - } - `) - - runtimeInterface := &testRuntimeInterface{} - - location := common.ScriptLocation{0x1} - - _, err := runtime.ExecuteScript( - Script{ - Source: script, - }, - Context{ - Interface: runtimeInterface, - Location: location, - }, - ) - - require.EqualError(t, err, - "Execution failed:\n"+ - " --> 0100000000000000000000000000000000000000000000000000000000000000:15:12\n"+ - " |\n"+ - "15 | destroy createR()\n"+ - " | ^^^^^^^^^\n"+ - "\n"+ - " --> 0100000000000000000000000000000000000000000000000000000000000000:9:21\n"+ - " |\n"+ - " 9 | return <- create R(\n"+ - "10 | s: \"argument\"\n"+ - "11 | /*comment here to fill line */)\n"+ - " | ^^^^^^^^^^^^^^\n"+ - "\n"+ - "error: panic: 42\n"+ - " --> 0100000000000000000000000000000000000000000000000000000000000000:4:5\n"+ - " |\n"+ - "4 | panic(\"42\")\n"+ - " | ^^^^^^^^^^^\n", - ) - }) - t.Run("parse error in import", func(t *testing.T) { t.Parallel() From 7d515e31f6e5bb2a45ec21a06156fd5afa981889 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 4 May 2023 16:40:56 -0400 Subject: [PATCH 156/246] replace docs with links --- docs/FAQ.md | 26 +- docs/README.md | 13 +- docs/anti-patterns.mdx | 381 +--- docs/contract-upgrades.mdx | 30 +- docs/design-patterns.mdx | 491 +---- docs/development.md | 116 +- docs/goland.md | 16 +- docs/index.mdx | 189 +- docs/json-cadence-spec.md | 869 +-------- docs/language/access-control.md | 223 +-- docs/language/accounts.mdx | 1012 +--------- docs/language/attachments.md | 221 +-- docs/language/built-in-functions.mdx | 90 +- .../capability-based-access-control.md | 257 +-- docs/language/composite-types.mdx | 508 +---- docs/language/constants-and-variables.md | 102 +- docs/language/contract-updatability.md | 401 +--- docs/language/contracts.mdx | 496 +---- docs/language/control-flow.md | 423 +---- docs/language/core-events.md | 184 +- docs/language/crypto.mdx | 430 +---- docs/language/enumerations.md | 63 +- docs/language/environment-information.md | 68 +- docs/language/events.md | 72 +- docs/language/functions.mdx | 508 +---- docs/language/glossary.md | 293 +-- docs/language/imports.mdx | 29 +- docs/language/index.md | 49 +- docs/language/interfaces.mdx | 547 +----- docs/language/operators.md | 902 +-------- docs/language/references.md | 139 +- docs/language/resources.mdx | 609 +----- docs/language/restricted-types.md | 162 +- docs/language/run-time-types.md | 252 +-- docs/language/scope.md | 81 +- docs/language/syntax.md | 127 +- docs/language/transactions.md | 226 +-- docs/language/type-annotations.md | 55 +- docs/language/type-hierarchy.md | 6 +- docs/language/type-inference.md | 146 +- docs/language/type-safety.md | 65 +- docs/language/values-and-types.mdx | 1627 +---------------- docs/measuring-time.mdx | 97 +- docs/releasing.md | 153 +- docs/security-best-practices.mdx | 64 +- docs/solidity-to-cadence.mdx | 496 +---- docs/subtyping.md | 216 +-- docs/syntax-highlighting.md | 22 +- docs/testing-framework.mdx | 532 +----- docs/tutorial/01-first-steps.mdx | 80 +- docs/tutorial/02-hello-world.mdx | 310 +--- docs/tutorial/03-resources.mdx | 517 +----- docs/tutorial/04-capabilities.mdx | 434 +---- docs/tutorial/05-non-fungible-tokens-1.mdx | 354 +--- docs/tutorial/05-non-fungible-tokens-2.mdx | 681 +------ docs/tutorial/06-fungible-tokens.mdx | 1116 +---------- docs/tutorial/07-marketplace-setup.mdx | 253 +-- docs/tutorial/08-marketplace-compose.mdx | 856 +-------- docs/tutorial/09-voting.mdx | 421 +---- docs/tutorial/10-resources-compose.mdx | 277 +-- docs/why.md | 17 +- 61 files changed, 122 insertions(+), 19278 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index b643c8f613..381e841d55 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -1,25 +1,3 @@ -# FAQ +# This document has been moved to a new location: -## Is there a formal grammar (e.g. in BNF) for Cadence? - -Yes, there is a [EBNF for Cadence](https://github.com/onflow/cadence/blob/master/docs/cadence.ebnf). - -## How can I inject additional values when executing a transaction or script? - -The runtime `Interface` functions `ExecuteTransaction` and `ExecuteScript` require a `Context` argument. -The context has an `Environment` field, in which `stdlib.StandardLibraryValue`s can be declared. - -## How is Cadence parsed? - -Cadence's parser is implemented as a hand-written recursive descent parser which uses operator precedence parsing. -The recursive decent parsing technique allows for greater control, e.g. when implementing whitespace sensitivity, ambiguities, etc. -The handwritten parser also allows for better / great custom error reporting and recovery. - -The operator precedence parsing technique avoids constructing a CST and the associated overhead, where each grammar rule is translated to a CST node. -For example, a simple integer literal would be "boxed" in several outer grammar rule nodes. - -## What is the algorithmic efficiency of operations on arrays and dictionaries? - -Arrays and dictionaries are implemented [as trees](https://github.com/onflow/atree). -This means that lookup operations do not run in constant time. -In certain cases, a mutation operation may cause a rebalancing of the tree. +https://github.com/onflow/docs/tree/main/docs/cadence/FAQ.md diff --git a/docs/README.md b/docs/README.md index e84c3a5e9e..1dd3422d76 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,3 @@ -# Documentation +# This document has been moved to a new location: -This directory contains the documentation for Cadence. - -The [`language` directory](https://github.com/onflow/cadence/tree/master/docs/language) contains the Cadence Language Reference. -The contents of this directory are deployed to the [Flow Documentation website](https://docs.onflow.org). -This is done through the `docs` directory in the [`onflow/flow` repository](https://github.com/onflow/flow), -which pulls in the content when it is built. - -The remaining files in this directory can be considered developer/contributor documentation. - -Documentation is written in Markdown or [MDX](https://mdxjs.com/), an extension of Markdown. +https://github.com/onflow/docs/tree/main/docs/cadence/README.md diff --git a/docs/anti-patterns.mdx b/docs/anti-patterns.mdx index e38c1ed8ad..4afe64abb7 100644 --- a/docs/anti-patterns.mdx +++ b/docs/anti-patterns.mdx @@ -1,380 +1,3 @@ ---- -title: Cadence Anti-Patterns ---- +# This document has been moved to a new location: -This is an opinionated list of issues that can be improved if they are found in Cadence code intended for production. - -# Security and Robustness - -## Avoid using `AuthAccount` as a function parameter - -### Problem - -Some may choose to authenticate or perform operations for their users by using the users' account addresses. -In order to do this, a commonly seen case would be to pass the user's `AuthAccount` object -as a parameter to a contract function to use for querying the account or storing objects directly. -This is problematic, as the `AuthAccount` object allows access to ALL private areas of the account, -for example, all of the user's storage, authorized keys, etc., -which provides the opportunity for bad actors to take advantage of. - -### Example: - -```cadence -... -// BAD CODE -// DO NOT COPY - -// Imagine this code is in a contract that uses AuthAccount to authenticate users -// To transfer NFTs - -// They could deploy the contract with an Ethereum-style access control list functionality - -pub fun transferNFT(id: UInt64, owner: AuthAccount) { - assert(owner(id) == owner.address) - - transfer(id) -} - -// But they could upgrade the function to have the same signature -// so it looks like it is doing the same thing, but they could also drain a little bit -// of FLOW from the user's vault, a totally separate piece of the account that -// should not be accessible in this function -// BAD - -pub fun transferNFT(id: UInt64, owner: AuthAccount) { - assert(owner(id) == owner.address) - - transfer(id) - - // Sneakily borrow a reference to the user's Flow Token Vault - // and withdraw a bit of FLOW - // BAD - let vaultRef = owner.borrow<&FlowToken.Vault>(/storage/flowTokenVault)! - let stolenTokens <- vaultRef.withdraw(amount: 0.1) - - // deposit the stolen funds in the contract owners vault - // BAD - contractVault.deposit(from: <-stolenTokens) -} -... -``` - -### Solution - -Projects should find other ways to authenticate users, such as using resources and capabilities as authentication objects. -They should also expect to perform most storage and linking operations within transaction bodies -rather than inside contract utility functions. - -There are some scenarios where using an `AuthAccount` object is necessary, such as a cold storage multi-sig, -but those cases are extremely rare and `AuthAccount` usage should still be avoided unless absolutely necessary. - -## Auth references and capabilities should be avoided - -### Problem - -[Authorized references](language/references) allow downcasting restricted -types to their unrestricted type and should be avoided unless necessary. -The type that is being restricted could expose functionality that was not intended to be exposed. -If the `auth` keyword is used on local variables they will be references. -References are ephemeral and cannot be stored. -This prevents any reference casting to be stored under account storage. -Additionally, if the `auth` keyword is used to store a public capability, serious harm -could happen since the value could be downcasted to a type -that has functionality and values altered. - -### Example - -A commonly seen pattern in NFT smart contracts is including a public borrow function -that borrows an auth reference to an NFT (eg. [NBA Top Shot](https://github.com/dapperlabs/nba-smart-contracts/blob/95fe72b7e94f43c9eff28412ce3642b69dcd8cd5/contracts/TopShot.cdc#L889-L906)). -This allows anyone to access the stored metadata or extra fields that weren't part -of the NFT standard. While generally safe in most scenarios, not all NFTs are built the same. -Some NFTs may have privileged functions that shouldn't be exposed by this method, -so please be cautious and mindful when imitating NFT projects that use this pattern. - -### Another Example - -When we create a public capability for our `FungibleToken.Vault` we do not use an auth capability: - -```cadence -// GOOD: Create a public capability to the Vault that only exposes -// the balance field through the Balance interface -signer.link<&FlowToken.Vault{FungibleToken.Balance}>( - /public/flowTokenBalance, - target: /storage/flowTokenVault -) -``` - -If we were to use an authorized type for the capability, like so: - -```cadence -// BAD: Create an Authorized public capability to the Vault that only exposes -// the balance field through the Balance interface -// Authorized referenced can be downcasted to their unrestricted types, which is dangerous -signer.link( - /public/flowTokenBalance, - target: /storage/flowTokenVault -) -``` - -Then anyone in the network could take that restricted reference -that is only supposed to expose the balance field and downcast it to expose the withdraw field -and steal all our money! - -```cadence -// Exploit of the auth capability to expose withdraw -let balanceRef = getAccount(account) - .getCapability(/public/flowTokenBalance) - .borrow()! - -let fullVaultRef = balanceRef as! &FlowToken.Vault - -// Withdraw the newly exposed funds -let stolenFunds <- fullVaultRef.withdraw(amount: 1000000) -``` - -## Events from resources may not be unique - -### Problem - -Public functions in a contract can be called by anyone, e.g. any other contract or any transaction. -If that function creates a resource, and that resource has functions that emit events, -that means any account can create an instance of that resource and emit those events. -If those events are meant to indicate actions taken using a single instance of that resource -(eg. admin object, registry), or instances created through a particular workflow, -it's possible that events from other instances may be mixed in with the ones you're querying for - -making the event log search and management more cumbersome. - -### Solution - -To fix this, if there should be only a single instance of the resource, -it should be created and `link()`ed to a public path in an admin account's storage -during the contracts's initializer. - -# Access Control - -## Public functions and fields should be avoided - -### Problem - -Be sure to keep track of access modifiers when structuring your code, and make public only what should be public. -Accidentally exposed fields can be a security hole. - -### Solution - -When writing your smart contract, look at every field and function and make sure -that they are all `access(self)`, `access(contract)`, or `access(account)`, unless otherwise needed. - -## Capability-Typed public fields are a security hole - -This is a specific case of "Public Functions And Fields Should Be Avoided", above. - -### Problem - -The values of public fields can be copied. Capabilities are value types, -so if they are used as a public field, anyone can copy it from the field -and call the functions that it exposes. -This almost certainly is not what you want if a capability -has been stored as a field on a contract or resource in this way. - -### Solution - -For public access to a capability, place it in an accounts public area so this expectation is explicit. - -## Array or dictionary fields should be private - - - -This anti-pattern has been addressed with [FLIP #703](https://github.com/onflow/flips/blob/main/flips/20211129-cadence-mutability-restrictions.md) - - - -### Problem - -This is a specific case of "Public Functions And Fields Should Be Avoided", above. -Public array or dictionary fields are not directly over-writable, -but their members can be accessed and overwritten if the field is public. -This could potentially result in security vulnerabilities for the contract -if these fields are mistakenly made public. - -Ex: - -```cadence -pub contract Array { - // array is intended to be initialized to something constant - pub let shouldBeConstantArray: [Int] -} -``` - -Anyone could use a transaction like this to modify it: - -```cadence -import Array from 0x01 - -transaction { - execute { - Array.shouldbeConstantArray[0] = 1000 - } -} -``` - -### Solution - -Make sure that any array or dictionary fields in contracts, structs, or resources -are `access(contract)` or `access(self)` unless they need to be intentionally made public. - -```cadence -pub contract Array { - // array is inteded to be initialized to something constant - access(self) let shouldBeConstantArray: [Int] -} -``` - -## Public admin resource creation functions are unsafe - -This is a specific case of "Public Functions And Fields Should Be Avoided", above. - -### Problem - -A public function on a contract that creates a resource can be called by any account. -If that resource provides access to admin functions then the creation function should not be public. - -### Solution - -To fix this, a single instance of that resource should be created in the contract's `init()` method, -and then a new creation function can be potentially included within the admin resource, if necessary. -The admin resource can then be `link()`ed to a private path in an admin's account storage during the contract's initializer. - -### Example - -```cadence -// Pseudo-code - -// BAD -pub contract Currency { - pub resource Admin { - pub fun mintTokens() - } - - // Anyone in the network can call this function - // And use the Admin resource to mint tokens - pub fun createAdmin(): @Admin { - return <-create Admin() - } -} - -// This contract makes the admin creation private and in the initializer -// so that only the one who controls the account can mint tokens -// GOOD -pub contract Currency { - pub resource Admin { - pub fun mintTokens() - - // Only an admin can create new Admins - pub fun createAdmin(): @Admin { - return <-create Admin() - } - } - - init() { - // Create a single admin resource - let firstAdmin <- create Admin() - - // Store it in private account storage in `init` so only the admin can use it - self.account.save(<-firstAdmin, to: /storage/currencyAdmin) - } -} -``` - -## Do not modify smart contract state or emit events in public struct initializers - -This is another example of the risks of having publicly accessible parts to your smart contract. - -### Problem - -Data structure definitions in Cadence currently must be declared as public so that they can be used by anyone. -Structs do not have the same restrictions that resources have on them, -which means that anyone can create a new instance of a struct without going through any authorization. - -### Solution - -Any contract state-modifying operations related to the creation of structs -should be contained in restricted resources instead of the initializers of structs. - -### Example - -This used to be a bug in the NBA Top Shot smart contract, so we'll use that as an example. -Before, when it created a new play, -[it would initialize the play record with a struct,](https://github.com/dapperlabs/nba-smart-contracts/blob/55645478594858a6830e4ab095034068ef9753e9/contracts/TopShot.cdc#L155-L158) -which increments the number that tracks the play IDs and emits an event: - -```cadence -// Simplified Code -// BAD -// -pub contract TopShot { - - // The Record that is used to track every unique play ID - pub var nextPlayID: UInt32 - - pub struct Play { - - pub let playID: UInt32 - - init() { - - self.playID = TopShot.nextPlayID - - // Increment the ID so that it isn't used again - TopShot.nextPlayID = TopShot.nextPlayID + 1 - - emit PlayCreated(id: self.playID, metadata: metadata) - } - } -} -``` - -This is a risk because anyone can create the `Play` struct as many times as they want, -which could increment the `nextPlayID` field to the max `UInt32` value, -effectively preventing new plays from being created. It also would emit bogus events. - -This bug was fixed by -[instead updating the contract state in the admin function](https://github.com/dapperlabs/nba-smart-contracts/blob/master/contracts/TopShot.cdc#L682-L685) -that creates the plays. - - -```cadence -// Update contract state in admin resource functions -// GOOD -// -pub contract TopShot { - - // The Record that is used to track every unique play ID - pub var nextPlayID: UInt32 - - pub struct Play { - - pub let playID: UInt32 - - init() { - self.playID = TopShot.nextPlayID - } - } - - pub resource Admin { - - // Protected within the private admin resource - pub fun createPlay() { - // Create the new Play - var newPlay = Play() - - // Increment the ID so that it isn't used again - TopShot.nextPlayID = TopShot.nextPlayID + UInt32(1) - - emit PlayCreated(id: newPlay.playID, metadata: metadata) - - // Store it in the contract storage - TopShot.playDatas[newPlay.playID] = newPlay - } - } -} -``` +https://github.com/onflow/docs/tree/main/docs/cadence/anti-patterns.mdx diff --git a/docs/contract-upgrades.mdx b/docs/contract-upgrades.mdx index 7698f5c152..5aefec9546 100644 --- a/docs/contract-upgrades.mdx +++ b/docs/contract-upgrades.mdx @@ -1,29 +1,3 @@ ---- -title: Contract Upgrades with Incompatible Changes ---- +# This document has been moved to a new location: -### Problem - -I have an incompatible upgrade for a contract. How can I deploy this? - -### Solution - -Please don't perform incompatible upgrades between contract versions in the same account. -There is too much that can go wrong. - -You can make [compatible upgrades](language/contract-updatability) and then run a post-upgrade function on the new contract code if needed. - -If you must replace your contract rather than update it, -the simplest solution is to add or increase a suffix on any named paths in the contract code -(e.g. `/public/MyProjectVault` becomes `/public/MyProjectVault002`) in addition to making the incompatible changes, -then create a new account and deploy the updated contract there. - -⚠️ Flow identifies types relative to addresses, so you will also need to provide _upgrade transactions_ to exchange the old contract's resources for the new contract's ones. Make sure to inform users as soon as possible when and how they will need to perform this task. - -If you absolutely must keep the old address when making an incompatible upgrade, then you do so at your own risk. Make sure you perform the following actions in this exact order: - -1. Delete any resources used in the contract account, e.g. an Admin resource. -2. Delete the contract from the account. -3. Deploy the new contract to the account. - -⚠️ Note that if any user accounts contain `structs` or `resources` from the _old_ version of the contract that have been replaced with incompatible versions in the new one, **they will not load and will cause transactions that attempt to access them to crash**. For this reason, once any users have received `structs` or `resources` from the contract, this method of making an incompatible upgrade should not be attempted! +https://github.com/onflow/docs/tree/main/docs/cadence/contract-upgrades.mdx diff --git a/docs/design-patterns.mdx b/docs/design-patterns.mdx index 461736bd84..00966d9a6a 100644 --- a/docs/design-patterns.mdx +++ b/docs/design-patterns.mdx @@ -1,490 +1,3 @@ ---- -title: Cadence Design Patterns ---- +# This document has been moved to a new location: -This is a selection of software design patterns developed by core Flow developers -while writing Cadence code for deployment to Flow Mainnet. - -Many of these design patters apply to most other programming languages, but some are specific to Cadence. - -[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are building blocks for software development. -They may provide a solution to a problem that you encounter when writing smart contracts in Cadence. -If they do not clearly fit, these patterns may not be the right solution for a given situation or problem. -They are not meant to be rules to be followed strictly, especially where a better solution presents itself. - -# General - -These are general patterns to follow when writing smart contracts. - -## Use named value fields for constants instead of hard-coding - -### Problem - -Your contracts, resources, and scripts all have to refer to the same value. -A number, a string, a storage path, etc. -Entering these values manually in transactions and scripts is a potential source of error. -See [Wikipedia's page on magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) - -### Solution - -Add a public (`pub`), constant (`let`) field, e.g. a `Path` , to the contract responsible for the value, -and set it in the contract's initializer. -Refer to that value via this public field rather than specifying it manually. - -Example Snippet: - -```cadence - -// BAD Practice: Do not hard code storage paths -pub contract NamedFields { - pub resource Test {} - - init() { - // BAD: Hard-coded storage path - self.account.save(<-create Test(), to: /storage/testStorage) - } -} - -// GOOD practice: Instead, use a field -// -pub contract NamedFields { - pub resource Test {} - - // GOOD: field storage path - pub let TestStoragePath: StoragePath - - init() { - // assign and access the field here and in transactions - self.TestStoragePath = /storage/testStorage - self.account.save(<-create Test(), to: self.TestStoragePath) - } -} - -``` - -[Example Code](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L718) - -## Script-Accessible public field/function - -Data availability is important in a blockchain environment. -It is useful to publicize information about your smart contract and the assets it controls -so other smart contracts and apps can easily query it. - -### Problem - -Your contract, resource or struct has a field or resource that will need to be read and used on or off-chain, often in bulk. - -### Solution - -Make sure that the field can be accessed from a script (using a `PublicAccount`) -rather than requiring a transaction (using an `AuthAccount`). -This saves the time and fees required to read a property using a transaction. -Making the field or function `pub` and exposing it via a `/public/` capability will allow this. - -Be careful not to expose any data or functionality that should be kept private when doing so. - -Example: - -```cadence -// BAD: Field is private, so it cannot be read by the public -access(self) let totalSupply: UFix64 - -// GOOD: Field is public, so it can be read and used by anyone -pub let totalSupply: UFix64 -``` - -## Script-Accessible report - -### Problem - -Your contract has a resource that you wish to access fields of. -Resources are often stored in private places and are hard to access. -Additionally, scripts cannot return resources to the external context, -so a struct must be used to hold the data. - -### Solution - -Return a reference to a resource if the data from a single resource is all that is needed. -Otherwise, declare a struct to hold the data that you wish to return from the script. -Write a function that fills out the fields of this struct with the data -from the resource that you wish to access. -Then call this on the resource that you wish to access the fields of in a script, -and return the struct from the script. - -See [Script-Accessible public field/function](#script-accessible-public-fieldfunction), above, for how best to expose this capability. - -### Example Code - -```cadence -pub contract AContract { - pub let BResourceStoragePath: StoragePath - pub let BResourcePublicPath: PublicPath - - init() { - self.BResourceStoragePath = /storage/BResource - self.BResourcePublicPath = /public/BResource - } - - // Resource definition - pub resource BResource { - pub var c: UInt64 - pub var d: String - - - // Generate a struct with the same fields - // to return when a script wants to see the fields of the resource - // without having to return the actual resource - pub fun generateReport(): BReportStruct { - return BReportStruct(c: self.c, d: self.d) - } - - init(c: UInt64, d: String) { - self.c = c - self.d = d - } - } - - // Define a struct with the same fields as the resource - pub struct BReportStruct { - pub var c: UInt64 - pub var d: String - - init(c: UInt64, d: String) { - self.c = c - self.d = d - } - - } -} -... -// Transaction -import AContract from 0xAContract - -transaction { - prepare(acct: AuthAccount) { - //... - acct.link<&AContract.BResource>(AContract.BResourcePublicPath, target: AContract.BResourceStoragePath) - } -} -// Script -import AContract from 0xAContract - -// Return the struct with a script -pub fun main(account: Address): AContract.BReportStruct { - // borrow the resource - let b = getAccount(account) - .getCapability(AContract.BResourcePublicPath) - .borrow<&AContract.BResource>() - // return the struct - return b.generateReport() -} -``` - -## Init Singleton - -### Problem - -An admin resource must be created and delivered to a specified account. -There should not be a function to do this, as that would allow anyone to create an admin resource. - -### Solution - -Create any one-off resources in the contract's `init()` function -and deliver them to an address or `AuthAccount` specified as an argument. - -See how this is done in the LockedTokens contract init function: - -[LockedTokens.cdc](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L718) - -and in the transaction that is used to deploy it: - -[admin_deploy_contract.cdc](https://github.com/onflow/flow-core-contracts/blob/master/transactions/lockedTokens/admin/admin_deploy_contract.cdc) - - -## Use descriptive names for fields, paths, functions and variables - -### Problem - -Smart contracts often are vitally important pieces of a project and often have many other -smart contracts and applications that rely on them. -Therefore, they need to be clearly written and easy to understand. - -### Solution - -All fields, functions, types, variables, etc., need to have names that clearly describe what they are used for. - -`account` / `accounts` is better than `array` / `element`. - -`providerAccount` / `tokenRecipientAccount` is better than `acct1` / `acct2`. - -`/storage/bestPracticesDocsCollectionPath` is better than `/storage/collection` - -### Example -```cadence -// BAD: Unclear naming -// -pub contract Tax { - // Do not use abbreviations unless absolutely necessary - pub var pcnt: UFix64 - - // Not clear what the function is calculating or what the parameter should be - pub fun calculate(num: UFix64): UFix64 { - // What total is this referring to? - let total = num + (num * self.pcnt) - - return total - } -} - -// GOOD: Clear naming -// -pub contract TaxUtilities { - // Clearly states what the field is for - pub var taxPercentage: UFix64 - - // Clearly states that this function calculates the - // total cost after tax - pub fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { - let postTaxCost = preTaxCost + (preTaxCost * self.taxPercentage) - - return postTaxCost - } -} -``` - -## Include concrete types in type constraints, especially "Any" types - -### Problem - -When specifying type constraints for capabilities or borrows, concrete types often do not get specified, -making it unclear if the developer actually intended it to be unspecified or not. -Paths also use a shared namespace between contracts, so an account may have stored a different object -in a path that you would expect to be used for something else. -Therefore, it is important to be explicit when getting objects or references to resources. - - -### Solution - -A good example of when the code should specify the type being restricted is checking the FLOW balance: -The code must borrow `&FlowToken.Vault{FungibleToken.Balance}`, in order to ensure that it gets a FLOW token balance, -and not just `&{FungibleToken.Balance}`, any balance – the user could store another object -that conforms to the balance interface and return whatever value as the amount. - -When the developer does not care what the concrete type is, they should explicitly indicate that -by using `&AnyResource{Receiver}` instead of `&{Receiver}`. -In the latter case, `AnyResource` is implicit, but not as clear as the former case. - -## Plural names for arrays and maps are preferable - -e.g. `accounts` rather than `account` for an array of accounts. - -This signals that the field or variable is not scalar. -It also makes it easier to use the singular form for a variable name during iteration. - -## Use transaction post-conditions when applicable - -### Problem - -Transactions can contain any amount of valid Cadence code and access many contracts and accounts. -The power of resources and capabilities means that there may be some behaviors of programs that are not expected. - -### Solution - -It is usually safe to include post-conditions in transactions to verify the intended outcome. - -### Example - -This could be used when purchasing an NFT to verify that the NFT was deposited in your account's collection. - -```cadence -// Psuedo-code - -transaction { - - pub let buyerCollectionRef: &NonFungibleToken.Collection - - prepare(acct: AuthAccount) { - - // Get tokens to buy and a collection to deposit the bought NFT to - let temporaryVault <- vaultRef.withdraw(amount: 10.0) - let self.buyerCollectionRef = acct.borrow(from: /storage/Collection) - - // purchase, supplying the buyers collection reference - saleRef.purchase(tokenID: 1, recipient: self.buyerCollectionRef, buyTokens: <-temporaryVault) - - } - post { - // verify that the buyer now owns the NFT - self.buyerCollectionRef.idExists(1) == true: "Bought NFT ID was not deposited into the buyers collection" - } -} -``` - -## Avoid excessive load and save storage operations (prefer in-place mutations) - -### Problem - -When modifying data in account storage, `load()` and `save()` are costly operations. -This can quickly cause your transaction to reach the gas limit or slow down the network. - -This also applies to contract objects and their fields (which are implicitly stored in storage, i.e. read from/written to), -or nested resources. Loading them from their fields just to modify them and save them back -is just as costly. - -For example, a collection contains a dictionary of NFTs. There is no need to move the whole dictionary out of the field, -update the dictionary on the stack (e.g. adding or removing an NFT), -and then move the whole dictionary back to the field, it can be updated in-place. -The same goes for a more complex data structure like a dictionary of nested resources: -Each resource can be updated in-place by taking a reference to the nested object instead of loading and saving. - -### Solution - -For making modifications to values in storage or accessing stored objects, -`borrow()` should always be used to access them instead of `load` or `save` unless absolutely necessary. -`borrow()` returns a reference to the object at the storage path instead of having to load the entire object. -This reference can be assigned to or can be used to access fields or call methods on stored objects. - -### Example - -```cadence -// BAD: Loads and stores a resource to use it -// -transaction { - - prepare(acct: AuthAccount) { - - // Removes the vault from storage, a costly operation - let vault <- acct.load<@ExampleToken.Vault>(from: /storage/exampleToken) - - // Withdraws tokens - let burnVault <- vault.withdraw(amount: 10) - - destroy burnVault - - // Saves the used vault back to storage, another costly operation - acct.save(to: /storage/exampleToken) - - } -} - -// GOOD: Uses borrow instead to avoid costly operations -// -transaction { - - prepare(acct: AuthAccount) { - - // Borrows a reference to the stored vault, much less costly operation - let vault <- acct.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) - - let burnVault <- vault.withdraw(amount: 10) - - destroy burnVault - - // No `save` required because we only used a reference - } -} -``` - -# Capabilities - -## Capability Bootstrapping - -### Problem - -An account must be given a [capability](language/capability-based-access-control) -to a resource or contract in another account. To create, i.e. link the capability, -the transaction must be signed by a key which has access to the target account. - -To transfer / deliver the capability to the other account, the transaction also needs write access to that one. -It is not as easy to produce a single transaction which is authorized by two accounts -as it is to produce a typical transaction which is authorized by one account. - -This prevents a single transaction from fetching the capability -from one account and delivering it to the other. - -### Solution - -The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](language/accounts#account-inbox) - -Account A (which we will call the provider) creates the capability they wish to send to B (which we will call the recipient), -and stores this capability on their account in a place where the recipient can access it using the `Inbox.publish` function on their account. -They choose a name for the capability that the recipient can later use to identify it, and specify the recipient's address when calling `publish`. -This call to `publish` will emit an `InboxValuePublished` event that the recipient can listen for off-chain to know that the Capability is ready for them to claim. - -The recipient then later can use the `Inbox.claim` function to securely grab the capability from the provider's account. -They must provide the name and type with which the capability was published, as well as the address of the provider's account -(all of this information is available in the `InboxValuePublished` event emitted on `publish`). -This will remove the capability from the provider's account and emit an `InboxValueClaimed` event that the provider can listen for off-chain. - -One important caveat to this is that the published capability is stored on the provider's account until the recipient claims it, -so the provider can also use the `Inbox.unpublish` function to remove the capability from their account if they no longer wish to pay for storage for it. -This also requires the name and type which the capability was published, -and emits an `InboxValueUnpublished` event that the recipient can listen for off-chain. - -It is also important to note that the recipient becomes the owner of the capability object once they have claimed it, -and can thus store it or copy it anywhere they have access to. -This means providers should only publish capabilities to recipients they trust to use them properly, -or limit the type with which the capability is restricted in order to only give recipients access to the functionality -that the provider is willing to allow them to copy. - -## Capability Revocation - -### Problem - -A capability provided by one account to a second account must able to be revoked -by the first account without the co-operation of the second. - -See the [Capability Controller FLIP](https://github.com/onflow/flow/pull/798) for a proposal to improve this in the future. - -### Solution - -The first account should create the capability as a link to a capability in `/private/`, -which then links to a resource in `/storage/`. That second-order link is then handed -to the second account as the capability for them to use. - -**Account 1:** `/private/capability` → `/storage/resource` - -`/private/revokableLink` -> `/private/capability` - -**Account 2:** `/storage/capability -> (Capability(→Account 1: /private/revokableLink))` - -If the first account wants to revoke access to the resource in storage, -they should delete the `/private/` link that the second account's capability refers to. -Capabilities use paths rather than resource identifiers, so this will break the capability. - -The first account should be careful not to create another link at the same location -in its private storage once the capability has been revoked, -otherwise this will restore the second account's capability. - - -## Check for existing links before creating new ones - -When linking a capability, the link might be already present. -In that case, Cadence will not panic with a runtime error but the link function will return nil. -The documentation states that: The link function does not check if the target path is valid/exists -at the time the capability is created and does not check if the target value conforms to the given type. -In that sense, it is a good practice to check if the link does already exist with `AuthAccount.getLinkTarget` -before creating it with `AuthAccount.link()`. -`AuthAccount.getLinkTarget` will return nil if the link does not exist. - -### Example - -```cadence -transaction { - prepare(signer: AuthAccount) { - // Create a public capability to the Vault that only exposes - // the deposit function through the Receiver interface - // - // Check to see if there is a link already and unlink it if there is - - if signer.getLinkTarget(/public/exampleTokenReceiver) != nil { - signer.unlink(/public/exampleTokenReceiver) - } - - signer.link<&ExampleToken.Vault{FungibleToken.Receiver}>( - /public/exampleTokenReceiver, - target: /storage/exampleTokenVault - ) - } -} -``` +https://github.com/onflow/docs/tree/main/docs/cadence/design-patterns.mdx diff --git a/docs/development.md b/docs/development.md index fd8ab99c76..c09c0de88a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,115 +1,3 @@ -# Development - -## Running the latest version of the Language Server in the Visual Studio Code Extension - -- Ensure that a `replace` statement exists in `languageserver/go.mod`, so that the language server compiles with the local changes to Cadence. - -- Find the Visual Studio Code preference named "Cadence: Flow Command" and change it to: - - ```text - /path/to/cadence/languageserver/run.sh - ``` - -- Restart Visual Studio Code - -This will automatically recompile the language server every time it is started. - -## Debugging the Language Server - -- Follow the instructions above (see "Running the latest version of the Language Server in the Visual Studio Code Extension") - -- Attach to the process of the language server started by Visual Studio Code. - - For example, in Goland, choose Run -> Attach to Process. - - This requires gops to be installed, which can be done using `go get github.com/google/gops`. - -## Tools - -The [`runtime/cmd` directory](https://github.com/onflow/cadence/tree/master/runtime/cmd) -contains command-line tools that are useful when working on the implementation for Cadence, or with Cadence code: - -- The [`parse`](https://github.com/onflow/cadence/tree/master/runtime/cmd/parse) tool - can be used to parse (syntactically analyze) Cadence code. - By default, it reports syntactical errors in the given Cadence program, if any, in a human-readable format. - By providing the `-json` it returns the AST of the program in JSON format if the given program is syntactically valid, - or syntactical errors in JSON format (including position information). - - ``` - $ echo "X" | go run ./runtime/cmd/parse - error: unexpected token: identifier - --> :1:0 - | - 1 | X - | ^ - ``` - - ``` - $ echo "let x = 1" | go run ./runtime/cmd/parse -json - [ - { - "program": { - "Type": "Program", - "Declarations": [ - { - "Type": "VariableDeclaration", - "StartPos": { - "Offset": 0, - "Line": 1, - "Column": 0 - }, - "EndPos": { - "Offset": 8, - "Line": 1, - "Column": 8 - }, - [...] - ``` - -- The [`check`](https://github.com/onflow/cadence/tree/master/runtime/cmd/check) tool - can be used to check (semantically analyze) Cadence code. - By default, it reports semantic errors in the given Cadence program, if any, in a human-readable format. - By providing the `-json` it returns the AST in JSON format, or semantic errors in JSON format (including position information). - - ``` - $ echo "let x = 1" | go run ./runtime/cmd/check 1 ↵ - error: error: missing access modifier for constant - --> :1:0 - | - 1 | let x = 1 - | ^ - ``` - -- The [`main`](https://github.com/onflow/cadence/tree/master/runtime/cmd/check) tools - can be used to execute Cadence programs. - If a no argument is provided, the REPL (Read-Eval-Print-Loop) is started. - If an argument is provided, the Cadence program at the given path is executed. - The program must have a function named `main` which has no parameters and no return type. - - ``` - $ go run ./runtime/cmd/main 130 ↵ - Welcome to Cadence v0.12.3! - Type '.help' for assistance. - - 1> let x = 2 - 2> x + 3 - 5 - ``` - - ``` - $ echo 'pub fun main () { log("Hello, world!") }' > hello.cdc - $ go run ./runtime/cmd/main hello.cdc - "Hello, world!" - ``` - -## How is it possible to detect non-determinism and data races in the checker? - -Run the checker tests with the `cadence.checkConcurrently` flag, e.g. - -```shell -go test -race -v ./runtime/tests/checker -cadence.checkConcurrently=10 -``` - -This runs each check of a checker test 10 times, concurrently, -and asserts that the checker errors of all checks are equal. +# This document has been moved to a new location: +https://github.com/onflow/docs/tree/main/docs/cadence/development.md diff --git a/docs/goland.md b/docs/goland.md index 3d565b738c..7a78b759c3 100644 --- a/docs/goland.md +++ b/docs/goland.md @@ -1,15 +1,3 @@ -# GoLand +# This document has been moved to a new location: -## Linter Integration - -- Build golangci-lint and the custom analyzers: Run `make build-linter` -- In GoLand go to `Preferences` -> `Tools` -> `File Watchers` -> Add `golangci-lint` - - File Type: Go files - - Scope: Project files - - Program: `/path/to/cadence/tools/golangci-lint/golangci-lint` (NOTE: NOT `~/go/bin/golangci-lint`) - - Arguments: `run $FileDir$` - - Advanced Options: - - Create output file from stdout - - Show console: Never - - Output filters: `$FILE_PATH$:$LINE$:$COLUMN$: $MESSAGE$` - +https://github.com/onflow/docs/tree/main/docs/cadence/goland.md diff --git a/docs/index.mdx b/docs/index.mdx index 7cdd278a80..8b868fc3e0 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -1,188 +1,3 @@ ---- -title: Introduction to Cadence ---- +# This document has been moved to a new location: -In a blockchain environment like Flow, programs that are stored on-chain in accounts are commonly referred to as smart contracts. -A smart contract is a program that verifies and executes the performance of a contract without the need for a trusted third party. -Programs that run on blockchains are commonly referred to as smart contracts because they mediate important functionality (such as currency) -without having to rely on a central authority (like a bank). - -## A New Programming Language - ---- - -Cadence is a resource-oriented programming language that introduces new features to smart contract programming -that help developers ensure that their code is safe, secure, clear, and approachable. Some of these features are: - -- Type safety and a strong static type system. -- Resource-oriented programming, a new paradigm that pairs linear types with object capabilities -to create a secure and declarative model for digital ownership by ensuring that resources (which are used to represent scarce digital assets) -can only exist in one location at a time, cannot be copied, and cannot be accidentally lost or deleted. -- Built-in pre-conditions and post-conditions for functions and transactions. -- The utilization of capability-based security, which enforces that access to objects -is restricted to only the owner of the object and those who have a valid reference to it. -This is Cadence's main form of access control. - -Cadence’s syntax is inspired by popular modern general-purpose programming languages -like [Swift](https://developer.apple.com/swift/), [Kotlin](https://kotlinlang.org/), and [Rust](https://www.rust-lang.org/). -Its use of resource types maps well to that of [Move](https://medium.com/coinmonks/overview-of-move-programming-language-a860ffd8f55d), -the programming language being developed by the Diem team. - -## Cadence's Programming Language Pillars - ---- - -Cadence, a new high-level programming language, observes the following requirements: - -- **Safety and Security:** Safety is the underlying reliability of any smart contract (i.e., it’s bug-free and performs its function). -Security is the prevention of attacks on the network or smart contracts (i.e., unauthorized actions by malicious actors). -Safety and security are critical in smart contracts because of the immutable nature of blockchains, -and because they often deal with high-value assets. While auditing and reviewing code will be a crucial part of smart contract development, -Cadence maximizes efficiency while maintaining the highest levels of safety and security at its foundation. -It accomplishes this via a strong static type system, design by contract, and ownership primitives inspired by linear types (which are useful when dealing with assets). -- **Clarity:** Code needs to be easy to read, and its meaning should be as unambiguous as possible. -It should also be suited for verification so that tooling can help with ensuring safety and security guarantees. -These guarantees can be achieved by making the code declarative and allowing the developer to express their intentions directly. -We make those intentions explicit by design, which, along with readability, make auditing and reviewing more efficient, at a small cost to verbosity. -- **Approachability:** Writing code and creating programs should be as approachable as possible. -Incorporating features from languages like Swift and Rust, developers should find Cadence’s syntax and semantics familiar. -Practical tooling, documentation, and examples enable developers to start creating programs quickly and effectively. -- **Developer Experience:** The developer should be supported throughout the entire development lifecycle, from initial application logic to on-chain bugfixes. -- **Intuiting Ownership with Resources:** Resources are a composite data type, similar to a struct, that expresses direct ownership of assets. -Cadence’s strong static type system ensures that resources can only exist in one location at a time and cannot be copied or lost because of a coding mistake. -Most smart contract languages currently use a ledger-style approach to record ownership, -where an asset like a fungible token is stored in the smart contract as an entry in a central ledger. -Cadence’s resources directly tie an asset’s ownership to the account that owns it by saving the resource in the account’s storage. -As a result, ownership isn’t centralized in a smart contract’s storage. Each account owns its assets, -and the assets can be transferred freely between accounts without the need for arbitration by a central smart contract. - -## Addressing Challenges with Existing Languages - ---- - -Other languages pioneered smart contract development, but they lack in areas that affect the long-term viability of next-generation applications. - -### Safety - -Safety is the reliability of a smart contract to perform its function as intended. -It is heavily influenced by the unchangeable-once-deployed nature of smart contracts: -Developers must take certain precautions in order to avoid introducing any potentially catastrophic vulnerabilities -prior to publishing a smart contract on the blockchain. -It is standard across many blockchains that modifying or updating a smart contract, even to fix a vulnerability, is not allowed. -Thus, any bugs that are present in the smart contract will exist forever. - -For example, in 2016, an overlooked vulnerability in an Ethereum DAO smart contract (Decentralized Autonomous Organization) -saw millions of dollars siphoned from a smart contract, -eventually leading to a fork in Ethereum and two separate active blockchains (Ethereum and Ethereum Classic). - -Bug fixes are only possible if a smart contract is designed to support changes, -a feature that introduces complexity and security issues. -Lengthy auditing and review processes can ensure a bug-free smart contract. -Still, they add substantial time to the already time-consuming task of getting the smart contract’s core logic working correctly. - -Overlooked mistakes cause the most damaging scenarios. -It is easy to lose or duplicate monetary value or assets in existing languages because they don’t check relevant invariants -or make it harder to express them. -For example, a plain number represents a transferred amount that can be accidentally (or maliciously) multiplied or ignored. - -Some languages also express behaviors that developers tend to forget about. -For example, a fixed-range type might express monetary value, without considerations for a potential overflow or underflow. -In Solidity, Ethereum's smart contract language, an overflow causes the value to wrap around, as shown [here](https://ethfiddle.com/CAp-kQrDUP). -Solidity also allows contracts to declare variables without initializing them. -If the developer forgets to add an initialization somewhere, -and then tries to read the variable somewhere else in the code expecting it to be a specific value, issues will occur. - -Cadence is type safe and has a strong static type system, -which prevents important classes of erroneous or undesirable program behavior at compile-time (i.e., before the program is run on-chain). -Types are checked statically and are not implicitly converted. Cadence also improves the safety of programs by preventing arithmetic underflow and overflow, -introduces optionals to make nil-cases explicit, and always requires variables to be initialized. -This helps ensure the behavior of these smart contracts is apparent and not dependent on context. - -### Security - -Security, in combination with safety, ensures the successful execution of a smart contract over time -by preventing unsanctioned access and guaranteeing that only authorized actions can be performed in the protocol. -In some languages, functions are public by default, creating vulnerabilities that allow malicious users to find attack vectors. -Cadence utilizes capability-based security, which allows the type system to enforce access control based on rules that users and developers have control over. - -Security is a consideration when interacting with other smart contracts. Any external call potentially allows malicious code to be executed. -For example, in Solidity, when the called function signature does not match any of the available ones, it triggers Solidity’s fallback functions. -These functions can be used in malicious ways. Language features such as multiple inheritances and overloading or dispatch can also make it difficult -to determine which code is invoked. - -In Cadence, the safety and security of programs are enhanced by **Design By Contract** and **Ownership Primitives.** -Design by contract allows developers to state pre-conditions and post-conditions for functions and interfaces in a declarative manner -so that callers can be certain about the behavior of called code. Ownership primitives are inspired by linear types and increase safety when working with assets. -They ensure that valuable assets are, for example, not accidentally or maliciously lost or duplicated. - -### Clarity and Approachability - -Implicitness, context-dependability, and expressiveness are language-based challenges that developers often encounter. -They affect the clarity (i.e. the readability of code and the ability to determine its intended function) -and the approachability (i.e. the ability to interpret or write code) of the language and the programs built using it. -For example, in Solidity, storage must be implemented in a low-level key-value manner, which obfuscates the developer’s intentions. -Syntax confusion is another example, with “=+” being legal syntax leading to an assignment instead of a probably-intended increment. -Solidity also has features with uncommon behaviors that can lead to unintended results. -[Multiple inheritance may lead to unexpected behaviours in the program](https://medium.com/consensys-diligence/a-case-against-inheritance-in-smart-contracts-d7f2c738f78e), -and testing and auditing the code is unlikely to identify this issue. - -The Ethereum blockchain’s code immutability showcases the need for considerations around extensibility and mechanisms that allow ad-hoc fixes. -Developers using custom-made approaches such as the 'data separation' approach to upgradability -may run into problems with the complexity of data structures, -while developers using ‘delegatecall-based proxies` may run into problems with the consistency of memory layouts. -Either way, these challenges compromise approachability and overall extensibility. -Cadence has [contract upgradability built in by default](language/contract-updatability), -and contracts can be made immutable by removing all keys from an account. - -Cadence improves the clarity and extensibility of programs by utilizing interfaces to allow extensibility, code reuse, and interoperability between contracts. -Cadence modules also have configurable and transparent upgradeability built-in to enable projects to test and iterate before making their code immutable. - -Cadence allows the use of argument labels to describe the meaning of function arguments. -It also provides a rich standard library with useful data structures (e.g., dictionaries, sets) and data types for common use cases, -like fixed-point arithmetic, which helps when working with currencies. - -## Intuiting Ownership with Resources - -Most smart contract languages currently use a ledger-style approach to record ownership, -where an asset is stored in the smart contract as an entry in a central ledger, and this ledger is the source of truth around asset ownership. -There are many disadvantages to this design, especially when it comes to tracking the ownership of multiple assets belonging to a single account. -To find out all of the assets that an account owns, you would have to enumerate all the possible smart contracts that could potentially include this account -and search to see if the account owns these assets. - -In a resource-oriented language like Cadence, resources directly tie an asset to the account that owns it -by saving the resource in the account’s storage. As a result, ownership isn’t centralized in a single, central smart contract’s storage. -Instead, each account owns and stores its own assets, and the assets can be transferred freely between accounts without the need for arbitration by a central smart contract. - -Resources are inspired by linear types and increase safety when working with assets, which often have real, intrinsic value. -Resources, as enforced by Cadence’s type system, ensure that assets are correctly manipulated and not abused. - -- Every resource has exactly one owner. If a resource is used as a function parameter, an initial value for a variable, or something similar, the object is not copied. -Instead, it is moved to the new location, and the old location is immediately invalidated. -- The language will report an error if ownership of a resource was not properly transferred, i.e., -when the program attempts to introduce multiple owners for the resource or the resource ends up in a state where it does not have an owner. -For example, a resource can only be assigned to exactly one variable and cannot be passed to functions multiple times. -- Resources cannot go out of scope. If a function or transaction removes a resource from an account’s storage, -it either needs to end the transaction in an account's storage, or it needs to be explicitly and safely deleted. There is no “garbage collection” for resources. - -The special status of Resource objects must be enforced by the runtime; if they were just a compiler abstraction it would be easy for malicious code to break the value guarantees. - -Resources change how assets are used in a programming environment to better resemble assets in the real world. -Users store their currencies and assets in their own account, in their own wallet storage, and they can do with them as they wish. -Users can define custom logic and structures for resources that give them flexibility with how they are stored. -Additionally, because everyone stores their own assets, the calculation and charging of state rent is fair and balanced across all users in the network. - -## An Interpreted Language - ---- - -Currently, Cadence is an interpreted language, as opposed to a compiled language. This means that there is no Cadence Assembly, bytecode, compiler, or Cadence VM. - -The structure of the language lends itself well to compilation (for example, static typing), -but using an interpreter for the first version allows us to refine the language features more quickly as we define them. - -## Getting Started with Cadence - ---- - -Now that you've learned about the goals and design of Cadence and Flow, you're ready to get started with the Flow emulator and tools! -Go to the [Getting Started](tutorial/01-first-steps) page to work through language fundamentals and tutorials. +https://github.com/onflow/docs/tree/main/docs/cadence/intro.md diff --git a/docs/json-cadence-spec.md b/docs/json-cadence-spec.md index 6d2fb92e77..17c54da112 100644 --- a/docs/json-cadence-spec.md +++ b/docs/json-cadence-spec.md @@ -1,868 +1,3 @@ ---- -title: JSON-Cadence Data Interchange Format ---- +# This document has been moved to a new location: -> Version 0.3.1 - -JSON-Cadence is a data interchange format used to represent Cadence values as language-independent JSON objects. - -This format includes less type information than a complete [ABI](https://en.wikipedia.org/wiki/Application_binary_interface), and instead promotes the following tenets: - -- **Human-readability** - JSON-Cadence is easy to read and comprehend, which speeds up development and debugging. -- **Compatibility** - JSON is a common format with built-in support in most high-level programming languages, making it easy to parse on a variety of platforms. -- **Portability** - JSON-Cadence is self-describing and thus can be transported and decoded without accompanying type definitions (i.e. an ABI). - -# Values - ---- - -## Void - -```json -{ - "type": "Void" -} -``` - -### Example - -```json -{ - "type": "Void" -} -``` - ---- - -## Optional - -```json -{ - "type": "Optional", - "value": null | -} -``` - -### Example - -```json -// Non-nil - -{ - "type": "Optional", - "value": { - "type": "UInt8", - "value": "123" - } -} - -// Nil - -{ - "type": "Optional", - "value": null -} -``` - ---- - -## Bool - -```json -{ - "type": "Bool", - "value": true | false -} -``` - -### Example - -```json -{ - "type": "Bool", - "value": true -} -``` - ---- - -## String - -```json -{ - "type": "String", - "value": "..." -} - -``` - -### Example - -```json -{ - "type": "String", - "value": "Hello, world!" -} -``` - ---- - -## Address - -```json -{ - "type": "Address", - "value": "0x0" // as hex-encoded string with 0x prefix -} -``` - -### Example - -```json -{ - "type": "Address", - "value": "0x1234" -} -``` - ---- - -## Integers - -`[U]Int`, `[U]Int8`, `[U]Int16`, `[U]Int32`,`[U]Int64`,`[U]Int128`, `[U]Int256`, `Word8`, `Word16`, `Word32`, or `Word64` - -Although JSON supports integer literals up to 64 bits, all integer types are encoded as strings for consistency. - -While the static type is not strictly required for decoding, it is provided to inform client of potential range. - -```json -{ - "type": "", - "value": "" -} -``` - -### Example - -```json -{ - "type": "UInt8", - "value": "123" -} -``` - ---- - -## Fixed Point Numbers - -`[U]Fix64` - -Although fixed point numbers are implemented as integers, JSON-Cadence uses a decimal string representation for readability. - -```json -{ - "type": "[U]Fix64", - "value": "." -} -``` - -### Example - -```json -{ - "type": "Fix64", - "value": "12.3" -} -``` - ---- - -## Array - -```json -{ - "type": "Array", - "value": [ - , - - // ... - ] -} -``` - -### Example - -```json -{ - "type": "Array", - "value": [ - { - "type": "Int16", - "value": "123" - }, - { - "type": "String", - "value": "test" - }, - { - "type": "Bool", - "value": true - } - ] -} -``` - ---- - -## Dictionary - -Dictionaries are encoded as a list of key-value pairs to preserve the deterministic ordering implemented by Cadence. - -```json -{ - "type": "Dictionary", - "value": [ - { - "key": "", - "value": - }, - ... - ] -} -``` - -### Example - -```json -{ - "type": "Dictionary", - "value": [ - { - "key": { - "type": "UInt8", - "value": "123" - }, - "value": { - "type": "String", - "value": "test" - } - } - ], - // ... -} -``` - ---- - -## Composites (Struct, Resource, Event, Contract, Enum) - -Composite fields are encoded as a list of name-value pairs in the order in which they appear in the composite type declaration. - -```json -{ - "type": "Struct" | "Resource" | "Event" | "Contract" | "Enum", - "value": { - "id": "", - "fields": [ - { - "name": "", - "value": - }, - // ... - ] - } -} -``` - -### Example - -```json -{ - "type": "Resource", - "value": { - "id": "0x3.GreatContract.GreatNFT", - "fields": [ - { - "name": "power", - "value": {"type": "Int", "value": "1"} - } - ] - } -} -``` - ---- - -## Path - -```json -{ - "type": "Path", - "value": { - "domain": "storage" | "private" | "public", - "identifier": "..." - } -} -``` - -### Example - -```json -{ - "type": "Path", - "value": { - "domain": "storage", - "identifier": "flowTokenVault" - } -} -``` - ---- - -## Type Value - -```json -{ - "type": "Type", - "value": { - "staticType": - } -} -``` - -### Example - -```json -{ - "type": "Type", - "value": { - "staticType": { - "kind": "Int", - } - } -} -``` - ---- - -## Capability - -```json -{ - "type": "Capability", - "value": { - "path": , - "address": "0x0", // as hex-encoded string with 0x prefix - "borrowType": , - } -} -``` - -### Example - -```json -{ - "type": "Capability", - "value": { - "path": { - "type": "Path", - "value": { - "domain": "public", - "identifier": "someInteger" - } - }, - "address": "0x1", - "borrowType": { - "kind": "Int" - } - } -} -``` - ---- - -## Functions - -```json -{ - "type": "Function", - "value": { - "functionType": - } -} -``` - -Function values can only be exported, they cannot be imported. - -### Example - -```json -{ - "type": "Function", - "value": { - "functionType": { - "kind": "Function", - "typeID": "(():Void)", - "parameters": [], - "return": { - "kind": "Void" - } - } - } -} -``` - ---- - -# Types - -## Simple Types - -These are basic types like `Int`, `String`, or `StoragePath`. - -```json -{ - "kind": "Any" | "AnyStruct" | "AnyResource" | "AnyStructAttachment" | "AnyResourceAttachment" | "Type" | - "Void" | "Never" | "Bool" | "String" | "Character" | - "Bytes" | "Address" | "Number" | "SignedNumber" | - "Integer" | "SignedInteger" | "FixedPoint" | - "SignedFixedPoint" | "Int" | "Int8" | "Int16" | - "Int32" | "Int64" | "Int128" | "Int256" | "UInt" | - "UInt8" | "UInt16" | "UInt32" | "UInt64" | "UInt128" | - "UInt256" | "Word8" | "Word16" | "Word32" | "Word64" | - "Fix64" | "UFix64" | "Path" | "CapabilityPath" | "StoragePath" | - "PublicPath" | "PrivatePath" | "AuthAccount" | "PublicAccount" | - "AuthAccount.Keys" | "PublicAccount.Keys" | "AuthAccount.Contracts" | - "PublicAccount.Contracts" | "DeployedContract" | "AccountKey" | "Block" -} -``` - -### Example - -```json -{ - "kind": "UInt8" -} -``` - ---- - -## Optional Types - -```json -{ - "kind": "Optional", - "type": -} -``` - -### Example - -```json -{ - "kind": "Optional", - "type": { - "kind": "String" - } -} -``` - ---- - -## Variable Sized Array Types - -```json -{ - "kind": "VariableSizedArray", - "type": -} -``` - -### Example - -```json -{ - "kind": "VariableSizedArray", - "type": { - "kind": "String" - } -} -``` - ---- - -## Constant Sized Array Types - -```json -{ - "kind": "ConstantSizedArray", - "type": , - "size": , -} -``` - -### Example - -```json -{ - "kind": "ConstantSizedArray", - "type": { - "kind": "String" - }, - "size":3 -} -``` - ---- - -## Dictionary Types - -```json -{ - "kind": "Dictionary", - "key": , - "value": -} -``` - -### Example - -```json -{ - "kind": "Dictionary", - "key": { - "kind": "String" - }, - "value": { - "kind": "UInt16" - }, -} -``` - ---- - -## Composite Types - -```json -{ - "kind": "Struct" | "Resource" | "Event" | "Contract" | "StructInterface" | "ResourceInterface" | "ContractInterface", - "type": "", // this field exists only to keep parity with the enum structure below; the value must be the empty string - "typeID": "", - "initializers": [ - , - - // ... - ], - "fields": [ - , - - // ... - ], -} -``` - -### Example - -```json -{ - "kind": "Resource", - "type": "", - "typeID": "0x3.GreatContract.GreatNFT", - "initializers":[ - [ - { - "label": "foo", - "id": "bar", - "type": { - "kind": "String" - } - } - ] - ], - "fields": [ - { - "id": "foo", - "type": { - "kind": "String" - } - } - ] -} -``` - ---- - -## Field Types - -```json -{ - "id": "", - "type": -} -``` - -### Example - -```json -{ - "id": "foo", - "type": { - "kind": "String" - } -} -``` - ---- - -## Parameter Types - -```json -{ - "label": "