Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add access top type to model inaccessible access for identity maps #3406

Merged
merged 10 commits into from
Jun 11, 2024
5 changes: 5 additions & 0 deletions runtime/ast/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ type PrimitiveAccess uint8

const (
AccessNotSpecified PrimitiveAccess = iota
AccessNone // "top" access, only used for mapping operations, not actually expressible in the language
AccessSelf
AccessContract
AccessAccount
Expand Down Expand Up @@ -256,6 +257,8 @@ func (a PrimitiveAccess) Keyword() string {
return "access(contract)"
case AccessPubSettableLegacy:
return "pub(set)"
case AccessNone:
return "inaccessible"
}

panic(errors.NewUnreachableError())
Expand All @@ -275,6 +278,8 @@ func (a PrimitiveAccess) Description() string {
return "contract"
case AccessPubSettableLegacy:
return "legacy public settable"
case AccessNone:
return "inaccessible"
}

panic(errors.NewUnreachableError())
Expand Down
15 changes: 8 additions & 7 deletions runtime/ast/primitiveaccess_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions runtime/interpreter/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,12 @@ func (d TypeDecoder) decodeStaticAuthorization() (Authorization, error) {
return nil, err
}
return UnauthorizedAccess, nil
case CBORTagInaccessibleStaticAuthorization:
err := d.decoder.DecodeNil()
if err != nil {
return nil, err
}
return InaccessibleAccess, nil
case CBORTagEntitlementMapStaticAuthorization:
typeID, err := d.decoder.DecodeString()
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions runtime/interpreter/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ const (
CBORTagUnauthorizedStaticAuthorization
CBORTagEntitlementMapStaticAuthorization
CBORTagEntitlementSetStaticAuthorization
CBORTagInaccessibleStaticAuthorization

_
_
_
_

CBORTagInclusiveRangeStaticType

// !!! *WARNING* !!!
Expand Down Expand Up @@ -1317,6 +1324,17 @@ func (t Unauthorized) Encode(e *cbor.StreamEncoder) error {
return e.EncodeNil()
}

func (t Inaccessible) Encode(e *cbor.StreamEncoder) error {
err := e.EncodeRawBytes([]byte{
// tag number
0xd8, CBORTagInaccessibleStaticAuthorization,
})
if err != nil {
return err
}
return e.EncodeNil()
}

func (a EntitlementMapAuthorization) Encode(e *cbor.StreamEncoder) error {
err := e.EncodeRawBytes([]byte{
// tag number
Expand Down
2 changes: 1 addition & 1 deletion runtime/interpreter/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3587,7 +3587,7 @@ func TestCBORTagValue(t *testing.T) {
t.Parallel()

t.Run("No new types added in between", func(t *testing.T) {
require.Equal(t, byte(226), byte(CBORTag_Count))
require.Equal(t, byte(231), byte(CBORTag_Count))
})
}

Expand Down
2 changes: 1 addition & 1 deletion runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4991,7 +4991,7 @@ func (interpreter *Interpreter) mapMemberValueAuthorization(

default:
var access sema.Access
if mappedAccess.Type == sema.IdentityType {
if mappedAccess.Type.IncludesIdentity {
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
access = sema.AllSupportedEntitlements(resultingType)
}

Expand Down
40 changes: 36 additions & 4 deletions runtime/interpreter/statictype.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,11 +643,11 @@ var FullyEntitledAccountAccess = ConvertSemaAccessToStaticAuthorization(nil, sem
func (Unauthorized) isAuthorization() {}

func (Unauthorized) String() string {
return ""
return "Unauthorized"
}

func (Unauthorized) MeteredString(_ common.MemoryGauge) string {
return ""
func (a Unauthorized) MeteredString(_ common.MemoryGauge) string {
return "Unauthorized"
}

func (Unauthorized) ID() TypeID {
Expand All @@ -659,6 +659,29 @@ func (Unauthorized) Equal(auth Authorization) bool {
return ok
}

type Inaccessible struct{}

var InaccessibleAccess Authorization = Inaccessible{}

func (Inaccessible) isAuthorization() {}

func (Inaccessible) String() string {
return "Inaccessible"
}

func (Inaccessible) MeteredString(_ common.MemoryGauge) string {
return "Inaccessible"
}

func (Inaccessible) ID() TypeID {
panic(errors.NewUnreachableError())
}

func (Inaccessible) Equal(auth Authorization) bool {
_, ok := auth.(Inaccessible)
return ok
}

type EntitlementSetAuthorization struct {
Entitlements *sema.TypeIDOrderedSet
SetKind sema.EntitlementSetKind
Expand Down Expand Up @@ -831,7 +854,10 @@ func (t *ReferenceStaticType) String() string {

func (t *ReferenceStaticType) MeteredString(memoryGauge common.MemoryGauge) string {
typeStr := t.ReferencedType.MeteredString(memoryGauge)
authString := t.Authorization.MeteredString(memoryGauge)
authString := ""
if !t.Authorization.Equal(InaccessibleAccess) && !t.Authorization.Equal(UnauthorizedAccess) {
authString = t.Authorization.MeteredString(memoryGauge)
}

common.UseMemory(memoryGauge, common.NewRawStringMemoryUsage(len(typeStr)+1+len(authString)))
return fmt.Sprintf("%s&%s", authString, typeStr)
Expand Down Expand Up @@ -1049,6 +1075,9 @@ func ConvertSemaAccessToStaticAuthorization(
if access.Equal(sema.UnauthorizedAccess) {
return UnauthorizedAccess
}
if access.Equal(sema.InaccessibleAccess) {
return InaccessibleAccess
}

case sema.EntitlementSetAccess:
var entitlements []common.TypeID
Expand Down Expand Up @@ -1124,6 +1153,9 @@ func ConvertStaticAuthorizationToSemaAccess(
case Unauthorized:
return sema.UnauthorizedAccess, nil

case Inaccessible:
return sema.InaccessibleAccess, nil

case EntitlementMapAuthorization:
entitlement, err := handler.GetEntitlementMapType(auth.TypeID)
if err != nil {
Expand Down
13 changes: 11 additions & 2 deletions runtime/sema/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ type EntitlementMapAccess struct {
Type *EntitlementMapType
domain EntitlementSetAccess
domainOnce sync.Once
codomain EntitlementSetAccess
codomain Access
codomainOnce sync.Once
images sync.Map
}
Expand Down Expand Up @@ -326,7 +326,13 @@ func (e *EntitlementMapAccess) PermitsAccess(other Access) bool {
// the input entitlement. It is only safe for `R` to give out these entitlements if it actually
// possesses them, so we require the initializing value to have every possible entitlement that may
// be produced by the map
//
// However, if the map is or includes the `Identity`, there is no possible set that is permitted by
// this map, since the theoretical codomain of the Identity map is infinite
case EntitlementSetAccess:
if e.Type.IncludesIdentity {
return false
}
return e.Codomain().PermitsAccess(otherAccess)
default:
return false
Expand All @@ -346,7 +352,7 @@ func (e *EntitlementMapAccess) Domain() EntitlementSetAccess {
return e.domain
}

func (e *EntitlementMapAccess) Codomain() EntitlementSetAccess {
func (e *EntitlementMapAccess) Codomain() Access {
e.codomainOnce.Do(func() {
codomain := common.MappedSliceWithNoDuplicates(
e.Type.Relations,
Expand Down Expand Up @@ -458,6 +464,9 @@ func (a PrimitiveAccess) Equal(other Access) bool {
}

func (a PrimitiveAccess) PermitsAccess(otherAccess Access) bool {
if a == InaccessibleAccess {
return otherAccess == InaccessibleAccess
}
if otherPrimitive, ok := otherAccess.(PrimitiveAccess); ok {
return ast.PrimitiveAccess(a) >= ast.PrimitiveAccess(otherPrimitive)
}
Expand Down
10 changes: 9 additions & 1 deletion runtime/sema/check_member_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,12 @@ func (checker *Checker) mapAccess(
// we could use this to then extract a `auth(Insert, Remove) &[T]` reference to that array by accessing `member`
// on an owned copy of `S`. As such, when in an assignment, we return the full codomain here as the "granted authorization"
// of the access expression, since the checker will later enforce that the incoming reference value is a subtype of that full codomain.
// However, if the map is or includes the `Identity` map, the theoretical codomain of that map is infinite, and so no reference can
// possibly be authorized enough to write to it
if checker.inAssignment {
if mappedAccess.Type.IncludesIdentity {
return true, InaccessibleAccess
}
return true, mappedAccess.Codomain()
}
return true, grantedAccess
Expand All @@ -492,7 +497,10 @@ func (checker *Checker) mapAccess(
return checker.mapAccess(mappedAccess, ty.Type, resultingType, accessRange)

default:
if mappedAccess.Type == IdentityType {
if mappedAccess.Type.IncludesIdentity {
if checker.inAssignment {
return true, InaccessibleAccess
}
access := AllSupportedEntitlements(resultingType)
if access != nil {
return true, access
Expand Down
13 changes: 9 additions & 4 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -6946,6 +6946,7 @@ var _ ValueIndexableType = &ReferenceType{}
var _ TypeIndexableType = &ReferenceType{}

var UnauthorizedAccess Access = PrimitiveAccess(ast.AccessAll)
var InaccessibleAccess Access = PrimitiveAccess(ast.AccessNone)

func NewReferenceType(
memoryGauge common.MemoryGauge,
Expand Down Expand Up @@ -9263,10 +9264,14 @@ func NewEntitlementRelation(
}

type EntitlementMapType struct {
Location common.Location
containerType Type
Identifier string
Relations []EntitlementRelation
Location common.Location
containerType Type
Identifier string
Relations []EntitlementRelation

// Whether this map type includes the special identity relation,
// which maps every input to itself. The `Identity` mapping itself
// is defined as the empty map type that includes the identity relation
IncludesIdentity bool
resolveInclusions sync.Once
}
Expand Down
Loading
Loading