From f400c355e08685c6c8c5510304e94728ea6a59f7 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:05:44 -0600 Subject: [PATCH 001/173] Bump fxamacker/cbor stream-mode branch to latest This is to allow CCF codec to use new StreamEncoder.Close() function provided by latest fxamacker/cbor. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1f8b1555f9..01b121e51c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/bits-and-blooms/bitset v1.2.2 github.com/bytecodealliance/wasmtime-go v0.40.0 github.com/c-bata/go-prompt v0.2.5 - github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f + github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c github.com/go-test/deep v1.0.5 github.com/k0kubun/pp v3.0.1+incompatible github.com/leanovate/gopter v0.2.9 diff --git a/go.sum b/go.sum index afa16862eb..bc6fd920c1 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f h1:dxTR4AaxCwuQv9LAVTAC2r1szlS+epeuPT5ClLKT6ZY= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= From 86cda98760c679d4676ea81a99c027e24a89b882 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:08:23 -0600 Subject: [PATCH 002/173] Create metered UFix64/Fix64 from uint64/int64 - added NewMeteredUFix64FromUint64 to create metered UFix64 from uint64. - added NewMeteredFix64FromInt64 to create metered Fix64 from int64. --- values.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/values.go b/values.go index ed4af69b8f..4b725728f8 100644 --- a/values.go +++ b/values.go @@ -1267,6 +1267,11 @@ func NewMeteredFix64(gauge common.MemoryGauge, constructor func() (string, error return NewFix64(value) } +func NewMeteredFix64FromInt64(gauge common.MemoryGauge, n int64) (Fix64, error) { + common.UseMemory(gauge, fix64MemoryUsage) + return Fix64(n), nil +} + func (Fix64) isValue() {} func (Fix64) Type() Type { @@ -1336,6 +1341,11 @@ func ParseUFix64(s string) (uint64, error) { return v.Uint64(), nil } +func NewMeteredUFix64FromUint64(gauge common.MemoryGauge, n uint64) (UFix64, error) { + common.UseMemory(gauge, ufix64MemoryUsage) + return UFix64(n), nil +} + func (UFix64) isValue() {} func (UFix64) Type() Type { From 0b621a7070da17dad8167c44a8d0fe7fb1779ef1 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 14:48:27 -0600 Subject: [PATCH 003/173] Add CCF codec implementating CCF Specs (RC1) Cadence Compact Format (CCF) is a data format designed for compact, efficient, and deterministic encoding of Cadence external values. CCF obsoletes JSON-Cadence Data Interchange Format (JCDIF) for use cases that do not require JSON. Unlike JCDIF, CCF Specifications explicitly defines requirements for - well-formed encodings - valid encodings - deterministic encodings CCF is hybrid data format. CCF-based messages can be: - fully self-describing or - partially self-describing. Both CCF modes are more compact than JSON-based messages. CCF-based protocols can send Cadence metadata just once for all messages of that type. Malformed data can be detected without Cadence metadata and without creating Cadence objects. CCF codec implements CCF Specification (RC1), which is temporarily at github.com/fxamacker/ccf_draft. The CCF specs will be hosted under github.com/onflow after it is updated, cleaned up, and reformatted. --- encoding/ccf/cache.go | 45 + encoding/ccf/ccf.go | 20 + encoding/ccf/ccf_type_id.go | 77 ++ encoding/ccf/consts.go | 144 +++ encoding/ccf/decode.go | 1859 +++++++++++++++++++++++++++++ encoding/ccf/decode_type.go | 543 +++++++++ encoding/ccf/decode_typedef.go | 469 ++++++++ encoding/ccf/encode.go | 1683 ++++++++++++++++++++++++++ encoding/ccf/encode_type.go | 510 ++++++++ encoding/ccf/encode_typedef.go | 242 ++++ encoding/ccf/simple_type_utils.go | 197 +++ encoding/ccf/sort.go | 207 ++++ encoding/ccf/traverse_value.go | 229 ++++ 13 files changed, 6225 insertions(+) create mode 100644 encoding/ccf/cache.go create mode 100644 encoding/ccf/ccf.go create mode 100644 encoding/ccf/ccf_type_id.go create mode 100644 encoding/ccf/consts.go create mode 100644 encoding/ccf/decode.go create mode 100644 encoding/ccf/decode_type.go create mode 100644 encoding/ccf/decode_typedef.go create mode 100644 encoding/ccf/encode.go create mode 100644 encoding/ccf/encode_type.go create mode 100644 encoding/ccf/encode_typedef.go create mode 100644 encoding/ccf/simple_type_utils.go create mode 100644 encoding/ccf/sort.go create mode 100644 encoding/ccf/traverse_value.go diff --git a/encoding/ccf/cache.go b/encoding/ccf/cache.go new file mode 100644 index 0000000000..951b5207ec --- /dev/null +++ b/encoding/ccf/cache.go @@ -0,0 +1,45 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "sort" + "sync" + + "github.com/onflow/cadence" +) + +// cachedSortedFieldIndex contains sorted field index of Cadence composite types. +var cachedSortedFieldIndex sync.Map // key: cadence.CompositeType, value: []int + +func getSortedFieldIndex(t cadence.CompositeType) []int { + if v, _ := cachedSortedFieldIndex.Load(t); v != nil { + return v.([]int) + } + + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fields in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(t.CompositeFields()) + + sort.Sort(sorter) + + cachedSortedFieldIndex.Store(t, sorter.indexes) + return sorter.indexes +} diff --git a/encoding/ccf/ccf.go b/encoding/ccf/ccf.go new file mode 100644 index 0000000000..660e80910b --- /dev/null +++ b/encoding/ccf/ccf.go @@ -0,0 +1,20 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf implements CCF specification +package ccf diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go new file mode 100644 index 0000000000..8b06735ef5 --- /dev/null +++ b/encoding/ccf/ccf_type_id.go @@ -0,0 +1,77 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "fmt" + "math/big" + + "github.com/onflow/cadence" +) + +// ccfTypeID represents CCF type ID. +type ccfTypeID uint64 + +func newCCFTypeID(b []byte) ccfTypeID { + return ccfTypeID(new(big.Int).SetBytes(b).Uint64()) +} + +func newCCFTypeIDFromUint64(i uint64) ccfTypeID { + return ccfTypeID(i) +} + +func (id ccfTypeID) Bytes() []byte { + return new(big.Int).SetUint64(uint64(id)).Bytes() +} + +func (id ccfTypeID) Equal(other ccfTypeID) bool { + return id == other +} + +// IMPORTANT: Don't use cadence.Type as map key because all Cadence composite/interface +// types are pointers, and different instance of the same type will be treated as +// different map key. +type ccfTypeIDByCadenceType map[string]ccfTypeID + +func (types ccfTypeIDByCadenceType) id(t cadence.Type) (ccfTypeID, error) { + id, ok := types[t.ID()] + if !ok { + return 0, fmt.Errorf("failed to get ccf id for cadence type %s", t.ID()) + } + return id, nil +} + +type cadenceTypeByCCFTypeID map[ccfTypeID]cadence.Type + +func (ids cadenceTypeByCCFTypeID) add(id ccfTypeID, typ cadence.Type) bool { + _, ok := ids[id] + if ok { + return false + } + ids[id] = typ + return true +} + +func (ids cadenceTypeByCCFTypeID) typ(id ccfTypeID) (cadence.Type, error) { + t, ok := ids[id] + if !ok { + return nil, fmt.Errorf("failed to get cadence type for ccf id %d", id) + } + return t, nil +} diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go new file mode 100644 index 0000000000..4a90425b41 --- /dev/null +++ b/encoding/ccf/consts.go @@ -0,0 +1,144 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +// CCF uses CBOR tag numbers 128-255, which are unassigned by [IANA] +// (https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). + +const ( + // CBOR tag numbers (128-135) for root objects (131-135 are reserved) + CBORTagTypeDef = 128 + iota + CBORTagTypeDefAndValue + CBORTagTypeAndValue + _ + _ + _ + _ + _ + + // CBOR tag numbers (136-183) for types + // inline types (145-159 are reserved) + CBORTagTypeRef + CBORTagSimpleType + CBORTagOptionalType + CBORTagVarsizedArrayType + CBORTagConstsizedArrayType + CBORTagDictType + CBORTagReferenceType + CBORTagRestrictedType + CBORTagCapabilityType + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // composite types (165-175 are reserved) + CBORTagStructType + CBORTagResourceType + CBORTagEventType + CBORTagContractType + CBORTagEnumType + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // interface types (179-183 are reserved) + CBORTagStructInterfaceType + CBORTagResourceInterfaceType + CBORTagContractInterfaceType + _ + _ + _ + _ + _ + + // CBOR tag numbers (184-231) for type value + // non-composite and non-interface type values (194-207 are reserved) + CBORTagTypeValueRef + CBORTagSimpleTypeValue + CBORTagOptionalTypeValue + CBORTagVarsizedArrayTypeValue + CBORTagConstsizedArrayTypeValue + CBORTagDictTypeValue + CBORTagReferenceTypeValue + CBORTagRestrictedTypeValue + CBORTagCapabilityTypeValue + CBORTagFunctionTypeValue + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // composite type values (213-223 are reserved) + CBORTagStructTypeValue + CBORTagResourceTypeValue + CBORTagEventTypeValue + CBORTagContractTypeValue + CBORTagEnumTypeValue + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // interface type values (227-231 are reserved) + CBORTagStructInterfaceTypeValue + CBORTagResourceInterfaceTypeValue + CBORTagContractInterfaceTypeValue + _ + _ + _ + _ + _ +) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go new file mode 100644 index 0000000000..98d2544be2 --- /dev/null +++ b/encoding/ccf/decode.go @@ -0,0 +1,1859 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "fmt" + "math" + "math/big" + + "github.com/fxamacker/cbor/v2" + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" +) + +// CBORDecMode +// +// See https://github.com/fxamacker/cbor: +// "For best performance, reuse EncMode and DecMode after creating them." +var CBORDecMode = func() cbor.DecMode { + decMode, err := cbor.DecOptions{ + IntDec: cbor.IntDecConvertNone, + MaxArrayElements: math.MaxInt64, + MaxMapPairs: math.MaxInt64, + MaxNestedLevels: math.MaxInt16, + }.DecMode() + if err != nil { + panic(err) + } + return decMode +}() + +// A Decoder decodes CCF-encoded representations of Cadence values. +type Decoder struct { + // CCF codec uses CBOR codec under the hood. + dec *cbor.StreamDecoder + gauge common.MemoryGauge +} + +// Decode returns a Cadence value decoded from its CCF-encoded representation. +// +// This function returns an error if the bytes represent CCF that is malformed, +// invalid, or does not comply with requirements in the CCF specification. +func Decode(gauge common.MemoryGauge, b []byte) (cadence.Value, error) { + dec := NewDecoder(gauge, b) + + v, err := dec.Decode() + if err != nil { + return nil, err + } + + return v, nil +} + +// NewDecoder initializes a Decoder that will decode CCF-encoded bytes from the +// given bytes. +func NewDecoder(gauge common.MemoryGauge, b []byte) *Decoder { + // NOTE: encoded data is not copied by decoder. + // CCF codec uses CBOR codec under the hood. + return &Decoder{ + dec: CBORDecMode.NewByteStreamDecoder(b), + gauge: gauge, + } +} + +// Decode reads CCF-encoded bytes and decodes them to a Cadence value. +// +// This function returns an error if the bytes represent CCF that is malformed, +// invalid, or does not comply with requirements in the CCF specification. +func (d *Decoder) Decode() (value cadence.Value, err error) { + // Capture panics that occur during decoding. + defer func() { + if r := recover(); r != nil { + panicErr, isError := r.(error) + if !isError { + panic(r) + } + + err = errors.NewDefaultUserError("failed to decode value: %w", panicErr) + } + }() + + // Decode top level message. + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return nil, err + } + + switch tagNum { + case CBORTagTypeDefAndValue: + // Decode ccf-type-and-value-message. + return d.decodeTypeDefAndValue() + + case CBORTagTypeAndValue: + // Decode ccf-typedef-and-value-message. + return d.decodeTypeAndValue(cadenceTypeByCCFTypeID{}) + + default: + return nil, fmt.Errorf( + "failed to decode top level message: expect CBOR tag num %d or %d, got %d", + CBORTagTypeDefAndValue, + CBORTagTypeAndValue, + tagNum, + ) + } +} + +// decodeTypeDefAndValue decodes encoded ccf-typedef-and-value-message +// without tag number as +// language=CDDL +// ccf-typedef-and-value-message = +// +// ; cbor-tag-typedef-and-value +// #6.129([ +// typedef: composite-typedef, +// type-and-value: inline-type-and-value +// ]) +func (d *Decoder) decodeTypeDefAndValue() (cadence.Value, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: typedef + types, err := d.decodeTypeDefs() + if err != nil { + return nil, err + } + + // element 1: type and value + return d.decodeTypeAndValue(types) +} + +// decodeTypeAndValue decodes encoded ccf-type-and-value-message +// without tag number as +// language=CDDL +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130(inline-type-and-value) +// +// inline-type-and-value = [ +// +// type: inline-type, +// value: value, +// +// ] +func (d *Decoder) decodeTypeAndValue(types cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: inline-type + t, err := d.decodeInlineType(types) + if err != nil { + return nil, err + } + + // element 1: value + return d.decodeValue(t, types) +} + +// decodeValue decodes encoded value of type t. +// language=CDDL +// value = +// +// ccf-type-and-value-message +// / simple-value +// / optional-value +// / array-value +// / dict-value +// / composite-value +// / path-value +// / capability-value +// / function-value +// / type-value +// +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130([ +// type: inline-type, +// value: value +// ]) +// +// simple-value = +// +// void-value +// / bool-value +// / character-value +// / string-value +// / address-value +// / uint-value +// / uint8-value +// / uint16-value +// / uint32-value +// / uint64-value +// / uint128-value +// / uint256-value +// / int-value +// / int8-value +// / int16-value +// / int32-value +// / int64-value +// / int128-value +// / int256-value +// / word8-value +// / word16-value +// / word32-value +// / word64-value +// / fix64-value +// / ufix64-value +func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // 'inline-type-and-value MUST NOT be used when type can be omitted as described + // in "Cadence Types and Values Encoding".' + // If type t for the value to be decoded is a concrete type (e.g. IntType), + // value MUST NOT be ccf-type-and-value-message. + + switch typ := t.(type) { + case cadence.VoidType: + return d.decodeVoid() + + case *cadence.OptionalType: + return d.decodeOptional(typ, types) + + case cadence.BoolType: + return d.decodeBool() + + case cadence.CharacterType: + return d.decodeCharacter() + + case cadence.StringType: + return d.decodeString() + + case cadence.AddressType: + return d.decodeAddress() + + case cadence.IntType: + return d.decodeInt() + + case cadence.Int8Type: + return d.decodeInt8() + + case cadence.Int16Type: + return d.decodeInt16() + + case cadence.Int32Type: + return d.decodeInt32() + + case cadence.Int64Type: + return d.decodeInt64() + + case cadence.Int128Type: + return d.decodeInt128() + + case cadence.Int256Type: + return d.decodeInt256() + + case cadence.UIntType: + return d.decodeUInt() + + case cadence.UInt8Type: + return d.decodeUInt8() + + case cadence.UInt16Type: + return d.decodeUInt16() + + case cadence.UInt32Type: + return d.decodeUInt32() + + case cadence.UInt64Type: + return d.decodeUInt64() + + case cadence.UInt128Type: + return d.decodeUInt128() + + case cadence.UInt256Type: + return d.decodeUInt256() + + case cadence.Word8Type: + return d.decodeWord8() + + case cadence.Word16Type: + return d.decodeWord16() + + case cadence.Word32Type: + return d.decodeWord32() + + case cadence.Word64Type: + return d.decodeWord64() + + case cadence.Fix64Type: + return d.decodeFix64() + + case cadence.UFix64Type: + return d.decodeUFix64() + + case *cadence.VariableSizedArrayType: + return d.decodeArray(typ, false, 0, types) + + case *cadence.ConstantSizedArrayType: + return d.decodeArray(typ, true, typ.Size, types) + + case *cadence.DictionaryType: + return d.decodeDictionary(typ, types) + + case *cadence.ResourceType: + return d.decodeResource(typ, types) + + case *cadence.StructType: + return d.decodeStruct(typ, types) + + case *cadence.EventType: + return d.decodeEvent(typ, types) + + case *cadence.ContractType: + return d.decodeContract(typ, types) + + case cadence.PathType: + return d.decodePath() + + case cadence.MetaType: + return d.decodeTypeValue() + + case *cadence.CapabilityType: + return d.decodeCapability(typ) + + case *cadence.EnumType: + return d.decodeEnum(typ, types) + + default: + err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) + if err != nil { + return nil, fmt.Errorf("failed to decode value of type %s %T: %w", typ, typ, err) + } + // Decode ccf-type-and-value-message. + return d.decodeTypeAndValue(types) + } +} + +// decodeVoid decodes encoded void-value as +// language=CDDL +// void-value = nil +func (d *Decoder) decodeVoid() (cadence.Value, error) { + err := d.dec.DecodeNil() + if err != nil { + return nil, err + } + return cadence.NewMeteredVoid(d.gauge), nil +} + +// decodeBool decodes encoded bool-value as +// language=CDDL +// bool-value = bool +func (d *Decoder) decodeBool() (cadence.Value, error) { + b, err := d.dec.DecodeBool() + if err != nil { + return nil, err + } + return cadence.NewMeteredBool(d.gauge, b), nil +} + +// decodeCharacter decodes encoded character-value as +// language=CDDL +// character-value = tstr +func (d *Decoder) decodeCharacter() (cadence.Value, error) { + s, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + return cadence.NewMeteredCharacter( + d.gauge, + common.NewCadenceCharacterMemoryUsage(len(s)), + func() string { + return s + }) +} + +// decodeString decodes encoded string-value as +// language=CDDL +// string-value = tstr +// NOTE: invalid UTF-8 is rejected. +func (d *Decoder) decodeString() (cadence.Value, error) { + s, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + return cadence.NewMeteredString( + d.gauge, + common.NewCadenceStringMemoryUsage(len(s)), + func() string { + return s + }, + ) +} + +// decodeAddress decodes address-value as +// language=CDDL +// address-value = bstr .size 8 +func (d *Decoder) decodeAddress() (cadence.Value, error) { + b, err := d.dec.DecodeBytes() + if err != nil { + return nil, err + } + if len(b) != 8 { + return nil, fmt.Errorf("failed to decode address-value: expect 8 bytes, got %d bytes", len(b)) + } + return cadence.BytesToMeteredAddress(d.gauge, b), nil +} + +// decodeInt decodes int-value as +// language=CDDL +// int-value = bigint +func (d *Decoder) decodeInt() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + + return cadence.NewMeteredIntFromBig( + d.gauge, + common.NewCadenceIntMemoryUsage( + common.BigIntByteLength(bigInt), + ), + func() *big.Int { + return bigInt + }, + ), nil +} + +// decodeInt8 decodes int8-value as +// language=CDDL +// int8-value = (int .ge -128) .le 127 +func (d *Decoder) decodeInt8() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt8 || i > math.MaxInt8 { + return nil, fmt.Errorf( + "failed to decode int8-value: %d is outside range of Int8 [%d, %d]", + i, + math.MaxInt8, + math.MaxInt8, + ) + } + return cadence.NewMeteredInt8(d.gauge, int8(i)), nil +} + +// decodeInt16 decodes int16-value as +// language=CDDL +// int16-value = (int .ge -32768) .le 32767 +func (d *Decoder) decodeInt16() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt16 || i > math.MaxInt16 { + return nil, fmt.Errorf( + "failed to decode int16-value: %d is outside range of Int16 [%d, %d]", + i, + math.MinInt16, + math.MaxInt16, + ) + } + return cadence.NewMeteredInt16(d.gauge, int16(i)), nil +} + +// decodeInt32 decodes int32-value as +// language=CDDL +// int32-value = (int .ge -2147483648) .le 2147483647 +func (d *Decoder) decodeInt32() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt32 || i > math.MaxInt32 { + return nil, fmt.Errorf( + "failed to decode int32-value: %d is outside range of Int32 [%d, %d]", + i, + math.MinInt32, + math.MaxInt32, + ) + } + return cadence.NewMeteredInt32(d.gauge, int32(i)), nil +} + +// decodeInt64 decodes int64-value as +// language=CDDL +// int64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (d *Decoder) decodeInt64() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + return cadence.NewMeteredInt64(d.gauge, i), nil +} + +// decodeInt128 decodes int128-value as +// language=CDDL +// int128-value = bigint +func (d *Decoder) decodeInt128() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + return cadence.NewMeteredInt128FromBig( + d.gauge, + func() *big.Int { + return bigInt + }, + ) +} + +// decodeInt256 decodes int256-value as +// language=CDDL +// int256-value = bigint +func (d *Decoder) decodeInt256() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + return cadence.NewMeteredInt256FromBig( + d.gauge, + func() *big.Int { + return bigInt + }, + ) +} + +// decodeUInt decodes uint-value as +// language=CDDL +// uint-value = bigint .ge 0 +func (d *Decoder) decodeUInt() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + if bigInt.Sign() < 0 { + return nil, fmt.Errorf( + "failed to decode uint-value: %s is negative", + bigInt.String(), + ) + } + return cadence.NewMeteredUIntFromBig( + d.gauge, + common.NewCadenceIntMemoryUsage( + common.BigIntByteLength(bigInt), + ), + func() *big.Int { + return bigInt + }, + ) +} + +// decodeUInt8 decodes uint8-value as +// language=CDDL +// uint8-value = uint .le 255 +func (d *Decoder) decodeUInt8() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint8 { + return nil, fmt.Errorf( + "failed to decode uint8-value: %d is outside range of Uint8 [0, %d]", + i, + math.MaxUint8, + ) + } + return cadence.NewMeteredUInt8(d.gauge, uint8(i)), nil +} + +// decodeUInt16 decodes uint16-value as +// language=CDDL +// uint16-value = uint .le 65535 +func (d *Decoder) decodeUInt16() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint16 { + return nil, fmt.Errorf( + "failed to decode uint16-value: %d is outside range of Uint16 [0, %d]", + i, + math.MaxUint16, + ) + } + return cadence.NewMeteredUInt16(d.gauge, uint16(i)), nil +} + +// decodeUInt32 decodes uint32-value as +// language=CDDL +// uint32-value = uint .le 4294967295 +func (d *Decoder) decodeUInt32() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint32 { + return nil, fmt.Errorf( + "failed to decode uint32-value: %d is outside range of Uint32 [0, %d]", + i, + math.MaxUint32, + ) + } + return cadence.NewMeteredUInt32(d.gauge, uint32(i)), nil +} + +// decodeUInt64 decodes uint64-value as +// language=CDDL +// uint64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeUInt64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredUInt64(d.gauge, i), nil +} + +// decodeUInt128 decodes uint128-value as +// language=CDDL +// uint128-value = bigint .ge 0 +func (d *Decoder) decodeUInt128() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + if bigInt.Sign() < 0 { + return nil, fmt.Errorf( + "failed to decode uint128-value: %s is negative", + bigInt.String(), + ) + } + return cadence.NewMeteredUInt128FromBig( + d.gauge, + func() *big.Int { + return bigInt + }, + ) +} + +// decodeUInt256 decodes uint256-value as +// language=CDDL +// uint256-value = bigint .ge 0 +func (d *Decoder) decodeUInt256() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + if bigInt.Sign() < 0 { + return nil, fmt.Errorf( + "failed to decode uint256-value: %s is negative", + bigInt.String(), + ) + } + return cadence.NewMeteredUInt256FromBig( + d.gauge, + func() *big.Int { + return bigInt + }, + ) +} + +// decodeWord8 decodes word8-value as +// language=CDDL +// word8-value = uint .le 255 +func (d *Decoder) decodeWord8() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint8 { + return nil, fmt.Errorf( + "failed to decode word8-value: %d is outside range of Word8 [0, %d]", + i, + math.MaxUint8, + ) + } + return cadence.NewMeteredWord8(d.gauge, uint8(i)), nil +} + +// decodeWord16 decodes word16-value as +// language=CDDL +// word16-value = uint .le 65535 +func (d *Decoder) decodeWord16() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint16 { + return nil, fmt.Errorf( + "failed to decode word16-value: %d is outside range of Word16 [0, %d]", + i, + math.MaxUint16, + ) + } + return cadence.NewMeteredWord16(d.gauge, uint16(i)), nil +} + +// decodeWord32 decodes word32-value as +// language=CDDL +// word32-value = uint .le 4294967295 +func (d *Decoder) decodeWord32() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint32 { + return nil, fmt.Errorf( + "failed to decode word32-value: %d is outside range of Word32 [0, %d]", + i, + math.MaxUint32, + ) + } + return cadence.NewMeteredWord32(d.gauge, uint32(i)), nil +} + +// decodeWord64 decodes word64-value as +// language=CDDL +// word64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeWord64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredWord64(d.gauge, i), nil +} + +// decodeFix64 decodes fix64-value as +// language=CDDL +// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (d *Decoder) decodeFix64() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + return cadence.NewMeteredFix64FromInt64(d.gauge, i) +} + +// decodeUFix64 decodes ufix64-value as +// language=CDDL +// ufix64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeUFix64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredUFix64FromUint64(d.gauge, i) +} + +// decodeOptional decodes encoded optional-value as +// language=CDDL +// optional-value = nil / value +func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Peek ahead for next CBOR data item type + nextType, err := d.dec.NextType() + if err != nil { + return nil, err + } + + switch nextType { + case cbor.NilType: + // Decode nil. + err := d.dec.DecodeNil() + if err != nil { + return nil, err + } + return newNilOptionalValue(d.gauge, typ), nil + + default: + // Decode value. + value, err := d.decodeValue(typ.Type, types) + if err != nil { + return nil, err + } + return cadence.NewMeteredOptional(d.gauge, value), nil + } +} + +// decodeArray decodes encoded array-value as +// language=CDDL +// array-value = [* value] +func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array length. + n, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if hasKnownSize && uint64(knownSize) != n { + return nil, fmt.Errorf( + "failed to decode array-value: expect %d elements, got %d elements", + knownSize, + n, + ) + } + + elementType := typ.Element() + + values := make([]cadence.Value, n) + for i := 0; i < int(n); i++ { + // Decode value. + element, err := d.decodeValue(elementType, types) + if err != nil { + return nil, err + } + values[i] = element + } + + v, err := cadence.NewMeteredArray( + d.gauge, + len(values), + func() ([]cadence.Value, error) { + return values, nil + }, + ) + if err != nil { + return nil, err + } + + return v.WithType(typ), nil +} + +// decodeDictionary decodes encoded dict-value as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array length. + n, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + // Check if number of elements is even. + if n%2 != 0 { + return nil, fmt.Errorf("failed to decode dictionary, expect even number of elements, got %d elements", n) + } + + // previousKeyRawBytes is used to determine if dictionary keys are sorted + var previousKeyRawBytes []byte + + pairCount := n / 2 + pairs := make([]cadence.KeyValuePair, pairCount) + for i := 0; i < int(pairCount); i++ { + // element i: key + + // Decode key as raw bytes to check that key pairs are sorted by key. + keyRawBytes, err := d.dec.DecodeRawBytes() + if err != nil { + return nil, err + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "dict-value key-value pairs MUST be sorted by key." + if !bytesAreSortedBytewise(previousKeyRawBytes, keyRawBytes) { + return nil, fmt.Errorf("dictionary keys are not sorted") + } + + previousKeyRawBytes = keyRawBytes + + // decode key from raw bytes + keyDecoder := NewDecoder(d.gauge, keyRawBytes) + key, err := keyDecoder.decodeValue(typ.KeyType, types) + if err != nil { + return nil, err + } + + // element i+1: value + element, err := d.decodeValue(typ.ElementType, types) + if err != nil { + return nil, err + } + + pairs[i] = cadence.NewMeteredKeyValuePair(d.gauge, key, element) + } + + value, err := cadence.NewMeteredDictionary( + d.gauge, + len(pairs), + func() ([]cadence.KeyValuePair, error) { + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "Keys MUST be unique in dict-value. Decoders are not always required to check + // for duplicate dictionary keys. In some cases, checking for duplicate dictionary + // key is not necessary or it may be delegated to the application." + // + // Here, decoder doesn't check uniqueness of dictionary keys + // because checking is delegated (entrusted) to Cadence runtime. + return pairs, nil + }, + ) + if err != nil { + return nil, err + } + + return value.WithType(typ), nil +} + +// decodeComposite decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types cadenceTypeByCCFTypeID) ([]cadence.Value, error) { + fieldCount := len(fieldTypes) + + // Decode number of fields. + err := decodeCBORArrayWithKnownSize(d.dec, uint64(fieldCount)) + if err != nil { + return nil, err + } + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceField, + Amount: uint64(fieldCount), + }) + + fieldValues := make([]cadence.Value, fieldCount) + + for i := 0; i < fieldCount; i++ { + // Decode field. + field, err := d.decodeValue(fieldTypes[i].Type, types) + if err != nil { + return nil, err + } + fieldValues[i] = field + } + + return fieldValues, nil +} + +// decodeStruct decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeStruct(typ *cadence.StructType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredStruct( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeResource decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeResource(typ *cadence.ResourceType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + resource, err := cadence.NewMeteredResource( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return resource.WithType(typ), nil +} + +// decodeEvent decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeEvent(typ *cadence.EventType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredEvent( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeContract decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeContract(typ *cadence.ContractType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredContract( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeEnum decodes encoded composite-value as +// language=CDDL +// composite-value = [+ (field: value)] +func (d *Decoder) decodeEnum(typ *cadence.EnumType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredEnum( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodePath decodes path-value as +// language=CDDL +// path-value = [ +// +// domain: uint, +// identifier: tstr, +// +// ] +func (d *Decoder) decodePath() (cadence.Value, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // Decode domain. + pathDomain, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + // Get domain identifier. + // Identifier() panics if pathDomain is invalid. + domain := common.PathDomain(pathDomain).Identifier() + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindRawString, + // No need to add 1 to account for empty string: string is metered in Path struct. + Amount: uint64(len(domain)), + }) + + // Decode identifer + identifier, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindRawString, + // No need to add 1 to account for empty string: string is metered in Path struct. + Amount: uint64(len(identifier)), + }) + + return cadence.NewMeteredPath(d.gauge, domain, identifier), nil +} + +// decodeCapability decodes encoded capability-value as +// language=CDDL +// capability-value = [ +// +// address: address-value, +// path: path-value +// +// ] +func (d *Decoder) decodeCapability(typ *cadence.CapabilityType) (cadence.Value, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // Decode address. + address, err := d.decodeAddress() + if err != nil { + return nil, err + } + + // Decode path. + path, err := d.decodePath() + if err != nil { + return nil, err + } + + return cadence.NewMeteredStorageCapability( + d.gauge, + path.(cadence.Path), + address.(cadence.Address), + typ.BorrowType), nil +} + +// decodeTypeValue decodes encoded type-value. +// See _decodeTypeValue() for details. +func (d *Decoder) decodeTypeValue() (cadence.Value, error) { + t, err := d._decodeTypeValue(cadenceTypeByCCFTypeID{}) + if err != nil { + return nil, err + } + return cadence.NewMeteredTypeValue(d.gauge, t), nil +} + +// _decodeTypeValue decodes encoded type-value as +// language=CDDL +// type-value = +// +// nil +// / simple-type-value +// / optional-type-value +// / varsized-array-type-value +// / constsized-array-type-value +// / dict-type-value +// / struct-type-value +// / resource-type-value +// / contract-type-value +// / event-type-value +// / enum-type-value +// / struct-interface-type-value +// / resource-interface-type-value +// / contract-interface-type-value +// / function-type-value +// / reference-type-value +// / restricted-type-value +// / capability-type-value +// / type-value-ref +func (d *Decoder) _decodeTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + // Decode tag number. + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + if _, ok := err.(*cbor.WrongTypeError); ok { + // Decode nil. + err = d.dec.DecodeNil() + return nil, err + } + return nil, err + } + + switch tagNum { + + case CBORTagTypeValueRef: + return d.decodeTypeRef(visited) + + case CBORTagSimpleTypeValue: + return d.decodeSimpleTypeID() + + case CBORTagOptionalTypeValue: + return d.decodeOptionalType(visited, d._decodeTypeValue) + + case CBORTagVarsizedArrayTypeValue: + return d.decodeVarSizedArrayType(visited, d._decodeTypeValue) + + case CBORTagConstsizedArrayTypeValue: + return d.decodeConstantSizedArrayType(visited, d._decodeTypeValue) + + case CBORTagDictTypeValue: + return d.decodeDictType(visited, d._decodeTypeValue) + + case CBORTagCapabilityTypeValue: + return d.decodeCapabilityType(visited, d._decodeTypeValue) + + case CBORTagReferenceTypeValue: + return d.decodeReferenceType(visited, d._decodeTypeValue) + + case CBORTagRestrictedTypeValue: + return d.decodeRestrictedType(visited, d._decodeTypeValue) + + case CBORTagFunctionTypeValue: + return d.decodeFunctionTypeValue(visited) + + case CBORTagStructTypeValue: + return d.decodeStructTypeValue(visited) + + case CBORTagResourceTypeValue: + return d.decodeResourceTypeValue(visited) + + case CBORTagEventTypeValue: + return d.decodeEventTypeValue(visited) + + case CBORTagContractTypeValue: + return d.decodeContractTypeValue(visited) + + case CBORTagEnumTypeValue: + return d.decodeEnumTypeValue(visited) + + case CBORTagStructInterfaceTypeValue: + return d.decodeStructInterfaceTypeValue(visited) + + case CBORTagResourceInterfaceTypeValue: + return d.decodeResourceInterfaceTypeValue(visited) + + case CBORTagContractInterfaceTypeValue: + return d.decodeContractInterfaceTypeValue(visited) + + default: + return nil, fmt.Errorf("failed to decode type value: unexpected CBOR tag number %d", tagNum) + } +} + +// decodeStructTypeValue decodes struct-type-value as +// language=CDDL +// struct-type-value = +// +// ; cbor-tag-struct-type-value +// #6.208(composite-type-value) +func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode struct type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredStructType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeResourceTypeValue decodes resource-type-value as +// language=CDDL +// resource-type-value = +// +// ; cbor-tag-resource-type-value +// #6.209(composite-type-value) +func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode resource type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredResourceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeEventTypeValue decodes event-type-value as +// language=CDDL +// event-type-value = +// +// ; cbor-tag-event-type-value +// #6.210(composite-type-value) +func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode event type value, expect nil type, got %s type", + typ.ID(), + ) + } + if len(inits) != 1 { + return nil, fmt.Errorf( + "failed to decode event type value, expect 0 or 1 initialization, got %d", + len(inits), + ) + } + return cadence.NewMeteredEventType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits[0], + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeContractTypeValue decodes contract-type-value as +// language=CDDL +// contract-type-value = +// +// ; cbor-tag-contract-type-value +// #6.211(composite-type-value) +func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode contract type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredContractType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeEnumTypeValue decodes enum-type-value as +// language=CDDL +// enum-type-value = +// +// ; cbor-tag-enum-type-value +// #6.212(composite-type-value) +func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + return cadence.NewMeteredEnumType( + d.gauge, + location, + qualifiedIdentifier, + typ, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeStructInterfaceTypeValue decodes struct-inteface-type-value as +// language=CDDL +// struct-interface-type-value = +// +// ; cbor-tag-struct-interface-type-value +// #6.224(composite-type-value) +func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode struct interface type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredStructInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeResourceInterfaceTypeValue decodes resource-inteface-type-value as +// language=CDDL +// resource-interface-type-value = +// +// ; cbor-tag-resource-interface-type-value +// #6.225(composite-type-value) +func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode resource interface type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredResourceInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeContractInterfaceTypeValue decodes contract-inteface-type-value as +// language=CDDL +// contract-interface-type-value = +// +// ; cbor-tag-contract-interface-type-value +// #6.226(composite-type-value) +func (d *Decoder) decodeContractInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "failed to decode contract interface type value, expect nil type, got %s type", + typ.ID(), + ) + } + return cadence.NewMeteredContractInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + inits, + ), nil + } + return d.decodeCompositeTypeValue(visited, ctr) +} + +type compositeTypeConstructor func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + inits [][]cadence.Parameter, +) (cadence.Type, error) + +type compositeTypeValue struct { + ccfID ccfTypeID + location common.Location + identifier string + typ cadence.Type + rawField []byte + initializerTypes [][]cadence.Parameter +} + +// decodeCompositeTypeValue decodes composite-type-value. +// See _decodeCompositeTypeValue for details. +func (d *Decoder) decodeCompositeTypeValue( + visited cadenceTypeByCCFTypeID, + constructor compositeTypeConstructor, +) (cadence.Type, error) { + compTypeValue, err := d._decodeCompositeTypeValue(visited) + if err != nil { + return nil, err + } + + compositeType, err := constructor( + compTypeValue.location, + compTypeValue.identifier, + compTypeValue.typ, + compTypeValue.initializerTypes, + ) + if err != nil { + return nil, err + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.id MUST be identical to the zero-based encoding order type-value." + if compTypeValue.ccfID != newCCFTypeIDFromUint64(uint64(len(visited))) { + return nil, fmt.Errorf( + "ccf id %d in encoded composite type value isn't identical to the zero-based encoding order type-value", + compTypeValue.ccfID, + ) + } + + newType := visited.add(compTypeValue.ccfID, compositeType) + if !newType { + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.id MUST be unique in the same composite-type-value data item." + return nil, fmt.Errorf("duplicate ccf id %d in encoded composite type value", compTypeValue.ccfID) + } + + // Decode fields after type is resolved to handle recursive types. + dec := NewDecoder(d.gauge, compTypeValue.rawField) + fields, err := dec.decodeCompositeFields(visited, dec._decodeTypeValue) + if err != nil { + return nil, err + } + + switch compositeType := compositeType.(type) { + case cadence.CompositeType: + compositeType.SetCompositeFields(fields) + case cadence.InterfaceType: + compositeType.SetInterfaceFields(fields) + } + + return compositeType, nil +} + +// _decodeCompositeTypeValue decodes composite-type-value as +// language=CDDL +// composite-type-value = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// ; type is only used by enum type value +// type: nil / type-value, +// fields: [ +// + [ +// name: tstr, +// type: type-value +// ] +// ] +// initializers: [ +// * [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +// +// ] +func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (compositeTypeValue, error) { + // Decode array of length 5 + err := decodeCBORArrayWithKnownSize(d.dec, 5) + if err != nil { + return compositeTypeValue{}, err + } + + // element 0: id (used to lookup repeated or recursive types) + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return compositeTypeValue{}, err + } + + // element 1: cadence-type-id + _, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return compositeTypeValue{}, err + } + + // element 2: type (only used by enum type value) + typ, err := d._decodeTypeValue(visited) + if err != nil { + return compositeTypeValue{}, err + } + + // element 3: fields + rawField, err := d.dec.DecodeRawBytes() + if err != nil { + return compositeTypeValue{}, err + } + + // element 4: initializers + initializerTypes, err := d.decodeInitializerTypeValues(visited) + if err != nil { + return compositeTypeValue{}, err + } + + return compositeTypeValue{ccfID, location, identifier, typ, rawField, initializerTypes}, nil +} + +// decodeInitializerTypeValues decodes composite initializers as +// language=CDDL +// +// initializers: [ +// * [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +func (d *Decoder) decodeInitializerTypeValues(visited cadenceTypeByCCFTypeID) ([][]cadence.Parameter, error) { + // Decode number of initializers. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + // Unmetered because this is created as an array of nil arrays, not Parameter structs. + initializerTypes := make([][]cadence.Parameter, count) + for i := 0; i < int(count); i++ { + initializerTypes[i], err = d.decodeParameterTypeValues(visited) + if err != nil { + return nil, err + } + } + + return initializerTypes, nil +} + +// decodeParameterTypeValues decodes composite initializer parameter types as +// language=CDDL +// +// [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]cadence.Parameter, error) { + // Decode number of paramaters + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + parameterTypes := make([]cadence.Parameter, count) + parameterLabels := make(map[string]struct{}, count) + parameterIdentifiers := make(map[string]struct{}, count) + var previousParameterIdentifier string + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceParameter, + Amount: uint64(count), + }) + + for i := 0; i < int(count); i++ { + // Decode parameter. + param, err := d.decodeParameterTypeValue(visited) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "All parameter lists MUST have unique identifier" + if _, ok := parameterLabels[param.Label]; ok { + return nil, fmt.Errorf("found duplicate parameter label %s", param.Label) + } + + if _, ok := parameterIdentifiers[param.Identifier]; ok { + return nil, fmt.Errorf("found duplicate parameter identifier %s", param.Identifier) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.initializers MUST be sorted by identifier." + if !stringsAreSortedBytewise(previousParameterIdentifier, param.Identifier) { + return nil, fmt.Errorf("parameter identifiers are not sorted") + } + + parameterLabels[param.Label] = struct{}{} + parameterIdentifiers[param.Identifier] = struct{}{} + previousParameterIdentifier = param.Identifier + + parameterTypes[i] = param + } + + return parameterTypes, nil +} + +// decodeParameterTypeValue decodes composite initializer parameter as +// language=CDDL +// +// [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Parameter, error) { + // Decode array head of length 3 + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return cadence.Parameter{}, err + } + + // element 0: label + label, err := d.dec.DecodeString() + if err != nil { + return cadence.Parameter{}, err + } + + // element 1: identifier + identifier, err := d.dec.DecodeString() + if err != nil { + return cadence.Parameter{}, err + } + + // element 2: type + t, err := d._decodeTypeValue(visited) + if err != nil { + return cadence.Parameter{}, err + } + + // Unmetered because decodeParamTypeValue is metered in decodeParamTypeValues and called nowhere else + // Type is metered. + return cadence.NewParameter(label, identifier, t), nil +} + +// decodeFunctionTypeValue decodes encoded function-value as +// language=CDDL +// function-value = [ +// +// cadence-type-id: cadence-type-id, +// parameters: [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// return-type: type-value +// +// ] +func (d *Decoder) decodeFunctionTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { + // Decode array head of length 3 + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return nil, err + } + + // element 0: cadence-type-id + typeID, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + // element 1: parameters + parameters, err := d.decodeParameterTypeValues(visited) + if err != nil { + return nil, err + } + + // element 2: return-type + returnType, err := d._decodeTypeValue(visited) + if err != nil { + return nil, err + } + + return cadence.NewMeteredFunctionType( + d.gauge, + "", + parameters, + returnType, + ).WithID(typeID), nil +} + +func decodeCBORArrayWithKnownSize(dec *cbor.StreamDecoder, n uint64) error { + c, err := dec.DecodeArrayHead() + if err != nil { + return err + } + if c != n { + return fmt.Errorf("failed to decode CBOR array of known length, expect length %d, got length %d", n, c) + } + return nil +} + +func decodeCBORTagWithKnownNumber(dec *cbor.StreamDecoder, n uint64) error { + tagNum, err := dec.DecodeTagNumber() + if err != nil { + return err + } + if tagNum != n { + return fmt.Errorf("failed to decode CBOR tag of known tag number, expect %d, got %d", n, tagNum) + } + return nil +} + +// newNilOptionalValue returns (nested) cadence.Optional nil value. +func newNilOptionalValue(gauge common.MemoryGauge, ot *cadence.OptionalType) cadence.Optional { + v := cadence.NewMeteredOptional(gauge, nil) + for { + var ok bool + ot, ok = ot.Type.(*cadence.OptionalType) + if !ok { + break + } + v = cadence.NewMeteredOptional(gauge, v) + } + return v +} diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go new file mode 100644 index 0000000000..2e3f9bed1e --- /dev/null +++ b/encoding/ccf/decode_type.go @@ -0,0 +1,543 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "fmt" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" +) + +type cadenceTypeID string + +type decodeTypeFn func(types cadenceTypeByCCFTypeID) (cadence.Type, error) + +// decodeInlineType decodes inline-type as +// language=CDDL +// inline-type = +// +// simple-type +// / optional-type +// / varsized-array-type +// / constsized-array-type +// / dict-type +// / reference-type +// / restricted-type +// / capability-type +// / type-ref +// +// All exported Cadence types needs to be handled in this function, +// including abstract and interface types. +func (d *Decoder) decodeInlineType(types cadenceTypeByCCFTypeID) (cadence.Type, error) { + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return nil, err + } + + switch tagNum { + case CBORTagSimpleType: + return d.decodeSimpleTypeID() + + case CBORTagOptionalType: + return d.decodeOptionalType(types, d.decodeInlineType) + + case CBORTagVarsizedArrayType: + return d.decodeVarSizedArrayType(types, d.decodeInlineType) + + case CBORTagConstsizedArrayType: + return d.decodeConstantSizedArrayType(types, d.decodeInlineType) + + case CBORTagDictType: + return d.decodeDictType(types, d.decodeInlineType) + + case CBORTagReferenceType: + return d.decodeReferenceType(types, d.decodeInlineType) + + case CBORTagRestrictedType: + return d.decodeRestrictedType(types, d.decodeInlineType) + + case CBORTagCapabilityType: + return d.decodeCapabilityType(types, d.decodeInlineType) + + case CBORTagTypeRef: + return d.decodeTypeRef(types) + + default: + return nil, fmt.Errorf("faild to decode inline type %d", tagNum) + } +} + +// decodeSimpleTypeID decodes encoded simple-type-id. +// See CCF specification for complete list of simple-type-id. +func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { + simpleTypeID, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + switch simpleTypeID { + case TypeBool: + return cadence.TheBoolType, nil + case TypeString: + return cadence.TheStringType, nil + case TypeCharacter: + return cadence.TheCharacterType, nil + case TypeAddress: + return cadence.TheAddressType, nil + case TypeInt: + return cadence.TheIntType, nil + case TypeInt8: + return cadence.TheInt8Type, nil + case TypeInt16: + return cadence.TheInt16Type, nil + case TypeInt32: + return cadence.TheInt32Type, nil + case TypeInt64: + return cadence.TheInt64Type, nil + case TypeInt128: + return cadence.TheInt128Type, nil + case TypeInt256: + return cadence.TheInt256Type, nil + case TypeUInt: + return cadence.TheUIntType, nil + case TypeUInt8: + return cadence.TheUInt8Type, nil + case TypeUInt16: + return cadence.TheUInt16Type, nil + case TypeUInt32: + return cadence.TheUInt32Type, nil + case TypeUInt64: + return cadence.TheUInt64Type, nil + case TypeUInt128: + return cadence.TheUInt128Type, nil + case TypeUInt256: + return cadence.TheUInt256Type, nil + case TypeWord8: + return cadence.TheWord8Type, nil + case TypeWord16: + return cadence.TheWord16Type, nil + case TypeWord32: + return cadence.TheWord32Type, nil + case TypeWord64: + return cadence.TheWord64Type, nil + case TypeFix64: + return cadence.TheFix64Type, nil + case TypeUFix64: + return cadence.TheUFix64Type, nil + case TypePath: + return cadence.ThePathType, nil + case TypeCapabilityPath: + return cadence.TheCapabilityPathType, nil + case TypeStoragePath: + return cadence.TheStoragePathType, nil + case TypePublicPath: + return cadence.ThePublicPathType, nil + case TypePrivatePath: + return cadence.ThePrivatePathType, nil + case TypeAuthAccount: + return cadence.TheAuthAccountType, nil + case TypePublicAccount: + return cadence.ThePublicAccountType, nil + case TypeAuthAccountKeys: + return cadence.TheAuthAccountKeysType, nil + case TypePublicAccountKeys: + return cadence.ThePublicAccountKeysType, nil + case TypeAuthAccountContracts: + return cadence.TheAuthAccountContractsType, nil + case TypePublicAccountContracts: + return cadence.ThePublicAccountContractsType, nil + case TypeDeployedContract: + return cadence.TheDeployedContractType, nil + case TypeAccountKey: + return cadence.TheAccountKeyType, nil + case TypeBlock: + return cadence.TheBlockType, nil + case TypeAny: + return cadence.TheAnyType, nil + case TypeAnyStruct: + return cadence.TheAnyStructType, nil + case TypeAnyResource: + return cadence.TheAnyResourceType, nil + case TypeMetaType: + return cadence.TheMetaType, nil + case TypeNever: + return cadence.TheNeverType, nil + case TypeNumber: + return cadence.TheNumberType, nil + case TypeSignedNumber: + return cadence.TheSignedNumberType, nil + case TypeInteger: + return cadence.TheIntegerType, nil + case TypeSignedInteger: + return cadence.TheSignedIntegerType, nil + case TypeFixedPoint: + return cadence.TheFixedPointType, nil + case TypeSignedFixedPoint: + return cadence.TheSignedFixedPointType, nil + case TypeBytes: + return cadence.TheBytesType, nil + case TypeVoid: + return cadence.TheVoidType, nil + default: + return nil, fmt.Errorf("failed to decode simple type: unexpected id %d", simpleTypeID) + } +} + +// decodeOptionalType decodes optional-type or optional-type-value as +// language=CDDL +// optional-type = +// +// ; cbor-tag-optional-type +// #6.138(inline-type) +// +// optional-type-value = +// +// ; cbor-tag-optional-type-value +// #6.186(type-value) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeOptionalType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode inline-type or type-value. + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + return cadence.NewMeteredOptionalType(d.gauge, elementType), nil +} + +// decodeVarSizedArrayType decodes varsized-array-type or varsized-array-type-value as +// language=CDDL +// varsized-array-type = +// +// ; cbor-tag-varsized-array-type +// #6.139(inline-type) +// +// varsized-array-type-value = +// +// ; cbor-tag-varsized-array-type-value +// #6.187(type-value) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeVarSizedArrayType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode inline-type or type-value. + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + return cadence.NewMeteredVariableSizedArrayType(d.gauge, elementType), nil +} + +// decodeConstantSizedArrayType decodes constsized-array-type or constsized-array-type-value as +// language=CDDL +// constsized-array-type = +// +// ; cbor-tag-constsized-array-type +// #6.140([ +// array-size: uint, +// element-type: inline-type +// ]) +// +// constsized-array-type-value = +// +// ; cbor-tag-constsized-array-type-value +// #6.188([ +// array-size: uint, +// element-type: type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeConstantSizedArrayType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: array-size + size, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + // element 1: element-type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + return cadence.NewMeteredConstantSizedArrayType(d.gauge, uint(size), elementType), nil +} + +// decodeDictType decodes dict-type or dict-type-value as +// language=CDDL +// dict-type = +// +// ; cbor-tag-dict-type +// #6.141([ +// key-type: inline-type, +// element-type: inline-type +// ]) +// +// dict-type-value = +// +// ; cbor-tag-dict-type-value +// #6.189([ +// key-type: type-value, +// element-type: type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeDictType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: key type (inline-type or type-value) + keyType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + // element 1: element type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + return cadence.NewMeteredDictionaryType(d.gauge, keyType, elementType), nil +} + +// decodeCapabilityType decodes capability-type or capability-type-value as +// language=CDDL +// capability-type = +// +// ; cbor-tag-capability-type +// ; use an array as an extension point +// #6.144([ +// ; borrow-type +// inline-type +// ]) +// +// capability-type-value = +// +// ; cbor-tag-capability-type-value +// ; use an array as an extension point +// #6.192([ +// ; borrow-type +// type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeCapabilityType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode array head of length 1 + err := decodeCBORArrayWithKnownSize(d.dec, 1) + if err != nil { + return nil, err + } + + // element 0: borrow-type (inline-type or type-value) + borrowType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + return cadence.NewMeteredCapabilityType(d.gauge, borrowType), nil +} + +// decodeReferenceType decodes reference-type or reference-type-value as +// language=CDDL +// reference-type = +// +// ; cbor-tag-reference-type +// #6.142([ +// authorized: bool, +// type: inline-type, +// ]) +// +// reference-type-value = +// +// ; cbor-tag-reference-type-value +// #6.190([ +// authorized: bool, +// type: type-value, +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeReferenceType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: authorized + authorized, err := d.dec.DecodeBool() + if err != nil { + return nil, err + } + + // element 0: type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + return cadence.NewMeteredReferenceType(d.gauge, authorized, elementType), nil +} + +// decodeRestrictedType decodes restricted-type or restricted-type-value as +// language=CDDL +// restricted-type = +// +// ; cbor-tag-restricted-type +// #6.143([ +// cadence-type-id: cadence-type-id, +// type: inline-type, +// restrictions: [+ inline-type] +// ]) +// +// restricted-type-value = +// +// ; cbor-tag-restricted-type-value +// #6.191([ +// cadence-type-id: cadence-type-id, +// type: type-value, +// restrictions: [+ type-value] +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeRestrictedType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { + // Decode array of length 3. + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return nil, err + } + + // element 0: cadence-type-id + typeID, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + // element 1: type + typ, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + // element 2: restrictions + restrictionCount, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + restrictionTypeIDs := make(map[string]struct{}, restrictionCount) + var previousRestrictedTypeID string + + restrictions := make([]cadence.Type, restrictionCount) + for i := 0; i < int(restrictionCount); i++ { + // Decode restriction. + restrictedType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "Elements MUST be unique in restricted-type or restricted-type-value." + if _, ok := restrictionTypeIDs[restrictedType.ID()]; ok { + return nil, fmt.Errorf("found duplicate restricted type %s", restrictedType.ID()) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" + // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." + if !stringsAreSortedBytewise(previousRestrictedTypeID, restrictedType.ID()) { + return nil, fmt.Errorf("restricted types are not sorted") + } + + restrictionTypeIDs[restrictedType.ID()] = struct{}{} + previousRestrictedTypeID = restrictedType.ID() + + restrictions[i] = restrictedType + } + + return cadence.NewMeteredRestrictedType( + d.gauge, + "", + typ, + restrictions, + ).WithID(typeID), nil +} + +// decodeCCFTypeID decodes encoded id as +// language=CDDL +// id = bstr +func (d *Decoder) decodeCCFTypeID() (ccfTypeID, error) { + b, err := d.dec.DecodeBytes() + if err != nil { + return 0, err + } + return newCCFTypeID(b), nil +} + +// decodeCadenceTypeID decodes encoded cadence-type-id as +// language=CDDL +// cadence-type-id = tstr +func (d *Decoder) decodeCadenceTypeID() (cadenceTypeID, common.Location, string, error) { + typeID, err := d.dec.DecodeString() + if err != nil { + return "", nil, "", err + } + + location, identifier, err := common.DecodeTypeID(d.gauge, typeID) + if err != nil { + return cadenceTypeID(typeID), nil, "", fmt.Errorf("invalid type ID `%s`: %w", typeID, err) + } else if location == nil && sema.NativeCompositeTypes[typeID] == nil { + // If the location is nil and there is no native composite type with this ID, then it's an invalid type. + // Note: This was moved out from the common.DecodeTypeID() to avoid the circular dependency. + return cadenceTypeID(typeID), nil, "", fmt.Errorf("invalid type ID for built-in: `%s`", typeID) + } + + return cadenceTypeID(typeID), location, identifier, nil +} + +// decodeTypeRef decodes encoded type-ref as +// language=CDDL +// type-ref = +// +// ; cbor-tag-type-ref +// #6.136(id) +func (d *Decoder) decodeTypeRef(types cadenceTypeByCCFTypeID) (cadence.Type, error) { + id, err := d.decodeCCFTypeID() + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "type-ref.id MUST refer to composite-type.id." + // "type-value-ref.id MUST refer to composite-type-value.id in the same composite-type-value data item." + return types.typ(id) +} diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go new file mode 100644 index 0000000000..e68aefa73c --- /dev/null +++ b/encoding/ccf/decode_typedef.go @@ -0,0 +1,469 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "errors" + "fmt" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" +) + +// decodeTypeDefs decodes composite/interface type definitions as +// language=CDDL +// composite-typedef = [ +// +// +( +// struct-type +// / resource-type +// / contract-type +// / event-type +// / enum-type +// / struct-interface-type +// / resource-interface-type +// / contract-interface-type +// )] +func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { + // Decode number of type definitions. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count == 0 { + return nil, errors.New("expect one or more type defintions in composite-typedef, got 0") + } + + types := make(map[ccfTypeID]cadence.Type, count) + + // NOTE: composite fields are not decoded while composite types are decoded + // because field type might reference composite type that hasn't decoded yet. + rawFields := make(map[ccfTypeID][]byte, count) + + // cadenceTypeIDs is used to check if cadence type IDs are unique in type definitions. + cadenceTypeIDs := make(map[cadenceTypeID]struct{}, count) + + // previousCadenceID is used to check if type definitions are sorted by cadence type IDs. + var previousCadenceID cadenceTypeID + + for i := uint64(0); i < count; i++ { + ccfID, cadenceID, err := d.decodeTypeDef(types, rawFields) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.cadence-type-id MUST be unique in + // ccf-typedef-message or ccf-typedef-and-value-message." + if _, ok := cadenceTypeIDs[cadenceID]; ok { + return nil, fmt.Errorf("found duplicated cadence type id %s in type definition", cadenceID) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id in ccf-typedef-and-value-message MUST + // be identical to its zero-based index in composite-typedef." + if !ccfID.Equal(newCCFTypeIDFromUint64(i)) { + return nil, fmt.Errorf( + "encoded id is not the same as composite-typedef index", + ) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "Type definitions MUST be sorted by cadence-type-id in composite-typedef." + if !stringsAreSortedBytewise(string(previousCadenceID), string(cadenceID)) { + return nil, fmt.Errorf( + "encoded cadence type ID in type definition isn't sorted using bytewise lexicographic order", + ) + } + + cadenceTypeIDs[cadenceID] = struct{}{} + previousCadenceID = cadenceID + } + + // Decode fields after all high-level type definitions are resolved. + for id, raw := range rawFields { + typ, ok := types[id] + if !ok { + return nil, fmt.Errorf("failed to decode type definition field, type with ccf id %d not found", id) + } + + dec := NewDecoder(d.gauge, raw) + fields, err := dec.decodeCompositeFields(types, dec.decodeInlineType) + if err != nil { + return nil, err + } + + switch t := typ.(type) { + case cadence.CompositeType: + t.SetCompositeFields(fields) + default: + return nil, fmt.Errorf("found encoded fields in type definition for non-composite type %T", t) + } + } + + return types, nil +} + +// decodeTypeDef decodes composite/interface type in type definition as +// language=CDDL +// struct-type = +// +// ; cbor-tag-struct-type +// #6.160(composite-type) +// +// resource-type = +// +// ; cbor-tag-resource-type +// #6.161(composite-type) +// +// event-type = +// +// ; cbor-tag-event-type +// #6.162(composite-type) +// +// contract-type = +// +// ; cbor-tag-contract-type +// #6.163(composite-type) +// +// enum-type = +// +// ; cbor-tag-enum-type +// #6.164(composite-type) +// +// struct-interface-type = +// +// ; cbor-tag-struct-interface-type +// #6.176(interface-type) +// +// resource-interface-type = +// +// ; cbor-tag-resource-interface-type +// #6.177(interface-type) +// +// contract-interface-type = +// +// ; cbor-tag-contract-interface-type +// #6.178(interface-type) +func (d *Decoder) decodeTypeDef( + types cadenceTypeByCCFTypeID, + rawFields map[ccfTypeID][]byte, +) ( + ccfTypeID, + cadenceTypeID, + error, +) { + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + switch tagNum { + case CBORTagStructType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredStructType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, rawFields, ctr) + + case CBORTagResourceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredResourceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, rawFields, ctr) + + case CBORTagEventType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredEventType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, rawFields, ctr) + + case CBORTagContractType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredContractType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, rawFields, ctr) + + case CBORTagEnumType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredEnumType( + d.gauge, + location, + identifier, + nil, + nil, + nil, + ) + } + return d.decodeCompositeType(types, rawFields, ctr) + + case CBORTagStructInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredStructInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + case CBORTagResourceInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredResourceInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + case CBORTagContractInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredContractInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + default: + return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf( + "failed to decode type definition: got tag number %d", + tagNum, + ) + } +} + +// decodeCompositeType decodes composite type in type definition as +// language=CDDL +// composite-type = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// fields: [ +// + [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +// +// ] +func (d *Decoder) decodeCompositeType( + types cadenceTypeByCCFTypeID, + rawFields map[ccfTypeID][]byte, + constructor func(common.Location, string) cadence.Type, +) (ccfTypeID, cadenceTypeID, error) { + + // Decode array head of length 3. + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + // element 0: id + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." + if _, ok := types[ccfID]; ok { + return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicated ccf type id %d in type definition", ccfID) + } + + // element 1: cadence-type-id + cadenceID, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + // element 2: fields + rawField, err := d.dec.DecodeRawBytes() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + types[ccfID] = constructor(location, identifier) + rawFields[ccfID] = rawField + return ccfID, cadenceID, nil +} + +// decodeCompositeFields decodes field types as +// language=CDDL +// +// fields: [ +// + [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) ([]cadence.Field, error) { + // Decode number of fields. + fieldCount, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + fields := make([]cadence.Field, fieldCount) + fieldNames := make(map[string]struct{}, fieldCount) + var previousFieldName string + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceField, + Amount: uint64(fieldCount), + }) + + for i := 0; i < int(fieldCount); i++ { + field, err := d.decodeCompositeField(types, decodeTypeFn) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "field-name MUST be unique in composite-type." + // "name MUST be unique in composite-type-value.fields." + if _, ok := fieldNames[field.Identifier]; ok { + return nil, fmt.Errorf("found duplicate field name in %s type definition", field.Identifier) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.fields MUST be sorted by name" + // "composite-type-value.fields MUST be sorted by name." + if !stringsAreSortedBytewise(previousFieldName, field.Identifier) { + return nil, fmt.Errorf("field names are not sorted in type definition") + } + + fieldNames[field.Identifier] = struct{}{} + previousFieldName = field.Identifier + fields[i] = field + } + + return fields, nil +} + +// decodeCompositeField decodes field type as +// language=CDDL +// +// [ +// field-name: tstr, +// field-type: inline-type +// ] +func (d *Decoder) decodeCompositeField(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Field, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return cadence.Field{}, err + } + + // element 0: field-name + fieldName, err := d.dec.DecodeString() + if err != nil { + return cadence.Field{}, err + } + + // element 1: field-type + fieldType, err := decodeTypeFn(types) + if err != nil { + return cadence.Field{}, err + } + + // Unmetered because decodeCompositeField is metered in decodeCompositeFields and called nowhere else + // fieldType is still metered. + return cadence.NewField(fieldName, fieldType), nil +} + +// decodeInterfaceType decodes interface type as +// language=CDDL +// interface-type = [ +// +// id: id, +// cadence-type-id: tstr, +// +// ] +func (d *Decoder) decodeInterfaceType( + types cadenceTypeByCCFTypeID, + constructor func(common.Location, string) cadence.Type, +) (ccfTypeID, cadenceTypeID, error) { + + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + // element 0: id + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." + if _, ok := types[ccfID]; ok { + return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicated ccf type id %d in type definition", ccfID) + } + + // element 1: cadence-type-id + cadenceID, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), err + } + + types[ccfID] = constructor(location, identifier) + return ccfID, cadenceID, nil +} diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go new file mode 100644 index 0000000000..bb583793b1 --- /dev/null +++ b/encoding/ccf/encode.go @@ -0,0 +1,1683 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "bytes" + "fmt" + "io" + goRuntime "runtime" + "sort" + "sync" + "unicode/utf8" + + "github.com/fxamacker/cbor/v2" + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" +) + +// CBOREncMode +// +// See https://github.com/fxamacker/cbor: +// "For best performance, reuse EncMode and DecMode after creating them." +var CBOREncMode = func() cbor.EncMode { + options := cbor.CoreDetEncOptions() + options.BigIntConvert = cbor.BigIntConvertNone + encMode, err := options.EncMode() + if err != nil { + panic(err) + } + return encMode +}() + +// An Encoder converts Cadence values into CCF-encoded bytes. +type Encoder struct { + // CCF codec uses CBOR codec under the hood. + enc *cbor.StreamEncoder +} + +// Encode returns the CCF-encoded representation of the given value. +// +// This function returns an error if the Cadence value cannot be represented in CCF. +func Encode(value cadence.Value) ([]byte, error) { + var w bytes.Buffer + enc := NewEncoder(&w) + + err := enc.Encode(value) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +// MustEncode returns the CCF-encoded representation of the given value, or panics +// if the value cannot be represented in CCF. +func MustEncode(value cadence.Value) []byte { + b, err := Encode(value) + if err != nil { + panic(err) + } + return b +} + +// NewEncoder initializes an Encoder that will write CCF-encoded bytes to the +// given io.Writer. +func NewEncoder(w io.Writer) *Encoder { + // CCF codec uses CBOR codec under the hood. + return &Encoder{enc: CBOREncMode.NewStreamEncoder(w)} +} + +// Encode writes the CCF-encoded representation of the given value to this +// encoder's io.Writer. +// +// This function returns an error if the given value's type is not supported +// by the encoder. +func (e *Encoder) Encode(value cadence.Value) (err error) { + // capture panics + defer func() { + e.enc.Close() + + if r := recover(); r != nil { + // don't recover Go errors + goErr, ok := r.(goRuntime.Error) + if ok { + panic(goErr) + } + + panicErr, isError := r.(error) + if !isError { + panic(r) + } + + err = fmt.Errorf("failed to encode value: %w", panicErr) + } + }() + + // Traverse value to find all composite types. + types, tids := compositeTypesFromValue(value) + + if len(types) == 0 { + // Encode top level message: ccf-type-and-value-message. + err = e.encodeTypeAndValue(value, tids) + } else { + // Encode top level message: ccf-typedef-and-value-message. + err = e.encodeTypeDefAndValue(value, types, tids) + } + if err != nil { + return err + } + + return e.enc.Flush() +} + +// encodeTypeDefAndValue encodes type definition and value as +// language=CDDL +// ccf-typedef-and-value-message = +// +// ; cbor-tag-typedef-and-value +// #6.129([ +// typedef: composite-typedef, +// type-and-value: inline-type-and-value +// ]) +func (e *Encoder) encodeTypeDefAndValue( + value cadence.Value, + types []cadence.Type, + tids ccfTypeIDByCadenceType, +) error { + // Encode tag number cbor-tag-typedef-and-value and array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: typedef + err = e.encodeTypeDefs(types, tids) + if err != nil { + return err + } + + // element 1: type and value + return e.encodeInlineTypeAndValue(value, tids) +} + +// encodeTypeAndValue encodes type and value as +// language=CDDL +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130(inline-type-and-value) +func (e *Encoder) encodeTypeAndValue(value cadence.Value, tids ccfTypeIDByCadenceType) error { + // Encode tag number + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeAndValue, + }) + if err != nil { + return err + } + + return e.encodeInlineTypeAndValue(value, tids) +} + +// encodeTypeAndValueWithNoTag encodes inline type and value as +// language=CDDL +// inline-type-and-value = [ +// +// type: inline-type, +// value: value, +// +// ] +func (e *Encoder) encodeInlineTypeAndValue(value cadence.Value, tids ccfTypeIDByCadenceType) error { + // Encode array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + runtimeType := value.Type() + + // element 0: inline-type + err = e.encodeInlineType(runtimeType, tids) + if err != nil { + return err + } + + // element 1: value + return e.encodeValue(value, runtimeType, tids) +} + +// encodeTypeDefs encodes composite/interface type definitions as +// language=CDDL +// composite-typedef = [ +// +// +( +// struct-type +// / resource-type +// / contract-type +// / event-type +// / enum-type +// / struct-interface-type +// / resource-interface-type +// / contract-interface-type +// )] +func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceType) error { + // Encode array head with number of type definitions. + err := e.enc.EncodeArrayHead(uint64(len(types))) + if err != nil { + return err + } + + for _, typ := range types { + switch x := typ.(type) { + case cadence.CompositeType: + // Encode struct-type, resource-type, contract-type, event-type, or enum-type. + err = e.encodeCompositeType(x, tids) + if err != nil { + return err + } + case cadence.InterfaceType: + // Encode struct-interface-type, resource-interface-type, or contract-interface-type. + err = e.encodeInterfaceType(x, tids) + if err != nil { + return err + } + default: + return fmt.Errorf("failed to encode %s in type definition", typ) + } + } + + return nil +} + +// encodeValue traverses the object graph of the provided value and encodes it as +// language=CDDL +// value = +// +// ccf-type-and-value-message +// / simple-value +// / optional-value +// / array-value +// / dict-value +// / composite-value +// / path-value +// / capability-value +// / function-value +// / type-value +// +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130([ +// type: inline-type, +// value: value +// ]) +// +// simple-value = +// +// void-value +// / bool-value +// / character-value +// / string-value +// / address-value +// / uint-value +// / uint8-value +// / uint16-value +// / uint32-value +// / uint64-value +// / uint128-value +// / uint256-value +// / int-value +// / int8-value +// / int16-value +// / int32-value +// / int64-value +// / int128-value +// / int256-value +// / word8-value +// / word16-value +// / word32-value +// / word64-value +// / fix64-value +// / ufix64-value +func (e *Encoder) encodeValue(v cadence.Value, staticType cadence.Type, tids ccfTypeIDByCadenceType) error { + runtimeType := v.Type() + + if needToEncodeRuntimeType(staticType, runtimeType) { + // Encode ccf-type-and-value-message. + + // Encode tag number and array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: type as inline-type + err = e.encodeInlineType(runtimeType, tids) + if err != nil { + return err + } + + // element 1: value + } + + switch x := v.(type) { + case cadence.Void: + return e.encodeVoid(x) + case cadence.Optional: + return e.encodeOptional(x, tids) + case cadence.Bool: + return e.encodeBool(x) + case cadence.Character: + return e.encodeCharacter(x) + case cadence.String: + return e.encodeString(x) + case cadence.Address: + return e.encodeAddress(x) + case cadence.Int: + return e.encodeInt(x) + case cadence.Int8: + return e.encodeInt8(x) + case cadence.Int16: + return e.encodeInt16(x) + case cadence.Int32: + return e.encodeInt32(x) + case cadence.Int64: + return e.encodeInt64(x) + case cadence.Int128: + return e.encodeInt128(x) + case cadence.Int256: + return e.encodeInt256(x) + case cadence.UInt: + return e.encodeUInt(x) + case cadence.UInt8: + return e.encodeUInt8(x) + case cadence.UInt16: + return e.encodeUInt16(x) + case cadence.UInt32: + return e.encodeUInt32(x) + case cadence.UInt64: + return e.encodeUInt64(x) + case cadence.UInt128: + return e.encodeUInt128(x) + case cadence.UInt256: + return e.encodeUInt256(x) + case cadence.Word8: + return e.encodeWord8(x) + case cadence.Word16: + return e.encodeWord16(x) + case cadence.Word32: + return e.encodeWord32(x) + case cadence.Word64: + return e.encodeWord64(x) + case cadence.Fix64: + return e.encodeFix64(x) + case cadence.UFix64: + return e.encodeUFix64(x) + case cadence.Array: + return e.encodeArray(x, tids) + case cadence.Dictionary: + return e.encodeDictionary(x, tids) + case cadence.Struct: + return e.encodeStruct(x, tids) + case cadence.Resource: + return e.encodeResource(x, tids) + case cadence.Event: + return e.encodeEvent(x, tids) + case cadence.Contract: + return e.encodeContract(x, tids) + case cadence.Path: + return e.encodePath(x) + case cadence.TypeValue: + return e.encodeTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) + case cadence.StorageCapability: + return e.encodeCapability(x) + case cadence.Enum: + return e.encodeEnum(x, tids) + case cadence.Function: + return e.encodeFunction(x.FunctionType, ccfTypeIDByCadenceType{}) + default: + panic(fmt.Errorf("unsupported value: %T, %v", v, v)) + } +} + +// encodeVoid encodes cadence.Void as +// language=CDDL +// void-value = nil +func (e *Encoder) encodeVoid(v cadence.Void) error { + return e.enc.EncodeNil() +} + +// encodeOptional encodes cadence.Optional as +// language=CDDL +// optional-value = nil / value +func (e *Encoder) encodeOptional(v cadence.Optional, tids ccfTypeIDByCadenceType) error { + switch innerValue := v.Value.(type) { + case cadence.Optional: + return e.encodeOptional(innerValue, tids) + case nil: + return e.enc.EncodeNil() + default: + // Use innerValue.Type() as static type to avoid encoding type + // because OptionalType is already encoded. + return e.encodeValue(innerValue, innerValue.Type(), tids) + } +} + +// encodeBool encodes cadence.Bool as +// language=CDDL +// bool-value = bool +func (e *Encoder) encodeBool(v cadence.Bool) error { + return e.enc.EncodeBool(bool(v)) +} + +// encodeCharacter encodes cadence.Character as +// language=CDDL +// character-value = tstr +func (e *Encoder) encodeCharacter(v cadence.Character) error { + return e.enc.EncodeString(string(v)) +} + +// encodeString encodes cadence.String as +// language=CDDL +// string-value = tstr +// NOTE: cadence.String must be valid UTF-8. +func (e *Encoder) encodeString(v cadence.String) error { + s := string(v) + if !utf8.ValidString(s) { + return fmt.Errorf("invalid UTF-8 in string: %s", s) + } + return e.enc.EncodeString(string(v)) +} + +// encodeAddress encodes cadence.Address as +// language=CDDL +// address-value = bstr .size 8 +func (e *Encoder) encodeAddress(v cadence.Address) error { + return e.enc.EncodeBytes(v[:]) +} + +// encodeInt encodes cadence.Int as +// language=CDDL +// int-value = bigint +func (e *Encoder) encodeInt(v cadence.Int) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeInt8 encodes cadence.Int8 as +// language=CDDL +// int8-value = (int .ge -128) .le 127 +func (e *Encoder) encodeInt8(v cadence.Int8) error { + return e.enc.EncodeInt8(int8(v)) +} + +// encodeInt16 encodes cadence.Int16 as +// language=CDDL +// int16-value = (int .ge -32768) .le 32767 +func (e *Encoder) encodeInt16(v cadence.Int16) error { + return e.enc.EncodeInt16(int16(v)) +} + +// encodeInt32 encodes cadence.Int32 as +// language=CDDL +// int32-value = (int .ge -2147483648) .le 2147483647 +func (e *Encoder) encodeInt32(v cadence.Int32) error { + return e.enc.EncodeInt32(int32(v)) +} + +// encodeInt64 encodes cadence.Int64 as +// language=CDDL +// int64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (e *Encoder) encodeInt64(v cadence.Int64) error { + return e.enc.EncodeInt64(int64(v)) +} + +// encodeInt128 encodes cadence.Int128 as +// language=CDDL +// int128-value = bigint +func (e *Encoder) encodeInt128(v cadence.Int128) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeInt256 encodes cadence.Int256 as +// language=CDDL +// int256-value = bigint +func (e *Encoder) encodeInt256(v cadence.Int256) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt encodes cadence.UInt as +// language=CDDL +// uint-value = bigint .ge 0 +func (e *Encoder) encodeUInt(v cadence.UInt) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt8 encodes cadence.UInt8 as +// language=CDDL +// uint8-value = uint .le 255 +func (e *Encoder) encodeUInt8(v cadence.UInt8) error { + return e.enc.EncodeUint8(uint8(v)) +} + +// encodeUInt16 encodes cadence.UInt16 as +// language=CDDL +// uint16-value = uint .le 65535 +func (e *Encoder) encodeUInt16(v cadence.UInt16) error { + return e.enc.EncodeUint16(uint16(v)) +} + +// encodeUInt32 encodes cadence.UInt32 as CBOR uint. +// language=CDDL +// uint32-value = uint .le 4294967295 +func (e *Encoder) encodeUInt32(v cadence.UInt32) error { + return e.enc.EncodeUint32(uint32(v)) +} + +// encodeUInt64 encodes cadence.UInt64 as +// language=CDDL +// uint64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeUInt64(v cadence.UInt64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeUInt128 encodes cadence.UInt128 as +// language=CDDL +// uint128-value = bigint .ge 0 +func (e *Encoder) encodeUInt128(v cadence.UInt128) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt256 encodes cadence.UInt256 as +// language=CDDL +// uint256-value = bigint .ge 0 +func (e *Encoder) encodeUInt256(v cadence.UInt256) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeWord8 encodes cadence.Word8 as +// language=CDDL +// word8-value = uint .le 255 +func (e *Encoder) encodeWord8(v cadence.Word8) error { + return e.enc.EncodeUint8(uint8(v)) +} + +// encodeWord16 encodes cadence.Word16 as +// language=CDDL +// word16-value = uint .le 65535 +func (e *Encoder) encodeWord16(v cadence.Word16) error { + return e.enc.EncodeUint16(uint16(v)) +} + +// encodeWord32 encodes cadence.Word32 as +// language=CDDL +// word32-value = uint .le 4294967295 +func (e *Encoder) encodeWord32(v cadence.Word32) error { + return e.enc.EncodeUint32(uint32(v)) +} + +// encodeWord64 encodes cadence.Word64 as +// language=CDDL +// word64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeWord64(v cadence.Word64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeFix64 encodes cadence.Fix64 as +// language=CDDL +// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (e *Encoder) encodeFix64(v cadence.Fix64) error { + return e.enc.EncodeInt64(int64(v)) +} + +// encodeUFix64 encodes cadence.UFix64 as +// language=CDDL +// ufix64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeUFix64(v cadence.UFix64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeArray encodes cadence.Array as +// language=CDDL +// array-value = [* value] +func (e *Encoder) encodeArray(v cadence.Array, tids ccfTypeIDByCadenceType) error { + staticElementType := v.ArrayType.Element() + + // Encode array head with number of array elements. + err := e.enc.EncodeArrayHead(uint64(len(v.Values))) + if err != nil { + return err + } + + for _, value := range v.Values { + // Encode element as value. + err = e.encodeValue(value, staticElementType, tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeDictionary encodes cadence.Dictionary as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadenceType) error { + if len(v.Pairs) > 1 { + return e.encodeSortedDictionary(v, tids) + } + + dictionaryType := v.DictionaryType.(*cadence.DictionaryType) + if dictionaryType == nil { + return fmt.Errorf("failed to retrieve DictionaryType from cadence.Dictionary") + } + + staticKeyType := dictionaryType.KeyType + staticElementType := dictionaryType.ElementType + + // Encode array head with array size of 2 * number of pairs. + err := e.enc.EncodeArrayHead(uint64(len(v.Pairs) * 2)) + if err != nil { + return err + } + + for _, pair := range v.Pairs { + // Encode dictionary key as value. + err = e.encodeValue(pair.Key, staticKeyType, tids) + if err != nil { + return err + } + + // Encode dictionary value as value. + err = e.encodeValue(pair.Value, staticElementType, tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeDictionary encodes cadence.Dictionary as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (e *Encoder) encodeSortedDictionary(v cadence.Dictionary, tids ccfTypeIDByCadenceType) error { + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "dict-value key-value pairs MUST be sorted by key." + + // Use a new buffer for sorting key value pairs. + buf := getBuffer() + defer putBuffer(buf) + + // Encode and sort key value pairs. + sortedPairs, err := encodeAndSortKeyValuePairs(buf, v, tids) + if err != nil { + return err + } + + // Encode array head with 2 * number of pairs. + err = e.enc.EncodeArrayHead(uint64(len(v.Pairs) * 2)) + if err != nil { + return err + } + + for i := 0; i < len(sortedPairs); i++ { + // Encode key and value. + err = e.enc.EncodeRawBytes(sortedPairs[i].encodedPair) + if err != nil { + return err + } + } + + return nil +} + +func encodeAndSortKeyValuePairs( + buf *bytes.Buffer, + v cadence.Dictionary, + tids ccfTypeIDByCadenceType, +) ( + []encodedKeyValuePair, + error, +) { + dictionaryType := v.DictionaryType.(*cadence.DictionaryType) + if dictionaryType == nil { + return nil, fmt.Errorf("failed to retrieve DictionaryType from cadence.Dictionary") + } + + staticKeyType := dictionaryType.KeyType + staticElementType := dictionaryType.ElementType + + encodedPairs := make([]encodedKeyValuePair, len(v.Pairs)) + + e := NewEncoder(buf) + + for i, pair := range v.Pairs { + off := buf.Len() + + // Encode dictionary key as value. + err := e.encodeValue(pair.Key, staticKeyType, tids) + if err != nil { + return nil, err + } + + // Get encoded key length (must flush first). + e.enc.Flush() + keyLength := buf.Len() - off + + // Encode dictionary value as value. + err = e.encodeValue(pair.Value, staticElementType, tids) + if err != nil { + return nil, err + } + + // Get encoded pair length (must flush first). + e.enc.Flush() + pairLength := buf.Len() - off + + encodedPairs[i] = encodedKeyValuePair{keyLength: keyLength, pairLength: pairLength} + } + + // Reslice buf for encoded key and pair by offset and length. + b := buf.Bytes() + off := 0 + for i := 0; i < len(encodedPairs); i++ { + encodedPairs[i].encodedKey = b[off : off+encodedPairs[i].keyLength] + encodedPairs[i].encodedPair = b[off : off+encodedPairs[i].pairLength] + off += encodedPairs[i].pairLength + } + if off != len(b) { + // Sanity check + return nil, fmt.Errorf("failed to encode and sort dictionary pairs: off %d, len(b) %d", off, len(b)) + } + + sort.Sort(bytewiseKeyValuePairSorter(encodedPairs)) + + return encodedPairs, nil +} + +// encodeStruct encodes cadence.Struct as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeStruct(v cadence.Struct, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.StructType, v.Fields, tids) +} + +// encodeResource encodes cadence.Resource as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeResource(v cadence.Resource, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.ResourceType, v.Fields, tids) +} + +// encodeEvent encodes cadence.Event as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeEvent(v cadence.Event, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.EventType, v.Fields, tids) +} + +// encodeContract encodes cadence.Contract as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeContract(v cadence.Contract, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.ContractType, v.Fields, tids) +} + +// encodeEnum encodes cadence.Enum as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeEnum(v cadence.Enum, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.EnumType, v.Fields, tids) +} + +// encodeComposite encodes composite types as +// language=CDDL +// composite-value = [+ (field: value)] +func (e *Encoder) encodeComposite( + typ cadence.CompositeType, + fields []cadence.Value, + tids ccfTypeIDByCadenceType, +) error { + id := typ.ID() + staticFieldTypes := typ.CompositeFields() + + if len(staticFieldTypes) != len(fields) { + panic(fmt.Errorf( + "%s field count (%d) does not match declared type (%d)", + id, + len(fields), + len(staticFieldTypes), + )) + } + + // Encode array head with number of fields. + err := e.enc.EncodeArrayHead(uint64(len(fields))) + if err != nil { + return err + } + + if len(fields) == 1 { + // Avoid overhead of sorting if there is only one field. + return e.encodeValue(fields[0], staticFieldTypes[0].Type, tids) + } + + sortedIndexes := getSortedFieldIndex(typ) + + for _, index := range sortedIndexes { + // Encode sorted field as value. + err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) + if err != nil { + return err + } + } + + return nil +} + +// encodePath encodes cadence.Path as +// language=CDDL +// path-value = [ +// +// domain: uint, +// identifier: tstr, +// +// ] +func (e *Encoder) encodePath(x cadence.Path) error { + // Encode array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: domain as CBOR uint. + domain := common.PathDomainFromIdentifier(x.Domain) + err = e.enc.EncodeUint8(uint8(domain)) + if err != nil { + return err + } + + // element 1: identifier as CBOR tstr. + return e.enc.EncodeString(x.Identifier) +} + +// encodeCapability encodes cadence.StorageCapability as +// language=CDDL +// capability-value = [ +// +// address: address-value, +// path: path-value +// +// ] +func (e *Encoder) encodeCapability(capability cadence.StorageCapability) 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: path + return e.encodePath(capability.Path) +} + +// encodeFunction encodes cadence.FunctionType as +// language=CDDL +// function-value = [ +// +// cadence-type-id: cadence-type-id, +// parameters: [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// return-type: type-value +// +// ] +func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { + // Encode array head of length 3. + err := e.enc.EncodeRawBytes([]byte{ + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + + // element 0: cadence-type-id as tstr. + err = e.encodeCadenceTypeID(typ.ID()) + if err != nil { + return err + } + + // element 1: parameters as array. + err = e.encodeParameterTypeValues(typ.Parameters, visited) + if err != nil { + return err + } + + // element 2: return type as type-value. + return e.encodeTypeValue(typ.ReturnType, visited) +} + +// encodeTypeValue encodes cadence.Type as +// language=CDDL +// type-value = +// +// nil +// / simple-type-value +// / optional-type-value +// / varsized-array-type-value +// / constsized-array-type-value +// / dict-type-value +// / struct-type-value +// / resource-type-value +// / contract-type-value +// / event-type-value +// / enum-type-value +// / struct-interface-type-value +// / resource-interface-type-value +// / contract-interface-type-value +// / function-type-value +// / reference-type-value +// / restricted-type-value +// / capability-type-value +// / type-value-ref +// +// TypeValue is used differently from inline type or type definition. +// Inline type and type definition are used to decode exported value, +// while TypeValue is exported value, which type is cadence.MetaType. +// Thus, TypeValue can encode more information than type or type definition. +func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceType) error { + switch typ.(type) { + case cadence.CompositeType, cadence.InterfaceType: + cadenceTypeID := typ.ID() + if ccfID, ok := visited[cadenceTypeID]; ok { + // Encode visited composite/interface type value + // using CCF type id for compactness. + return e.encodeTypeValueRef(ccfID) + } + // type value ccf type id is index of visited composite/interface + // type value depth first. + visited[cadenceTypeID] = ccfTypeID(len(visited)) + } + + simpleTypeID, ok := simpleTypeIDByType(typ) + if ok { + return e.encodeSimpleTypeValue(simpleTypeID) + } + + switch typ := typ.(type) { + case *cadence.OptionalType: + return e.encodeOptionalTypeValue(typ, visited) + + case *cadence.VariableSizedArrayType: + return e.encodeVarSizedArrayTypeValue(typ, visited) + + case *cadence.ConstantSizedArrayType: + return e.encodeConstantSizedArrayTypeValue(typ, visited) + + case *cadence.DictionaryType: + return e.encodeDictTypeValue(typ, visited) + + case *cadence.StructType: + return e.encodeStructTypeValue(typ, visited) + + case *cadence.ResourceType: + return e.encodeResourceTypeValue(typ, visited) + + case *cadence.EventType: + return e.encodeEventTypeValue(typ, visited) + + case *cadence.ContractType: + return e.encodeContractTypeValue(typ, visited) + + case *cadence.StructInterfaceType: + return e.encodeStructInterfaceTypeValue(typ, visited) + + case *cadence.ResourceInterfaceType: + return e.encodeResourceInterfaceTypeValue(typ, visited) + + case *cadence.ContractInterfaceType: + return e.encodeContractInterfaceTypeValue(typ, visited) + + case *cadence.FunctionType: + return e.encodeFunctionTypeValue(typ, visited) + + case *cadence.ReferenceType: + return e.encodeReferenceTypeValue(typ, visited) + + case *cadence.RestrictedType: + return e.encodeRestrictedTypeValue(typ, visited) + + case *cadence.CapabilityType: + return e.encodeCapabilityTypeValue(typ, visited) + + case *cadence.EnumType: + return e.encodeEnumTypeValue(typ, visited) + + case nil: + return e.encodeNilTypeValue() + + default: + panic(fmt.Errorf("unsupported type: %T, %v", typ, typ)) + } +} + +// encodeTypeValueRef encodes type value ID as +// language=CDDL +// type-value-ref = +// +// ; cbor-tag-type-value-ref +// #6.184(id) +func (e *Encoder) encodeTypeValueRef(id ccfTypeID) error { + rawTagNum := []byte{0xd8, CBORTagTypeValueRef} + return e.encodeTypeRefWithRawTag(id, rawTagNum) +} + +// encodeSimpleTypeValue encodes cadence simple type value as +// language=CDDL +// simple-type-value = +// +// ; cbor-tag-simple-type-value +// #6.185(simple-type-id) +func (e *Encoder) encodeSimpleTypeValue(id uint64) error { + rawTagNum := []byte{0xd8, CBORTagSimpleTypeValue} + return e.encodeSimpleTypeWithRawTag(id, rawTagNum) +} + +// encodeOptionalTypeValue encodes cadence.OptionalType as +// language=CDDL +// optional-type-value = +// +// ; cbor-tag-optional-type-value +// #6.186(type-value) +func (e *Encoder) encodeOptionalTypeValue(typ *cadence.OptionalType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagOptionalTypeValue} + return e.encodeOptionalTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeVarSizedArrayTypeValue encodes cadence.VariableSizedArrayType as +// language=CDDL +// varsized-array-type-value = +// +// ; cbor-tag-varsized-array-type-value +// #6.187(type-value) +func (e *Encoder) encodeVarSizedArrayTypeValue(typ *cadence.VariableSizedArrayType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagVarsizedArrayTypeValue} + return e.encodeVarSizedArrayTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeConstantSizedArrayTypeValue encodes cadence.ConstantSizedArrayType as +// language=CDDL +// constsized-array-type-value = +// +// ; cbor-tag-constsized-array-type-value +// #6.188([ +// array-size: uint, +// element-type: type-value +// ]) +func (e *Encoder) encodeConstantSizedArrayTypeValue(typ *cadence.ConstantSizedArrayType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagConstsizedArrayTypeValue} + return e.encodeConstantSizedArrayTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeDictTypeValue encodes cadence.DictionaryType as +// language=CDDL +// dict-type-value = +// +// ; cbor-tag-dict-type-value +// #6.189([ +// key-type: type-value, +// element-type: type-value +// ]) +func (e *Encoder) encodeDictTypeValue(typ *cadence.DictionaryType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagDictTypeValue} + return e.encodeDictTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeReferenceTypeValue encodes cadence.ReferenceType as +// language=CDDL +// reference-type-value = +// +// ; cbor-tag-reference-type-value +// #6.190([ +// authorized: bool, +// type: type-value, +// ]) +func (e *Encoder) encodeReferenceTypeValue(typ *cadence.ReferenceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagReferenceTypeValue} + return e.encodeReferenceTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeRestrictedTypeValue encodes cadence.RestrictedType as +// language=CDDL +// restricted-type-value = +// +// ; cbor-tag-restricted-type-value +// #6.191([ +// cadence-type-id: cadence-type-id, +// type: type-value, +// restrictions: [+ type-value] +// ]) +func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagRestrictedTypeValue} + return e.encodeRestrictedTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeCapabilityTypeValue encodes cadence.CapabilityType as +// language=CDDL +// capability-type-value = +// +// ; cbor-tag-capability-type-value +// ; use an array as an extension point +// #6.192([ +// ; borrow-type +// type-value +// ]) +func (e *Encoder) encodeCapabilityTypeValue(typ *cadence.CapabilityType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagCapabilityTypeValue} + return e.encodeCapabilityTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeStructTypeValue encodes cadence.StructType as +// language=CDDL +// struct-type-value = +// +// ; cbor-tag-struct-type-value +// #6.208(composite-type-value) +func (e *Encoder) encodeStructTypeValue(typ *cadence.StructType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagStructTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeResourceTypeValue encodes cadence.ResourceType as +// language=CDDL +// resource-type-value = +// +// ; cbor-tag-resource-type-value +// #6.209(composite-type-value) +func (e *Encoder) encodeResourceTypeValue(typ *cadence.ResourceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagResourceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeEventTypeValue encodes cadence.EventType as +// language=CDDL +// event-type-value = +// +// ; cbor-tag-event-type-value +// #6.210(composite-type-value) +func (e *Encoder) encodeEventTypeValue(typ *cadence.EventType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagEventTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + [][]cadence.Parameter{typ.Initializer}, + visited, + rawTagNum, + ) +} + +// encodeContractTypeValue encodes cadence.ContractType as +// language=CDDL +// contract-type-value = +// +// ; cbor-tag-contract-type-value +// #6.211(composite-type-value) +func (e *Encoder) encodeContractTypeValue(typ *cadence.ContractType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagContractTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeEnumTypeValue encodes cadence.EnumType as +// language=CDDL +// enum-type-value = +// +// ; cbor-tag-enum-type-value +// #6.212(composite-type-value) +func (e *Encoder) encodeEnumTypeValue(typ *cadence.EnumType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagEnumTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + typ.RawType, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeStructInterfaceTypeValue encodes cadence.StructInterfaceType as +// language=CDDL +// struct-interface-type-value = +// +// ; cbor-tag-struct-interface-type-value +// #6.224(composite-type-value) +func (e *Encoder) encodeStructInterfaceTypeValue(typ *cadence.StructInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagStructInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeResourceInterfaceTypeValue encodes cadence.ResourceInterfaceType as +// language=CDDL +// resource-interface-type-value = +// +// ; cbor-tag-resource-interface-type-value +// #6.225(composite-type-value) +func (e *Encoder) encodeResourceInterfaceTypeValue(typ *cadence.ResourceInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagResourceInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeContractInterfaceTypeValue encodes cadence.ContractInterfaceType as +// language=CDDL +// contract-interface-type-value = +// +// ; cbor-tag-contract-interface-type-value +// #6.226(composite-type-value) +func (e *Encoder) encodeContractInterfaceTypeValue(typ *cadence.ContractInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagContractInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeCompositeTypeValue encodes composite and interface type values as +// language=CDDL +// composite-type-value = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// ; type is only used by enum type value +// type: nil / type-value, +// fields: [ +// + [ +// name: tstr, +// type: type-value +// ] +// ] +// initializers: [ +// * [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +// +// ] +func (e *Encoder) encodeCompositeTypeValue( + cadenceTypeID string, + typ cadence.Type, + fieldTypes []cadence.Field, + initializerTypes [][]cadence.Parameter, + visited ccfTypeIDByCadenceType, + rawTagNum []byte, +) error { + ccfID, ok := visited[cadenceTypeID] + if !ok { + return fmt.Errorf("failed to get ccf type id for cadence type %s", cadenceTypeID) + } + + // Encode given tag number indicating cadence type value. + err := e.enc.EncodeRawBytes(rawTagNum) + if err != nil { + return err + } + + // Encode CBOR array head with length 5. + err = e.enc.EncodeArrayHead(5) + if err != nil { + return err + } + + // element 0: ccf type id as bstr. + // It is used to lookup repeated or recursive types within the same encoded type value. + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence type id as tstr. + err = e.encodeCadenceTypeID(cadenceTypeID) + if err != nil { + return err + } + + // element 2: type as nil or type-value. + // Type is only used by enum type value. + if typ == nil { + err = e.enc.EncodeNil() + } else { + err = e.encodeTypeValue(typ, visited) + } + if err != nil { + return err + } + + // element 3: fields as array. + err = e.encodeFieldTypeValues(fieldTypes, visited) + if err != nil { + return err + } + + // element 4: initializers as array. + return e.encodeInitializerTypeValues(initializerTypes, visited) +} + +// encodeFieldTypeValues encodes composite field types as +// language=CDDL +// +// fields: [ +// + [ +// name: tstr, +// type: type-value +// ] +// ] +func (e *Encoder) encodeFieldTypeValues(fieldTypes []cadence.Field, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of field types. + err := e.enc.EncodeArrayHead(uint64(len(fieldTypes))) + if err != nil { + return err + } + + if len(fieldTypes) == 1 { + // Avoid overhead of sorting if there is only one field type + return e.encodeFieldTypeValue(fieldTypes[0], visited) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.fields MUST be sorted by name." + + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fieldTypes in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(fieldTypes) + + sort.Sort(sorter) + + // Encode sorted field types. + for _, index := range sorter.indexes { + err = e.encodeFieldTypeValue(fieldTypes[index], visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeFieldTypeValue encodes one composite field type as +// language=CDDL +// +// [ +// name: tstr, +// type: type-value +// ] +func (e *Encoder) encodeFieldTypeValue(fieldType cadence.Field, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: field name as tstr. + err = e.enc.EncodeString(fieldType.Identifier) + if err != nil { + return err + } + + // element 1: field type as type-value. + return e.encodeTypeValue(fieldType.Type, visited) +} + +// encodeInitializerTypeValues encodes composite initializers as +// language=CDDL +// +// initializers: [ +// * [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Parameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of initializers. + err := e.enc.EncodeArrayHead(uint64(len(initializerTypes))) + if err != nil { + return err + } + + // Encode initializers. + for _, params := range initializerTypes { + err = e.encodeParameterTypeValues(params, visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeParameterTypeValues encodes composite initializer parameter types as +// language=CDDL +// +// [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of parameter types. + err := e.enc.EncodeArrayHead(uint64(len(parameterTypes))) + if err != nil { + return err + } + + if len(parameterTypes) == 1 { + // Avoid overhead of sorting if there is only one parameter type. + return e.encodeParameterTypeValue(parameterTypes[0], visited) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.initializers MUST be sorted by identifier." + + // NOTE: bytewiseParmaeterSorter doesn't sort parameterTypes in place. + // bytewiseParmaeterSorter.indexes is used as sorted parameterTypes + // index. + sorter := newBytewiseParameterSorter(parameterTypes) + + sort.Sort(sorter) + + // Encode sorted parameter types. + for _, index := range sorter.indexes { + err = e.encodeParameterTypeValue(parameterTypes[index], visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeParameterTypeValue encodes composite initializer parameter as +// language=CDDL +// +// [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +func (e *Encoder) encodeParameterTypeValue(parameterType cadence.Parameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 3 + err := e.enc.EncodeRawBytes([]byte{ + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + + // element 0: label as tstr. + err = e.enc.EncodeString(parameterType.Label) + if err != nil { + return err + } + + // element 1: identifier as tstr. + err = e.enc.EncodeString(parameterType.Identifier) + if err != nil { + return err + } + + // element 2: type as type-value. + return e.encodeTypeValue(parameterType.Type, visited) +} + +// encodeFunctionTypeValue encodes cadence.FunctionType as +// language=CDDL +// function-type-value = +// +// ; cbor-tag-function-type-value +// #6.193(function-value) +func (e *Encoder) encodeFunctionTypeValue(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { + // Encode tag number and array head of length 3. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagFunctionTypeValue, + }) + if err != nil { + return err + } + + //Encode function-value. + return e.encodeFunction(typ, visited) +} + +// encodeNilTypeValue encodes nil type value as CBOR nil. +func (e *Encoder) encodeNilTypeValue() error { + return e.enc.EncodeNil() +} + +// needToEncodeType returns true if runtimeType needs to be encoded because: +// - static type is missing (top level value doesn't have static type) +// - static type is different from runtime type (static type is abstract type) +func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) bool { + if staticType == nil { + return true + } + if staticType.Equal(runtimeType) { + return false + } + // Here, static type is different from runtime type. + // Handle special case of runtime type being OptionalType{NeverType}. + if _, ok := staticType.(*cadence.OptionalType); ok { + if isOptionalNeverType(runtimeType) { + return false + } + } + return true +} + +// isOptionalNeverType returns true if t is (nested) optional never type. +func isOptionalNeverType(t cadence.Type) bool { + for { + switch ot := t.(type) { + case *cadence.OptionalType: + if ot.Type.Equal(cadence.NewNeverType()) { + return true + } + t = ot.Type + default: + return false + } + } +} + +var bufferPool = sync.Pool{ + New: func() interface{} { + e := new(bytes.Buffer) + e.Grow(64) + return e + }, +} + +func getBuffer() *bytes.Buffer { + return bufferPool.Get().(*bytes.Buffer) +} + +func putBuffer(e *bytes.Buffer) { + e.Reset() + bufferPool.Put(e) +} diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go new file mode 100644 index 0000000000..3d7d773965 --- /dev/null +++ b/encoding/ccf/encode_type.go @@ -0,0 +1,510 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "fmt" + "sort" + + "github.com/onflow/cadence" +) + +type encodeTypeFn func(typ cadence.Type, tids ccfTypeIDByCadenceType) error + +// encodeInlineType encodes cadence.Type as +// language=CDDL +// inline-type = +// +// simple-type +// / optional-type +// / varsized-array-type +// / constsized-array-type +// / dict-type +// / reference-type +// / restricted-type +// / capability-type +// / type-ref +// +// All exported Cadence types need to be supported by this function, +// including abstract and interface types. +func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType) error { + simpleTypeID, ok := simpleTypeIDByType(typ) + if ok { + return e.encodeSimpleType(simpleTypeID) + } + + switch typ := typ.(type) { + case *cadence.OptionalType: + return e.encodeOptionalType(typ, tids) + + case *cadence.VariableSizedArrayType: + return e.encodeVarSizedArrayType(typ, tids) + + case *cadence.ConstantSizedArrayType: + return e.encodeConstantSizedArrayType(typ, tids) + + case *cadence.DictionaryType: + return e.encodeDictType(typ, tids) + + case cadence.CompositeType, cadence.InterfaceType: + id, err := tids.id(typ) + if err != nil { + panic(err) + } + return e.encodeTypeRef(id) + + case *cadence.ReferenceType: + return e.encodeReferenceType(typ, tids) + + case *cadence.RestrictedType: + return e.encodeRestrictedType(typ, tids) + + case *cadence.CapabilityType: + return e.encodeCapabilityType(typ, tids) + + case *cadence.FunctionType: + return e.encodeSimpleType(TypeFunction) + + default: + panic(fmt.Errorf("unsupported type: %T, %v", typ, typ)) + } +} + +// encodeSimpleType encodes cadence simple type as +// language=CDDL +// simple-type = +// +// ; cbor-tag-simple-type +// #6.137(simple-type-id) +func (e *Encoder) encodeSimpleType(id uint64) error { + rawTagNum := []byte{0xd8, CBORTagSimpleType} + return e.encodeSimpleTypeWithRawTag(id, rawTagNum) +} + +// encodeSimpleTypeWithRawTag encodes simple type with given tag number as +// language=CDDL +// simple-type-id = uint +func (e *Encoder) encodeSimpleTypeWithRawTag(id uint64, rawTagNumber []byte) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode simple-type-id as uint. + return e.enc.EncodeUint64(id) +} + +// encodeOptionalType encodes cadence.OptionalType as +// language=CDDL +// optional-type = +// +// ; cbor-tag-optional-type +// #6.138(inline-type) +func (e *Encoder) encodeOptionalType( + typ *cadence.OptionalType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagOptionalType} + return e.encodeOptionalTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeOptionalTypeWithRawTag encodes cadence.OptionalType +// with given tag number and encode type function. +func (e *Encoder) encodeOptionalTypeWithRawTag( + typ *cadence.OptionalType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode non-optional type with given encodeTypeFn. + return encodeTypeFn(typ.Type, tids) +} + +// encodeVarSizedArrayType encodes cadence.VariableSizedArrayType as +// language=CDDL +// varsized-array-type = +// +// ; cbor-tag-varsized-array-type +// #6.139(inline-type) +func (e *Encoder) encodeVarSizedArrayType( + typ *cadence.VariableSizedArrayType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagVarsizedArrayType} + return e.encodeVarSizedArrayTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeVarSizedArrayTypeWithRawTag encodes cadence.VariableSizedArrayType +// with given tag number and encode type function. +func (e *Encoder) encodeVarSizedArrayTypeWithRawTag( + typ *cadence.VariableSizedArrayType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array element type with given encodeTypeFn. + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeConstantSizedArrayType encodes cadence.ConstantSizedArrayType as +// language=CDDL +// constsized-array-type = +// +// ; cbor-tag-constsized-array-type +// #6.140([ +// array-size: uint, +// element-type: inline-type +// ]) +func (e *Encoder) encodeConstantSizedArrayType( + typ *cadence.ConstantSizedArrayType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagConstsizedArrayType} + return e.encodeConstantSizedArrayTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeConstantSizedArrayTypeWithRawTag encodes cadence.ConstantSizedArrayType +// with given tag number and encode type function as +func (e *Encoder) encodeConstantSizedArrayTypeWithRawTag( + typ *cadence.ConstantSizedArrayType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: array size as uint + err = e.enc.EncodeUint(typ.Size) + if err != nil { + return err + } + + // element 1: array element type with given encodeTypeFn + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeDictType encodes cadence.DictionaryType as +// language=CDDL +// dict-type = +// +// ; cbor-tag-dict-type +// #6.141([ +// key-type: inline-type, +// element-type: inline-type +// ]) +func (e *Encoder) encodeDictType( + typ *cadence.DictionaryType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagDictType} + return e.encodeDictTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeDictTypeWithRawTag encodes cadence.DictionaryType +// with given tag number and encode type function. +func (e *Encoder) encodeDictTypeWithRawTag( + typ *cadence.DictionaryType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: key type with given encodeTypeFn + err = encodeTypeFn(typ.KeyType, tids) + if err != nil { + return err + } + + // element 1: element type with given encodeTypeFn + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeReferenceType encodes cadence.ReferenceType as +// language=CDDL +// reference-type = +// +// ; cbor-tag-reference-type +// #6.142([ +// authorized: bool, +// type: inline-type, +// ]) +func (e *Encoder) encodeReferenceType( + typ *cadence.ReferenceType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagReferenceType} + return e.encodeReferenceTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeReferenceTypeWithRawTag encodes cadence.ReferenceType +// with given tag number and encode type function. +func (e *Encoder) encodeReferenceTypeWithRawTag( + typ *cadence.ReferenceType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: authorized as bool + err = e.enc.EncodeBool(typ.Authorized) + if err != nil { + return err + } + + // element 1: referenced type with given encodeTypeFn + return encodeTypeFn(typ.Type, tids) +} + +// encodeRestrictedType encodes cadence.RestrictedType as +// language=CDDL +// restricted-type = +// +// ; cbor-tag-restricted-type +// #6.143([ +// cadence-type-id: cadence-type-id, +// type: inline-type, +// restrictions: [+ inline-type] +// ]) +func (e *Encoder) encodeRestrictedType(typ *cadence.RestrictedType, tids ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagRestrictedType} + return e.encodeRestrictedTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeRestrictedTypeWithRawTag encodes cadence.RestrictedType +// with given tag number and encode type function. +func (e *Encoder) encodeRestrictedTypeWithRawTag( + typ *cadence.RestrictedType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 3. + err = e.enc.EncodeArrayHead(3) + if err != nil { + return err + } + + // element 0: cadence type id. + err = e.encodeCadenceTypeID(typ.ID()) + if err != nil { + return err + } + + // element 1: type with given encodeTypeFn + err = encodeTypeFn(typ.Type, tids) + if err != nil { + return err + } + + // element 2: restrictions as array. + + // Encode array head with number of restrictions. + restrictions := typ.Restrictions + err = e.enc.EncodeArrayHead(uint64(len(restrictions))) + if err != nil { + return err + } + + if len(restrictions) == 1 { + // Avoid overhead of sorting if there is only one restriction. + // Encode restriction type with given encodeTypeFn. + return encodeTypeFn(restrictions[0], tids) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" + // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." + sorter := newBytewiseCadenceTypeSorter(restrictions) + + sort.Sort(sorter) + + for _, index := range sorter.indexes { + // Encode restriction type with given encodeTypeFn. + err = encodeTypeFn(restrictions[index], tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeCapabilityType encodes cadence.CapabilityType as +// language=CDDL +// capability-type = +// +// ; cbor-tag-capability-type +// ; use an array as an extension point +// #6.144([ +// ; borrow-type +// inline-type +// ]) +func (e *Encoder) encodeCapabilityType( + typ *cadence.CapabilityType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagCapabilityType} + return e.encodeCapabilityTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeCapabilityTypeWithRawTag encodes cadence.CapabilityType +// with given tag number and encode type function. +func (e *Encoder) encodeCapabilityTypeWithRawTag( + typ *cadence.CapabilityType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 1. + err = e.enc.EncodeArrayHead(1) + if err != nil { + return err + } + + // element 0: borrow type with given encodeTypeFn. + return encodeTypeFn(typ.BorrowType, tids) +} + +// encodeTypeRef encodes CCF type id as +// language=CDDL +// type-ref = +// +// ; cbor-tag-type-ref +// #6.136(id) +func (e *Encoder) encodeTypeRef(ref ccfTypeID) error { + rawTagNum := []byte{0xd8, CBORTagTypeRef} + return e.encodeTypeRefWithRawTag(ref, rawTagNum) +} + +// encodeTypeRefWithRawTag encodes CCF type ID as +// with given tag number. +func (e *Encoder) encodeTypeRefWithRawTag(ref ccfTypeID, rawTagNumber []byte) error { + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + return e.encodeCCFTypeID(ref) +} + +// encodeCCFTypeID encodes CCF type ID as +// language=CDDL +// id = bstr +func (e *Encoder) encodeCCFTypeID(id ccfTypeID) error { + return e.enc.EncodeBytes(id.Bytes()) +} + +// encodeCadenceTypeID encodes Cadence type ID as +// language=CDDL +// cadence-type-id = tstr +func (e *Encoder) encodeCadenceTypeID(id string) error { + return e.enc.EncodeString(id) +} diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go new file mode 100644 index 0000000000..54edf38568 --- /dev/null +++ b/encoding/ccf/encode_typedef.go @@ -0,0 +1,242 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "fmt" + + "github.com/onflow/cadence" +) + +// encodeCompositeType encodes cadence.CompositeType in type definition as +// language=CDDL +// struct-type = +// +// ; cbor-tag-struct-type +// #6.160(composite-type) +// +// resource-type = +// +// ; cbor-tag-resource-type +// #6.161(composite-type) +// +// event-type = +// +// ; cbor-tag-event-type +// #6.162(composite-type) +// +// contract-type = +// +// ; cbor-tag-contract-type +// #6.163(composite-type) +// +// enum-type = +// +// ; cbor-tag-enum-type +// #6.164(composite-type) +// +// composite-type = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// fields: [ +// + [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +// +// ] +func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { + ccfID, err := tids.id(typ) + if err != nil { + return fmt.Errorf("failed to find CCF type ID for composite type %v (%T)", typ, typ) + } + + var cborTagNum uint64 + + switch t := typ.(type) { + case *cadence.StructType: + cborTagNum = CBORTagStructType + case *cadence.ResourceType: + cborTagNum = CBORTagResourceType + case *cadence.EventType: + cborTagNum = CBORTagEventType + case *cadence.ContractType: + cborTagNum = CBORTagContractType + case *cadence.EnumType: + cborTagNum = CBORTagEnumType + default: + return fmt.Errorf("unsupported type in type definition: %T, %v", t, t) + } + + // Encode tag number indicating composite type. + err = e.enc.EncodeTagHead(cborTagNum) + if err != nil { + return err + } + + // Encode array head of length 3. + err = e.enc.EncodeArrayHead(3) + if err != nil { + return err + } + + // element 0: CCF type id + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence-type-id + err = e.encodeCadenceTypeID(typ.ID()) + if err != nil { + return err + } + + // element 2: fields as array + return e.encodeCompositeTypeFields(typ, tids) +} + +// encodeCompositeTypeFields encodes field types as +// language=CDDL +// +// fields: [ +// + [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { + fieldTypes := typ.CompositeFields() + + // Encode array head with number of fields. + err := e.enc.EncodeArrayHead(uint64(len(fieldTypes))) + if err != nil { + return err + } + + if len(fieldTypes) == 1 { + // Avoid overhead of sorting if there is only one field. + return e.encodeCompositeTypeField(fieldTypes[0], tids) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.fields MUST be sorted by name" + sortedIndexes := getSortedFieldIndex(typ) + + for _, index := range sortedIndexes { + // Encode field + err = e.encodeCompositeTypeField(fieldTypes[index], tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeCompositeTypeField encodes field type as +// language=CDDL +// +// [ +// field-name: tstr, +// field-type: inline-type +// ] +func (e *Encoder) encodeCompositeTypeField(typ cadence.Field, tids ccfTypeIDByCadenceType) error { + // Encode array head of length 2. + err := e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: field identifier as tstr + err = e.enc.EncodeString(typ.Identifier) + if err != nil { + return err + } + + // element 1: field type as inline-type + return e.encodeInlineType(typ.Type, tids) +} + +// encodeInterfaceType encodes cadence.InterfaceType as +// language=CDDL +// struct-interface-type = +// +// ; cbor-tag-struct-interface-type +// #6.176(interface-type) +// +// resource-interface-type = +// +// ; cbor-tag-resource-interface-type +// #6.177(interface-type) +// +// contract-interface-type = +// +// ; cbor-tag-contract-interface-type +// #6.178(interface-type) +// +// interface-type = [ +// +// id: id, +// cadence-type-id: tstr, +// +// ] +func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDByCadenceType) error { + ccfID, err := tids.id(typ) + if err != nil { + return fmt.Errorf("failed to find CCF type ID for interface type %v (%T)", typ, typ) + } + + var cborTagNum uint64 + + switch t := typ.(type) { + case *cadence.StructInterfaceType: + cborTagNum = CBORTagStructInterfaceType + case *cadence.ResourceInterfaceType: + cborTagNum = CBORTagResourceInterfaceType + case *cadence.ContractInterfaceType: + cborTagNum = CBORTagContractInterfaceType + default: + return fmt.Errorf("unsupported type in type definition: %T, %v", t, t) + } + + // Encode tag number indicating interface type. + err = e.enc.EncodeTagHead(cborTagNum) + if err != nil { + return err + } + + // Encode array head with length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: CCf type ID + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence-type-id + return e.encodeCadenceTypeID(typ.ID()) +} diff --git a/encoding/ccf/simple_type_utils.go b/encoding/ccf/simple_type_utils.go new file mode 100644 index 0000000000..92af6cb984 --- /dev/null +++ b/encoding/ccf/simple_type_utils.go @@ -0,0 +1,197 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import "github.com/onflow/cadence" + +// IMPORTANT: +// +// Don't change existing simple type IDs. +// +// When new simple cadence.Type is added, +// - add new ID to the end of existing IDs, +// - add new simple cadence.Type and its ID in simpleTypeIDByType() + +const ( // Cadence simple type IDs + TypeBool = iota + TypeString + TypeCharacter + TypeAddress + TypeInt + TypeInt8 + TypeInt16 + TypeInt32 + TypeInt64 + TypeInt128 + TypeInt256 + TypeUInt + TypeUInt8 + TypeUInt16 + TypeUInt32 + TypeUInt64 + TypeUInt128 + TypeUInt256 + TypeWord8 + TypeWord16 + TypeWord32 + TypeWord64 + TypeFix64 + TypeUFix64 + TypePath + TypeCapabilityPath + TypeStoragePath + TypePublicPath + TypePrivatePath + TypeAuthAccount + TypePublicAccount + TypeAuthAccountKeys + TypePublicAccountKeys + TypeAuthAccountContracts + TypePublicAccountContracts + TypeDeployedContract + TypeAccountKey + TypeBlock + TypeAny + TypeAnyStruct + TypeAnyResource + TypeMetaType + TypeNever + TypeNumber + TypeSignedNumber + TypeInteger + TypeSignedInteger + TypeFixedPoint + TypeSignedFixedPoint + TypeBytes + TypeVoid + TypeFunction +) + +// NOTE: cadence.FunctionType isn't included in simpleTypeIDByType +// because this function is used by both inline-type and type-value. +// cadence.FunctionType needs to be handled differently when this +// function is used by inline-type and type-value. +func simpleTypeIDByType(typ cadence.Type) (uint64, bool) { + switch typ.(type) { + case cadence.AnyType: + return TypeAny, true + case cadence.AnyStructType: + return TypeAnyStruct, true + case cadence.AnyResourceType: + return TypeAnyResource, true + case cadence.AddressType: + return TypeAddress, true + case cadence.MetaType: + return TypeMetaType, true + case cadence.VoidType: + return TypeVoid, true + case cadence.NeverType: + return TypeNever, true + case cadence.BoolType: + return TypeBool, true + case cadence.StringType: + return TypeString, true + case cadence.CharacterType: + return TypeCharacter, true + case cadence.BytesType: + return TypeBytes, true + case cadence.NumberType: + return TypeNumber, true + case cadence.SignedNumberType: + return TypeSignedNumber, true + case cadence.IntegerType: + return TypeInteger, true + case cadence.SignedIntegerType: + return TypeSignedInteger, true + case cadence.FixedPointType: + return TypeFixedPoint, true + case cadence.SignedFixedPointType: + return TypeSignedFixedPoint, true + case cadence.IntType: + return TypeInt, true + case cadence.Int8Type: + return TypeInt8, true + case cadence.Int16Type: + return TypeInt16, true + case cadence.Int32Type: + return TypeInt32, true + case cadence.Int64Type: + return TypeInt64, true + case cadence.Int128Type: + return TypeInt128, true + case cadence.Int256Type: + return TypeInt256, true + case cadence.UIntType: + return TypeUInt, true + case cadence.UInt8Type: + return TypeUInt8, true + case cadence.UInt16Type: + return TypeUInt16, true + case cadence.UInt32Type: + return TypeUInt32, true + case cadence.UInt64Type: + return TypeUInt64, true + case cadence.UInt128Type: + return TypeUInt128, true + case cadence.UInt256Type: + return TypeUInt256, true + case cadence.Word8Type: + return TypeWord8, true + case cadence.Word16Type: + return TypeWord16, true + case cadence.Word32Type: + return TypeWord32, true + case cadence.Word64Type: + return TypeWord64, true + case cadence.Fix64Type: + return TypeFix64, true + case cadence.UFix64Type: + return TypeUFix64, true + case cadence.BlockType: + return TypeBlock, true + case cadence.PathType: + return TypePath, true + case cadence.CapabilityPathType: + return TypeCapabilityPath, true + case cadence.StoragePathType: + return TypeStoragePath, true + case cadence.PublicPathType: + return TypePublicPath, true + case cadence.PrivatePathType: + return TypePrivatePath, true + case cadence.AccountKeyType: + return TypeAccountKey, true + case cadence.AuthAccountContractsType: + return TypeAuthAccountContracts, true + case cadence.AuthAccountKeysType: + return TypeAuthAccountKeys, true + case cadence.AuthAccountType: + return TypeAuthAccount, true + case cadence.PublicAccountContractsType: + return TypePublicAccountContracts, true + case cadence.PublicAccountKeysType: + return TypePublicAccountKeys, true + case cadence.PublicAccountType: + return TypePublicAccount, true + case cadence.DeployedContractType: + return TypeDeployedContract, true + } + + return 0, false +} diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go new file mode 100644 index 0000000000..c75d6a764b --- /dev/null +++ b/encoding/ccf/sort.go @@ -0,0 +1,207 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "bytes" + + "github.com/onflow/cadence" +) + +// bytewiseFieldSorter + +// bytewiseFieldSorter is used to sort fields by identifier. +// NOTE: in order to avoid making a copy of fields for sorting, +// - create sorter by calling newBytewiseFieldIdentifierSorter() +// - sort by calling sort.Sort(sorter) +// - iterate sorted fields by +// for _, index := range sorter.indexes { +// // process sorted field at fields[index] +// } +type bytewiseFieldSorter struct { + // NOTE: DON'T sort fields because it isn't a copy. + fields []cadence.Field + // indexes represents sorted indexes of fields + indexes []int +} + +func newBytewiseFieldSorter(types []cadence.Field) bytewiseFieldSorter { + indexes := make([]int, len(types)) + for i := 0; i < len(indexes); i++ { + indexes[i] = i + } + return bytewiseFieldSorter{types, indexes} +} + +func (x bytewiseFieldSorter) Len() int { + return len(x.indexes) +} + +func (x bytewiseFieldSorter) Swap(i, j int) { + x.indexes[i], x.indexes[j] = x.indexes[j], x.indexes[i] +} + +func (x bytewiseFieldSorter) Less(i, j int) bool { + i = x.indexes[i] + j = x.indexes[j] + + iIdentifier := x.fields[i].Identifier + jIdentifier := x.fields[j].Identifier + + if len(iIdentifier) != len(jIdentifier) { + return len(iIdentifier) < len(jIdentifier) + } + return iIdentifier <= jIdentifier +} + +// bytewiseParameterSorter + +// bytewiseParameterSorter is used to sort parameters by identifier. +// NOTE: in order to avoid making a copy of parameters for sorting, +// - create sorter by calling newBytewiseParameterSorter() +// - sort by calling sort.Sort(sorter) +// - iterate sorted fields by +// for _, index := range sorter.indexes { +// // process sorted field at parameters[index] +// } +type bytewiseParameterSorter struct { + // NOTE: DON'T sort parameters because it isn't a copy. + parameters []cadence.Parameter + // indexes represents sorted indexes of fields + indexes []int +} + +func newBytewiseParameterSorter(parameters []cadence.Parameter) bytewiseParameterSorter { + indexes := make([]int, len(parameters)) + for i := 0; i < len(indexes); i++ { + indexes[i] = i + } + return bytewiseParameterSorter{parameters, indexes} +} + +func (x bytewiseParameterSorter) Len() int { + return len(x.indexes) +} + +func (x bytewiseParameterSorter) Swap(i, j int) { + x.indexes[i], x.indexes[j] = x.indexes[j], x.indexes[i] +} + +func (x bytewiseParameterSorter) Less(i, j int) bool { + i = x.indexes[i] + j = x.indexes[j] + + iIdentifier := x.parameters[i].Identifier + jIdentifier := x.parameters[j].Identifier + + if len(iIdentifier) != len(jIdentifier) { + return len(iIdentifier) < len(jIdentifier) + } + return iIdentifier <= jIdentifier +} + +// bytewiseKeyValuePairSorter + +type encodedKeyValuePair struct { + encodedKey []byte + encodedPair []byte + keyLength, pairLength int +} + +type bytewiseKeyValuePairSorter []encodedKeyValuePair + +func (x bytewiseKeyValuePairSorter) Len() int { + return len(x) +} + +func (x bytewiseKeyValuePairSorter) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x bytewiseKeyValuePairSorter) Less(i, j int) bool { + return bytes.Compare(x[i].encodedKey, x[j].encodedKey) <= 0 +} + +// bytewiseCadenceTypeInPlaceSorter + +// bytewiseCadenceTypeInPlaceSorter is used to sort Cadence types by Cadence type ID. +type bytewiseCadenceTypeInPlaceSorter []cadence.Type + +func (t bytewiseCadenceTypeInPlaceSorter) Len() int { + return len(t) +} + +func (t bytewiseCadenceTypeInPlaceSorter) Swap(i, j int) { + t[i], t[j] = t[j], t[i] +} + +func (t bytewiseCadenceTypeInPlaceSorter) Less(i, j int) bool { + iID := t[i].ID() + jID := t[j].ID() + if len(iID) != len(jID) { + return len(iID) < len(jID) + } + return iID <= jID +} + +// bytewiseCadenceTypeSorter + +// bytewiseCadenceTypeSorter is used to sort Cadence types by Cadence type ID. +type bytewiseCadenceTypeSorter struct { + // NOTE: DON'T sort fields because it isn't a copy. + types []cadence.Type + // indexes represents sorted indexes of fields + indexes []int +} + +func newBytewiseCadenceTypeSorter(types []cadence.Type) bytewiseCadenceTypeSorter { + indexes := make([]int, len(types)) + for i := 0; i < len(indexes); i++ { + indexes[i] = i + } + return bytewiseCadenceTypeSorter{types, indexes} +} + +func (t bytewiseCadenceTypeSorter) Len() int { + return len(t.indexes) +} + +func (t bytewiseCadenceTypeSorter) Swap(i, j int) { + t.indexes[i], t.indexes[j] = t.indexes[j], t.indexes[i] +} + +func (t bytewiseCadenceTypeSorter) Less(i, j int) bool { + iID := t.types[i].ID() + jID := t.types[j].ID() + if len(iID) != len(jID) { + return len(iID) < len(jID) + } + return iID <= jID +} + +// Utility sort functions + +func stringsAreSortedBytewise(s1, s2 string) bool { + return len(s1) < len(s2) || + (len(s1) == len(s2) && s1 < s2) +} + +func bytesAreSortedBytewise(b1, b2 []byte) bool { + return bytes.Compare(b1, b2) <= 0 +} diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go new file mode 100644 index 0000000000..8b0b308248 --- /dev/null +++ b/encoding/ccf/traverse_value.go @@ -0,0 +1,229 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf + +import ( + "sort" + + "github.com/onflow/cadence" +) + +type compositeTypes struct { + ids ccfTypeIDByCadenceType + types []cadence.Type +} + +// compositeTypesFromValue returns all composite/interface types for value v. +// Returned types are sorted unique list of static and runtime composite/interface types. +// NOTE: nested composite/interface types are included in the returned types. +func compositeTypesFromValue(v cadence.Value) ([]cadence.Type, ccfTypeIDByCadenceType) { + ct := &compositeTypes{ + ids: make(ccfTypeIDByCadenceType), + types: make([]cadence.Type, 0, 1), + } + + // Traverse v to get all unique: + // - static composite types + // - static interface types + // - runtime composite types + // - runtime interface types + ct.traverseValue(v) + + if len(ct.ids) < 2 { + // No need to reassign ccf id, nor sort types. + return ct.types, ct.ids + } + + // Sort Cadence types by Cadence type ID. + sort.Sort(bytewiseCadenceTypeInPlaceSorter(ct.types)) + + // Assign sorted array index as local ccf ID. + for i, t := range ct.types { + ct.ids[t.ID()] = ccfTypeID(i) + } + + return ct.types, ct.ids +} + +func (ct *compositeTypes) traverseValue(v cadence.Value) { + + // Traverse type for composite/interface types. + checkRuntimeType := ct.traverseType(v.Type()) + + if !checkRuntimeType { + // Return without traversing value for runtime types. + return + } + + // Traverse v's elements for runtime types. + switch x := v.(type) { + + case cadence.Optional: + ct.traverseValue(x.Value) + + case cadence.Array: + for i := 0; i < len(x.Values); i++ { + ct.traverseValue(x.Values[i]) + } + + case cadence.Dictionary: + for i := 0; i < len(x.Pairs); i++ { + ct.traverseValue(x.Pairs[i].Key) + ct.traverseValue(x.Pairs[i].Value) + } + + case cadence.Struct: + for i := 0; i < len(x.Fields); i++ { + ct.traverseValue(x.Fields[i]) + } + + case cadence.Resource: + for i := 0; i < len(x.Fields); i++ { + ct.traverseValue(x.Fields[i]) + } + + case cadence.Event: + for i := 0; i < len(x.Fields); i++ { + ct.traverseValue(x.Fields[i]) + } + + case cadence.Contract: + for i := 0; i < len(x.Fields); i++ { + ct.traverseValue(x.Fields[i]) + } + + case cadence.Enum: + for i := 0; i < len(x.Fields); i++ { + ct.traverseValue(x.Fields[i]) + } + } +} + +// traverseType traverses cadence type typ to find composite/interface types and +// returns true if typ contains any abstract type and runtime type needs to be checked. +// It recurisvely traverse cadence.Type if the type contains other cadence.Type, +// such as OptionalType. +// Runtime needs to be checked when typ contains any abstract type. +func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) { + switch t := typ.(type) { + + case *cadence.OptionalType: + return ct.traverseType(t.Type) + + case cadence.ArrayType: + return ct.traverseType(t.Element()) + + case *cadence.DictionaryType: + checkKeyRuntimeType := ct.traverseType(t.KeyType) + checkValueRuntimeType := ct.traverseType(t.ElementType) + return checkKeyRuntimeType || checkValueRuntimeType + + case *cadence.CapabilityType: + return ct.traverseType(t.BorrowType) + + case *cadence.ReferenceType: + return ct.traverseType(t.Type) + + case *cadence.RestrictedType: + check := ct.traverseType(t.Type) + for _, restriction := range t.Restrictions { + checkRestriction := ct.traverseType(restriction) + check = check || checkRestriction + } + return check + + case cadence.CompositeType: // struct, resource, event, contract, enum + check := false + + newType := ct.add(t) + if newType { + fields := t.CompositeFields() + for _, f := range fields { + checkField := ct.traverseType(f.Type) + check = check || checkField + } + + // Don't need to traverse initializers because + // they are not encoded and their types aren't needed. + } + + return check + + case cadence.InterfaceType: // struct interface, resource interface, contract interface + ct.add(t) + // Don't need to traverse fields or initializers because + // they are not encoded and their types aren't needed. + + // Return true to check runtime type. + return true + + case cadence.VoidType, + cadence.BoolType, + cadence.NeverType, + cadence.CharacterType, + cadence.StringType, + cadence.BytesType, + cadence.AddressType, + cadence.IntType, + cadence.Int8Type, + cadence.Int16Type, + cadence.Int32Type, + cadence.Int64Type, + cadence.Int128Type, + cadence.Int256Type, + cadence.UIntType, + cadence.UInt8Type, + cadence.UInt16Type, + cadence.UInt32Type, + cadence.UInt64Type, + cadence.UInt128Type, + cadence.UInt256Type, + cadence.Word8Type, + cadence.Word16Type, + cadence.Word32Type, + cadence.Word64Type, + cadence.Fix64Type, + cadence.UFix64Type, + cadence.PathType, + cadence.MetaType, + *cadence.FunctionType, + cadence.NumberType, + cadence.SignedNumberType, + cadence.IntegerType, + cadence.SignedIntegerType, + cadence.FixedPointType, + cadence.SignedFixedPointType: + // TODO: Maybe there are more types that we can skip checking runtime type for composite type. + + return false + + default: + return true + } +} + +func (ct *compositeTypes) add(t cadence.Type) bool { + cadenceTypeID := t.ID() + if _, ok := ct.ids[cadenceTypeID]; ok { + return false + } + ct.ids[cadenceTypeID] = 0 + ct.types = append(ct.types, t) + return true +} From a2c6bc0bccaf610342379c199fb4959d6da25db3 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:17:48 -0600 Subject: [PATCH 004/173] Add CCF codec tests --- encoding/ccf/ccf_test.go | 8208 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 8208 insertions(+) create mode 100644 encoding/ccf/ccf_test.go diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go new file mode 100644 index 0000000000..03c19d368e --- /dev/null +++ b/encoding/ccf/ccf_test.go @@ -0,0 +1,8208 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf_test + +import ( + "bytes" + "encoding/hex" + "fmt" + "math" + "math/big" + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/runtime" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/tests/checker" + "github.com/onflow/cadence/runtime/tests/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type encodeTest struct { + name string + val cadence.Value + expected []byte +} + +func TestEncodeVoid(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.NewVoid(), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Void"} + // + // language=edn, format=ccf + // 130([137(50), null]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // void type ID (50) + 0x18, 0x32, + // nil + 0xf6, + }, + ) +} + +func TestEncodeOptional(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Optional(nil)", + cadence.NewOptional(nil), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Optional","value":null} + // + // language=edn, format=ccf + // 130([138(137(42)), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + "Optional(non-nil)", + cadence.NewOptional(cadence.NewInt(42)), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Optional","value":{"type":"Int","value":"42"}} + // + // language=edn, format=ccf + // 130([138(137(4)), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + "Optional(Optional(nil))", + cadence.NewOptional(cadence.NewOptional(nil)), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":{"value":null,"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(137(42))), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + "Optional(Optional(non-nil))", + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(137(4))), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + "Optional(Optional(Optional(nil)))", + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(138(137(42)))), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + "Optional(Optional(Optional(non-nil)))", + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":{"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(138(137(4)))), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + }...) +} + +func TestEncodeBool(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "True", + cadence.NewBool(true), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Bool","value":true} + // + // language=edn, format=ccf + // 130([137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + "False", + cadence.NewBool(false), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Bool","value":false} + // + // language=edn, format=ccf + // 130([137(0), false]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // false + 0xf4, + }, + }, + }...) +} + +func TestEncodeCharacter(t *testing.T) { + + t.Parallel() + + a, _ := cadence.NewCharacter("a") + b, _ := cadence.NewCharacter("b") + + testAllEncodeAndDecode(t, []encodeTest{ + { + "a", + a, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Character","value":"a"} + // + // language=edn, format=ccf + // 130([137(2), "a"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Character type ID (2) + 0x02, + // UTF-8 string, 1 bytes follow + 0x61, + // a + 0x61, + }, + }, + { + "b", + b, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Character","value":"b"} + // + // language=edn, format=ccf + // 130([137(2), "b"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Character type ID (2) + 0x02, + // UTF-8 string, 1 bytes follow + 0x61, + // b + 0x62, + }, + }, + }...) +} + +func TestEncodeString(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Empty", + cadence.String(""), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"String","value":""} + // + // language=edn, format=ccf + // 130([137(1), ""]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // UTF-8 string, 0 bytes follow + 0x60, + }, + }, + { + "Non-empty", + cadence.String("foo"), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"String","value":"foo"} + // + // language=edn, format=ccf + // 130([137(1), "foo"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // UTF-8 string, 3 bytes follow + 0x63, + // f, o, o + 0x66, 0x6f, 0x6f, + }, + }, + }...) +} + +func TestEncodeAddress(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Address","value":"0x0000000102030405"} + // + // language=edn, format=ccf + // 130([137(3), h'0000000102030405']) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // bytes, 8 bytes follow + 0x48, + // 0, 0, 0, 1, 2, 3, 4, 5 + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x5, + }, + ) +} + +func TestEncodeInt(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Negative", + cadence.NewInt(-42), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int","value":"-42"} + // + // language=edn, format=ccf + // 130([137(4), -42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (negative big number) + 0xc3, + // bytes, 1 byte follow + 0x41, + // -42 + 0x29, + }, + }, + { + "Zero", + cadence.NewInt(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int","value":"0"} + // + // language=edn, format=ccf + // 130([137(4), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 0 byte follow + 0x40, + }, + }, + { + "Positive", + cadence.NewInt(42), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int","value":"42"} + // + // language=edn, format=ccf + // 130([137(4), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + "SmallerThanMinInt256", + cadence.NewIntFromBig(new(big.Int).Sub(sema.Int256TypeMinIntBig, big.NewInt(10))), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819978"} + // + // language=edn, format=ccf + // 130([137(4), -57896044618658097711785492504343953926634992332820282019728792003956564819978]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (negative big number) + 0xc3, + // bytes, 32 bytes follow + 0x58, 0x20, + // -57896044618658097711785492504343953926634992332820282019728792003956564819978 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + }, + }, + { + "LargerThanMaxUInt256", + cadence.NewIntFromBig(new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} + // + // language=edn, format=ccf + // 130([137(4), 115792089237316195423570985008687907853269984665640564039457584007913129639945]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 33 bytes follow + 0x58, 0x21, + // 115792089237316195423570985008687907853269984665640564039457584007913129639945 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, + }, + }, + }...) +} + +func TestEncodeInt8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.NewInt8(math.MinInt8), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int8","value":"-128"} + // + // language=edn, format=ccf + // 130([137(5), -128]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // -128 + 0x38, 0x7f, + }, + }, + { + "Zero", + cadence.NewInt8(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int8","value":"0"} + // + // language=edn, format=ccf + // 130([137(5), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewInt8(math.MaxInt8), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int8","value":"127"} + // + // language=edn, format=ccf + // 130([137(5), 127]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 127 + 0x18, 0x7f, + }, + }, + }...) +} + +func TestEncodeInt16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.NewInt16(math.MinInt16), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int16","value":"-32768"} + // + // language=edn, format=ccf + // 130([137(6), -32768]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // -32768 + 0x39, 0x7F, 0xFF, + }, + }, + { + "Zero", + cadence.NewInt16(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int16","value":"0"} + // + // language=edn, format=ccf + // 130([137(6), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewInt16(math.MaxInt16), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int16","value":"32767"} + // + // language=edn, format=ccf + // 130([137(6), 32767]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 32767 + 0x19, 0x7F, 0xFF, + }, + }, + }...) +} + +func TestEncodeInt32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.NewInt32(math.MinInt32), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int32","value":"-2147483648"} + // + // language=edn, format=ccf + // 130([137(7), -2147483648]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // -2147483648 + 0x3a, 0x7f, 0xff, 0xff, 0xff, + }, + }, + { + "Zero", + cadence.NewInt32(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int32","value":"0"} + // + // language=edn, format=ccf + // 130([137(7), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewInt32(math.MaxInt32), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int32","value":"2147483647"} + // + // language=edn, format=ccf + // 130([137(7), 2147483647]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 2147483647 + 0x1a, 0x7f, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.NewInt64(math.MinInt64), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int64","value":"-9223372036854775808"} + // + // language=edn, format=ccf + // 130([137(8), -9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // -9223372036854775808 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + "Zero", + cadence.NewInt64(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int64","value":"0"} + // + // language=edn, format=ccf + // 130([137(8), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewInt64(math.MaxInt64), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int64","value":"9223372036854775807"} + // + // language=edn, format=ccf + // 130([137(8), 9223372036854775807]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // 9223372036854775807 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt128(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.Int128{Value: sema.Int128TypeMinIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int128","value":"-170141183460469231731687303715884105728"} + // + // language=edn, format=ccf + // 130([137(9), -170141183460469231731687303715884105728]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc3, + // bytes, 16 bytes follow + 0x50, + // -170141183460469231731687303715884105728 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + "Zero", + cadence.NewInt128(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int128","value":"0"} + // + // language=edn, format=ccf + // 130([137(9), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + "Max", + cadence.Int128{Value: sema.Int128TypeMaxIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int128","value":"170141183460469231731687303715884105727"} + // + // language=edn, format=ccf + // 130([137(9), 170141183460469231731687303715884105727]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc2, + // bytes, 16 bytes follow + 0x50, + // 170141183460469231731687303715884105727 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt256(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Min", + cadence.Int256{Value: sema.Int256TypeMinIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int256","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819968"} + // + // language=edn, format=ccf + // 130([137(10), -57896044618658097711785492504343953926634992332820282019728792003956564819968]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc3, + // bytes, 32 bytes follow + 0x58, 0x20, + // -57896044618658097711785492504343953926634992332820282019728792003956564819968 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + "Zero", + cadence.NewInt256(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int256","value":"0"} + // + // language=edn, format=ccf + // 130([137(10), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + "Max", + cadence.Int256{Value: sema.Int256TypeMaxIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Int256","value":"57896044618658097711785492504343953926634992332820282019728792003956564819967"} + // + // language=edn, format=ccf + // 130([137(10), 57896044618658097711785492504343953926634992332820282019728792003956564819967]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x20, + // 57896044618658097711785492504343953926634992332820282019728792003956564819967 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt","value":"0"} + // + // language=edn, format=ccf + // 130([137(11), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + "Positive", + cadence.NewUInt(42), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt","value":"42"} + // + // language=edn, format=ccf + // 130([137(11), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 42 + 0x2a, + }, + }, + { + "LargerThanMaxUInt256", + cadence.UInt{Value: new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} + // + // language=edn, format=ccf + // 130([137(11), 115792089237316195423570985008687907853269984665640564039457584007913129639945]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x21, + // 115792089237316195423570985008687907853269984665640564039457584007913129639945 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, + }, + }, + }...) +} + +func TestEncodeUInt8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt8(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt8","value":"0"} + // + // language=edn, format=ccf + // 130([137(12), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewUInt8(math.MaxUint8), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt8","value":"255"} + // + // language=edn, format=ccf + // 130([137(12), 255]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 255 + 0x18, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt16(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt16","value":"0"} + // + // language=edn, format=ccf + // 130([137(13), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt16 type ID (13) + 0x0d, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewUInt16(math.MaxUint16), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt16","value":"65535"} + // + // language=edn, format=ccf + // 130([137(13), 65535]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt16 type ID (13) + 0x0d, + // 65535 + 0x19, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt32(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt32","value":"0"} + // + // language=edn, format=ccf + // 130([137(14), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewUInt32(math.MaxUint32), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt32","value":"4294967295"} + // + // language=edn, format=ccf + // 130([137(14), 4294967295]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + // 4294967295 + 0x1a, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt64(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt64","value":"0"} + // + // language=edn, format=ccf + // 130([137(15), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt64 type ID (15) + 0x0f, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewUInt64(uint64(math.MaxUint64)), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt64","value":"18446744073709551615"} + // + // language=edn, format=ccf + // 130([137(15), 18446744073709551615]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt64 type ID (15) + 0x0f, + // 18446744073709551615 + 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt128(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt128(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt128","value":"0"} + // + // language=edn, format=ccf + // 130([137(16), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt128 type ID (16) + 0x10, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + "Max", + cadence.UInt128{Value: sema.UInt128TypeMaxIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt128","value":"340282366920938463463374607431768211455"} + // + // language=edn, format=ccf + // 130([137(16), 340282366920938463463374607431768211455]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt128 type ID (16) + 0x10, + // tag (big num) + 0xc2, + // bytes, 16 bytes follow + 0x50, + // 340282366920938463463374607431768211455 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt256(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewUInt256(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt256","value":"0"} + // + // language=edn, format=ccf + // 130([137(17), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt256 type ID (17) + 0x11, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + "Max", + cadence.UInt256{Value: sema.UInt256TypeMaxIntBig}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UInt256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"} + // + // language=edn, format=ccf + // 130([137(17), 115792089237316195423570985008687907853269984665640564039457584007913129639935]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt256 type ID (17) + 0x11, + // tag (big num) + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x20, + // 115792089237316195423570985008687907853269984665640564039457584007913129639935 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewWord8(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word8","value":"0"} + // + // language=edn, format=ccf + // 130([137(18), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word8 type ID (18) + 0x12, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewWord8(math.MaxUint8), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word8","value":"255"} + // + // language=edn, format=ccf + // 130([137(18), 255]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word8 type ID (18) + 0x12, + // 255 + 0x18, 0xff, + }, + }, + }...) +} + +func TestEncodeWord16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewWord16(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word16","value":"0"} + // + // language=edn, format=ccf + // 130([137(19), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word16 type ID (19) + 0x13, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewWord16(math.MaxUint16), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word16","value":"65535"} + // + // language=edn, format=ccf + // 130([137(19), 65535]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word16 type ID (19) + 0x13, + // 65535 + 0x19, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewWord32(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word32","value":"0"} + // + // language=edn, format=ccf + // 130([137(20), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word32 type ID (20) + 0x14, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewWord32(math.MaxUint32), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word32","value":"4294967295"} + // + // language=edn, format=ccf + // 130([137(20), 4294967295]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word32 type ID (20) + 0x14, + // 4294967295 + 0x1a, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.NewWord64(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word64","value":"0"} + // + // language=edn, format=ccf + // 130([137(21), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word64 type ID (21) + 0x15, + // 0 + 0x00, + }, + }, + { + "Max", + cadence.NewWord64(math.MaxUint64), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Word64","value":"18446744073709551615"} + // + // language=edn, format=ccf + // 130([137(21), 18446744073709551615]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word64 type ID (21) + 0x15, + // 18446744073709551615 + 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeFix64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.Fix64(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Fix64","value":"0.00000000"} + // + // language=edn, format=ccf + // 130([137(22), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 0 + 0x00, + }, + }, + { + "789.00123010", + cadence.Fix64(78_900_123_010), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Fix64","value":"789.00123010"} + // + // language=edn, format=ccf + // 130([137(22), 78900123010]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 78900123010 + 0x1b, 0x00, 0x00, 0x00, 0x12, 0x5e, 0xd0, 0x55, 0x82, + }, + }, + { + "1234.056", + cadence.Fix64(123_405_600_000), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Fix64","value":"1234.05600000"} + // + // language=edn, format=ccf + // 130([137(22), 123405600000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 123405600000 + 0x1b, 0x00, 0x00, 0x00, 0x1c, 0xbb, 0x8c, 0x05, 0x00, + }, + }, + { + "-12345.006789", + cadence.Fix64(-1_234_500_678_900), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Fix64","value":"-12345.00678900"} + // + // language=edn, format=ccf + // 130([137(22), -1234500678900]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // -1234500678900 + 0x3b, 0x00, 0x00, 0x01, 0x1f, 0x6d, 0xf9, 0x74, 0xf3, + }, + }, + }...) +} + +func TestEncodeUFix64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + "Zero", + cadence.UFix64(0), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UFix64","value":"0.00000000"} + // + // language=edn, format=ccf + // 130([137(23), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 0 + 0x00, + }, + }, + { + "789.00123010", + cadence.UFix64(78_900_123_010), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UFix64","value":"789.00123010"} + // + // language=edn, format=ccf + // 130([137(23), 78900123010]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 78900123010 + 0x1b, 0x00, 0x00, 0x00, 0x12, 0x5e, 0xd0, 0x55, 0x82, + }, + }, + { + "1234.056", + cadence.UFix64(123_405_600_000), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"UFix64","value":"1234.05600000"} + // + // language=edn, format=ccf + // 130([137(23), 123405600000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 123405600000 + 0x1b, 0x00, 0x00, 0x00, 0x1c, 0xbb, 0x8c, 0x05, 0x00, + }, + }, + }...) +} + +func TestEncodeArray(t *testing.T) { + + t.Parallel() + + // [] + emptyArray := encodeTest{ + "Empty", + cadence.NewArray( + []cadence.Value{}, + ).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Array","value":[]} + // + // language=edn, format=ccf + // 130([139(137(4)), []]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []int + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type + // array, 0 items follow + 0x80, + }, + } + + // [1, 2, 3] + intArray := encodeTest{ + "Integers", + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + cadence.NewInt(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} + // + // language=edn, format=ccf + // 130([139(137(4)), [1, 2, 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []int + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // [S.test.Foo{1}, S.test.Foo{2}, S.test.Foo{3}] + resourceArray := encodeTest{ + "Resources", + cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }).WithType(cadence.NewVariableSizedArrayType(fooResourceType)), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [139(136(h'')), [[1], [2], [3]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // [S.test.Foo{1}, S.test.Foo{2}, S.test.Foo{3}] + // array, 3 items follow + 0x83, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + s, err := cadence.NewString("a") + require.NoError(t, err) + + resourceWithAbstractFieldArray := encodeTest{ + "Resources with abstract field", + cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(1), + }).WithType(foooResourceTypeWithAbstractField), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + s, + }).WithType(foooResourceTypeWithAbstractField), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + cadence.NewBool(true), + }).WithType(foooResourceTypeWithAbstractField), + }).WithType(cadence.NewVariableSizedArrayType(foooResourceTypeWithAbstractField)), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}},{"name":"baz","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}},{"name":"baz","value":{"type":"String","value":"a"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}},{"name":"baz","value":{"type":"Bool","value":true}}]}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Fooo", [["bar", 137(4)], ["baz", 137(39)]]])], [139(136(h'')), [[1, 130([137(4), 1])], [2, 130([137(1), "a"])], [3, 130([137(0), true])]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Fooo" + // fields: [["bar", int type], ["baz", any type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // S.test.Fooo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // 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, + // field 1 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // [S.test.Foo{1, 1}, S.test.Foo{2, "a"}, S.test.Foo{3, true}] + // array, 3 items follow + 0x83, + // element 0 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, 1 byte + 0x61, + // "a" + 0x61, + // element 2 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + } + + // [1, "a", true] + heterogeneousSimpleTypeArray := encodeTest{ + "Heterogenous AnyStruct Array with Simple Values", + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + s, + cadence.NewBool(true), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"String","value":"a"},{"type":"Bool","value":true}]} + // + // language=edn, format=ccf + // 130([139(137(39)), [130([137(4), 1]), 130([137(1), "a"]), 130([137(0), true])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type ([]AnyStruct) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data with inlined type because static array element type is abstract (AnyStruct) + // array, 3 items follow + 0x83, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, length 1 + 0x61, + // "a" + 0x61, + // element 2 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + } + + // [Int8(1), Int16(2), Int32(3)] + heterogeneousNumberTypeArray := encodeTest{ + "Heterogeous Number Array", + cadence.NewArray([]cadence.Value{ + cadence.NewInt8(1), + cadence.NewInt16(2), + cadence.NewInt32(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewNumberType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":[{"value":"1","type":"Int8"},{"value":"2","type":"Int16"},{"value":"3","type":"Int32"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(43)), [130([137(5), 1]), 130([137(6), 2]), 130([137(7), 3])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type ([]Integer) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Number type ID (43) + 0x18, 0x2b, + // array data with inlined type because static array element type is abstract (Number) + // array, 3 items follow + 0x83, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 2 + 0x02, + // element 2 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 3 + 0x03, + }, + } + + // [1, S.test.Foo{1}] + heterogeneousCompositeTypeArray := encodeTest{ + "Heterogenous AnyStruct Array with Composite Value", + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":[{"value":"1","type":"Int"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [139(137(39)), [130([137(4), 1]), 130([136(h''), [1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // element 1: type and value + // array, 2 items follow + 0x82, + // type ([]AnyStruct) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data with inlined type because static array element type is abstract (AnyStruct) + // array, 2 items follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // S.test.Foo{1} + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + + resourceInterfaceType := &cadence.ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + } + + // [S.test.Foo{1}, S.test.Fooo{2, "a"}] + heterogeneousInterfaceTypeArray := encodeTest{ + "Heterogenous Interface Array", + cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + s, + }).WithType(foooResourceTypeWithAbstractField), + }).WithType(cadence.NewVariableSizedArrayType(resourceInterfaceType)), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},{"value":{"id":"S.test.Fooo","fields":[{"value":{"value":"2","type":"Int"},"name":"bar"},{"value":{"value":"a","type":"String"},"name":"baz"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[177([h'', "S.test.Bar"]), 161([h'01', "S.test.Foo", [["bar", 137(4)]]]), 161([h'02', "S.test.Fooo", [["bar", 137(4)], ["baz", 137(39)]]])], [139(136(h'')), [130([136(h'01'), [1]]), 130([136(h'02'), [2, 130([137(1), "a"])]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // type definition 0 + // resource interface type: + // id: []byte{} + // cadence-type-id: "S.test.Bar" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Boo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type definition 1 + // resource type: + // id: []byte{1} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + // type definition 2: + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Fooo" + // fields: [["bar", int type], ["baz", any type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // S.test.Fooo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // 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, + // field 1 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // S.test.Foo{1} + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // S.test.Fooo{2, "a"} + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, 1 byte + 0x61, + // "a" + 0x61, + }, + } + + testAllEncodeAndDecode(t, + emptyArray, + intArray, + resourceArray, + resourceWithAbstractFieldArray, + heterogeneousSimpleTypeArray, + heterogeneousNumberTypeArray, + heterogeneousCompositeTypeArray, + heterogeneousInterfaceTypeArray, + ) +} + +func TestEncodeDictionary(t *testing.T) { + + t.Parallel() + + emptyDict := encodeTest{ + "empty", + cadence.NewDictionary( + []cadence.KeyValuePair{}, + ).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"value":[],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), []]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 6 items follow + 0x80, + }, + } + + // {"a":1, "b":2, "c":3} + simpleDict := encodeTest{ + "Simple", + cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), ["a", 1, "b", 2, "c", 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // {"a":{"1":1}, "b":{"2":2}, "c":{"3:3"}} + nestedDict := encodeTest{ + "Nested", + cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("1"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("b"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("2"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("c"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("3"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + ), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"1"},"value":{"type":"Int","value":"1"}}]}},{"key":{"type":"String","value":"b"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"2"},"value":{"type":"Int","value":"2"}}]}},{"key":{"type":"String","value":"c"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"3"},"value":{"type":"Int","value":"3"}}]}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 141([137(1), 137(4)])]), ["a", ["1", 1], "b", ["2", 2], "c", ["3", 3]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]map[string, int]) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "1" + 0x31, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "2" + 0x32, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "3" + 0x33, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // {"a":foo{1}, "b":foo{2}, "c":foo{3}} + resourceDict := encodeTest{ + "Resources", + cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("b"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("c"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + fooResourceType, + )), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}}},{"key":{"type":"String","value":"b"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}}},{"key":{"type":"String","value":"c"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [141([137(1), 136(h'')]), ["a", [1], "b", [2], "c", [3]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + testAllEncodeAndDecode(t, + emptyDict, + simpleDict, + nestedDict, + resourceDict, + ) +} + +func TestEncodeSortedDictionary(t *testing.T) { + type testcase struct { + name string + val cadence.Value + expectedVal cadence.Value + expectedCBOR []byte + } + + dict := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())) + + expectedDict := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())) + + simpleDict := testcase{ + "Simple", + dict, + expectedDict, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), ["a", 1, "b", 2, "c", 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + testcases := []testcase{ + simpleDict, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + actualCBOR := testEncode(t, tc.val, tc.expectedCBOR) + testDecode(t, actualCBOR, tc.expectedVal) + }) + } +} + +func exportFromScript(t *testing.T, code string) cadence.Value { + checker, err := checker.ParseAndCheck(t, code) + require.NoError(t, err) + + var uuid uint64 = 0 + + inter, err := interpreter.NewInterpreter( + interpreter.ProgramFromChecker(checker), + checker.Location, + &interpreter.Config{ + UUIDHandler: func() (uint64, error) { + uuid++ + return uuid, nil + }, + AtreeStorageValidationEnabled: true, + AtreeValueValidationEnabled: true, + Storage: interpreter.NewInMemoryStorage(nil), + }, + ) + require.NoError(t, err) + + err = inter.Interpret() + require.NoError(t, err) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + exported, err := runtime.ExportValue(result, inter, interpreter.EmptyLocationRange) + require.NoError(t, err) + + return exported +} + +func TestEncodeResource(t *testing.T) { + + t.Parallel() + + t.Run("Simple", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Foo { + let bar: Int + + init(bar: Int) { + self.bar = bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: 42) + } + `) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedVal := cadence.NewResource([]cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + expectedCBOR := []byte{ + // language=json, format=json-cadence data interchange format + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)], ["uuid", 137(15)]]])], [136(h''), [42, 1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type(int)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // 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, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) + + t.Run("With function member", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Foo { + let bar: Int + + fun foo(): String { + return "foo" + } + + init(bar: Int) { + self.bar = bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: 42) + } + `) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedVal := cadence.NewResource([]cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + // function "foo" should be omitted from resulting CBOR + expectedCBOR := []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)], ["uuid", 137(15)]]])], [136(h''), [42, 1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type(int)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // 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, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) + + t.Run("Nested resource", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Bar { + let x: Int + + init(x: Int) { + self.x = x + } + } + + resource Foo { + let bar: @Bar + + init(bar: @Bar) { + self.bar <- bar + } + + destroy() { + destroy self.bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: <- create Bar(x: 42)) + } + `) + + // S.test.Foo(uuid: 2, bar: S.test.Bar(uuid: 1, x: 42)) (cadence.Resource) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedBarResourceType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Bar", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "x"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + ) + expectedBarResource := cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }, + ).WithType(expectedBarResourceType) + + expectedVal := cadence.NewResource( + []cadence.Value{ + expectedBarResource, + cadence.NewUInt64(2), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: expectedBarResourceType, Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + expectedCBOR := []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"2"}},{"name":"bar","value":{"type":"Resource","value":{"id":"S.test.Bar","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"x","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Bar", [["x", 137(4)], ["uuid", 137(15)]]]), 161([h'01', "S.test.Foo", [["bar", 136(h'')], ["uuid", 137(15)]]])], [136(h'01'), [[42, 1], 2]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{01} + // cadence-type-id: "S.test.Bar" + // 2 fields: [["x", type(int)], ["uuid", type(uint64)], ] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x61, + // x + 0x78, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint64 type ID (15) + 0x0f, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type ref(1)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagTypeRef, + // type definition ID (0) + // bytes, 0 bytes follow + 0x40, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint64 type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + // 2 + 0x02, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) +} + +func TestEncodeStruct(t *testing.T) { + + t.Parallel() + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleStruct := encodeTest{ + "Simple", + cadence.NewStruct( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleStructType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // 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" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // 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, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + resourceStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, + } + + resourceStruct := encodeTest{ + "Resources", + cadence.NewStruct( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceStructType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 160([h'01', "S.test.FooStruct", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // 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, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // type reference ID (1) + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, simpleStruct, resourceStruct) +} + +func TestEncodeEvent(t *testing.T) { + + t.Parallel() + + simpleEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleEvent := encodeTest{ + "Simple", + cadence.NewEvent( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleEventType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + resourceEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, + } + + resourceEvent := encodeTest{ + "Resources", + cadence.NewEvent( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceEventType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 162([h'01', "S.test.FooEvent", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // event type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, simpleEvent, resourceEvent) +} + +func TestEncodeContract(t *testing.T) { + + t.Parallel() + + simpleContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleContract := encodeTest{ + "Simple", + cadence.NewContract( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleContractType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[163([h'', "S.test.FooContract", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + resourceContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, + } + + resourceContract := encodeTest{ + "Resources", + cadence.NewContract( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceContractType), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 163([h'01', "S.test.FooContract", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, simpleContract, resourceContract) +} + +func TestEncodeSimpleTypes(t *testing.T) { + + t.Parallel() + + type simpleTypes struct { + typ cadence.Type + cborSimpleTypeID int + } + + var tests []encodeTest + + for _, ty := range []simpleTypes{ + {cadence.AnyType{}, ccf.TypeAny}, + {cadence.AnyResourceType{}, ccf.TypeAnyResource}, + {cadence.MetaType{}, ccf.TypeMetaType}, + {cadence.VoidType{}, ccf.TypeVoid}, + {cadence.NeverType{}, ccf.TypeNever}, + {cadence.BoolType{}, ccf.TypeBool}, + {cadence.StringType{}, ccf.TypeString}, + {cadence.CharacterType{}, ccf.TypeCharacter}, + {cadence.BytesType{}, ccf.TypeBytes}, + {cadence.AddressType{}, ccf.TypeAddress}, + {cadence.SignedNumberType{}, ccf.TypeSignedNumber}, + {cadence.IntegerType{}, ccf.TypeInteger}, + {cadence.SignedIntegerType{}, ccf.TypeSignedInteger}, + {cadence.FixedPointType{}, ccf.TypeFixedPoint}, + {cadence.IntType{}, ccf.TypeInt}, + {cadence.Int8Type{}, ccf.TypeInt8}, + {cadence.Int16Type{}, ccf.TypeInt16}, + {cadence.Int32Type{}, ccf.TypeInt32}, + {cadence.Int64Type{}, ccf.TypeInt64}, + {cadence.Int128Type{}, ccf.TypeInt128}, + {cadence.Int256Type{}, ccf.TypeInt256}, + {cadence.UIntType{}, ccf.TypeUInt}, + {cadence.UInt8Type{}, ccf.TypeUInt8}, + {cadence.UInt16Type{}, ccf.TypeUInt16}, + {cadence.UInt32Type{}, ccf.TypeUInt32}, + {cadence.UInt64Type{}, ccf.TypeUInt64}, + {cadence.UInt128Type{}, ccf.TypeUInt128}, + {cadence.UInt256Type{}, ccf.TypeUInt256}, + {cadence.Word8Type{}, ccf.TypeWord8}, + {cadence.Word16Type{}, ccf.TypeWord16}, + {cadence.Word32Type{}, ccf.TypeWord32}, + {cadence.Word64Type{}, ccf.TypeWord64}, + {cadence.Fix64Type{}, ccf.TypeFix64}, + {cadence.UFix64Type{}, ccf.TypeUFix64}, + {cadence.BlockType{}, ccf.TypeBlock}, + {cadence.PathType{}, ccf.TypePath}, + {cadence.CapabilityPathType{}, ccf.TypeCapabilityPath}, + {cadence.StoragePathType{}, ccf.TypeStoragePath}, + {cadence.PublicPathType{}, ccf.TypePublicPath}, + {cadence.PrivatePathType{}, ccf.TypePrivatePath}, + {cadence.AccountKeyType{}, ccf.TypeAccountKey}, + {cadence.AuthAccountContractsType{}, ccf.TypeAuthAccountContracts}, + {cadence.AuthAccountKeysType{}, ccf.TypeAuthAccountKeys}, + {cadence.AuthAccountType{}, ccf.TypeAuthAccount}, + {cadence.PublicAccountContractsType{}, ccf.TypePublicAccountContracts}, + {cadence.PublicAccountKeysType{}, ccf.TypePublicAccountKeys}, + {cadence.PublicAccountType{}, ccf.TypePublicAccount}, + {cadence.DeployedContractType{}, ccf.TypeDeployedContract}, + } { + var w bytes.Buffer + + encoder := ccf.CBOREncMode.NewStreamEncoder(&w) + + err := encoder.EncodeRawBytes([]byte{ + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + }) + require.NoError(t, err) + + err = encoder.EncodeInt(ty.cborSimpleTypeID) + require.NoError(t, err) + + encoder.Flush() + + tests = append(tests, encodeTest{ + name: fmt.Sprintf("with static %s", ty.typ.ID()), + val: cadence.TypeValue{ + StaticType: ty.typ, + }, + expected: w.Bytes(), + // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"[ty.ID()]"}}} + // + // language=edn, format=ccf + // 130([137(41), 185(simple_type_id)]) + }) + } + + testAllEncodeAndDecode(t, tests...) +} + +func TestEncodeType(t *testing.T) { + + t.Parallel() + + t.Run("with static int?", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: cadence.IntType{}}, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static int??", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: &cadence.OptionalType{Type: cadence.IntType{}}}, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(186(185(4)))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + t.Run("with static [int]", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.VariableSizedArrayType{ElementType: cadence.IntType{}}, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"VariableSizedArray", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 187(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static [int; 3]", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ConstantSizedArrayType{ + ElementType: cadence.IntType{}, + Size: 3, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"ConstantSizedArray", "type" : {"kind" : "Int"}, "size" : 3}}} + // + // language=edn, format=ccf + // 130([137(41), 188([3, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static {int:string}", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.DictionaryType{ + ElementType: cadence.StringType{}, + KeyType: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Dictionary", "key" : {"kind" : "Int"}, "value" : {"kind" : "String"}}}} + // + // language=edn, format=ccf + // 130([137(41), 189([185(4), 185(1)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + + }) + + t.Run("with static struct", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "Struct", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + t.Run("with static resource", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "R", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "Resource", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static contract", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "Contract", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (42) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static struct interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "StructInterface", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.S + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static resource interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "R", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "ResourceInterface", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static contract interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ContractInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "ContractInterface", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for contract interface) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static event", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "E", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializer: []cadence.Parameter{ + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "Event", "type" : "", "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [[{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}, {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}]] } } } + // + // language=edn, format=ccf + // 130([137(41), 210([h'', "S.test.E", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEventTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // type (nil for event) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static enum", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.EnumType{ + Location: utils.TestLocation, + QualifiedIdentifier: "E", + RawType: cadence.StringType{}, + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, + {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + }, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type", "value": {"staticType": {"kind": "Enum", "type" : {"kind" : "String"}, "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // + // language=edn, format=ccf + // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEnumTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static &int", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ReferenceType{ + Authorized: false, + Type: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Reference", "type" : {"kind" : "Int"}, "authorized" : false}}}` + // + // language=edn, format=ccf + // 130([137(41), 190([false, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagReferenceTypeValue, + // array, 2 elements follow + 0x82, + // authorized + // bool + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + + }) + + t.Run("with static function", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + ReturnType: cadence.IntType{}, + }).WithID("Foo"), + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"Foo", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } + // + // language=edn, format=ccf + // 130([137(41), 193(["Foo", [["qux", "baz", 185(1)]], 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + + }) + + t.Run("with static Capability", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.CapabilityType{ + BorrowType: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Capability", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 192([185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagCapabilityTypeValue, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + }) + + t.Run("with static restricted type", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }).WithID("Int{String}"), + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } + // + // language=edn, format=ccf + // 130([137(41), 191(["Int{String}", 185(4), [185(1)]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 3 elements follow + 0x83, + // ID + // string, 11 bytes follow + 0x6b, + // Int{String} + 0x49, 0x6e, 0x74, 0x7b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x7d, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + }, + ) + + }) + + t.Run("without static type", func(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.TypeValue{}, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":""}} + // + // language=edn, format=ccf + // 130([137(41), null]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // nil + 0xf6, + }, + ) + }) +} + +func TestEncodeCapability(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.StorageCapability{ + Path: cadence.NewPath("storage", "foo"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"}} + // + // language=edn, format=ccf + // 130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]) + // + // 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 folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) +} + +func TestDecodeFix64(t *testing.T) { + + t.Parallel() + + var maxInt int64 = sema.Fix64TypeMaxInt + var minInt int64 = sema.Fix64TypeMinInt + var maxFrac int64 = sema.Fix64TypeMaxFractional + var minFrac int64 = sema.Fix64TypeMinFractional + var factor int64 = sema.Fix64Factor + + type test struct { + name string + expected cadence.Fix64 + encodedData []byte + check func(t *testing.T, actual cadence.Value, err error) + } + + var tests = []test{ + { + name: "12.3", + expected: cadence.Fix64(12_30000000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.3"} + // + // language=edn, format=ccf + // 130([137(22), 1230000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1230000000 + 0x1a, 0x49, 0x50, 0x4f, 0x80, + }, + }, + { + name: "12.03", + expected: cadence.Fix64(12_03000000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.03"} + // + // language=edn, format=ccf + // 130([137(22), 1203000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1203000000 + 0x1a, 0x47, 0xb4, 0x52, 0xc0, + }, + }, + { + name: "12.003", + expected: cadence.Fix64(12_00300000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.003"} + // + // language=edn, format=ccf + // 130([137(22), 1200300000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200300000 + 0x1a, 0x47, 0x8b, 0x1f, 0xe0, + }, + }, + { + name: "12.0003", + expected: cadence.Fix64(12_00030000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.0003"} + // + // language=edn, format=ccf + // 130([137(22), 1200030000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200030000 + 0x1a, 0x47, 0x87, 0x01, 0x30, + }, + }, + { + name: "12.00003", + expected: cadence.Fix64(12_00003000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.00003"} + // + // language=edn, format=ccf + // 130([137(22), 1200003000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200003000 + 0x1a, 0x47, 0x86, 0x97, 0xb8, + }, + }, + { + name: "12.000003", + expected: cadence.Fix64(12_00000300), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.000003"} + // + // language=edn, format=ccf + // 130([137(22), 1200000300]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200000300 + 0x1a, 0x47, 0x86, 0x8d, 0x2c, + }, + }, + { + name: "12.0000003", + expected: cadence.Fix64(12_00000030), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "12.0000003"} + // + // language=edn, format=ccf + // 130([137(22), 1200000030]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200000030 + 0x1a, 0x47, 0x86, 0x8c, 0x1e, + }, + }, + { + name: "120.3", + expected: cadence.Fix64(120_30000000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "120.3"} + // + // language=edn, format=ccf + // 130([137(22), 12030000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 12030000000 + 0x1b, 0x00, 0x00, 0x00, 0x02, 0xcd, 0x0b, 0x3b, 0x80, + }, + }, + { + // 92233720368.1 + name: fmt.Sprintf("%d.1", maxInt), + expected: cadence.Fix64(9223372036810000000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "92233720368.1"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036810000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036810000000 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xfd, 0x54, 0xc6, 0x80, + }, + }, + { + // 92233720369.1 + name: fmt.Sprintf("%d.1", maxInt+1), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "92233720369.1"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036910000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036910000000 + 0x1b, 0x80, 0x00, 0x00, 0x00, 0x03, 0x4a, 0xa7, 0x80, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // -92233720368.1 + name: fmt.Sprintf("%d.1", minInt), + expected: cadence.Fix64(-9223372036810000000), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "-92233720368.1"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036810000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036810000000 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xfd, 0x54, 0xc6, 0x7f, + }, + }, + { + // -92233720369.1 + name: fmt.Sprintf("%d.1", minInt-1), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "-92233720369.1"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036910000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036910000000 + 0x3b, 0x80, 0x00, 0x00, 0x00, 0x03, 0x4a, 0xa7, 0x7f, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // 92233720368.54775807 + name: fmt.Sprintf("%d.%d", maxInt, maxFrac), + expected: cadence.Fix64(maxInt*factor + maxFrac), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "92233720368.54775807"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036854775807]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036854775807 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + // 92233720368.54775808 + name: fmt.Sprintf("%d.%d", maxInt, maxFrac+1), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "92233720368.54775808"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036854775808 + 0x1b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // -92233720368.54775808 + name: fmt.Sprintf("%d.%d", minInt, -(minFrac)), + expected: cadence.Fix64(-9223372036854775808), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "-92233720368.54775808"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036854775808 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + // -92233720368.54775809 + name: fmt.Sprintf("%d.%d", minInt, -(minFrac - 1)), + encodedData: []byte{ + // language=json, format=json-cadence data interchange format + // {"type": "Fix64", "value": "-92233720368.54775809"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036854775809]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036854775809 + 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := ccf.Decode(nil, tt.encodedData) + if tt.check != nil { + tt.check(t, actual, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected, actual) + } + }) + } +} + +func TestExportRecursiveType(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncode( + t, + cadence.Resource{ + Fields: []cadence.Value{ + cadence.Optional{}, + }, + }.WithType(ty), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"foo","value":{"type": "Optional","value":null}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["foo", 138(136(h''))]]])], [136(h''), [null]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 1 fields: [["foo", optional(type ref id(0))]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // nil + 0xf6, + }, + ) + +} + +func TestExportTypeValueRecursiveType(t *testing.T) { + + t.Parallel() + + t.Run("recursive", func(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + Initializers: [][]cadence.Parameter{}, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: ty, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Foo","fields":[{"id":"foo","type":{"kind":"Optional","type":"S.test.Foo"}}],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Foo", null, [["foo", 186(184(h''))]], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6F, 0x6F, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 bytes follow + 0x40, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + + }) + + t.Run("non-recursive, repeated", func(t *testing.T) { + + t.Parallel() + + fooTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + barTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + Fields: []cadence.Field{ + { + Identifier: "foo1", + Type: fooTy, + }, + { + Identifier: "foo2", + Type: fooTy, + }, + }, + Initializers: [][]cadence.Parameter{}, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: barTy, + }, + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Bar","fields":[{"id":"foo1","type":{"kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[],"type":""}},{"id":"foo2","type":"S.test.Foo"}],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type (nil for struct) + 0xf6, + // fields + // array, 2 element follows + 0x82, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo1 + 0x66, 0x6f, 0x6f, 0x31, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 elements follow + 0x80, + // initializer + // array, 0 elements follow + 0x80, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo2 + 0x66, 0x6f, 0x6f, 0x32, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + }) +} + +func TestEncodePath(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.NewPath("storage", "foo"), + []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Path","value":{"domain":"storage","identifier":"foo"}} + // + // language=edn, format=ccf + // 130([137(24), [1, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Path type ID (24) + 0x18, 0x18, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) +} + +func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { + + test := func(testCase encodeTest) { + + t.Run(testCase.name, func(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode(t, testCase.val, testCase.expected) + }) + } + + for _, testCase := range tests { + test(testCase) + } +} + +func TestDecodeInvalidType(t *testing.T) { + + t.Parallel() + + t.Run("empty type", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cadence data interchange format + // { "type":"Struct", "value":{ "id":"", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 0 bytes follow + 0x60, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "invalid type ID for built-in: ``", err.Error()) + }) + + t.Run("undefined type", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cadence data interchange format + // { "type":"Struct", "value":{ "id":"I.Foo", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "I.Foo", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "I.Foo" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 5 bytes follow + 0x65, + // I.Foo + 0x49, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "invalid type ID `I.Foo`: invalid identifier location type ID: missing qualified identifier", err.Error()) + }) + + t.Run("unknown location prefix", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cadence data interchange format + // { "type":"Struct", "value":{ "id":"N.PublicKey", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "N.PublicKey", []])], [136(h''), []]]) + // + // 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: "N.PublicKey" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // N.PublicKey + 0x4e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "invalid type ID for built-in: `N.PublicKey`", err.Error()) + }) +} + +func testEncodeAndDecode(t *testing.T, val cadence.Value, expectedCBOR []byte) { + actualCBOR := testEncode(t, val, expectedCBOR) + testDecode(t, actualCBOR, val) +} + +// testEncodeAndDecodeEx is used when val != expectedVal because of deterministic encoding. +func testEncodeAndDecodeEx(t *testing.T, val cadence.Value, expectedCBOR []byte, expectedVal cadence.Value) { + actualCBOR := testEncode(t, val, expectedCBOR) + testDecode(t, actualCBOR, expectedVal) +} + +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)) + return actualCBOR +} + +func testDecode(t *testing.T, actualCBOR []byte, expectedVal cadence.Value) { + decodedVal, err := ccf.Decode(nil, actualCBOR) + require.NoError(t, err) + assert.Equal( + t, + cadence.ValueWithCachedTypeID(expectedVal), + cadence.ValueWithCachedTypeID(decodedVal), + ) +} + +var fooResourceType = &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, +} + +var foooResourceTypeWithAbstractField = &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Fooo", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + { + Identifier: "baz", + Type: cadence.AnyStructType{}, + }, + }, +} + +func TestEncodeBuiltinComposites(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + typ cadence.Type + encoded []byte + }{ + { + name: "Struct", + typ: &cadence.StructType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Struct","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "StructInterface", + typ: &cadence.StructInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"StructInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 224([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Resource", + typ: &cadence.ResourceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "ResourceInterface", + typ: &cadence.ResourceInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"ResourceInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 225([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Contract", + typ: &cadence.ContractType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Contract","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 211([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "ContractInterface", + typ: &cadence.ContractInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"ContractInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 226([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Enum", + typ: &cadence.EnumType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Enum","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 212([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEnumTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Event", + typ: &cadence.EventType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cadence data interchange format + // {"type":"Type","value":{"staticType":{"kind":"Event","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 210([h'', "Foo", null, [], [[]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEventTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 1 elements follow + 0x81, + // array, 0 element follow + 0x80, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + typeValue := cadence.NewTypeValue(test.typ) + testEncode(t, typeValue, test.encoded) + }) + } +} + +func TestExportFunctionValue(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncode( + t, + cadence.Function{ + FunctionType: (&cadence.FunctionType{ + Parameters: []cadence.Parameter{}, + ReturnType: cadence.VoidType{}, + }).WithID("(():Void)"), + }, + []byte{ // language=json, format=json-cadence data interchange format + // { "type": "Function", "value": { "functionType": { "kind": "Function", "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" } } } } + // + // language=edn, format=ccf + // 130([137(51), ["(():Void)", [], 185(50)]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Function type ID (51) + 0x18, 0x33, + // array, 3 elements follow + 0x83, + // element 0: cadence-type-id + // string, 9 bytes follow + 0x69, + // (():Void) + 0x28, 0x28, 0x29, 0x3a, 0x56, 0x6f, 0x69, 0x64, 0x29, + // element 1: parameters + // array, 0 element + 0x80, + // element 2: return type + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Void type ID (50) + 0x18, 0x32, + }, + ) +} + +func TestDeployedEvents(t *testing.T) { + var tests = []struct { + name string + event cadence.Event + expectedCBOR []byte + }{ + { + name: "FlowFees.FeesDeducted", + event: createFlowFeesFeesDeductedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.f919ee77447b7497.FlowFees.FeesDeducted","fields":[{"value":{"value":"0.01797293","type":"UFix64"},"name":"amount"},{"value":{"value":"1.00000000","type":"UFix64"},"name":"inclusionEffort"},{"value":{"value":"0.00360123","type":"UFix64"},"name":"executionEffort"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.f919ee77447b7497.FlowFees.FeesDeducted", [["amount", 137(23)], ["executionEffort", 137(23)], ["inclusionEffort", 137(23)]]])], [136(h''), [1797293, 360123, 100000000]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.f919ee77447b7497.FlowFees.FeesDeducted" + // 3 fields: [["amount", type(ufix64)], ["executionEffort", type(ufix64)], ["inclusionEffort", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 elements follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 40 bytes follow + 0x78, 0x28, + // A.f919ee77447b7497.FlowFees.FeesDeducted + 0x41, 0x2e, 0x66, 0x39, 0x31, 0x39, 0x65, 0x65, 0x37, 0x37, 0x34, 0x34, 0x37, 0x62, 0x37, 0x34, 0x39, 0x37, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x65, 0x65, 0x73, 0x2e, 0x46, 0x65, 0x65, 0x73, 0x44, 0x65, 0x64, 0x75, 0x63, 0x74, 0x65, 0x64, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 6 bytes follow + 0x66, + // amount + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 items follow + 0x82, + // text, 15 bytes follow + 0x6f, + // executionEffort + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 2 + // array, 2 items follow + 0x82, + // text, 15 bytes follow + 0x6f, + // inclusionEffort + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // 1797293 + 0x1a, 0x00, 0x1b, 0x6c, 0xad, + // 360123 + 0x1a, 0x00, 0x05, 0x7e, 0xbb, + // 100000000 + 0x1a, 0x05, 0xf5, 0xe1, 0x00, + }, + }, + { + name: "FlowFees.TokensWithdrawn", + event: createFlowFeesTokensWithdrawnEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.f919ee77447b7497.FlowFees.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.f919ee77447b7497.FlowFees.TokensWithdrawn", [["amount", 137(23)]]])], [136(h''), [5304112895]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.f919ee77447b7497.FlowFees.TokensWithdrawn" + // 1 field: [["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 43 bytes follow + 0x78, 0x2b, + // "A.f919ee77447b7497.FlowFees.TokensWithdrawn" + 0x41, 0x2e, 0x66, 0x39, 0x31, 0x39, 0x65, 0x65, 0x37, 0x37, 0x34, 0x34, 0x37, 0x62, 0x37, 0x34, 0x39, 0x37, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x65, 0x65, 0x73, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + }, + }, + { + name: "FlowIDTableStaking.DelegatorRewardsPaid", + event: createFlowIDTableStakingDelegatorRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"92","type":"UInt32"},"name":"delegatorID"},{"value":{"value":"4.38760261","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid", [["amount", 137(23)], ["nodeID", 137(1)], ["delegatorID", 137(14)]]])], [136(h''), [438760261, "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb", 92]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid" + // 3 field: [["amount", type(ufix64)], ["nodeID", type(string)], ["delegatorID", type(uint32)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 58 bytes follow + 0x78, 0x3a, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "nodeID" + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 2 + // array, 2 element follows + 0x82, + // text, 11 bytes follow + 0x6b, + // "delegatorID" + 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // 438760261 + 0x1a, 0x1a, 0x26, 0xf3, 0x45, + // text, 64 bytes follow + 0x78, 0x40, + // "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb" + 0x65, 0x35, 0x32, 0x63, 0x62, 0x63, 0x64, 0x38, 0x32, 0x35, 0x65, 0x33, 0x32, 0x38, 0x61, 0x63, 0x61, 0x63, 0x38, 0x64, 0x62, 0x36, 0x62, 0x63, 0x62, 0x64, 0x63, 0x62, 0x62, 0x36, 0x65, 0x37, 0x37, 0x32, 0x34, 0x38, 0x36, 0x32, 0x63, 0x38, 0x62, 0x38, 0x39, 0x62, 0x30, 0x39, 0x64, 0x38, 0x35, 0x65, 0x64, 0x63, 0x63, 0x66, 0x34, 0x31, 0x66, 0x66, 0x39, 0x39, 0x38, 0x31, 0x65, 0x62, + // 92 + 0x18, 0x5c, + }, + }, + { + name: "FlowIDTableStaking.EpochTotalRewardsPaid", + event: createFlowIDTableStakingEpochTotalRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid","fields":[{"value":{"value":"1316543.00000000","type":"UFix64"},"name":"total"},{"value":{"value":"53.04112895","type":"UFix64"},"name":"fromFees"},{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"minted"},{"value":{"value":"6.04080767","type":"UFix64"},"name":"feesBurned"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid", [["total", 137(23)], ["minted", 137(23)], ["fromFees", 137(23)], ["feesBurned", 137(23)]]])], [136(h''), [131654300000000, 131648995887105, 5304112895, 604080767]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid" + // 4 field: [["total", type(ufix64)], ["minted", type(ufix64)], ["fromFees", type(ufix64)], ["feesBurned", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 59 bytes follow + 0x78, 0x3b, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 4 items follow + 0x84, + // field 0 + // array, 2 element follows + 0x82, + // text, 5 bytes follow + 0x65, + // "total" + 0x74, 0x6f, 0x74, 0x61, 0x6c, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "minted" + 0x6d, 0x69, 0x6e, 0x74, 0x65, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 2 + // array, 2 element follows + 0x82, + // text, 8 bytes follow + 0x68, + // "fromFees" + 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x73, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 3 + // array, 2 element follows + 0x82, + // text, 10 bytes follow + 0x6a, + // "feesBurned" + 0x66, 0x65, 0x65, 0x73, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 4 items follow + 0x84, + // 131654300000000 + 0x1b, 0x00, 0x00, 0x77, 0xbd, 0x27, 0xc8, 0xdf, 0x00, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + // 604080767 + 0x1a, 0x24, 0x01, 0x8a, 0x7f, + }, + }, + { + name: "FlowIDTableStaking.NewWeeklyPayout", + event: createFlowIDTableStakingNewWeeklyPayoutEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout","fields":[{"value":{"value":"1317778.00000000","type":"UFix64"},"name":"newPayout"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout", [["newPayout", 137(23)]]])], [136(h''), [131777800000000]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout" + // 1 field: [["newPayout", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 53 bytes follow + 0x78, 0x35, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x4e, 0x65, 0x77, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 9 bytes follow + 0x69, + // "newPayout" + 0x6e, 0x65, 0x77, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 131777800000000 + 0x1b, 0x00, 0x00, 0x77, 0xd9, 0xe8, 0xf5, 0x52, 0x00, + }, + }, + { + name: "FlowIDTableStaking.RewardsPaid", + event: createFlowIDTableStakingRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid", [["amount", 137(23)], ["nodeID", 137(1)]]])], [136(h''), [174549955740, "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid" + // 2 field: [["amount", type(ufix64)], ["nodeID", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 49 bytes follow + 0x78, 0x31, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "nodeID" + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // 174549955740 + 0x1b, 0x00, 0x00, 0x00, 0x28, 0xa3, 0xfc, 0xf4, 0x9c, + // string, 64 bytes follow + 0x78, 0x40, + // "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb" + 0x65, 0x35, 0x32, 0x63, 0x62, 0x63, 0x64, 0x38, 0x32, 0x35, 0x65, 0x33, 0x32, 0x38, 0x61, 0x63, 0x61, 0x63, 0x38, 0x64, 0x62, 0x36, 0x62, 0x63, 0x62, 0x64, 0x63, 0x62, 0x62, 0x36, 0x65, 0x37, 0x37, 0x32, 0x34, 0x38, 0x36, 0x32, 0x63, 0x38, 0x62, 0x38, 0x39, 0x62, 0x30, 0x39, 0x64, 0x38, 0x35, 0x65, 0x64, 0x63, 0x63, 0x66, 0x34, 0x31, 0x66, 0x66, 0x39, 0x39, 0x38, 0x31, 0x65, 0x62, + }, + }, + { + name: "FlowToken.TokensDeposited with nil receiver", + event: createFlowTokenTokensDepositedEventNoReceiver(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"},{"value":{"value":null,"type":"Optional"},"name":"to"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensDeposited", [["to", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [null, 131648995887105]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensDeposited" + // 2 field: [["to", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensDeposited" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 2 bytes follow + 0x62, + // "to" + 0x74, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // null + 0xf6, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + }, + }, + { + name: "FlowToken.TokensDeposited", + event: createFlowTokenTokensDepositedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0x8624b52f9ddcd04a","type":"Address"},"type":"Optional"},"name":"to"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensDeposited", [["to", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [h'8624B52F9DDCD04A', 174549955740]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensDeposited" + // 2 field: [["to", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensDeposited" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 2 bytes follow + 0x62, + // "to" + 0x74, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // bytes, 8 bytes follow + 0x48, + // 0x8624b52f9ddcd04a + 0x86, 0x24, 0xb5, 0x2f, 0x9d, 0xdc, 0xd0, 0x4a, + // 174549955740 + 0x1b, 0x00, 0x00, 0x00, 0x28, 0xa3, 0xfc, 0xf4, 0x9c, + }, + }, + { + name: "FlowToken.TokensMinted", + event: createFlowTokenTokensMintedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensMinted","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensMinted", [["amount", 137(23)]]])], [136(h''), [131648995887105]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensMinted" + // 1 field: [["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 41 bytes follow + 0x78, 0x29, + // "A.1654653399040a61.FlowToken.TokensMinted" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x4d, 0x69, 0x6e, 0x74, 0x65, 0x64, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + }, + }, + { + name: "FlowToken.TokensWithdrawn", + event: createFlowTokenTokensWithdrawnEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cadence data interchange format + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0xf919ee77447b7497","type":"Address"},"type":"Optional"},"name":"from"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensWithdrawn", [["from", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [h'F919EE77447B7497', 5304112895]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensWithdrawn" + // 2 field: [["from", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensWithdrawn" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 4 bytes follow + 0x64, + // "from" + 0x66, 0x72, 0x6f, 0x6d, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // bytes, 8 bytes follow + 0x48, + // 0xf919ee77447b7497 + 0xf9, 0x19, 0xee, 0x77, 0x44, 0x7b, 0x74, 0x97, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Encode Cadence value to CCF + actualCBOR, err := ccf.Encode(tt.event) + require.NoError(t, err) + require.Equal(t, tt.expectedCBOR, actualCBOR) + + // Decode CCF to Cadence value + decodedEvent, err := ccf.Decode(nil, actualCBOR) + require.NoError(t, err) + + // Test original event and decoded events are equal even if + // fields are ordered differently due to deterministic encoding. + testEventEquality(t, tt.event, decodedEvent.(cadence.Event)) + }) + } +} + +func newFlowFeesFeesDeductedEventType() *cadence.EventType { + // pub event FeesDeducted(amount: UFix64, inclusionEffort: UFix64, executionEffort: UFix64) + + address, _ := common.HexToAddress("f919ee77447b7497") + location := common.NewAddressLocation(nil, address, "FlowFees") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowFees.FeesDeducted", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "inclusionEffort", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "executionEffort", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowFeesFeesDeductedEvent() cadence.Event { + /* + A.f919ee77447b7497.FlowFees.FeesDeducted + { + "amount": "0.01797293", + "inclusionEffort": "1.00000000", + "executionEffort": "0.00360123" + } + */ + amount, _ := cadence.NewUFix64("0.01797293") + inclusionEffort, _ := cadence.NewUFix64("1.00000000") + executionEffort, _ := cadence.NewUFix64("0.00360123") + + return cadence.NewEvent( + []cadence.Value{amount, inclusionEffort, executionEffort}, + ).WithType(newFlowFeesFeesDeductedEventType()) +} + +func newFlowFeesTokensWithdrawnEventType() *cadence.EventType { + // pub event TokensWithdrawn(amount: UFix64) + + address, _ := common.HexToAddress("f919ee77447b7497") + location := common.NewAddressLocation(nil, address, "FlowFees") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowFees.TokensWithdrawn", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowFeesTokensWithdrawnEvent() cadence.Event { + /* + A.f919ee77447b7497.FlowFees.TokensWithdrawn + { + "amount": "53.04112895" + } + */ + amount, _ := cadence.NewUFix64("53.04112895") + + return cadence.NewEvent( + []cadence.Value{amount}, + ).WithType(newFlowFeesTokensWithdrawnEventType()) +} + +func newFlowTokenTokensDepositedEventType() *cadence.EventType { + // pub event TokensDeposited(amount: UFix64, to: Address?) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensDeposited", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "to", + Type: &cadence.OptionalType{ + Type: cadence.NewAddressType(), + }, + }, + }, + } +} + +func createFlowTokenTokensDepositedEventNoReceiver() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensDeposited + { + "amount": "1316489.95887105", + "to": null + } + */ + amount, _ := cadence.NewUFix64("1316489.95887105") + to := cadence.NewOptional(nil) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensDepositedEventType()) +} + +func createFlowTokenTokensDepositedEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensDeposited + { + "amount": "1745.49955740", + "to": "0x8624b52f9ddcd04a" + } + */ + addressBytes, _ := hex.DecodeString("8624b52f9ddcd04a") + + amount, _ := cadence.NewUFix64("1745.49955740") + to := cadence.NewOptional(cadence.BytesToAddress(addressBytes)) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensDepositedEventType()) +} + +func newFlowTokenTokensMintedEventType() *cadence.EventType { + // pub event TokensMinted(amount: UFix64) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensMinted", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowTokenTokensMintedEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensMinted + { + "amount": "1316489.95887105" + } + */ + amount, _ := cadence.NewUFix64("1316489.95887105") + + return cadence.NewEvent( + []cadence.Value{amount}, + ).WithType(newFlowTokenTokensMintedEventType()) +} + +func newFlowTokenTokensWithdrawnEventType() *cadence.EventType { + // pub event TokensWithdrawn(amount: UFix64, from: Address?) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensWithdrawn", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "from", + Type: &cadence.OptionalType{ + Type: cadence.NewAddressType(), + }, + }, + }, + } +} + +func createFlowTokenTokensWithdrawnEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensWithdrawn + { + "amount": "53.04112895", + "from": "0xf919ee77447b7497" + } + */ + addressBytes, _ := hex.DecodeString("f919ee77447b7497") + + amount, _ := cadence.NewUFix64("53.04112895") + to := cadence.NewOptional(cadence.BytesToAddress(addressBytes)) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensWithdrawnEventType()) +} + +func newFlowIDTableStakingDelegatorRewardsPaidEventType() *cadence.EventType { + // pub event DelegatorRewardsPaid(nodeID: String, delegatorID: UInt32, amount: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.DelegatorRewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "nodeID", + Type: cadence.StringType{}, + }, + { + Identifier: "delegatorID", + Type: cadence.UInt32Type{}, + }, + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingDelegatorRewardsPaidEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid + { + "nodeID": "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb", + "delegatorID": 92, + "amount": "4.38760261" + } + */ + nodeID := cadence.String("e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb") + delegatorID := cadence.UInt32(92) + amount, _ := cadence.NewUFix64("4.38760261") + + return cadence.NewEvent( + []cadence.Value{nodeID, delegatorID, amount}, + ).WithType(newFlowIDTableStakingDelegatorRewardsPaidEventType()) +} + +func newFlowIDTableStakingEpochTotalRewardsPaidEventType() *cadence.EventType { + // pub event EpochTotalRewardsPaid(total: UFix64, fromFees: UFix64, minted: UFix64, feesBurned: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.EpochTotalRewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "total", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "fromFees", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "minted", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "feesBurned", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingEpochTotalRewardsPaidEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid + { + "total": "1316543.00000000", + "fromFees": "53.04112895", + "minted": "1316489.95887105", + "feesBurned": "6.04080767" + } + */ + total, _ := cadence.NewUFix64("1316543.00000000") + fromFees, _ := cadence.NewUFix64("53.04112895") + minted, _ := cadence.NewUFix64("1316489.95887105") + feesBurned, _ := cadence.NewUFix64("6.04080767") + + return cadence.NewEvent( + []cadence.Value{total, fromFees, minted, feesBurned}, + ).WithType(newFlowIDTableStakingEpochTotalRewardsPaidEventType()) +} + +func newFlowIDTableStakingNewWeeklyPayoutEventType() *cadence.EventType { + // pub event NewWeeklyPayout(newPayout: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.NewWeeklyPayout", + Fields: []cadence.Field{ + { + Identifier: "newPayout", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingNewWeeklyPayoutEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout + { + "newPayout": "1317778.00000000" + } + */ + newPayout, _ := cadence.NewUFix64("1317778.00000000") + + return cadence.NewEvent( + []cadence.Value{newPayout}, + ).WithType(newFlowIDTableStakingNewWeeklyPayoutEventType()) +} + +func newFlowIDTableStakingRewardsPaidEventType() *cadence.EventType { + // pub event RewardsPaid(nodeID: String, amount: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.RewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "nodeID", + Type: cadence.StringType{}, + }, + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingRewardsPaidEvent() cadence.Event { + nodeID, _ := cadence.NewString("e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb") + amount, _ := cadence.NewUFix64("1745.49955740") + + return cadence.NewEvent( + []cadence.Value{nodeID, amount}, + ).WithType(newFlowIDTableStakingRewardsPaidEventType()) +} + +func testEventEquality(t *testing.T, event1 cadence.Event, event2 cadence.Event) { + require.True(t, event1.Type().Equal(event2.Type())) + require.Equal(t, len(event1.Fields), len(event2.Fields)) + require.Equal(t, len(event1.EventType.Fields), len(event2.EventType.Fields)) + + for i, event1FieldType := range event1.EventType.Fields { + + foundField := false + + for j, event2FieldType := range event2.EventType.Fields { + if event1FieldType.Identifier == event2FieldType.Identifier { + require.Equal(t, event1.Fields[i], event2.Fields[j]) + foundField = true + break + } + } + + require.True(t, foundField) + } +} From a3d00bafd0eb572c6c0ca66676fe73d3105d71a9 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:18:12 -0600 Subject: [PATCH 005/173] Add CCF codec benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CCF obsoletes JSON-Cadence Data Interchange Format for use cases that do not require JSON. Given this, preliminary comparisons are described here for the CCF codec implementing CCF Specifications (RC1). PRELIMINARY COMPARISONS Comparisons used 48,309 events from a single mainnet transaction. There were 9 event types. To simplify benchmark code, the first event's value in each of the 9 event types was used. CCF's partially self-describing mode (aka "detached" mode) would be even smaller than this (e.g. maybe less than 1/4 the size of JSON when Flow eventually supports detached mode). SIZE COMPARISON Encoding | Num Events | Encoded size | Comments -------- | ---------- | ------------ | -------- JSON | 48,309 | 13,858,836 | JSON-Cadence Data Interchange CCF | 48,309 | 6,159,931 | CCF in fully self-describing mode CCF SPEED AND MEMORY COMPARISONS This isn't apples to apples comparison. JSON data isn't sorted, etc. - CCF encoder sorts data to encode event data deterministically. - CCF decoder also verifies event data is sorted, well-formed, and valid. - CCF encoding is less than 1/2 size of JSON-Cadence Data Interchange. ENCODER COMPARISON 48k_events_encode_json.log │ 48k_events_encode_ccf.log sec/op │ sec/op vs base 89.84m ± 17% 69.28m ± 3% -22.88% 48k_events_encode_json.log │ 48k_events_encode_ccf.log B/op │ B/op vs base 32.45Mi ± 0% 25.82Mi ± 0% -20.45% 48k_events_encode_json.log │ 48k_events_encode_ccf.log allocs/op │ allocs/op vs base 756.6k ± 0% 370.4k ± 0% -51.05% DECODER COMPARISON 48k_events_decode_json.log │ 48k_events_decode_ccf.log sec/op │ sec/op vs base 646.2m ± 8% 158.3m ± 5% -75.50% 48k_events_decode_json.log │ 48k_events_decode_ccf.log B/op │ B/op vs base 234.97Mi ± 0% 56.16Mi ± 0% -76.10% 48k_events_decode_json.log │ 48k_events_decode_ccf.log allocs/op │ allocs/op vs base 4.746M ± 0% 1.288M ± 0% -72.86% Benchmarked using Go 1.19.6, linux_amd64, i5-13600k. Results are subject to change because CCF codec reference implementation in Go has not yet been reviewed or merged into onflow/cadence yet. --- encoding/ccf/bench_test.go | 214 +++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 encoding/ccf/bench_test.go diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go new file mode 100644 index 0000000000..e869829ffe --- /dev/null +++ b/encoding/ccf/bench_test.go @@ -0,0 +1,214 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2022-2023 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 ccf_test + +import ( + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/encoding/json" + "github.com/stretchr/testify/require" +) + +var encoded []byte +var val cadence.Value + +var benchmarks = []struct { + name string + value cadence.Value +}{ + {name: "FlowFees.FeesDeducted", value: createFlowFeesFeesDeductedEvent()}, + {name: "FlowFees.TokensWithdrawn", value: createFlowFeesTokensWithdrawnEvent()}, + + {name: "FlowIDTableStaking.DelegatorRewardsPaid", value: createFlowIDTableStakingDelegatorRewardsPaidEvent()}, + {name: "FlowIDTableStaking.EpochTotalRewardsPaid", value: createFlowIDTableStakingEpochTotalRewardsPaidEvent()}, + {name: "FlowIDTableStaking.NewWeeklyPayout", value: createFlowIDTableStakingNewWeeklyPayoutEvent()}, + {name: "FlowIDTableStaking.RewardsPaid", value: createFlowIDTableStakingRewardsPaidEvent()}, + + {name: "FlowToken.TokensDeposited with nil receiver", value: createFlowTokenTokensDepositedEventNoReceiver()}, + {name: "FlowToken.TokensDeposited", value: createFlowTokenTokensDepositedEvent()}, + {name: "FlowToken.TokensMinted", value: createFlowTokenTokensMintedEvent()}, + {name: "FlowToken.TokensWithdrawn", value: createFlowTokenTokensWithdrawnEvent()}, +} + +// Events for transaction 03aa46047cdadfcf7ee23ee86cd53064e05f8b5f8a6f570e9f53b2744eddbee4. +// This transaction was selected from mainnet because of its large number of events (48309). +// - The number of events for each type are real. +// - The types of events are real. +// - To simplify benchmark code, all event values for each event type are the same +// (i.e. the values are from the first event of that event type). +var batchBenchmarks = []struct { + count int + value cadence.Value +}{ + {count: 16102, value: createFlowTokenTokensDepositedEvent()}, + {count: 1, value: createFlowTokenTokensMintedEvent()}, + {count: 16102, value: createFlowTokenTokensWithdrawnEvent()}, + {count: 15783, value: createFlowIDTableStakingDelegatorRewardsPaidEvent()}, + {count: 1, value: createFlowIDTableStakingEpochTotalRewardsPaidEvent()}, + {count: 1, value: createFlowIDTableStakingNewWeeklyPayoutEvent()}, + {count: 317, value: createFlowIDTableStakingRewardsPaidEvent()}, + {count: 1, value: createFlowFeesFeesDeductedEvent()}, + {count: 1, value: createFlowFeesTokensWithdrawnEvent()}, +} + +func BenchmarkEncodeJSON(b *testing.B) { + var err error + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + encoded, err = json.Encode(bm.value) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkDecodeJSON(b *testing.B) { + for _, bm := range benchmarks { + encoded, err := json.Encode(bm.value) + require.NoError(b, err) + + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + val, err = json.Decode(nil, encoded) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkEncodeCCF(b *testing.B) { + var err error + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + encoded, err = ccf.Encode(bm.value) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkDecodeCCF(b *testing.B) { + for _, bm := range benchmarks { + encoded, err := ccf.Encode(bm.value) + require.NoError(b, err) + + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + val, err = ccf.Decode(nil, encoded) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkEncodeBatchEventsJSON(b *testing.B) { + var err error + var encodedSize int + + for i := 0; i < b.N; i++ { + encodedSize = 0 + for _, bm := range batchBenchmarks { + for i := 0; i < bm.count; i++ { + encoded, err = json.Encode(bm.value) + encodedSize += len(encoded) + } + } + } + require.NoError(b, err) + + //fmt.Printf("Batch events encoded in JSON are %d bytes\n", encodedSize) +} + +func BenchmarkDecodeBatchEventsJSON(b *testing.B) { + type encodedBatchEvent struct { + count int + encoded []byte + } + + benchmarks := make([]encodedBatchEvent, len(batchBenchmarks)) + + for i, bm := range batchBenchmarks { + benchmarks[i] = encodedBatchEvent{ + count: bm.count, + encoded: json.MustEncode(bm.value), + } + } + + var err error + for i := 0; i < b.N; i++ { + for _, bm := range benchmarks { + for i := 0; i < bm.count; i++ { + val, err = json.Decode(nil, bm.encoded) + } + } + } + + require.NoError(b, err) +} + +func BenchmarkEncodeBatchEventsCCF(b *testing.B) { + var err error + var encodedSize int + + for i := 0; i < b.N; i++ { + encodedSize = 0 + for _, bm := range batchBenchmarks { + for i := 0; i < bm.count; i++ { + encoded, err = ccf.Encode(bm.value) + encodedSize += len(encoded) + } + } + } + require.NoError(b, err) + + //fmt.Printf("Batch events encoded in CCF are %d bytes\n", encodedSize) +} + +func BenchmarkDecodeBatchEventsCCF(b *testing.B) { + type encodedBatchEvent struct { + count int + encoded []byte + } + + benchmarks := make([]encodedBatchEvent, len(batchBenchmarks)) + + for i, bm := range batchBenchmarks { + benchmarks[i] = encodedBatchEvent{ + count: bm.count, + encoded: ccf.MustEncode(bm.value), + } + } + + var err error + for i := 0; i < b.N; i++ { + for _, bm := range benchmarks { + for i := 0; i < bm.count; i++ { + val, err = ccf.Decode(nil, bm.encoded) + } + } + } + + require.NoError(b, err) +} From 4c4b6cb8f4ca6009406152187df54bd03065f132 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 21:23:46 -0600 Subject: [PATCH 006/173] Remove valid UTF-8 check in encodeString Added comment stating that cadence.String and cadence.Character must be valid UTF-8 and it is the application's responsibility to provide the CCF encoder with valid UTF-8 strings. "Valid CCF Encoding Requirements" in CCF Specification states: "Encoders are not required to check for invalid input items (e.g. invalid UTF-8 strings, duplicate dictionary keys, etc.) Applications MUST NOT provide invalid items to encoders." --- encoding/ccf/encode.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index bb583793b1..953d604d79 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -25,7 +25,6 @@ import ( goRuntime "runtime" "sort" "sync" - "unicode/utf8" "github.com/fxamacker/cbor/v2" "github.com/onflow/cadence" @@ -304,6 +303,17 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy // / word64-value // / fix64-value // / ufix64-value +// +// IMPORTANT: +// "Valid CCF Encoding Requirements" in CCF Specification states: +// +// "Encoders are not required to check for invalid input items +// (e.g. invalid UTF-8 strings, duplicate dictionary keys, etc.) +// Applications MUST NOT provide invalid items to encoders." +// +// cadence.String and cadence.Character must be valid UTF-8 +// and it is the application's responsibility to provide +// the CCF encoder with valid UTF-8 strings. func (e *Encoder) encodeValue(v cadence.Value, staticType cadence.Type, tids ccfTypeIDByCadenceType) error { runtimeType := v.Type() @@ -450,12 +460,7 @@ func (e *Encoder) encodeCharacter(v cadence.Character) error { // encodeString encodes cadence.String as // language=CDDL // string-value = tstr -// NOTE: cadence.String must be valid UTF-8. func (e *Encoder) encodeString(v cadence.String) error { - s := string(v) - if !utf8.ValidString(s) { - return fmt.Errorf("invalid UTF-8 in string: %s", s) - } return e.enc.EncodeString(string(v)) } From e0af91fc1d6b1724a3473ffe7538a51c1284845a Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Feb 2023 21:27:43 -0600 Subject: [PATCH 007/173] Close the cbor.StreamEncoder in ccf.Encode() --- encoding/ccf/encode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 953d604d79..ccd4177eb7 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -56,7 +56,9 @@ type Encoder struct { // This function returns an error if the Cadence value cannot be represented in CCF. func Encode(value cadence.Value) ([]byte, error) { var w bytes.Buffer + enc := NewEncoder(&w) + defer enc.enc.Close() err := enc.Encode(value) if err != nil { @@ -91,8 +93,6 @@ func NewEncoder(w io.Writer) *Encoder { func (e *Encoder) Encode(value cadence.Value) (err error) { // capture panics defer func() { - e.enc.Close() - if r := recover(); r != nil { // don't recover Go errors goErr, ok := r.(goRuntime.Error) From d8bb86131df3ad56d93391399ab909a2516efef2 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 1 Mar 2023 10:17:27 -0600 Subject: [PATCH 008/173] Refactor CCF encoder error handling --- encoding/ccf/ccf_type_id.go | 4 +- encoding/ccf/encode.go | 81 ++++++++++++++++++++++++++----- encoding/ccf/encode_type.go | 2 +- encoding/ccf/encode_typedef.go | 16 ++++-- encoding/ccf/simple_type_utils.go | 50 +++++++++++++++++++ 5 files changed, 134 insertions(+), 19 deletions(-) diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go index 8b06735ef5..cecfbe3c7e 100644 --- a/encoding/ccf/ccf_type_id.go +++ b/encoding/ccf/ccf_type_id.go @@ -52,7 +52,7 @@ type ccfTypeIDByCadenceType map[string]ccfTypeID func (types ccfTypeIDByCadenceType) id(t cadence.Type) (ccfTypeID, error) { id, ok := types[t.ID()] if !ok { - return 0, fmt.Errorf("failed to get ccf id for cadence type %s", t.ID()) + return 0, fmt.Errorf("CCF type ID not found for type %s", t.ID()) } return id, nil } @@ -71,7 +71,7 @@ func (ids cadenceTypeByCCFTypeID) add(id ccfTypeID, typ cadence.Type) bool { func (ids cadenceTypeByCCFTypeID) typ(id ccfTypeID) (cadence.Type, error) { t, ok := ids[id] if !ok { - return nil, fmt.Errorf("failed to get cadence type for ccf id %d", id) + return nil, fmt.Errorf("type not found for CCF type ID %d", id) } return t, nil } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index ccd4177eb7..e6cebb6819 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -93,6 +93,7 @@ func NewEncoder(w io.Writer) *Encoder { func (e *Encoder) Encode(value cadence.Value) (err error) { // capture panics defer func() { + // Recover panic error if there is any. if r := recover(); r != nil { // don't recover Go errors goErr, ok := r.(goRuntime.Error) @@ -105,7 +106,17 @@ func (e *Encoder) Encode(value cadence.Value) (err error) { panic(r) } - err = fmt.Errorf("failed to encode value: %w", panicErr) + err = panicErr + } + + // Add context to error if there is any. + if err != nil { + err = fmt.Errorf( + "ccf: failed to encode value %q (type %q): %s", + value.String(), + value.Type().ID(), + err, + ) } }() @@ -239,14 +250,16 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy if err != nil { return err } + case cadence.InterfaceType: // Encode struct-interface-type, resource-interface-type, or contract-interface-type. err = e.encodeInterfaceType(x, tids) if err != nil { return err } + default: - return fmt.Errorf("failed to encode %s in type definition", typ) + panic(fmt.Errorf("unexpected type %s in type definition", typ.ID())) } } @@ -314,7 +327,12 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy // cadence.String and cadence.Character must be valid UTF-8 // and it is the application's responsibility to provide // the CCF encoder with valid UTF-8 strings. -func (e *Encoder) encodeValue(v cadence.Value, staticType cadence.Type, tids ccfTypeIDByCadenceType) error { +func (e *Encoder) encodeValue( + v cadence.Value, + staticType cadence.Type, + tids ccfTypeIDByCadenceType, +) error { + runtimeType := v.Type() if needToEncodeRuntimeType(staticType, runtimeType) { @@ -343,80 +361,117 @@ func (e *Encoder) encodeValue(v cadence.Value, staticType cadence.Type, tids ccf switch x := v.(type) { case cadence.Void: return e.encodeVoid(x) + case cadence.Optional: return e.encodeOptional(x, tids) + case cadence.Bool: return e.encodeBool(x) + case cadence.Character: return e.encodeCharacter(x) + case cadence.String: return e.encodeString(x) + case cadence.Address: return e.encodeAddress(x) + case cadence.Int: return e.encodeInt(x) + case cadence.Int8: return e.encodeInt8(x) + case cadence.Int16: return e.encodeInt16(x) + case cadence.Int32: return e.encodeInt32(x) + case cadence.Int64: return e.encodeInt64(x) + case cadence.Int128: return e.encodeInt128(x) + case cadence.Int256: return e.encodeInt256(x) + case cadence.UInt: return e.encodeUInt(x) + case cadence.UInt8: return e.encodeUInt8(x) + case cadence.UInt16: return e.encodeUInt16(x) + case cadence.UInt32: return e.encodeUInt32(x) + case cadence.UInt64: return e.encodeUInt64(x) + case cadence.UInt128: return e.encodeUInt128(x) + case cadence.UInt256: return e.encodeUInt256(x) + case cadence.Word8: return e.encodeWord8(x) + case cadence.Word16: return e.encodeWord16(x) + case cadence.Word32: return e.encodeWord32(x) + case cadence.Word64: return e.encodeWord64(x) + case cadence.Fix64: return e.encodeFix64(x) + case cadence.UFix64: return e.encodeUFix64(x) + case cadence.Array: return e.encodeArray(x, tids) + case cadence.Dictionary: return e.encodeDictionary(x, tids) + case cadence.Struct: return e.encodeStruct(x, tids) + case cadence.Resource: return e.encodeResource(x, tids) + case cadence.Event: return e.encodeEvent(x, tids) + case cadence.Contract: return e.encodeContract(x, tids) + case cadence.Path: return e.encodePath(x) + case cadence.TypeValue: return e.encodeTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) + case cadence.StorageCapability: return e.encodeCapability(x) + case cadence.Enum: return e.encodeEnum(x, tids) + case cadence.Function: return e.encodeFunction(x.FunctionType, ccfTypeIDByCadenceType{}) + default: - panic(fmt.Errorf("unsupported value: %T, %v", v, v)) + panic(fmt.Errorf("unsupported value %s (%T)", v, v)) } } @@ -434,8 +489,10 @@ func (e *Encoder) encodeOptional(v cadence.Optional, tids ccfTypeIDByCadenceType switch innerValue := v.Value.(type) { case cadence.Optional: return e.encodeOptional(innerValue, tids) + case nil: return e.enc.EncodeNil() + default: // Use innerValue.Type() as static type to avoid encoding type // because OptionalType is already encoded. @@ -644,7 +701,7 @@ func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadence dictionaryType := v.DictionaryType.(*cadence.DictionaryType) if dictionaryType == nil { - return fmt.Errorf("failed to retrieve DictionaryType from cadence.Dictionary") + return fmt.Errorf("unexpected dictionary type %s", v.DictionaryType.ID()) } staticKeyType := dictionaryType.KeyType @@ -718,7 +775,7 @@ func encodeAndSortKeyValuePairs( ) { dictionaryType := v.DictionaryType.(*cadence.DictionaryType) if dictionaryType == nil { - return nil, fmt.Errorf("failed to retrieve DictionaryType from cadence.Dictionary") + return nil, fmt.Errorf("expected dictionary type %s", v.DictionaryType.ID()) } staticKeyType := dictionaryType.KeyType @@ -764,7 +821,7 @@ func encodeAndSortKeyValuePairs( } if off != len(b) { // Sanity check - return nil, fmt.Errorf("failed to encode and sort dictionary pairs: off %d, len(b) %d", off, len(b)) + panic(fmt.Errorf("encoded dictionary pairs' offset %d doesn't match buffer length %d", off, len(b))) } sort.Sort(bytewiseKeyValuePairSorter(encodedPairs)) @@ -815,13 +872,12 @@ func (e *Encoder) encodeComposite( fields []cadence.Value, tids ccfTypeIDByCadenceType, ) error { - id := typ.ID() staticFieldTypes := typ.CompositeFields() if len(staticFieldTypes) != len(fields) { panic(fmt.Errorf( - "%s field count (%d) does not match declared type (%d)", - id, + "%s field count %d doesn't match declared field type count %d", + typ.ID(), len(fields), len(staticFieldTypes), )) @@ -1049,7 +1105,7 @@ func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceTy return e.encodeNilTypeValue() default: - panic(fmt.Errorf("unsupported type: %T, %v", typ, typ)) + panic(fmt.Errorf("unsupported type value %s (%T)", typ.ID(), typ)) } } @@ -1383,7 +1439,7 @@ func (e *Encoder) encodeCompositeTypeValue( ) error { ccfID, ok := visited[cadenceTypeID] if !ok { - return fmt.Errorf("failed to get ccf type id for cadence type %s", cadenceTypeID) + return fmt.Errorf("CCF type ID not found for composite type value %s", cadenceTypeID) } // Encode given tag number indicating cadence type value. @@ -1664,6 +1720,7 @@ func isOptionalNeverType(t cadence.Type) bool { return true } t = ot.Type + default: return false } diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 3d7d773965..5dc55a8df8 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -82,7 +82,7 @@ func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType return e.encodeSimpleType(TypeFunction) default: - panic(fmt.Errorf("unsupported type: %T, %v", typ, typ)) + panic(fmt.Errorf("unsupported type %s (%T)", typ.ID(), typ)) } } diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 54edf38568..b504dc33c6 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -66,7 +66,7 @@ import ( func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { ccfID, err := tids.id(typ) if err != nil { - return fmt.Errorf("failed to find CCF type ID for composite type %v (%T)", typ, typ) + return fmt.Errorf("CCF type ID not found for composite type %s (%T)", typ.ID(), typ) } var cborTagNum uint64 @@ -74,16 +74,21 @@ func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDB switch t := typ.(type) { case *cadence.StructType: cborTagNum = CBORTagStructType + case *cadence.ResourceType: cborTagNum = CBORTagResourceType + case *cadence.EventType: cborTagNum = CBORTagEventType + case *cadence.ContractType: cborTagNum = CBORTagContractType + case *cadence.EnumType: cborTagNum = CBORTagEnumType + default: - return fmt.Errorf("unsupported type in type definition: %T, %v", t, t) + panic(fmt.Errorf("unexpected composite type %s (%T)", t.ID(), t)) } // Encode tag number indicating composite type. @@ -203,7 +208,7 @@ func (e *Encoder) encodeCompositeTypeField(typ cadence.Field, tids ccfTypeIDByCa func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDByCadenceType) error { ccfID, err := tids.id(typ) if err != nil { - return fmt.Errorf("failed to find CCF type ID for interface type %v (%T)", typ, typ) + return fmt.Errorf("CCF type ID not found for interface type %s (%T)", typ.ID(), typ) } var cborTagNum uint64 @@ -211,12 +216,15 @@ func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDB switch t := typ.(type) { case *cadence.StructInterfaceType: cborTagNum = CBORTagStructInterfaceType + case *cadence.ResourceInterfaceType: cborTagNum = CBORTagResourceInterfaceType + case *cadence.ContractInterfaceType: cborTagNum = CBORTagContractInterfaceType + default: - return fmt.Errorf("unsupported type in type definition: %T, %v", t, t) + panic(fmt.Errorf("unexpected interface type %s (%T)", t.ID(), t)) } // Encode tag number indicating interface type. diff --git a/encoding/ccf/simple_type_utils.go b/encoding/ccf/simple_type_utils.go index 92af6cb984..bf7142c9c4 100644 --- a/encoding/ccf/simple_type_utils.go +++ b/encoding/ccf/simple_type_utils.go @@ -91,104 +91,154 @@ func simpleTypeIDByType(typ cadence.Type) (uint64, bool) { switch typ.(type) { case cadence.AnyType: return TypeAny, true + case cadence.AnyStructType: return TypeAnyStruct, true + case cadence.AnyResourceType: return TypeAnyResource, true + case cadence.AddressType: return TypeAddress, true + case cadence.MetaType: return TypeMetaType, true + case cadence.VoidType: return TypeVoid, true + case cadence.NeverType: return TypeNever, true + case cadence.BoolType: return TypeBool, true + case cadence.StringType: return TypeString, true + case cadence.CharacterType: return TypeCharacter, true + case cadence.BytesType: return TypeBytes, true + case cadence.NumberType: return TypeNumber, true + case cadence.SignedNumberType: return TypeSignedNumber, true + case cadence.IntegerType: return TypeInteger, true + case cadence.SignedIntegerType: return TypeSignedInteger, true + case cadence.FixedPointType: return TypeFixedPoint, true + case cadence.SignedFixedPointType: return TypeSignedFixedPoint, true + case cadence.IntType: return TypeInt, true + case cadence.Int8Type: return TypeInt8, true + case cadence.Int16Type: return TypeInt16, true + case cadence.Int32Type: return TypeInt32, true + case cadence.Int64Type: return TypeInt64, true + case cadence.Int128Type: return TypeInt128, true + case cadence.Int256Type: return TypeInt256, true + case cadence.UIntType: return TypeUInt, true + case cadence.UInt8Type: return TypeUInt8, true + case cadence.UInt16Type: return TypeUInt16, true + case cadence.UInt32Type: return TypeUInt32, true + case cadence.UInt64Type: return TypeUInt64, true + case cadence.UInt128Type: return TypeUInt128, true + case cadence.UInt256Type: return TypeUInt256, true + case cadence.Word8Type: return TypeWord8, true + case cadence.Word16Type: return TypeWord16, true + case cadence.Word32Type: return TypeWord32, true + case cadence.Word64Type: return TypeWord64, true + case cadence.Fix64Type: return TypeFix64, true + case cadence.UFix64Type: return TypeUFix64, true + case cadence.BlockType: return TypeBlock, true + case cadence.PathType: return TypePath, true + case cadence.CapabilityPathType: return TypeCapabilityPath, true + case cadence.StoragePathType: return TypeStoragePath, true + case cadence.PublicPathType: return TypePublicPath, true + case cadence.PrivatePathType: return TypePrivatePath, true + case cadence.AccountKeyType: return TypeAccountKey, true + case cadence.AuthAccountContractsType: return TypeAuthAccountContracts, true + case cadence.AuthAccountKeysType: return TypeAuthAccountKeys, true + case cadence.AuthAccountType: return TypeAuthAccount, true + case cadence.PublicAccountContractsType: return TypePublicAccountContracts, true + case cadence.PublicAccountKeysType: return TypePublicAccountKeys, true + case cadence.PublicAccountType: return TypePublicAccount, true + case cadence.DeployedContractType: return TypeDeployedContract, true } From 4b303bffafbd090c27338d0947fc3c1f5ea70b99 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:51:31 -0600 Subject: [PATCH 009/173] Refactor CCF decoder error handling --- encoding/ccf/bench_test.go | 4 +- encoding/ccf/ccf_test.go | 8 +-- encoding/ccf/decode.go | 96 ++++++++++++++++++++-------------- encoding/ccf/decode_type.go | 92 ++++++++++++++++++++++++++++---- encoding/ccf/decode_typedef.go | 32 +++++++----- encoding/ccf/encode.go | 4 +- 6 files changed, 166 insertions(+), 70 deletions(-) diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go index e869829ffe..e9dfcd59ab 100644 --- a/encoding/ccf/bench_test.go +++ b/encoding/ccf/bench_test.go @@ -138,7 +138,7 @@ func BenchmarkEncodeBatchEventsJSON(b *testing.B) { } require.NoError(b, err) - //fmt.Printf("Batch events encoded in JSON are %d bytes\n", encodedSize) + // fmt.Printf("Batch events encoded in JSON are %d bytes\n", encodedSize) } func BenchmarkDecodeBatchEventsJSON(b *testing.B) { @@ -183,7 +183,7 @@ func BenchmarkEncodeBatchEventsCCF(b *testing.B) { } require.NoError(b, err) - //fmt.Printf("Batch events encoded in CCF are %d bytes\n", encodedSize) + // fmt.Printf("Batch events encoded in CCF are %d bytes\n", encodedSize) } func BenchmarkDecodeBatchEventsCCF(b *testing.B) { diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 03c19d368e..4332df715b 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -3121,7 +3121,7 @@ func exportFromScript(t *testing.T, code string) cadence.Value { checker, err := checker.ParseAndCheck(t, code) require.NoError(t, err) - var uuid uint64 = 0 + var uuid uint64 inter, err := interpreter.NewInterpreter( interpreter.ProgramFromChecker(checker), @@ -6445,7 +6445,7 @@ func TestDecodeInvalidType(t *testing.T) { } _, err := ccf.Decode(nil, encodedData) require.Error(t, err) - assert.Equal(t, "invalid type ID for built-in: ``", err.Error()) + assert.Equal(t, "ccf: failed to decode: invalid type ID for built-in: ``", err.Error()) }) t.Run("undefined type", func(t *testing.T) { @@ -6498,7 +6498,7 @@ func TestDecodeInvalidType(t *testing.T) { } _, err := ccf.Decode(nil, encodedData) require.Error(t, err) - assert.Equal(t, "invalid type ID `I.Foo`: invalid identifier location type ID: missing qualified identifier", err.Error()) + assert.Equal(t, "ccf: failed to decode: invalid type ID `I.Foo`: invalid identifier location type ID: missing qualified identifier", err.Error()) }) t.Run("unknown location prefix", func(t *testing.T) { @@ -6551,7 +6551,7 @@ func TestDecodeInvalidType(t *testing.T) { } _, err := ccf.Decode(nil, encodedData) require.Error(t, err) - assert.Equal(t, "invalid type ID for built-in: `N.PublicKey`", err.Error()) + assert.Equal(t, "ccf: failed to decode: invalid type ID for built-in: `N.PublicKey`", err.Error()) }) } diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 98d2544be2..09dcc7d6db 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -22,6 +22,7 @@ import ( "fmt" "math" "math/big" + goRuntime "runtime" "github.com/fxamacker/cbor/v2" "github.com/onflow/cadence" @@ -86,13 +87,25 @@ func NewDecoder(gauge common.MemoryGauge, b []byte) *Decoder { func (d *Decoder) Decode() (value cadence.Value, err error) { // Capture panics that occur during decoding. defer func() { + // Recover panic error if there is any. if r := recover(); r != nil { + // Don't recover Go errors. + goErr, ok := r.(goRuntime.Error) + if ok { + panic(goErr) + } + panicErr, isError := r.(error) if !isError { panic(r) } - err = errors.NewDefaultUserError("failed to decode value: %w", panicErr) + err = panicErr + } + + // Add context to error if there is any. + if err != nil { + err = errors.NewDefaultUserError("ccf: failed to decode: %s", err) } }() @@ -113,9 +126,7 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { default: return nil, fmt.Errorf( - "failed to decode top level message: expect CBOR tag num %d or %d, got %d", - CBORTagTypeDefAndValue, - CBORTagTypeAndValue, + "unsupported top level CCF message with CBOR tag number %d", tagNum, ) } @@ -353,8 +364,9 @@ func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cad default: err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) if err != nil { - return nil, fmt.Errorf("failed to decode value of type %s %T: %w", typ, typ, err) + return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", typ.ID(), typ, err.Error()) } + // Decode ccf-type-and-value-message. return d.decodeTypeAndValue(types) } @@ -426,7 +438,7 @@ func (d *Decoder) decodeAddress() (cadence.Value, error) { return nil, err } if len(b) != 8 { - return nil, fmt.Errorf("failed to decode address-value: expect 8 bytes, got %d bytes", len(b)) + return nil, fmt.Errorf("encoded address-value has length %d (expected 8 bytes)", len(b)) } return cadence.BytesToMeteredAddress(d.gauge, b), nil } @@ -461,7 +473,7 @@ func (d *Decoder) decodeInt8() (cadence.Value, error) { } if i < math.MinInt8 || i > math.MaxInt8 { return nil, fmt.Errorf( - "failed to decode int8-value: %d is outside range of Int8 [%d, %d]", + "encoded int8-value %d is outside range of Int8 [%d, %d]", i, math.MaxInt8, math.MaxInt8, @@ -480,7 +492,7 @@ func (d *Decoder) decodeInt16() (cadence.Value, error) { } if i < math.MinInt16 || i > math.MaxInt16 { return nil, fmt.Errorf( - "failed to decode int16-value: %d is outside range of Int16 [%d, %d]", + "encoded int16-value %d is outside range of Int16 [%d, %d]", i, math.MinInt16, math.MaxInt16, @@ -499,7 +511,7 @@ func (d *Decoder) decodeInt32() (cadence.Value, error) { } if i < math.MinInt32 || i > math.MaxInt32 { return nil, fmt.Errorf( - "failed to decode int32-value: %d is outside range of Int32 [%d, %d]", + "encoded int32-value %d is outside range of Int32 [%d, %d]", i, math.MinInt32, math.MaxInt32, @@ -561,7 +573,7 @@ func (d *Decoder) decodeUInt() (cadence.Value, error) { } if bigInt.Sign() < 0 { return nil, fmt.Errorf( - "failed to decode uint-value: %s is negative", + "encoded uint-value %s is negative", bigInt.String(), ) } @@ -586,7 +598,7 @@ func (d *Decoder) decodeUInt8() (cadence.Value, error) { } if i > math.MaxUint8 { return nil, fmt.Errorf( - "failed to decode uint8-value: %d is outside range of Uint8 [0, %d]", + "encoded uint8-value %d is outside range of Uint8 [0, %d]", i, math.MaxUint8, ) @@ -604,7 +616,7 @@ func (d *Decoder) decodeUInt16() (cadence.Value, error) { } if i > math.MaxUint16 { return nil, fmt.Errorf( - "failed to decode uint16-value: %d is outside range of Uint16 [0, %d]", + "encoded uint16-value %d is outside range of Uint16 [0, %d]", i, math.MaxUint16, ) @@ -622,7 +634,7 @@ func (d *Decoder) decodeUInt32() (cadence.Value, error) { } if i > math.MaxUint32 { return nil, fmt.Errorf( - "failed to decode uint32-value: %d is outside range of Uint32 [0, %d]", + "encoded uint32-value %d is outside range of Uint32 [0, %d]", i, math.MaxUint32, ) @@ -651,7 +663,7 @@ func (d *Decoder) decodeUInt128() (cadence.Value, error) { } if bigInt.Sign() < 0 { return nil, fmt.Errorf( - "failed to decode uint128-value: %s is negative", + "encoded uint128-value %s is negative", bigInt.String(), ) } @@ -673,7 +685,7 @@ func (d *Decoder) decodeUInt256() (cadence.Value, error) { } if bigInt.Sign() < 0 { return nil, fmt.Errorf( - "failed to decode uint256-value: %s is negative", + "encoded uint256-value %s is negative", bigInt.String(), ) } @@ -695,7 +707,7 @@ func (d *Decoder) decodeWord8() (cadence.Value, error) { } if i > math.MaxUint8 { return nil, fmt.Errorf( - "failed to decode word8-value: %d is outside range of Word8 [0, %d]", + "encoded word8-value %d is outside range of Word8 [0, %d]", i, math.MaxUint8, ) @@ -713,7 +725,7 @@ func (d *Decoder) decodeWord16() (cadence.Value, error) { } if i > math.MaxUint16 { return nil, fmt.Errorf( - "failed to decode word16-value: %d is outside range of Word16 [0, %d]", + "encoded word16-value %d is outside range of Word16 [0, %d]", i, math.MaxUint16, ) @@ -731,7 +743,7 @@ func (d *Decoder) decodeWord32() (cadence.Value, error) { } if i > math.MaxUint32 { return nil, fmt.Errorf( - "failed to decode word32-value: %d is outside range of Word32 [0, %d]", + "encoded word32-value %d is outside range of Word32 [0, %d]", i, math.MaxUint32, ) @@ -813,9 +825,9 @@ func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSiz if hasKnownSize && uint64(knownSize) != n { return nil, fmt.Errorf( - "failed to decode array-value: expect %d elements, got %d elements", - knownSize, + "encoded array-value has %d elements (expected %d elements)", n, + knownSize, ) } @@ -857,7 +869,10 @@ func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTyp // Check if number of elements is even. if n%2 != 0 { - return nil, fmt.Errorf("failed to decode dictionary, expect even number of elements, got %d elements", n) + return nil, fmt.Errorf( + "encoded dict-value has %d elements (expected even number of elements)", + n, + ) } // previousKeyRawBytes is used to determine if dictionary keys are sorted @@ -878,7 +893,7 @@ func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTyp // // "dict-value key-value pairs MUST be sorted by key." if !bytesAreSortedBytewise(previousKeyRawBytes, keyRawBytes) { - return nil, fmt.Errorf("dictionary keys are not sorted") + return nil, fmt.Errorf("encoded dict-value keys are not sorted") } previousKeyRawBytes = keyRawBytes @@ -1103,7 +1118,7 @@ func (d *Decoder) decodePath() (cadence.Value, error) { Amount: uint64(len(domain)), }) - // Decode identifer + // Decode identifier. identifier, err := d.dec.DecodeString() if err != nil { return nil, err @@ -1254,7 +1269,7 @@ func (d *Decoder) _decodeTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type return d.decodeContractInterfaceTypeValue(visited) default: - return nil, fmt.Errorf("failed to decode type value: unexpected CBOR tag number %d", tagNum) + return nil, fmt.Errorf("unsupported type-value with CBOR tag number %d", tagNum) } } @@ -1273,7 +1288,7 @@ func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode struct type value, expect nil type, got %s type", + "encoded struct-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1304,7 +1319,7 @@ func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (caden ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode resource type value, expect nil type, got %s type", + "encoded resource-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1335,13 +1350,13 @@ func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence. ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode event type value, expect nil type, got %s type", + "encoded event-type-value has type %s (expected nil type)", typ.ID(), ) } if len(inits) != 1 { return nil, fmt.Errorf( - "failed to decode event type value, expect 0 or 1 initialization, got %d", + "encoded event-type-value has %d initializations (expected 1 initialization)", len(inits), ) } @@ -1372,7 +1387,7 @@ func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (caden ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode contract type value, expect nil type, got %s type", + "encoded contract-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1429,7 +1444,7 @@ func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode struct interface type value, expect nil type, got %s type", + "encoded struct-interface-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1460,7 +1475,7 @@ func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeI ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode resource interface type value, expect nil type, got %s type", + "encoded resource-interface-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1491,7 +1506,7 @@ func (d *Decoder) decodeContractInterfaceTypeValue(visited cadenceTypeByCCFTypeI ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( - "failed to decode contract interface type value, expect nil type, got %s type", + "encoded contract-interface-type-value has type %s (expected nil type)", typ.ID(), ) } @@ -1548,7 +1563,7 @@ func (d *Decoder) decodeCompositeTypeValue( // "composite-type-value.id MUST be identical to the zero-based encoding order type-value." if compTypeValue.ccfID != newCCFTypeIDFromUint64(uint64(len(visited))) { return nil, fmt.Errorf( - "ccf id %d in encoded composite type value isn't identical to the zero-based encoding order type-value", + "encoded composite-type-value's CCF type ID %d doesn't match zero-based encoding order composite-type-value", compTypeValue.ccfID, ) } @@ -1558,7 +1573,7 @@ func (d *Decoder) decodeCompositeTypeValue( // "Valid CCF Encoding Requirements" in CCF specs: // // "composite-type-value.id MUST be unique in the same composite-type-value data item." - return nil, fmt.Errorf("duplicate ccf id %d in encoded composite type value", compTypeValue.ccfID) + return nil, fmt.Errorf("found duplicate CCF type ID %d in encoded composite-type-value", compTypeValue.ccfID) } // Decode fields after type is resolved to handle recursive types. @@ -1571,6 +1586,7 @@ func (d *Decoder) decodeCompositeTypeValue( switch compositeType := compositeType.(type) { case cadence.CompositeType: compositeType.SetCompositeFields(fields) + case cadence.InterfaceType: compositeType.SetInterfaceFields(fields) } @@ -1685,7 +1701,7 @@ func (d *Decoder) decodeInitializerTypeValues(visited cadenceTypeByCCFTypeID) ([ // ] // ] func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]cadence.Parameter, error) { - // Decode number of paramaters + // Decode number of parameters. count, err := d.dec.DecodeArrayHead() if err != nil { return nil, err @@ -1723,7 +1739,11 @@ func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]c // // "composite-type-value.initializers MUST be sorted by identifier." if !stringsAreSortedBytewise(previousParameterIdentifier, param.Identifier) { - return nil, fmt.Errorf("parameter identifiers are not sorted") + return nil, fmt.Errorf( + "parameter identifiers are not sorted (%s, %s)", + previousParameterIdentifier, + param.Identifier, + ) } parameterLabels[param.Label] = struct{}{} @@ -1828,7 +1848,7 @@ func decodeCBORArrayWithKnownSize(dec *cbor.StreamDecoder, n uint64) error { return err } if c != n { - return fmt.Errorf("failed to decode CBOR array of known length, expect length %d, got length %d", n, c) + return fmt.Errorf("CBOR array has %d elements (expected %d elements)", c, n) } return nil } @@ -1839,7 +1859,7 @@ func decodeCBORTagWithKnownNumber(dec *cbor.StreamDecoder, n uint64) error { return err } if tagNum != n { - return fmt.Errorf("failed to decode CBOR tag of known tag number, expect %d, got %d", n, tagNum) + return fmt.Errorf("CBOR tag number is %d (expected %d)", tagNum, n) } return nil } diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 2e3f9bed1e..4d88edf2de 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -81,7 +81,7 @@ func (d *Decoder) decodeInlineType(types cadenceTypeByCCFTypeID) (cadence.Type, return d.decodeTypeRef(types) default: - return nil, fmt.Errorf("faild to decode inline type %d", tagNum) + return nil, fmt.Errorf("unsupported encoded inline type with CBOR tag number %d", tagNum) } } @@ -96,108 +96,159 @@ func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { switch simpleTypeID { case TypeBool: return cadence.TheBoolType, nil + case TypeString: return cadence.TheStringType, nil + case TypeCharacter: return cadence.TheCharacterType, nil + case TypeAddress: return cadence.TheAddressType, nil + case TypeInt: return cadence.TheIntType, nil + case TypeInt8: return cadence.TheInt8Type, nil + case TypeInt16: return cadence.TheInt16Type, nil + case TypeInt32: return cadence.TheInt32Type, nil + case TypeInt64: return cadence.TheInt64Type, nil + case TypeInt128: return cadence.TheInt128Type, nil + case TypeInt256: return cadence.TheInt256Type, nil + case TypeUInt: return cadence.TheUIntType, nil + case TypeUInt8: return cadence.TheUInt8Type, nil + case TypeUInt16: return cadence.TheUInt16Type, nil + case TypeUInt32: return cadence.TheUInt32Type, nil + case TypeUInt64: return cadence.TheUInt64Type, nil + case TypeUInt128: return cadence.TheUInt128Type, nil + case TypeUInt256: return cadence.TheUInt256Type, nil + case TypeWord8: return cadence.TheWord8Type, nil + case TypeWord16: return cadence.TheWord16Type, nil + case TypeWord32: return cadence.TheWord32Type, nil + case TypeWord64: return cadence.TheWord64Type, nil + case TypeFix64: return cadence.TheFix64Type, nil + case TypeUFix64: return cadence.TheUFix64Type, nil + case TypePath: return cadence.ThePathType, nil + case TypeCapabilityPath: return cadence.TheCapabilityPathType, nil + case TypeStoragePath: return cadence.TheStoragePathType, nil + case TypePublicPath: return cadence.ThePublicPathType, nil + case TypePrivatePath: return cadence.ThePrivatePathType, nil + case TypeAuthAccount: return cadence.TheAuthAccountType, nil + case TypePublicAccount: return cadence.ThePublicAccountType, nil + case TypeAuthAccountKeys: return cadence.TheAuthAccountKeysType, nil + case TypePublicAccountKeys: return cadence.ThePublicAccountKeysType, nil + case TypeAuthAccountContracts: return cadence.TheAuthAccountContractsType, nil + case TypePublicAccountContracts: return cadence.ThePublicAccountContractsType, nil + case TypeDeployedContract: return cadence.TheDeployedContractType, nil + case TypeAccountKey: return cadence.TheAccountKeyType, nil + case TypeBlock: return cadence.TheBlockType, nil + case TypeAny: return cadence.TheAnyType, nil + case TypeAnyStruct: return cadence.TheAnyStructType, nil + case TypeAnyResource: return cadence.TheAnyResourceType, nil + case TypeMetaType: return cadence.TheMetaType, nil + case TypeNever: return cadence.TheNeverType, nil + case TypeNumber: return cadence.TheNumberType, nil + case TypeSignedNumber: return cadence.TheSignedNumberType, nil + case TypeInteger: return cadence.TheIntegerType, nil + case TypeSignedInteger: return cadence.TheSignedIntegerType, nil + case TypeFixedPoint: return cadence.TheFixedPointType, nil + case TypeSignedFixedPoint: return cadence.TheSignedFixedPointType, nil + case TypeBytes: return cadence.TheBytesType, nil + case TypeVoid: return cadence.TheVoidType, nil + default: - return nil, fmt.Errorf("failed to decode simple type: unexpected id %d", simpleTypeID) + return nil, fmt.Errorf("unsupported encoded simple type ID %d", simpleTypeID) } } @@ -214,7 +265,10 @@ func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { // #6.186(type-value) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeOptionalType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeOptionalType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode inline-type or type-value. elementType, err := decodeTypeFn(types) if err != nil { @@ -236,7 +290,10 @@ func (d *Decoder) decodeOptionalType(types cadenceTypeByCCFTypeID, decodeTypeFn // #6.187(type-value) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeVarSizedArrayType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeVarSizedArrayType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode inline-type or type-value. elementType, err := decodeTypeFn(types) if err != nil { @@ -264,7 +321,10 @@ func (d *Decoder) decodeVarSizedArrayType(types cadenceTypeByCCFTypeID, decodeTy // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeConstantSizedArrayType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeConstantSizedArrayType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode array head of length 2. err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { @@ -305,7 +365,10 @@ func (d *Decoder) decodeConstantSizedArrayType(types cadenceTypeByCCFTypeID, dec // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeDictType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeDictType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode array head of length 2. err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { @@ -348,7 +411,10 @@ func (d *Decoder) decodeDictType(types cadenceTypeByCCFTypeID, decodeTypeFn deco // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeCapabilityType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeCapabilityType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode array head of length 1 err := decodeCBORArrayWithKnownSize(d.dec, 1) if err != nil { @@ -383,7 +449,10 @@ func (d *Decoder) decodeCapabilityType(types cadenceTypeByCCFTypeID, decodeTypeF // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeReferenceType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeReferenceType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode array head of length 2 err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { @@ -426,7 +495,10 @@ func (d *Decoder) decodeReferenceType(types cadenceTypeByCCFTypeID, decodeTypeFn // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. -func (d *Decoder) decodeRestrictedType(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Type, error) { +func (d *Decoder) decodeRestrictedType( + types cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { // Decode array of length 3. err := decodeCBORArrayWithKnownSize(d.dec, 3) if err != nil { @@ -474,7 +546,7 @@ func (d *Decoder) decodeRestrictedType(types cadenceTypeByCCFTypeID, decodeTypeF // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." if !stringsAreSortedBytewise(previousRestrictedTypeID, restrictedType.ID()) { - return nil, fmt.Errorf("restricted types are not sorted") + return nil, fmt.Errorf("restricted types are not sorted (%s, %s)", previousRestrictedTypeID, restrictedType.ID()) } restrictionTypeIDs[restrictedType.ID()] = struct{}{} diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index e68aefa73c..3421da90e2 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -48,7 +48,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { } if count == 0 { - return nil, errors.New("expect one or more type defintions in composite-typedef, got 0") + return nil, errors.New("found 0 type definition in composite-typedef (expected at least 1 type definition)") } types := make(map[ccfTypeID]cadence.Type, count) @@ -74,7 +74,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { // "composite-type.cadence-type-id MUST be unique in // ccf-typedef-message or ccf-typedef-and-value-message." if _, ok := cadenceTypeIDs[cadenceID]; ok { - return nil, fmt.Errorf("found duplicated cadence type id %s in type definition", cadenceID) + return nil, fmt.Errorf("found duplicate Cadence type ID %s in composite-typedef", cadenceID) } // "Deterministic CCF Encoding Requirements" in CCF specs: @@ -83,7 +83,9 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { // be identical to its zero-based index in composite-typedef." if !ccfID.Equal(newCCFTypeIDFromUint64(i)) { return nil, fmt.Errorf( - "encoded id is not the same as composite-typedef index", + "CCF type ID %d doesn't match composite-typedef index %d in composite-typedef", + ccfID, + i, ) } @@ -92,7 +94,9 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { // "Type definitions MUST be sorted by cadence-type-id in composite-typedef." if !stringsAreSortedBytewise(string(previousCadenceID), string(cadenceID)) { return nil, fmt.Errorf( - "encoded cadence type ID in type definition isn't sorted using bytewise lexicographic order", + "Cadence type ID (%s, %s) isn't sorted in composite-typedef", + string(previousCadenceID), + string(cadenceID), ) } @@ -104,7 +108,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { for id, raw := range rawFields { typ, ok := types[id] if !ok { - return nil, fmt.Errorf("failed to decode type definition field, type with ccf id %d not found", id) + return nil, fmt.Errorf("composite fields' CCF type ID %d not found in composite-typedef", id) } dec := NewDecoder(d.gauge, raw) @@ -116,8 +120,9 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { switch t := typ.(type) { case cadence.CompositeType: t.SetCompositeFields(fields) + default: - return nil, fmt.Errorf("found encoded fields in type definition for non-composite type %T", t) + return nil, fmt.Errorf("unsupported type %s (%T) in composite-typedef", t.ID(), t) } } @@ -277,10 +282,9 @@ func (d *Decoder) decodeTypeDef( return d.decodeInterfaceType(types, ctr) default: - return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf( - "failed to decode type definition: got tag number %d", - tagNum, - ) + return ccfTypeID(0), + cadenceTypeID(""), + fmt.Errorf("unsupported type definition with CBOR tag number %d", tagNum) } } @@ -320,7 +324,7 @@ func (d *Decoder) decodeCompositeType( // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." if _, ok := types[ccfID]; ok { - return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicated ccf type id %d in type definition", ccfID) + return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in composite-type", ccfID) } // element 1: cadence-type-id @@ -376,7 +380,7 @@ func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeType // "field-name MUST be unique in composite-type." // "name MUST be unique in composite-type-value.fields." if _, ok := fieldNames[field.Identifier]; ok { - return nil, fmt.Errorf("found duplicate field name in %s type definition", field.Identifier) + return nil, fmt.Errorf("found duplicate field name %s in composite-type", field.Identifier) } // "Deterministic CCF Encoding Requirements" in CCF specs: @@ -384,7 +388,7 @@ func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeType // "composite-type.fields MUST be sorted by name" // "composite-type-value.fields MUST be sorted by name." if !stringsAreSortedBytewise(previousFieldName, field.Identifier) { - return nil, fmt.Errorf("field names are not sorted in type definition") + return nil, fmt.Errorf("field names are not sorted in composite-type (%s, %s)", previousFieldName, field.Identifier) } fieldNames[field.Identifier] = struct{}{} @@ -455,7 +459,7 @@ func (d *Decoder) decodeInterfaceType( // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." if _, ok := types[ccfID]; ok { - return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicated ccf type id %d in type definition", ccfID) + return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in interface-type", ccfID) } // element 1: cadence-type-id diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index e6cebb6819..2bd5b35913 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -95,7 +95,7 @@ func (e *Encoder) Encode(value cadence.Value) (err error) { defer func() { // Recover panic error if there is any. if r := recover(); r != nil { - // don't recover Go errors + // Don't recover Go errors. goErr, ok := r.(goRuntime.Error) if ok { panic(goErr) @@ -1682,7 +1682,7 @@ func (e *Encoder) encodeFunctionTypeValue(typ *cadence.FunctionType, visited ccf return err } - //Encode function-value. + // Encode function-value. return e.encodeFunction(typ, visited) } From 8f5c69cf68131074ccc215d036fb611a6a67a50e Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:09:12 -0600 Subject: [PATCH 010/173] Panic if Cadence value has nil type in CCF encoder --- encoding/ccf/encode.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 2bd5b35913..1fc0301972 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -20,6 +20,7 @@ package ccf import ( "bytes" + "errors" "fmt" "io" goRuntime "runtime" @@ -335,6 +336,11 @@ func (e *Encoder) encodeValue( runtimeType := v.Type() + // CCF requires value to have non-nil type. + if runtimeType == nil { + panic(errors.New("value has nil type")) + } + if needToEncodeRuntimeType(staticType, runtimeType) { // Encode ccf-type-and-value-message. From 1556c4f129257f60126b8d90dd657333a178270e Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:21:46 -0600 Subject: [PATCH 011/173] Fix linter errors --- encoding/ccf/decode.go | 25 ++++++++++++++++--------- encoding/ccf/decode_typedef.go | 4 ++-- encoding/ccf/encode.go | 32 +++++++++++++++++++++++--------- encoding/ccf/encode_type.go | 4 +++- encoding/ccf/encode_typedef.go | 4 +++- encoding/ccf/sort.go | 6 +++--- encoding/ccf/traverse_value.go | 12 ++++++------ 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 09dcc7d6db..c2c826b7dd 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1619,44 +1619,51 @@ func (d *Decoder) decodeCompositeTypeValue( // ] // // ] -func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (compositeTypeValue, error) { +func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (*compositeTypeValue, error) { // Decode array of length 5 err := decodeCBORArrayWithKnownSize(d.dec, 5) if err != nil { - return compositeTypeValue{}, err + return nil, err } // element 0: id (used to lookup repeated or recursive types) ccfID, err := d.decodeCCFTypeID() if err != nil { - return compositeTypeValue{}, err + return nil, err } // element 1: cadence-type-id _, location, identifier, err := d.decodeCadenceTypeID() if err != nil { - return compositeTypeValue{}, err + return nil, err } // element 2: type (only used by enum type value) typ, err := d._decodeTypeValue(visited) if err != nil { - return compositeTypeValue{}, err + return nil, err } // element 3: fields rawField, err := d.dec.DecodeRawBytes() if err != nil { - return compositeTypeValue{}, err + return nil, err } // element 4: initializers initializerTypes, err := d.decodeInitializerTypeValues(visited) if err != nil { - return compositeTypeValue{}, err + return nil, err } - return compositeTypeValue{ccfID, location, identifier, typ, rawField, initializerTypes}, nil + return &compositeTypeValue{ + ccfID: ccfID, + location: location, + identifier: identifier, + typ: typ, + rawField: rawField, + initializerTypes: initializerTypes, + }, nil } // decodeInitializerTypeValues decodes composite initializers as @@ -1714,7 +1721,7 @@ func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]c common.UseMemory(d.gauge, common.MemoryUsage{ Kind: common.MemoryKindCadenceParameter, - Amount: uint64(count), + Amount: count, }) for i := 0; i < int(count); i++ { diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 3421da90e2..a91a5318cf 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -105,7 +105,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { } // Decode fields after all high-level type definitions are resolved. - for id, raw := range rawFields { + for id, raw := range rawFields { //nolint:maprange typ, ok := types[id] if !ok { return nil, fmt.Errorf("composite fields' CCF type ID %d not found in composite-typedef", id) @@ -366,7 +366,7 @@ func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeType common.UseMemory(d.gauge, common.MemoryUsage{ Kind: common.MemoryKindCadenceField, - Amount: uint64(fieldCount), + Amount: fieldCount, }) for i := 0; i < int(fieldCount); i++ { diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 1fc0301972..f49008e81b 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -243,7 +243,9 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy return err } - for _, typ := range types { + for i := 0; i < len(types); i++ { + typ := types[i] + switch x := typ.(type) { case cadence.CompositeType: // Encode struct-type, resource-type, contract-type, event-type, or enum-type. @@ -686,9 +688,9 @@ func (e *Encoder) encodeArray(v cadence.Array, tids ccfTypeIDByCadenceType) erro return err } - for _, value := range v.Values { + for i := 0; i < len(v.Values); i++ { // Encode element as value. - err = e.encodeValue(value, staticElementType, tids) + err = e.encodeValue(v.Values[i], staticElementType, tids) if err != nil { return err } @@ -719,7 +721,9 @@ func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadence return err } - for _, pair := range v.Pairs { + for i := 0; i < len(v.Pairs); i++ { + pair := v.Pairs[i] + // Encode dictionary key as value. err = e.encodeValue(pair.Key, staticKeyType, tids) if err != nil { @@ -791,7 +795,9 @@ func encodeAndSortKeyValuePairs( e := NewEncoder(buf) - for i, pair := range v.Pairs { + for i := 0; i < len(v.Pairs); i++ { + pair := v.Pairs[i] + off := buf.Len() // Encode dictionary key as value. @@ -902,7 +908,9 @@ func (e *Encoder) encodeComposite( sortedIndexes := getSortedFieldIndex(typ) - for _, index := range sortedIndexes { + for i := 0; i < len(sortedIndexes); i++ { + index := sortedIndexes[i] + // Encode sorted field as value. err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) if err != nil { @@ -1527,7 +1535,9 @@ func (e *Encoder) encodeFieldTypeValues(fieldTypes []cadence.Field, visited ccfT sort.Sort(sorter) // Encode sorted field types. - for _, index := range sorter.indexes { + for i := 0; i < len(sorter.indexes); i++ { + index := sorter.indexes[i] + err = e.encodeFieldTypeValue(fieldTypes[index], visited) if err != nil { return err @@ -1584,7 +1594,9 @@ func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Param } // Encode initializers. - for _, params := range initializerTypes { + for i := 0; i < len(initializerTypes); i++ { + params := initializerTypes[i] + err = e.encodeParameterTypeValues(params, visited) if err != nil { return err @@ -1628,7 +1640,9 @@ func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, sort.Sort(sorter) // Encode sorted parameter types. - for _, index := range sorter.indexes { + for i := 0; i < len(sorter.indexes); i++ { + index := sorter.indexes[i] + err = e.encodeParameterTypeValue(parameterTypes[index], visited) if err != nil { return err diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 5dc55a8df8..755a04be53 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -416,7 +416,9 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( sort.Sort(sorter) - for _, index := range sorter.indexes { + for i := 0; i < len(sorter.indexes); i++ { + index := sorter.indexes[i] + // Encode restriction type with given encodeTypeFn. err = encodeTypeFn(restrictions[index], tids) if err != nil { diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index b504dc33c6..5b0bc3c8f0 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -147,7 +147,9 @@ func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfT // "composite-type.fields MUST be sorted by name" sortedIndexes := getSortedFieldIndex(typ) - for _, index := range sortedIndexes { + for i := 0; i < len(sortedIndexes); i++ { + index := sortedIndexes[i] + // Encode field err = e.encodeCompositeTypeField(fieldTypes[index], tids) if err != nil { diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go index c75d6a764b..786a7fddaa 100644 --- a/encoding/ccf/sort.go +++ b/encoding/ccf/sort.go @@ -46,7 +46,7 @@ func newBytewiseFieldSorter(types []cadence.Field) bytewiseFieldSorter { for i := 0; i < len(indexes); i++ { indexes[i] = i } - return bytewiseFieldSorter{types, indexes} + return bytewiseFieldSorter{fields: types, indexes: indexes} } func (x bytewiseFieldSorter) Len() int { @@ -92,7 +92,7 @@ func newBytewiseParameterSorter(parameters []cadence.Parameter) bytewiseParamete for i := 0; i < len(indexes); i++ { indexes[i] = i } - return bytewiseParameterSorter{parameters, indexes} + return bytewiseParameterSorter{parameters: parameters, indexes: indexes} } func (x bytewiseParameterSorter) Len() int { @@ -175,7 +175,7 @@ func newBytewiseCadenceTypeSorter(types []cadence.Type) bytewiseCadenceTypeSorte for i := 0; i < len(indexes); i++ { indexes[i] = i } - return bytewiseCadenceTypeSorter{types, indexes} + return bytewiseCadenceTypeSorter{types: types, indexes: indexes} } func (t bytewiseCadenceTypeSorter) Len() int { diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 8b0b308248..17de9f60c9 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -54,8 +54,8 @@ func compositeTypesFromValue(v cadence.Value) ([]cadence.Type, ccfTypeIDByCadenc sort.Sort(bytewiseCadenceTypeInPlaceSorter(ct.types)) // Assign sorted array index as local ccf ID. - for i, t := range ct.types { - ct.ids[t.ID()] = ccfTypeID(i) + for i := 0; i < len(ct.types); i++ { + ct.ids[ct.types[i].ID()] = ccfTypeID(i) } return ct.types, ct.ids @@ -142,8 +142,8 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) case *cadence.RestrictedType: check := ct.traverseType(t.Type) - for _, restriction := range t.Restrictions { - checkRestriction := ct.traverseType(restriction) + for i := 0; i < len(t.Restrictions); i++ { + checkRestriction := ct.traverseType(t.Restrictions[i]) check = check || checkRestriction } return check @@ -154,8 +154,8 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) newType := ct.add(t) if newType { fields := t.CompositeFields() - for _, f := range fields { - checkField := ct.traverseType(f.Type) + for i := 0; i < len(fields); i++ { + checkField := ct.traverseType(fields[i].Type) check = check || checkField } From f911063ca19956976ac3d9f15bc5a35bfaa0cf91 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:25:13 -0600 Subject: [PATCH 012/173] Fix goimports linter errors --- encoding/ccf/bench_test.go | 3 ++- encoding/ccf/ccf_test.go | 5 +++-- encoding/ccf/decode.go | 1 + encoding/ccf/encode.go | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go index e9dfcd59ab..17453c1a69 100644 --- a/encoding/ccf/bench_test.go +++ b/encoding/ccf/bench_test.go @@ -21,10 +21,11 @@ package ccf_test import ( "testing" + "github.com/stretchr/testify/require" + "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/encoding/json" - "github.com/stretchr/testify/require" ) var encoded []byte diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 4332df715b..c55f8179b1 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -26,6 +26,9 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime" @@ -34,8 +37,6 @@ import ( "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/tests/checker" "github.com/onflow/cadence/runtime/tests/utils" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type encodeTest struct { diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index c2c826b7dd..7350fafb11 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -25,6 +25,7 @@ import ( goRuntime "runtime" "github.com/fxamacker/cbor/v2" + "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index f49008e81b..6b20818646 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -28,6 +28,7 @@ import ( "sync" "github.com/fxamacker/cbor/v2" + "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" ) From 82e1812f11a327cea381fb37e155726ee4a41f3c Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:05:36 -0500 Subject: [PATCH 013/173] Remove copyright years since it's in NOTICE --- encoding/ccf/bench_test.go | 2 +- encoding/ccf/cache.go | 2 +- encoding/ccf/ccf.go | 2 +- encoding/ccf/ccf_test.go | 2 +- encoding/ccf/ccf_type_id.go | 2 +- encoding/ccf/consts.go | 2 +- encoding/ccf/decode.go | 2 +- encoding/ccf/decode_type.go | 2 +- encoding/ccf/decode_typedef.go | 2 +- encoding/ccf/encode.go | 2 +- encoding/ccf/encode_type.go | 2 +- encoding/ccf/encode_typedef.go | 2 +- encoding/ccf/simple_type_utils.go | 2 +- encoding/ccf/sort.go | 2 +- encoding/ccf/traverse_value.go | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go index 17453c1a69..5279c4dfcb 100644 --- a/encoding/ccf/bench_test.go +++ b/encoding/ccf/bench_test.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/cache.go b/encoding/ccf/cache.go index 951b5207ec..dd209bda42 100644 --- a/encoding/ccf/cache.go +++ b/encoding/ccf/cache.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/ccf.go b/encoding/ccf/ccf.go index 660e80910b..2f0f54d177 100644 --- a/encoding/ccf/ccf.go +++ b/encoding/ccf/ccf.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index c55f8179b1..e95a1f39ba 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go index cecfbe3c7e..0caa3d73f6 100644 --- a/encoding/ccf/ccf_type_id.go +++ b/encoding/ccf/ccf_type_id.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go index 4a90425b41..0bc58ce7b8 100644 --- a/encoding/ccf/consts.go +++ b/encoding/ccf/consts.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 7350fafb11..e48f592d07 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 4d88edf2de..96ad3d6335 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index a91a5318cf..58c55cc58f 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 6b20818646..f0840f24df 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 755a04be53..9889a0024a 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 5b0bc3c8f0..9ae3724ae2 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/simple_type_utils.go b/encoding/ccf/simple_type_utils.go index bf7142c9c4..66531e94b3 100644 --- a/encoding/ccf/simple_type_utils.go +++ b/encoding/ccf/simple_type_utils.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go index 786a7fddaa..e1452fc5ad 100644 --- a/encoding/ccf/sort.go +++ b/encoding/ccf/sort.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 17de9f60c9..46785ccbc9 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -1,7 +1,7 @@ /* * Cadence - The resource-oriented smart contract programming language * - * Copyright 2022-2023 Dapper Labs, Inc. + * 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. From 2ce376a2b6267226e8ae337c5f0f22e5b7a18dc2 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:34:44 -0500 Subject: [PATCH 014/173] Replace for-loop with range-loop for readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/encode.go | 33 +++++++++----------------- encoding/ccf/encode_type.go | 4 +--- encoding/ccf/encode_typedef.go | 4 +--- encoding/ccf/traverse_value.go | 42 +++++++++++++++++----------------- 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index f0840f24df..135f373c45 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -244,8 +244,7 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy return err } - for i := 0; i < len(types); i++ { - typ := types[i] + for _, typ := range types { switch x := typ.(type) { case cadence.CompositeType: @@ -689,9 +688,9 @@ func (e *Encoder) encodeArray(v cadence.Array, tids ccfTypeIDByCadenceType) erro return err } - for i := 0; i < len(v.Values); i++ { + for _, element := range v.Values { // Encode element as value. - err = e.encodeValue(v.Values[i], staticElementType, tids) + err = e.encodeValue(element, staticElementType, tids) if err != nil { return err } @@ -722,8 +721,7 @@ func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadence return err } - for i := 0; i < len(v.Pairs); i++ { - pair := v.Pairs[i] + for _, pair := range v.Pairs { // Encode dictionary key as value. err = e.encodeValue(pair.Key, staticKeyType, tids) @@ -765,9 +763,9 @@ func (e *Encoder) encodeSortedDictionary(v cadence.Dictionary, tids ccfTypeIDByC return err } - for i := 0; i < len(sortedPairs); i++ { + for _, pair := range sortedPairs { // Encode key and value. - err = e.enc.EncodeRawBytes(sortedPairs[i].encodedPair) + err = e.enc.EncodeRawBytes(pair.encodedPair) if err != nil { return err } @@ -796,8 +794,7 @@ func encodeAndSortKeyValuePairs( e := NewEncoder(buf) - for i := 0; i < len(v.Pairs); i++ { - pair := v.Pairs[i] + for i, pair := range v.Pairs { off := buf.Len() @@ -909,9 +906,7 @@ func (e *Encoder) encodeComposite( sortedIndexes := getSortedFieldIndex(typ) - for i := 0; i < len(sortedIndexes); i++ { - index := sortedIndexes[i] - + for _, index := range sortedIndexes { // Encode sorted field as value. err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) if err != nil { @@ -1536,9 +1531,7 @@ func (e *Encoder) encodeFieldTypeValues(fieldTypes []cadence.Field, visited ccfT sort.Sort(sorter) // Encode sorted field types. - for i := 0; i < len(sorter.indexes); i++ { - index := sorter.indexes[i] - + for _, index := range sorter.indexes { err = e.encodeFieldTypeValue(fieldTypes[index], visited) if err != nil { return err @@ -1595,9 +1588,7 @@ func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Param } // Encode initializers. - for i := 0; i < len(initializerTypes); i++ { - params := initializerTypes[i] - + for _, params := range initializerTypes { err = e.encodeParameterTypeValues(params, visited) if err != nil { return err @@ -1641,9 +1632,7 @@ func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, sort.Sort(sorter) // Encode sorted parameter types. - for i := 0; i < len(sorter.indexes); i++ { - index := sorter.indexes[i] - + for _, index := range sorter.indexes { err = e.encodeParameterTypeValue(parameterTypes[index], visited) if err != nil { return err diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 9889a0024a..899786d45d 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -416,9 +416,7 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( sort.Sort(sorter) - for i := 0; i < len(sorter.indexes); i++ { - index := sorter.indexes[i] - + for _, index := range sorter.indexes { // Encode restriction type with given encodeTypeFn. err = encodeTypeFn(restrictions[index], tids) if err != nil { diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 9ae3724ae2..a8bd312864 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -147,9 +147,7 @@ func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfT // "composite-type.fields MUST be sorted by name" sortedIndexes := getSortedFieldIndex(typ) - for i := 0; i < len(sortedIndexes); i++ { - index := sortedIndexes[i] - + for _, index := range sortedIndexes { // Encode field err = e.encodeCompositeTypeField(fieldTypes[index], tids) if err != nil { diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 46785ccbc9..32ab8ebe84 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -54,8 +54,8 @@ func compositeTypesFromValue(v cadence.Value) ([]cadence.Type, ccfTypeIDByCadenc sort.Sort(bytewiseCadenceTypeInPlaceSorter(ct.types)) // Assign sorted array index as local ccf ID. - for i := 0; i < len(ct.types); i++ { - ct.ids[ct.types[i].ID()] = ccfTypeID(i) + for i, typ := range ct.types { + ct.ids[typ.ID()] = ccfTypeID(i) } return ct.types, ct.ids @@ -78,39 +78,39 @@ func (ct *compositeTypes) traverseValue(v cadence.Value) { ct.traverseValue(x.Value) case cadence.Array: - for i := 0; i < len(x.Values); i++ { - ct.traverseValue(x.Values[i]) + for _, element := range x.Values { + ct.traverseValue(element) } case cadence.Dictionary: - for i := 0; i < len(x.Pairs); i++ { - ct.traverseValue(x.Pairs[i].Key) - ct.traverseValue(x.Pairs[i].Value) + for _, pair := range x.Pairs { + ct.traverseValue(pair.Key) + ct.traverseValue(pair.Value) } case cadence.Struct: - for i := 0; i < len(x.Fields); i++ { - ct.traverseValue(x.Fields[i]) + for _, field := range x.Fields { + ct.traverseValue(field) } case cadence.Resource: - for i := 0; i < len(x.Fields); i++ { - ct.traverseValue(x.Fields[i]) + for _, field := range x.Fields { + ct.traverseValue(field) } case cadence.Event: - for i := 0; i < len(x.Fields); i++ { - ct.traverseValue(x.Fields[i]) + for _, field := range x.Fields { + ct.traverseValue(field) } case cadence.Contract: - for i := 0; i < len(x.Fields); i++ { - ct.traverseValue(x.Fields[i]) + for _, field := range x.Fields { + ct.traverseValue(field) } case cadence.Enum: - for i := 0; i < len(x.Fields); i++ { - ct.traverseValue(x.Fields[i]) + for _, field := range x.Fields { + ct.traverseValue(field) } } } @@ -142,8 +142,8 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) case *cadence.RestrictedType: check := ct.traverseType(t.Type) - for i := 0; i < len(t.Restrictions); i++ { - checkRestriction := ct.traverseType(t.Restrictions[i]) + for _, restriction := range t.Restrictions { + checkRestriction := ct.traverseType(restriction) check = check || checkRestriction } return check @@ -154,8 +154,8 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) newType := ct.add(t) if newType { fields := t.CompositeFields() - for i := 0; i < len(fields); i++ { - checkField := ct.traverseType(fields[i].Type) + for _, field := range fields { + checkField := ct.traverseType(field.Type) check = check || checkField } From f89eb0615b95c5bb5deeeed7966fa0d6fa98e9d0 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:38:33 -0500 Subject: [PATCH 015/173] Update code comments to match latest CCF Specs Updated Go comments containing CCF specs in CDDL notation. Specifically, changed CDDL notation representing "one or more" to "zero or more" for: - composite-value.fields - composite-type.fields - composite-type-value.fields See CCF specification PRs: - https://github.com/fxamacker/ccf_draft/pull/66 - https://github.com/fxamacker/ccf_draft/pull/67 --- encoding/ccf/decode.go | 14 +++++++------- encoding/ccf/decode_typedef.go | 2 +- encoding/ccf/encode.go | 14 +++++++------- encoding/ccf/encode_typedef.go | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index e48f592d07..4b1111ab2a 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -939,7 +939,7 @@ func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTyp // decodeComposite decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types cadenceTypeByCCFTypeID) ([]cadence.Value, error) { fieldCount := len(fieldTypes) @@ -970,7 +970,7 @@ func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types cadenceTypeB // decodeStruct decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeStruct(typ *cadence.StructType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { @@ -994,7 +994,7 @@ func (d *Decoder) decodeStruct(typ *cadence.StructType, types cadenceTypeByCCFTy // decodeResource decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeResource(typ *cadence.ResourceType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { @@ -1018,7 +1018,7 @@ func (d *Decoder) decodeResource(typ *cadence.ResourceType, types cadenceTypeByC // decodeEvent decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeEvent(typ *cadence.EventType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { @@ -1042,7 +1042,7 @@ func (d *Decoder) decodeEvent(typ *cadence.EventType, types cadenceTypeByCCFType // decodeContract decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeContract(typ *cadence.ContractType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { @@ -1066,7 +1066,7 @@ func (d *Decoder) decodeContract(typ *cadence.ContractType, types cadenceTypeByC // decodeEnum decodes encoded composite-value as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (d *Decoder) decodeEnum(typ *cadence.EnumType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { @@ -1604,7 +1604,7 @@ func (d *Decoder) decodeCompositeTypeValue( // ; type is only used by enum type value // type: nil / type-value, // fields: [ -// + [ +// * [ // name: tstr, // type: type-value // ] diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 58c55cc58f..fd3b12115d 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -295,7 +295,7 @@ func (d *Decoder) decodeTypeDef( // id: id, // cadence-type-id: cadence-type-id, // fields: [ -// + [ +// * [ // field-name: tstr, // field-type: inline-type // ] diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 135f373c45..87c26d53b7 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -841,42 +841,42 @@ func encodeAndSortKeyValuePairs( // encodeStruct encodes cadence.Struct as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeStruct(v cadence.Struct, tids ccfTypeIDByCadenceType) error { return e.encodeComposite(v.StructType, v.Fields, tids) } // encodeResource encodes cadence.Resource as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeResource(v cadence.Resource, tids ccfTypeIDByCadenceType) error { return e.encodeComposite(v.ResourceType, v.Fields, tids) } // encodeEvent encodes cadence.Event as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeEvent(v cadence.Event, tids ccfTypeIDByCadenceType) error { return e.encodeComposite(v.EventType, v.Fields, tids) } // encodeContract encodes cadence.Contract as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeContract(v cadence.Contract, tids ccfTypeIDByCadenceType) error { return e.encodeComposite(v.ContractType, v.Fields, tids) } // encodeEnum encodes cadence.Enum as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeEnum(v cadence.Enum, tids ccfTypeIDByCadenceType) error { return e.encodeComposite(v.EnumType, v.Fields, tids) } // encodeComposite encodes composite types as // language=CDDL -// composite-value = [+ (field: value)] +// composite-value = [* (field: value)] func (e *Encoder) encodeComposite( typ cadence.CompositeType, fields []cadence.Value, @@ -1423,7 +1423,7 @@ func (e *Encoder) encodeContractInterfaceTypeValue(typ *cadence.ContractInterfac // ; type is only used by enum type value // type: nil / type-value, // fields: [ -// + [ +// * [ // name: tstr, // type: type-value // ] diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index a8bd312864..89477940ae 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -56,7 +56,7 @@ import ( // id: id, // cadence-type-id: cadence-type-id, // fields: [ -// + [ +// * [ // field-name: tstr, // field-type: inline-type // ] From 42d865792c152b0af33991e4ad5d1b6adf796249 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:58:21 -0500 Subject: [PATCH 016/173] Update code comments to match latest CCF Specs Updated Go comments containing CCF specs in CDDL notation. Specifically, changed CDDL notation representing "one or more" to "zero or more" for: - restricted-type.restrictions - restricted-type-value.restrictions See CCF specification PRs: - https://github.com/fxamacker/ccf_draft/pull/66 - https://github.com/fxamacker/ccf_draft/pull/67 --- encoding/ccf/decode_type.go | 4 ++-- encoding/ccf/encode.go | 2 +- encoding/ccf/encode_type.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 96ad3d6335..cc251b81a3 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -482,7 +482,7 @@ func (d *Decoder) decodeReferenceType( // #6.143([ // cadence-type-id: cadence-type-id, // type: inline-type, -// restrictions: [+ inline-type] +// restrictions: [* inline-type] // ]) // // restricted-type-value = @@ -491,7 +491,7 @@ func (d *Decoder) decodeReferenceType( // #6.191([ // cadence-type-id: cadence-type-id, // type: type-value, -// restrictions: [+ type-value] +// restrictions: [* type-value] // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 87c26d53b7..be5f50978b 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1238,7 +1238,7 @@ func (e *Encoder) encodeReferenceTypeValue(typ *cadence.ReferenceType, visited c // #6.191([ // cadence-type-id: cadence-type-id, // type: type-value, -// restrictions: [+ type-value] +// restrictions: [* type-value] // ]) func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited ccfTypeIDByCadenceType) error { rawTagNum := []byte{0xd8, CBORTagRestrictedTypeValue} diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 899786d45d..121824dfdb 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -349,7 +349,7 @@ func (e *Encoder) encodeReferenceTypeWithRawTag( // #6.143([ // cadence-type-id: cadence-type-id, // type: inline-type, -// restrictions: [+ inline-type] +// restrictions: [* inline-type] // ]) func (e *Encoder) encodeRestrictedType(typ *cadence.RestrictedType, tids ccfTypeIDByCadenceType) error { rawTagNum := []byte{0xd8, CBORTagRestrictedType} From 266fb6a280996e376da1e56ac228bd006ee5c2b0 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:41:57 -0500 Subject: [PATCH 017/173] Check for "zero or more" sortable items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CCF specs were updated to allow "zero or more" sortable items rather than "one or more". Given this, update CCF codec to check for zero items before sorting. Relevant changes to CCF specification include: - https://github.com/fxamacker/ccf_draft/pull/66 - https://github.com/fxamacker/ccf_draft/pull/67 Co-authored-by: Bastian Müller --- encoding/ccf/decode_typedef.go | 2 +- encoding/ccf/encode.go | 110 +++++++++++++++++++-------------- encoding/ccf/encode_type.go | 40 +++++++----- encoding/ccf/encode_typedef.go | 36 ++++++----- 4 files changed, 109 insertions(+), 79 deletions(-) diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index fd3b12115d..3cd88f25ae 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -348,7 +348,7 @@ func (d *Decoder) decodeCompositeType( // language=CDDL // // fields: [ -// + [ +// * [ // field-name: tstr, // field-type: inline-type // ] diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index be5f50978b..3cca72e0ab 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -899,22 +899,28 @@ func (e *Encoder) encodeComposite( return err } - if len(fields) == 1 { + switch len(fields) { + case 0: + // Short-circuit if there is no field. + return nil + + case 1: // Avoid overhead of sorting if there is only one field. return e.encodeValue(fields[0], staticFieldTypes[0].Type, tids) - } - sortedIndexes := getSortedFieldIndex(typ) + default: + sortedIndexes := getSortedFieldIndex(typ) - for _, index := range sortedIndexes { - // Encode sorted field as value. - err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) - if err != nil { - return err + for _, index := range sortedIndexes { + // Encode sorted field as value. + err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) + if err != nil { + return err + } } - } - return nil + return nil + } } // encodePath encodes cadence.Path as @@ -1502,7 +1508,7 @@ func (e *Encoder) encodeCompositeTypeValue( // language=CDDL // // fields: [ -// + [ +// * [ // name: tstr, // type: type-value // ] @@ -1514,31 +1520,37 @@ func (e *Encoder) encodeFieldTypeValues(fieldTypes []cadence.Field, visited ccfT return err } - if len(fieldTypes) == 1 { - // Avoid overhead of sorting if there is only one field type + switch len(fieldTypes) { + case 0: + // Short-circuit if there is no field type. + return nil + + case 1: + // Avoid overhead of sorting if there is only one field type. return e.encodeFieldTypeValue(fieldTypes[0], visited) - } - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "composite-type-value.fields MUST be sorted by name." + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.fields MUST be sorted by name." - // NOTE: bytewiseFieldIdentifierSorter doesn't sort fieldTypes in place. - // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes - // index. - sorter := newBytewiseFieldSorter(fieldTypes) + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fieldTypes in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(fieldTypes) - sort.Sort(sorter) + sort.Sort(sorter) - // Encode sorted field types. - for _, index := range sorter.indexes { - err = e.encodeFieldTypeValue(fieldTypes[index], visited) - if err != nil { - return err + // Encode sorted field types. + for _, index := range sorter.indexes { + err = e.encodeFieldTypeValue(fieldTypes[index], visited) + if err != nil { + return err + } } - } - return nil + return nil + } } // encodeFieldTypeValue encodes one composite field type as @@ -1615,31 +1627,37 @@ func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, return err } - if len(parameterTypes) == 1 { + switch len(parameterTypes) { + case 0: + // Short-circuit if there is no parameter type. + return nil + + case 1: // Avoid overhead of sorting if there is only one parameter type. return e.encodeParameterTypeValue(parameterTypes[0], visited) - } - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "composite-type-value.initializers MUST be sorted by identifier." + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.initializers MUST be sorted by identifier." - // NOTE: bytewiseParmaeterSorter doesn't sort parameterTypes in place. - // bytewiseParmaeterSorter.indexes is used as sorted parameterTypes - // index. - sorter := newBytewiseParameterSorter(parameterTypes) + // NOTE: bytewiseParmaeterSorter doesn't sort parameterTypes in place. + // bytewiseParmaeterSorter.indexes is used as sorted parameterTypes + // index. + sorter := newBytewiseParameterSorter(parameterTypes) - sort.Sort(sorter) + sort.Sort(sorter) - // Encode sorted parameter types. - for _, index := range sorter.indexes { - err = e.encodeParameterTypeValue(parameterTypes[index], visited) - if err != nil { - return err + // Encode sorted parameter types. + for _, index := range sorter.indexes { + err = e.encodeParameterTypeValue(parameterTypes[index], visited) + if err != nil { + return err + } } - } - return nil + return nil + } } // encodeParameterTypeValue encodes composite initializer parameter as diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 121824dfdb..86a4150879 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -402,29 +402,35 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( return err } - if len(restrictions) == 1 { + switch len(restrictions) { + case 0: + // Short-circuit if there is no restriction. + return nil + + case 1: // Avoid overhead of sorting if there is only one restriction. // Encode restriction type with given encodeTypeFn. return encodeTypeFn(restrictions[0], tids) - } - - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" - // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." - sorter := newBytewiseCadenceTypeSorter(restrictions) - sort.Sort(sorter) - - for _, index := range sorter.indexes { - // Encode restriction type with given encodeTypeFn. - err = encodeTypeFn(restrictions[index], tids) - if err != nil { - return err + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" + // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." + sorter := newBytewiseCadenceTypeSorter(restrictions) + + sort.Sort(sorter) + + for _, index := range sorter.indexes { + // Encode restriction type with given encodeTypeFn. + err = encodeTypeFn(restrictions[index], tids) + if err != nil { + return err + } } - } - return nil + return nil + } } // encodeCapabilityType encodes cadence.CapabilityType as diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 89477940ae..cb73ed1059 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -123,7 +123,7 @@ func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDB // language=CDDL // // fields: [ -// + [ +// * [ // field-name: tstr, // field-type: inline-type // ] @@ -137,25 +137,31 @@ func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfT return err } - if len(fieldTypes) == 1 { + switch len(fieldTypes) { + case 0: + // Short-circuit if there is no field type. + return nil + + case 1: // Avoid overhead of sorting if there is only one field. return e.encodeCompositeTypeField(fieldTypes[0], tids) - } - - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "composite-type.fields MUST be sorted by name" - sortedIndexes := getSortedFieldIndex(typ) - for _, index := range sortedIndexes { - // Encode field - err = e.encodeCompositeTypeField(fieldTypes[index], tids) - if err != nil { - return err + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.fields MUST be sorted by name" + sortedIndexes := getSortedFieldIndex(typ) + + for _, index := range sortedIndexes { + // Encode field + err = e.encodeCompositeTypeField(fieldTypes[index], tids) + if err != nil { + return err + } } - } - return nil + return nil + } } // encodeCompositeTypeField encodes field type as From 4aa5f6b302b01140ec109f5c4eb5745137c1a2b4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 19:43:55 -0500 Subject: [PATCH 018/173] Refactor error handling and use UnexpectedError - Return Cadence UnexpectedError for implemention errors. - Rethrow Go runtime errors and Cadence internal errors in: - CCF Encoder.Encode() - CCF Decoder.Decode() --- encoding/ccf/decode.go | 21 +++++++++----------- encoding/ccf/encode.go | 35 ++++++++++++++++------------------ encoding/ccf/encode_type.go | 6 +++--- encoding/ccf/encode_typedef.go | 5 +++-- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 4b1111ab2a..4f84810018 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -28,7 +28,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" + cadenceErrors "github.com/onflow/cadence/runtime/errors" ) // CBORDecMode @@ -90,23 +90,20 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { defer func() { // Recover panic error if there is any. if r := recover(); r != nil { - // Don't recover Go errors. - goErr, ok := r.(goRuntime.Error) - if ok { - panic(goErr) - } - - panicErr, isError := r.(error) - if !isError { + // Don't recover Go errors, internal errors, or non-errors. + switch r := r.(type) { + case goRuntime.Error, cadenceErrors.InternalError: + panic(r) + case error: + err = r + default: panic(r) } - - err = panicErr } // Add context to error if there is any. if err != nil { - err = errors.NewDefaultUserError("ccf: failed to decode: %s", err) + err = cadenceErrors.NewDefaultUserError("ccf: failed to decode: %s", err) } }() diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 3cca72e0ab..eb08312fc5 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -20,7 +20,6 @@ package ccf import ( "bytes" - "errors" "fmt" "io" goRuntime "runtime" @@ -31,6 +30,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" + cadenceErrors "github.com/onflow/cadence/runtime/errors" ) // CBOREncMode @@ -97,25 +97,22 @@ func (e *Encoder) Encode(value cadence.Value) (err error) { defer func() { // Recover panic error if there is any. if r := recover(); r != nil { - // Don't recover Go errors. - goErr, ok := r.(goRuntime.Error) - if ok { - panic(goErr) - } - - panicErr, isError := r.(error) - if !isError { + // Don't recover Go errors, internal errors, or non-errors. + switch r := r.(type) { + case goRuntime.Error, cadenceErrors.InternalError: + panic(r) + case error: + err = r + default: panic(r) } - - err = panicErr } // Add context to error if there is any. if err != nil { err = fmt.Errorf( - "ccf: failed to encode value %q (type %q): %s", - value.String(), + "ccf: failed to encode value (type %T, %q): %s", + value, value.Type().ID(), err, ) @@ -262,7 +259,7 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy } default: - panic(fmt.Errorf("unexpected type %s in type definition", typ.ID())) + panic(cadenceErrors.NewUnexpectedError("unexpected type %s in type definition", typ.ID())) } } @@ -340,7 +337,7 @@ func (e *Encoder) encodeValue( // CCF requires value to have non-nil type. if runtimeType == nil { - panic(errors.New("value has nil type")) + panic(cadenceErrors.NewUnexpectedError("value (%T) has nil type", v)) } if needToEncodeRuntimeType(staticType, runtimeType) { @@ -479,7 +476,7 @@ func (e *Encoder) encodeValue( return e.encodeFunction(x.FunctionType, ccfTypeIDByCadenceType{}) default: - panic(fmt.Errorf("unsupported value %s (%T)", v, v)) + panic(cadenceErrors.NewUnexpectedError("cannot encode unsupported value (%T)", v)) } } @@ -831,7 +828,7 @@ func encodeAndSortKeyValuePairs( } if off != len(b) { // Sanity check - panic(fmt.Errorf("encoded dictionary pairs' offset %d doesn't match buffer length %d", off, len(b))) + panic(cadenceErrors.NewUnexpectedError("encoded dictionary pairs' offset %d doesn't match buffer length %d", off, len(b))) } sort.Sort(bytewiseKeyValuePairSorter(encodedPairs)) @@ -885,7 +882,7 @@ func (e *Encoder) encodeComposite( staticFieldTypes := typ.CompositeFields() if len(staticFieldTypes) != len(fields) { - panic(fmt.Errorf( + panic(cadenceErrors.NewUnexpectedError( "%s field count %d doesn't match declared field type count %d", typ.ID(), len(fields), @@ -1121,7 +1118,7 @@ func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceTy return e.encodeNilTypeValue() default: - panic(fmt.Errorf("unsupported type value %s (%T)", typ.ID(), typ)) + panic(cadenceErrors.NewUnexpectedError("unsupported type value %s (%T)", typ.ID(), typ)) } } diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 86a4150879..0d1d5020c9 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -19,10 +19,10 @@ package ccf import ( - "fmt" "sort" "github.com/onflow/cadence" + cadenceErrors "github.com/onflow/cadence/runtime/errors" ) type encodeTypeFn func(typ cadence.Type, tids ccfTypeIDByCadenceType) error @@ -65,7 +65,7 @@ func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType case cadence.CompositeType, cadence.InterfaceType: id, err := tids.id(typ) if err != nil { - panic(err) + panic(cadenceErrors.NewUnexpectedErrorFromCause(err)) } return e.encodeTypeRef(id) @@ -82,7 +82,7 @@ func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType return e.encodeSimpleType(TypeFunction) default: - panic(fmt.Errorf("unsupported type %s (%T)", typ.ID(), typ)) + panic(cadenceErrors.NewUnexpectedError("unsupported type %s (%T)", typ.ID(), typ)) } } diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index cb73ed1059..39b319c374 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/onflow/cadence" + cadenceErrors "github.com/onflow/cadence/runtime/errors" ) // encodeCompositeType encodes cadence.CompositeType in type definition as @@ -88,7 +89,7 @@ func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDB cborTagNum = CBORTagEnumType default: - panic(fmt.Errorf("unexpected composite type %s (%T)", t.ID(), t)) + panic(cadenceErrors.NewUnexpectedError("unexpected composite type %s (%T)", t.ID(), t)) } // Encode tag number indicating composite type. @@ -230,7 +231,7 @@ func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDB cborTagNum = CBORTagContractInterfaceType default: - panic(fmt.Errorf("unexpected interface type %s (%T)", t.ID(), t)) + panic(cadenceErrors.NewUnexpectedError("unexpected interface type %s (%T)", t.ID(), t)) } // Encode tag number indicating interface type. From bc220dce48e3811da284fd94fd47e5be21c810de Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:04:01 -0500 Subject: [PATCH 019/173] Improve CCF error messages --- encoding/ccf/decode.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 4f84810018..0061f34738 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -19,6 +19,7 @@ package ccf import ( + "errors" "fmt" "math" "math/big" @@ -473,7 +474,7 @@ func (d *Decoder) decodeInt8() (cadence.Value, error) { return nil, fmt.Errorf( "encoded int8-value %d is outside range of Int8 [%d, %d]", i, - math.MaxInt8, + math.MinInt8, math.MaxInt8, ) } @@ -570,10 +571,7 @@ func (d *Decoder) decodeUInt() (cadence.Value, error) { return nil, err } if bigInt.Sign() < 0 { - return nil, fmt.Errorf( - "encoded uint-value %s is negative", - bigInt.String(), - ) + return nil, errors.New("encoded uint-value is negative") } return cadence.NewMeteredUIntFromBig( d.gauge, @@ -660,10 +658,7 @@ func (d *Decoder) decodeUInt128() (cadence.Value, error) { return nil, err } if bigInt.Sign() < 0 { - return nil, fmt.Errorf( - "encoded uint128-value %s is negative", - bigInt.String(), - ) + return nil, errors.New("encoded uint128-value is negative") } return cadence.NewMeteredUInt128FromBig( d.gauge, @@ -682,10 +677,7 @@ func (d *Decoder) decodeUInt256() (cadence.Value, error) { return nil, err } if bigInt.Sign() < 0 { - return nil, fmt.Errorf( - "encoded uint256-value %s is negative", - bigInt.String(), - ) + return nil, errors.New("encoded uint256-value is negative") } return cadence.NewMeteredUInt256FromBig( d.gauge, From cae3f98820310ae8761975694a938f51fe155919 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:36:32 -0500 Subject: [PATCH 020/173] Add comments and instructions for updating CCF tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/consts.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go index 0bc58ce7b8..a19e5aa4b9 100644 --- a/encoding/ccf/consts.go +++ b/encoding/ccf/consts.go @@ -20,6 +20,21 @@ package ccf // CCF uses CBOR tag numbers 128-255, which are unassigned by [IANA] // (https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). +// +// !!! *WARNING* !!! +// +// CCF Codec *MUST* comply with CCF Specifications. Relevant changes +// must be in sync between codec and specifications. +// +// Only add new tag number by: +// - replacing existing placeholders (`_`) with new tag number +// +// Only remove tag number by: +// - replace existing tag number with a placeholder `_` +// +// DO *NOT* REPLACE EXISTING TAG NUMBERS! +// DO *NOT* ADD NEW TAG NUMBERS IN BETWEEN! +// DO *NOT* APPEND NEW TAG NUMBERS AT END! const ( // CBOR tag numbers (128-135) for root objects (131-135 are reserved) From ef5f6d95271ef9fbd10a86e7019690660944ae00 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:41:55 -0500 Subject: [PATCH 021/173] Reuse restricted type ID during CCF decoding Thanks @SupunS for the suggestion! --- encoding/ccf/decode_type.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index cc251b81a3..cf2da44780 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -534,23 +534,25 @@ func (d *Decoder) decodeRestrictedType( return nil, err } + restrictedTypeID := restrictedType.ID() + // "Valid CCF Encoding Requirements" in CCF specs: // // "Elements MUST be unique in restricted-type or restricted-type-value." - if _, ok := restrictionTypeIDs[restrictedType.ID()]; ok { - return nil, fmt.Errorf("found duplicate restricted type %s", restrictedType.ID()) + if _, ok := restrictionTypeIDs[restrictedTypeID]; ok { + return nil, fmt.Errorf("found duplicate restricted type %s", restrictedTypeID) } // "Deterministic CCF Encoding Requirements" in CCF specs: // // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." - if !stringsAreSortedBytewise(previousRestrictedTypeID, restrictedType.ID()) { - return nil, fmt.Errorf("restricted types are not sorted (%s, %s)", previousRestrictedTypeID, restrictedType.ID()) + if !stringsAreSortedBytewise(previousRestrictedTypeID, restrictedTypeID) { + return nil, fmt.Errorf("restricted types are not sorted (%s, %s)", previousRestrictedTypeID, restrictedTypeID) } - restrictionTypeIDs[restrictedType.ID()] = struct{}{} - previousRestrictedTypeID = restrictedType.ID() + restrictionTypeIDs[restrictedTypeID] = struct{}{} + previousRestrictedTypeID = restrictedTypeID restrictions[i] = restrictedType } From 8791232f56fcd97771f3d9a255b524e73624fc9f Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:48:18 -0500 Subject: [PATCH 022/173] Cleanup some comments and code from PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/bench_test.go | 4 ---- encoding/ccf/decode.go | 10 +++++----- encoding/ccf/encode.go | 4 ++-- encoding/ccf/encode_typedef.go | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go index 5279c4dfcb..37a81dc72f 100644 --- a/encoding/ccf/bench_test.go +++ b/encoding/ccf/bench_test.go @@ -138,8 +138,6 @@ func BenchmarkEncodeBatchEventsJSON(b *testing.B) { } } require.NoError(b, err) - - // fmt.Printf("Batch events encoded in JSON are %d bytes\n", encodedSize) } func BenchmarkDecodeBatchEventsJSON(b *testing.B) { @@ -183,8 +181,6 @@ func BenchmarkEncodeBatchEventsCCF(b *testing.B) { } } require.NoError(b, err) - - // fmt.Printf("Batch events encoded in CCF are %d bytes\n", encodedSize) } func BenchmarkDecodeBatchEventsCCF(b *testing.B) { diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 0061f34738..856df5c9ca 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -116,11 +116,11 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { switch tagNum { case CBORTagTypeDefAndValue: - // Decode ccf-type-and-value-message. + // Decode ccf-typedef-and-value-message. return d.decodeTypeDefAndValue() case CBORTagTypeAndValue: - // Decode ccf-typedef-and-value-message. + // Decode ccf-type-and-value-message. return d.decodeTypeAndValue(cadenceTypeByCCFTypeID{}) default: @@ -331,7 +331,7 @@ func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cad return d.decodeArray(typ, false, 0, types) case *cadence.ConstantSizedArrayType: - return d.decodeArray(typ, true, typ.Size, types) + return d.decodeArray(typ, true, uint64(typ.Size), types) case *cadence.DictionaryType: return d.decodeDictionary(typ, types) @@ -806,14 +806,14 @@ func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types cadenceTypeByC // decodeArray decodes encoded array-value as // language=CDDL // array-value = [* value] -func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint64, types cadenceTypeByCCFTypeID) (cadence.Value, error) { // Decode array length. n, err := d.dec.DecodeArrayHead() if err != nil { return nil, err } - if hasKnownSize && uint64(knownSize) != n { + if hasKnownSize && knownSize != n { return nil, fmt.Errorf( "encoded array-value has %d elements (expected %d elements)", n, diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index eb08312fc5..818fb4ec92 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -713,7 +713,7 @@ func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadence staticElementType := dictionaryType.ElementType // Encode array head with array size of 2 * number of pairs. - err := e.enc.EncodeArrayHead(uint64(len(v.Pairs) * 2)) + err := e.enc.EncodeArrayHead(uint64(len(v.Pairs)) * 2) if err != nil { return err } @@ -755,7 +755,7 @@ func (e *Encoder) encodeSortedDictionary(v cadence.Dictionary, tids ccfTypeIDByC } // Encode array head with 2 * number of pairs. - err = e.enc.EncodeArrayHead(uint64(len(v.Pairs) * 2)) + err = e.enc.EncodeArrayHead(uint64(len(v.Pairs)) * 2) if err != nil { return err } diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 39b319c374..1790485749 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -246,7 +246,7 @@ func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDB return err } - // element 0: CCf type ID + // element 0: CCF type ID err = e.encodeCCFTypeID(ccfID) if err != nil { return err From 588594e75d0d5a0632cf3d18d395041ceb171f91 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:20:39 -0500 Subject: [PATCH 023/173] Decode CCF (U)Int128 and (U)Int256 in metered funcs --- encoding/ccf/decode.go | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 856df5c9ca..bdc65e2652 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -534,13 +534,13 @@ func (d *Decoder) decodeInt64() (cadence.Value, error) { // language=CDDL // int128-value = bigint func (d *Decoder) decodeInt128() (cadence.Value, error) { - bigInt, err := d.dec.DecodeBigInt() - if err != nil { - return nil, err - } return cadence.NewMeteredInt128FromBig( d.gauge, func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode Int128: %s", err)) + } return bigInt }, ) @@ -550,13 +550,13 @@ func (d *Decoder) decodeInt128() (cadence.Value, error) { // language=CDDL // int256-value = bigint func (d *Decoder) decodeInt256() (cadence.Value, error) { - bigInt, err := d.dec.DecodeBigInt() - if err != nil { - return nil, err - } return cadence.NewMeteredInt256FromBig( d.gauge, func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode Int256: %s", err)) + } return bigInt }, ) @@ -653,16 +653,14 @@ func (d *Decoder) decodeUInt64() (cadence.Value, error) { // language=CDDL // uint128-value = bigint .ge 0 func (d *Decoder) decodeUInt128() (cadence.Value, error) { - bigInt, err := d.dec.DecodeBigInt() - if err != nil { - return nil, err - } - if bigInt.Sign() < 0 { - return nil, errors.New("encoded uint128-value is negative") - } + // NewMeteredUInt128FromBig checks if decoded big.Int is positive. return cadence.NewMeteredUInt128FromBig( d.gauge, func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode UInt128: %s", err)) + } return bigInt }, ) @@ -672,16 +670,14 @@ func (d *Decoder) decodeUInt128() (cadence.Value, error) { // language=CDDL // uint256-value = bigint .ge 0 func (d *Decoder) decodeUInt256() (cadence.Value, error) { - bigInt, err := d.dec.DecodeBigInt() - if err != nil { - return nil, err - } - if bigInt.Sign() < 0 { - return nil, errors.New("encoded uint256-value is negative") - } + // NewMeteredUInt256FromBig checks if decoded big.Int is positive. return cadence.NewMeteredUInt256FromBig( d.gauge, func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode UInt256: %s", err)) + } return bigInt }, ) From 2e6e4a4cd53ad48abd3ae5f12080656cceca4c9b Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:35:41 -0500 Subject: [PATCH 024/173] Decode CCF dictionary in metered function --- encoding/ccf/decode.go | 80 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index bdc65e2652..d056327df1 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -861,49 +861,51 @@ func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTyp ) } - // previousKeyRawBytes is used to determine if dictionary keys are sorted - var previousKeyRawBytes []byte - - pairCount := n / 2 - pairs := make([]cadence.KeyValuePair, pairCount) - for i := 0; i < int(pairCount); i++ { - // element i: key - - // Decode key as raw bytes to check that key pairs are sorted by key. - keyRawBytes, err := d.dec.DecodeRawBytes() - if err != nil { - return nil, err - } - - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "dict-value key-value pairs MUST be sorted by key." - if !bytesAreSortedBytewise(previousKeyRawBytes, keyRawBytes) { - return nil, fmt.Errorf("encoded dict-value keys are not sorted") - } - - previousKeyRawBytes = keyRawBytes - - // decode key from raw bytes - keyDecoder := NewDecoder(d.gauge, keyRawBytes) - key, err := keyDecoder.decodeValue(typ.KeyType, types) - if err != nil { - return nil, err - } - - // element i+1: value - element, err := d.decodeValue(typ.ElementType, types) - if err != nil { - return nil, err - } - - pairs[i] = cadence.NewMeteredKeyValuePair(d.gauge, key, element) - } + pairCount := int(n / 2) value, err := cadence.NewMeteredDictionary( d.gauge, - len(pairs), + pairCount, func() ([]cadence.KeyValuePair, error) { + pairs := make([]cadence.KeyValuePair, pairCount) + + // previousKeyRawBytes is used to determine if dictionary keys are sorted + var previousKeyRawBytes []byte + + for i := 0; i < pairCount; i++ { + // element i: key + + // Decode key as raw bytes to check that key pairs are sorted by key. + keyRawBytes, err := d.dec.DecodeRawBytes() + if err != nil { + return nil, err + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "dict-value key-value pairs MUST be sorted by key." + if !bytesAreSortedBytewise(previousKeyRawBytes, keyRawBytes) { + return nil, fmt.Errorf("encoded dict-value keys are not sorted") + } + + previousKeyRawBytes = keyRawBytes + + // decode key from raw bytes + keyDecoder := NewDecoder(d.gauge, keyRawBytes) + key, err := keyDecoder.decodeValue(typ.KeyType, types) + if err != nil { + return nil, err + } + + // element i+1: value + element, err := d.decodeValue(typ.ElementType, types) + if err != nil { + return nil, err + } + + pairs[i] = cadence.NewMeteredKeyValuePair(d.gauge, key, element) + } + // "Valid CCF Encoding Requirements" in CCF specs: // // "Keys MUST be unique in dict-value. Decoders are not always required to check From 9d172c996d5dc692ba465690a1744428a6806c3f Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 15 Mar 2023 20:48:31 -0500 Subject: [PATCH 025/173] Clarify comments about CCF sorting --- .golangci.yml | 30 +++++++++++++++--------------- encoding/ccf/sort.go | 9 ++++++--- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 1ddc6a338a..2deaa6caa3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,8 +8,8 @@ linters: - ineffassign - typecheck - misspell - - maprange - - unkeyed + #- maprange + #- unkeyed - unused - exportloopref - gocritic @@ -38,16 +38,16 @@ linters-settings: - exitAfterDefer goimports: local-prefixes: github.com/onflow/cadence - custom: - maprange: - path: tools/maprange/maprange.so - description: reports for-range statements over maps - original-url: github.com/onflow/cadence/tools/maprange - unkeyed: - path: tools/unkeyed/unkeyed.so - description: reports unkeyed composite literals - original-url: github.com/onflow/cadence/tools/unkeyed - constructorcheck: - path: tools/constructorcheck/constructorcheck.so - description: reports range statements over maps - original-url: github.com/onflow/cadence/tools/constructorcheck + #custom: + # maprange: + # path: tools/maprange/maprange.so + # description: reports for-range statements over maps + # original-url: github.com/onflow/cadence/tools/maprange + # unkeyed: + # path: tools/unkeyed/unkeyed.so + # description: reports unkeyed composite literals + # original-url: github.com/onflow/cadence/tools/unkeyed + # constructorcheck: + # path: tools/constructorcheck/constructorcheck.so + # description: reports range statements over maps + # original-url: github.com/onflow/cadence/tools/constructorcheck diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go index e1452fc5ad..226504c922 100644 --- a/encoding/ccf/sort.go +++ b/encoding/ccf/sort.go @@ -35,7 +35,8 @@ import ( // // process sorted field at fields[index] // } type bytewiseFieldSorter struct { - // NOTE: DON'T sort fields because it isn't a copy. + // NOTE: DON'T sort fields in place because it isn't a copy. + // Instead, sort indexes by field identifier. fields []cadence.Field // indexes represents sorted indexes of fields indexes []int @@ -81,7 +82,8 @@ func (x bytewiseFieldSorter) Less(i, j int) bool { // // process sorted field at parameters[index] // } type bytewiseParameterSorter struct { - // NOTE: DON'T sort parameters because it isn't a copy. + // NOTE: DON'T sort parameters in place because it isn't a copy. + // Instead, sort indexes by parameter identifier. parameters []cadence.Parameter // indexes represents sorted indexes of fields indexes []int @@ -164,7 +166,8 @@ func (t bytewiseCadenceTypeInPlaceSorter) Less(i, j int) bool { // bytewiseCadenceTypeSorter is used to sort Cadence types by Cadence type ID. type bytewiseCadenceTypeSorter struct { - // NOTE: DON'T sort fields because it isn't a copy. + // NOTE: DON'T sort types in place because it isn't a copy. + // Instead, sort indexes by Cadence type ID. types []cadence.Type // indexes represents sorted indexes of fields indexes []int From 36b52d7949f6ab2557f4132490d468b8478d6477 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 15 Mar 2023 21:43:35 -0500 Subject: [PATCH 026/173] Add comments for encodeTypeValue and encodeFunction Explained why tids isn't passed down and other related aspects. --- encoding/ccf/encode.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 818fb4ec92..1ae5a95f51 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -464,6 +464,16 @@ func (e *Encoder) encodeValue( return e.encodePath(x) case cadence.TypeValue: + // cadence.TypeValue is encoded as self-contained, without any + // reference to tids. So tids isn't passed to encodeTypeValue(). + // + // encodeTypeValue() receives a new ccfTypeIDByCadenceType to deduplicate + // composite type values within the same CCF type value encoding. + // For example, when a composite type appears more than once + // (recursive or repeated as nested type) within the same type value, + // it is only encoded once and is subsequently represented by its CCF ID. + // For type value encoding, CCF type ID is sequentially generated by + // traversal order. return e.encodeTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) case cadence.StorageCapability: @@ -473,6 +483,16 @@ func (e *Encoder) encodeValue( return e.encodeEnum(x, tids) case cadence.Function: + // cadence.Function is encoded as self-contained, without any + // reference to tids. So tids isn't passed to encodeFunction(). + // + // encodeFunction() receives a new ccfTypeIDByCadenceType to deduplicate + // composite type values within the same CCF function value encoding. + // For example, when a composite type appears more than once + // (recursive or repeated as nested type) within the same function value, + // it is only encoded once and is subsequently represented by its CCF ID. + // For function value encoding, CCF type ID is sequentially generated by + // traversal order of sorted parameters and return type. return e.encodeFunction(x.FunctionType, ccfTypeIDByCadenceType{}) default: From 705fdd57ffb42d9f68fe3904e2fbc0aace362751 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 15 Mar 2023 22:07:23 -0500 Subject: [PATCH 027/173] Simplify CCF encodeOptional() --- encoding/ccf/encode.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 1ae5a95f51..80ca4db4e4 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -511,18 +511,13 @@ func (e *Encoder) encodeVoid(v cadence.Void) error { // language=CDDL // optional-value = nil / value func (e *Encoder) encodeOptional(v cadence.Optional, tids ccfTypeIDByCadenceType) error { - switch innerValue := v.Value.(type) { - case cadence.Optional: - return e.encodeOptional(innerValue, tids) - - case nil: + innerValue := v.Value + if innerValue == nil { return e.enc.EncodeNil() - - default: - // Use innerValue.Type() as static type to avoid encoding type - // because OptionalType is already encoded. - return e.encodeValue(innerValue, innerValue.Type(), tids) } + // Use innerValue.Type() as static type to avoid encoding type + // because OptionalType is already encoded. + return e.encodeValue(innerValue, innerValue.Type(), tids) } // encodeBool encodes cadence.Bool as From 852c52749cd03693cb4854f6feafde7d4afa3aae Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:00:35 -0500 Subject: [PATCH 028/173] Revert accidental changes to .golangci.yml --- .golangci.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2deaa6caa3..1ddc6a338a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,8 +8,8 @@ linters: - ineffassign - typecheck - misspell - #- maprange - #- unkeyed + - maprange + - unkeyed - unused - exportloopref - gocritic @@ -38,16 +38,16 @@ linters-settings: - exitAfterDefer goimports: local-prefixes: github.com/onflow/cadence - #custom: - # maprange: - # path: tools/maprange/maprange.so - # description: reports for-range statements over maps - # original-url: github.com/onflow/cadence/tools/maprange - # unkeyed: - # path: tools/unkeyed/unkeyed.so - # description: reports unkeyed composite literals - # original-url: github.com/onflow/cadence/tools/unkeyed - # constructorcheck: - # path: tools/constructorcheck/constructorcheck.so - # description: reports range statements over maps - # original-url: github.com/onflow/cadence/tools/constructorcheck + custom: + maprange: + path: tools/maprange/maprange.so + description: reports for-range statements over maps + original-url: github.com/onflow/cadence/tools/maprange + unkeyed: + path: tools/unkeyed/unkeyed.so + description: reports unkeyed composite literals + original-url: github.com/onflow/cadence/tools/unkeyed + constructorcheck: + path: tools/constructorcheck/constructorcheck.so + description: reports range statements over maps + original-url: github.com/onflow/cadence/tools/constructorcheck From cd55481beedde1b8da789e485fad97adc8dd1f88 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:19:32 -0500 Subject: [PATCH 029/173] Move sorted field cache from global to CCF encoder --- encoding/ccf/cache.go | 45 ---------------------------------- encoding/ccf/encode.go | 27 ++++++++++++++++++-- encoding/ccf/encode_typedef.go | 2 +- 3 files changed, 26 insertions(+), 48 deletions(-) delete mode 100644 encoding/ccf/cache.go diff --git a/encoding/ccf/cache.go b/encoding/ccf/cache.go deleted file mode 100644 index dd209bda42..0000000000 --- a/encoding/ccf/cache.go +++ /dev/null @@ -1,45 +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 ccf - -import ( - "sort" - "sync" - - "github.com/onflow/cadence" -) - -// cachedSortedFieldIndex contains sorted field index of Cadence composite types. -var cachedSortedFieldIndex sync.Map // key: cadence.CompositeType, value: []int - -func getSortedFieldIndex(t cadence.CompositeType) []int { - if v, _ := cachedSortedFieldIndex.Load(t); v != nil { - return v.([]int) - } - - // NOTE: bytewiseFieldIdentifierSorter doesn't sort fields in place. - // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes - // index. - sorter := newBytewiseFieldSorter(t.CompositeFields()) - - sort.Sort(sorter) - - cachedSortedFieldIndex.Store(t, sorter.indexes) - return sorter.indexes -} diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 80ca4db4e4..eda685ba2a 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -51,6 +51,8 @@ var CBOREncMode = func() cbor.EncMode { type Encoder struct { // CCF codec uses CBOR codec under the hood. enc *cbor.StreamEncoder + // cachedSortedFieldIndex contains sorted field index of Cadence composite types. + cachedSortedFieldIndex map[string][]int // key: composite type ID, value: sorted field indexes } // Encode returns the CCF-encoded representation of the given value. @@ -84,7 +86,10 @@ func MustEncode(value cadence.Value) []byte { // given io.Writer. func NewEncoder(w io.Writer) *Encoder { // CCF codec uses CBOR codec under the hood. - return &Encoder{enc: CBOREncMode.NewStreamEncoder(w)} + return &Encoder{ + enc: CBOREncMode.NewStreamEncoder(w), + cachedSortedFieldIndex: make(map[string][]int), + } } // Encode writes the CCF-encoded representation of the given value to this @@ -921,7 +926,7 @@ func (e *Encoder) encodeComposite( return e.encodeValue(fields[0], staticFieldTypes[0].Type, tids) default: - sortedIndexes := getSortedFieldIndex(typ) + sortedIndexes := e.getSortedFieldIndex(typ) for _, index := range sortedIndexes { // Encode sorted field as value. @@ -1783,3 +1788,21 @@ func putBuffer(e *bytes.Buffer) { e.Reset() bufferPool.Put(e) } + +func (e *Encoder) getSortedFieldIndex(t cadence.CompositeType) []int { + cadenceTypeID := t.ID() + + if indexes, ok := e.cachedSortedFieldIndex[cadenceTypeID]; ok { + return indexes + } + + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fields in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(t.CompositeFields()) + + sort.Sort(sorter) + + e.cachedSortedFieldIndex[cadenceTypeID] = sorter.indexes + return sorter.indexes +} diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 1790485749..075676117b 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -151,7 +151,7 @@ func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfT // "Deterministic CCF Encoding Requirements" in CCF specs: // // "composite-type.fields MUST be sorted by name" - sortedIndexes := getSortedFieldIndex(typ) + sortedIndexes := e.getSortedFieldIndex(typ) for _, index := range sortedIndexes { // Encode field From 33505965e31b3eb97a7eef705db64b3b9e0b7ebb Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 16 Mar 2023 19:02:20 -0500 Subject: [PATCH 030/173] Add CCF type ID related tests --- encoding/ccf/ccf_type_id_test.go | 137 +++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 encoding/ccf/ccf_type_id_test.go diff --git a/encoding/ccf/ccf_type_id_test.go b/encoding/ccf/ccf_type_id_test.go new file mode 100644 index 0000000000..1b3ef0db4e --- /dev/null +++ b/encoding/ccf/ccf_type_id_test.go @@ -0,0 +1,137 @@ +/* + * 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 ccf + +import ( + "math" + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/tests/utils" + "github.com/stretchr/testify/require" +) + +func TestCCFTypeID(t *testing.T) { + testCases := []struct { + name string + input uint64 + encodedBytes []byte + }{ + {name: "min", input: 0, encodedBytes: []byte{}}, + {name: "42", input: 42, encodedBytes: []byte{0x2a}}, + {name: "256", input: 256, encodedBytes: []byte{0x01, 0x00}}, + {name: "max", input: math.MaxUint64, encodedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create CCF type ID from uint64. + ccfID := newCCFTypeIDFromUint64(tc.input) + + // Encode CCF type ID to bytes. + encodedBytes := ccfID.Bytes() + require.Equal(t, tc.encodedBytes, encodedBytes) + + // Decode CCF type ID from bytes. + decodedCCFID := newCCFTypeID(encodedBytes) + require.Equal(t, ccfTypeID(tc.input), decodedCCFID) + + // Compare decoded CCF type ID with original. + require.True(t, ccfID.Equal(decodedCCFID)) + + // Compare modified CCF type ID with original. + if len(encodedBytes) == 0 { + encodedBytes = []byte{0x00} + } + encodedBytes[0] = ^encodedBytes[0] + require.False(t, ccfID.Equal(newCCFTypeID(encodedBytes))) + }) + } +} + +func TestCCFTypeIDByCadenceType(t *testing.T) { + // Create ccfTypeIDByCadenceType map + ccfIDs := make(ccfTypeIDByCadenceType) + + // Lookup non-existent CCF type ID. + id, err := ccfIDs.id(simpleStructType()) + require.Error(t, err) + + // Add entry. + ccfID := newCCFTypeIDFromUint64(1) + ccfIDs[simpleStructType().ID()] = ccfID + + // Lookup existing CCF type ID. + id, err = ccfIDs.id(simpleStructType()) + require.Equal(t, ccfID, id) + require.NoError(t, err) +} + +func TestCadenceTypeByCCFTypeID(t *testing.T) { + // Create cadenceTypeByCCFTypeID map + cadenceTypes := make(cadenceTypeByCCFTypeID) + + // Add new entry. + newType := cadenceTypes.add(newCCFTypeIDFromUint64(0), simpleStructType()) + require.True(t, newType) + + // Add entry with duplicate CCF type ID. + newType = cadenceTypes.add(newCCFTypeIDFromUint64(0), simpleStructType2()) + require.False(t, newType) + + // Lookup existing cadence type. + typ, err := cadenceTypes.typ(newCCFTypeIDFromUint64(0)) + require.True(t, typ.Equal(simpleStructType())) + require.False(t, typ.Equal(simpleStructType2())) + require.NoError(t, err) + + // Lookup non-existent cadence type. + typ, err = cadenceTypes.typ(newCCFTypeIDFromUint64(1)) + require.Nil(t, typ) + require.Error(t, err) +} + +func simpleStructType() *cadence.StructType { + return &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } +} + +func simpleStructType2() *cadence.StructType { + return &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct2", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } +} From b20b4b1c615ae8af4f0084f371720ce190cab2c7 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:24:02 -0500 Subject: [PATCH 031/173] Abbreviate comments to use json-cdc --- encoding/ccf/ccf_test.go | 302 +++++++++++++++++++-------------------- 1 file changed, 151 insertions(+), 151 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index e95a1f39ba..6b4307a14d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -52,7 +52,7 @@ func TestEncodeVoid(t *testing.T) { testEncodeAndDecode( t, cadence.NewVoid(), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Void"} // // language=edn, format=ccf @@ -81,7 +81,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(nil)", cadence.NewOptional(nil), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Optional","value":null} // // language=edn, format=ccf @@ -105,7 +105,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(non-nil)", cadence.NewOptional(cadence.NewInt(42)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Optional","value":{"type":"Int","value":"42"}} // // language=edn, format=ccf @@ -133,7 +133,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(Optional(nil))", cadence.NewOptional(cadence.NewOptional(nil)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":{"value":null,"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -159,7 +159,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(Optional(non-nil))", cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -189,7 +189,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(Optional(Optional(nil)))", cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -217,7 +217,7 @@ func TestEncodeOptional(t *testing.T) { { "Optional(Optional(Optional(non-nil)))", cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -257,7 +257,7 @@ func TestEncodeBool(t *testing.T) { { "True", cadence.NewBool(true), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Bool","value":true} // // language=edn, format=ccf @@ -279,7 +279,7 @@ func TestEncodeBool(t *testing.T) { { "False", cadence.NewBool(false), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Bool","value":false} // // language=edn, format=ccf @@ -312,7 +312,7 @@ func TestEncodeCharacter(t *testing.T) { { "a", a, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Character","value":"a"} // // language=edn, format=ccf @@ -336,7 +336,7 @@ func TestEncodeCharacter(t *testing.T) { { "b", b, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Character","value":"b"} // // language=edn, format=ccf @@ -368,7 +368,7 @@ func TestEncodeString(t *testing.T) { { "Empty", cadence.String(""), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"String","value":""} // // language=edn, format=ccf @@ -390,7 +390,7 @@ func TestEncodeString(t *testing.T) { { "Non-empty", cadence.String("foo"), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"String","value":"foo"} // // language=edn, format=ccf @@ -421,7 +421,7 @@ func TestEncodeAddress(t *testing.T) { testEncodeAndDecode( t, cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Address","value":"0x0000000102030405"} // // language=edn, format=ccf @@ -452,7 +452,7 @@ func TestEncodeInt(t *testing.T) { { "Negative", cadence.NewInt(-42), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int","value":"-42"} // // language=edn, format=ccf @@ -478,7 +478,7 @@ func TestEncodeInt(t *testing.T) { { "Zero", cadence.NewInt(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int","value":"0"} // // language=edn, format=ccf @@ -502,7 +502,7 @@ func TestEncodeInt(t *testing.T) { { "Positive", cadence.NewInt(42), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int","value":"42"} // // language=edn, format=ccf @@ -528,7 +528,7 @@ func TestEncodeInt(t *testing.T) { { "SmallerThanMinInt256", cadence.NewIntFromBig(new(big.Int).Sub(sema.Int256TypeMinIntBig, big.NewInt(10))), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819978"} // // language=edn, format=ccf @@ -557,7 +557,7 @@ func TestEncodeInt(t *testing.T) { { "LargerThanMaxUInt256", cadence.NewIntFromBig(new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} // // language=edn, format=ccf @@ -595,7 +595,7 @@ func TestEncodeInt8(t *testing.T) { { "Min", cadence.NewInt8(math.MinInt8), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"-128"} // // language=edn, format=ccf @@ -617,7 +617,7 @@ func TestEncodeInt8(t *testing.T) { { "Zero", cadence.NewInt8(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"0"} // // language=edn, format=ccf @@ -639,7 +639,7 @@ func TestEncodeInt8(t *testing.T) { { "Max", cadence.NewInt8(math.MaxInt8), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"127"} // // language=edn, format=ccf @@ -669,7 +669,7 @@ func TestEncodeInt16(t *testing.T) { { "Min", cadence.NewInt16(math.MinInt16), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"-32768"} // // language=edn, format=ccf @@ -691,7 +691,7 @@ func TestEncodeInt16(t *testing.T) { { "Zero", cadence.NewInt16(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"0"} // // language=edn, format=ccf @@ -713,7 +713,7 @@ func TestEncodeInt16(t *testing.T) { { "Max", cadence.NewInt16(math.MaxInt16), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"32767"} // // language=edn, format=ccf @@ -743,7 +743,7 @@ func TestEncodeInt32(t *testing.T) { { "Min", cadence.NewInt32(math.MinInt32), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"-2147483648"} // // language=edn, format=ccf @@ -765,7 +765,7 @@ func TestEncodeInt32(t *testing.T) { { "Zero", cadence.NewInt32(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"0"} // // language=edn, format=ccf @@ -787,7 +787,7 @@ func TestEncodeInt32(t *testing.T) { { "Max", cadence.NewInt32(math.MaxInt32), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"2147483647"} // // language=edn, format=ccf @@ -817,7 +817,7 @@ func TestEncodeInt64(t *testing.T) { { "Min", cadence.NewInt64(math.MinInt64), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"-9223372036854775808"} // // language=edn, format=ccf @@ -839,7 +839,7 @@ func TestEncodeInt64(t *testing.T) { { "Zero", cadence.NewInt64(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"0"} // // language=edn, format=ccf @@ -861,7 +861,7 @@ func TestEncodeInt64(t *testing.T) { { "Max", cadence.NewInt64(math.MaxInt64), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"9223372036854775807"} // // language=edn, format=ccf @@ -891,7 +891,7 @@ func TestEncodeInt128(t *testing.T) { { "Min", cadence.Int128{Value: sema.Int128TypeMinIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"-170141183460469231731687303715884105728"} // // language=edn, format=ccf @@ -918,7 +918,7 @@ func TestEncodeInt128(t *testing.T) { { "Zero", cadence.NewInt128(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"0"} // // language=edn, format=ccf @@ -942,7 +942,7 @@ func TestEncodeInt128(t *testing.T) { { "Max", cadence.Int128{Value: sema.Int128TypeMaxIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"170141183460469231731687303715884105727"} // // language=edn, format=ccf @@ -977,7 +977,7 @@ func TestEncodeInt256(t *testing.T) { { "Min", cadence.Int256{Value: sema.Int256TypeMinIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819968"} // // language=edn, format=ccf @@ -1006,7 +1006,7 @@ func TestEncodeInt256(t *testing.T) { { "Zero", cadence.NewInt256(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"0"} // // language=edn, format=ccf @@ -1030,7 +1030,7 @@ func TestEncodeInt256(t *testing.T) { { "Max", cadence.Int256{Value: sema.Int256TypeMaxIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"57896044618658097711785492504343953926634992332820282019728792003956564819967"} // // language=edn, format=ccf @@ -1067,7 +1067,7 @@ func TestEncodeUInt(t *testing.T) { { "Zero", cadence.NewUInt(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"0"} // // language=edn, format=ccf @@ -1091,7 +1091,7 @@ func TestEncodeUInt(t *testing.T) { { "Positive", cadence.NewUInt(42), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"42"} // // language=edn, format=ccf @@ -1117,7 +1117,7 @@ func TestEncodeUInt(t *testing.T) { { "LargerThanMaxUInt256", cadence.UInt{Value: new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} // // language=edn, format=ccf @@ -1155,7 +1155,7 @@ func TestEncodeUInt8(t *testing.T) { { "Zero", cadence.NewUInt8(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt8","value":"0"} // // language=edn, format=ccf @@ -1177,7 +1177,7 @@ func TestEncodeUInt8(t *testing.T) { { "Max", cadence.NewUInt8(math.MaxUint8), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt8","value":"255"} // // language=edn, format=ccf @@ -1207,7 +1207,7 @@ func TestEncodeUInt16(t *testing.T) { { "Zero", cadence.NewUInt16(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt16","value":"0"} // // language=edn, format=ccf @@ -1229,7 +1229,7 @@ func TestEncodeUInt16(t *testing.T) { { "Max", cadence.NewUInt16(math.MaxUint16), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt16","value":"65535"} // // language=edn, format=ccf @@ -1259,7 +1259,7 @@ func TestEncodeUInt32(t *testing.T) { { "Zero", cadence.NewUInt32(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt32","value":"0"} // // language=edn, format=ccf @@ -1281,7 +1281,7 @@ func TestEncodeUInt32(t *testing.T) { { "Max", cadence.NewUInt32(math.MaxUint32), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt32","value":"4294967295"} // // language=edn, format=ccf @@ -1311,7 +1311,7 @@ func TestEncodeUInt64(t *testing.T) { { "Zero", cadence.NewUInt64(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt64","value":"0"} // // language=edn, format=ccf @@ -1333,7 +1333,7 @@ func TestEncodeUInt64(t *testing.T) { { "Max", cadence.NewUInt64(uint64(math.MaxUint64)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt64","value":"18446744073709551615"} // // language=edn, format=ccf @@ -1363,7 +1363,7 @@ func TestEncodeUInt128(t *testing.T) { { "Zero", cadence.NewUInt128(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt128","value":"0"} // // language=edn, format=ccf @@ -1387,7 +1387,7 @@ func TestEncodeUInt128(t *testing.T) { { "Max", cadence.UInt128{Value: sema.UInt128TypeMaxIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt128","value":"340282366920938463463374607431768211455"} // // language=edn, format=ccf @@ -1422,7 +1422,7 @@ func TestEncodeUInt256(t *testing.T) { { "Zero", cadence.NewUInt256(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt256","value":"0"} // // language=edn, format=ccf @@ -1446,7 +1446,7 @@ func TestEncodeUInt256(t *testing.T) { { "Max", cadence.UInt256{Value: sema.UInt256TypeMaxIntBig}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UInt256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"} // // language=edn, format=ccf @@ -1483,7 +1483,7 @@ func TestEncodeWord8(t *testing.T) { { "Zero", cadence.NewWord8(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word8","value":"0"} // // language=edn, format=ccf @@ -1505,7 +1505,7 @@ func TestEncodeWord8(t *testing.T) { { "Max", cadence.NewWord8(math.MaxUint8), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word8","value":"255"} // // language=edn, format=ccf @@ -1535,7 +1535,7 @@ func TestEncodeWord16(t *testing.T) { { "Zero", cadence.NewWord16(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word16","value":"0"} // // language=edn, format=ccf @@ -1557,7 +1557,7 @@ func TestEncodeWord16(t *testing.T) { { "Max", cadence.NewWord16(math.MaxUint16), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word16","value":"65535"} // // language=edn, format=ccf @@ -1587,7 +1587,7 @@ func TestEncodeWord32(t *testing.T) { { "Zero", cadence.NewWord32(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word32","value":"0"} // // language=edn, format=ccf @@ -1609,7 +1609,7 @@ func TestEncodeWord32(t *testing.T) { { "Max", cadence.NewWord32(math.MaxUint32), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word32","value":"4294967295"} // // language=edn, format=ccf @@ -1639,7 +1639,7 @@ func TestEncodeWord64(t *testing.T) { { "Zero", cadence.NewWord64(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word64","value":"0"} // // language=edn, format=ccf @@ -1661,7 +1661,7 @@ func TestEncodeWord64(t *testing.T) { { "Max", cadence.NewWord64(math.MaxUint64), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Word64","value":"18446744073709551615"} // // language=edn, format=ccf @@ -1691,7 +1691,7 @@ func TestEncodeFix64(t *testing.T) { { "Zero", cadence.Fix64(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"0.00000000"} // // language=edn, format=ccf @@ -1713,7 +1713,7 @@ func TestEncodeFix64(t *testing.T) { { "789.00123010", cadence.Fix64(78_900_123_010), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"789.00123010"} // // language=edn, format=ccf @@ -1735,7 +1735,7 @@ func TestEncodeFix64(t *testing.T) { { "1234.056", cadence.Fix64(123_405_600_000), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"1234.05600000"} // // language=edn, format=ccf @@ -1757,7 +1757,7 @@ func TestEncodeFix64(t *testing.T) { { "-12345.006789", cadence.Fix64(-1_234_500_678_900), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"-12345.00678900"} // // language=edn, format=ccf @@ -1787,7 +1787,7 @@ func TestEncodeUFix64(t *testing.T) { { "Zero", cadence.UFix64(0), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"0.00000000"} // // language=edn, format=ccf @@ -1809,7 +1809,7 @@ func TestEncodeUFix64(t *testing.T) { { "789.00123010", cadence.UFix64(78_900_123_010), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"789.00123010"} // // language=edn, format=ccf @@ -1831,7 +1831,7 @@ func TestEncodeUFix64(t *testing.T) { { "1234.056", cadence.UFix64(123_405_600_000), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"1234.05600000"} // // language=edn, format=ccf @@ -1863,7 +1863,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewArray( []cadence.Value{}, ).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Array","value":[]} // // language=edn, format=ccf @@ -1895,7 +1895,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(2), cadence.NewInt(3), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} // // language=edn, format=ccf @@ -1951,7 +1951,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(3), }).WithType(fooResourceType), }).WithType(cadence.NewVariableSizedArrayType(fooResourceType)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}]} // // language=edn, format=ccf @@ -2054,7 +2054,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewBool(true), }).WithType(foooResourceTypeWithAbstractField), }).WithType(cadence.NewVariableSizedArrayType(foooResourceTypeWithAbstractField)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}},{"name":"baz","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}},{"name":"baz","value":{"type":"String","value":"a"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}},{"name":"baz","value":{"type":"Bool","value":true}}]}}]} // // language=edn, format=ccf @@ -2196,7 +2196,7 @@ func TestEncodeArray(t *testing.T) { s, cadence.NewBool(true), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"String","value":"a"},{"type":"Bool","value":true}]} // // language=edn, format=ccf @@ -2267,7 +2267,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt16(2), cadence.NewInt32(3), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewNumberType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":[{"value":"1","type":"Int8"},{"value":"2","type":"Int16"},{"value":"3","type":"Int32"}],"type":"Array"} // // language=edn, format=ccf @@ -2333,7 +2333,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(1), }).WithType(fooResourceType), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":[{"value":"1","type":"Int"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"}],"type":"Array"} // // language=edn, format=ccf @@ -2444,7 +2444,7 @@ func TestEncodeArray(t *testing.T) { s, }).WithType(foooResourceTypeWithAbstractField), }).WithType(cadence.NewVariableSizedArrayType(resourceInterfaceType)), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},{"value":{"id":"S.test.Fooo","fields":[{"value":{"value":"2","type":"Int"},"name":"bar"},{"value":{"value":"a","type":"String"},"name":"baz"}]},"type":"Resource"}],"type":"Array"} // // language=edn, format=ccf @@ -2639,7 +2639,7 @@ func TestEncodeDictionary(t *testing.T) { cadence.NewDictionary( []cadence.KeyValuePair{}, ).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"value":[],"type":"Dictionary"} // // language=edn, format=ccf @@ -2685,7 +2685,7 @@ func TestEncodeDictionary(t *testing.T) { Value: cadence.NewInt(3), }, }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} // // language=edn, format=ccf @@ -2780,7 +2780,7 @@ func TestEncodeDictionary(t *testing.T) { cadence.NewStringType(), cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), ), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"1"},"value":{"type":"Int","value":"1"}}]}},{"key":{"type":"String","value":"b"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"2"},"value":{"type":"Int","value":"2"}}]}},{"key":{"type":"String","value":"c"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"3"},"value":{"type":"Int","value":"3"}}]}}]} // // language=edn, format=ccf @@ -2895,7 +2895,7 @@ func TestEncodeDictionary(t *testing.T) { cadence.NewStringType(), fooResourceType, )), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}}},{"key":{"type":"String","value":"b"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}}},{"key":{"type":"String","value":"c"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}}]} // // language=edn, format=ccf @@ -3046,7 +3046,7 @@ func TestEncodeSortedDictionary(t *testing.T) { "Simple", dict, expectedDict, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} // // language=edn, format=ccf @@ -3189,7 +3189,7 @@ func TestEncodeResource(t *testing.T) { )) expectedCBOR := []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} // // language=edn, format=ccf @@ -3305,7 +3305,7 @@ func TestEncodeResource(t *testing.T) { )) // function "foo" should be omitted from resulting CBOR - expectedCBOR := []byte{ // language=json, format=json-cadence data interchange format + expectedCBOR := []byte{ // language=json, format=json-cdc // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} // // language=edn, format=ccf @@ -3447,7 +3447,7 @@ func TestEncodeResource(t *testing.T) { nil, )) - expectedCBOR := []byte{ // language=json, format=json-cadence data interchange format + expectedCBOR := []byte{ // language=json, format=json-cdc // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"2"}},{"name":"bar","value":{"type":"Resource","value":{"id":"S.test.Bar","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"x","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -3605,7 +3605,7 @@ func TestEncodeStruct(t *testing.T) { cadence.String("foo"), }, ).WithType(simpleStructType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -3710,7 +3710,7 @@ func TestEncodeStruct(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceStructType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -3857,7 +3857,7 @@ func TestEncodeEvent(t *testing.T) { cadence.String("foo"), }, ).WithType(simpleEventType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -3962,7 +3962,7 @@ func TestEncodeEvent(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceEventType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -4108,7 +4108,7 @@ func TestEncodeContract(t *testing.T) { cadence.String("foo"), }, ).WithType(simpleContractType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -4213,7 +4213,7 @@ func TestEncodeContract(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceContractType), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -4422,7 +4422,7 @@ func TestEncodeSimpleTypes(t *testing.T) { StaticType: ty.typ, }, expected: w.Bytes(), - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"[ty.ID()]"}}} // // language=edn, format=ccf @@ -4444,7 +4444,7 @@ func TestEncodeType(t *testing.T) { cadence.TypeValue{ StaticType: &cadence.OptionalType{Type: cadence.IntType{}}, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} // // language=edn, format=ccf @@ -4477,7 +4477,7 @@ func TestEncodeType(t *testing.T) { cadence.TypeValue{ StaticType: &cadence.OptionalType{Type: &cadence.OptionalType{Type: cadence.IntType{}}}, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} // // language=edn, format=ccf @@ -4511,7 +4511,7 @@ func TestEncodeType(t *testing.T) { cadence.TypeValue{ StaticType: &cadence.VariableSizedArrayType{ElementType: cadence.IntType{}}, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"VariableSizedArray", "type" : {"kind" : "Int"}}}} // // language=edn, format=ccf @@ -4547,7 +4547,7 @@ func TestEncodeType(t *testing.T) { Size: 3, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"ConstantSizedArray", "type" : {"kind" : "Int"}, "size" : 3}}} // // language=edn, format=ccf @@ -4587,7 +4587,7 @@ func TestEncodeType(t *testing.T) { KeyType: cadence.IntType{}, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Dictionary", "key" : {"kind" : "Int"}, "value" : {"kind" : "String"}}}} // // language=edn, format=ccf @@ -4636,7 +4636,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "Struct", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -4731,7 +4731,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "Resource", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -4827,7 +4827,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "Contract", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -4923,7 +4923,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "StructInterface", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -5019,7 +5019,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "ResourceInterface", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -5115,7 +5115,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "ContractInterface", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -5211,7 +5211,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "Event", "type" : "", "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [[{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}, {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}]] } } } // // language=edn, format=ccf @@ -5306,7 +5306,7 @@ func TestEncodeType(t *testing.T) { }, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type", "value": {"staticType": {"kind": "Enum", "type" : {"kind" : "String"}, "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } // // language=edn, format=ccf @@ -5397,7 +5397,7 @@ func TestEncodeType(t *testing.T) { Type: cadence.IntType{}, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Reference", "type" : {"kind" : "Int"}, "authorized" : false}}}` // // language=edn, format=ccf @@ -5440,7 +5440,7 @@ func TestEncodeType(t *testing.T) { ReturnType: cadence.IntType{}, }).WithID("Foo"), }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"Foo", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } // // language=edn, format=ccf @@ -5497,7 +5497,7 @@ func TestEncodeType(t *testing.T) { BorrowType: cadence.IntType{}, }, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Capability", "type" : {"kind" : "Int"}}}} // // language=edn, format=ccf @@ -5536,7 +5536,7 @@ func TestEncodeType(t *testing.T) { Type: cadence.IntType{}, }).WithID("Int{String}"), }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } // // language=edn, format=ccf @@ -5582,7 +5582,7 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{}, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":""}} // // language=edn, format=ccf @@ -5615,7 +5615,7 @@ func TestEncodeCapability(t *testing.T) { Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"}} // // language=edn, format=ccf @@ -5675,7 +5675,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.3", expected: cadence.Fix64(12_30000000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.3"} // // language=edn, format=ccf @@ -5698,7 +5698,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.03", expected: cadence.Fix64(12_03000000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.03"} // // language=edn, format=ccf @@ -5721,7 +5721,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.003", expected: cadence.Fix64(12_00300000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.003"} // // language=edn, format=ccf @@ -5744,7 +5744,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.0003", expected: cadence.Fix64(12_00030000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.0003"} // // language=edn, format=ccf @@ -5767,7 +5767,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.00003", expected: cadence.Fix64(12_00003000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.00003"} // // language=edn, format=ccf @@ -5790,7 +5790,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.000003", expected: cadence.Fix64(12_00000300), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.000003"} // // language=edn, format=ccf @@ -5813,7 +5813,7 @@ func TestDecodeFix64(t *testing.T) { name: "12.0000003", expected: cadence.Fix64(12_00000030), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "12.0000003"} // // language=edn, format=ccf @@ -5836,7 +5836,7 @@ func TestDecodeFix64(t *testing.T) { name: "120.3", expected: cadence.Fix64(120_30000000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "120.3"} // // language=edn, format=ccf @@ -5860,7 +5860,7 @@ func TestDecodeFix64(t *testing.T) { name: fmt.Sprintf("%d.1", maxInt), expected: cadence.Fix64(9223372036810000000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "92233720368.1"} // // language=edn, format=ccf @@ -5883,7 +5883,7 @@ func TestDecodeFix64(t *testing.T) { // 92233720369.1 name: fmt.Sprintf("%d.1", maxInt+1), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "92233720369.1"} // // language=edn, format=ccf @@ -5910,7 +5910,7 @@ func TestDecodeFix64(t *testing.T) { name: fmt.Sprintf("%d.1", minInt), expected: cadence.Fix64(-9223372036810000000), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "-92233720368.1"} // // language=edn, format=ccf @@ -5933,7 +5933,7 @@ func TestDecodeFix64(t *testing.T) { // -92233720369.1 name: fmt.Sprintf("%d.1", minInt-1), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "-92233720369.1"} // // language=edn, format=ccf @@ -5960,7 +5960,7 @@ func TestDecodeFix64(t *testing.T) { name: fmt.Sprintf("%d.%d", maxInt, maxFrac), expected: cadence.Fix64(maxInt*factor + maxFrac), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "92233720368.54775807"} // // language=edn, format=ccf @@ -5983,7 +5983,7 @@ func TestDecodeFix64(t *testing.T) { // 92233720368.54775808 name: fmt.Sprintf("%d.%d", maxInt, maxFrac+1), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "92233720368.54775808"} // // language=edn, format=ccf @@ -6010,7 +6010,7 @@ func TestDecodeFix64(t *testing.T) { name: fmt.Sprintf("%d.%d", minInt, -(minFrac)), expected: cadence.Fix64(-9223372036854775808), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "-92233720368.54775808"} // // language=edn, format=ccf @@ -6033,7 +6033,7 @@ func TestDecodeFix64(t *testing.T) { // -92233720368.54775809 name: fmt.Sprintf("%d.%d", minInt, -(minFrac - 1)), encodedData: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"type": "Fix64", "value": "-92233720368.54775809"} // // language=edn, format=ccf @@ -6094,7 +6094,7 @@ func TestExportRecursiveType(t *testing.T) { cadence.Optional{}, }, }.WithType(ty), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"foo","value":{"type": "Optional","value":null}}]}} // // language=edn, format=ccf @@ -6185,7 +6185,7 @@ func TestExportTypeValueRecursiveType(t *testing.T) { cadence.TypeValue{ StaticType: ty, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Foo","fields":[{"id":"foo","type":{"kind":"Optional","type":"S.test.Foo"}}],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6267,7 +6267,7 @@ func TestExportTypeValueRecursiveType(t *testing.T) { cadence.TypeValue{ StaticType: barTy, }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Bar","fields":[{"id":"foo1","type":{"kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[],"type":""}},{"id":"foo2","type":"S.test.Foo"}],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6350,7 +6350,7 @@ func TestEncodePath(t *testing.T) { testEncodeAndDecode( t, cadence.NewPath("storage", "foo"), - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // {"type":"Path","value":{"domain":"storage","identifier":"foo"}} // // language=edn, format=ccf @@ -6402,7 +6402,7 @@ func TestDecodeInvalidType(t *testing.T) { t.Parallel() encodedData := []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // { "type":"Struct", "value":{ "id":"", "fields":[] } } // // language=edn, format=ccf @@ -6453,7 +6453,7 @@ func TestDecodeInvalidType(t *testing.T) { t.Parallel() encodedData := []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // { "type":"Struct", "value":{ "id":"I.Foo", "fields":[] } } // // language=edn, format=ccf @@ -6506,7 +6506,7 @@ func TestDecodeInvalidType(t *testing.T) { t.Parallel() encodedData := []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // { "type":"Struct", "value":{ "id":"N.PublicKey", "fields":[] } } // // language=edn, format=ccf @@ -6624,7 +6624,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Struct","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6665,7 +6665,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"StructInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6706,7 +6706,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6747,7 +6747,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"ResourceInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6788,7 +6788,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Contract","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6829,7 +6829,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"ContractInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6870,7 +6870,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Enum","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6911,7 +6911,7 @@ func TestEncodeBuiltinComposites(t *testing.T) { Location: nil, QualifiedIdentifier: "Foo", }, - encoded: []byte{ // language=json, format=json-cadence data interchange format + encoded: []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType":{"kind":"Event","typeID":"Foo","fields":[],"initializers":[],"type":""}}} // // language=edn, format=ccf @@ -6984,7 +6984,7 @@ func TestExportFunctionValue(t *testing.T) { ReturnType: cadence.VoidType{}, }).WithID("(():Void)"), }, - []byte{ // language=json, format=json-cadence data interchange format + []byte{ // language=json, format=json-cdc // { "type": "Function", "value": { "functionType": { "kind": "Function", "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" } } } } // // language=edn, format=ccf @@ -7028,7 +7028,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowFees.FeesDeducted", event: createFlowFeesFeesDeductedEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.f919ee77447b7497.FlowFees.FeesDeducted","fields":[{"value":{"value":"0.01797293","type":"UFix64"},"name":"amount"},{"value":{"value":"1.00000000","type":"UFix64"},"name":"inclusionEffort"},{"value":{"value":"0.00360123","type":"UFix64"},"name":"executionEffort"}]},"type":"Event"} // // language=edn, format=ccf @@ -7116,7 +7116,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowFees.TokensWithdrawn", event: createFlowFeesTokensWithdrawnEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.f919ee77447b7497.FlowFees.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"}]},"type":"Event"} // // language=edn, format=ccf @@ -7178,7 +7178,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowIDTableStaking.DelegatorRewardsPaid", event: createFlowIDTableStakingDelegatorRewardsPaidEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"92","type":"UInt32"},"name":"delegatorID"},{"value":{"value":"4.38760261","type":"UFix64"},"name":"amount"}]},"type":"Event"} // // language=edn, format=ccf @@ -7269,7 +7269,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowIDTableStaking.EpochTotalRewardsPaid", event: createFlowIDTableStakingEpochTotalRewardsPaidEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid","fields":[{"value":{"value":"1316543.00000000","type":"UFix64"},"name":"total"},{"value":{"value":"53.04112895","type":"UFix64"},"name":"fromFees"},{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"minted"},{"value":{"value":"6.04080767","type":"UFix64"},"name":"feesBurned"}]},"type":"Event"} // // language=edn, format=ccf @@ -7370,7 +7370,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowIDTableStaking.NewWeeklyPayout", event: createFlowIDTableStakingNewWeeklyPayoutEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout","fields":[{"value":{"value":"1317778.00000000","type":"UFix64"},"name":"newPayout"}]},"type":"Event"} // // language=edn, format=ccf @@ -7432,7 +7432,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowIDTableStaking.RewardsPaid", event: createFlowIDTableStakingRewardsPaidEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"}]},"type":"Event"} // // language=edn, format=ccf @@ -7509,7 +7509,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowToken.TokensDeposited with nil receiver", event: createFlowTokenTokensDepositedEventNoReceiver(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"},{"value":{"value":null,"type":"Optional"},"name":"to"}]},"type":"Event"} // // language=edn, format=ccf @@ -7586,7 +7586,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowToken.TokensDeposited", event: createFlowTokenTokensDepositedEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0x8624b52f9ddcd04a","type":"Address"},"type":"Optional"},"name":"to"}]},"type":"Event"} // // language=edn, format=ccf @@ -7665,7 +7665,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowToken.TokensMinted", event: createFlowTokenTokensMintedEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.1654653399040a61.FlowToken.TokensMinted","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"}]},"type":"Event"} // // language=edn, format=ccf @@ -7727,7 +7727,7 @@ func TestDeployedEvents(t *testing.T) { name: "FlowToken.TokensWithdrawn", event: createFlowTokenTokensWithdrawnEvent(), expectedCBOR: []byte{ - // language=json, format=json-cadence data interchange format + // language=json, format=json-cdc // {"value":{"id":"A.1654653399040a61.FlowToken.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0xf919ee77447b7497","type":"Address"},"type":"Optional"},"name":"from"}]},"type":"Event"} // // language=edn, format=ccf From 657e4edb154bb0956801905126510e46f7f0cc91 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:54:13 -0500 Subject: [PATCH 032/173] Fix lint errors --- encoding/ccf/ccf_type_id_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/ccf_type_id_test.go b/encoding/ccf/ccf_type_id_test.go index 1b3ef0db4e..8d4f810b29 100644 --- a/encoding/ccf/ccf_type_id_test.go +++ b/encoding/ccf/ccf_type_id_test.go @@ -22,9 +22,10 @@ import ( "math" "testing" + "github.com/stretchr/testify/require" + "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/tests/utils" - "github.com/stretchr/testify/require" ) func TestCCFTypeID(t *testing.T) { @@ -69,7 +70,7 @@ func TestCCFTypeIDByCadenceType(t *testing.T) { ccfIDs := make(ccfTypeIDByCadenceType) // Lookup non-existent CCF type ID. - id, err := ccfIDs.id(simpleStructType()) + _, err := ccfIDs.id(simpleStructType()) require.Error(t, err) // Add entry. @@ -77,7 +78,7 @@ func TestCCFTypeIDByCadenceType(t *testing.T) { ccfIDs[simpleStructType().ID()] = ccfID // Lookup existing CCF type ID. - id, err = ccfIDs.id(simpleStructType()) + id, err := ccfIDs.id(simpleStructType()) require.Equal(t, ccfID, id) require.NoError(t, err) } From 60d132c8bef65501e4571cb3fb2e9f7bea2f7fff Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 21 Mar 2023 18:36:46 -0500 Subject: [PATCH 033/173] Remove RawType comparison in EnumType.Equal() --- types.go | 3 +-- types_test.go | 22 ---------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/types.go b/types.go index 0b25ec19f8..dab86a4d3e 100644 --- a/types.go +++ b/types.go @@ -2130,8 +2130,7 @@ func (t *EnumType) Equal(other Type) bool { } return t.Location == otherType.Location && - t.QualifiedIdentifier == otherType.QualifiedIdentifier && - t.RawType.Equal(otherType.RawType) + t.QualifiedIdentifier == otherType.QualifiedIdentifier } // AuthAccountType diff --git a/types_test.go b/types_test.go index eaafba7a00..a04bacfad5 100644 --- a/types_test.go +++ b/types_test.go @@ -1639,28 +1639,6 @@ func TestTypeEquality(t *testing.T) { assert.True(t, source.Equal(target)) }) - t.Run("different raw type", func(t *testing.T) { - t.Parallel() - - source := &EnumType{ - Location: common.AddressLocation{ - Name: "Foo", - Address: common.Address{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}, - }, - QualifiedIdentifier: "Bar", - RawType: IntType{}, - } - target := &EnumType{ - Location: common.AddressLocation{ - Name: "Foo", - Address: common.Address{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}, - }, - QualifiedIdentifier: "Bar", - RawType: StringType{}, - } - assert.False(t, source.Equal(target)) - }) - t.Run("different location name", func(t *testing.T) { t.Parallel() From 7624694f92d89a63baf12940f3ac277a33e8ffe9 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 21 Mar 2023 18:42:02 -0500 Subject: [PATCH 034/173] Add more CCF codec tests for array --- encoding/ccf/ccf_test.go | 250 +++++++++++++++++++++++++++++++++++---- 1 file changed, 229 insertions(+), 21 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 6b4307a14d..35e45a4c8d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -103,7 +103,7 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(non-nil)", + "Optional(Int)", cadence.NewOptional(cadence.NewInt(42)), []byte{ // language=json, format=json-cdc // {"type":"Optional","value":{"type":"Int","value":"42"}} @@ -157,7 +157,7 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(non-nil))", + "Optional(Optional(Int))", cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"} @@ -215,7 +215,7 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(Optional(non-nil)))", + "Optional(Optional(Optional(int)))", cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"} @@ -1887,6 +1887,60 @@ func TestEncodeArray(t *testing.T) { }, } + // constant sized array [1, 2, 3] + constantSizedIntArray := encodeTest{ + "Constant-sized Integers", + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + cadence.NewInt(3), + }).WithType(cadence.NewConstantSizedArrayType(3, cadence.NewIntType())), + []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} + // + // language=edn, format=ccf + // 130([140[3, (137(4))], [1, 2, 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type constant-sized [3]int + // tag + 0xd8, ccf.CBORTagConstsizedArrayType, + // array, 2 items follow + 0x82, + // number of elements + 0x03, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + // [1, 2, 3] intArray := encodeTest{ "Integers", @@ -2043,15 +2097,15 @@ func TestEncodeArray(t *testing.T) { cadence.NewArray([]cadence.Value{ cadence.NewResource([]cadence.Value{ cadence.NewInt(1), - cadence.NewInt(1), + cadence.NewInt(1), // field is AnyStruct type }).WithType(foooResourceTypeWithAbstractField), cadence.NewResource([]cadence.Value{ cadence.NewInt(2), - s, + s, // field is AnyStruct type }).WithType(foooResourceTypeWithAbstractField), cadence.NewResource([]cadence.Value{ cadence.NewInt(3), - cadence.NewBool(true), + cadence.NewBool(true), // field is AnyStruct type }).WithType(foooResourceTypeWithAbstractField), }).WithType(cadence.NewVariableSizedArrayType(foooResourceTypeWithAbstractField)), []byte{ // language=json, format=json-cdc @@ -2618,9 +2672,163 @@ func TestEncodeArray(t *testing.T) { }, } + // [S.test.FooStruct{"a", S.test.Foo{0}}, S.test.FooStruct{"b", S.test.Foo{1}}] + resourceStructArray := encodeTest{ + "Resource Struct Array", + cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.String("a"), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(0), + }).WithType(fooResourceType), + }).WithType(resourceStructType), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }).WithType(resourceStructType), + }).WithType(cadence.NewVariableSizedArrayType(resourceStructType)), + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"a","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"0","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 160([h'01', "S.test.FooStruct", [["a", 137(1)], ["b", 136(h'')]]])], [139(136(h'01')), [["a", [0]], ["b", [1]]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // 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, + // type definition 1 + // struct type: + // id: []byte{1} + // cadence-type-id: "S.test.FooStruct" + // fields: [["a", string type], ["b", foo resource type]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // 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, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // [S.test.FooStruct{"a", S.test.Foo{0}}, S.test.FooStruct{"b", S.test.Foo{1}}] + // array, 2 item follow + 0x82, + // element 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + // element 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + testAllEncodeAndDecode(t, emptyArray, + constantSizedIntArray, intArray, + resourceStructArray, resourceArray, resourceWithAbstractFieldArray, heterogeneousSimpleTypeArray, @@ -3683,21 +3891,6 @@ func TestEncodeStruct(t *testing.T) { }, } - resourceStructType := &cadence.StructType{ - Location: utils.TestLocation, - QualifiedIdentifier: "FooStruct", - Fields: []cadence.Field{ - { - Identifier: "a", - Type: cadence.StringType{}, - }, - { - Identifier: "b", - Type: fooResourceType, - }, - }, - } - resourceStruct := encodeTest{ "Resources", cadence.NewStruct( @@ -6584,6 +6777,21 @@ func testDecode(t *testing.T, actualCBOR []byte, expectedVal cadence.Value) { ) } +var resourceStructType = &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, +} + var fooResourceType = &cadence.ResourceType{ Location: utils.TestLocation, QualifiedIdentifier: "Foo", From 46925ecc016227370e79a13740bb586b14a7b61b Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 21 Mar 2023 18:45:30 -0500 Subject: [PATCH 035/173] Add CCF codec test for Enum --- encoding/ccf/ccf_test.go | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 35e45a4c8d..75763ac58e 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -4525,6 +4525,89 @@ func TestEncodeContract(t *testing.T) { testAllEncodeAndDecode(t, simpleContract, resourceContract) } +func TestEncodeEnum(t *testing.T) { + t.Parallel() + + simpleEnumType := &cadence.EnumType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEnum", + Fields: []cadence.Field{ + { + Identifier: "raw", + Type: cadence.UInt8Type{}, + }, + }, + } + + simpleEnum := encodeTest{ + "Simple", + cadence.NewEnum( + []cadence.Value{ + cadence.NewUInt8(1), + }, + ).WithType(simpleEnumType), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooEnum","fields":[{"value":{"value":"1","type":"UInt8"},"name":"raw"}]},"type":"Enum"} + // + // language=edn, format=ccf + // 129([[164([h'', "S.test.FooEnum", [["raw", 137(12)]]])], [136(h''), [1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // enum type: + // id: []byte{} + // cadence-type-id: "S.test.FooEnum" + // 1 fields: [["raw", type(uint8)]] + // tag + 0xd8, ccf.CBORTagEnumType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 14 bytes follow + 0x6e, + // S.test.FooEnum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x6e, 0x75, 0x6d, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // raw + 0x72, 0x61, 0x77, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 1 + 0x01, + }, + } + + testAllEncodeAndDecode(t, simpleEnum) +} + func TestEncodeSimpleTypes(t *testing.T) { t.Parallel() From c23378092c1cfa8348bf17d0e82041e334a378d8 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:03:35 -0500 Subject: [PATCH 036/173] Fix CCF encoding edge case for optional value If optional value's static type is an optional abstract type, such as OptionalType{AnyStructType}, encoded runtime type of that optional value should be runtime type with OptionalType layer removed because static type is already encoded and encoded runtime type shouldn't include the same type information. Added more tests for optional values. --- encoding/ccf/ccf_test.go | 699 +++++++++++++++++++++++++++++++++++++++ encoding/ccf/encode.go | 34 +- 2 files changed, 732 insertions(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 75763ac58e..3430127917 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -77,6 +77,55 @@ func TestEncodeOptional(t *testing.T) { t.Parallel() + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, + } + + structType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.NewOptionalType(cadence.NewIntType()), + }, + { + Identifier: "b", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewIntType())), + }, + { + Identifier: "c", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewIntType()))), + }, + }, + } + + structTypeWithOptionalAbstractField := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.NewOptionalType(cadence.NewAnyStructType()), + }, + { + Identifier: "b", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewAnyStructType())), + }, + { + Identifier: "c", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewAnyStructType()))), + }, + }, + } + testAllEncodeAndDecode(t, []encodeTest{ { "Optional(nil)", @@ -246,6 +295,656 @@ func TestEncodeOptional(t *testing.T) { 0x2a, }, }, + { + "struct with nil optional fields", + cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(nil), + cadence.NewOptional(cadence.NewOptional(nil)), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + }).WithType(structType), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(4))], ["b", 138(138(137(4)))], ["c", 138(138(138(137(4))))]]])], [136(h''), [null, null, null]]]) + // + // 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.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // nil + 0xf6, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + "struct with non-nil optional fields", + cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewInt(1)), + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3)))), + }).WithType(structType), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(4))], ["b", 138(138(137(4)))], ["c", 138(138(138(137(4))))]]])], [136(h''), [1, 2, 3]]]) + // + // 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.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 2 + 0x02, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + }, + }, + { + "struct with nil optional abstract fields", + cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(nil), + cadence.NewOptional(cadence.NewOptional(nil)), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + }).WithType(structTypeWithOptionalAbstractField), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]])], [136(h''), [null, null, null]]]) + // + // 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.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // nil + 0xf6, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + "struct with optional Int for optional abstract fields", + cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(1))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2)))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3))))), + }).WithType(structTypeWithOptionalAbstractField), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]])], [136(h''), [130([138(137(4)), 1]), 130([138(137(4)), 2]), 130([138(137(4)), 3])]]]) + // + // 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.Foo" + // fields: [["a", OptionalType(AnyStructType)], ["b", OptionalType(OptionalType(AnyStructType))], ["c", OptionalType(OptionalType(OptionalType(AnyStructType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // field 0 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // field 1 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // field 2 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + }, + { + "struct with non-nil optional abstract fields", + cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewInt(1)), + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewStruct([]cadence.Value{ + cadence.NewInt(3), + }).WithType(simpleStructType)))), + }).WithType(structTypeWithOptionalAbstractField), + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"3","type":"Int"},"name":"bar"}]},"type":"Struct"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]]), 160([h'01', "S.test.FooStruct", [["bar", 137(4)]]])], [136(h''), [130([137(4), 1]), 130([137(4), 2]), 130([136(h'01'), [3]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(AnyStructType)], ["b", OptionalType(OptionalType(AnyStructType))], ["c", OptionalType(OptionalType(OptionalType(AnyStructType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["bar", IntType]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // 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, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // field 0 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // field 1 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // field 2 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + }, }...) } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index eda685ba2a..2e58b6c3eb 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -346,6 +346,9 @@ func (e *Encoder) encodeValue( } if needToEncodeRuntimeType(staticType, runtimeType) { + // Get type that needs to be encoded as inline type. + inlineType := getTypeToEncodeAsCCFInlineType(staticType, runtimeType) + // Encode ccf-type-and-value-message. // Encode tag number and array head of length 2. @@ -360,7 +363,7 @@ func (e *Encoder) encodeValue( } // element 0: type as inline-type - err = e.encodeInlineType(runtimeType, tids) + err = e.encodeInlineType(inlineType, tids) if err != nil { return err } @@ -1756,6 +1759,35 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) return true } +func getTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { + if _, ok := staticType.(*cadence.OptionalType); ok { + return getOptionalInnerTypeToEncodeAsCCFInlineType(staticType, runtimeType) + } + return runtimeType +} + +// getOptionalInnerTypeToEncodeAsCCFInlineType returns cadence.Type that needs to be encoded as CCF inline type. +// Since static type is encoded at higher level, inline type shouldn't repeat encoded static type. +// So inline type is runtime type after removing OptionalType wrappers that are present in static type. +func getOptionalInnerTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { + for { + sot, ok := staticType.(*cadence.OptionalType) + if !ok { + break + } + rot, ok := runtimeType.(*cadence.OptionalType) + if !ok { + // static type is optional type while runtime type isn't. + panic(cadenceErrors.NewUnexpectedError("static type (%T) is optional type while runtime type (%T) isn't", staticType, runtimeType)) + } + + staticType = sot.Type + runtimeType = rot.Type + } + + return runtimeType +} + // isOptionalNeverType returns true if t is (nested) optional never type. func isOptionalNeverType(t cadence.Type) bool { for { From 24dcca667b4c930aad03fade3087ab9a05fa0a02 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:34:59 -0500 Subject: [PATCH 037/173] Update CCF encoder to use DictionaryType directly Dictionary.DictionaryType used to be of type cadence.Type, which requires type assertion during encoding. Now Dictionary.DictionaryType is of type *cadence.DictionaryType, and type assertion is no longer needed. Since all cadence.Value's runtime type is checked to be non-nil in encodeValue(), v.DictionaryType is not nil in encodeDictionary(). --- encoding/ccf/encode.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 2e58b6c3eb..16a233860c 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -727,13 +727,8 @@ func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadence return e.encodeSortedDictionary(v, tids) } - dictionaryType := v.DictionaryType.(*cadence.DictionaryType) - if dictionaryType == nil { - return fmt.Errorf("unexpected dictionary type %s", v.DictionaryType.ID()) - } - - staticKeyType := dictionaryType.KeyType - staticElementType := dictionaryType.ElementType + staticKeyType := v.DictionaryType.KeyType + staticElementType := v.DictionaryType.ElementType // Encode array head with array size of 2 * number of pairs. err := e.enc.EncodeArrayHead(uint64(len(v.Pairs)) * 2) @@ -802,13 +797,8 @@ func encodeAndSortKeyValuePairs( []encodedKeyValuePair, error, ) { - dictionaryType := v.DictionaryType.(*cadence.DictionaryType) - if dictionaryType == nil { - return nil, fmt.Errorf("expected dictionary type %s", v.DictionaryType.ID()) - } - - staticKeyType := dictionaryType.KeyType - staticElementType := dictionaryType.ElementType + staticKeyType := v.DictionaryType.KeyType + staticElementType := v.DictionaryType.ElementType encodedPairs := make([]encodedKeyValuePair, len(v.Pairs)) From 49d23b175ccf510f635c9e8fe8cdc68eefbb649a Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 14:07:53 -0500 Subject: [PATCH 038/173] Add comment for OptionalType{NeverType} edge case --- encoding/ccf/encode.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 16a233860c..41a292a7e2 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1741,6 +1741,12 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) } // Here, static type is different from runtime type. // Handle special case of runtime type being OptionalType{NeverType}. + // We handle special case of Optional{nil} because its runtime type is OptionalType{NeverType} + // while its static type can be different, such as OptionalType{AddressType}. + // For example, TokensDeposited event is defined as `TokensDeposited(amount: UFix64, to: Address?)`, + // field to's type is OptionalType{AddressType} and its value can be nil with runtime type + // OptionalType{NeverType}. Even though runtime type is different from static type (field type), + // encoder encodes nil value without encoding its runtime type. if _, ok := staticType.(*cadence.OptionalType); ok { if isOptionalNeverType(runtimeType) { return false From cadecce0f45b1ce41422f968d1c52e7de2080ab4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:02:36 -0500 Subject: [PATCH 039/173] Add more CCF tests --- encoding/ccf/ccf_test.go | 289 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 3430127917..601b2d55cb 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -3186,8 +3186,8 @@ func TestEncodeArray(t *testing.T) { } // [S.test.Foo{1}, S.test.Fooo{2, "a"}] - heterogeneousInterfaceTypeArray := encodeTest{ - "Heterogenous Interface Array", + resourceInterfaceTypeArray := encodeTest{ + "Resource Interface Array", cadence.NewArray([]cadence.Value{ cadence.NewResource([]cadence.Value{ cadence.NewInt(1), @@ -3523,6 +3523,286 @@ func TestEncodeArray(t *testing.T) { }, } + structInterfaceType := &cadence.StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStructInterface", + } + + structType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } + + // [S.test.FooStruct{1}, S.test.FooStruct{2}] + structInterfaceTypeArray := encodeTest{ + "Struct Interface Array", + cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.NewInt(1), + }).WithType(structType), + cadence.NewStruct([]cadence.Value{ + cadence.NewInt(2), + }).WithType(structType), + }).WithType(cadence.NewVariableSizedArrayType(structInterfaceType)), + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", 137(4)]]]), 176([h'01', "S.test.FooStructInterface"])], [139(136(h'01')), [130([136(h''), [1]]), 130([136(h''), [2]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["a", int type]] + // 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, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 1 + // struct interface type: + // id: []byte{1} + // cadence-type-id: "S.test.FooStructInterface" + // tag + 0xd8, ccf.CBORTagStructInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 25 bytes follow + 0x78, 0x19, + // S.test.FooStructInterface + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + }, + } + + contractInterfaceType := &cadence.ContractInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContractInterface", + } + + contractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } + + // [S.test.FooContract{1}, S.test.FooContract{2}] + contractInterfaceTypeArray := encodeTest{ + "Contract Interface Array", + cadence.NewArray([]cadence.Value{ + cadence.NewContract([]cadence.Value{ + cadence.NewInt(1), + }).WithType(contractType), + cadence.NewContract([]cadence.Value{ + cadence.NewInt(2), + }).WithType(contractType), + }).WithType(cadence.NewVariableSizedArrayType(contractInterfaceType)), + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Contract"},{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Contract"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[163([h'', "S.test.FooContract", [["a", 137(4)]]]), 178([h'01', "S.test.FooContractInterface"])], [139(136(h'01')), [130([136(h''), [1]]), 130([136(h''), [2]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // fields: [["a", int type]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 1 + // constract interface type: + // id: []byte{1} + // cadence-type-id: "S.test.FooContractInterface" + // tag + 0xd8, ccf.CBORTagContractInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 27 bytes follow + 0x78, 0x1b, + // S.test.FooContractInterface + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + }, + } testAllEncodeAndDecode(t, emptyArray, constantSizedIntArray, @@ -3533,7 +3813,9 @@ func TestEncodeArray(t *testing.T) { heterogeneousSimpleTypeArray, heterogeneousNumberTypeArray, heterogeneousCompositeTypeArray, - heterogeneousInterfaceTypeArray, + resourceInterfaceTypeArray, + structInterfaceTypeArray, + contractInterfaceTypeArray, ) } @@ -5333,6 +5615,7 @@ func TestEncodeSimpleTypes(t *testing.T) { {cadence.IntegerType{}, ccf.TypeInteger}, {cadence.SignedIntegerType{}, ccf.TypeSignedInteger}, {cadence.FixedPointType{}, ccf.TypeFixedPoint}, + {cadence.SignedFixedPointType{}, ccf.TypeSignedFixedPoint}, {cadence.IntType{}, ccf.TypeInt}, {cadence.Int8Type{}, ccf.TypeInt8}, {cadence.Int16Type{}, ccf.TypeInt16}, From 9672fa4a056c36d296fe24411a5afbcacce3eaf3 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:03:53 -0500 Subject: [PATCH 040/173] Add more CCF tests --- encoding/ccf/ccf_test.go | 854 ++++++++++++++++++++++++++------------- 1 file changed, 568 insertions(+), 286 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 601b2d55cb..4b6507abc9 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -40,9 +40,10 @@ import ( ) type encodeTest struct { - name string - val cadence.Value - expected []byte + name string + val cadence.Value + expected []byte + expectedVal cadence.Value } func TestEncodeVoid(t *testing.T) { @@ -128,9 +129,9 @@ func TestEncodeOptional(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Optional(nil)", - cadence.NewOptional(nil), - []byte{ // language=json, format=json-cdc + name: "Optional(nil)", + val: cadence.NewOptional(nil), + expected: []byte{ // language=json, format=json-cdc // {"type":"Optional","value":null} // // language=edn, format=ccf @@ -152,9 +153,9 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Int)", - cadence.NewOptional(cadence.NewInt(42)), - []byte{ // language=json, format=json-cdc + name: "Optional(Int)", + val: cadence.NewOptional(cadence.NewInt(42)), + expected: []byte{ // language=json, format=json-cdc // {"type":"Optional","value":{"type":"Int","value":"42"}} // // language=edn, format=ccf @@ -180,9 +181,9 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(nil))", - cadence.NewOptional(cadence.NewOptional(nil)), - []byte{ // language=json, format=json-cdc + name: "Optional(Optional(nil))", + val: cadence.NewOptional(cadence.NewOptional(nil)), + expected: []byte{ // language=json, format=json-cdc // {"value":{"value":null,"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -206,9 +207,9 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(Int))", - cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), - []byte{ // language=json, format=json-cdc + name: "Optional(Optional(Int))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), + expected: []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -236,9 +237,9 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(Optional(nil)))", - cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), - []byte{ // language=json, format=json-cdc + name: "Optional(Optional(Optional(nil)))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + expected: []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -264,9 +265,9 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "Optional(Optional(Optional(int)))", - cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), - []byte{ // language=json, format=json-cdc + name: "Optional(Optional(Optional(int)))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), + expected: []byte{ // language=json, format=json-cdc // {"value":{"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"} // // language=edn, format=ccf @@ -296,13 +297,13 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "struct with nil optional fields", - cadence.NewStruct([]cadence.Value{ + name: "struct with nil optional fields", + val: cadence.NewStruct([]cadence.Value{ cadence.NewOptional(nil), cadence.NewOptional(cadence.NewOptional(nil)), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), }).WithType(structType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} // // language=edn, format=ccf @@ -399,13 +400,13 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "struct with non-nil optional fields", - cadence.NewStruct([]cadence.Value{ + name: "struct with non-nil optional fields", + val: cadence.NewStruct([]cadence.Value{ cadence.NewOptional(cadence.NewInt(1)), cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3)))), }).WithType(structType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} // // language=edn, format=ccf @@ -514,13 +515,13 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "struct with nil optional abstract fields", - cadence.NewStruct([]cadence.Value{ + name: "struct with nil optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ cadence.NewOptional(nil), cadence.NewOptional(cadence.NewOptional(nil)), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), }).WithType(structTypeWithOptionalAbstractField), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} // // language=edn, format=ccf @@ -617,13 +618,13 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "struct with optional Int for optional abstract fields", - cadence.NewStruct([]cadence.Value{ + name: "struct with optional Int for optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ cadence.NewOptional(cadence.NewOptional(cadence.NewInt(1))), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2)))), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3))))), }).WithType(structTypeWithOptionalAbstractField), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} // // language=edn, format=ccf @@ -765,15 +766,15 @@ func TestEncodeOptional(t *testing.T) { }, }, { - "struct with non-nil optional abstract fields", - cadence.NewStruct([]cadence.Value{ + name: "struct with non-nil optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ cadence.NewOptional(cadence.NewInt(1)), cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewStruct([]cadence.Value{ cadence.NewInt(3), }).WithType(simpleStructType)))), }).WithType(structTypeWithOptionalAbstractField), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"3","type":"Int"},"name":"bar"}]},"type":"Struct"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} // // language=edn, format=ccf @@ -954,9 +955,9 @@ func TestEncodeBool(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "True", - cadence.NewBool(true), - []byte{ // language=json, format=json-cdc + name: "True", + val: cadence.NewBool(true), + expected: []byte{ // language=json, format=json-cdc // {"type":"Bool","value":true} // // language=edn, format=ccf @@ -976,9 +977,9 @@ func TestEncodeBool(t *testing.T) { }, }, { - "False", - cadence.NewBool(false), - []byte{ // language=json, format=json-cdc + name: "False", + val: cadence.NewBool(false), + expected: []byte{ // language=json, format=json-cdc // {"type":"Bool","value":false} // // language=edn, format=ccf @@ -1009,9 +1010,9 @@ func TestEncodeCharacter(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "a", - a, - []byte{ // language=json, format=json-cdc + name: "a", + val: a, + expected: []byte{ // language=json, format=json-cdc // {"type":"Character","value":"a"} // // language=edn, format=ccf @@ -1033,9 +1034,9 @@ func TestEncodeCharacter(t *testing.T) { }, }, { - "b", - b, - []byte{ // language=json, format=json-cdc + name: "b", + val: b, + expected: []byte{ // language=json, format=json-cdc // {"type":"Character","value":"b"} // // language=edn, format=ccf @@ -1065,9 +1066,9 @@ func TestEncodeString(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Empty", - cadence.String(""), - []byte{ // language=json, format=json-cdc + name: "Empty", + val: cadence.String(""), + expected: []byte{ // language=json, format=json-cdc // {"type":"String","value":""} // // language=edn, format=ccf @@ -1087,9 +1088,9 @@ func TestEncodeString(t *testing.T) { }, }, { - "Non-empty", - cadence.String("foo"), - []byte{ // language=json, format=json-cdc + name: "Non-empty", + val: cadence.String("foo"), + expected: []byte{ // language=json, format=json-cdc // {"type":"String","value":"foo"} // // language=edn, format=ccf @@ -1149,9 +1150,9 @@ func TestEncodeInt(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Negative", - cadence.NewInt(-42), - []byte{ // language=json, format=json-cdc + name: "Negative", + val: cadence.NewInt(-42), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int","value":"-42"} // // language=edn, format=ccf @@ -1175,9 +1176,9 @@ func TestEncodeInt(t *testing.T) { }, }, { - "Zero", - cadence.NewInt(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int","value":"0"} // // language=edn, format=ccf @@ -1199,9 +1200,9 @@ func TestEncodeInt(t *testing.T) { }, }, { - "Positive", - cadence.NewInt(42), - []byte{ // language=json, format=json-cdc + name: "Positive", + val: cadence.NewInt(42), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int","value":"42"} // // language=edn, format=ccf @@ -1225,9 +1226,9 @@ func TestEncodeInt(t *testing.T) { }, }, { - "SmallerThanMinInt256", - cadence.NewIntFromBig(new(big.Int).Sub(sema.Int256TypeMinIntBig, big.NewInt(10))), - []byte{ // language=json, format=json-cdc + name: "SmallerThanMinInt256", + val: cadence.NewIntFromBig(new(big.Int).Sub(sema.Int256TypeMinIntBig, big.NewInt(10))), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819978"} // // language=edn, format=ccf @@ -1254,9 +1255,9 @@ func TestEncodeInt(t *testing.T) { }, }, { - "LargerThanMaxUInt256", - cadence.NewIntFromBig(new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))), - []byte{ // language=json, format=json-cdc + name: "LargerThanMaxUInt256", + val: cadence.NewIntFromBig(new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} // // language=edn, format=ccf @@ -1292,9 +1293,9 @@ func TestEncodeInt8(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.NewInt8(math.MinInt8), - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.NewInt8(math.MinInt8), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"-128"} // // language=edn, format=ccf @@ -1314,9 +1315,9 @@ func TestEncodeInt8(t *testing.T) { }, }, { - "Zero", - cadence.NewInt8(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt8(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"0"} // // language=edn, format=ccf @@ -1336,9 +1337,9 @@ func TestEncodeInt8(t *testing.T) { }, }, { - "Max", - cadence.NewInt8(math.MaxInt8), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewInt8(math.MaxInt8), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int8","value":"127"} // // language=edn, format=ccf @@ -1366,9 +1367,9 @@ func TestEncodeInt16(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.NewInt16(math.MinInt16), - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.NewInt16(math.MinInt16), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"-32768"} // // language=edn, format=ccf @@ -1388,9 +1389,9 @@ func TestEncodeInt16(t *testing.T) { }, }, { - "Zero", - cadence.NewInt16(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt16(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"0"} // // language=edn, format=ccf @@ -1410,9 +1411,9 @@ func TestEncodeInt16(t *testing.T) { }, }, { - "Max", - cadence.NewInt16(math.MaxInt16), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewInt16(math.MaxInt16), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int16","value":"32767"} // // language=edn, format=ccf @@ -1440,9 +1441,9 @@ func TestEncodeInt32(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.NewInt32(math.MinInt32), - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.NewInt32(math.MinInt32), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"-2147483648"} // // language=edn, format=ccf @@ -1462,9 +1463,9 @@ func TestEncodeInt32(t *testing.T) { }, }, { - "Zero", - cadence.NewInt32(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt32(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"0"} // // language=edn, format=ccf @@ -1484,9 +1485,9 @@ func TestEncodeInt32(t *testing.T) { }, }, { - "Max", - cadence.NewInt32(math.MaxInt32), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewInt32(math.MaxInt32), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int32","value":"2147483647"} // // language=edn, format=ccf @@ -1514,9 +1515,9 @@ func TestEncodeInt64(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.NewInt64(math.MinInt64), - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.NewInt64(math.MinInt64), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"-9223372036854775808"} // // language=edn, format=ccf @@ -1536,9 +1537,9 @@ func TestEncodeInt64(t *testing.T) { }, }, { - "Zero", - cadence.NewInt64(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt64(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"0"} // // language=edn, format=ccf @@ -1558,9 +1559,9 @@ func TestEncodeInt64(t *testing.T) { }, }, { - "Max", - cadence.NewInt64(math.MaxInt64), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewInt64(math.MaxInt64), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int64","value":"9223372036854775807"} // // language=edn, format=ccf @@ -1588,9 +1589,9 @@ func TestEncodeInt128(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.Int128{Value: sema.Int128TypeMinIntBig}, - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.Int128{Value: sema.Int128TypeMinIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"-170141183460469231731687303715884105728"} // // language=edn, format=ccf @@ -1615,9 +1616,9 @@ func TestEncodeInt128(t *testing.T) { }, }, { - "Zero", - cadence.NewInt128(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt128(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"0"} // // language=edn, format=ccf @@ -1639,9 +1640,9 @@ func TestEncodeInt128(t *testing.T) { }, }, { - "Max", - cadence.Int128{Value: sema.Int128TypeMaxIntBig}, - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.Int128{Value: sema.Int128TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"Int128","value":"170141183460469231731687303715884105727"} // // language=edn, format=ccf @@ -1674,9 +1675,9 @@ func TestEncodeInt256(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Min", - cadence.Int256{Value: sema.Int256TypeMinIntBig}, - []byte{ // language=json, format=json-cdc + name: "Min", + val: cadence.Int256{Value: sema.Int256TypeMinIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819968"} // // language=edn, format=ccf @@ -1703,9 +1704,9 @@ func TestEncodeInt256(t *testing.T) { }, }, { - "Zero", - cadence.NewInt256(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewInt256(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"0"} // // language=edn, format=ccf @@ -1727,9 +1728,9 @@ func TestEncodeInt256(t *testing.T) { }, }, { - "Max", - cadence.Int256{Value: sema.Int256TypeMaxIntBig}, - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.Int256{Value: sema.Int256TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"Int256","value":"57896044618658097711785492504343953926634992332820282019728792003956564819967"} // // language=edn, format=ccf @@ -1764,9 +1765,9 @@ func TestEncodeUInt(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"0"} // // language=edn, format=ccf @@ -1788,9 +1789,9 @@ func TestEncodeUInt(t *testing.T) { }, }, { - "Positive", - cadence.NewUInt(42), - []byte{ // language=json, format=json-cdc + name: "Positive", + val: cadence.NewUInt(42), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"42"} // // language=edn, format=ccf @@ -1814,9 +1815,9 @@ func TestEncodeUInt(t *testing.T) { }, }, { - "LargerThanMaxUInt256", - cadence.UInt{Value: new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))}, - []byte{ // language=json, format=json-cdc + name: "LargerThanMaxUInt256", + val: cadence.UInt{Value: new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))}, + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} // // language=edn, format=ccf @@ -1852,9 +1853,9 @@ func TestEncodeUInt8(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt8(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt8(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt8","value":"0"} // // language=edn, format=ccf @@ -1874,9 +1875,9 @@ func TestEncodeUInt8(t *testing.T) { }, }, { - "Max", - cadence.NewUInt8(math.MaxUint8), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewUInt8(math.MaxUint8), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt8","value":"255"} // // language=edn, format=ccf @@ -1904,9 +1905,9 @@ func TestEncodeUInt16(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt16(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt16(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt16","value":"0"} // // language=edn, format=ccf @@ -1926,9 +1927,9 @@ func TestEncodeUInt16(t *testing.T) { }, }, { - "Max", - cadence.NewUInt16(math.MaxUint16), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewUInt16(math.MaxUint16), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt16","value":"65535"} // // language=edn, format=ccf @@ -1956,9 +1957,9 @@ func TestEncodeUInt32(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt32(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt32(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt32","value":"0"} // // language=edn, format=ccf @@ -1978,9 +1979,9 @@ func TestEncodeUInt32(t *testing.T) { }, }, { - "Max", - cadence.NewUInt32(math.MaxUint32), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewUInt32(math.MaxUint32), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt32","value":"4294967295"} // // language=edn, format=ccf @@ -2008,9 +2009,9 @@ func TestEncodeUInt64(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt64(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt64(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt64","value":"0"} // // language=edn, format=ccf @@ -2030,9 +2031,9 @@ func TestEncodeUInt64(t *testing.T) { }, }, { - "Max", - cadence.NewUInt64(uint64(math.MaxUint64)), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewUInt64(uint64(math.MaxUint64)), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt64","value":"18446744073709551615"} // // language=edn, format=ccf @@ -2060,9 +2061,9 @@ func TestEncodeUInt128(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt128(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt128(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt128","value":"0"} // // language=edn, format=ccf @@ -2084,9 +2085,9 @@ func TestEncodeUInt128(t *testing.T) { }, }, { - "Max", - cadence.UInt128{Value: sema.UInt128TypeMaxIntBig}, - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.UInt128{Value: sema.UInt128TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt128","value":"340282366920938463463374607431768211455"} // // language=edn, format=ccf @@ -2119,9 +2120,9 @@ func TestEncodeUInt256(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewUInt256(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewUInt256(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt256","value":"0"} // // language=edn, format=ccf @@ -2143,9 +2144,9 @@ func TestEncodeUInt256(t *testing.T) { }, }, { - "Max", - cadence.UInt256{Value: sema.UInt256TypeMaxIntBig}, - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.UInt256{Value: sema.UInt256TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc // {"type":"UInt256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"} // // language=edn, format=ccf @@ -2180,9 +2181,9 @@ func TestEncodeWord8(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewWord8(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewWord8(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word8","value":"0"} // // language=edn, format=ccf @@ -2202,9 +2203,9 @@ func TestEncodeWord8(t *testing.T) { }, }, { - "Max", - cadence.NewWord8(math.MaxUint8), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewWord8(math.MaxUint8), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word8","value":"255"} // // language=edn, format=ccf @@ -2232,9 +2233,9 @@ func TestEncodeWord16(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewWord16(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewWord16(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word16","value":"0"} // // language=edn, format=ccf @@ -2254,9 +2255,9 @@ func TestEncodeWord16(t *testing.T) { }, }, { - "Max", - cadence.NewWord16(math.MaxUint16), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewWord16(math.MaxUint16), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word16","value":"65535"} // // language=edn, format=ccf @@ -2284,9 +2285,9 @@ func TestEncodeWord32(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewWord32(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewWord32(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word32","value":"0"} // // language=edn, format=ccf @@ -2306,9 +2307,9 @@ func TestEncodeWord32(t *testing.T) { }, }, { - "Max", - cadence.NewWord32(math.MaxUint32), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewWord32(math.MaxUint32), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word32","value":"4294967295"} // // language=edn, format=ccf @@ -2336,9 +2337,9 @@ func TestEncodeWord64(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.NewWord64(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.NewWord64(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word64","value":"0"} // // language=edn, format=ccf @@ -2358,9 +2359,9 @@ func TestEncodeWord64(t *testing.T) { }, }, { - "Max", - cadence.NewWord64(math.MaxUint64), - []byte{ // language=json, format=json-cdc + name: "Max", + val: cadence.NewWord64(math.MaxUint64), + expected: []byte{ // language=json, format=json-cdc // {"type":"Word64","value":"18446744073709551615"} // // language=edn, format=ccf @@ -2388,9 +2389,9 @@ func TestEncodeFix64(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.Fix64(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.Fix64(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"0.00000000"} // // language=edn, format=ccf @@ -2410,9 +2411,9 @@ func TestEncodeFix64(t *testing.T) { }, }, { - "789.00123010", - cadence.Fix64(78_900_123_010), - []byte{ // language=json, format=json-cdc + name: "789.00123010", + val: cadence.Fix64(78_900_123_010), + expected: []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"789.00123010"} // // language=edn, format=ccf @@ -2432,9 +2433,9 @@ func TestEncodeFix64(t *testing.T) { }, }, { - "1234.056", - cadence.Fix64(123_405_600_000), - []byte{ // language=json, format=json-cdc + name: "1234.056", + val: cadence.Fix64(123_405_600_000), + expected: []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"1234.05600000"} // // language=edn, format=ccf @@ -2454,9 +2455,9 @@ func TestEncodeFix64(t *testing.T) { }, }, { - "-12345.006789", - cadence.Fix64(-1_234_500_678_900), - []byte{ // language=json, format=json-cdc + name: "-12345.006789", + val: cadence.Fix64(-1_234_500_678_900), + expected: []byte{ // language=json, format=json-cdc // {"type":"Fix64","value":"-12345.00678900"} // // language=edn, format=ccf @@ -2484,9 +2485,9 @@ func TestEncodeUFix64(t *testing.T) { testAllEncodeAndDecode(t, []encodeTest{ { - "Zero", - cadence.UFix64(0), - []byte{ // language=json, format=json-cdc + name: "Zero", + val: cadence.UFix64(0), + expected: []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"0.00000000"} // // language=edn, format=ccf @@ -2506,9 +2507,9 @@ func TestEncodeUFix64(t *testing.T) { }, }, { - "789.00123010", - cadence.UFix64(78_900_123_010), - []byte{ // language=json, format=json-cdc + name: "789.00123010", + val: cadence.UFix64(78_900_123_010), + expected: []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"789.00123010"} // // language=edn, format=ccf @@ -2528,9 +2529,9 @@ func TestEncodeUFix64(t *testing.T) { }, }, { - "1234.056", - cadence.UFix64(123_405_600_000), - []byte{ // language=json, format=json-cdc + name: "1234.056", + val: cadence.UFix64(123_405_600_000), + expected: []byte{ // language=json, format=json-cdc // {"type":"UFix64","value":"1234.05600000"} // // language=edn, format=ccf @@ -2558,11 +2559,11 @@ func TestEncodeArray(t *testing.T) { // [] emptyArray := encodeTest{ - "Empty", - cadence.NewArray( + name: "Empty", + val: cadence.NewArray( []cadence.Value{}, ).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[]} // // language=edn, format=ccf @@ -2588,13 +2589,13 @@ func TestEncodeArray(t *testing.T) { // constant sized array [1, 2, 3] constantSizedIntArray := encodeTest{ - "Constant-sized Integers", - cadence.NewArray([]cadence.Value{ + name: "Constant-sized Integers", + val: cadence.NewArray([]cadence.Value{ cadence.NewInt(1), cadence.NewInt(2), cadence.NewInt(3), }).WithType(cadence.NewConstantSizedArrayType(3, cadence.NewIntType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} // // language=edn, format=ccf @@ -2642,13 +2643,13 @@ func TestEncodeArray(t *testing.T) { // [1, 2, 3] intArray := encodeTest{ - "Integers", - cadence.NewArray([]cadence.Value{ + name: "Integers", + val: cadence.NewArray([]cadence.Value{ cadence.NewInt(1), cadence.NewInt(2), cadence.NewInt(3), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} // // language=edn, format=ccf @@ -2690,10 +2691,74 @@ func TestEncodeArray(t *testing.T) { }, } + // [[1], [2], [3]] + nestedArray := encodeTest{ + name: "Nested", + val: cadence.NewArray([]cadence.Value{ + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + cadence.NewArray([]cadence.Value{ + cadence.NewInt(2), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + cadence.NewArray([]cadence.Value{ + cadence.NewInt(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewVariableSizedArrayType(cadence.NewIntType()))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":[{"value":"1","type":"Int"}],"type":"Array"},{"value":[{"value":"2","type":"Int"}],"type":"Array"},{"value":[{"value":"3","type":"Int"}],"type":"Array"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(139(137(4))), [[1], [2], [3]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type [[]int] + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + // [S.test.Foo{1}, S.test.Foo{2}, S.test.Foo{3}] resourceArray := encodeTest{ - "Resources", - cadence.NewArray([]cadence.Value{ + name: "Resources", + val: cadence.NewArray([]cadence.Value{ cadence.NewResource([]cadence.Value{ cadence.NewInt(1), }).WithType(fooResourceType), @@ -2704,7 +2769,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(3), }).WithType(fooResourceType), }).WithType(cadence.NewVariableSizedArrayType(fooResourceType)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}]} // // language=edn, format=ccf @@ -2792,8 +2857,8 @@ func TestEncodeArray(t *testing.T) { require.NoError(t, err) resourceWithAbstractFieldArray := encodeTest{ - "Resources with abstract field", - cadence.NewArray([]cadence.Value{ + name: "Resources with abstract field", + val: cadence.NewArray([]cadence.Value{ cadence.NewResource([]cadence.Value{ cadence.NewInt(1), cadence.NewInt(1), // field is AnyStruct type @@ -2807,7 +2872,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewBool(true), // field is AnyStruct type }).WithType(foooResourceTypeWithAbstractField), }).WithType(cadence.NewVariableSizedArrayType(foooResourceTypeWithAbstractField)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}},{"name":"baz","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}},{"name":"baz","value":{"type":"String","value":"a"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}},{"name":"baz","value":{"type":"Bool","value":true}}]}}]} // // language=edn, format=ccf @@ -2943,13 +3008,13 @@ func TestEncodeArray(t *testing.T) { // [1, "a", true] heterogeneousSimpleTypeArray := encodeTest{ - "Heterogenous AnyStruct Array with Simple Values", - cadence.NewArray([]cadence.Value{ + name: "Heterogenous AnyStruct Array with Simple Values", + val: cadence.NewArray([]cadence.Value{ cadence.NewInt(1), s, cadence.NewBool(true), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"String","value":"a"},{"type":"Bool","value":true}]} // // language=edn, format=ccf @@ -3014,13 +3079,13 @@ func TestEncodeArray(t *testing.T) { // [Int8(1), Int16(2), Int32(3)] heterogeneousNumberTypeArray := encodeTest{ - "Heterogeous Number Array", - cadence.NewArray([]cadence.Value{ + name: "Heterogeous Number Array", + val: cadence.NewArray([]cadence.Value{ cadence.NewInt8(1), cadence.NewInt16(2), cadence.NewInt32(3), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewNumberType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":"1","type":"Int8"},{"value":"2","type":"Int16"},{"value":"3","type":"Int32"}],"type":"Array"} // // language=edn, format=ccf @@ -3079,14 +3144,14 @@ func TestEncodeArray(t *testing.T) { // [1, S.test.Foo{1}] heterogeneousCompositeTypeArray := encodeTest{ - "Heterogenous AnyStruct Array with Composite Value", - cadence.NewArray([]cadence.Value{ + name: "Heterogenous AnyStruct Array with Composite Value", + val: cadence.NewArray([]cadence.Value{ cadence.NewInt(1), cadence.NewResource([]cadence.Value{ cadence.NewInt(1), }).WithType(fooResourceType), }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":"1","type":"Int"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"}],"type":"Array"} // // language=edn, format=ccf @@ -3187,8 +3252,8 @@ func TestEncodeArray(t *testing.T) { // [S.test.Foo{1}, S.test.Fooo{2, "a"}] resourceInterfaceTypeArray := encodeTest{ - "Resource Interface Array", - cadence.NewArray([]cadence.Value{ + name: "Resource Interface Array", + val: cadence.NewArray([]cadence.Value{ cadence.NewResource([]cadence.Value{ cadence.NewInt(1), }).WithType(fooResourceType), @@ -3197,7 +3262,7 @@ func TestEncodeArray(t *testing.T) { s, }).WithType(foooResourceTypeWithAbstractField), }).WithType(cadence.NewVariableSizedArrayType(resourceInterfaceType)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},{"value":{"id":"S.test.Fooo","fields":[{"value":{"value":"2","type":"Int"},"name":"bar"},{"value":{"value":"a","type":"String"},"name":"baz"}]},"type":"Resource"}],"type":"Array"} // // language=edn, format=ccf @@ -3373,8 +3438,8 @@ func TestEncodeArray(t *testing.T) { // [S.test.FooStruct{"a", S.test.Foo{0}}, S.test.FooStruct{"b", S.test.Foo{1}}] resourceStructArray := encodeTest{ - "Resource Struct Array", - cadence.NewArray([]cadence.Value{ + name: "Resource Struct Array", + val: cadence.NewArray([]cadence.Value{ cadence.NewStruct([]cadence.Value{ cadence.String("a"), cadence.NewResource([]cadence.Value{ @@ -3388,7 +3453,7 @@ func TestEncodeArray(t *testing.T) { }).WithType(fooResourceType), }).WithType(resourceStructType), }).WithType(cadence.NewVariableSizedArrayType(resourceStructType)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"a","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"0","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"}],"type":"Array"} // // language=edn, format=ccf @@ -3541,8 +3606,8 @@ func TestEncodeArray(t *testing.T) { // [S.test.FooStruct{1}, S.test.FooStruct{2}] structInterfaceTypeArray := encodeTest{ - "Struct Interface Array", - cadence.NewArray([]cadence.Value{ + name: "Struct Interface Array", + val: cadence.NewArray([]cadence.Value{ cadence.NewStruct([]cadence.Value{ cadence.NewInt(1), }).WithType(structType), @@ -3550,7 +3615,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(2), }).WithType(structType), }).WithType(cadence.NewVariableSizedArrayType(structInterfaceType)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Struct"}],"type":"Array"} // // language=edn, format=ccf @@ -3682,8 +3747,8 @@ func TestEncodeArray(t *testing.T) { // [S.test.FooContract{1}, S.test.FooContract{2}] contractInterfaceTypeArray := encodeTest{ - "Contract Interface Array", - cadence.NewArray([]cadence.Value{ + name: "Contract Interface Array", + val: cadence.NewArray([]cadence.Value{ cadence.NewContract([]cadence.Value{ cadence.NewInt(1), }).WithType(contractType), @@ -3691,7 +3756,7 @@ func TestEncodeArray(t *testing.T) { cadence.NewInt(2), }).WithType(contractType), }).WithType(cadence.NewVariableSizedArrayType(contractInterfaceType)), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Contract"},{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Contract"}],"type":"Array"} // // language=edn, format=ccf @@ -3803,10 +3868,12 @@ func TestEncodeArray(t *testing.T) { 0x02, }, } + testAllEncodeAndDecode(t, emptyArray, constantSizedIntArray, intArray, + nestedArray, resourceStructArray, resourceArray, resourceWithAbstractFieldArray, @@ -3823,12 +3890,13 @@ func TestEncodeDictionary(t *testing.T) { t.Parallel() + // {} emptyDict := encodeTest{ - "empty", - cadence.NewDictionary( + name: "empty", + val: cadence.NewDictionary( []cadence.KeyValuePair{}, ).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":[],"type":"Dictionary"} // // language=edn, format=ccf @@ -3857,10 +3925,24 @@ func TestEncodeDictionary(t *testing.T) { }, } - // {"a":1, "b":2, "c":3} + // {"c":3, "b":2, "a":1} simpleDict := encodeTest{ - "Simple", - cadence.NewDictionary([]cadence.KeyValuePair{ + name: "Simple", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ { Key: cadence.String("a"), Value: cadence.NewInt(1), @@ -3874,7 +3956,7 @@ func TestEncodeDictionary(t *testing.T) { Value: cadence.NewInt(3), }, }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} // // language=edn, format=ccf @@ -3934,10 +4016,42 @@ func TestEncodeDictionary(t *testing.T) { }, } - // {"a":{"1":1}, "b":{"2":2}, "c":{"3:3"}} + // {"c":{"3:3"}, "b":{"2":2}, "a":{"1":1}} nestedDict := encodeTest{ - "Nested", - cadence.NewDictionary([]cadence.KeyValuePair{ + name: "Nested", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("3"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("b"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("2"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("a"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("1"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + ), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ { Key: cadence.String("a"), Value: cadence.NewDictionary([]cadence.KeyValuePair{ @@ -3969,7 +4083,7 @@ func TestEncodeDictionary(t *testing.T) { cadence.NewStringType(), cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), ), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"1"},"value":{"type":"Int","value":"1"}}]}},{"key":{"type":"String","value":"b"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"2"},"value":{"type":"Int","value":"2"}}]}},{"key":{"type":"String","value":"c"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"3"},"value":{"type":"Int","value":"3"}}]}}]} // // language=edn, format=ccf @@ -4058,16 +4172,40 @@ func TestEncodeDictionary(t *testing.T) { }, } - // {"a":foo{1}, "b":foo{2}, "c":foo{3}} + // {"c":foo{3}, "b":foo{2}, "a":foo{1}} resourceDict := encodeTest{ - "Resources", - cadence.NewDictionary([]cadence.KeyValuePair{ + name: "Resources", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("b"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + }, { Key: cadence.String("a"), Value: cadence.NewResource([]cadence.Value{ cadence.NewInt(1), }).WithType(fooResourceType), }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + fooResourceType, + )), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }, + { Key: cadence.String("b"), Value: cadence.NewResource([]cadence.Value{ @@ -4084,7 +4222,7 @@ func TestEncodeDictionary(t *testing.T) { cadence.NewStringType(), fooResourceType, )), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}}},{"key":{"type":"String","value":"b"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}}},{"key":{"type":"String","value":"c"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}}]} // // language=edn, format=ccf @@ -4185,11 +4323,151 @@ func TestEncodeDictionary(t *testing.T) { }, } + // {"c":3, 0:1, true:3} + heterogeneousSimpleTypeDict := encodeTest{ + name: "heterogeneous simple type", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.NewInt(0), + Value: cadence.NewInt(1), + }, + { + Key: cadence.NewBool(true), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewAnyStructType(), cadence.NewAnyStructType())), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.NewBool(true), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.NewInt(0), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewAnyStructType(), cadence.NewAnyStructType())), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"key":{"value":"c","type":"String"},"value":{"value":"2","type":"Int"}},{"key":{"value":"0","type":"Int"},"value":{"value":"1","type":"Int"}},{"key":{"value":true,"type":"Bool"},"value":{"value":"3","type":"Int"}}],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(39), 137(39)]), [130([137(0), true]), 130([137(4), 3]), 130([137(1), "c"]), 130([137(4), 2]), 130([137(4), 0]), 130([137(4), 1])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[AnyStruct]AnyStruct) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text, 1 byte follows + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + testAllEncodeAndDecode(t, emptyDict, simpleDict, nestedDict, resourceDict, + heterogeneousSimpleTypeDict, + //heterogeneousNumberTypeDict, + //heterogeneousCompositeTypeDict, ) } @@ -4787,14 +5065,14 @@ func TestEncodeStruct(t *testing.T) { } simpleStruct := encodeTest{ - "Simple", - cadence.NewStruct( + name: "Simple", + val: cadence.NewStruct( []cadence.Value{ cadence.NewInt(1), cadence.String("foo"), }, ).WithType(simpleStructType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -4873,8 +5151,8 @@ func TestEncodeStruct(t *testing.T) { } resourceStruct := encodeTest{ - "Resources", - cadence.NewStruct( + name: "Resources", + val: cadence.NewStruct( []cadence.Value{ cadence.String("foo"), cadence.NewResource( @@ -4884,7 +5162,7 @@ func TestEncodeStruct(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceStructType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -5024,14 +5302,14 @@ func TestEncodeEvent(t *testing.T) { } simpleEvent := encodeTest{ - "Simple", - cadence.NewEvent( + name: "Simple", + val: cadence.NewEvent( []cadence.Value{ cadence.NewInt(1), cadence.String("foo"), }, ).WithType(simpleEventType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -5125,8 +5403,8 @@ func TestEncodeEvent(t *testing.T) { } resourceEvent := encodeTest{ - "Resources", - cadence.NewEvent( + name: "Resources", + val: cadence.NewEvent( []cadence.Value{ cadence.String("foo"), cadence.NewResource( @@ -5136,7 +5414,7 @@ func TestEncodeEvent(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceEventType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -5275,14 +5553,14 @@ func TestEncodeContract(t *testing.T) { } simpleContract := encodeTest{ - "Simple", - cadence.NewContract( + name: "Simple", + val: cadence.NewContract( []cadence.Value{ cadence.NewInt(1), cadence.String("foo"), }, ).WithType(simpleContractType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} // // language=edn, format=ccf @@ -5376,8 +5654,8 @@ func TestEncodeContract(t *testing.T) { } resourceContract := encodeTest{ - "Resources", - cadence.NewContract( + name: "Resources", + val: cadence.NewContract( []cadence.Value{ cadence.String("foo"), cadence.NewResource( @@ -5387,7 +5665,7 @@ func TestEncodeContract(t *testing.T) { ).WithType(fooResourceType), }, ).WithType(resourceContractType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} // // language=edn, format=ccf @@ -5521,13 +5799,13 @@ func TestEncodeEnum(t *testing.T) { } simpleEnum := encodeTest{ - "Simple", - cadence.NewEnum( + name: "Simple", + val: cadence.NewEnum( []cadence.Value{ cadence.NewUInt8(1), }, ).WithType(simpleEnumType), - []byte{ // language=json, format=json-cdc + expected: []byte{ // language=json, format=json-cdc // {"value":{"id":"S.test.FooEnum","fields":[{"value":{"value":"1","type":"UInt8"},"name":"raw"}]},"type":"Enum"} // // language=edn, format=ccf @@ -7643,7 +7921,11 @@ func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { t.Parallel() - testEncodeAndDecode(t, testCase.val, testCase.expected) + if testCase.expectedVal == nil { + testEncodeAndDecode(t, testCase.val, testCase.expected) + } else { + testEncodeAndDecodeEx(t, testCase.val, testCase.expected, testCase.expectedVal) + } }) } From f7c2d9f54dcb0bf34dd65e7e09fd40ceb0c9fc00 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:21:50 -0500 Subject: [PATCH 041/173] Add more CCF tests --- encoding/ccf/ccf_test.go | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 4b6507abc9..3bccb1a7ee 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7111,6 +7111,70 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with static multiple restricted type", func(t *testing.T) { + + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.NewAnyStructType(), + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }).WithID("Int{AnyStruct, String}"), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191(["Int{AnyStruct, String}", 185(4), [185(1), 185(39)]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 3 elements follow + 0x83, + // ID + // string, 22 bytes follow + 0x76, + // Int{AnyStruct, String} + 0x49, 0x6e, 0x74, 0x7b, 0x41, 0x6e, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2c, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x7d, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type ID (39) + 0x18, 0x27, + }, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + cadence.NewAnyStructType(), + }, + Type: cadence.IntType{}, + }).WithID("Int{AnyStruct, String}"), + }, + ) + + }) t.Run("without static type", func(t *testing.T) { t.Parallel() From 91fba701a982159655ef836299ead4efc5077c57 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 23 Mar 2023 16:28:08 -0500 Subject: [PATCH 042/173] Add ccf test for value of restricted type --- encoding/ccf/ccf_test.go | 228 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 3bccb1a7ee..3d6f9e9f13 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5867,6 +5867,234 @@ func TestEncodeEnum(t *testing.T) { testAllEncodeAndDecode(t, simpleEnum) } +func TestEncodeValueOfRestrictedType(t *testing.T) { + hasCountInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasCount", + nil, + nil, + ) + + hasSumInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasSum", + nil, + nil, + ) + + statsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("count", cadence.NewIntType()), + cadence.NewField("sum", cadence.NewIntType()), + }, + nil, + ) + + countSumRestrictedType := cadence.NewRestrictedType( + "Stats{HasCount, HasSum}", + statsType, + []cadence.Type{ + hasCountInterfaceType, + hasSumInterfaceType, + }, + ) + + val := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + }, + ).WithType(statsType), + }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) + + expectedStatsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("sum", cadence.NewIntType()), + cadence.NewField("count", cadence.NewIntType()), + }, + nil, + ) + + expectedCountSumRestrictedType := cadence.NewRestrictedType( + "Stats{HasCount, HasSum}", + expectedStatsType, + []cadence.Type{ + hasSumInterfaceType, + hasCountInterfaceType, + }, + ) + + expectedVal := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(2), + cadence.NewInt(1), + }, + ).WithType(expectedStatsType), + }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + + testEncodeAndDecodeEx( + t, + val, + []byte{ + // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"sum"},{"value":{"value":"2","type":"Int"},"name":"count"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143(["Stats{HasCount, HasSum}", 136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Stats" + // 2 fields: [["sum", type(int)], ["count", type(int)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x6c, + // S.test.Stats + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // sum + 0x73, 0x75, 0x6d, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 5 bytes follow + 0x65, + // count + 0x63, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // resource interface type: + // id: []byte{1} + // cadence-type-id: "S.test.HasSum" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 13 bytes follow + 0x6d, + // S.test.HasSum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, + // resource interface type: + // id: []byte{2} + // cadence-type-id: "S.test.HasCount" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.HasCount + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 3 items follow + 0x83, + // cadence type id + // text, 23 bytes follow + 0x77, + // Stats{HasCount, HasSum} + 0x53, 0x74, 0x61, 0x74, 0x73, 0x7b, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, 0x7d, + // type + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + }, + expectedVal, + ) +} + func TestEncodeSimpleTypes(t *testing.T) { t.Parallel() From 7a1b3f52bdd1ce36504e0e0222ec34d656ad430c Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 23 Mar 2023 19:58:46 -0500 Subject: [PATCH 043/173] Remove cadence-type-id from restricted-type(-value) In JSON-CDC, sometimes cadence-type-id was encoded when it was just a "stringification" of other encoded data and not necessary to encode. In CCF, we don't need to keep this inefficiency for the sake of compatibility. This commit removes cadence-type-id from restricted-type and restricted-type-value. It also modifies RestrictedType to generate ID if it is empty. Generated ID is stringification of its type and restrictions. --- encoding/ccf/ccf_test.go | 42 ++++++++++++------------------------ encoding/ccf/decode_type.go | 18 +++++----------- encoding/ccf/encode.go | 1 - encoding/ccf/encode_type.go | 15 ++++--------- types.go | 21 ++++++++++++++++++ types_test.go | 43 +++++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 53 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 3d6f9e9f13..9cafe5ebb5 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5893,7 +5893,7 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { ) countSumRestrictedType := cadence.NewRestrictedType( - "Stats{HasCount, HasSum}", + "", statsType, []cadence.Type{ hasCountInterfaceType, @@ -5921,7 +5921,7 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { ) expectedCountSumRestrictedType := cadence.NewRestrictedType( - "Stats{HasCount, HasSum}", + "", expectedStatsType, []cadence.Type{ hasSumInterfaceType, @@ -5946,7 +5946,7 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"sum"},{"value":{"value":"2","type":"Int"},"name":"count"}]},"type":"Resource"}],"type":"Array"} // // language=edn, format=ccf - // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143(["Stats{HasCount, HasSum}", 136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) // // language=cbor, format=ccf // tag @@ -6039,13 +6039,8 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { 0xd8, ccf.CBORTagVarsizedArrayType, // tag 0xd8, ccf.CBORTagRestrictedType, - // array, 3 items follow - 0x83, - // cadence type id - // text, 23 bytes follow - 0x77, - // Stats{HasCount, HasSum} - 0x53, 0x74, 0x61, 0x74, 0x73, 0x7b, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, 0x7d, + // array, 2 items follow + 0x82, // type // tag 0xd8, ccf.CBORTagTypeRef, @@ -7304,7 +7299,7 @@ func TestEncodeType(t *testing.T) { // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } // // language=edn, format=ccf - // 130([137(41), 191(["Int{String}", 185(4), [185(1)]])]) + // 130([137(41), 191([185(4), [185(1)]])]) // // language=cbor, format=ccf // tag @@ -7317,13 +7312,8 @@ func TestEncodeType(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagRestrictedTypeValue, - // array, 3 elements follow - 0x83, - // ID - // string, 11 bytes follow - 0x6b, - // Int{String} - 0x49, 0x6e, 0x74, 0x7b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x7d, + // array, 2 elements follow + 0x82, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type ID (4) @@ -7356,7 +7346,7 @@ func TestEncodeType(t *testing.T) { // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 191(["Int{AnyStruct, String}", 185(4), [185(1), 185(39)]])]) + // 130([137(41), 191([185(4), [185(1), 185(39)]])]) // // language=cbor, format=ccf // tag @@ -7369,13 +7359,8 @@ func TestEncodeType(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagRestrictedTypeValue, - // array, 3 elements follow - 0x83, - // ID - // string, 22 bytes follow - 0x76, - // Int{AnyStruct, String} - 0x49, 0x6e, 0x74, 0x7b, 0x41, 0x6e, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2c, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x7d, + // array, 2 elements follow + 0x82, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type ID (4) @@ -7391,6 +7376,7 @@ func TestEncodeType(t *testing.T) { // AnyStruct type ID (39) 0x18, 0x27, }, + // Expected decoded val with sorted restrictions and type ID. cadence.TypeValue{ StaticType: (&cadence.RestrictedType{ Restrictions: []cadence.Type{ @@ -7398,11 +7384,11 @@ func TestEncodeType(t *testing.T) { cadence.NewAnyStructType(), }, Type: cadence.IntType{}, - }).WithID("Int{AnyStruct, String}"), + }).WithID("Int{String, AnyStruct}"), }, ) - }) + t.Run("without static type", func(t *testing.T) { t.Parallel() diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index cf2da44780..2e37f07bcb 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -480,7 +480,6 @@ func (d *Decoder) decodeReferenceType( // // ; cbor-tag-restricted-type // #6.143([ -// cadence-type-id: cadence-type-id, // type: inline-type, // restrictions: [* inline-type] // ]) @@ -489,7 +488,6 @@ func (d *Decoder) decodeReferenceType( // // ; cbor-tag-restricted-type-value // #6.191([ -// cadence-type-id: cadence-type-id, // type: type-value, // restrictions: [* type-value] // ]) @@ -499,25 +497,19 @@ func (d *Decoder) decodeRestrictedType( types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { - // Decode array of length 3. - err := decodeCBORArrayWithKnownSize(d.dec, 3) - if err != nil { - return nil, err - } - - // element 0: cadence-type-id - typeID, err := d.dec.DecodeString() + // Decode array of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { return nil, err } - // element 1: type + // element 0: type typ, err := decodeTypeFn(types) if err != nil { return nil, err } - // element 2: restrictions + // element 1: restrictions restrictionCount, err := d.dec.DecodeArrayHead() if err != nil { return nil, err @@ -562,7 +554,7 @@ func (d *Decoder) decodeRestrictedType( "", typ, restrictions, - ).WithID(typeID), nil + ), nil } // decodeCCFTypeID decodes encoded id as diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 41a292a7e2..ced3d15d38 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1252,7 +1252,6 @@ func (e *Encoder) encodeReferenceTypeValue(typ *cadence.ReferenceType, visited c // // ; cbor-tag-restricted-type-value // #6.191([ -// cadence-type-id: cadence-type-id, // type: type-value, // restrictions: [* type-value] // ]) diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 0d1d5020c9..e1abc5f3ba 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -347,7 +347,6 @@ func (e *Encoder) encodeReferenceTypeWithRawTag( // // ; cbor-tag-restricted-type // #6.143([ -// cadence-type-id: cadence-type-id, // type: inline-type, // restrictions: [* inline-type] // ]) @@ -375,25 +374,19 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( return err } - // Encode array head of length 3. - err = e.enc.EncodeArrayHead(3) - if err != nil { - return err - } - - // element 0: cadence type id. - err = e.encodeCadenceTypeID(typ.ID()) + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) if err != nil { return err } - // element 1: type with given encodeTypeFn + // element 0: type with given encodeTypeFn err = encodeTypeFn(typ.Type, tids) if err != nil { return err } - // element 2: restrictions as array. + // element 1: restrictions as array. // Encode array head with number of restrictions. restrictions := typ.Restrictions diff --git a/types.go b/types.go index dab86a4d3e..26df1fced1 100644 --- a/types.go +++ b/types.go @@ -20,6 +20,7 @@ package cadence import ( "fmt" + "strings" "sync" "github.com/onflow/cadence/runtime/common" @@ -1843,9 +1844,29 @@ func NewMeteredRestrictedType( func (*RestrictedType) isType() {} func (t *RestrictedType) ID() string { + if t.typeID == "" { + t.typeID = t.id() + } return t.typeID } +func (t *RestrictedType) id() string { + typeID := t.Type.ID() + + switch len(t.Restrictions) { + case 0: + return typeID + "{}" + case 1: + return typeID + "{" + t.Restrictions[0].ID() + "}" + default: + restrictions := make([]string, len(t.Restrictions)) + for i, restriction := range t.Restrictions { + restrictions[i] = restriction.ID() + } + return typeID + "{" + strings.Join(restrictions, ", ") + "}" + } +} + func (t *RestrictedType) WithID(id string) *RestrictedType { t.typeID = id return t diff --git a/types_test.go b/types_test.go index a04bacfad5..b87fc76be7 100644 --- a/types_test.go +++ b/types_test.go @@ -172,6 +172,49 @@ func TestType_ID(t *testing.T) { (&RestrictedType{}).WithID("S.test.Foo{S.test.FooI}"), "S.test.Foo{S.test.FooI}", }, + { + (&RestrictedType{ + Type: &StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + }, + }), + "S.test.Foo{}", + }, + { + (&RestrictedType{ + Type: &StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + }, + Restrictions: []Type{ + &StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooI", + }, + }, + }), + "S.test.Foo{S.test.FooI}", + }, + { + (&RestrictedType{ + Type: &StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + }, + Restrictions: []Type{ + &StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooI", + }, + &StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooII", + }, + }, + }), + "S.test.Foo{S.test.FooI, S.test.FooII}", + }, { &EventType{ QualifiedIdentifier: "Event", From 54c69875633b42577b942a5ecbeba0b687b35c22 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:43:20 -0500 Subject: [PATCH 044/173] Remove cadence-type-id from function-value In JSON-CDC, sometimes cadence-type-id was encoded when it was just a "stringification" of other encoded data and not necessary to encode. In CCF, we don't need to keep this inefficiency for the sake of compatibility. This commit removes cadence-type-id from function-value, which is also used by function-type-value. It also modifies FunctionType to generate ID if it is empty. Generated ID is stringification of its parameter types and return type. --- encoding/ccf/ccf_test.go | 25 +++++++-------------- encoding/ccf/decode.go | 17 +++++---------- encoding/ccf/encode.go | 17 +++++---------- types.go | 20 +++++++++++++++++ types_test.go | 47 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 41 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 9cafe5ebb5..6c36605c0f 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7197,13 +7197,13 @@ func TestEncodeType(t *testing.T) { {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, ReturnType: cadence.IntType{}, - }).WithID("Foo"), + }).WithID("((String):Int)"), }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"Foo", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } + // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"((String):Int)", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } // // language=edn, format=ccf - // 130([137(41), 193(["Foo", [["qux", "baz", 185(1)]], 185(4)])]) + // 130([137(41), 193([[["qux", "baz", 185(1)]], 185(4)])]) // // language=cbor, format=ccf // tag @@ -7216,12 +7216,8 @@ func TestEncodeType(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagFunctionTypeValue, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // Foo - 0x46, 0x6f, 0x6f, + // array, 2 elements follow + 0x82, // array, 1 elements follow 0x81, // array, 3 elements follow @@ -8821,7 +8817,7 @@ func TestExportFunctionValue(t *testing.T) { // { "type": "Function", "value": { "functionType": { "kind": "Function", "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" } } } } // // language=edn, format=ccf - // 130([137(51), ["(():Void)", [], 185(50)]]) + // 130([137(51), [[], 185(50)]]) // // language=cbor, format=ccf // tag @@ -8832,13 +8828,8 @@ func TestExportFunctionValue(t *testing.T) { 0xd8, ccf.CBORTagSimpleType, // Function type ID (51) 0x18, 0x33, - // array, 3 elements follow - 0x83, - // element 0: cadence-type-id - // string, 9 bytes follow - 0x69, - // (():Void) - 0x28, 0x28, 0x29, 0x3a, 0x56, 0x6f, 0x69, 0x64, 0x29, + // array, 2 elements follow + 0x82, // element 1: parameters // array, 0 element 0x80, diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index d056327df1..cdc4e1cf28 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1793,7 +1793,6 @@ func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cade // language=CDDL // function-value = [ // -// cadence-type-id: cadence-type-id, // parameters: [ // * [ // label: tstr, @@ -1805,25 +1804,19 @@ func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cade // // ] func (d *Decoder) decodeFunctionTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { - // Decode array head of length 3 - err := decodeCBORArrayWithKnownSize(d.dec, 3) - if err != nil { - return nil, err - } - - // element 0: cadence-type-id - typeID, err := d.dec.DecodeString() + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { return nil, err } - // element 1: parameters + // element 0: parameters parameters, err := d.decodeParameterTypeValues(visited) if err != nil { return nil, err } - // element 2: return-type + // element 1: return-type returnType, err := d._decodeTypeValue(visited) if err != nil { return nil, err @@ -1834,7 +1827,7 @@ func (d *Decoder) decodeFunctionTypeValue(visited cadenceTypeByCCFTypeID) (caden "", parameters, returnType, - ).WithID(typeID), nil + ), nil } func decodeCBORArrayWithKnownSize(dec *cbor.StreamDecoder, n uint64) error { diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index ced3d15d38..bd5138cea9 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -994,7 +994,6 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { // language=CDDL // function-value = [ // -// cadence-type-id: cadence-type-id, // parameters: [ // * [ // label: tstr, @@ -1006,28 +1005,22 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { // // ] func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { - // Encode array head of length 3. + // Encode array head of length 2. err := e.enc.EncodeRawBytes([]byte{ - // array, 3 items follow - 0x83, + // array, 2 items follow + 0x82, }) if err != nil { return err } - // element 0: cadence-type-id as tstr. - err = e.encodeCadenceTypeID(typ.ID()) - if err != nil { - return err - } - - // element 1: parameters as array. + // element 0: parameters as array. err = e.encodeParameterTypeValues(typ.Parameters, visited) if err != nil { return err } - // element 2: return type as type-value. + // element 1: return type as type-value. return e.encodeTypeValue(typ.ReturnType, visited) } diff --git a/types.go b/types.go index 26df1fced1..a2d2d68836 100644 --- a/types.go +++ b/types.go @@ -1727,9 +1727,29 @@ func NewMeteredFunctionType( func (*FunctionType) isType() {} func (t *FunctionType) ID() string { + if t.typeID == "" { + t.typeID = t.id() + } return t.typeID } +func (t *FunctionType) id() string { + returnTypeID := t.ReturnType.ID() + + switch len(t.Parameters) { + case 0: + return "(():" + returnTypeID + ")" + case 1: + return "((" + t.Parameters[0].Type.ID() + "):" + returnTypeID + ")" + default: + params := make([]string, len(t.Parameters)) + for i, param := range t.Parameters { + params[i] = param.Type.ID() + } + return "((" + strings.Join(params, ", ") + "):" + returnTypeID + ")" + } +} + func (t *FunctionType) WithID(id string) *FunctionType { t.typeID = id return t diff --git a/types_test.go b/types_test.go index b87fc76be7..46d14b6f92 100644 --- a/types_test.go +++ b/types_test.go @@ -267,6 +267,53 @@ func TestType_ID(t *testing.T) { }, "S.test.ContractI", }, + { + &FunctionType{ + UInt8Type{}, + "Foo", + []Parameter{ + { + Type: StringType{}, + }, + }, + }, + "Foo", + }, + { + &FunctionType{ + UInt8Type{}, + "", + []Parameter{}, + }, + "(():UInt8)", + }, + { + &FunctionType{ + UInt8Type{}, + "", + []Parameter{ + { + Type: StringType{}, + }, + }, + }, + "((String):UInt8)", + }, + { + &FunctionType{ + UInt8Type{}, + "", + []Parameter{ + { + Type: StringType{}, + }, + { + Type: AddressType{}, + }, + }, + }, + "((String, Address):UInt8)", + }, } test := func(ty Type, expected string) { From 7715d561d77fc2aec1958e7a7145053b90e6a170 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:08:13 -0500 Subject: [PATCH 045/173] Update CCF test --- encoding/ccf/ccf_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 6c36605c0f..a1183f67e5 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -8263,12 +8263,12 @@ func TestDecodeInvalidType(t *testing.T) { assert.Equal(t, "ccf: failed to decode: invalid type ID for built-in: ``", err.Error()) }) - t.Run("undefined type", func(t *testing.T) { + t.Run("invalid type ID", func(t *testing.T) { t.Parallel() encodedData := []byte{ // language=json, format=json-cdc - // { "type":"Struct", "value":{ "id":"I.Foo", "fields":[] } } + // { "type":"Struct", "value":{ "id":"I", "fields":[] } } // // language=edn, format=ccf // 129([[160([h'', "I.Foo", []])], [136(h''), []]]) @@ -8283,7 +8283,7 @@ func TestDecodeInvalidType(t *testing.T) { 0x81, // struct type: // id: []byte{} - // cadence-type-id: "I.Foo" + // cadence-type-id: "I" // 0 field // tag 0xd8, ccf.CBORTagStructType, @@ -8293,10 +8293,10 @@ func TestDecodeInvalidType(t *testing.T) { // bytes, 0 bytes follow 0x40, // cadence-type-id - // string, 5 bytes follow - 0x65, - // I.Foo - 0x49, 0x2e, 0x46, 0x6f, 0x6f, + // string, 1 bytes follow + 0x61, + // I + 0x49, // fields // array, 0 items follow 0x80, @@ -8313,7 +8313,7 @@ func TestDecodeInvalidType(t *testing.T) { } _, err := ccf.Decode(nil, encodedData) require.Error(t, err) - assert.Equal(t, "ccf: failed to decode: invalid type ID `I.Foo`: invalid identifier location type ID: missing qualified identifier", err.Error()) + assert.Equal(t, "ccf: failed to decode: invalid type ID `I`: invalid identifier location type ID: missing location", err.Error()) }) t.Run("unknown location prefix", func(t *testing.T) { From efc5dc5ce74ade53771eb3f1a477d01de0e82948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 10:11:18 -0700 Subject: [PATCH 046/173] remove link decoding from JSON-CDC --- encoding/json/decode.go | 25 ------------------------- encoding/json/encoding_test.go | 2 +- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index ac62ea620a..fbbc268f2a 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -215,8 +215,6 @@ func (d *Decoder) decodeJSON(v any) cadence.Value { return d.decodeEvent(valueJSON) case contractTypeStr: return d.decodeContract(valueJSON) - case linkTypeStr: - return d.decodeLink(valueJSON) case pathTypeStr: return d.decodePath(valueJSON) case typeTypeStr: @@ -823,29 +821,6 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum { )) } -func (d *Decoder) decodeLink(valueJSON any) cadence.PathLink { - obj := toObject(valueJSON) - - targetPath, ok := d.decodeJSON(obj.Get(targetPathKey)).(cadence.Path) - if !ok { - panic(errors.NewDefaultUserError("invalid link: missing or invalid target path")) - } - - borrowType := obj.GetString(borrowTypeKey) - - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Link struct - Amount: uint64(len(borrowType)), - }) - - return cadence.NewMeteredLink( - d.gauge, - targetPath, - borrowType, - ) -} - func (d *Decoder) decodePath(valueJSON any) cadence.Path { obj := toObject(valueJSON) diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index b8d231eac7..67b108d1fa 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1649,7 +1649,7 @@ func TestEncodeLink(t *testing.T) { t.Parallel() - testEncodeAndDecode( + testEncode( t, cadence.NewPathLink( cadence.NewPath("storage", "foo"), From 587700e5473b60f712191a874b6e68128bf6c3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 10:27:37 -0700 Subject: [PATCH 047/173] add cadence.AccountLink --- encoding/json/encode.go | 89 +++++----- encoding/json/encoding_test.go | 18 ++- runtime/common/memorykind.go | 1 + runtime/common/memorykind_string.go | 243 ++++++++++++++-------------- runtime/common/metering.go | 1 + runtime/convertValues.go | 12 +- runtime/convertValues_test.go | 26 ++- runtime/interpreter/value_test.go | 6 +- values.go | 35 +++- values_test.go | 17 +- 10 files changed, 276 insertions(+), 172 deletions(-) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index ddd9f73bae..fe385941a5 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -211,44 +211,45 @@ type jsonFunctionValue struct { } const ( - voidTypeStr = "Void" - optionalTypeStr = "Optional" - boolTypeStr = "Bool" - characterTypeStr = "Character" - stringTypeStr = "String" - addressTypeStr = "Address" - intTypeStr = "Int" - int8TypeStr = "Int8" - int16TypeStr = "Int16" - int32TypeStr = "Int32" - int64TypeStr = "Int64" - int128TypeStr = "Int128" - int256TypeStr = "Int256" - uintTypeStr = "UInt" - uint8TypeStr = "UInt8" - uint16TypeStr = "UInt16" - uint32TypeStr = "UInt32" - uint64TypeStr = "UInt64" - uint128TypeStr = "UInt128" - uint256TypeStr = "UInt256" - word8TypeStr = "Word8" - word16TypeStr = "Word16" - word32TypeStr = "Word32" - word64TypeStr = "Word64" - fix64TypeStr = "Fix64" - ufix64TypeStr = "UFix64" - arrayTypeStr = "Array" - dictionaryTypeStr = "Dictionary" - structTypeStr = "Struct" - resourceTypeStr = "Resource" - eventTypeStr = "Event" - contractTypeStr = "Contract" - linkTypeStr = "Link" - pathTypeStr = "Path" - typeTypeStr = "Type" - capabilityTypeStr = "Capability" - enumTypeStr = "Enum" - functionTypeStr = "Function" + voidTypeStr = "Void" + optionalTypeStr = "Optional" + boolTypeStr = "Bool" + characterTypeStr = "Character" + stringTypeStr = "String" + addressTypeStr = "Address" + intTypeStr = "Int" + int8TypeStr = "Int8" + int16TypeStr = "Int16" + int32TypeStr = "Int32" + int64TypeStr = "Int64" + int128TypeStr = "Int128" + int256TypeStr = "Int256" + uintTypeStr = "UInt" + uint8TypeStr = "UInt8" + uint16TypeStr = "UInt16" + uint32TypeStr = "UInt32" + uint64TypeStr = "UInt64" + uint128TypeStr = "UInt128" + uint256TypeStr = "UInt256" + word8TypeStr = "Word8" + word16TypeStr = "Word16" + word32TypeStr = "Word32" + word64TypeStr = "Word64" + fix64TypeStr = "Fix64" + ufix64TypeStr = "UFix64" + arrayTypeStr = "Array" + dictionaryTypeStr = "Dictionary" + structTypeStr = "Struct" + resourceTypeStr = "Resource" + eventTypeStr = "Event" + contractTypeStr = "Contract" + linkTypeStr = "Link" + accountLinkTypeStr = "AccountLink" + pathTypeStr = "Path" + typeTypeStr = "Type" + capabilityTypeStr = "Capability" + enumTypeStr = "Enum" + functionTypeStr = "Function" ) // Prepare traverses the object graph of the provided value and constructs @@ -320,7 +321,9 @@ func Prepare(v cadence.Value) jsonValue { case cadence.Contract: return prepareContract(x) case cadence.PathLink: - return prepareLink(x) + return preparePathLink(x) + case cadence.AccountLink: + return prepareAccountLink() case cadence.Path: return preparePath(x) case cadence.TypeValue: @@ -600,7 +603,7 @@ func prepareComposite(kind, id string, fieldTypes []cadence.Field, fields []cade } } -func prepareLink(x cadence.PathLink) jsonValue { +func preparePathLink(x cadence.PathLink) jsonValue { return jsonValueObject{ Type: linkTypeStr, Value: jsonPathLinkValue{ @@ -610,6 +613,12 @@ func prepareLink(x cadence.PathLink) jsonValue { } } +func prepareAccountLink() jsonValue { + return jsonEmptyValueObject{ + Type: accountLinkTypeStr, + } +} + func preparePath(x cadence.Path) jsonValue { return jsonValueObject{ Type: pathTypeStr, diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 67b108d1fa..1dfa484bb6 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1645,7 +1645,7 @@ func TestEncodeContract(t *testing.T) { testAllEncodeAndDecode(t, simpleContract, resourceContract) } -func TestEncodeLink(t *testing.T) { +func TestEncodePathLink(t *testing.T) { t.Parallel() @@ -1674,6 +1674,22 @@ func TestEncodeLink(t *testing.T) { ) } +func TestEncodeAccountLink(t *testing.T) { + + t.Parallel() + + testEncode( + t, + cadence.NewAccountLink(), + // language=json + ` + { + "type": "AccountLink" + } + `, + ) +} + func TestEncodeSimpleTypes(t *testing.T) { t.Parallel() diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 1924f6591e..28e2e7209d 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -101,6 +101,7 @@ const ( MemoryKindCadenceEnumValueBase MemoryKindCadenceEnumValueSize MemoryKindCadencePathLinkValue + MemoryKindCadenceAccountLinkValue MemoryKindCadencePathValue MemoryKindCadenceTypeValue MemoryKindCadenceStorageCapabilityValue diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 81146edb94..abf35e72c7 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -76,130 +76,131 @@ func _() { _ = x[MemoryKindCadenceEnumValueBase-65] _ = x[MemoryKindCadenceEnumValueSize-66] _ = x[MemoryKindCadencePathLinkValue-67] - _ = x[MemoryKindCadencePathValue-68] - _ = x[MemoryKindCadenceTypeValue-69] - _ = x[MemoryKindCadenceStorageCapabilityValue-70] - _ = x[MemoryKindCadenceFunctionValue-71] - _ = x[MemoryKindCadenceOptionalType-72] - _ = x[MemoryKindCadenceVariableSizedArrayType-73] - _ = x[MemoryKindCadenceConstantSizedArrayType-74] - _ = x[MemoryKindCadenceDictionaryType-75] - _ = x[MemoryKindCadenceField-76] - _ = x[MemoryKindCadenceParameter-77] - _ = x[MemoryKindCadenceStructType-78] - _ = x[MemoryKindCadenceResourceType-79] - _ = x[MemoryKindCadenceAttachmentType-80] - _ = x[MemoryKindCadenceEventType-81] - _ = x[MemoryKindCadenceContractType-82] - _ = x[MemoryKindCadenceStructInterfaceType-83] - _ = x[MemoryKindCadenceResourceInterfaceType-84] - _ = x[MemoryKindCadenceContractInterfaceType-85] - _ = x[MemoryKindCadenceFunctionType-86] - _ = x[MemoryKindCadenceReferenceType-87] - _ = x[MemoryKindCadenceRestrictedType-88] - _ = x[MemoryKindCadenceCapabilityType-89] - _ = x[MemoryKindCadenceEnumType-90] - _ = x[MemoryKindRawString-91] - _ = x[MemoryKindAddressLocation-92] - _ = x[MemoryKindBytes-93] - _ = x[MemoryKindVariable-94] - _ = x[MemoryKindCompositeTypeInfo-95] - _ = x[MemoryKindCompositeField-96] - _ = x[MemoryKindInvocation-97] - _ = x[MemoryKindStorageMap-98] - _ = x[MemoryKindStorageKey-99] - _ = x[MemoryKindTypeToken-100] - _ = x[MemoryKindErrorToken-101] - _ = x[MemoryKindSpaceToken-102] - _ = x[MemoryKindProgram-103] - _ = x[MemoryKindIdentifier-104] - _ = x[MemoryKindArgument-105] - _ = x[MemoryKindBlock-106] - _ = x[MemoryKindFunctionBlock-107] - _ = x[MemoryKindParameter-108] - _ = x[MemoryKindParameterList-109] - _ = x[MemoryKindTypeParameter-110] - _ = x[MemoryKindTypeParameterList-111] - _ = x[MemoryKindTransfer-112] - _ = x[MemoryKindMembers-113] - _ = x[MemoryKindTypeAnnotation-114] - _ = x[MemoryKindDictionaryEntry-115] - _ = x[MemoryKindFunctionDeclaration-116] - _ = x[MemoryKindCompositeDeclaration-117] - _ = x[MemoryKindAttachmentDeclaration-118] - _ = x[MemoryKindInterfaceDeclaration-119] - _ = x[MemoryKindEnumCaseDeclaration-120] - _ = x[MemoryKindFieldDeclaration-121] - _ = x[MemoryKindTransactionDeclaration-122] - _ = x[MemoryKindImportDeclaration-123] - _ = x[MemoryKindVariableDeclaration-124] - _ = x[MemoryKindSpecialFunctionDeclaration-125] - _ = x[MemoryKindPragmaDeclaration-126] - _ = x[MemoryKindAssignmentStatement-127] - _ = x[MemoryKindBreakStatement-128] - _ = x[MemoryKindContinueStatement-129] - _ = x[MemoryKindEmitStatement-130] - _ = x[MemoryKindExpressionStatement-131] - _ = x[MemoryKindForStatement-132] - _ = x[MemoryKindIfStatement-133] - _ = x[MemoryKindReturnStatement-134] - _ = x[MemoryKindSwapStatement-135] - _ = x[MemoryKindSwitchStatement-136] - _ = x[MemoryKindWhileStatement-137] - _ = x[MemoryKindRemoveStatement-138] - _ = x[MemoryKindBooleanExpression-139] - _ = x[MemoryKindVoidExpression-140] - _ = x[MemoryKindNilExpression-141] - _ = x[MemoryKindStringExpression-142] - _ = x[MemoryKindIntegerExpression-143] - _ = x[MemoryKindFixedPointExpression-144] - _ = x[MemoryKindArrayExpression-145] - _ = x[MemoryKindDictionaryExpression-146] - _ = x[MemoryKindIdentifierExpression-147] - _ = x[MemoryKindInvocationExpression-148] - _ = x[MemoryKindMemberExpression-149] - _ = x[MemoryKindIndexExpression-150] - _ = x[MemoryKindConditionalExpression-151] - _ = x[MemoryKindUnaryExpression-152] - _ = x[MemoryKindBinaryExpression-153] - _ = x[MemoryKindFunctionExpression-154] - _ = x[MemoryKindCastingExpression-155] - _ = x[MemoryKindCreateExpression-156] - _ = x[MemoryKindDestroyExpression-157] - _ = x[MemoryKindReferenceExpression-158] - _ = x[MemoryKindForceExpression-159] - _ = x[MemoryKindPathExpression-160] - _ = x[MemoryKindAttachExpression-161] - _ = x[MemoryKindConstantSizedType-162] - _ = x[MemoryKindDictionaryType-163] - _ = x[MemoryKindFunctionType-164] - _ = x[MemoryKindInstantiationType-165] - _ = x[MemoryKindNominalType-166] - _ = x[MemoryKindOptionalType-167] - _ = x[MemoryKindReferenceType-168] - _ = x[MemoryKindRestrictedType-169] - _ = x[MemoryKindVariableSizedType-170] - _ = x[MemoryKindPosition-171] - _ = x[MemoryKindRange-172] - _ = x[MemoryKindElaboration-173] - _ = x[MemoryKindActivation-174] - _ = x[MemoryKindActivationEntries-175] - _ = x[MemoryKindVariableSizedSemaType-176] - _ = x[MemoryKindConstantSizedSemaType-177] - _ = x[MemoryKindDictionarySemaType-178] - _ = x[MemoryKindOptionalSemaType-179] - _ = x[MemoryKindRestrictedSemaType-180] - _ = x[MemoryKindReferenceSemaType-181] - _ = x[MemoryKindCapabilitySemaType-182] - _ = x[MemoryKindOrderedMap-183] - _ = x[MemoryKindOrderedMapEntryList-184] - _ = x[MemoryKindOrderedMapEntry-185] - _ = x[MemoryKindLast-186] + _ = 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[MemoryKindCadenceStructType-79] + _ = x[MemoryKindCadenceResourceType-80] + _ = x[MemoryKindCadenceAttachmentType-81] + _ = x[MemoryKindCadenceEventType-82] + _ = x[MemoryKindCadenceContractType-83] + _ = x[MemoryKindCadenceStructInterfaceType-84] + _ = x[MemoryKindCadenceResourceInterfaceType-85] + _ = x[MemoryKindCadenceContractInterfaceType-86] + _ = x[MemoryKindCadenceFunctionType-87] + _ = x[MemoryKindCadenceReferenceType-88] + _ = x[MemoryKindCadenceRestrictedType-89] + _ = x[MemoryKindCadenceCapabilityType-90] + _ = x[MemoryKindCadenceEnumType-91] + _ = x[MemoryKindRawString-92] + _ = x[MemoryKindAddressLocation-93] + _ = x[MemoryKindBytes-94] + _ = x[MemoryKindVariable-95] + _ = x[MemoryKindCompositeTypeInfo-96] + _ = x[MemoryKindCompositeField-97] + _ = x[MemoryKindInvocation-98] + _ = x[MemoryKindStorageMap-99] + _ = x[MemoryKindStorageKey-100] + _ = x[MemoryKindTypeToken-101] + _ = x[MemoryKindErrorToken-102] + _ = x[MemoryKindSpaceToken-103] + _ = x[MemoryKindProgram-104] + _ = x[MemoryKindIdentifier-105] + _ = x[MemoryKindArgument-106] + _ = x[MemoryKindBlock-107] + _ = x[MemoryKindFunctionBlock-108] + _ = x[MemoryKindParameter-109] + _ = x[MemoryKindParameterList-110] + _ = x[MemoryKindTypeParameter-111] + _ = x[MemoryKindTypeParameterList-112] + _ = x[MemoryKindTransfer-113] + _ = x[MemoryKindMembers-114] + _ = x[MemoryKindTypeAnnotation-115] + _ = x[MemoryKindDictionaryEntry-116] + _ = x[MemoryKindFunctionDeclaration-117] + _ = x[MemoryKindCompositeDeclaration-118] + _ = x[MemoryKindAttachmentDeclaration-119] + _ = x[MemoryKindInterfaceDeclaration-120] + _ = x[MemoryKindEnumCaseDeclaration-121] + _ = x[MemoryKindFieldDeclaration-122] + _ = x[MemoryKindTransactionDeclaration-123] + _ = x[MemoryKindImportDeclaration-124] + _ = x[MemoryKindVariableDeclaration-125] + _ = x[MemoryKindSpecialFunctionDeclaration-126] + _ = x[MemoryKindPragmaDeclaration-127] + _ = x[MemoryKindAssignmentStatement-128] + _ = x[MemoryKindBreakStatement-129] + _ = x[MemoryKindContinueStatement-130] + _ = x[MemoryKindEmitStatement-131] + _ = x[MemoryKindExpressionStatement-132] + _ = x[MemoryKindForStatement-133] + _ = x[MemoryKindIfStatement-134] + _ = x[MemoryKindReturnStatement-135] + _ = x[MemoryKindSwapStatement-136] + _ = x[MemoryKindSwitchStatement-137] + _ = x[MemoryKindWhileStatement-138] + _ = x[MemoryKindRemoveStatement-139] + _ = x[MemoryKindBooleanExpression-140] + _ = x[MemoryKindVoidExpression-141] + _ = x[MemoryKindNilExpression-142] + _ = x[MemoryKindStringExpression-143] + _ = x[MemoryKindIntegerExpression-144] + _ = x[MemoryKindFixedPointExpression-145] + _ = x[MemoryKindArrayExpression-146] + _ = x[MemoryKindDictionaryExpression-147] + _ = x[MemoryKindIdentifierExpression-148] + _ = x[MemoryKindInvocationExpression-149] + _ = x[MemoryKindMemberExpression-150] + _ = x[MemoryKindIndexExpression-151] + _ = x[MemoryKindConditionalExpression-152] + _ = x[MemoryKindUnaryExpression-153] + _ = x[MemoryKindBinaryExpression-154] + _ = x[MemoryKindFunctionExpression-155] + _ = x[MemoryKindCastingExpression-156] + _ = x[MemoryKindCreateExpression-157] + _ = x[MemoryKindDestroyExpression-158] + _ = x[MemoryKindReferenceExpression-159] + _ = x[MemoryKindForceExpression-160] + _ = x[MemoryKindPathExpression-161] + _ = x[MemoryKindAttachExpression-162] + _ = x[MemoryKindConstantSizedType-163] + _ = x[MemoryKindDictionaryType-164] + _ = x[MemoryKindFunctionType-165] + _ = x[MemoryKindInstantiationType-166] + _ = x[MemoryKindNominalType-167] + _ = x[MemoryKindOptionalType-168] + _ = x[MemoryKindReferenceType-169] + _ = x[MemoryKindRestrictedType-170] + _ = x[MemoryKindVariableSizedType-171] + _ = x[MemoryKindPosition-172] + _ = x[MemoryKindRange-173] + _ = x[MemoryKindElaboration-174] + _ = x[MemoryKindActivation-175] + _ = x[MemoryKindActivationEntries-176] + _ = x[MemoryKindVariableSizedSemaType-177] + _ = x[MemoryKindConstantSizedSemaType-178] + _ = x[MemoryKindDictionarySemaType-179] + _ = x[MemoryKindOptionalSemaType-180] + _ = x[MemoryKindRestrictedSemaType-181] + _ = x[MemoryKindReferenceSemaType-182] + _ = x[MemoryKindCapabilitySemaType-183] + _ = x[MemoryKindOrderedMap-184] + _ = x[MemoryKindOrderedMapEntryList-185] + _ = x[MemoryKindOrderedMapEntry-186] + _ = x[MemoryKindLast-187] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -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, 1299, 1315, 1344, 1364, 1383, 1412, 1441, 1462, 1474, 1490, 1507, 1526, 1547, 1563, 1582, 1608, 1636, 1664, 1683, 1703, 1724, 1745, 1760, 1769, 1784, 1789, 1797, 1814, 1828, 1838, 1848, 1858, 1867, 1877, 1887, 1894, 1904, 1912, 1917, 1930, 1939, 1952, 1965, 1982, 1990, 1997, 2011, 2026, 2045, 2065, 2086, 2106, 2125, 2141, 2163, 2180, 2199, 2225, 2242, 2261, 2275, 2292, 2305, 2324, 2336, 2347, 2362, 2375, 2390, 2404, 2419, 2436, 2450, 2463, 2479, 2496, 2516, 2531, 2551, 2571, 2591, 2607, 2622, 2643, 2658, 2674, 2692, 2709, 2725, 2742, 2761, 2776, 2790, 2806, 2823, 2837, 2849, 2866, 2877, 2889, 2902, 2916, 2933, 2941, 2946, 2957, 2967, 2984, 3005, 3026, 3044, 3060, 3078, 3095, 3113, 3123, 3142, 3157, 3161} +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, 1530, 1549, 1570, 1586, 1605, 1631, 1659, 1687, 1706, 1726, 1747, 1768, 1783, 1792, 1807, 1812, 1820, 1837, 1851, 1861, 1871, 1881, 1890, 1900, 1910, 1917, 1927, 1935, 1940, 1953, 1962, 1975, 1988, 2005, 2013, 2020, 2034, 2049, 2068, 2088, 2109, 2129, 2148, 2164, 2186, 2203, 2222, 2248, 2265, 2284, 2298, 2315, 2328, 2347, 2359, 2370, 2385, 2398, 2413, 2427, 2442, 2459, 2473, 2486, 2502, 2519, 2539, 2554, 2574, 2594, 2614, 2630, 2645, 2666, 2681, 2697, 2715, 2732, 2748, 2765, 2784, 2799, 2813, 2829, 2846, 2860, 2872, 2889, 2900, 2912, 2925, 2939, 2956, 2964, 2969, 2980, 2990, 3007, 3028, 3049, 3067, 3083, 3101, 3118, 3136, 3146, 3165, 3180, 3184} 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 6babf16b1c..8ff9aba66f 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -201,6 +201,7 @@ var ( CadenceFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionValue) CadenceKeyValuePairMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceKeyValuePair) CadencePathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) + CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) CadenceOptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceOptionalValue) CadencePathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathValue) CadenceVoidValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceVoidValue) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index e5393c31d1..4e6cdfd95b 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -213,6 +213,8 @@ func exportValueWithInterpreter( return cadence.NewMeteredAddress(inter, v), nil case interpreter.PathLinkValue: return exportPathLinkValue(v, inter), nil + case interpreter.AccountLinkValue: + return exportAccountLinkValue(inter), nil case interpreter.PathValue: return exportPathValue(inter, v), nil case interpreter.TypeValue: @@ -588,7 +590,11 @@ func exportDictionaryValue( func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) cadence.PathLink { path := exportPathValue(inter, v.TargetPath) ty := string(inter.MustConvertStaticToSemaType(v.Type).ID()) - return cadence.NewMeteredLink(inter, path, ty) + return cadence.NewMeteredPathLink(inter, path, ty) +} + +func exportAccountLinkValue(inter *interpreter.Interpreter) cadence.AccountLink { + return cadence.NewMeteredAccountLink(inter) } func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) cadence.Path { @@ -811,7 +817,9 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) case cadence.Function: return nil, errors.NewDefaultUserError("cannot import function") case cadence.PathLink: - return nil, errors.NewDefaultUserError("cannot import link") + return nil, errors.NewDefaultUserError("cannot import path link") + case cadence.AccountLink: + return nil, errors.NewDefaultUserError("cannot import account link") default: // This means the implementation has unhandled types. // Hence, return an internal error diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 86d7f18978..caa19b275e 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -818,7 +818,7 @@ func TestImportValue(t *testing.T) { }, }, { - label: "Link (invalid)", + label: "Path Link (invalid)", value: cadence.PathLink{ TargetPath: cadence.Path{ Domain: "storage", @@ -828,6 +828,11 @@ func TestImportValue(t *testing.T) { }, expected: nil, }, + { + label: "Account Link (invalid)", + value: cadence.AccountLink{}, + expected: nil, + }, { label: "Capability (invalid)", value: cadence.StorageCapability{ @@ -2183,6 +2188,25 @@ func TestExportPathLinkValue(t *testing.T) { }) } +func TestExportAccountLinkValue(t *testing.T) { + + t.Parallel() + + link := interpreter.AccountLinkValue{} + + actual, err := exportValueWithInterpreter( + link, + newTestInterpreter(t), + interpreter.EmptyLocationRange, + seenReferences{}, + ) + require.NoError(t, err) + + expected := cadence.AccountLink{} + + assert.Equal(t, expected, actual) +} + func TestExportCompositeValueWithFunctionValueField(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index b1f119cba9..e242ed1d4e 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -1075,7 +1075,7 @@ func TestStringer(t *testing.T) { }(), expected: "y --> bar", }, - "Link": { + "PathLink": { value: PathLinkValue{ TargetPath: PathValue{ Domain: common.PathDomainStorage, @@ -1085,6 +1085,10 @@ func TestStringer(t *testing.T) { }, expected: "PathLink(/storage/foo)", }, + "AccountLink": { + value: AccountLinkValue{}, + expected: "AccountLink()", + }, "Path": { value: PathValue{ Domain: common.PathDomainStorage, diff --git a/values.go b/values.go index c5c6b6a80e..dbea97a266 100644 --- a/values.go +++ b/values.go @@ -1862,7 +1862,7 @@ func NewPathLink(targetPath Path, borrowType string) PathLink { } } -func NewMeteredLink(gauge common.MemoryGauge, targetPath Path, borrowType string) PathLink { +func NewMeteredPathLink(gauge common.MemoryGauge, targetPath Path, borrowType string) PathLink { common.UseMemory(gauge, common.CadencePathLinkValueMemoryUsage) return NewPathLink(targetPath, borrowType) } @@ -1888,6 +1888,39 @@ func (v PathLink) String() string { ) } +// AccountLink + +type AccountLink struct{} + +var _ Value = AccountLink{} + +func NewAccountLink() AccountLink { + return AccountLink{} +} + +func NewMeteredAccountLink(gauge common.MemoryGauge) AccountLink { + common.UseMemory(gauge, common.CadenceAccountLinkValueMemoryUsage) + return NewAccountLink() +} + +func (AccountLink) isValue() {} + +func (v AccountLink) Type() Type { + return nil +} + +func (v AccountLink) MeteredType(_ common.MemoryGauge) Type { + return v.Type() +} + +func (v AccountLink) ToGoValue() any { + return nil +} + +func (v AccountLink) String() string { + return format.AccountLink +} + // Path type Path struct { diff --git a/values_test.go b/values_test.go index 2b358911ce..b45acda240 100644 --- a/values_test.go +++ b/values_test.go @@ -330,7 +330,7 @@ func newValueTestCases() map[string]valueTestCase { }, string: "S.test.FooAttachment(bar: 1)", }, - "Link": { + "PathLink": { value: NewPathLink( Path{ Domain: "storage", @@ -341,6 +341,11 @@ func newValueTestCases() map[string]valueTestCase { string: "PathLink(/storage/foo)", noType: true, }, + "AccountLink": { + value: NewAccountLink(), + string: "AccountLink()", + noType: true, + }, "Path": { value: Path{ Domain: "storage", @@ -764,10 +769,12 @@ func TestValue_Type(t *testing.T) { require.Equal(t, exampleType, returnedType) } - // 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) - checkedTypes[returnedType] = struct{}{} + 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) + checkedTypes[returnedType] = struct{}{} + } }) } From d78f2f79d8a8376506c1503f6506b5b86ee085b4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:20:52 -0500 Subject: [PATCH 048/173] Revert FunctionType & RestrictedType typeID change We may entirely remove FunctionType.typeID and RestrictedType.typeID in a separate PR, so this change won't be needed here anymore. --- encoding/ccf/ccf_test.go | 26 +++++++++++--- types.go | 41 --------------------- types_test.go | 78 ---------------------------------------- 3 files changed, 22 insertions(+), 123 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index a1183f67e5..356bc5c215 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7189,7 +7189,7 @@ func TestEncodeType(t *testing.T) { t.Run("with static function", func(t *testing.T) { - testEncodeAndDecode( + testEncodeAndDecodeEx( t, cadence.TypeValue{ StaticType: (&cadence.FunctionType{ @@ -7239,6 +7239,15 @@ func TestEncodeType(t *testing.T) { // Int type ID (4) 0x04, }, + // Expected decoded FunctionType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + ReturnType: cadence.IntType{}, + }), + }, ) }) @@ -7281,7 +7290,7 @@ func TestEncodeType(t *testing.T) { t.Run("with static restricted type", func(t *testing.T) { - testEncodeAndDecode( + testEncodeAndDecodeEx( t, cadence.TypeValue{ StaticType: (&cadence.RestrictedType{ @@ -7321,6 +7330,15 @@ func TestEncodeType(t *testing.T) { // String type ID (1) 0x01, }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }), + }, ) }) @@ -7372,7 +7390,7 @@ func TestEncodeType(t *testing.T) { // AnyStruct type ID (39) 0x18, 0x27, }, - // Expected decoded val with sorted restrictions and type ID. + // Expected decoded RestrictedType has sorted restrictions and no type ID. cadence.TypeValue{ StaticType: (&cadence.RestrictedType{ Restrictions: []cadence.Type{ @@ -7380,7 +7398,7 @@ func TestEncodeType(t *testing.T) { cadence.NewAnyStructType(), }, Type: cadence.IntType{}, - }).WithID("Int{String, AnyStruct}"), + }), }, ) }) diff --git a/types.go b/types.go index a2d2d68836..dab86a4d3e 100644 --- a/types.go +++ b/types.go @@ -20,7 +20,6 @@ package cadence import ( "fmt" - "strings" "sync" "github.com/onflow/cadence/runtime/common" @@ -1727,29 +1726,9 @@ func NewMeteredFunctionType( func (*FunctionType) isType() {} func (t *FunctionType) ID() string { - if t.typeID == "" { - t.typeID = t.id() - } return t.typeID } -func (t *FunctionType) id() string { - returnTypeID := t.ReturnType.ID() - - switch len(t.Parameters) { - case 0: - return "(():" + returnTypeID + ")" - case 1: - return "((" + t.Parameters[0].Type.ID() + "):" + returnTypeID + ")" - default: - params := make([]string, len(t.Parameters)) - for i, param := range t.Parameters { - params[i] = param.Type.ID() - } - return "((" + strings.Join(params, ", ") + "):" + returnTypeID + ")" - } -} - func (t *FunctionType) WithID(id string) *FunctionType { t.typeID = id return t @@ -1864,29 +1843,9 @@ func NewMeteredRestrictedType( func (*RestrictedType) isType() {} func (t *RestrictedType) ID() string { - if t.typeID == "" { - t.typeID = t.id() - } return t.typeID } -func (t *RestrictedType) id() string { - typeID := t.Type.ID() - - switch len(t.Restrictions) { - case 0: - return typeID + "{}" - case 1: - return typeID + "{" + t.Restrictions[0].ID() + "}" - default: - restrictions := make([]string, len(t.Restrictions)) - for i, restriction := range t.Restrictions { - restrictions[i] = restriction.ID() - } - return typeID + "{" + strings.Join(restrictions, ", ") + "}" - } -} - func (t *RestrictedType) WithID(id string) *RestrictedType { t.typeID = id return t diff --git a/types_test.go b/types_test.go index 46d14b6f92..05dcd6b92a 100644 --- a/types_test.go +++ b/types_test.go @@ -172,49 +172,6 @@ func TestType_ID(t *testing.T) { (&RestrictedType{}).WithID("S.test.Foo{S.test.FooI}"), "S.test.Foo{S.test.FooI}", }, - { - (&RestrictedType{ - Type: &StructType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Foo", - }, - }), - "S.test.Foo{}", - }, - { - (&RestrictedType{ - Type: &StructType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Foo", - }, - Restrictions: []Type{ - &StructInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "FooI", - }, - }, - }), - "S.test.Foo{S.test.FooI}", - }, - { - (&RestrictedType{ - Type: &StructType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Foo", - }, - Restrictions: []Type{ - &StructInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "FooI", - }, - &StructInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "FooII", - }, - }, - }), - "S.test.Foo{S.test.FooI, S.test.FooII}", - }, { &EventType{ QualifiedIdentifier: "Event", @@ -279,41 +236,6 @@ func TestType_ID(t *testing.T) { }, "Foo", }, - { - &FunctionType{ - UInt8Type{}, - "", - []Parameter{}, - }, - "(():UInt8)", - }, - { - &FunctionType{ - UInt8Type{}, - "", - []Parameter{ - { - Type: StringType{}, - }, - }, - }, - "((String):UInt8)", - }, - { - &FunctionType{ - UInt8Type{}, - "", - []Parameter{ - { - Type: StringType{}, - }, - { - Type: AddressType{}, - }, - }, - }, - "((String, Address):UInt8)", - }, } test := func(ty Type, expected string) { From 490bcbd22996904c952056e7aaaa65f7e3c08f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 11:21:26 -0700 Subject: [PATCH 049/173] remove type ID from function type and restricted type --- encoding/json/decode.go | 15 +++++---------- encoding/json/encode.go | 4 ---- encoding/json/encoding_test.go | 13 +++++-------- runtime/convertTypes.go | 6 ++---- runtime/convertValues_test.go | 17 ++++++++++------- runtime/runtime_test.go | 12 ++++++------ types.go | 22 ++++------------------ types_test.go | 22 +++++++++++++++++++++- values_test.go | 1 - 9 files changed, 53 insertions(+), 59 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index fbbc268f2a..e115c60bc0 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -894,16 +894,15 @@ func (d *Decoder) decodeFieldType(valueJSON any, results typeDecodingResults) ca ) } -func (d *Decoder) decodeFunctionType(returnValue, parametersValue, id any, results typeDecodingResults) cadence.Type { +func (d *Decoder) decodeFunctionType(returnValue, parametersValue any, results typeDecodingResults) cadence.Type { parameters := d.decodeParamTypes(toSlice(parametersValue), results) returnType := d.decodeType(returnValue, results) return cadence.NewMeteredFunctionType( d.gauge, - "", parameters, returnType, - ).WithID(toString(id)) + ) } func (d *Decoder) decodeNominalType( @@ -1026,10 +1025,10 @@ func (d *Decoder) decodeNominalType( func (d *Decoder) decodeRestrictedType( typeValue any, restrictionsValue []any, - typeIDValue string, results typeDecodingResults, ) cadence.Type { typ := d.decodeType(typeValue, results) + restrictions := make([]cadence.Type, 0, len(restrictionsValue)) for _, restriction := range restrictionsValue { restrictions = append(restrictions, d.decodeType(restriction, results)) @@ -1037,10 +1036,9 @@ func (d *Decoder) decodeRestrictedType( return cadence.NewMeteredRestrictedType( d.gauge, - "", typ, restrictions, - ).WithID(typeIDValue) + ) } type typeDecodingResults map[string]cadence.Type @@ -1069,16 +1067,13 @@ func (d *Decoder) decodeType(valueJSON any, results typeDecodingResults) cadence case "Function": returnValue := obj.Get(returnKey) parametersValue := obj.Get(parametersKey) - idValue := obj.Get(typeIDKey) - return d.decodeFunctionType(returnValue, parametersValue, idValue, results) + return d.decodeFunctionType(returnValue, parametersValue, results) case "Restriction": restrictionsValue := obj.Get(restrictionsKey) - typeIDValue := toString(obj.Get(typeIDKey)) typeValue := obj.Get(typeKey) return d.decodeRestrictedType( typeValue, toSlice(restrictionsValue), - typeIDValue, results, ) case "Optional": diff --git a/encoding/json/encode.go b/encoding/json/encode.go index fe385941a5..398e8af07e 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -178,7 +178,6 @@ type jsonReferenceType struct { type jsonRestrictedType struct { Kind string `json:"kind"` - TypeID string `json:"typeID"` Type jsonValue `json:"type"` Restrictions []jsonValue `json:"restrictions"` } @@ -192,7 +191,6 @@ type jsonParameterType struct { type jsonFunctionType struct { Return jsonValue `json:"return"` Kind string `json:"kind"` - TypeID string `json:"typeID"` Parameters []jsonParameterType `json:"parameters"` } @@ -826,7 +824,6 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { case *cadence.FunctionType: return jsonFunctionType{ Kind: "Function", - TypeID: typ.ID(), Return: prepareType(typ.ReturnType, results), Parameters: prepareParameters(typ.Parameters, results), } @@ -843,7 +840,6 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { } return jsonRestrictedType{ Kind: "Restriction", - TypeID: typ.ID(), Type: prepareType(typ.Type, results), Restrictions: restrictions, } diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 1dfa484bb6..f7aebc259c 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2399,12 +2399,12 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ + StaticType: &cadence.FunctionType{ Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, ReturnType: cadence.IntType{}, - }).WithID("Foo"), + }, }, // language=json ` @@ -2413,7 +2413,6 @@ func TestEncodeType(t *testing.T) { "value": { "staticType": { "kind": "Function", - "typeID": "Foo", "return": { "kind": "Int" }, @@ -2466,12 +2465,12 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ + StaticType: &cadence.RestrictedType{ Restrictions: []cadence.Type{ cadence.StringType{}, }, Type: cadence.IntType{}, - }).WithID("Int{String}"), + }, }, // language=json ` @@ -2480,7 +2479,6 @@ func TestEncodeType(t *testing.T) { "value": { "staticType": { "kind": "Restriction", - "typeID": "Int{String}", "type": { "kind": "Int" }, @@ -3260,7 +3258,7 @@ func TestExportFunctionValue(t *testing.T) { FunctionType: (&cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("(():Void)"), + }), }, // language=json ` @@ -3269,7 +3267,6 @@ func TestExportFunctionValue(t *testing.T) { "value": { "functionType": { "kind": "Function", - "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 914c647cfd..17bdb46ada 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -424,10 +424,9 @@ func exportFunctionType( return cadence.NewMeteredFunctionType( gauge, - "", convertedParameters, convertedReturnType, - ).WithID(string(t.ID())) + ) } func exportReferenceType( @@ -460,10 +459,9 @@ func exportRestrictedType( return cadence.NewMeteredRestrictedType( gauge, - "", convertedType, restrictions, - ).WithID(string(t.ID())) + ) } func exportCapabilityType( diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index caa19b275e..980be600c3 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -133,7 +133,10 @@ func TestExportValue(t *testing.T) { }, } - testFunctionType := cadence.NewFunctionType("(():Void)", []cadence.Parameter{}, cadence.VoidType{}) + testFunctionType := cadence.NewFunctionType( + []cadence.Parameter{}, + cadence.VoidType{}, + ) for _, tt := range []exportTest{ { @@ -1959,7 +1962,7 @@ func TestExportTypeValue(t *testing.T) { assert.Equal(t, cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ + StaticType: &cadence.RestrictedType{ Type: &cadence.StructType{ QualifiedIdentifier: "S", Location: TestLocation, @@ -1972,7 +1975,7 @@ func TestExportTypeValue(t *testing.T) { Fields: []cadence.Field{}, }, }, - }).WithID("S.test.S{S.test.SI}"), + }, }, actual, ) @@ -2237,10 +2240,10 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { }, { Identifier: "f", - Type: (&cadence.FunctionType{ + Type: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("(():Void)"), + }, }, }, } @@ -2249,10 +2252,10 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { expected := cadence.NewStruct([]cadence.Value{ cadence.NewInt(42), cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("(():Void)"), + }, }, }).WithType(fooStructType) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index fce275aeff..83ca547b44 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2736,10 +2736,10 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.IntType{}, - }).WithID("(():Int)"), + }, }, }, ) @@ -2757,7 +2757,7 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{ { Label: sema.ArgumentLabelNotRequired, @@ -2766,7 +2766,7 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { }, }, ReturnType: cadence.NeverType{}, - }).WithID("((String):Never)"), + }, }, }, ) @@ -2789,10 +2789,10 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("(():Void)"), + }, }, }, ) diff --git a/types.go b/types.go index 0b25ec19f8..3be9337302 100644 --- a/types.go +++ b/types.go @@ -1702,12 +1702,10 @@ type FunctionType struct { } func NewFunctionType( - typeID string, parameters []Parameter, returnType Type, ) *FunctionType { return &FunctionType{ - typeID: typeID, Parameters: parameters, ReturnType: returnType, } @@ -1715,25 +1713,20 @@ func NewFunctionType( func NewMeteredFunctionType( gauge common.MemoryGauge, - typeID string, parameters []Parameter, returnType Type, ) *FunctionType { common.UseMemory(gauge, common.CadenceFunctionTypeMemoryUsage) - return NewFunctionType(typeID, parameters, returnType) + return NewFunctionType(parameters, returnType) } func (*FunctionType) isType() {} func (t *FunctionType) ID() string { + // TODO: return t.typeID } -func (t *FunctionType) WithID(id string) *FunctionType { - t.typeID = id - return t -} - func (t *FunctionType) Equal(other Type) bool { otherType, ok := other.(*FunctionType) if !ok { @@ -1819,12 +1812,10 @@ type RestrictedType struct { } func NewRestrictedType( - typeID string, typ Type, restrictions []Type, ) *RestrictedType { return &RestrictedType{ - typeID: typeID, Type: typ, Restrictions: restrictions, } @@ -1832,25 +1823,20 @@ func NewRestrictedType( func NewMeteredRestrictedType( gauge common.MemoryGauge, - typeID string, typ Type, restrictions []Type, ) *RestrictedType { common.UseMemory(gauge, common.CadenceRestrictedTypeMemoryUsage) - return NewRestrictedType(typeID, typ, restrictions) + return NewRestrictedType(typ, restrictions) } func (*RestrictedType) isType() {} func (t *RestrictedType) ID() string { + // TODO: return t.typeID } -func (t *RestrictedType) WithID(id string) *RestrictedType { - t.typeID = id - return t -} - func (t *RestrictedType) Equal(other Type) bool { otherType, ok := other.(*RestrictedType) if !ok { diff --git a/types_test.go b/types_test.go index eaafba7a00..a361ffaf0a 100644 --- a/types_test.go +++ b/types_test.go @@ -169,9 +169,29 @@ func TestType_ID(t *testing.T) { "S.test.BarI", }, { - (&RestrictedType{}).WithID("S.test.Foo{S.test.FooI}"), + &RestrictedType{ + Type: &ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + }, + Restrictions: []Type{ + &ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooI", + }, + }, + }, "S.test.Foo{S.test.FooI}", }, + { + &FunctionType{ + Parameters: []Parameter{ + {Type: IntType{}}, + }, + ReturnType: StringType{}, + }, + "((Int):String)", + }, { &EventType{ QualifiedIdentifier: "Event", diff --git a/values_test.go b/values_test.go index b45acda240..fdcbdc3611 100644 --- a/values_test.go +++ b/values_test.go @@ -45,7 +45,6 @@ func newValueTestCases() map[string]valueTestCase { fix64, _ := NewFix64("-32.11") testFunctionType := NewFunctionType( - "((String):UInt8)", []Parameter{ { Type: StringType{}, From 76a322d673bee364bbb336b4a94f151a67417487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 11:48:45 -0700 Subject: [PATCH 050/173] improve reference type ID generation --- runtime/sema/type.go | 45 ++++++++++++++++++++++++++------------------ types.go | 11 ++++------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index fd9b10654f..330d67de45 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4762,37 +4762,46 @@ func (t *ReferenceType) Tag() TypeTag { return ReferenceTypeTag } -func (t *ReferenceType) string(typeFormatter func(Type) string) string { - if t.Type == nil { - return "reference" - } +func formatReferenceType( + separator string, + authorized bool, + typeString string, +) string { var builder strings.Builder - if t.Authorized { - builder.WriteString("auth ") + if authorized { + builder.WriteString("auth") + builder.WriteString(separator) } builder.WriteRune('&') - builder.WriteString(typeFormatter(t.Type)) + builder.WriteString(typeString) return builder.String() } +func FormatReferenceTypeID(authorized bool, typeString string) string { + return formatReferenceType("", authorized, typeString) +} + func (t *ReferenceType) String() string { - return t.string(func(ty Type) string { - return ty.String() - }) + if t.Type == nil { + return "reference" + } + return formatReferenceType(" ", t.Authorized, t.Type.String()) } func (t *ReferenceType) QualifiedString() string { - return t.string(func(ty Type) string { - return ty.QualifiedString() - }) + if t.Type == nil { + return "reference" + } + return formatReferenceType(" ", t.Authorized, t.Type.QualifiedString()) } func (t *ReferenceType) ID() TypeID { - return TypeID( - t.string(func(ty Type) string { - return string(ty.ID()) - }), - ) + if t.Type == nil { + return "reference" + } + authorized := t.Authorized + typeString := string(t.Type.ID()) + return TypeID(FormatReferenceTypeID(authorized, typeString)) } func (t *ReferenceType) Equal(other Type) bool { diff --git a/types.go b/types.go index 3be9337302..99862d4ca1 100644 --- a/types.go +++ b/types.go @@ -23,6 +23,7 @@ import ( "sync" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" ) type Type interface { @@ -1779,12 +1780,8 @@ func NewMeteredReferenceType( func (*ReferenceType) isType() {} func (t *ReferenceType) ID() string { - if len(t.typeID) == 0 { - var prefix string - if t.Authorized { - prefix = "auth" - } - t.typeID = fmt.Sprintf("%s&%s", prefix, t.Type.ID()) + if t.typeID == "" { + t.typeID = sema.FormatReferenceTypeID(t.Authorized, t.Type.ID()) } return t.typeID } @@ -2017,7 +2014,7 @@ func NewMeteredCapabilityType( func (*CapabilityType) isType() {} func (t *CapabilityType) ID() string { - if len(t.typeID) == 0 { + if t.typeID == "" { if t.BorrowType != nil { t.typeID = fmt.Sprintf("Capability<%s>", t.BorrowType.ID()) } else { From c5c2782bc2e389da7e6bff7aaefb17ade6869b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 11:55:25 -0700 Subject: [PATCH 051/173] refactor capability type ID formatting --- runtime/sema/type.go | 37 +++++++++++++++++++++++++------------ types.go | 9 +++++---- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 330d67de45..3850464cb6 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6248,33 +6248,46 @@ func (t *CapabilityType) Tag() TypeTag { return CapabilityTypeTag } -func (t *CapabilityType) string(typeFormatter func(Type) string) string { +func formatCapabilityType(borrowTypeString string) string { var builder strings.Builder builder.WriteString("Capability") - if t.BorrowType != nil { + if borrowTypeString != "" { builder.WriteRune('<') - builder.WriteString(typeFormatter(t.BorrowType)) + builder.WriteString(borrowTypeString) builder.WriteRune('>') } return builder.String() } +func FormatCapabilityTypeID(borrowTypeString string) string { + return formatCapabilityType(borrowTypeString) +} + func (t *CapabilityType) String() string { - return t.string(func(t Type) string { - return t.String() - }) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.String() + } + return formatCapabilityType(borrowTypeString) } func (t *CapabilityType) QualifiedString() string { - return t.string(func(t Type) string { - return t.QualifiedString() - }) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.QualifiedString() + } + return formatCapabilityType(borrowTypeString) } func (t *CapabilityType) ID() TypeID { - return TypeID(t.string(func(t Type) string { - return string(t.ID()) - })) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = string(borrowType.ID()) + } + return TypeID(FormatCapabilityTypeID(borrowTypeString)) } func (t *CapabilityType) Equal(other Type) bool { diff --git a/types.go b/types.go index 99862d4ca1..597642d20c 100644 --- a/types.go +++ b/types.go @@ -2015,11 +2015,12 @@ func (*CapabilityType) isType() {} func (t *CapabilityType) ID() string { if t.typeID == "" { - if t.BorrowType != nil { - t.typeID = fmt.Sprintf("Capability<%s>", t.BorrowType.ID()) - } else { - t.typeID = "Capability" + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.ID() } + t.typeID = sema.FormatCapabilityTypeID(borrowTypeString) } return t.typeID } From 11e8830816420733f8a1c23e9e6faa230f1af333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 12:07:11 -0700 Subject: [PATCH 052/173] use faster WriteByte instead of WriteRune --- runtime/ast/expression.go | 4 +-- runtime/cmd/minifier/minifier.go | 2 +- runtime/sema/resources.go | 2 +- runtime/sema/type.go | 36 ++++++++++++------------ runtime/tests/checker/fixedpoint_test.go | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/runtime/ast/expression.go b/runtime/ast/expression.go index 10c7483723..aa3eeb1580 100644 --- a/runtime/ast/expression.go +++ b/runtime/ast/expression.go @@ -268,7 +268,7 @@ func (e *IntegerExpression) String() string { func (e *IntegerExpression) Doc() prettier.Doc { var b strings.Builder if e.Value.Sign() < 0 { - b.WriteRune('-') + b.WriteByte('-') } b.Write(e.PositiveLiteral) return prettier.Text(b.String()) @@ -359,7 +359,7 @@ func (e *FixedPointExpression) Doc() prettier.Doc { builder.WriteByte('.') fractional := e.Fractional.String() for i := uint(0); i < (e.Scale - uint(len(fractional))); i++ { - builder.WriteRune('0') + builder.WriteByte('0') } builder.WriteString(fractional) } diff --git a/runtime/cmd/minifier/minifier.go b/runtime/cmd/minifier/minifier.go index 0616f37da1..3dc08368d0 100644 --- a/runtime/cmd/minifier/minifier.go +++ b/runtime/cmd/minifier/minifier.go @@ -98,7 +98,7 @@ func minify(inputFile, outputFile string) error { } if !eof { - _, err = writer.WriteRune('\n') + _, err = writer.WriteByte('\n') if err != nil { return err } diff --git a/runtime/sema/resources.go b/runtime/sema/resources.go index 3f6e8cd43f..5c28541dc3 100644 --- a/runtime/sema/resources.go +++ b/runtime/sema/resources.go @@ -98,7 +98,7 @@ func (ris *Resources) String() string { builder.WriteString(fmt.Sprint(resource)) builder.WriteString(": ") builder.WriteString(fmt.Sprint(info)) - builder.WriteRune('\n') + builder.WriteByte('\n') }) return builder.String() } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 3850464cb6..9f3c6f8e1e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2387,15 +2387,15 @@ func formatParameter(spaces bool, label, identifier, typeAnnotation string) stri if label != "" { builder.WriteString(label) if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } if identifier != "" { builder.WriteString(identifier) - builder.WriteRune(':') + builder.WriteByte(':') if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } @@ -2518,37 +2518,37 @@ func formatFunctionType( ) string { var builder strings.Builder - builder.WriteRune('(') + builder.WriteByte('(') if len(typeParameters) > 0 { - builder.WriteRune('<') + builder.WriteByte('<') for i, typeParameter := range typeParameters { if i > 0 { - builder.WriteRune(',') + builder.WriteByte(',') if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } builder.WriteString(typeParameter) } - builder.WriteRune('>') + builder.WriteByte('>') } - builder.WriteRune('(') + builder.WriteByte('(') for i, parameter := range parameters { if i > 0 { - builder.WriteRune(',') + builder.WriteByte(',') if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } builder.WriteString(parameter) } builder.WriteString("):") if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } builder.WriteString(returnTypeAnnotation) - builder.WriteRune(')') + builder.WriteByte(')') return builder.String() } @@ -4772,7 +4772,7 @@ func formatReferenceType( builder.WriteString("auth") builder.WriteString(separator) } - builder.WriteRune('&') + builder.WriteByte('&') builder.WriteString(typeString) return builder.String() } @@ -6008,12 +6008,12 @@ func (t *RestrictedType) string(separator string, typeFormatter func(Type) strin result.WriteRune('{') for i, restriction := range t.Restrictions { if i > 0 { - result.WriteRune(',') + result.WriteByte(',') result.WriteString(separator) } result.WriteString(typeFormatter(restriction)) } - result.WriteRune('}') + result.WriteByte('}') return result.String() } @@ -6252,9 +6252,9 @@ func formatCapabilityType(borrowTypeString string) string { var builder strings.Builder builder.WriteString("Capability") if borrowTypeString != "" { - builder.WriteRune('<') + builder.WriteByte('<') builder.WriteString(borrowTypeString) - builder.WriteRune('>') + builder.WriteByte('>') } return builder.String() } diff --git a/runtime/tests/checker/fixedpoint_test.go b/runtime/tests/checker/fixedpoint_test.go index ba65b9c1d5..18d1be7708 100644 --- a/runtime/tests/checker/fixedpoint_test.go +++ b/runtime/tests/checker/fixedpoint_test.go @@ -144,7 +144,7 @@ func TestCheckFixedPointLiteralRanges(t *testing.T) { formatLiteral := func(integer, fractional *big.Int) string { var builder strings.Builder builder.WriteString(integer.String()) - builder.WriteRune('.') + builder.WriteByte('.') builder.WriteString(format.PadLeft(fractional.String(), '0', ranged.Scale())) return builder.String() } From fd46833cd09b2c00c5e8ce2cc880df1e9a9974ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 12:07:30 -0700 Subject: [PATCH 053/173] simplify reference type string formatting --- runtime/sema/type.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9f3c6f8e1e..fe7e64181e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4781,18 +4781,23 @@ func FormatReferenceTypeID(authorized bool, typeString string) string { return formatReferenceType("", authorized, typeString) } -func (t *ReferenceType) String() string { +func (t *ReferenceType) string(typeFormatter func(Type) string) string { if t.Type == nil { return "reference" } - return formatReferenceType(" ", t.Authorized, t.Type.String()) + return formatReferenceType(" ", t.Authorized, typeFormatter(t.Type)) +} + +func (t *ReferenceType) String() string { + return t.string(func(t Type) string { + return t.String() + }) } func (t *ReferenceType) QualifiedString() string { - if t.Type == nil { - return "reference" - } - return formatReferenceType(" ", t.Authorized, t.Type.QualifiedString()) + return t.string(func(t Type) string { + return t.QualifiedString() + }) } func (t *ReferenceType) ID() TypeID { From 100802072dbb30fa52c40266e76def3eb6d4c7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 12:14:07 -0700 Subject: [PATCH 054/173] refactor restricted type ID formatting --- runtime/sema/type.go | 26 +++++++++++++++++++++----- types.go | 12 +++++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index fe7e64181e..a12566064a 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6007,21 +6007,37 @@ func (t *RestrictedType) Tag() TypeTag { return RestrictedTypeTag } -func (t *RestrictedType) string(separator string, typeFormatter func(Type) string) string { +func formatRestrictedType(separator string, typeString string, restrictionStrings []string) string { var result strings.Builder - result.WriteString(typeFormatter(t.Type)) - result.WriteRune('{') - for i, restriction := range t.Restrictions { + result.WriteString(typeString) + result.WriteByte('{') + for i, restrictionString := range restrictionStrings { if i > 0 { result.WriteByte(',') result.WriteString(separator) } - result.WriteString(typeFormatter(restriction)) + result.WriteString(restrictionString) } result.WriteByte('}') return result.String() } +func FormatRestrictedTypeID(typeString string, restrictionStrings []string) string { + return formatRestrictedType("", typeString, restrictionStrings) +} + +func (t *RestrictedType) string(separator string, typeFormatter func(Type) string) string { + var restrictionStrings []string + restrictionCount := len(t.Restrictions) + if restrictionCount > 0 { + restrictionStrings = make([]string, 0, restrictionCount) + for _, restriction := range t.Restrictions { + restrictionStrings = append(restrictionStrings, typeFormatter(restriction)) + } + } + return formatRestrictedType(separator, typeFormatter(t.Type), restrictionStrings) +} + func (t *RestrictedType) String() string { return t.string(" ", func(ty Type) string { return ty.String() diff --git a/types.go b/types.go index 597642d20c..a2a82dcaa8 100644 --- a/types.go +++ b/types.go @@ -1830,7 +1830,17 @@ func NewMeteredRestrictedType( func (*RestrictedType) isType() {} func (t *RestrictedType) ID() string { - // TODO: + if t.typeID == "" { + var restrictionStrings []string + restrictionCount := len(t.Restrictions) + if restrictionCount > 0 { + restrictionStrings = make([]string, 0, restrictionCount) + for _, restriction := range t.Restrictions { + restrictionStrings = append(restrictionStrings, restriction.ID()) + } + } + t.typeID = sema.FormatRestrictedTypeID(t.Type.ID(), restrictionStrings) + } return t.typeID } From 4a5105a65aa4eea1f0f9a7700e6163f34582f85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 12:54:41 -0700 Subject: [PATCH 055/173] refactor function type ID formatting --- runtime/sema/type.go | 110 +++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index a12566064a..103ddf0bfc 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2511,7 +2511,7 @@ func (p TypeParameter) checkTypeBound(ty Type, typeRange ast.Range) error { // Function types func formatFunctionType( - spaces bool, + separator string, typeParameters []string, parameters []string, returnTypeAnnotation string, @@ -2525,9 +2525,7 @@ func formatFunctionType( for i, typeParameter := range typeParameters { if i > 0 { builder.WriteByte(',') - if spaces { - builder.WriteByte(' ') - } + builder.WriteString(separator) } builder.WriteString(typeParameter) } @@ -2537,16 +2535,12 @@ func formatFunctionType( for i, parameter := range parameters { if i > 0 { builder.WriteByte(',') - if spaces { - builder.WriteByte(' ') - } + builder.WriteString(separator) } builder.WriteString(parameter) } builder.WriteString("):") - if spaces { - builder.WriteByte(' ') - } + builder.WriteString(separator) builder.WriteString(returnTypeAnnotation) builder.WriteByte(')') return builder.String() @@ -2575,14 +2569,18 @@ func (t *FunctionType) Tag() TypeTag { return FunctionTypeTag } -func (t *FunctionType) String() string { +func (t *FunctionType) string( + typeParameterFormatter func(*TypeParameter) string, + parameterFormatter func(Parameter) string, + returnTypeAnnotationFormatter func(TypeAnnotation) string, +) string { var typeParameters []string typeParameterCount := len(t.TypeParameters) if typeParameterCount > 0 { typeParameters = make([]string, typeParameterCount) for i, typeParameter := range t.TypeParameters { - typeParameters[i] = typeParameter.String() + typeParameters[i] = typeParameterFormatter(typeParameter) } } @@ -2591,64 +2589,88 @@ func (t *FunctionType) String() string { if parameterCount > 0 { parameters = make([]string, parameterCount) for i, parameter := range t.Parameters { - parameters[i] = parameter.String() + parameters[i] = parameterFormatter(parameter) } } - returnTypeAnnotation := t.ReturnTypeAnnotation.String() + returnTypeAnnotation := returnTypeAnnotationFormatter(t.ReturnTypeAnnotation) return formatFunctionType( - true, + " ", typeParameters, parameters, returnTypeAnnotation, ) } -func (t *FunctionType) QualifiedString() string { - - typeParameters := make([]string, len(t.TypeParameters)) - - for i, typeParameter := range t.TypeParameters { - typeParameters[i] = typeParameter.QualifiedString() - } - - parameters := make([]string, len(t.Parameters)) - - for i, parameter := range t.Parameters { - parameters[i] = parameter.QualifiedString() - } - - returnTypeAnnotation := t.ReturnTypeAnnotation.QualifiedString() - +func FormatFunctionTypeID( + typeParameters []string, + parameters []string, + returnTypeAnnotation string, +) string { return formatFunctionType( - true, + "", typeParameters, parameters, returnTypeAnnotation, ) } +func (t *FunctionType) String() string { + return t.string( + func(parameter *TypeParameter) string { + return parameter.String() + }, + func(parameter Parameter) string { + return parameter.String() + }, + func(typeAnnotation TypeAnnotation) string { + return typeAnnotation.String() + }, + ) +} + +func (t *FunctionType) QualifiedString() string { + return t.string( + func(parameter *TypeParameter) string { + return parameter.QualifiedString() + }, + func(parameter Parameter) string { + return parameter.QualifiedString() + }, + func(typeAnnotation TypeAnnotation) string { + return typeAnnotation.QualifiedString() + }, + ) +} + // NOTE: parameter names and argument labels are *not* part of the ID! func (t *FunctionType) ID() TypeID { - typeParameters := make([]string, len(t.TypeParameters)) - - for i, typeParameter := range t.TypeParameters { - typeParameters[i] = string(typeParameter.TypeBound.ID()) + typeParameterCount := len(t.TypeParameters) + var typeParameters []string + if typeParameterCount > 0 { + typeParameters = make([]string, typeParameterCount) + for i, typeParameter := range t.TypeParameters { + if typeParameter.TypeBound != nil { + typeParameters[i] = string(typeParameter.TypeBound.ID()) + } + } } - parameters := make([]string, len(t.Parameters)) - - for i, parameter := range t.Parameters { - parameters[i] = string(parameter.TypeAnnotation.Type.ID()) + parameterCount := len(t.Parameters) + var parameters []string + if parameterCount > 0 { + parameters = make([]string, parameterCount) + for i, parameter := range t.Parameters { + parameters[i] = string(parameter.TypeAnnotation.Type.ID()) + } } returnTypeAnnotation := string(t.ReturnTypeAnnotation.Type.ID()) return TypeID( - formatFunctionType( - false, + FormatFunctionTypeID( typeParameters, parameters, returnTypeAnnotation, @@ -4804,9 +4826,7 @@ func (t *ReferenceType) ID() TypeID { if t.Type == nil { return "reference" } - authorized := t.Authorized - typeString := string(t.Type.ID()) - return TypeID(FormatReferenceTypeID(authorized, typeString)) + return TypeID(FormatReferenceTypeID(t.Authorized, string(t.Type.ID()))) } func (t *ReferenceType) Equal(other Type) bool { From eb11e52c3728af7e0d0a1e915d8f14eeb4cab869 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:59:57 -0500 Subject: [PATCH 056/173] Return error if decoded size isn't same as received This would detect extraneous data appended to a valid CCF message. --- encoding/ccf/decode.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index cdc4e1cf28..1c950cc554 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -68,6 +68,10 @@ func Decode(gauge common.MemoryGauge, b []byte) (cadence.Value, error) { return nil, err } + if dec.dec.NumBytesDecoded() != len(b) { + return nil, cadenceErrors.NewDefaultUserError("ccf: failed to decode: decoded %d bytes, received %d bytes", dec.dec.NumBytesDecoded(), len(b)) + } + return v, nil } From 8c43d331182bfc0c4da7561d1ab4828caffcc89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 13:13:32 -0700 Subject: [PATCH 057/173] include type parameter name instead of bound --- runtime/sema/type.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 103ddf0bfc..1fcab3cb48 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2652,9 +2652,7 @@ func (t *FunctionType) ID() TypeID { if typeParameterCount > 0 { typeParameters = make([]string, typeParameterCount) for i, typeParameter := range t.TypeParameters { - if typeParameter.TypeBound != nil { - typeParameters[i] = string(typeParameter.TypeBound.ID()) - } + typeParameters[i] = typeParameter.Name } } From 33323902b238bc9325e807878b4569a101133ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 13:14:04 -0700 Subject: [PATCH 058/173] add type parameters to cadence package --- encoding/json/decode.go | 96 ++++++++---- encoding/json/encode.go | 47 ++++-- encoding/json/encoding_test.go | 16 +- runtime/cmd/minifier/minifier.go | 2 +- runtime/common/memorykind.go | 1 + runtime/common/memorykind_string.go | 223 ++++++++++++++-------------- runtime/convertTypes.go | 54 +++++-- runtime/convertValues_test.go | 5 +- runtime/runtime_test.go | 2 - types.go | 33 +++- values_test.go | 1 + 11 files changed, 303 insertions(+), 177 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index e115c60bc0..e6492216a3 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -111,28 +111,30 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { } const ( - typeKey = "type" - kindKey = "kind" - valueKey = "value" - keyKey = "key" - nameKey = "name" - fieldsKey = "fields" - initializersKey = "initializers" - idKey = "id" - targetPathKey = "targetPath" - borrowTypeKey = "borrowType" - domainKey = "domain" - identifierKey = "identifier" - staticTypeKey = "staticType" - addressKey = "address" - pathKey = "path" - authorizedKey = "authorized" - sizeKey = "size" - typeIDKey = "typeID" - restrictionsKey = "restrictions" - labelKey = "label" - parametersKey = "parameters" - returnKey = "return" + typeKey = "type" + kindKey = "kind" + valueKey = "value" + keyKey = "key" + nameKey = "name" + fieldsKey = "fields" + initializersKey = "initializers" + idKey = "id" + targetPathKey = "targetPath" + borrowTypeKey = "borrowType" + domainKey = "domain" + identifierKey = "identifier" + staticTypeKey = "staticType" + addressKey = "address" + pathKey = "path" + authorizedKey = "authorized" + sizeKey = "size" + typeIDKey = "typeID" + restrictionsKey = "restrictions" + labelKey = "label" + parametersKey = "parameters" + typeParametersKey = "typeParameters" + returnKey = "return" + typeBoundKey = "typeBound" ) func (d *Decoder) decodeJSON(v any) cadence.Value { @@ -846,9 +848,38 @@ func (d *Decoder) decodePath(valueJSON any) cadence.Path { ) } -func (d *Decoder) decodeParamType(valueJSON any, results typeDecodingResults) cadence.Parameter { +func (d *Decoder) decodeTypeParameter(valueJSON any, results typeDecodingResults) cadence.TypeParameter { obj := toObject(valueJSON) - // Unmetered because decodeParamType is metered in decodeParamTypes and called nowhere else + // Unmetered because decodeTypeParameter is metered in decodeTypeParameters and called nowhere else + typeBoundObj, ok := obj[typeBoundKey] + var typeBound cadence.Type + if ok { + typeBound = d.decodeType(typeBoundObj, results) + } + + return cadence.NewTypeParameter( + toString(obj.Get(nameKey)), + typeBound, + ) +} + +func (d *Decoder) decodeTypeParameters(typeParams []any, results typeDecodingResults) []cadence.TypeParameter { + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceTypeParameter, + Amount: uint64(len(typeParams)), + }) + typeParameters := make([]cadence.TypeParameter, 0, len(typeParams)) + + for _, param := range typeParams { + typeParameters = append(typeParameters, d.decodeTypeParameter(param, results)) + } + + return typeParameters +} + +func (d *Decoder) decodeParameter(valueJSON any, results typeDecodingResults) cadence.Parameter { + obj := toObject(valueJSON) + // Unmetered because decodeParameter is metered in decodeParameters and called nowhere else return cadence.NewParameter( toString(obj.Get(labelKey)), toString(obj.Get(idKey)), @@ -856,7 +887,7 @@ func (d *Decoder) decodeParamType(valueJSON any, results typeDecodingResults) ca ) } -func (d *Decoder) decodeParamTypes(params []any, results typeDecodingResults) []cadence.Parameter { +func (d *Decoder) decodeParameters(params []any, results typeDecodingResults) []cadence.Parameter { common.UseMemory(d.gauge, common.MemoryUsage{ Kind: common.MemoryKindCadenceParameter, Amount: uint64(len(params)), @@ -864,7 +895,7 @@ func (d *Decoder) decodeParamTypes(params []any, results typeDecodingResults) [] parameters := make([]cadence.Parameter, 0, len(params)) for _, param := range params { - parameters = append(parameters, d.decodeParamType(param, results)) + parameters = append(parameters, d.decodeParameter(param, results)) } return parameters @@ -894,12 +925,14 @@ func (d *Decoder) decodeFieldType(valueJSON any, results typeDecodingResults) ca ) } -func (d *Decoder) decodeFunctionType(returnValue, parametersValue any, results typeDecodingResults) cadence.Type { - parameters := d.decodeParamTypes(toSlice(parametersValue), results) +func (d *Decoder) decodeFunctionType(typeParametersValue, parametersValue, returnValue any, results typeDecodingResults) cadence.Type { + typeParameters := d.decodeTypeParameters(toSlice(typeParametersValue), results) + parameters := d.decodeParameters(toSlice(parametersValue), results) returnType := d.decodeType(returnValue, results) return cadence.NewMeteredFunctionType( d.gauge, + typeParameters, parameters, returnType, ) @@ -917,7 +950,7 @@ func (d *Decoder) decodeNominalType( for _, params := range initializers { inits = append( inits, - d.decodeParamTypes(toSlice(params), results), + d.decodeParameters(toSlice(params), results), ) } @@ -1065,9 +1098,10 @@ func (d *Decoder) decodeType(valueJSON any, results typeDecodingResults) cadence switch kindValue { case "Function": - returnValue := obj.Get(returnKey) + typeParametersValue := obj.Get(typeParametersKey) parametersValue := obj.Get(parametersKey) - return d.decodeFunctionType(returnValue, parametersValue, results) + returnValue := obj.Get(returnKey) + return d.decodeFunctionType(typeParametersValue, parametersValue, returnValue, results) case "Restriction": restrictionsValue := obj.Get(restrictionsKey) typeValue := obj.Get(typeKey) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 398e8af07e..7a59ccab35 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -182,6 +182,11 @@ type jsonRestrictedType struct { Restrictions []jsonValue `json:"restrictions"` } +type jsonTypeParameter struct { + Name string `json:"name"` + TypeBound jsonValue `json:"typeBound"` +} + type jsonParameterType struct { Type jsonValue `json:"type"` Label string `json:"label"` @@ -189,9 +194,10 @@ type jsonParameterType struct { } type jsonFunctionType struct { - Return jsonValue `json:"return"` - Kind string `json:"kind"` - Parameters []jsonParameterType `json:"parameters"` + Kind string `json:"kind"` + TypeParameters []jsonTypeParameter `json:"typeParameters"` + Parameters []jsonParameterType `json:"parameters"` + Return jsonValue `json:"return"` } type jsonTypeValue struct { @@ -627,7 +633,19 @@ func preparePath(x cadence.Path) jsonValue { } } -func prepareParameterType(parameterType cadence.Parameter, results typePreparationResults) jsonParameterType { +func prepareTypeParameter(typeParameter cadence.TypeParameter, results typePreparationResults) jsonTypeParameter { + typeBound := typeParameter.TypeBound + var preparedTypeBound jsonValue + if typeBound != nil { + preparedTypeBound = prepareType(typeBound, results) + } + return jsonTypeParameter{ + Name: typeParameter.Name, + TypeBound: preparedTypeBound, + } +} + +func prepareParameter(parameterType cadence.Parameter, results typePreparationResults) jsonParameterType { return jsonParameterType{ Label: parameterType.Label, Id: parameterType.Identifier, @@ -650,12 +668,20 @@ func prepareFields(fieldTypes []cadence.Field, results typePreparationResults) [ return fields } +func prepareTypeParameters(typeParameters []cadence.TypeParameter, results typePreparationResults) []jsonTypeParameter { + result := make([]jsonTypeParameter, 0) + for _, typeParameter := range typeParameters { + result = append(result, prepareTypeParameter(typeParameter, results)) + } + return result +} + func prepareParameters(parameterTypes []cadence.Parameter, results typePreparationResults) []jsonParameterType { - parameters := make([]jsonParameterType, 0) + result := make([]jsonParameterType, 0) for _, param := range parameterTypes { - parameters = append(parameters, prepareParameterType(param, results)) + result = append(result, prepareParameter(param, results)) } - return parameters + return result } func prepareInitializers(initializerTypes [][]cadence.Parameter, results typePreparationResults) [][]jsonParameterType { @@ -823,9 +849,10 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { } case *cadence.FunctionType: return jsonFunctionType{ - Kind: "Function", - Return: prepareType(typ.ReturnType, results), - Parameters: prepareParameters(typ.Parameters, results), + Kind: "Function", + TypeParameters: prepareTypeParameters(typ.TypeParameters, results), + Parameters: prepareParameters(typ.Parameters, results), + Return: prepareType(typ.ReturnType, results), } case *cadence.ReferenceType: return jsonReferenceType{ diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index f7aebc259c..ec5f55efb7 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2400,6 +2400,9 @@ func TestEncodeType(t *testing.T) { t, cadence.TypeValue{ StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.AnyStructType{}}, + }, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, @@ -2416,6 +2419,14 @@ func TestEncodeType(t *testing.T) { "return": { "kind": "Int" }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "AnyStruct" + } + } + ], "parameters": [ { "label": "qux", @@ -3255,10 +3266,10 @@ func TestExportFunctionValue(t *testing.T) { testEncode( t, cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }), + }, }, // language=json ` @@ -3268,6 +3279,7 @@ func TestExportFunctionValue(t *testing.T) { "functionType": { "kind": "Function", "parameters": [], + "typeParameters": [], "return": { "kind": "Void" } diff --git a/runtime/cmd/minifier/minifier.go b/runtime/cmd/minifier/minifier.go index 3dc08368d0..735280393c 100644 --- a/runtime/cmd/minifier/minifier.go +++ b/runtime/cmd/minifier/minifier.go @@ -98,7 +98,7 @@ func minify(inputFile, outputFile string) error { } if !eof { - _, err = writer.WriteByte('\n') + _ = writer.WriteByte('\n') if err != nil { return err } diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 28e2e7209d..04ea030df6 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -114,6 +114,7 @@ const ( MemoryKindCadenceDictionaryType MemoryKindCadenceField MemoryKindCadenceParameter + MemoryKindCadenceTypeParameter MemoryKindCadenceStructType MemoryKindCadenceResourceType MemoryKindCadenceAttachmentType diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index abf35e72c7..922940626b 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -87,120 +87,121 @@ func _() { _ = x[MemoryKindCadenceDictionaryType-76] _ = x[MemoryKindCadenceField-77] _ = x[MemoryKindCadenceParameter-78] - _ = x[MemoryKindCadenceStructType-79] - _ = x[MemoryKindCadenceResourceType-80] - _ = x[MemoryKindCadenceAttachmentType-81] - _ = x[MemoryKindCadenceEventType-82] - _ = x[MemoryKindCadenceContractType-83] - _ = x[MemoryKindCadenceStructInterfaceType-84] - _ = x[MemoryKindCadenceResourceInterfaceType-85] - _ = x[MemoryKindCadenceContractInterfaceType-86] - _ = x[MemoryKindCadenceFunctionType-87] - _ = x[MemoryKindCadenceReferenceType-88] - _ = x[MemoryKindCadenceRestrictedType-89] - _ = x[MemoryKindCadenceCapabilityType-90] - _ = x[MemoryKindCadenceEnumType-91] - _ = x[MemoryKindRawString-92] - _ = x[MemoryKindAddressLocation-93] - _ = x[MemoryKindBytes-94] - _ = x[MemoryKindVariable-95] - _ = x[MemoryKindCompositeTypeInfo-96] - _ = x[MemoryKindCompositeField-97] - _ = x[MemoryKindInvocation-98] - _ = x[MemoryKindStorageMap-99] - _ = x[MemoryKindStorageKey-100] - _ = x[MemoryKindTypeToken-101] - _ = x[MemoryKindErrorToken-102] - _ = x[MemoryKindSpaceToken-103] - _ = x[MemoryKindProgram-104] - _ = x[MemoryKindIdentifier-105] - _ = x[MemoryKindArgument-106] - _ = x[MemoryKindBlock-107] - _ = x[MemoryKindFunctionBlock-108] - _ = x[MemoryKindParameter-109] - _ = x[MemoryKindParameterList-110] - _ = x[MemoryKindTypeParameter-111] - _ = x[MemoryKindTypeParameterList-112] - _ = x[MemoryKindTransfer-113] - _ = x[MemoryKindMembers-114] - _ = x[MemoryKindTypeAnnotation-115] - _ = x[MemoryKindDictionaryEntry-116] - _ = x[MemoryKindFunctionDeclaration-117] - _ = x[MemoryKindCompositeDeclaration-118] - _ = x[MemoryKindAttachmentDeclaration-119] - _ = x[MemoryKindInterfaceDeclaration-120] - _ = x[MemoryKindEnumCaseDeclaration-121] - _ = x[MemoryKindFieldDeclaration-122] - _ = x[MemoryKindTransactionDeclaration-123] - _ = x[MemoryKindImportDeclaration-124] - _ = x[MemoryKindVariableDeclaration-125] - _ = x[MemoryKindSpecialFunctionDeclaration-126] - _ = x[MemoryKindPragmaDeclaration-127] - _ = x[MemoryKindAssignmentStatement-128] - _ = x[MemoryKindBreakStatement-129] - _ = x[MemoryKindContinueStatement-130] - _ = x[MemoryKindEmitStatement-131] - _ = x[MemoryKindExpressionStatement-132] - _ = x[MemoryKindForStatement-133] - _ = x[MemoryKindIfStatement-134] - _ = x[MemoryKindReturnStatement-135] - _ = x[MemoryKindSwapStatement-136] - _ = x[MemoryKindSwitchStatement-137] - _ = x[MemoryKindWhileStatement-138] - _ = x[MemoryKindRemoveStatement-139] - _ = x[MemoryKindBooleanExpression-140] - _ = x[MemoryKindVoidExpression-141] - _ = x[MemoryKindNilExpression-142] - _ = x[MemoryKindStringExpression-143] - _ = x[MemoryKindIntegerExpression-144] - _ = x[MemoryKindFixedPointExpression-145] - _ = x[MemoryKindArrayExpression-146] - _ = x[MemoryKindDictionaryExpression-147] - _ = x[MemoryKindIdentifierExpression-148] - _ = x[MemoryKindInvocationExpression-149] - _ = x[MemoryKindMemberExpression-150] - _ = x[MemoryKindIndexExpression-151] - _ = x[MemoryKindConditionalExpression-152] - _ = x[MemoryKindUnaryExpression-153] - _ = x[MemoryKindBinaryExpression-154] - _ = x[MemoryKindFunctionExpression-155] - _ = x[MemoryKindCastingExpression-156] - _ = x[MemoryKindCreateExpression-157] - _ = x[MemoryKindDestroyExpression-158] - _ = x[MemoryKindReferenceExpression-159] - _ = x[MemoryKindForceExpression-160] - _ = x[MemoryKindPathExpression-161] - _ = x[MemoryKindAttachExpression-162] - _ = x[MemoryKindConstantSizedType-163] - _ = x[MemoryKindDictionaryType-164] - _ = x[MemoryKindFunctionType-165] - _ = x[MemoryKindInstantiationType-166] - _ = x[MemoryKindNominalType-167] - _ = x[MemoryKindOptionalType-168] - _ = x[MemoryKindReferenceType-169] - _ = x[MemoryKindRestrictedType-170] - _ = x[MemoryKindVariableSizedType-171] - _ = x[MemoryKindPosition-172] - _ = x[MemoryKindRange-173] - _ = x[MemoryKindElaboration-174] - _ = x[MemoryKindActivation-175] - _ = x[MemoryKindActivationEntries-176] - _ = x[MemoryKindVariableSizedSemaType-177] - _ = x[MemoryKindConstantSizedSemaType-178] - _ = x[MemoryKindDictionarySemaType-179] - _ = x[MemoryKindOptionalSemaType-180] - _ = x[MemoryKindRestrictedSemaType-181] - _ = x[MemoryKindReferenceSemaType-182] - _ = x[MemoryKindCapabilitySemaType-183] - _ = x[MemoryKindOrderedMap-184] - _ = x[MemoryKindOrderedMapEntryList-185] - _ = x[MemoryKindOrderedMapEntry-186] - _ = x[MemoryKindLast-187] + _ = 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 = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +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, 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, 1530, 1549, 1570, 1586, 1605, 1631, 1659, 1687, 1706, 1726, 1747, 1768, 1783, 1792, 1807, 1812, 1820, 1837, 1851, 1861, 1871, 1881, 1890, 1900, 1910, 1917, 1927, 1935, 1940, 1953, 1962, 1975, 1988, 2005, 2013, 2020, 2034, 2049, 2068, 2088, 2109, 2129, 2148, 2164, 2186, 2203, 2222, 2248, 2265, 2284, 2298, 2315, 2328, 2347, 2359, 2370, 2385, 2398, 2413, 2427, 2442, 2459, 2473, 2486, 2502, 2519, 2539, 2554, 2574, 2594, 2614, 2630, 2645, 2666, 2681, 2697, 2715, 2732, 2748, 2765, 2784, 2799, 2813, 2829, 2846, 2860, 2872, 2889, 2900, 2912, 2925, 2939, 2956, 2964, 2969, 2980, 2990, 3007, 3028, 3049, 3067, 3083, 3101, 3118, 3136, 3146, 3165, 3180, 3184} +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) { diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 17bdb46ada..1c277d45a2 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -403,27 +403,59 @@ func exportFunctionType( t *sema.FunctionType, results map[sema.TypeID]cadence.Type, ) cadence.Type { + // Type parameters + typeParameterCount := len(t.TypeParameters) common.UseMemory(gauge, common.MemoryUsage{ - Kind: common.MemoryKindCadenceParameter, - Amount: uint64(len(t.Parameters)), + Kind: common.MemoryKindCadenceTypeParameter, + Amount: uint64(typeParameterCount), }) - convertedParameters := make([]cadence.Parameter, len(t.Parameters)) + var convertedTypeParameters []cadence.TypeParameter + if typeParameterCount > 0 { + convertedTypeParameters = make([]cadence.TypeParameter, typeParameterCount) - for i, parameter := range t.Parameters { - convertedParameterType := ExportMeteredType(gauge, parameter.TypeAnnotation.Type, results) + for i, typeParameter := range t.TypeParameters { - // Metered above - convertedParameters[i] = cadence.NewParameter( - parameter.Label, - parameter.Identifier, - convertedParameterType, - ) + typeBound := typeParameter.TypeBound + var convertedParameterTypeBound cadence.Type + if typeBound != nil { + convertedParameterTypeBound = ExportMeteredType(gauge, typeBound, results) + } + + // Metered above + convertedTypeParameters[i] = cadence.NewTypeParameter( + typeParameter.Name, + convertedParameterTypeBound, + ) + } + } + + // Parameters + parameterCount := len(t.Parameters) + common.UseMemory(gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceParameter, + Amount: uint64(parameterCount), + }) + var convertedParameters []cadence.Parameter + if parameterCount > 0 { + convertedParameters = make([]cadence.Parameter, parameterCount) + + for i, parameter := range t.Parameters { + convertedParameterType := ExportMeteredType(gauge, parameter.TypeAnnotation.Type, results) + + // Metered above + convertedParameters[i] = cadence.NewParameter( + parameter.Label, + parameter.Identifier, + convertedParameterType, + ) + } } convertedReturnType := ExportMeteredType(gauge, t.ReturnTypeAnnotation.Type, results) return cadence.NewMeteredFunctionType( gauge, + convertedTypeParameters, convertedParameters, convertedReturnType, ) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 980be600c3..83454dd765 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -134,7 +134,8 @@ func TestExportValue(t *testing.T) { } testFunctionType := cadence.NewFunctionType( - []cadence.Parameter{}, + nil, + nil, cadence.VoidType{}, ) @@ -2241,7 +2242,6 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { { Identifier: "f", Type: &cadence.FunctionType{ - Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, }, }, @@ -2253,7 +2253,6 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { cadence.NewInt(42), cadence.Function{ FunctionType: &cadence.FunctionType{ - Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, }, }, diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 83ca547b44..c6ecec9733 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2737,7 +2737,6 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { `, expected: cadence.Function{ FunctionType: &cadence.FunctionType{ - Parameters: []cadence.Parameter{}, ReturnType: cadence.IntType{}, }, }, @@ -2790,7 +2789,6 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { `, expected: cadence.Function{ FunctionType: &cadence.FunctionType{ - Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, }, }, diff --git a/types.go b/types.go index a2a82dcaa8..4349bfd540 100644 --- a/types.go +++ b/types.go @@ -1054,6 +1054,23 @@ func NewParameter( } } +// TypeParameter + +type TypeParameter struct { + Name string + TypeBound Type +} + +func NewTypeParameter( + name string, + typeBound Type, +) TypeParameter { + return TypeParameter{ + Name: name, + TypeBound: typeBound, + } +} + // CompositeType type CompositeType interface { @@ -1697,28 +1714,32 @@ func (t *ContractInterfaceType) Equal(other Type) bool { // TODO: type parameters type FunctionType struct { - ReturnType Type - typeID string - Parameters []Parameter + TypeParameters []TypeParameter + Parameters []Parameter + ReturnType Type + typeID string } func NewFunctionType( + typeParameters []TypeParameter, parameters []Parameter, returnType Type, ) *FunctionType { return &FunctionType{ - Parameters: parameters, - ReturnType: returnType, + TypeParameters: typeParameters, + Parameters: parameters, + ReturnType: returnType, } } func NewMeteredFunctionType( gauge common.MemoryGauge, + typeParameters []TypeParameter, parameters []Parameter, returnType Type, ) *FunctionType { common.UseMemory(gauge, common.CadenceFunctionTypeMemoryUsage) - return NewFunctionType(parameters, returnType) + return NewFunctionType(typeParameters, parameters, returnType) } func (*FunctionType) isType() {} diff --git a/values_test.go b/values_test.go index fdcbdc3611..28f1d54ffb 100644 --- a/values_test.go +++ b/values_test.go @@ -45,6 +45,7 @@ func newValueTestCases() map[string]valueTestCase { fix64, _ := NewFix64("-32.11") testFunctionType := NewFunctionType( + nil, []Parameter{ { Type: StringType{}, From 9a6490dba478333bbe8eb3f3dd85f2f49a6a64b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 24 Mar 2023 13:20:05 -0700 Subject: [PATCH 059/173] implement function type ID --- types.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/types.go b/types.go index 4349bfd540..7fa063889e 100644 --- a/types.go +++ b/types.go @@ -1745,7 +1745,34 @@ func NewMeteredFunctionType( func (*FunctionType) isType() {} func (t *FunctionType) ID() string { - // TODO: + if t.typeID == "" { + + typeParameterCount := len(t.TypeParameters) + var typeParameters []string + if typeParameterCount > 0 { + typeParameters = make([]string, typeParameterCount) + for i, typeParameter := range t.TypeParameters { + typeParameters[i] = typeParameter.Name + } + } + + parameterCount := len(t.Parameters) + var parameters []string + if parameterCount > 0 { + parameters = make([]string, parameterCount) + for i, parameter := range t.Parameters { + parameters[i] = parameter.Type.ID() + } + } + + returnType := t.ReturnType.ID() + + t.typeID = sema.FormatFunctionTypeID( + typeParameters, + parameters, + returnType, + ) + } return t.typeID } From ee360923e5aefe90b2ae5e6d99b1c1179b21fc93 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 16:46:08 -0500 Subject: [PATCH 060/173] Add CCF support for reference type as static type When static type is a reference type, encoder doesn't need to encode runtime type if runtime type is the deferenced type of static type. Also added more tests. --- encoding/ccf/ccf_test.go | 370 +++++++++++++++++++++++++++++++++++++++ encoding/ccf/decode.go | 4 + encoding/ccf/encode.go | 25 ++- 3 files changed, 391 insertions(+), 8 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 356bc5c215..52e332891f 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -6090,6 +6090,376 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { ) } +func TestEncodeValueOfReferenceType(t *testing.T) { + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + }, + } + + // ["a", "b"] with static type []&String + referenceToSimpleType := encodeTest{ + name: "array of reference to string", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.String("b"), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewStringType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":"b","type":"String"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 137(1)])), ["a", "b"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&String + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + // ["a", nil] with static type []&String? + referenceToOptionalSimpleType := encodeTest{ + name: "array of reference to optional string", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewOptionalType(cadence.NewStringType())), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 138(137(1))])), ["a", null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&String? + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // nil + 0xf6, + }, + } + + // ["a", 1] with static type []&AnyStruct + referenceToAnyStructWithSimpleTypes := encodeTest{ + name: "array of reference to any", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.NewUInt8(1), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewAnyStructType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":"1","type":"UInt8"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 137(39)])), [130([137(1), "a"]), 130([137(12), 1])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&AnyStruct + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data without inlined type + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 1 + 0x01, + }, + } + + // [FooStruct(1), FooStruct(2)] with static type []&FooStruct + referenceToStructType := encodeTest{ + name: "array of reference to struct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.String("a"), + }).WithType(simpleStructType), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, simpleStructType), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"a","type":"String"},"name":"a"}]},"type":"Struct"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"b","type":"String"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 137(1)]]])], [139(142([false, 136(h'')])), [["a"], ["b"]]]]) + // + // 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.Foo" + // fields: [["a", StringType] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // array, 2 items follow + 0x82, + // type []&S.test.Foo + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array data without inlined type + // array, 2 items follow + 0x82, + // array, 1 items follow + 0x81, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // array, 1 items follow + 0x81, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + // ["a", FooStruct("b")] with static type []&AnyStruct + referenceToAnyStructWithStructType := encodeTest{ + name: "array of reference to any with struct", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewAnyStructType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 137(1)]]])], [139(142([false, 137(39)])), [130([137(1), "a"]), 130([136(h''), ["b"]])]]]) + // + // 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.Foo" + // fields: [["a", StringType] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // array, 2 items follow + 0x82, + // type []&AnyStruct + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 1 item follows + 0x81, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + testAllEncodeAndDecode(t, + referenceToSimpleType, + referenceToOptionalSimpleType, + referenceToStructType, + referenceToAnyStructWithSimpleTypes, + referenceToAnyStructWithStructType, + ) +} + func TestEncodeSimpleTypes(t *testing.T) { t.Parallel() diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 1c950cc554..2cb291a3cb 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -364,6 +364,10 @@ func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cad case *cadence.EnumType: return d.decodeEnum(typ, types) + case *cadence.ReferenceType: + // When static type is a reference type, encoded value is its deferenced type. + return d.decodeValue(typ.Type, types) + default: err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) if err != nil { diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index bd5138cea9..4a49e1a797 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1731,19 +1731,28 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) if staticType.Equal(runtimeType) { return false } + // Here, static type is different from runtime type. - // Handle special case of runtime type being OptionalType{NeverType}. - // We handle special case of Optional{nil} because its runtime type is OptionalType{NeverType} - // while its static type can be different, such as OptionalType{AddressType}. - // For example, TokensDeposited event is defined as `TokensDeposited(amount: UFix64, to: Address?)`, - // field to's type is OptionalType{AddressType} and its value can be nil with runtime type - // OptionalType{NeverType}. Even though runtime type is different from static type (field type), - // encoder encodes nil value without encoding its runtime type. - if _, ok := staticType.(*cadence.OptionalType); ok { + + switch typ := staticType.(type) { + case *cadence.OptionalType: + // Handle special case of runtime type being OptionalType{NeverType}. + // We handle special case of Optional{nil} because its runtime type is OptionalType{NeverType} + // while its static type can be different, such as OptionalType{AddressType}. + // For example, TokensDeposited event is defined as `TokensDeposited(amount: UFix64, to: Address?)`, + // field to's type is OptionalType{AddressType} and its value can be nil with runtime type + // OptionalType{NeverType}. Even though runtime type is different from static type (field type), + // encoder encodes nil value without encoding its runtime type. if isOptionalNeverType(runtimeType) { return false } + + case *cadence.ReferenceType: + // Handle special case of static type being ReferenceType. + // Encoder doesn't need to encode runtime type if runtime type is the deferenced type of static type. + return needToEncodeRuntimeType(typ.Type, runtimeType) } + return true } From 0e6fb3612e627a28ebd726a61b0515de64e48863 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:18:46 -0500 Subject: [PATCH 061/173] Add more CCF tests --- encoding/ccf/ccf_test.go | 320 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 318 insertions(+), 2 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 52e332891f..464b4da327 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5286,6 +5286,17 @@ func TestEncodeEvent(t *testing.T) { t.Parallel() + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "c", + Type: cadence.StringType{}, + }, + }, + } + simpleEventType := &cadence.EventType{ Location: utils.TestLocation, QualifiedIdentifier: "FooEvent", @@ -5387,6 +5398,153 @@ func TestEncodeEvent(t *testing.T) { }, } + abstractEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.AnyStructType{}, + }, + }, + } + + abstractEvent := encodeTest{ + name: "abstract event", + val: cadence.NewEvent( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }, + ).WithType(abstractEventType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooEvent","fields":[{"value":{"value":"1","type":"Int"},"name":"a"},{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"c"}]},"type":"Struct"},"name":"b"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(39)]]]), 160([h'01', "S.test.FooStruct", [["c", 137(1)]]])], [136(h''), [1, 130([136(h'01'), ["b"]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(anystruct)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // struct type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooStruct" + // 1 fields: [["c", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // 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, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + }, + } + resourceEventType := &cadence.EventType{ Location: utils.TestLocation, QualifiedIdentifier: "FooEvent", @@ -5530,13 +5688,24 @@ func TestEncodeEvent(t *testing.T) { }, } - testAllEncodeAndDecode(t, simpleEvent, resourceEvent) + testAllEncodeAndDecode(t, simpleEvent, resourceEvent, abstractEvent) } func TestEncodeContract(t *testing.T) { t.Parallel() + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "c", + Type: cadence.StringType{}, + }, + }, + } + simpleContractType := &cadence.ContractType{ Location: utils.TestLocation, QualifiedIdentifier: "FooContract", @@ -5638,6 +5807,153 @@ func TestEncodeContract(t *testing.T) { }, } + abstractContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.AnyStructType{}, + }, + }, + } + + abstractContract := encodeTest{ + name: "abstract contract", + val: cadence.NewContract( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }, + ).WithType(abstractContractType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"1","type":"Int"},"name":"a"},{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"c"}]},"type":"Struct"},"name":"b"}]},"type":"Contract"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["c", 137(1)]]]), 163([h'01', "S.test.FooContract", [["a", 137(4)], ["b", 137(39)]]])], [136(h'01'), [1, 130([136(h''), ["b"]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 1 fields: [["c", type(string)]] + // 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, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // contract type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follow + 0x40, + // array, 1 item follows + 0x81, + // String, 1 bytes follow + 0x61, + // "b" + 0x62, + }, + } + resourceContractType := &cadence.ContractType{ Location: utils.TestLocation, QualifiedIdentifier: "FooContract", @@ -5781,7 +6097,7 @@ func TestEncodeContract(t *testing.T) { }, } - testAllEncodeAndDecode(t, simpleContract, resourceContract) + testAllEncodeAndDecode(t, simpleContract, abstractContract, resourceContract) } func TestEncodeEnum(t *testing.T) { From 82cbfd35d594f1083c46079b0a0d9774e08b2611 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:28:10 -0500 Subject: [PATCH 062/173] Remove traversing cadence.Enum for composite type This isn't necessary because cadence.Enum field is an integer subtype. --- encoding/ccf/traverse_value.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 32ab8ebe84..f971934e10 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -72,6 +72,8 @@ func (ct *compositeTypes) traverseValue(v cadence.Value) { } // Traverse v's elements for runtime types. + // Note: don't need to traverse fields of cadence.Enum + // because enum's field is an integer subtype. switch x := v.(type) { case cadence.Optional: @@ -108,10 +110,6 @@ func (ct *compositeTypes) traverseValue(v cadence.Value) { ct.traverseValue(field) } - case cadence.Enum: - for _, field := range x.Fields { - ct.traverseValue(field) - } } } From 42a364cc71b2d901e2da296849de8946c893c8c4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:47:15 -0500 Subject: [PATCH 063/173] Add more CCF tests --- encoding/ccf/ccf_test.go | 153 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 464b4da327..ee5982b0cc 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5049,6 +5049,62 @@ func TestEncodeStruct(t *testing.T) { t.Parallel() + noFieldStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{}, + } + + noFieldStruct := encodeTest{ + name: "no field", + val: cadence.NewStruct( + []cadence.Value{}, + ).WithType(noFieldStructType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooStruct","fields":[]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", []])], [136(h''), []]]) + // + // 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" + // 0 fields: [] + // 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, + // array, 0 item follows + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + }, + } + simpleStructType := &cadence.StructType{ Location: utils.TestLocation, QualifiedIdentifier: "FooStruct", @@ -5279,7 +5335,7 @@ func TestEncodeStruct(t *testing.T) { }, } - testAllEncodeAndDecode(t, simpleStruct, resourceStruct) + testAllEncodeAndDecode(t, noFieldStruct, simpleStruct, resourceStruct) } func TestEncodeEvent(t *testing.T) { @@ -7064,6 +7120,55 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with static struct with no field", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[],"initializers":[]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + }) + t.Run("with static struct", func(t *testing.T) { testEncodeAndDecode( @@ -7974,6 +8079,52 @@ func TestEncodeType(t *testing.T) { ) }) + t.Run("with static no restricted type", func(t *testing.T) { + + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }).WithID("Int{String}"), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{String}","type":{"kind":"Int"},"restrictions":[]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([185(4), []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }), + }, + ) + }) + t.Run("with static restricted type", func(t *testing.T) { testEncodeAndDecodeEx( From 241ec38063e3542d0bbdef1ed6a96d842d746f4d Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:57:35 -0500 Subject: [PATCH 064/173] Add sanity check for retrieved sorted field from cache CCF Specs doesn't require this check but it can be useful to check for this and panic during fuzzing. "Valid CCF Encoding Requirements" in CCF Specs says, "Encoders MUST produce valid CCF encodings from valid input items. Encoders are not required to check for invalid input items (e.g. invalid UTF-8 strings, duplicate dictionary keys, etc.) Applications MUST NOT provide invalid items to encoders." This is a check for applications violating the CCF specs. Co-authored-by: Supun Setunga --- encoding/ccf/encode.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 4a49e1a797..2317bf9020 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -921,6 +921,10 @@ func (e *Encoder) encodeComposite( default: sortedIndexes := e.getSortedFieldIndex(typ) + if len(sortedIndexes) != len(staticFieldTypes) { + panic(cadenceErrors.NewUnexpectedError("number of sorted indexes doesn't match number of field types")) + } + for _, index := range sortedIndexes { // Encode sorted field as value. err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) From 27d1ec6c31b9663d8303028e5ad7fdb2d1313dd5 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Mar 2023 17:13:23 -0500 Subject: [PATCH 065/173] Check unexpected nil type in CCF decoder Also added tests for truncated, malformed, and invalid data. --- encoding/ccf/ccf_test.go | 698 +++++++++++++++++++++++++++++++++ encoding/ccf/decode.go | 23 ++ encoding/ccf/decode_type.go | 35 ++ encoding/ccf/decode_typedef.go | 4 + 4 files changed, 760 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index ee5982b0cc..d73166c07c 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -10886,3 +10886,701 @@ func testEventEquality(t *testing.T, event1 cadence.Event, event2 cadence.Event) require.True(t, foundField) } } + +func TestDecodeTruncatedData(t *testing.T) { + data, err := ccf.Encode(createFlowTokenTokensWithdrawnEvent()) + require.NoError(t, err) + + _, err = ccf.Decode(nil, data) + require.NoError(t, err) + + for i := len(data) - 1; i >= 0; i-- { + decodedVal, err := ccf.Decode(nil, data[:i]) + require.Nil(t, decodedVal) + require.Error(t, err) + } +} + +func TestDecodeInvalidData(t *testing.T) { + type testCase struct { + name string + data []byte + } + + testCases := []testCase{ + { + name: "nil", + data: nil, + }, + { + name: "empty", + data: []byte{}, + }, + { + name: "malformed CBOR data for potential OOM", + data: []byte{0x9b, 0x00, 0x00, 0x42, 0xfa, 0x42, 0xfa, 0x42, 0xfa, 0x42}, + }, + { + name: "mismatched type and value", + data: []byte{ + // language=edn, format=ccf + // 130([137(1), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // true + 0xf5, + }, + }, + { + name: "not found type defintion", + data: []byte{ + // language=edn, format=ccf + // 130([136(h''), [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil type", + data: []byte{ + // language=edn, format=ccf + // 130([null, true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // nil + 0xf6, + // true + 0xf5, + }, + }, + { + name: "nil type definitions", + data: []byte{ + // language=edn, format=ccf + // 129(null, [137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // nil + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + name: "nil type definition", + data: []byte{ + // language=edn, format=ccf + // 129([null], [137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // array, 1 items follow + 0x81, + // nil + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + name: "nil optional inner type", + data: []byte{ + // language=edn, format=ccf + // 130([138(null), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + name: "nil element type in constant sized array", + data: []byte{ + // language=edn, format=ccf + // 130([140[1, null], [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type constant-sized [1]nil + // tag + 0xd8, ccf.CBORTagConstsizedArrayType, + // array, 2 items follow + 0x82, + // number of elements + 0x01, + // nil + 0xf6, + // array data without inlined type definition + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil element type in variable sized array", + data: []byte{ + // language=edn, format=ccf + // 130([139(null), [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []nil + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // null + 0xf6, + // array data without inlined type definition + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil key type in dictionary type", + data: []byte{ + // language=edn, format=ccf + // 130([141([nil, 137(4)]), ["a", 1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[nil]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // null + 0xf6, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil element type in dictionary type", + data: []byte{ + // language=edn, format=ccf + // 130([141([137(1), nil]), ["a", 1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[int]nil) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // null + 0xf6, + // array data without inlined type definition + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil composite field type", + data: []byte{ + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", nil], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // 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" + // 2 fields: [["a", nil], ["b", type(string)]] + // 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, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // null + 0xf6, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + }, + { + name: "nil inner type in optional type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 186(null)]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // null + 0xf6, + }, + }, + { + name: "nil element type in constant sized array type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 188([3, null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // null + 0xf6, + }, + }, + { + name: "nil element type in variable sized array type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 187(null)]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + // null + 0xf6, + }, + }, + { + name: "nil key type in dictionary type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 189([null, 185(1)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // null + 0xf6, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "nil element type in dictionary type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 189([185(4), null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // null + 0xf6, + }, + }, + { + name: "nil field type in struct type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", null]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // null + 0xf6, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "nil initializer type in struct type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", null]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // null + 0xf6, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "null restriction in restricted type value", + // Data is generated by fuzzer. + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 191([208([h'', "S.\ufffd0000000.000000", null, [], []]), [null]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 items follow + 0x85, + // ccf type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 19 bytes follow + 0x73, + // "S.�0000000.000000" + 0x53, 0x2e, 0xef, 0xbf, 0xbd, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + // type + // nil + 0xf6, + // fields + // array, 0 item follows + 0x80, + // initializers + // array, 0 item follows + 0x80, + // restrictions + // array, 1 item follows + 0x81, + // nil + 0xf6, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decodedVal, err := ccf.Decode(nil, tc.data) + require.Nil(t, decodedVal) + require.Error(t, err) + }) + } +} diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 2cb291a3cb..e2fc015dea 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -244,6 +244,9 @@ func (d *Decoder) decodeTypeAndValue(types cadenceTypeByCCFTypeID) (cadence.Valu // / fix64-value // / ufix64-value func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cadence.Value, error) { + if t == nil { + return nil, fmt.Errorf("unexpected nil type") + } // "Deterministic CCF Encoding Requirements" in CCF specs: // @@ -1412,6 +1415,9 @@ func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.T typ cadence.Type, inits [][]cadence.Parameter, ) (cadence.Type, error) { + if typ == nil { + return nil, fmt.Errorf("encoded enum-type-value has nil type") + } return cadence.NewMeteredEnumType( d.gauge, location, @@ -1554,6 +1560,11 @@ func (d *Decoder) decodeCompositeTypeValue( return nil, err } + if compositeType == nil { + // Sanity check that compositeType isn't nil. + return nil, errors.New("unexpected nil composite type value") + } + // "Deterministic CCF Encoding Requirements" in CCF specs: // // "composite-type-value.id MUST be identical to the zero-based encoding order type-value." @@ -1710,6 +1721,10 @@ func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]c return nil, err } + if count == 0 { + return []cadence.Parameter{}, nil + } + parameterTypes := make([]cadence.Parameter, count) parameterLabels := make(map[string]struct{}, count) parameterIdentifiers := make(map[string]struct{}, count) @@ -1792,6 +1807,10 @@ func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cade return cadence.Parameter{}, err } + if t == nil { + return cadence.Parameter{}, errors.New("unexpected nil parameter type") + } + // Unmetered because decodeParamTypeValue is metered in decodeParamTypeValues and called nowhere else // Type is metered. return cadence.NewParameter(label, identifier, t), nil @@ -1830,6 +1849,10 @@ func (d *Decoder) decodeFunctionTypeValue(visited cadenceTypeByCCFTypeID) (caden return nil, err } + if returnType == nil { + return nil, errors.New("unexpected nil function return type") + } + return cadence.NewMeteredFunctionType( d.gauge, "", diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 2e37f07bcb..6e53c93d3d 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -19,6 +19,7 @@ package ccf import ( + "errors" "fmt" "github.com/onflow/cadence" @@ -274,6 +275,9 @@ func (d *Decoder) decodeOptionalType( if err != nil { return nil, err } + if elementType == nil { + return nil, errors.New("unexpected nil type as optional inner type") + } return cadence.NewMeteredOptionalType(d.gauge, elementType), nil } @@ -299,6 +303,9 @@ func (d *Decoder) decodeVarSizedArrayType( if err != nil { return nil, err } + if elementType == nil { + return nil, errors.New("unexpected nil type as variable sized array element type") + } return cadence.NewMeteredVariableSizedArrayType(d.gauge, elementType), nil } @@ -343,6 +350,10 @@ func (d *Decoder) decodeConstantSizedArrayType( return nil, err } + if elementType == nil { + return nil, errors.New("unexpected nil type as constant sized array element type") + } + return cadence.NewMeteredConstantSizedArrayType(d.gauge, uint(size), elementType), nil } @@ -381,12 +392,20 @@ func (d *Decoder) decodeDictType( return nil, err } + if keyType == nil { + return nil, errors.New("unexpected nil type as dictionary key type") + } + // element 1: element type (inline-type or type-value) elementType, err := decodeTypeFn(types) if err != nil { return nil, err } + if elementType == nil { + return nil, errors.New("unexpected nil type as dictionary element type") + } + return cadence.NewMeteredDictionaryType(d.gauge, keyType, elementType), nil } @@ -427,6 +446,10 @@ func (d *Decoder) decodeCapabilityType( return nil, err } + if borrowType == nil { + return nil, errors.New("unexpected nil type as capability borrow type") + } + return cadence.NewMeteredCapabilityType(d.gauge, borrowType), nil } @@ -471,6 +494,10 @@ func (d *Decoder) decodeReferenceType( return nil, err } + if elementType == nil { + return nil, errors.New("unexpected nil type as reference type") + } + return cadence.NewMeteredReferenceType(d.gauge, authorized, elementType), nil } @@ -509,6 +536,10 @@ func (d *Decoder) decodeRestrictedType( return nil, err } + if typ == nil { + return nil, errors.New("unexpected nil type as restricted type") + } + // element 1: restrictions restrictionCount, err := d.dec.DecodeArrayHead() if err != nil { @@ -526,6 +557,10 @@ func (d *Decoder) decodeRestrictedType( return nil, err } + if restrictedType == nil { + return nil, errors.New("unexpected nil type as restriction type") + } + restrictedTypeID := restrictedType.ID() // "Valid CCF Encoding Requirements" in CCF specs: diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 3cd88f25ae..01ca6e9032 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -425,6 +425,10 @@ func (d *Decoder) decodeCompositeField(types cadenceTypeByCCFTypeID, decodeTypeF return cadence.Field{}, err } + if fieldType == nil { + return cadence.Field{}, errors.New("unexpected nil type as composite field type") + } + // Unmetered because decodeCompositeField is metered in decodeCompositeFields and called nowhere else // fieldType is still metered. return cadence.NewField(fieldName, fieldType), nil From 3245e84c49bc1b0b6090db5cc8aae93c20748e56 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Mar 2023 17:18:45 -0500 Subject: [PATCH 066/173] Add test for decoding extraneous CCF data --- encoding/ccf/ccf_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index d73166c07c..28f087e557 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -11574,6 +11574,27 @@ func TestDecodeInvalidData(t *testing.T) { 0xf6, }, }, + { + name: "extraneous data", + data: []byte{ + // language=edn, format=ccf + // 130([137(0), true]), 0 + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + // extraneous data + 0x00, + }, + }, } for _, tc := range testCases { From 205d09c7f0fb4beec4af27ffd078d838a7697b36 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 28 Mar 2023 17:21:45 -0500 Subject: [PATCH 067/173] Fix lint error --- encoding/ccf/ccf_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 28f087e557..9f6b9d15d8 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -10940,7 +10940,7 @@ func TestDecodeInvalidData(t *testing.T) { }, }, { - name: "not found type defintion", + name: "not found type definition", data: []byte{ // language=edn, format=ccf // 130([136(h''), [1]]) From 70efc6889738c218f21292a71a86de7ff68b9233 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 29 Mar 2023 14:50:18 -0500 Subject: [PATCH 068/173] Fix restricted types sorting in CCF Supun found this with fuzzer. Thanks Supun! --- encoding/ccf/ccf_test.go | 149 ++++++++++++++++++++++++++++++++++++++- encoding/ccf/sort.go | 4 ++ types.go | 5 ++ 3 files changed, 157 insertions(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 9f6b9d15d8..14557de85f 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -8180,7 +8180,7 @@ func TestEncodeType(t *testing.T) { }) - t.Run("with static multiple restricted type", func(t *testing.T) { + t.Run("with static 2 restricted types", func(t *testing.T) { testEncodeAndDecodeEx( t, @@ -8240,6 +8240,153 @@ func TestEncodeType(t *testing.T) { ) }) + t.Run("with static 3 restricted types", func(t *testing.T) { + + // restrictedType is generated by fuzzer. + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"","type":{"kind":"AnyStruct"},"restrictions":[{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeA","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeB","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"I.LocationC.TypeC","fields":[],"initializers":[]}]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([185(39), [224([h'', "I.LocationC.TypeC", null, [], []]), 224([h'01', "A.0100000000000000.TypeA", null, [], []]), 224([h'02', "A.0100000000000000.TypeB", null, [], []])]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type ID (39) + 0x18, 0x27, + // 3 sorted restrictions + // array, 3 element follows + 0x83, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 17 bytes follow + 0x71, + // "I.LocationC.TypeC" + 0x49, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x43, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeA" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x41, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeB" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x42, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType has sorted restrictions and no type ID. + cadence.TypeValue{ + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, + }, + }, + ) + }) + t.Run("without static type", func(t *testing.T) { t.Parallel() diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go index 226504c922..08ed79e39c 100644 --- a/encoding/ccf/sort.go +++ b/encoding/ccf/sort.go @@ -190,8 +190,12 @@ func (t bytewiseCadenceTypeSorter) Swap(i, j int) { } func (t bytewiseCadenceTypeSorter) Less(i, j int) bool { + i = t.indexes[i] + j = t.indexes[j] + iID := t.types[i].ID() jID := t.types[j].ID() + if len(iID) != len(jID) { return len(iID) < len(jID) } diff --git a/types.go b/types.go index dab86a4d3e..d4f28ad96b 100644 --- a/types.go +++ b/types.go @@ -2318,6 +2318,11 @@ func TypeWithCachedTypeID(t Type) Type { TypeWithCachedTypeID(p.Type) } } + + case *RestrictedType: + for _, restriction := range t.Restrictions { + TypeWithCachedTypeID(restriction) + } } return t From 6fa154ede4c6462306eb26e2e2126333b868439c Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:50:56 -0500 Subject: [PATCH 069/173] Fix CCF encoding of value of optional reference type Supun found this by fuzzing. Thanks Supun! --- encoding/ccf/ccf_test.go | 78 ++++++++++++++++++++++++++++++++++++++++ encoding/ccf/encode.go | 6 ++++ 2 files changed, 84 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 14557de85f..f3a9378d2d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -6568,6 +6568,83 @@ func TestEncodeValueOfReferenceType(t *testing.T) { }, } + dictionaryType := &cadence.DictionaryType{ + KeyType: cadence.TheStringType, + ElementType: &cadence.OptionalType{ + Type: &cadence.ReferenceType{ + Type: cadence.TheInt128Type, + Authorized: false, + }, + }, + } + + // dictionary is generated by fuzzer. + dictionary := cadence.Dictionary{ + DictionaryType: dictionaryType, + Pairs: []cadence.KeyValuePair{ + { + Key: cadence.String("one"), + Value: cadence.Optional{ + Value: cadence.Int128{ + Value: big.NewInt(7456), + }, + }, + }, + }, + } + + // {"one": 7456} + optionalReferenceToSimpleType := encodeTest{ + name: "dictionary of optional reference to Int", + val: dictionary, + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"key":{"value":"one","type":"String"},"value":{"value":{"value":"7456","type":"Int128"},"type":"Optional"}}],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(1), 138(142([false, 137(9)]))]), ["one", 7456]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type {string: &Int128?} + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // "one" + 0x6f, 0x6e, 0x65, + // tag (big num) + 0xc2, + // bytes, 2 bytes follow + 0x42, + // 7456 + 0x1d, 0x20, + }, + } + // ["a", 1] with static type []&AnyStruct referenceToAnyStructWithSimpleTypes := encodeTest{ name: "array of reference to any", @@ -6826,6 +6903,7 @@ func TestEncodeValueOfReferenceType(t *testing.T) { testAllEncodeAndDecode(t, referenceToSimpleType, referenceToOptionalSimpleType, + optionalReferenceToSimpleType, referenceToStructType, referenceToAnyStructWithSimpleTypes, referenceToAnyStructWithStructType, diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 2317bf9020..5832ddad33 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1751,6 +1751,12 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) return false } + // Handle special case of static type being OptionalType{ReferenceType} + // since runtime type is optional type of the deferenced type. + if or, ok := runtimeType.(*cadence.OptionalType); ok { + return needToEncodeRuntimeType(typ.Type, or.Type) + } + case *cadence.ReferenceType: // Handle special case of static type being ReferenceType. // Encoder doesn't need to encode runtime type if runtime type is the deferenced type of static type. From 330b9a8929170641aaf0aaaae29efd0684b00814 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 29 Mar 2023 17:49:52 -0500 Subject: [PATCH 070/173] Fix CCF decoding of type value of recursive inits Supun found this by fuzzing. Thanks Supun! --- encoding/ccf/ccf_test.go | 390 ++++++++++++++++++++++++++++++++++++++- encoding/ccf/decode.go | 111 ++++++----- 2 files changed, 452 insertions(+), 49 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index f3a9378d2d..aaca3b205d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7342,6 +7342,144 @@ func TestEncodeType(t *testing.T) { }, ) }) + + t.Run("with static resource of composite fields and initializers", func(t *testing.T) { + + t.Parallel() + + fooTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + fooTy2 := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo2", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + barTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + Fields: []cadence.Field{ + { + Identifier: "foo1", + Type: fooTy, + }, + }, + Initializers: [][]cadence.Parameter{ + { + cadence.Parameter{ + Type: fooTy2, + Label: "aaa", + Identifier: "aaa", + }, + }, + }, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: barTy, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])]], [[["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo1 + 0x66, 0x6f, 0x6f, 0x31, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 elements follow + 0x80, + // initializer + // array, 0 elements follow + 0x80, + // initializers + // array, 1 elements follow + 0x81, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // text, 11 bytes follow + 0x6b, + // S.test.Foo2 + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + ) + }) + t.Run("with static resource", func(t *testing.T) { testEncodeAndDecode( @@ -9051,7 +9189,7 @@ func TestExportTypeValueRecursiveType(t *testing.T) { t.Parallel() - t.Run("recursive", func(t *testing.T) { + t.Run("recursive field", func(t *testing.T) { t.Parallel() @@ -9125,6 +9263,175 @@ func TestExportTypeValueRecursiveType(t *testing.T) { }) + t.Run("recursive initializer", func(t *testing.T) { + + // structType is generated by fuzzer. + structType := cadence.NewStructType( + common.StringLocation("foo"), + "Foo", + []cadence.Field{}, + [][]cadence.Parameter{{{}}}, + ) + + structType.Initializers[0][0] = cadence.Parameter{ + Type: &cadence.OptionalType{Type: structType}, + Label: "aaa", + Identifier: "aaa", + } + + typeValue := cadence.NewTypeValue(structType) + + testEncodeAndDecode( + t, + typeValue, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.foo.Foo","fields":[],"initializers":[[{"type":{"type":"S.foo.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.foo.Foo", null, [], [[["aaa", "aaa", 186(184(h''))]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 9 bytes follow + 0x69, + // "S.foo.Foo" + 0x53, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 1 element follows + 0x81, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows, + 0x40, + }, + ) + }) + + t.Run("recursive field and initializer", func(t *testing.T) { + + // structType is generated by fuzzer. + structType := cadence.NewStructType( + common.StringLocation("foo"), + "Foo", + []cadence.Field{{}}, + [][]cadence.Parameter{{{}}}, + ) + + structType.Fields[0] = cadence.Field{ + Type: &cadence.OptionalType{Type: structType}, + Identifier: "aa", + } + + structType.Initializers[0][0] = cadence.Parameter{ + Type: &cadence.OptionalType{Type: structType}, + Label: "aaa", + Identifier: "aaa", + } + + typeValue := cadence.NewTypeValue(structType) + + testEncodeAndDecode( + t, + typeValue, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.foo.Foo","fields":[{"type":{"type":"S.foo.Foo","kind":"Optional"},"id":"aa"}],"initializers":[[{"type":{"type":"S.foo.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.foo.Foo", null, [["aa", 186(184(h''))]], [[["aaa", "aaa", 186(184(h''))]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 9 bytes follow + 0x69, + // "S.foo.Foo" + 0x53, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // text, 2 bytes follow + 0x62, + // "aa" + 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows + 0x40, + // initializers + // array, 1 element follows + 0x81, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows, + 0x40, + }, + ) + }) + t.Run("non-recursive, repeated", func(t *testing.T) { t.Parallel() @@ -9136,6 +9443,13 @@ func TestExportTypeValueRecursiveType(t *testing.T) { Initializers: [][]cadence.Parameter{}, } + fooTy2 := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo2", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + barTy := &cadence.ResourceType{ Location: utils.TestLocation, QualifiedIdentifier: "Bar", @@ -9149,7 +9463,22 @@ func TestExportTypeValueRecursiveType(t *testing.T) { Type: fooTy, }, }, - Initializers: [][]cadence.Parameter{}, + Initializers: [][]cadence.Parameter{ + { + cadence.Parameter{ + Type: &cadence.OptionalType{Type: fooTy}, + Label: "aaa", + Identifier: "aaa", + }, + }, + { + cadence.Parameter{ + Type: fooTy2, + Label: "bbb", + Identifier: "bbb", + }, + }, + }, } testEncodeAndDecode( @@ -9158,10 +9487,10 @@ func TestExportTypeValueRecursiveType(t *testing.T) { StaticType: barTy, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Bar","fields":[{"id":"foo1","type":{"kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[],"type":""}},{"id":"foo2","type":"S.test.Foo"}],"initializers":[],"type":""}}} + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], []])]) + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], [[["aaa", "aaa", 186(184(h'01'))]], [["bbb", "bbb", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) // // language=cbor, format=ccf // tag @@ -9226,7 +9555,58 @@ func TestExportTypeValueRecursiveType(t *testing.T) { // 1 0x01, // initializers - // array, 0 elements follow + // array, 2 elements follow + 0x82, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // bbb + 0x62, 0x62, 0x62, + // text, 3 bytes follow + 0x63, + // bbb + 0x62, 0x62, 0x62, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // text, 11 bytes follow + 0x6b, + // S.test.Foo2 + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows 0x80, }, ) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index e2fc015dea..6e80d4ac4e 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1283,7 +1283,6 @@ func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1296,7 +1295,7 @@ func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence location, qualifiedIdentifier, nil, - inits, + nil, ), nil } @@ -1314,7 +1313,6 @@ func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (caden location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1327,7 +1325,7 @@ func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (caden location, qualifiedIdentifier, nil, - inits, + nil, ), nil } @@ -1345,7 +1343,6 @@ func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence. location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1353,18 +1350,12 @@ func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence. typ.ID(), ) } - if len(inits) != 1 { - return nil, fmt.Errorf( - "encoded event-type-value has %d initializations (expected 1 initialization)", - len(inits), - ) - } return cadence.NewMeteredEventType( d.gauge, location, qualifiedIdentifier, nil, - inits[0], + nil, ), nil } @@ -1382,7 +1373,6 @@ func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (caden location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1395,7 +1385,7 @@ func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (caden location, qualifiedIdentifier, nil, - inits, + nil, ), nil } @@ -1413,7 +1403,6 @@ func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.T location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ == nil { return nil, fmt.Errorf("encoded enum-type-value has nil type") @@ -1424,7 +1413,7 @@ func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.T qualifiedIdentifier, typ, nil, - inits, + nil, ), nil } @@ -1442,7 +1431,6 @@ func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1455,7 +1443,7 @@ func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) location, qualifiedIdentifier, nil, - inits, + nil, ), nil } @@ -1473,7 +1461,6 @@ func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeI location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1486,7 +1473,7 @@ func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeI location, qualifiedIdentifier, nil, - inits, + nil, ), nil } @@ -1504,7 +1491,6 @@ func (d *Decoder) decodeContractInterfaceTypeValue(visited cadenceTypeByCCFTypeI location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) { if typ != nil { return nil, fmt.Errorf( @@ -1517,7 +1503,7 @@ func (d *Decoder) decodeContractInterfaceTypeValue(visited cadenceTypeByCCFTypeI location, qualifiedIdentifier, nil, - inits, + nil, ), nil } return d.decodeCompositeTypeValue(visited, ctr) @@ -1527,16 +1513,15 @@ type compositeTypeConstructor func( location common.Location, qualifiedIdentifier string, typ cadence.Type, - inits [][]cadence.Parameter, ) (cadence.Type, error) type compositeTypeValue struct { - ccfID ccfTypeID - location common.Location - identifier string - typ cadence.Type - rawField []byte - initializerTypes [][]cadence.Parameter + ccfID ccfTypeID + location common.Location + identifier string + typ cadence.Type + rawFields []byte + rawInitializers []byte } // decodeCompositeTypeValue decodes composite-type-value. @@ -1554,7 +1539,6 @@ func (d *Decoder) decodeCompositeTypeValue( compTypeValue.location, compTypeValue.identifier, compTypeValue.typ, - compTypeValue.initializerTypes, ) if err != nil { return nil, err @@ -1584,18 +1568,57 @@ func (d *Decoder) decodeCompositeTypeValue( } // Decode fields after type is resolved to handle recursive types. - dec := NewDecoder(d.gauge, compTypeValue.rawField) + dec := NewDecoder(d.gauge, compTypeValue.rawFields) fields, err := dec.decodeCompositeFields(visited, dec._decodeTypeValue) if err != nil { return nil, err } - switch compositeType := compositeType.(type) { - case cadence.CompositeType: - compositeType.SetCompositeFields(fields) + // Decode initializers after type is resolved to handle recursive types. + dec = NewDecoder(d.gauge, compTypeValue.rawInitializers) + initializers, err := dec.decodeInitializerTypeValues(visited) + if err != nil { + return nil, err + } + + switch typ := compositeType.(type) { + case *cadence.StructType: + typ.Fields = fields + typ.Initializers = initializers + + case *cadence.ResourceType: + typ.Fields = fields + typ.Initializers = initializers + + case *cadence.EventType: + if len(initializers) != 1 { + return nil, fmt.Errorf( + "encoded event-type-value has %d initializations (expected 1 initialization)", + len(initializers), + ) + } + typ.Fields = fields + typ.Initializer = initializers[0] + + case *cadence.ContractType: + typ.Fields = fields + typ.Initializers = initializers + + case *cadence.EnumType: + typ.Fields = fields + typ.Initializers = initializers + + case *cadence.StructInterfaceType: + typ.Fields = fields + typ.Initializers = initializers + + case *cadence.ResourceInterfaceType: + typ.Fields = fields + typ.Initializers = initializers - case cadence.InterfaceType: - compositeType.SetInterfaceFields(fields) + case *cadence.ContractInterfaceType: + typ.Fields = fields + typ.Initializers = initializers } return compositeType, nil @@ -1652,24 +1675,24 @@ func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (*co } // element 3: fields - rawField, err := d.dec.DecodeRawBytes() + rawFields, err := d.dec.DecodeRawBytes() if err != nil { return nil, err } // element 4: initializers - initializerTypes, err := d.decodeInitializerTypeValues(visited) + rawInitializers, err := d.dec.DecodeRawBytes() if err != nil { return nil, err } return &compositeTypeValue{ - ccfID: ccfID, - location: location, - identifier: identifier, - typ: typ, - rawField: rawField, - initializerTypes: initializerTypes, + ccfID: ccfID, + location: location, + identifier: identifier, + typ: typ, + rawFields: rawFields, + rawInitializers: rawInitializers, }, nil } From 06a6d734d94bf80c82397d1fa9827be881d1ffbb Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 29 Mar 2023 18:18:04 -0500 Subject: [PATCH 071/173] Check encoded but unreferenced type def in CCF --- encoding/ccf/ccf_test.go | 120 +++++++++++++++++++++++++++++++ encoding/ccf/ccf_type_id.go | 40 +++++++++-- encoding/ccf/ccf_type_id_test.go | 3 +- encoding/ccf/decode.go | 70 ++++++++++-------- encoding/ccf/decode_type.go | 31 +++++--- encoding/ccf/decode_typedef.go | 28 ++++---- 6 files changed, 228 insertions(+), 64 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index aaca3b205d..920e21da8d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -11569,6 +11569,126 @@ func TestDecodeInvalidData(t *testing.T) { 0x01, }, }, + { + name: "unreferenced type definition", + data: []byte{ + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(1)]]]), 160([h'1', "S.test.FooStruct", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // 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, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + }, { name: "nil type", data: []byte{ diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go index 0caa3d73f6..3335562d03 100644 --- a/encoding/ccf/ccf_type_id.go +++ b/encoding/ccf/ccf_type_id.go @@ -57,21 +57,47 @@ func (types ccfTypeIDByCadenceType) id(t cadence.Type) (ccfTypeID, error) { return id, nil } -type cadenceTypeByCCFTypeID map[ccfTypeID]cadence.Type +type cadenceTypeByCCFTypeID struct { + types map[ccfTypeID]cadence.Type + referencedTypes map[ccfTypeID]struct{} +} + +func newCadenceTypeByCCFTypeID() *cadenceTypeByCCFTypeID { + return &cadenceTypeByCCFTypeID{ + types: make(map[ccfTypeID]cadence.Type), + referencedTypes: make(map[ccfTypeID]struct{}), + } +} -func (ids cadenceTypeByCCFTypeID) add(id ccfTypeID, typ cadence.Type) bool { - _, ok := ids[id] - if ok { +func (ids *cadenceTypeByCCFTypeID) add(id ccfTypeID, typ cadence.Type) bool { + if ids.has(id) { return false } - ids[id] = typ + ids.types[id] = typ return true } -func (ids cadenceTypeByCCFTypeID) typ(id ccfTypeID) (cadence.Type, error) { - t, ok := ids[id] +func (ids *cadenceTypeByCCFTypeID) reference(id ccfTypeID) { + ids.referencedTypes[id] = struct{}{} +} + +func (ids *cadenceTypeByCCFTypeID) typ(id ccfTypeID) (cadence.Type, error) { + t, ok := ids.types[id] if !ok { return nil, fmt.Errorf("type not found for CCF type ID %d", id) } return t, nil } + +func (ids *cadenceTypeByCCFTypeID) has(id ccfTypeID) bool { + _, ok := ids.types[id] + return ok +} + +func (ids *cadenceTypeByCCFTypeID) count() int { + return len(ids.types) +} + +func (ids *cadenceTypeByCCFTypeID) hasUnreferenced() bool { + return len(ids.types) > len(ids.referencedTypes) +} diff --git a/encoding/ccf/ccf_type_id_test.go b/encoding/ccf/ccf_type_id_test.go index 8d4f810b29..2ffbaa9584 100644 --- a/encoding/ccf/ccf_type_id_test.go +++ b/encoding/ccf/ccf_type_id_test.go @@ -84,8 +84,7 @@ func TestCCFTypeIDByCadenceType(t *testing.T) { } func TestCadenceTypeByCCFTypeID(t *testing.T) { - // Create cadenceTypeByCCFTypeID map - cadenceTypes := make(cadenceTypeByCCFTypeID) + cadenceTypes := newCadenceTypeByCCFTypeID() // Add new entry. newType := cadenceTypes.add(newCCFTypeIDFromUint64(0), simpleStructType()) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 6e80d4ac4e..5e60b7c8a0 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -125,7 +125,7 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { case CBORTagTypeAndValue: // Decode ccf-type-and-value-message. - return d.decodeTypeAndValue(cadenceTypeByCCFTypeID{}) + return d.decodeTypeAndValue(newCadenceTypeByCCFTypeID()) default: return nil, fmt.Errorf( @@ -159,7 +159,17 @@ func (d *Decoder) decodeTypeDefAndValue() (cadence.Value, error) { } // element 1: type and value - return d.decodeTypeAndValue(types) + val, err := d.decodeTypeAndValue(types) + if err != nil { + return nil, err + } + + // Check if there is any unreferenced type definition. + if types.hasUnreferenced() { + return nil, errors.New("found unreferenced type definition") + } + + return val, nil } // decodeTypeAndValue decodes encoded ccf-type-and-value-message @@ -176,7 +186,7 @@ func (d *Decoder) decodeTypeDefAndValue() (cadence.Value, error) { // value: value, // // ] -func (d *Decoder) decodeTypeAndValue(types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeTypeAndValue(types *cadenceTypeByCCFTypeID) (cadence.Value, error) { // Decode array head of length 2. err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { @@ -243,7 +253,7 @@ func (d *Decoder) decodeTypeAndValue(types cadenceTypeByCCFTypeID) (cadence.Valu // / word64-value // / fix64-value // / ufix64-value -func (d *Decoder) decodeValue(t cadence.Type, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { if t == nil { return nil, fmt.Errorf("unexpected nil type") } @@ -784,7 +794,7 @@ func (d *Decoder) decodeUFix64() (cadence.Value, error) { // decodeOptional decodes encoded optional-value as // language=CDDL // optional-value = nil / value -func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { // Peek ahead for next CBOR data item type nextType, err := d.dec.NextType() if err != nil { @@ -813,7 +823,7 @@ func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types cadenceTypeByC // decodeArray decodes encoded array-value as // language=CDDL // array-value = [* value] -func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint64, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint64, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { // Decode array length. n, err := d.dec.DecodeArrayHead() if err != nil { @@ -857,7 +867,7 @@ func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSiz // decodeDictionary decodes encoded dict-value as // language=CDDL // dict-value = [* (key: value, value: value)] -func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { // Decode array length. n, err := d.dec.DecodeArrayHead() if err != nil { @@ -938,7 +948,7 @@ func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types cadenceTyp // decodeComposite decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types cadenceTypeByCCFTypeID) ([]cadence.Value, error) { +func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types *cadenceTypeByCCFTypeID) ([]cadence.Value, error) { fieldCount := len(fieldTypes) // Decode number of fields. @@ -969,7 +979,7 @@ func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types cadenceTypeB // decodeStruct decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeStruct(typ *cadence.StructType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeStruct(typ *cadence.StructType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { return nil, err @@ -993,7 +1003,7 @@ func (d *Decoder) decodeStruct(typ *cadence.StructType, types cadenceTypeByCCFTy // decodeResource decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeResource(typ *cadence.ResourceType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeResource(typ *cadence.ResourceType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { return nil, err @@ -1017,7 +1027,7 @@ func (d *Decoder) decodeResource(typ *cadence.ResourceType, types cadenceTypeByC // decodeEvent decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeEvent(typ *cadence.EventType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeEvent(typ *cadence.EventType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { return nil, err @@ -1041,7 +1051,7 @@ func (d *Decoder) decodeEvent(typ *cadence.EventType, types cadenceTypeByCCFType // decodeContract decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeContract(typ *cadence.ContractType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeContract(typ *cadence.ContractType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { return nil, err @@ -1065,7 +1075,7 @@ func (d *Decoder) decodeContract(typ *cadence.ContractType, types cadenceTypeByC // decodeEnum decodes encoded composite-value as // language=CDDL // composite-value = [* (field: value)] -func (d *Decoder) decodeEnum(typ *cadence.EnumType, types cadenceTypeByCCFTypeID) (cadence.Value, error) { +func (d *Decoder) decodeEnum(typ *cadence.EnumType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { fieldValues, err := d.decodeComposite(typ.Fields, types) if err != nil { return nil, err @@ -1169,7 +1179,7 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType) (cadence.Value, // decodeTypeValue decodes encoded type-value. // See _decodeTypeValue() for details. func (d *Decoder) decodeTypeValue() (cadence.Value, error) { - t, err := d._decodeTypeValue(cadenceTypeByCCFTypeID{}) + t, err := d._decodeTypeValue(newCadenceTypeByCCFTypeID()) if err != nil { return nil, err } @@ -1199,7 +1209,7 @@ func (d *Decoder) decodeTypeValue() (cadence.Value, error) { // / restricted-type-value // / capability-type-value // / type-value-ref -func (d *Decoder) _decodeTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { // Decode tag number. tagNum, err := d.dec.DecodeTagNumber() if err != nil { @@ -1278,7 +1288,7 @@ func (d *Decoder) _decodeTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type // // ; cbor-tag-struct-type-value // #6.208(composite-type-value) -func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeStructTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1308,7 +1318,7 @@ func (d *Decoder) decodeStructTypeValue(visited cadenceTypeByCCFTypeID) (cadence // // ; cbor-tag-resource-type-value // #6.209(composite-type-value) -func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeResourceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1338,7 +1348,7 @@ func (d *Decoder) decodeResourceTypeValue(visited cadenceTypeByCCFTypeID) (caden // // ; cbor-tag-event-type-value // #6.210(composite-type-value) -func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeEventTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1368,7 +1378,7 @@ func (d *Decoder) decodeEventTypeValue(visited cadenceTypeByCCFTypeID) (cadence. // // ; cbor-tag-contract-type-value // #6.211(composite-type-value) -func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeContractTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1398,7 +1408,7 @@ func (d *Decoder) decodeContractTypeValue(visited cadenceTypeByCCFTypeID) (caden // // ; cbor-tag-enum-type-value // #6.212(composite-type-value) -func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeEnumTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1426,7 +1436,7 @@ func (d *Decoder) decodeEnumTypeValue(visited cadenceTypeByCCFTypeID) (cadence.T // // ; cbor-tag-struct-interface-type-value // #6.224(composite-type-value) -func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeStructInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1456,7 +1466,7 @@ func (d *Decoder) decodeStructInterfaceTypeValue(visited cadenceTypeByCCFTypeID) // // ; cbor-tag-resource-interface-type-value // #6.225(composite-type-value) -func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeResourceInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1486,7 +1496,7 @@ func (d *Decoder) decodeResourceInterfaceTypeValue(visited cadenceTypeByCCFTypeI // // ; cbor-tag-contract-interface-type-value // #6.226(composite-type-value) -func (d *Decoder) decodeContractInterfaceTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeContractInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { ctr := func( location common.Location, qualifiedIdentifier string, @@ -1527,7 +1537,7 @@ type compositeTypeValue struct { // decodeCompositeTypeValue decodes composite-type-value. // See _decodeCompositeTypeValue for details. func (d *Decoder) decodeCompositeTypeValue( - visited cadenceTypeByCCFTypeID, + visited *cadenceTypeByCCFTypeID, constructor compositeTypeConstructor, ) (cadence.Type, error) { compTypeValue, err := d._decodeCompositeTypeValue(visited) @@ -1552,7 +1562,7 @@ func (d *Decoder) decodeCompositeTypeValue( // "Deterministic CCF Encoding Requirements" in CCF specs: // // "composite-type-value.id MUST be identical to the zero-based encoding order type-value." - if compTypeValue.ccfID != newCCFTypeIDFromUint64(uint64(len(visited))) { + if compTypeValue.ccfID != newCCFTypeIDFromUint64(uint64(visited.count())) { return nil, fmt.Errorf( "encoded composite-type-value's CCF type ID %d doesn't match zero-based encoding order composite-type-value", compTypeValue.ccfID, @@ -1649,7 +1659,7 @@ func (d *Decoder) decodeCompositeTypeValue( // ] // // ] -func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (*compositeTypeValue, error) { +func (d *Decoder) _decodeCompositeTypeValue(visited *cadenceTypeByCCFTypeID) (*compositeTypeValue, error) { // Decode array of length 5 err := decodeCBORArrayWithKnownSize(d.dec, 5) if err != nil { @@ -1708,7 +1718,7 @@ func (d *Decoder) _decodeCompositeTypeValue(visited cadenceTypeByCCFTypeID) (*co // ] // ] // ] -func (d *Decoder) decodeInitializerTypeValues(visited cadenceTypeByCCFTypeID) ([][]cadence.Parameter, error) { +func (d *Decoder) decodeInitializerTypeValues(visited *cadenceTypeByCCFTypeID) ([][]cadence.Parameter, error) { // Decode number of initializers. count, err := d.dec.DecodeArrayHead() if err != nil { @@ -1737,7 +1747,7 @@ func (d *Decoder) decodeInitializerTypeValues(visited cadenceTypeByCCFTypeID) ([ // type: type-value // ] // ] -func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]cadence.Parameter, error) { +func (d *Decoder) decodeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([]cadence.Parameter, error) { // Decode number of parameters. count, err := d.dec.DecodeArrayHead() if err != nil { @@ -1805,7 +1815,7 @@ func (d *Decoder) decodeParameterTypeValues(visited cadenceTypeByCCFTypeID) ([]c // identifier: tstr, // type: type-value // ] -func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Parameter, error) { +func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Parameter, error) { // Decode array head of length 3 err := decodeCBORArrayWithKnownSize(d.dec, 3) if err != nil { @@ -1853,7 +1863,7 @@ func (d *Decoder) decodeParameterTypeValue(visited cadenceTypeByCCFTypeID) (cade // return-type: type-value // // ] -func (d *Decoder) decodeFunctionTypeValue(visited cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { // Decode array head of length 2 err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 6e53c93d3d..284afd0745 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -29,7 +29,7 @@ import ( type cadenceTypeID string -type decodeTypeFn func(types cadenceTypeByCCFTypeID) (cadence.Type, error) +type decodeTypeFn func(types *cadenceTypeByCCFTypeID) (cadence.Type, error) // decodeInlineType decodes inline-type as // language=CDDL @@ -47,7 +47,7 @@ type decodeTypeFn func(types cadenceTypeByCCFTypeID) (cadence.Type, error) // // All exported Cadence types needs to be handled in this function, // including abstract and interface types. -func (d *Decoder) decodeInlineType(types cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { tagNum, err := d.dec.DecodeTagNumber() if err != nil { return nil, err @@ -267,7 +267,7 @@ func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeOptionalType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode inline-type or type-value. @@ -295,7 +295,7 @@ func (d *Decoder) decodeOptionalType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeVarSizedArrayType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode inline-type or type-value. @@ -329,7 +329,7 @@ func (d *Decoder) decodeVarSizedArrayType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeConstantSizedArrayType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array head of length 2. @@ -377,7 +377,7 @@ func (d *Decoder) decodeConstantSizedArrayType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeDictType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array head of length 2. @@ -431,7 +431,7 @@ func (d *Decoder) decodeDictType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeCapabilityType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array head of length 1 @@ -473,7 +473,7 @@ func (d *Decoder) decodeCapabilityType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeReferenceType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array head of length 2 @@ -521,7 +521,7 @@ func (d *Decoder) decodeReferenceType( // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeRestrictedType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array of length 2. @@ -630,7 +630,7 @@ func (d *Decoder) decodeCadenceTypeID() (cadenceTypeID, common.Location, string, // // ; cbor-tag-type-ref // #6.136(id) -func (d *Decoder) decodeTypeRef(types cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeTypeRef(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { id, err := d.decodeCCFTypeID() if err != nil { return nil, err @@ -640,5 +640,14 @@ func (d *Decoder) decodeTypeRef(types cadenceTypeByCCFTypeID) (cadence.Type, err // // "type-ref.id MUST refer to composite-type.id." // "type-value-ref.id MUST refer to composite-type-value.id in the same composite-type-value data item." - return types.typ(id) + t, err := types.typ(id) + if err != nil { + return nil, err + } + + // Track referenced type definition so the decoder can detect + // encoded but not referenced type definition (extraneous data). + types.reference(id) + + return t, nil } diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 01ca6e9032..0b25153217 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -40,7 +40,7 @@ import ( // / resource-interface-type // / contract-interface-type // )] -func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { +func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { // Decode number of type definitions. count, err := d.dec.DecodeArrayHead() if err != nil { @@ -51,7 +51,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { return nil, errors.New("found 0 type definition in composite-typedef (expected at least 1 type definition)") } - types := make(map[ccfTypeID]cadence.Type, count) + types := newCadenceTypeByCCFTypeID() // NOTE: composite fields are not decoded while composite types are decoded // because field type might reference composite type that hasn't decoded yet. @@ -106,9 +106,9 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { // Decode fields after all high-level type definitions are resolved. for id, raw := range rawFields { //nolint:maprange - typ, ok := types[id] - if !ok { - return nil, fmt.Errorf("composite fields' CCF type ID %d not found in composite-typedef", id) + typ, err := types.typ(id) + if err != nil { + return nil, err } dec := NewDecoder(d.gauge, raw) @@ -171,7 +171,7 @@ func (d *Decoder) decodeTypeDefs() (cadenceTypeByCCFTypeID, error) { // ; cbor-tag-contract-interface-type // #6.178(interface-type) func (d *Decoder) decodeTypeDef( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, rawFields map[ccfTypeID][]byte, ) ( ccfTypeID, @@ -303,7 +303,7 @@ func (d *Decoder) decodeTypeDef( // // ] func (d *Decoder) decodeCompositeType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, rawFields map[ccfTypeID][]byte, constructor func(common.Location, string) cadence.Type, ) (ccfTypeID, cadenceTypeID, error) { @@ -323,7 +323,7 @@ func (d *Decoder) decodeCompositeType( // "Valid CCF Encoding Requirements" in CCF specs: // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." - if _, ok := types[ccfID]; ok { + if types.has(ccfID) { return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in composite-type", ccfID) } @@ -339,7 +339,7 @@ func (d *Decoder) decodeCompositeType( return ccfTypeID(0), cadenceTypeID(""), err } - types[ccfID] = constructor(location, identifier) + _ = types.add(ccfID, constructor(location, identifier)) rawFields[ccfID] = rawField return ccfID, cadenceID, nil } @@ -353,7 +353,7 @@ func (d *Decoder) decodeCompositeType( // field-type: inline-type // ] // ] -func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) ([]cadence.Field, error) { +func (d *Decoder) decodeCompositeFields(types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) ([]cadence.Field, error) { // Decode number of fields. fieldCount, err := d.dec.DecodeArrayHead() if err != nil { @@ -406,7 +406,7 @@ func (d *Decoder) decodeCompositeFields(types cadenceTypeByCCFTypeID, decodeType // field-name: tstr, // field-type: inline-type // ] -func (d *Decoder) decodeCompositeField(types cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Field, error) { +func (d *Decoder) decodeCompositeField(types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Field, error) { // Decode array head of length 2 err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { @@ -443,7 +443,7 @@ func (d *Decoder) decodeCompositeField(types cadenceTypeByCCFTypeID, decodeTypeF // // ] func (d *Decoder) decodeInterfaceType( - types cadenceTypeByCCFTypeID, + types *cadenceTypeByCCFTypeID, constructor func(common.Location, string) cadence.Type, ) (ccfTypeID, cadenceTypeID, error) { @@ -462,7 +462,7 @@ func (d *Decoder) decodeInterfaceType( // "Valid CCF Encoding Requirements" in CCF specs: // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." - if _, ok := types[ccfID]; ok { + if types.has(ccfID) { return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in interface-type", ccfID) } @@ -472,6 +472,6 @@ func (d *Decoder) decodeInterfaceType( return ccfTypeID(0), cadenceTypeID(""), err } - types[ccfID] = constructor(location, identifier) + _ = types.add(ccfID, constructor(location, identifier)) return ccfID, cadenceID, nil } From e053a2357d8cc67378e4126c3f05b5f6703775be Mon Sep 17 00:00:00 2001 From: Maksim Daunarovich Date: Thu, 30 Mar 2023 10:17:54 +0300 Subject: [PATCH 072/173] Update accounts.mdx Fix name of the argument --- docs/language/accounts.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index 5e5ad01a98..e0b994390c 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -130,7 +130,7 @@ to the `prepare` phase of the transaction. // Account storage API (see the section below for documentation) fun save(_ value: T, to: StoragePath) - fun type(at path: StoragePath): Type? + fun type(at: StoragePath): Type? fun load(from: StoragePath): T? fun copy(from: StoragePath): T? @@ -529,7 +529,7 @@ The path must be a storage path, i.e., only the domain `storage` is allowed. ```cadence -fun type(at path: StoragePath): Type? +fun type(at: StoragePath): Type? ``` Reads the type of an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. From 0758547aab2a3f02e353d3eafaef8257aa5a3837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 11:45:45 -0700 Subject: [PATCH 073/173] update dependencies --- go.mod | 46 ++++++++++---------- go.sum | 134 +++++++++++++++++++++++---------------------------------- 2 files changed, 77 insertions(+), 103 deletions(-) diff --git a/go.mod b/go.mod index 3096d2d69f..ecb6ef73ad 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,27 @@ module github.com/onflow/cadence go 1.18 require ( - github.com/bits-and-blooms/bitset v1.2.2 + github.com/bits-and-blooms/bitset v1.5.0 github.com/bytecodealliance/wasmtime-go v0.40.0 - github.com/c-bata/go-prompt v0.2.5 + github.com/c-bata/go-prompt v0.2.6 github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f - github.com/go-test/deep v1.0.5 + github.com/go-test/deep v1.1.0 github.com/k0kubun/pp v3.0.1+incompatible github.com/leanovate/gopter v0.2.9 - github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 + github.com/logrusorgru/aurora v2.0.3+incompatible github.com/onflow/atree v0.5.0 - github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a - github.com/schollz/progressbar/v3 v3.8.3 - github.com/stretchr/testify v1.8.1 + github.com/rivo/uniseg v0.4.4 + github.com/schollz/progressbar/v3 v3.13.1 + github.com/stretchr/testify v1.8.2 github.com/tidwall/pretty v1.2.1 github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d - go.opentelemetry.io/otel v1.8.0 - go.uber.org/goleak v1.1.10 - golang.org/x/crypto v0.1.0 - golang.org/x/mod v0.6.0 - golang.org/x/text v0.4.0 - golang.org/x/tools v0.2.0 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 + go.opentelemetry.io/otel v1.14.0 + go.uber.org/goleak v1.2.1 + golang.org/x/crypto v0.7.0 + golang.org/x/mod v0.9.0 + golang.org/x/text v0.8.0 + golang.org/x/tools v0.7.0 + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) require ( @@ -35,19 +35,19 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/circlehash v0.3.0 // indirect github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect - github.com/klauspost/cpuid/v2 v2.0.14 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mattn/go-tty v0.0.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-tty v0.0.4 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect - github.com/pkg/term v1.1.0 // indirect + github.com/pkg/term v1.2.0-beta.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.3 // indirect - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e91384bda0..aa2436c647 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,10 @@ -github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk= -github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= +github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bytecodealliance/wasmtime-go v0.40.0 h1:7cGLQEctJf09JWBl3Ai0eMl1PTrXVAjkAb27+KHfIq0= github.com/bytecodealliance/wasmtime-go v0.40.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= -github.com/c-bata/go-prompt v0.2.5 h1:3zg6PecEywxNn0xiqcXHD96fkbxghD+gdB2tbsYfl+Y= -github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= +github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= +github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc= github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= @@ -14,65 +15,66 @@ github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f h1:dxTR4AaxCwu github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= -github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= -github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8= -github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= +github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/onflow/atree v0.5.0 h1:y3lh8hY2fUo8KVE2ALVcz0EiNTq0tXJ6YTXKYVDA+3E= github.com/onflow/atree v0.5.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= -github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= -github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= +github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a h1:s7GrsqeorVkFR1vGmQ6WVL9nup0eyQCC+YVUeSQLH/Q= -github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8= -github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= +github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= @@ -87,66 +89,38 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg= -go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From b4e149429b7d38625b23cfa8c5a945125d443858 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 29 Mar 2023 13:59:35 -0700 Subject: [PATCH 074/173] Check cyclic storage references during export --- runtime/convertValues.go | 11 +++++- runtime/convertValues_test.go | 72 +++++++++++++++++++++++++++++++++++ runtime/interpreter/value.go | 10 +++++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index e5393c31d1..f3d41ccc4b 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -61,7 +61,7 @@ func ExportValue( // NOTE: Do not generalize to map[interpreter.Value], // as not all values are Go hashable, i.e. this might lead to run-time panics -type seenReferences map[*interpreter.EphemeralReferenceValue]struct{} +type seenReferences map[interpreter.ReferenceValue]struct{} // exportValueWithInterpreter exports the given internal (interpreter) value to an external value. // @@ -220,7 +220,7 @@ func exportValueWithInterpreter( case *interpreter.StorageCapabilityValue: return exportStorageCapabilityValue(v, inter), nil case *interpreter.EphemeralReferenceValue: - // Break recursion through ephemeral references + // Break recursion through references if _, ok := seenReferences[v]; ok { return nil, nil } @@ -233,6 +233,13 @@ func exportValueWithInterpreter( seenReferences, ) case *interpreter.StorageReferenceValue: + // Break recursion through references + if _, ok := seenReferences[v]; ok { + return nil, nil + } + defer delete(seenReferences, v) + seenReferences[v] = struct{}{} + referencedValue := v.ReferencedValue(inter, interpreter.EmptyLocationRange, true) if referencedValue == nil { return nil, nil diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 86d7f18978..ac824250c4 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1822,6 +1822,78 @@ func TestExportReferenceValue(t *testing.T) { assert.Equal(t, expected, actual) }) + + t.Run("storage, recursive, same reference", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): &AnyStruct { + var acct = getAuthAccount(0x01) + var v:[AnyStruct] = [] + acct.save(v, to: /storage/x) + + var ref = acct.borrow<&[AnyStruct]>(from: /storage/x)! + ref.append(ref) + return ref + } + ` + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + } + + _, err := rt.ExecuteScript( + Script{ + Source: []byte(script), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot store non-storable value") + }) + + t.Run("storage, recursive, two references", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): &AnyStruct { + var acct = getAuthAccount(0x01) + var v:[AnyStruct] = [] + acct.save(v, to: /storage/x) + + var ref1 = acct.borrow<&[AnyStruct]>(from: /storage/x)! + var ref2 = acct.borrow<&[AnyStruct]>(from: /storage/x)! + + ref1.append(ref2) + return ref1 + } + ` + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + } + + _, err := rt.ExecuteScript( + Script{ + Source: []byte(script), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot store non-storable value") + }) } func TestExportTypeValue(t *testing.T) { diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 56892f71be..b092e6bf05 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16963,6 +16963,10 @@ func (s SomeStorable) ChildStorables() []atree.Storable { } } +type ReferenceValue interface { + isReference() +} + // StorageReferenceValue type StorageReferenceValue struct { @@ -16977,6 +16981,7 @@ var _ EquatableValue = &StorageReferenceValue{} var _ ValueIndexableValue = &StorageReferenceValue{} var _ TypeIndexableValue = &StorageReferenceValue{} var _ MemberAccessibleValue = &StorageReferenceValue{} +var _ ReferenceValue = &StorageReferenceValue{} func NewUnmeteredStorageReferenceValue( authorized bool, @@ -17312,6 +17317,8 @@ func (*StorageReferenceValue) DeepRemove(_ *Interpreter) { // NO-OP } +func (*StorageReferenceValue) isReference() {} + // EphemeralReferenceValue type EphemeralReferenceValue struct { @@ -17325,6 +17332,7 @@ var _ EquatableValue = &EphemeralReferenceValue{} var _ ValueIndexableValue = &EphemeralReferenceValue{} var _ TypeIndexableValue = &EphemeralReferenceValue{} var _ MemberAccessibleValue = &EphemeralReferenceValue{} +var _ ReferenceValue = &EphemeralReferenceValue{} func NewUnmeteredEphemeralReferenceValue( authorized bool, @@ -17647,6 +17655,8 @@ func (*EphemeralReferenceValue) DeepRemove(_ *Interpreter) { // NO-OP } +func (*EphemeralReferenceValue) isReference() {} + // AddressValue type AddressValue common.Address From dd45746310d6da62212201089c22e7f0d17191c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 11:49:24 -0700 Subject: [PATCH 075/173] update aurora --- go.mod | 2 +- go.sum | 4 ++-- runtime/cmd/execute/colors.go | 2 +- runtime/pretty/print.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index ecb6ef73ad..35182c6a73 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/go-test/deep v1.1.0 github.com/k0kubun/pp v3.0.1+incompatible github.com/leanovate/gopter v0.2.9 - github.com/logrusorgru/aurora v2.0.3+incompatible github.com/onflow/atree v0.5.0 github.com/rivo/uniseg v0.4.4 github.com/schollz/progressbar/v3 v3.13.1 @@ -28,6 +27,7 @@ require ( require ( github.com/dave/dst v0.27.2 + github.com/logrusorgru/aurora/v4 v4.0.0 github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c ) diff --git a/go.sum b/go.sum index aa2436c647..5281f3cdc2 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= -github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= +github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= diff --git a/runtime/cmd/execute/colors.go b/runtime/cmd/execute/colors.go index 8d6b2bbc9c..ed4e6988ef 100644 --- a/runtime/cmd/execute/colors.go +++ b/runtime/cmd/execute/colors.go @@ -19,7 +19,7 @@ package execute import ( - "github.com/logrusorgru/aurora" + "github.com/logrusorgru/aurora/v4" "github.com/onflow/cadence/runtime/interpreter" ) diff --git a/runtime/pretty/print.go b/runtime/pretty/print.go index 33fc731ee5..077030537c 100644 --- a/runtime/pretty/print.go +++ b/runtime/pretty/print.go @@ -26,7 +26,7 @@ import ( "strconv" "strings" - "github.com/logrusorgru/aurora" + "github.com/logrusorgru/aurora/v4" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" From 3cc78b85bd2736ba84c3df6c3131be0907f672d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 11:55:18 -0700 Subject: [PATCH 076/173] update wasmtime-go and pp --- go.mod | 6 ++---- go.sum | 15 ++++++--------- runtime/cmd/json-cdc/main.go | 2 +- vm/vm.go | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 35182c6a73..4c9c09b263 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,9 @@ go 1.18 require ( github.com/bits-and-blooms/bitset v1.5.0 - github.com/bytecodealliance/wasmtime-go v0.40.0 github.com/c-bata/go-prompt v0.2.6 github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f github.com/go-test/deep v1.1.0 - github.com/k0kubun/pp v3.0.1+incompatible github.com/leanovate/gopter v0.2.9 github.com/onflow/atree v0.5.0 github.com/rivo/uniseg v0.4.4 @@ -26,7 +24,9 @@ require ( ) require ( + github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 github.com/dave/dst v0.27.2 + github.com/k0kubun/pp/v3 v3.2.0 github.com/logrusorgru/aurora/v4 v4.0.0 github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c ) @@ -34,7 +34,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/circlehash v0.3.0 // indirect - github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -48,6 +47,5 @@ require ( github.com/zeebo/blake3 v0.2.3 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 5281f3cdc2..54b15f2013 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bytecodealliance/wasmtime-go v0.40.0 h1:7cGLQEctJf09JWBl3Ai0eMl1PTrXVAjkAb27+KHfIq0= -github.com/bytecodealliance/wasmtime-go v0.40.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= +github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 h1:/rBNjgFju2HCZnkPb1eL+W4GBwP8DMbaQu7i+GR9DH4= +github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -18,15 +18,12 @@ github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5 github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= @@ -54,6 +51,7 @@ github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/onflow/atree v0.5.0 h1:y3lh8hY2fUo8KVE2ALVcz0EiNTq0tXJ6YTXKYVDA+3E= github.com/onflow/atree v0.5.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= @@ -119,8 +117,7 @@ golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/runtime/cmd/json-cdc/main.go b/runtime/cmd/json-cdc/main.go index 330527ef17..784909165c 100644 --- a/runtime/cmd/json-cdc/main.go +++ b/runtime/cmd/json-cdc/main.go @@ -25,7 +25,7 @@ import ( "io" "os" - "github.com/k0kubun/pp" + "github.com/k0kubun/pp/v3" jsoncdc "github.com/onflow/cadence/encoding/json" ) diff --git a/vm/vm.go b/vm/vm.go index 1144056b59..f96cadc8d0 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -27,7 +27,7 @@ import ( "C" - "github.com/bytecodealliance/wasmtime-go" + "github.com/bytecodealliance/wasmtime-go/v7" "github.com/onflow/cadence/runtime/interpreter" ) From 37f22841de92a056a86ba86c514acc5156b7b50c Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:05:25 -0500 Subject: [PATCH 077/173] Fix abstract type caching during CCF type traversal Supun found this by fuzzing. Thanks Supun! --- encoding/ccf/ccf_test.go | 210 +++++++++++++++++++++++++++++++++ encoding/ccf/traverse_value.go | 35 +++--- 2 files changed, 230 insertions(+), 15 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 920e21da8d..413a843e1d 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -12330,3 +12330,213 @@ func TestDecodeInvalidData(t *testing.T) { }) } } + +func TestEncodeValueOfRestrictedInterface(t *testing.T) { + + // Values and types are generated by fuzzer. + /* + // Type def + + struct OuterStruct { + var field: MiddleStruct + } + + struct MiddleStruct { + var field: AnyStruct{Interface} + } + + struct interface Interface {} + + struct InnerStruct: Interface {} // 'InnerStruct' conforms to 'Interface' + + // Value + + OuterStruct { + field: MiddleStruct { + field: InnerStruct{} // <-- here the value is the implementation, for the restricted type. + } + } + */ + + interfaceType := cadence.NewStructInterfaceType( + common.StringLocation("LocationA"), + "Interface", + nil, + nil, + ) + + middleStruct := cadence.NewStructType( + common.StringLocation("LocationB"), + "MiddleStruct", + []cadence.Field{ + { + Type: cadence.NewRestrictedType( + "", cadence.TheAnyStructType, []cadence.Type{interfaceType}), + Identifier: "field", + }, + }, + nil, + ) + + outerStructType := cadence.NewStructType( + common.StringLocation("LocationC"), + "OuterStruct", + []cadence.Field{ + { + Type: middleStruct, + Identifier: "field", + }, + }, + nil, + ) + + innerStructType := cadence.NewStructType( + common.StringLocation("LocationD"), + "InnerStruct", + []cadence.Field{}, + nil, + ) + + value := cadence.NewStruct([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.NewStruct([]cadence.Value{}).WithType(innerStructType), + }).WithType(middleStruct), + }).WithType(outerStructType) + + testEncodeAndDecode( + t, + value, + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.LocationC.OuterStruct","fields":[{"value":{"value":{"id":"S.LocationB.MiddleStruct","fields":[{"value":{"value":{"id":"S.LocationD.InnerStruct","fields":[]},"type":"Struct"},"name":"field"}]},"type":"Struct"},"name":"field"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[176([h'', "S.LocationA.Interface"]), 160([h'01', "S.LocationC.OuterStruct", [["field", 136(h'03')]]]), 160([h'02', "S.LocationD.InnerStruct", []]), 160([h'03', "S.LocationB.MiddleStruct", [["field", 143([137(39), [136(h'')]])]]])], [136(h'01'), [[130([136(h'02'), []])]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // array, 4 items follow + 0x84, + // tag + 0xd8, ccf.CBORTagStructInterfaceType, + // array, 2 items follow + 0x82, + // CCF type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 21 bytes follow + 0x75, + // "S.LocationA.Interface" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // text, 23 bytes follow + 0x77, + // "S.LocationC.OuterStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x4f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 item follows + 0x81, + // array, 2 item follows + 0x82, + // text, 5 bytes follow + 0x65, + // "field" + 0x66, 0x69, 0x65, 0x6c, 0x64, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 item follows + 0x83, + // CCF type ID + // bytes, 1 byte follow + 0x41, + // 2 + 0x02, + // Cadence type ID + // text, 23 bytes follow + 0x77, + // "S.LocationD.InnerStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x2e, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 0 item follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 item follows + 0x83, + // CCF type ID + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + // Cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "S.LocationB.MiddleStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 item follows + 0x81, + // array, 2 item follows + 0x82, + // text, 5 bytes follow + 0x65, + // "field" + 0x66, 0x69, 0x65, 0x6c, 0x64, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // array, 1 item follows + 0x81, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // array, 0 item follows + 0x80, + }, + ) +} diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index f971934e10..5f4a012e82 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -25,8 +25,9 @@ import ( ) type compositeTypes struct { - ids ccfTypeIDByCadenceType - types []cadence.Type + ids ccfTypeIDByCadenceType + abstractTypes map[string]bool + types []cadence.Type } // compositeTypesFromValue returns all composite/interface types for value v. @@ -34,8 +35,9 @@ type compositeTypes struct { // NOTE: nested composite/interface types are included in the returned types. func compositeTypesFromValue(v cadence.Value) ([]cadence.Type, ccfTypeIDByCadenceType) { ct := &compositeTypes{ - ids: make(ccfTypeIDByCadenceType), - types: make([]cadence.Type, 0, 1), + ids: make(ccfTypeIDByCadenceType), + abstractTypes: make(map[string]bool), + types: make([]cadence.Type, 0, 1), } // Traverse v to get all unique: @@ -147,20 +149,23 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) return check case cadence.CompositeType: // struct, resource, event, contract, enum - check := false - newType := ct.add(t) - if newType { - fields := t.CompositeFields() - for _, field := range fields { - checkField := ct.traverseType(field.Type) - check = check || checkField - } - - // Don't need to traverse initializers because - // they are not encoded and their types aren't needed. + if !newType { + return ct.abstractTypes[t.ID()] + } + + check := false + fields := t.CompositeFields() + for _, field := range fields { + checkField := ct.traverseType(field.Type) + check = check || checkField } + // Don't need to traverse initializers because + // they are not encoded and their types aren't needed. + + ct.abstractTypes[t.ID()] = check + return check case cadence.InterfaceType: // struct interface, resource interface, contract interface From 1802700000cf20723cfe0465939716937f730cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 15:06:09 -0700 Subject: [PATCH 078/173] remove outdated TODO Co-authored-by: Faye Amacker <33205765+fxamacker@users.noreply.github.com> --- types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/types.go b/types.go index 7fa063889e..515cd9dce2 100644 --- a/types.go +++ b/types.go @@ -1712,7 +1712,6 @@ func (t *ContractInterfaceType) Equal(other Type) bool { // Function -// TODO: type parameters type FunctionType struct { TypeParameters []TypeParameter Parameters []Parameter From f20f3ae5a3662d1ecad2d0fb7e84f3ee0684120f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 15:17:24 -0700 Subject: [PATCH 079/173] also compare type parameters --- types.go | 22 ++++++ types_test.go | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/types.go b/types.go index 515cd9dce2..a2598304c6 100644 --- a/types.go +++ b/types.go @@ -1781,6 +1781,28 @@ func (t *FunctionType) Equal(other Type) bool { return false } + // Type parameters + + if len(t.TypeParameters) != len(otherType.TypeParameters) { + return false + } + + for i, typeParameter := range t.TypeParameters { + otherTypeParameter := otherType.TypeParameters[i] + + if typeParameter.TypeBound == nil { + if otherTypeParameter.TypeBound != nil { + return false + } + } else if otherTypeParameter.TypeBound == nil || + !typeParameter.TypeBound.Equal(otherTypeParameter.TypeBound) { + + return false + } + } + + // Parameters + if len(t.Parameters) != len(otherType.Parameters) { return false } diff --git a/types_test.go b/types_test.go index a361ffaf0a..2543cce55d 100644 --- a/types_test.go +++ b/types_test.go @@ -1410,6 +1410,200 @@ func TestTypeEquality(t *testing.T) { assert.False(t, source.Equal(target)) }) + t.Run("different type param count", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{}, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param name", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "U", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.True(t, source.Equal(target)) + }) + + t.Run("different type param bound: nil, some", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param bound: some, nil", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param bounds", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("same type param bounds", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.True(t, source.Equal(target)) + }) + t.Run("different type", func(t *testing.T) { t.Parallel() From 1d226752b40a493d2bd5e3e2e2804140b81d09ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 15:33:41 -0700 Subject: [PATCH 080/173] pre-allocate --- encoding/json/encode.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 7a59ccab35..dc46bcf7fe 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -661,35 +661,35 @@ func prepareFieldType(fieldType cadence.Field, results typePreparationResults) j } func prepareFields(fieldTypes []cadence.Field, results typePreparationResults) []jsonFieldType { - fields := make([]jsonFieldType, 0) - for _, field := range fieldTypes { - fields = append(fields, prepareFieldType(field, results)) + fields := make([]jsonFieldType, len(fieldTypes)) + for i, fieldType := range fieldTypes { + fields[i] = prepareFieldType(fieldType, results) } return fields } func prepareTypeParameters(typeParameters []cadence.TypeParameter, results typePreparationResults) []jsonTypeParameter { - result := make([]jsonTypeParameter, 0) - for _, typeParameter := range typeParameters { - result = append(result, prepareTypeParameter(typeParameter, results)) + result := make([]jsonTypeParameter, len(typeParameters)) + for i, typeParameter := range typeParameters { + result[i] = prepareTypeParameter(typeParameter, results) } return result } -func prepareParameters(parameterTypes []cadence.Parameter, results typePreparationResults) []jsonParameterType { - result := make([]jsonParameterType, 0) - for _, param := range parameterTypes { - result = append(result, prepareParameter(param, results)) +func prepareParameters(parameters []cadence.Parameter, results typePreparationResults) []jsonParameterType { + result := make([]jsonParameterType, len(parameters)) + for i, param := range parameters { + result[i] = prepareParameter(param, results) } return result } -func prepareInitializers(initializerTypes [][]cadence.Parameter, results typePreparationResults) [][]jsonParameterType { - initializers := make([][]jsonParameterType, 0) - for _, params := range initializerTypes { - initializers = append(initializers, prepareParameters(params, results)) +func prepareInitializers(initializers [][]cadence.Parameter, results typePreparationResults) [][]jsonParameterType { + result := make([][]jsonParameterType, len(initializers)) + for i, params := range initializers { + result[i] = prepareParameters(params, results) } - return initializers + return result } func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { @@ -861,9 +861,9 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { Type: prepareType(typ.Type, results), } case *cadence.RestrictedType: - restrictions := make([]jsonValue, 0) - for _, restriction := range typ.Restrictions { - restrictions = append(restrictions, prepareType(restriction, results)) + restrictions := make([]jsonValue, len(typ.Restrictions)) + for i, restriction := range typ.Restrictions { + restrictions[i] = prepareType(restriction, results) } return jsonRestrictedType{ Kind: "Restriction", From 3f90c885e20d9b022764a930824c2511aa96e4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 15:42:17 -0700 Subject: [PATCH 081/173] optimize restriction set --- types.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/types.go b/types.go index a2598304c6..1b33b5d3f9 100644 --- a/types.go +++ b/types.go @@ -1867,13 +1867,13 @@ func (t *ReferenceType) Equal(other Type) bool { // RestrictedType -type restrictionSet = map[Type]struct{} +type RestrictionSet = map[Type]struct{} type RestrictedType struct { typeID string Type Type Restrictions []Type - restrictionSet restrictionSet + restrictionSet RestrictionSet restrictionSetOnce sync.Once } @@ -1923,15 +1923,15 @@ func (t *RestrictedType) Equal(other Type) bool { return false } - t.initializeRestrictionSet() - otherType.initializeRestrictionSet() + restrictionSet := t.RestrictionSet() + otherRestrictionSet := otherType.RestrictionSet() - if len(t.restrictionSet) != len(otherType.restrictionSet) { + if len(restrictionSet) != len(otherRestrictionSet) { return false } - for restriction := range t.restrictionSet { //nolint:maprange - _, ok := otherType.restrictionSet[restriction] + for restriction := range restrictionSet { //nolint:maprange + _, ok := otherRestrictionSet[restriction] if !ok { return false } @@ -1942,13 +1942,18 @@ func (t *RestrictedType) Equal(other Type) bool { func (t *RestrictedType) initializeRestrictionSet() { t.restrictionSetOnce.Do(func() { - t.restrictionSet = restrictionSet{} + t.restrictionSet = make(RestrictionSet, len(t.Restrictions)) for _, restriction := range t.Restrictions { t.restrictionSet[restriction] = struct{}{} } }) } +func (t *RestrictedType) RestrictionSet() RestrictionSet { + t.initializeRestrictionSet() + return t.restrictionSet +} + // BlockType type BlockType struct{} From 09eea5d0c58ee5a940fd01eedc47c63384b9b633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 30 Mar 2023 16:25:28 -0700 Subject: [PATCH 082/173] lint --- 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 8ff9aba66f..c36ff7ed9c 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -201,7 +201,7 @@ var ( CadenceFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionValue) CadenceKeyValuePairMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceKeyValuePair) CadencePathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) - CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) + CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) CadenceOptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceOptionalValue) CadencePathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathValue) CadenceVoidValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceVoidValue) From bf50554a86a3717626b6531ad75ea2202b8ff899 Mon Sep 17 00:00:00 2001 From: SupunS Date: Thu, 30 Mar 2023 23:37:49 +0000 Subject: [PATCH 083/173] v0.38.0 --- npm-packages/cadence-parser/package.json | 2 +- version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/npm-packages/cadence-parser/package.json b/npm-packages/cadence-parser/package.json index 970092b0f6..325ca99a4b 100644 --- a/npm-packages/cadence-parser/package.json +++ b/npm-packages/cadence-parser/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/cadence-parser", - "version": "0.37.0", + "version": "0.38.0", "description": "The Cadence parser", "homepage": "https://github.com/onflow/cadence", "repository": { diff --git a/version.go b/version.go index dfa632ff30..d1f72bb1fa 100644 --- a/version.go +++ b/version.go @@ -21,4 +21,4 @@ package cadence -const Version = "v0.37.0" +const Version = "v0.38.0" From 088de78a6768d6d8b6dfc9f8b550a7ef9121e5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 31 Mar 2023 09:15:39 -0700 Subject: [PATCH 084/173] remove parameter names when argument labels are given --- docs/language/environment-information.md | 2 +- docs/language/run-time-types.md | 2 +- docs/language/values-and-types.mdx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/language/environment-information.md b/docs/language/environment-information.md index 964f0ecdb9..61db4f71f5 100644 --- a/docs/language/environment-information.md +++ b/docs/language/environment-information.md @@ -24,7 +24,7 @@ To get information about a block, the functions `getCurrentBlock` and `getBlock` - ```cadence - fun getBlock(at height: UInt64): Block? + fun getBlock(at: UInt64): Block? ``` Returns the block at the given height. diff --git a/docs/language/run-time-types.md b/docs/language/run-time-types.md index 45fca64450..9476977b1c 100644 --- a/docs/language/run-time-types.md +++ b/docs/language/run-time-types.md @@ -32,7 +32,7 @@ Type() == Type() Type() != Type() ``` -The method `fun isSubtype(of otherType: Type): Bool` can be used to compare the run-time types of values. +The method `fun isSubtype(of: Type): Bool` can be used to compare the run-time types of values. ```cadence Type().isSubtype(of: Type()) // true diff --git a/docs/language/values-and-types.mdx b/docs/language/values-and-types.mdx index 3862b1d0d5..5a63bc3c85 100644 --- a/docs/language/values-and-types.mdx +++ b/docs/language/values-and-types.mdx @@ -1180,7 +1180,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun insert(at index: Int, _ element: T): Void + fun insert(at: Int, _ element: T): Void ``` Inserts the new element `element` of type `T` @@ -1211,7 +1211,7 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence - fun remove(at index: Int): T + fun remove(at: Int): T ``` Removes the element at the given `index` from the array and returns it. From 30ab566d51b4daa7604f14adbd26db3d586c8169 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 31 Mar 2023 11:32:40 -0500 Subject: [PATCH 085/173] Update to use latest Cadence APIs APIs for external values & types were improved, so update the CCF codec to use them. --- encoding/ccf/ccf_test.go | 14 ++++++-------- encoding/ccf/decode.go | 3 ++- encoding/ccf/decode_type.go | 1 - encoding/ccf/encode.go | 1 + 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 413a843e1d..2a15f5a775 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -6265,7 +6265,6 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { ) countSumRestrictedType := cadence.NewRestrictedType( - "", statsType, []cadence.Type{ hasCountInterfaceType, @@ -6293,7 +6292,6 @@ func TestEncodeValueOfRestrictedType(t *testing.T) { ) expectedCountSumRestrictedType := cadence.NewRestrictedType( - "", expectedStatsType, []cadence.Type{ hasSumInterfaceType, @@ -8204,7 +8202,7 @@ func TestEncodeType(t *testing.T) { {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, ReturnType: cadence.IntType{}, - }).WithID("((String):Int)"), + }), }, []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"((String):Int)", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } @@ -8303,7 +8301,7 @@ func TestEncodeType(t *testing.T) { StaticType: (&cadence.RestrictedType{ Restrictions: []cadence.Type{}, Type: cadence.IntType{}, - }).WithID("Int{String}"), + }), }, []byte{ // language=json, format=json-cdc // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{String}","type":{"kind":"Int"},"restrictions":[]}},"type":"Type"} @@ -8351,7 +8349,7 @@ func TestEncodeType(t *testing.T) { cadence.StringType{}, }, Type: cadence.IntType{}, - }).WithID("Int{String}"), + }), }, []byte{ // language=json, format=json-cdc // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } @@ -8407,7 +8405,7 @@ func TestEncodeType(t *testing.T) { cadence.StringType{}, }, Type: cadence.IntType{}, - }).WithID("Int{AnyStruct, String}"), + }), }, []byte{ // language=json, format=json-cdc // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} @@ -10271,7 +10269,7 @@ func TestExportFunctionValue(t *testing.T) { FunctionType: (&cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("(():Void)"), + }), }, []byte{ // language=json, format=json-cdc // { "type": "Function", "value": { "functionType": { "kind": "Function", "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" } } } } @@ -12371,7 +12369,7 @@ func TestEncodeValueOfRestrictedInterface(t *testing.T) { []cadence.Field{ { Type: cadence.NewRestrictedType( - "", cadence.TheAnyStructType, []cadence.Type{interfaceType}), + cadence.TheAnyStructType, []cadence.Type{interfaceType}), Identifier: "field", }, }, diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 5e60b7c8a0..026305fc61 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1863,6 +1863,7 @@ func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cad // return-type: type-value // // ] +// TODO: handle function type's type parameters func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { // Decode array head of length 2 err := decodeCBORArrayWithKnownSize(d.dec, 2) @@ -1888,7 +1889,7 @@ func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cade return cadence.NewMeteredFunctionType( d.gauge, - "", + nil, parameters, returnType, ), nil diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 284afd0745..29334c93f2 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -586,7 +586,6 @@ func (d *Decoder) decodeRestrictedType( return cadence.NewMeteredRestrictedType( d.gauge, - "", typ, restrictions, ), nil diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 5832ddad33..d2b39fd855 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1008,6 +1008,7 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { // return-type: type-value // // ] +// TODO: handle function type's type parameters func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { // Encode array head of length 2. err := e.enc.EncodeRawBytes([]byte{ From c885b1f727e20104dd4d283254d455e64ee22a2c Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Fri, 31 Mar 2023 14:31:02 -0500 Subject: [PATCH 086/173] Remove outdated test --- types_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/types_test.go b/types_test.go index 2609fc982a..d927e17230 100644 --- a/types_test.go +++ b/types_test.go @@ -244,18 +244,6 @@ func TestType_ID(t *testing.T) { }, "S.test.ContractI", }, - { - &FunctionType{ - UInt8Type{}, - "Foo", - []Parameter{ - { - Type: StringType{}, - }, - }, - }, - "Foo", - }, } test := func(ty Type, expected string) { From 6ddbd886b00a5ea1d11431ed4d083a378f36b55c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 2 Apr 2023 20:28:21 +0530 Subject: [PATCH 087/173] WIP: Allow equality comparisons for dictionaries --- docs/language/operators.md | 2 +- runtime/sema/type.go | 6 +- .../tests/checker/arrays_dictionaries_test.go | 85 +++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/docs/language/operators.md b/docs/language/operators.md index 081d4a0b80..758b680fb6 100644 --- a/docs/language/operators.md +++ b/docs/language/operators.md @@ -290,7 +290,7 @@ Logical operators work with the boolean values `true` and `false`. Comparison operators work with boolean and integer values. -- Equality: `==`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). Variable-sized arrays, fixed-size arrays, and optionals also support equality tests if their inner types do. +- Equality: `==`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). Variable-sized arrays, fixed-size arrays, dictionaries, and optionals also support equality tests if their inner types do. Both sides of the equality operator may be optional, even of different levels, so it is for example possible to compare a non-optional with a double-optional (`??`). diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 1fcab3cb48..d407b4f47e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4420,9 +4420,9 @@ func (t *DictionaryType) IsImportable(results map[*Member]bool) bool { t.ValueType.IsImportable(results) } -func (*DictionaryType) IsEquatable() bool { - // TODO: - return false +func (t *DictionaryType) IsEquatable() bool { + return t.KeyType.IsEquatable() && + t.ValueType.IsEquatable() } func (t *DictionaryType) TypeAnnotationState() TypeAnnotationState { diff --git a/runtime/tests/checker/arrays_dictionaries_test.go b/runtime/tests/checker/arrays_dictionaries_test.go index 357abe6014..c67f33af98 100644 --- a/runtime/tests/checker/arrays_dictionaries_test.go +++ b/runtime/tests/checker/arrays_dictionaries_test.go @@ -317,6 +317,91 @@ func TestCheckDictionaryValues(t *testing.T) { ) } +func TestCheckDictionaryEqual(t *testing.T) { + t.Parallel() + + testValid := func(name, code string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, code) + require.NoError(t, err) + }) + } + + assertInvalid := func(name, code string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, code) + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) + }) + } + + for _, opStr := range []string{"==", "!="} { + testValid("self_dict_equality", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + return d %s d + }`, opStr)) + + testValid("self_dict_equality_nested_1", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": {1: 100, 2: 200}, "def": {4: 400, 5: 500}} + return d %s d + }`, opStr)) + + testValid("self_dict_equality_nested_2", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + }`, opStr)) + + testValid("dict_equality_true", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + }`, opStr)) + + testValid("dict_equality_true_nested", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + }`, opStr)) + + testValid("dict_equality_false", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + }`, opStr)) + + testValid("dict_equality_false_nested", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + }`, opStr)) + + assertInvalid("dict_equality_invalid", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {1: "abc", 2: "def"} + return d %s d2 + }`, opStr)) + + assertInvalid("dict_equality_invalid_nested", fmt.Sprintf(` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {1000: "a"}, 2: {2000: "b"}}, "def": {4: {1000: "c"}, 5: {2000: "d"}}} + return d %s d2 + }`, opStr)) + } +} + func TestCheckLength(t *testing.T) { t.Parallel() From 66d1c81eb85fa5af3502f0d1905800ec60943f5e Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Mon, 3 Apr 2023 00:33:13 +0530 Subject: [PATCH 088/173] Add tests in interpreter_test and complete documentation --- docs/language/operators.md | 24 +++++++- runtime/tests/interpreter/interpreter_test.go | 59 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/language/operators.md b/docs/language/operators.md index 758b680fb6..2949ee2eed 100644 --- a/docs/language/operators.md +++ b/docs/language/operators.md @@ -349,8 +349,19 @@ Comparison operators work with boolean and integer values. xs == ys // is `true` ``` + ```cadence + // Equality tests of dictionaries are possible if the key and value types are equatable. + let d1 = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + d1 == d2 // is `true` + + let d3 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d4 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + d3 == d4 // is `true` + ``` + - Inequality: `!=`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). - Variable-sized arrays, fixed-size arrays, and optionals also support inequality tests if their inner types do. + Variable-sized arrays, fixed-size arrays, dictionaries, and optionals also support inequality tests if their inner types do. Both sides of the inequality operator may be optional, even of different levels, so it is for example possible to compare a non-optional with a double-optional (`??`). @@ -405,6 +416,17 @@ Comparison operators work with boolean and integer values. xs != ys // is `false` ``` + ```cadence + // Inequality tests of dictionaries are possible if the key and value types are equatable. + let d1 = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 500} + d1 != d2 // is `true` + + let d3 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d4 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + d3 != d4 // is `false` + ``` + - Less than: `<`, for integers ```cadence diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index ecf8fb6856..10aaa38111 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -4574,6 +4574,65 @@ func TestInterpretDictionaryIndexingAssignmentNil(t *testing.T) { ) } +func TestInterpretDictionaryEquality(t *testing.T) { + t.Parallel() + + testBooleanFunction := func(t *testing.T, name string, expected bool, innerCode string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + code := fmt.Sprintf("fun test(): Bool { \n %s \n }", innerCode) + + inter := parseCheckAndInterpret(t, code) + res, err := inter.Invoke("test") + + require.NoError(t, err) + + boolVal, ok := res.(interpreter.BoolValue) + require.True(t, ok) + + require.Equal(t, bool(boolVal), expected) + }) + + } + + for _, opStr := range []string{"==", "!="} { + testBooleanFunction(t, "dictionary should be equal to itself", opStr == "==", fmt.Sprintf(` + let d = {"abc": 1, "def": 2} + return d %s d + `, opStr)) + + testBooleanFunction(t, "nested dictionary should be equal to itself", opStr == "==", fmt.Sprintf(` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + `, opStr)) + + testBooleanFunction(t, "simple dictionary equality", opStr == "==", fmt.Sprintf(` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + `, opStr)) + + testBooleanFunction(t, "nested dictionary equality check", opStr == "==", fmt.Sprintf(` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, opStr)) + + testBooleanFunction(t, "simple dictionary unequal", opStr == "!=", fmt.Sprintf(` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + `, opStr)) + + testBooleanFunction(t, "nested dictionary unequal", opStr == "!=", fmt.Sprintf(` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, opStr)) + } +} + func TestInterpretOptionalAnyStruct(t *testing.T) { t.Parallel() From ac4ebf93f4f45ac8ffe0d9f5fb89e2d9ed60a3f9 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 3 Apr 2023 11:03:40 +0300 Subject: [PATCH 089/173] Improve test for Merge method on CoverageReport type --- runtime/coverage_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/runtime/coverage_test.go b/runtime/coverage_test.go index 9d4a36a7d4..819d1da11a 100644 --- a/runtime/coverage_test.go +++ b/runtime/coverage_test.go @@ -906,6 +906,10 @@ func TestCoverageReportMerge(t *testing.T) { otherLocation := common.StringLocation("Factorial") otherCoverageReport.InspectProgram(otherLocation, otherProgram) + // We add `IntegerTraits` to both coverage reports, to test that their + // line hits are properly merged. + coverageReport.InspectProgram(location, program) + coverageReport.AddLineHit(location, 9) excludedLocation := common.StringLocation("FooContract") otherCoverageReport.ExcludeLocation(excludedLocation) @@ -945,11 +949,11 @@ func TestCoverageReportMerge(t *testing.T) { "25": 0, "26": 0, "29": 0, - "9": 0 + "9": 1 }, - "missed_lines": [9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 26, 29], + "missed_lines": [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 26, 29], "statements": 14, - "percentage": "0.0%" + "percentage": "7.1%" } }, "excluded_locations": ["S.FooContract"] From ce732a847ddf31e9f07ea3d5414ea5d0707e7450 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 3 Apr 2023 11:22:37 +0300 Subject: [PATCH 090/173] Introduce a CoverageReport field on runtime.Config type This field deprecates the usage of CoverageReportingEnabled field, by passing the actual coverage report object. --- runtime/config.go | 3 +++ runtime/coverage_test.go | 25 +++++++++++-------------- runtime/environment.go | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/runtime/config.go b/runtime/config.go index b0bde580e4..c3f4b26e2a 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -34,7 +34,10 @@ type Config struct { // ResourceOwnerChangeCallbackEnabled configures if the resource owner change callback is enabled ResourceOwnerChangeHandlerEnabled bool // CoverageReportingEnabled configures if coverage reporting is enabled + // Deprecated: Use the `CoverageReport` field instead CoverageReportingEnabled bool + // CoverageReport enables and collects coverage reporting metrics + CoverageReport *CoverageReport // AccountLinkingEnabled specifies if account linking is enabled AccountLinkingEnabled bool // AttachmentsEnabled specifies if attachments are enabled diff --git a/runtime/coverage_test.go b/runtime/coverage_test.go index 819d1da11a..59fe97cb7b 100644 --- a/runtime/coverage_test.go +++ b/runtime/coverage_test.go @@ -1150,10 +1150,6 @@ func TestRuntimeCoverage(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - CoverageReportingEnabled: true, - }) - importedScript := []byte(` pub let specialNumbers: {Int: String} = { 1729: "Harshad", @@ -1234,6 +1230,7 @@ func TestRuntimeCoverage(t *testing.T) { } `) + coverageReport := NewCoverageReport() runtimeInterface := &testRuntimeInterface{ getCode: func(location Location) (bytes []byte, err error) { switch location { @@ -1244,8 +1241,9 @@ func TestRuntimeCoverage(t *testing.T) { } }, } - - coverageReport := NewCoverageReport() + runtime := NewInterpreterRuntime(Config{ + CoverageReport: coverageReport, + }) value, err := runtime.ExecuteScript( Script{ @@ -1326,10 +1324,6 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - CoverageReportingEnabled: true, - }) - importedScript := []byte(` pub let specialNumbers: {Int: String} = { 1729: "Harshad", @@ -1390,6 +1384,10 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { } `) + coverageReport := NewCoverageReport() + scriptlocation := common.ScriptLocation{} + coverageReport.ExcludeLocation(scriptlocation) + runtimeInterface := &testRuntimeInterface{ getCode: func(location Location) (bytes []byte, err error) { switch location { @@ -1400,10 +1398,9 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { } }, } - - coverageReport := NewCoverageReport() - scriptlocation := common.ScriptLocation{} - coverageReport.ExcludeLocation(scriptlocation) + runtime := NewInterpreterRuntime(Config{ + CoverageReport: coverageReport, + }) value, err := runtime.ExecuteScript( Script{ diff --git a/runtime/environment.go b/runtime/environment.go index b9ef2d6fb9..75374c8c8a 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -606,7 +606,7 @@ func (e *interpreterEnvironment) newInterpreter( } func (e *interpreterEnvironment) newOnStatementHandler() interpreter.OnStatementFunc { - if !e.config.CoverageReportingEnabled { + if e.config.CoverageReport == nil { return nil } From cc5644b671468ee11075b1a81cd1d7797a895b2a Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 3 Apr 2023 19:45:18 +0300 Subject: [PATCH 091/173] Completely remove CoverageReportingEnabled field from runtime.Config type --- runtime/config.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/runtime/config.go b/runtime/config.go index c3f4b26e2a..3b904f4887 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -33,9 +33,6 @@ type Config struct { TracingEnabled bool // ResourceOwnerChangeCallbackEnabled configures if the resource owner change callback is enabled ResourceOwnerChangeHandlerEnabled bool - // CoverageReportingEnabled configures if coverage reporting is enabled - // Deprecated: Use the `CoverageReport` field instead - CoverageReportingEnabled bool // CoverageReport enables and collects coverage reporting metrics CoverageReport *CoverageReport // AccountLinkingEnabled specifies if account linking is enabled From 271ade7db2bf8973818d979d6d46e771da9e694c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 09:53:22 -0700 Subject: [PATCH 092/173] add support for generating code of nested types --- runtime/sema/authaccount_contracts.go | 5 - runtime/sema/authaccount_type.go | 16 +- runtime/sema/gen/main.go | 327 ++++++++++++++++----- runtime/sema/gen/testdata/nested.cdc | 9 + runtime/sema/gen/testdata/nested.golden.go | 73 +++++ runtime/sema/public_account_contracts.go | 5 - runtime/sema/publicaccount_type.go | 14 +- runtime/sema/type.go | 8 + 8 files changed, 357 insertions(+), 100 deletions(-) create mode 100644 runtime/sema/gen/testdata/nested.cdc create mode 100644 runtime/sema/gen/testdata/nested.golden.go diff --git a/runtime/sema/authaccount_contracts.go b/runtime/sema/authaccount_contracts.go index 8b2dd074c1..8fd3eb3979 100644 --- a/runtime/sema/authaccount_contracts.go +++ b/runtime/sema/authaccount_contracts.go @@ -80,11 +80,6 @@ var AuthAccountContractsType = func() *CompositeType { return authAccountContractsType }() -func init() { - // Set the container type after initializing the `AuthAccountContractsType`, to avoid initializing loop. - AuthAccountContractsType.SetContainerType(AuthAccountType) -} - const authAccountContractsTypeAddFunctionDocString = ` Adds the given contract to the account. diff --git a/runtime/sema/authaccount_type.go b/runtime/sema/authaccount_type.go index 910ecd583b..0b6922a186 100644 --- a/runtime/sema/authaccount_type.go +++ b/runtime/sema/authaccount_type.go @@ -68,15 +68,12 @@ var AuthAccountType = func() *CompositeType { Kind: common.CompositeKindStructure, hasComputedMembers: true, importable: false, - NestedTypes: func() *StringTypeOrderedMap { - nestedTypes := &StringTypeOrderedMap{} - nestedTypes.Set(AuthAccountContractsTypeName, AuthAccountContractsType) - nestedTypes.Set(AccountKeysTypeName, AuthAccountKeysType) - nestedTypes.Set(AuthAccountInboxTypeName, AuthAccountInboxType) - return nestedTypes - }(), } + authAccountType.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) + authAccountType.SetNestedType(AccountKeysTypeName, AuthAccountKeysType) + authAccountType.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) + AuthAccountTypeLinkAccountFunctionType = &FunctionType{ Parameters: []Parameter{ { @@ -787,11 +784,6 @@ var AuthAccountKeysTypeRevokeFunctionType = &FunctionType{ RequiredArgumentCount: RequiredArgumentCount(1), } -func init() { - // Set the container type after initializing the AccountKeysTypes, to avoid initializing loop. - AuthAccountKeysType.SetContainerType(AuthAccountType) -} - const AccountKeysTypeName = "Keys" const AccountKeysTypeAddFunctionName = "add" const AccountKeysTypeGetFunctionName = "get" diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index fb6a932eca..db37fa169a 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -143,9 +143,21 @@ func renderDocString(s string) dst.Expr { return result } +type typeDecl struct { + typeName string + fullTypeName string + compositeKind common.CompositeKind + storable bool + equatable bool + exportable bool + importable bool + memberDeclarations []ast.Declaration + nestedTypes []*typeDecl +} + type generator struct { - containerTypeNames []string - decls []dst.Decl + typeStack []*typeDecl + decls []dst.Decl } var _ ast.DeclarationVisitor[struct{}] = &generator{} @@ -161,7 +173,7 @@ func (*generator) VisitVariableDeclaration(_ *ast.VariableDeclaration) struct{} } func (g *generator) VisitFunctionDeclaration(decl *ast.FunctionDeclaration) (_ struct{}) { - if len(g.containerTypeNames) == 0 { + if len(g.typeStack) == 0 { panic("global function declarations are not supported") } @@ -170,7 +182,7 @@ func (g *generator) VisitFunctionDeclaration(decl *ast.FunctionDeclaration) (_ s } functionName := decl.Identifier.Identifier - fullTypeName := g.fullTypeName() + fullTypeName := g.currentFullTypeName() g.addFunctionNameDeclaration(fullTypeName, functionName) @@ -296,7 +308,7 @@ func (g *generator) declarationDocString(decl ast.Declaration) dst.Expr { if len(docString) == 0 { panic(fmt.Errorf( "missing doc string for %s", - g.memberID(identifier), + g.currentMemberID(identifier), )) } @@ -308,72 +320,119 @@ func (*generator) VisitSpecialFunctionDeclaration(_ *ast.SpecialFunctionDeclarat } func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ struct{}) { - var isResource bool compositeKind := decl.CompositeKind switch compositeKind { - case common.CompositeKindStructure: + case common.CompositeKindStructure, + common.CompositeKindResource: break - case common.CompositeKindResource: - isResource = true default: panic(fmt.Sprintf("%s declarations are not supported", compositeKind.Name())) } typeName := decl.Identifier.Identifier - g.containerTypeNames = append(g.containerTypeNames, typeName) + typeDecl := &typeDecl{ + typeName: typeName, + fullTypeName: g.newFullTypeName(typeName), + compositeKind: compositeKind, + } + + if len(g.typeStack) > 0 { + parentType := g.typeStack[len(g.typeStack)-1] + parentType.nestedTypes = append( + parentType.nestedTypes, + typeDecl, + ) + } + + g.typeStack = append( + g.typeStack, + typeDecl, + ) defer func() { - g.containerTypeNames = g.containerTypeNames[:len(g.containerTypeNames)-1] + // Pop + lastIndex := len(g.typeStack) - 1 + g.typeStack[lastIndex] = nil + g.typeStack = g.typeStack[:lastIndex] }() - var memberDeclarations []ast.Declaration + // We can generate a SimpleType declaration, + // if this is a top-level type, + // and this declaration has no nested type declarations. + // Otherwise, we have to generate a CompositeType + + canGenerateSimpleType := len(g.typeStack) == 1 for _, memberDeclaration := range decl.Members.Declarations() { ast.AcceptDeclaration[struct{}](memberDeclaration, g) // Visiting unsupported declarations panics, // so only supported member declarations are added - memberDeclarations = append(memberDeclarations, memberDeclaration) - } + typeDecl.memberDeclarations = append( + typeDecl.memberDeclarations, + memberDeclaration, + ) - var ( - equatable, - storable, - exportable, - importable bool - ) + switch memberDeclaration.(type) { + case *ast.FieldDeclaration, + *ast.FunctionDeclaration: + break + + default: + canGenerateSimpleType = false + } + } for _, conformance := range decl.Conformances { switch conformance.Identifier.Identifier { case "Storable": - storable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explictly marked as storable: %s", + g.currentTypeID(), + )) + } + typeDecl.storable = true + case "Equatable": - equatable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explictly marked as equatable: %s", + g.currentTypeID(), + )) + } + typeDecl.equatable = true + case "Exportable": - exportable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explictly marked as exportable: %s", + g.currentTypeID(), + )) + } + typeDecl.exportable = true + case "Importable": - importable = true + typeDecl.importable = true } } + var typeVarDecl dst.Expr + if canGenerateSimpleType { + typeVarDecl = simpleTypeLiteral(typeDecl) + } else { + typeVarDecl = compositeTypeExpr(typeDecl) + } + g.addDecls( goConstDecl( - typeNameVarName(typeName), + typeNameVarName(typeDecl.fullTypeName), goStringLit(typeName), ), goVarDecl( - fmt.Sprintf("%sType", typeName), - simpleTypeLiteral(simpleType{ - typeName: typeName, - fullTypeName: g.fullTypeName(), - isResource: isResource, - Storable: storable, - Equatable: equatable, - Exportable: exportable, - Importable: importable, - memberDeclarations: memberDeclarations, - }), + typeVarName(typeDecl.fullTypeName), + typeVarDecl, ), ) @@ -394,7 +453,7 @@ func (*generator) VisitTransactionDeclaration(_ *ast.TransactionDeclaration) str func (g *generator) VisitFieldDeclaration(decl *ast.FieldDeclaration) (_ struct{}) { fieldName := decl.Identifier.Identifier - fullTypeName := g.fullTypeName() + fullTypeName := g.currentFullTypeName() docString := g.declarationDocString(decl) g.addDecls( @@ -415,6 +474,10 @@ func (g *generator) VisitFieldDeclaration(decl *ast.FieldDeclaration) (_ struct{ return } +func (g *generator) currentFullTypeName() string { + return g.typeStack[len(g.typeStack)-1].fullTypeName +} + func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { switch t := t.(type) { case *ast.NominalType: @@ -710,15 +773,36 @@ func (*generator) VisitImportDeclaration(_ *ast.ImportDeclaration) struct{} { panic("import declarations are not supported") } -func (g *generator) fullTypeName() string { - return strings.Join(g.containerTypeNames, "") +func (g *generator) newFullTypeName(typeName string) string { + if len(g.typeStack) == 0 { + return typeName + } + parentFullTypeName := g.typeStack[len(g.typeStack)-1].fullTypeName + return parentFullTypeName + typeName } -func (g *generator) memberID(fieldName string) string { - return fmt.Sprintf("%s.%s", - strings.Join(g.containerTypeNames, "."), - fieldName, - ) +func (g *generator) currentTypeID() string { + var b strings.Builder + for i := range g.typeStack { + if i > 0 { + b.WriteByte('.') + } + b.WriteString(g.typeStack[i].typeName) + } + return b.String() +} + +func (g *generator) currentMemberID(memberName string) string { + var b strings.Builder + for i := range g.typeStack { + if i > 0 { + b.WriteByte('.') + } + b.WriteString(g.typeStack[i].typeName) + } + b.WriteByte('.') + b.WriteString(memberName) + return b.String() } func goField(name string, ty dst.Expr) *dst.Field { @@ -798,6 +882,13 @@ func declarationKindExpr(kind string) *dst.Ident { } } +func compositeKindExpr(compositeKind common.CompositeKind) *dst.Ident { + return &dst.Ident{ + Path: "github.com/onflow/cadence/runtime/common", + Name: compositeKind.String(), + } +} + func typeVarName(typeName string) string { return fmt.Sprintf("%sType", typeName) } @@ -856,28 +947,31 @@ func functionDocStringVarName(fullTypeName, functionName string) string { return memberVarName(fullTypeName, functionName, "Function", "DocString") } -type simpleType struct { - typeName string - fullTypeName string - isResource bool - Storable bool - Equatable bool - Exportable bool - Importable bool - memberDeclarations []ast.Declaration -} - -func simpleTypeLiteral(ty simpleType) dst.Expr { +func simpleTypeLiteral(ty *typeDecl) dst.Expr { + + // &SimpleType{ + // Name: TestTypeName, + // QualifiedName: TestTypeName, + // TypeID: TestTypeName, + // tag: TestTypeTag, + // IsResource: true, + // Storable: false, + // Equatable: false, + // Exportable: false, + // Importable: false, + //} + + isResource := ty.compositeKind == common.CompositeKindResource elements := []dst.Expr{ - goKeyValue("Name", typeNameVarIdent(ty.typeName)), - goKeyValue("QualifiedName", typeNameVarIdent(ty.typeName)), - goKeyValue("TypeID", typeNameVarIdent(ty.typeName)), - goKeyValue("tag", typeTagVarIdent(ty.typeName)), - goKeyValue("IsResource", goBoolLit(ty.isResource)), - goKeyValue("Storable", goBoolLit(ty.Storable)), - goKeyValue("Equatable", goBoolLit(ty.Equatable)), - goKeyValue("Exportable", goBoolLit(ty.Exportable)), - goKeyValue("Importable", goBoolLit(ty.Importable)), + goKeyValue("Name", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("QualifiedName", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("TypeID", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("tag", typeTagVarIdent(ty.fullTypeName)), + goKeyValue("IsResource", goBoolLit(isResource)), + goKeyValue("Storable", goBoolLit(ty.storable)), + goKeyValue("Equatable", goBoolLit(ty.equatable)), + goKeyValue("Exportable", goBoolLit(ty.exportable)), + goKeyValue("Importable", goBoolLit(ty.importable)), } if len(ty.memberDeclarations) > 0 { @@ -903,8 +997,6 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. elements := make([]dst.Expr, 0, len(declarations)) for _, declaration := range declarations { - resolve := simpleTypeMemberResolver(fullTypeName, declaration) - var memberName string var kind dst.Expr @@ -927,6 +1019,13 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. ) kind = declarationKindExpr("Function") + case common.DeclarationKindStructureInterface, + common.DeclarationKindStructure, + common.DeclarationKindResource, + common.DeclarationKindResourceInterface: + + continue + default: panic(fmt.Errorf( "%s members are not supported", @@ -934,6 +1033,8 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. )) } + resolve := simpleTypeMemberResolver(fullTypeName, declaration) + elements = append( elements, goKeyValue( @@ -1126,6 +1227,98 @@ func stringMemberResolverMapType() *dst.MapType { } } +func compositeTypeExpr(ty *typeDecl) dst.Expr { + + // func() *CompositeType { + // var t = &CompositeType{ + // Identifier: FooTypeName, + // Kind: common.CompositeKindStructure, + // importable: false, + // hasComputedMembers: true, + // } + // + // t.SetNestedType(FooBarTypeName, FooBarType) + // return t + // }() + + const typeVarName = "t" + + statements := []dst.Stmt{ + &dst.DeclStmt{ + Decl: goVarDecl( + typeVarName, + compositeTypeLiteral(ty), + ), + }, + } + + for _, nestedType := range ty.nestedTypes { + statements = append( + statements, + &dst.ExprStmt{ + X: &dst.CallExpr{ + Fun: &dst.SelectorExpr{ + X: dst.NewIdent(typeVarName), + Sel: dst.NewIdent("SetNestedType"), + }, + Args: []dst.Expr{ + typeNameVarIdent(nestedType.fullTypeName), + typeVarIdent(nestedType.fullTypeName), + }, + }, + }, + ) + } + + statements = append( + statements, + &dst.ReturnStmt{ + Results: []dst.Expr{ + dst.NewIdent(typeVarName), + }, + }, + ) + + return &dst.CallExpr{ + Fun: &dst.FuncLit{ + Type: &dst.FuncType{ + Func: true, + Results: &dst.FieldList{ + List: []*dst.Field{ + { + Type: &dst.StarExpr{ + X: dst.NewIdent("CompositeType"), + }, + }, + }, + }, + }, + Body: &dst.BlockStmt{ + List: statements, + }, + }, + } +} + +func compositeTypeLiteral(ty *typeDecl) dst.Expr { + kind := compositeKindExpr(ty.compositeKind) + + elements := []dst.Expr{ + goKeyValue("Identifier", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("Kind", kind), + goKeyValue("importable", goBoolLit(ty.importable)), + goKeyValue("hasComputedMembers", goBoolLit(true)), + } + + return &dst.UnaryExpr{ + Op: token.AND, + X: &dst.CompositeLit{ + Type: dst.NewIdent("CompositeType"), + Elts: elements, + }, + } +} + func typeAnnotationCallExpr(ty dst.Expr) *dst.CallExpr { return &dst.CallExpr{ Fun: dst.NewIdent("NewTypeAnnotation"), diff --git a/runtime/sema/gen/testdata/nested.cdc b/runtime/sema/gen/testdata/nested.cdc new file mode 100644 index 0000000000..a03088106d --- /dev/null +++ b/runtime/sema/gen/testdata/nested.cdc @@ -0,0 +1,9 @@ +struct Foo { + /// foo + fun foo() + + struct Bar { + /// bar + fun bar() + } +} diff --git a/runtime/sema/gen/testdata/nested.golden.go b/runtime/sema/gen/testdata/nested.golden.go new file mode 100644 index 0000000000..e432214fb2 --- /dev/null +++ b/runtime/sema/gen/testdata/nested.golden.go @@ -0,0 +1,73 @@ +// Code generated from testdata/nested.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/common" + +const FooTypeFooFunctionName = "foo" + +var FooTypeFooFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const FooTypeFooFunctionDocString = ` +foo +` + +const FooBarTypeBarFunctionName = "bar" + +var FooBarTypeBarFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const FooBarTypeBarFunctionDocString = ` +bar +` + +const FooBarTypeName = "Bar" + +var FooBarType = func() *CompositeType { + var t = &CompositeType{ + Identifier: FooBarTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +const FooTypeName = "Foo" + +var FooType = func() *CompositeType { + var t = &CompositeType{ + Identifier: FooTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(FooBarTypeName, FooBarType) + return t +}() diff --git a/runtime/sema/public_account_contracts.go b/runtime/sema/public_account_contracts.go index 0c963bbc9a..0732c35fc9 100644 --- a/runtime/sema/public_account_contracts.go +++ b/runtime/sema/public_account_contracts.go @@ -58,8 +58,3 @@ var PublicAccountContractsType = func() *CompositeType { publicAccountContractsType.Fields = GetFieldNames(members) return publicAccountContractsType }() - -func init() { - // Set the container type after initializing the `PublicAccountContractsType`, to avoid initializing loop. - PublicAccountContractsType.SetContainerType(PublicAccountType) -} diff --git a/runtime/sema/publicaccount_type.go b/runtime/sema/publicaccount_type.go index 7599e2a264..63ac88e696 100644 --- a/runtime/sema/publicaccount_type.go +++ b/runtime/sema/publicaccount_type.go @@ -43,14 +43,11 @@ var PublicAccountType = func() *CompositeType { Kind: common.CompositeKindStructure, hasComputedMembers: true, importable: false, - NestedTypes: func() *StringTypeOrderedMap { - nestedTypes := &StringTypeOrderedMap{} - nestedTypes.Set(AccountKeysTypeName, PublicAccountKeysType) - nestedTypes.Set(PublicAccountContractsTypeName, PublicAccountContractsType) - return nestedTypes - }(), } + publicAccountType.SetNestedType(AccountKeysTypeName, PublicAccountKeysType) + publicAccountType.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) + var members = []*Member{ NewUnmeteredPublicConstantFieldMember( publicAccountType, @@ -207,11 +204,6 @@ var PublicAccountKeysType = func() *CompositeType { return accountKeys }() -func init() { - // Set the container type after initializing the AccountKeysTypes, to avoid initializing loop. - PublicAccountKeysType.SetContainerType(PublicAccountType) -} - var PublicAccountTypeGetCapabilityFunctionType = func() *FunctionType { typeParameter := &TypeParameter{ diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 1fcab3cb48..baf5fc9643 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3918,6 +3918,14 @@ func (t *CompositeType) FieldPosition(name string, declaration ast.CompositeLike return pos } +func (t *CompositeType) SetNestedType(name string, nestedType ContainedType) { + if t.NestedTypes == nil { + t.NestedTypes = &StringTypeOrderedMap{} + } + t.NestedTypes.Set(name, nestedType) + nestedType.SetContainerType(t) +} + // Member type Member struct { From a7a49d2bd29bf7218525aac07adf981eb4269103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 10:45:19 -0700 Subject: [PATCH 093/173] do not import sema in code generator --- runtime/sema/gen/main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index db37fa169a..7cbe38710d 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -30,13 +30,12 @@ import ( "github.com/dave/dst/decorator" "github.com/dave/dst/decorator/resolver/guess" + "github.com/dave/dst" + "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/pretty" - "github.com/onflow/cadence/runtime/sema" - - "github.com/dave/dst" ) const headerTemplate = `// Code generated from {{ . }}. DO NOT EDIT. @@ -658,7 +657,10 @@ func functionTypeExpr( if parameter.Label != "" { var lit dst.Expr - if parameter.Label == sema.ArgumentLabelNotRequired { + // NOTE: avoid import of sema (ArgumentLabelNotRequired), + // so sema can be in a non-buildable state + // and code generation will still succeed + if parameter.Label == "_" { lit = &dst.Ident{ Path: "github.com/onflow/cadence/runtime/sema", Name: "ArgumentLabelNotRequired", From 1c087d0f44b56dc3af977a6e625538f2115aabdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 10:45:35 -0700 Subject: [PATCH 094/173] do not produce cyclic import --- runtime/sema/gen/main.go | 1 - runtime/sema/gen/testdata/functions.golden.go | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 7cbe38710d..224787d111 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -662,7 +662,6 @@ func functionTypeExpr( // and code generation will still succeed if parameter.Label == "_" { lit = &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/sema", Name: "ArgumentLabelNotRequired", } } else { diff --git a/runtime/sema/gen/testdata/functions.golden.go b/runtime/sema/gen/testdata/functions.golden.go index 2549bd737a..6d5ff709fc 100644 --- a/runtime/sema/gen/testdata/functions.golden.go +++ b/runtime/sema/gen/testdata/functions.golden.go @@ -22,7 +22,6 @@ package sema import ( "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" ) const TestTypeNothingFunctionName = "nothing" @@ -46,7 +45,7 @@ var TestTypeParamsFunctionType = &FunctionType{ TypeAnnotation: NewTypeAnnotation(IntType), }, { - Label: sema.ArgumentLabelNotRequired, + Label: ArgumentLabelNotRequired, Identifier: "b", TypeAnnotation: NewTypeAnnotation(StringType), }, @@ -81,7 +80,7 @@ var TestTypeParamsAndReturnFunctionType = &FunctionType{ TypeAnnotation: NewTypeAnnotation(IntType), }, { - Label: sema.ArgumentLabelNotRequired, + Label: ArgumentLabelNotRequired, Identifier: "b", TypeAnnotation: NewTypeAnnotation(StringType), }, From d19ad1ea389576dca93f3c6c9810ce8b54079666 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 3 Apr 2023 14:26:49 -0500 Subject: [PATCH 095/173] Remove sorting of composite initializer parameters Composite initializer parameters have natual sorting, and shouldn't be changed. Thanks Bastian for spotting this during discussion! --- encoding/ccf/decode.go | 13 ------------ encoding/ccf/encode.go | 36 +++++++------------------------- encoding/ccf/sort.go | 47 ------------------------------------------ 3 files changed, 7 insertions(+), 89 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 026305fc61..8b27fe9470 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1761,7 +1761,6 @@ func (d *Decoder) decodeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([] parameterTypes := make([]cadence.Parameter, count) parameterLabels := make(map[string]struct{}, count) parameterIdentifiers := make(map[string]struct{}, count) - var previousParameterIdentifier string common.UseMemory(d.gauge, common.MemoryUsage{ Kind: common.MemoryKindCadenceParameter, @@ -1786,20 +1785,8 @@ func (d *Decoder) decodeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([] return nil, fmt.Errorf("found duplicate parameter identifier %s", param.Identifier) } - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "composite-type-value.initializers MUST be sorted by identifier." - if !stringsAreSortedBytewise(previousParameterIdentifier, param.Identifier) { - return nil, fmt.Errorf( - "parameter identifiers are not sorted (%s, %s)", - previousParameterIdentifier, - param.Identifier, - ) - } - parameterLabels[param.Label] = struct{}{} parameterIdentifiers[param.Identifier] = struct{}{} - previousParameterIdentifier = param.Identifier parameterTypes[i] = param } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index d2b39fd855..35608ce0e0 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1634,37 +1634,15 @@ func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, return err } - switch len(parameterTypes) { - case 0: - // Short-circuit if there is no parameter type. - return nil - - case 1: - // Avoid overhead of sorting if there is only one parameter type. - return e.encodeParameterTypeValue(parameterTypes[0], visited) - - default: - // "Deterministic CCF Encoding Requirements" in CCF specs: - // - // "composite-type-value.initializers MUST be sorted by identifier." - - // NOTE: bytewiseParmaeterSorter doesn't sort parameterTypes in place. - // bytewiseParmaeterSorter.indexes is used as sorted parameterTypes - // index. - sorter := newBytewiseParameterSorter(parameterTypes) - - sort.Sort(sorter) - - // Encode sorted parameter types. - for _, index := range sorter.indexes { - err = e.encodeParameterTypeValue(parameterTypes[index], visited) - if err != nil { - return err - } + // Encode parameter types. + for _, param := range parameterTypes { + err = e.encodeParameterTypeValue(param, visited) + if err != nil { + return err } - - return nil } + + return nil } // encodeParameterTypeValue encodes composite initializer parameter as diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go index 08ed79e39c..d0165ac7c9 100644 --- a/encoding/ccf/sort.go +++ b/encoding/ccf/sort.go @@ -71,53 +71,6 @@ func (x bytewiseFieldSorter) Less(i, j int) bool { return iIdentifier <= jIdentifier } -// bytewiseParameterSorter - -// bytewiseParameterSorter is used to sort parameters by identifier. -// NOTE: in order to avoid making a copy of parameters for sorting, -// - create sorter by calling newBytewiseParameterSorter() -// - sort by calling sort.Sort(sorter) -// - iterate sorted fields by -// for _, index := range sorter.indexes { -// // process sorted field at parameters[index] -// } -type bytewiseParameterSorter struct { - // NOTE: DON'T sort parameters in place because it isn't a copy. - // Instead, sort indexes by parameter identifier. - parameters []cadence.Parameter - // indexes represents sorted indexes of fields - indexes []int -} - -func newBytewiseParameterSorter(parameters []cadence.Parameter) bytewiseParameterSorter { - indexes := make([]int, len(parameters)) - for i := 0; i < len(indexes); i++ { - indexes[i] = i - } - return bytewiseParameterSorter{parameters: parameters, indexes: indexes} -} - -func (x bytewiseParameterSorter) Len() int { - return len(x.indexes) -} - -func (x bytewiseParameterSorter) Swap(i, j int) { - x.indexes[i], x.indexes[j] = x.indexes[j], x.indexes[i] -} - -func (x bytewiseParameterSorter) Less(i, j int) bool { - i = x.indexes[i] - j = x.indexes[j] - - iIdentifier := x.parameters[i].Identifier - jIdentifier := x.parameters[j].Identifier - - if len(iIdentifier) != len(jIdentifier) { - return len(iIdentifier) < len(jIdentifier) - } - return iIdentifier <= jIdentifier -} - // bytewiseKeyValuePairSorter type encodedKeyValuePair struct { From 4e15605d149fc38c7be34f6b821a5bc33cc36a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 12:27:23 -0700 Subject: [PATCH 096/173] simplify simple type member resolvers, avoid allocation on access --- runtime/sema/block.gen.go | 97 ++------ runtime/sema/gen/main.go | 162 ++++--------- .../sema/gen/testdata/docstrings.golden.go | 141 +++-------- runtime/sema/gen/testdata/fields.golden.go | 229 +++++------------- runtime/sema/gen/testdata/functions.golden.go | 163 ++++--------- runtime/sema/type.go | 49 ++-- 6 files changed, 247 insertions(+), 594 deletions(-) diff --git a/runtime/sema/block.gen.go b/runtime/sema/block.gen.go index e4eabfc481..f4e5192412 100644 --- a/runtime/sema/block.gen.go +++ b/runtime/sema/block.gen.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const BlockTypeHeightFieldName = "height" var BlockTypeHeightFieldType = UInt64Type @@ -79,71 +74,31 @@ var BlockType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - BlockTypeHeightFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeHeightFieldType, - BlockTypeHeightFieldDocString, - ) - }, - }, - BlockTypeViewFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeViewFieldType, - BlockTypeViewFieldDocString, - ) - }, - }, - BlockTypeTimestampFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeTimestampFieldType, - BlockTypeTimestampFieldDocString, - ) - }, - }, - BlockTypeIdFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeIdFieldType, - BlockTypeIdFieldDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeHeightFieldName, + BlockTypeHeightFieldType, + BlockTypeHeightFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeViewFieldName, + BlockTypeViewFieldType, + BlockTypeViewFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeTimestampFieldName, + BlockTypeTimestampFieldType, + BlockTypeTimestampFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeIdFieldName, + BlockTypeIdFieldType, + BlockTypeIdFieldDocString, + ), + }) }, } diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 224787d111..9544905a9d 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -994,31 +994,33 @@ func simpleTypeLiteral(ty *typeDecl) dst.Expr { } func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst.Expr { + // func(t *SimpleType) map[string]MemberResolver { + // return MembersAsResolvers([]*Member{ + // ... + // }) + // } + + const typeVarName = "t" elements := make([]dst.Expr, 0, len(declarations)) for _, declaration := range declarations { - var memberName string - var kind dst.Expr + var memberVarName string + memberName := declaration.DeclarationIdentifier().Identifier declarationKind := declaration.DeclarationKind() - - memberName = declaration.DeclarationIdentifier().Identifier - switch declarationKind { case common.DeclarationKindField: - memberName = fieldNameVarName( + memberVarName = fieldNameVarName( fullTypeName, memberName, ) - kind = declarationKindExpr("Field") case common.DeclarationKindFunction: - memberName = functionNameVarName( + memberVarName = functionNameVarName( fullTypeName, memberName, ) - kind = declarationKindExpr("Function") case common.DeclarationKindStructureInterface, common.DeclarationKindStructure, @@ -1034,33 +1036,25 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. )) } - resolve := simpleTypeMemberResolver(fullTypeName, declaration) + element := newDeclarationMember(fullTypeName, typeVarName, memberVarName, declaration) + element.Decorations().Before = dst.NewLine + element.Decorations().After = dst.NewLine - elements = append( - elements, - goKeyValue( - memberName, - &dst.CompositeLit{ - Elts: []dst.Expr{ - goKeyValue("Kind", kind), - goKeyValue("Resolve", resolve), - }, - }, - ), - ) + elements = append(elements, element) } - // func(t *SimpleType) map[string]MemberResolver { - // return map[string]MemberResolver{ - // ... - // } - // } - returnStatement := &dst.ReturnStmt{ Results: []dst.Expr{ - &dst.CompositeLit{ - Type: stringMemberResolverMapType(), - Elts: elements, + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsResolvers"), + Args: []dst.Expr{ + &dst.CompositeLit{ + Type: &dst.ArrayType{ + Elt: &dst.StarExpr{X: dst.NewIdent("Member")}, + }, + Elts: elements, + }, + }, }, }, } @@ -1072,7 +1066,7 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. Func: true, Params: &dst.FieldList{ List: []*dst.Field{ - goField("t", &dst.StarExpr{X: dst.NewIdent("SimpleType")}), + goField(typeVarName, simpleType()), }, }, Results: &dst.FieldList{ @@ -1091,78 +1085,24 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. } } -func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) dst.Expr { - - // func( - // memoryGauge common.MemoryGauge, - // identifier string, - // targetRange ast.Range, - // report func(error), - // ) *Member - - parameters := []*dst.Field{ - goField( - "memoryGauge", - &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/common", - Name: "MemoryGauge", - }, - ), - goField("identifier", dst.NewIdent("string")), - goField( - "targetRange", - &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/ast", - Name: "Range", - }, - ), - goField( - "report", - &dst.FuncType{ - Params: &dst.FieldList{ - List: []*dst.Field{ - {Type: dst.NewIdent("error")}, - }, - }, - }, - ), - } - - // TODO: bug: does not add newline before first and after last. - // Neither does setting decorations on the parameter field list - // or the function type work. Likely a problem in dst. - for _, parameter := range parameters { - parameter.Decorations().Before = dst.NewLine - parameter.Decorations().After = dst.NewLine - } - - functionType := &dst.FuncType{ - Func: true, - Params: &dst.FieldList{ - List: parameters, - }, - Results: &dst.FieldList{ - List: []*dst.Field{ - { - Type: &dst.StarExpr{ - X: dst.NewIdent("Member"), - }, - }, - }, - }, - } +func simpleType() *dst.StarExpr { + return &dst.StarExpr{X: dst.NewIdent("SimpleType")} +} +func newDeclarationMember( + fullTypeName string, + containerTypeVariableIdentifier string, + memberNameVariableIdentifier string, + declaration ast.Declaration, +) dst.Expr { declarationKind := declaration.DeclarationKind() declarationName := declaration.DeclarationIdentifier().Identifier - var result dst.Expr - switch declarationKind { case common.DeclarationKindField: args := []dst.Expr{ - dst.NewIdent("memoryGauge"), - dst.NewIdent("t"), - dst.NewIdent("identifier"), + dst.NewIdent(containerTypeVariableIdentifier), + dst.NewIdent(memberNameVariableIdentifier), dst.NewIdent(fieldTypeVarName(fullTypeName, declarationName)), dst.NewIdent(fieldDocStringVarName(fullTypeName, declarationName)), } @@ -1173,16 +1113,15 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) } // TODO: add support for var - result = &dst.CallExpr{ - Fun: dst.NewIdent("NewPublicConstantFieldMember"), + return &dst.CallExpr{ + Fun: dst.NewIdent("NewUnmeteredPublicConstantFieldMember"), Args: args, } case common.DeclarationKindFunction: args := []dst.Expr{ - dst.NewIdent("memoryGauge"), - dst.NewIdent("t"), - dst.NewIdent("identifier"), + dst.NewIdent(containerTypeVariableIdentifier), + dst.NewIdent(memberNameVariableIdentifier), dst.NewIdent(functionTypeVarName(fullTypeName, declarationName)), dst.NewIdent(functionDocStringVarName(fullTypeName, declarationName)), } @@ -1192,8 +1131,8 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) arg.Decorations().After = dst.NewLine } - result = &dst.CallExpr{ - Fun: dst.NewIdent("NewPublicFunctionMember"), + return &dst.CallExpr{ + Fun: dst.NewIdent("NewUnmeteredPublicFunctionMember"), Args: args, } @@ -1204,21 +1143,6 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) )) } - returnStatement := &dst.ReturnStmt{ - Results: []dst.Expr{ - result, - }, - } - returnStatement.Decorations().Before = dst.EmptyLine - - return &dst.FuncLit{ - Type: functionType, - Body: &dst.BlockStmt{ - List: []dst.Stmt{ - returnStatement, - }, - }, - } } func stringMemberResolverMapType() *dst.MapType { diff --git a/runtime/sema/gen/testdata/docstrings.golden.go b/runtime/sema/gen/testdata/docstrings.golden.go index 2459e56e5c..2a8142da28 100644 --- a/runtime/sema/gen/testdata/docstrings.golden.go +++ b/runtime/sema/gen/testdata/docstrings.golden.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const DocstringsTypeOwoFieldName = "owo" var DocstringsTypeOwoFieldType = IntType @@ -117,103 +112,43 @@ var DocstringsType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - DocstringsTypeOwoFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeOwoFieldType, - DocstringsTypeOwoFieldDocString, - ) - }, - }, - DocstringsTypeUwuFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeUwuFieldType, - DocstringsTypeUwuFieldDocString, - ) - }, - }, - DocstringsTypeNwnFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeNwnFunctionType, - DocstringsTypeNwnFunctionDocString, - ) - }, - }, - DocstringsTypeWithBlanksFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeWithBlanksFieldType, - DocstringsTypeWithBlanksFieldDocString, - ) - }, - }, - DocstringsTypeIsSmolBeanFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeIsSmolBeanFunctionType, - DocstringsTypeIsSmolBeanFunctionDocString, - ) - }, - }, - DocstringsTypeRunningOutOfIdeasFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeRunningOutOfIdeasFunctionType, - DocstringsTypeRunningOutOfIdeasFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeOwoFieldName, + DocstringsTypeOwoFieldType, + DocstringsTypeOwoFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeUwuFieldName, + DocstringsTypeUwuFieldType, + DocstringsTypeUwuFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeNwnFunctionName, + DocstringsTypeNwnFunctionType, + DocstringsTypeNwnFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeWithBlanksFieldName, + DocstringsTypeWithBlanksFieldType, + DocstringsTypeWithBlanksFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeIsSmolBeanFunctionName, + DocstringsTypeIsSmolBeanFunctionType, + DocstringsTypeIsSmolBeanFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeRunningOutOfIdeasFunctionName, + DocstringsTypeRunningOutOfIdeasFunctionType, + DocstringsTypeRunningOutOfIdeasFunctionDocString, + ), + }) }, } diff --git a/runtime/sema/gen/testdata/fields.golden.go b/runtime/sema/gen/testdata/fields.golden.go index 3e4fa3a02a..f0ba9b5f77 100644 --- a/runtime/sema/gen/testdata/fields.golden.go +++ b/runtime/sema/gen/testdata/fields.golden.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const TestTypeTestIntFieldName = "testInt" var TestTypeTestIntFieldType = UInt64Type @@ -132,167 +127,67 @@ var TestType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - TestTypeTestIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestIntFieldType, - TestTypeTestIntFieldDocString, - ) - }, - }, - TestTypeTestOptIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestOptIntFieldType, - TestTypeTestOptIntFieldDocString, - ) - }, - }, - TestTypeTestRefIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestRefIntFieldType, - TestTypeTestRefIntFieldDocString, - ) - }, - }, - TestTypeTestVarIntsFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestVarIntsFieldType, - TestTypeTestVarIntsFieldDocString, - ) - }, - }, - TestTypeTestConstIntsFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestConstIntsFieldType, - TestTypeTestConstIntsFieldDocString, - ) - }, - }, - TestTypeTestParamFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestParamFieldType, - TestTypeTestParamFieldDocString, - ) - }, - }, - TestTypeTestAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestAddressFieldType, - TestTypeTestAddressFieldDocString, - ) - }, - }, - TestTypeTestTypeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestTypeFieldType, - TestTypeTestTypeFieldDocString, - ) - }, - }, - TestTypeTestCapFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestCapFieldType, - TestTypeTestCapFieldDocString, - ) - }, - }, - TestTypeTestCapIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestCapIntFieldType, - TestTypeTestCapIntFieldDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestIntFieldName, + TestTypeTestIntFieldType, + TestTypeTestIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestOptIntFieldName, + TestTypeTestOptIntFieldType, + TestTypeTestOptIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestRefIntFieldName, + TestTypeTestRefIntFieldType, + TestTypeTestRefIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestVarIntsFieldName, + TestTypeTestVarIntsFieldType, + TestTypeTestVarIntsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestConstIntsFieldName, + TestTypeTestConstIntsFieldType, + TestTypeTestConstIntsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestParamFieldName, + TestTypeTestParamFieldType, + TestTypeTestParamFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestAddressFieldName, + TestTypeTestAddressFieldType, + TestTypeTestAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestTypeFieldName, + TestTypeTestTypeFieldType, + TestTypeTestTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestCapFieldName, + TestTypeTestCapFieldType, + TestTypeTestCapFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestCapIntFieldName, + TestTypeTestCapIntFieldType, + TestTypeTestCapIntFieldDocString, + ), + }) }, } diff --git a/runtime/sema/gen/testdata/functions.golden.go b/runtime/sema/gen/testdata/functions.golden.go index 6d5ff709fc..20ed76576f 100644 --- a/runtime/sema/gen/testdata/functions.golden.go +++ b/runtime/sema/gen/testdata/functions.golden.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const TestTypeNothingFunctionName = "nothing" var TestTypeNothingFunctionType = &FunctionType{ @@ -175,119 +170,49 @@ var TestType = &SimpleType{ Exportable: false, Importable: false, Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - TestTypeNothingFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeNothingFunctionType, - TestTypeNothingFunctionDocString, - ) - }, - }, - TestTypeParamsFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeParamsFunctionType, - TestTypeParamsFunctionDocString, - ) - }, - }, - TestTypeReturnFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeReturnFunctionType, - TestTypeReturnFunctionDocString, - ) - }, - }, - TestTypeParamsAndReturnFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeParamsAndReturnFunctionType, - TestTypeParamsAndReturnFunctionDocString, - ) - }, - }, - TestTypeTypeParamFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamFunctionType, - TestTypeTypeParamFunctionDocString, - ) - }, - }, - TestTypeTypeParamWithBoundFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamWithBoundFunctionType, - TestTypeTypeParamWithBoundFunctionDocString, - ) - }, - }, - TestTypeTypeParamWithBoundAndParamFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamWithBoundAndParamFunctionType, - TestTypeTypeParamWithBoundAndParamFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + TestTypeNothingFunctionName, + TestTypeNothingFunctionType, + TestTypeNothingFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeParamsFunctionName, + TestTypeParamsFunctionType, + TestTypeParamsFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeReturnFunctionName, + TestTypeReturnFunctionType, + TestTypeReturnFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeParamsAndReturnFunctionName, + TestTypeParamsAndReturnFunctionType, + TestTypeParamsAndReturnFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamFunctionName, + TestTypeTypeParamFunctionType, + TestTypeTypeParamFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamWithBoundFunctionName, + TestTypeTypeParamWithBoundFunctionType, + TestTypeTypeParamWithBoundFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamWithBoundAndParamFunctionName, + TestTypeTypeParamWithBoundAndParamFunctionType, + TestTypeTypeParamWithBoundAndParamFunctionDocString, + ), + }) }, } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index baf5fc9643..ab180d2126 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3872,18 +3872,7 @@ func (t *CompositeType) GetMembers() map[string]MemberResolver { func (t *CompositeType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - members := make(map[string]MemberResolver, t.Members.Len()) - - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) + memberResolvers := MembersMapAsResolvers(t.Members) // Check conformances. // If this composite type results from a normal composite declaration, @@ -3894,13 +3883,13 @@ func (t *CompositeType) initializeMemberResolvers() { t.ExplicitInterfaceConformanceSet(). ForEach(func(conformance *InterfaceType) { for name, resolver := range conformance.GetMembers() { //nolint:maprange - if _, ok := members[name]; !ok { - members[name] = resolver + if _, ok := memberResolvers[name]; !ok { + memberResolvers[name] = resolver } } }) - t.memberResolvers = withBuiltinMembers(t, members) + t.memberResolvers = withBuiltinMembers(t, memberResolvers) }) } @@ -6801,6 +6790,36 @@ func GetFieldNames(members []*Member) []string { return fields } +func MembersMapAsResolvers(members *StringMemberOrderedMap) map[string]MemberResolver { + resolvers := make(map[string]MemberResolver, members.Len()) + + members.Foreach(func(name string, member *Member) { + resolvers[name] = MemberResolver{ + Kind: member.DeclarationKind, + Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { + return member + }, + } + }) + return resolvers +} + +func MembersAsResolvers(members []*Member) map[string]MemberResolver { + resolvers := make(map[string]MemberResolver, len(members)) + + for _, loopMember := range members { + // NOTE: don't capture loop variable + member := loopMember + resolvers[member.Identifier.Identifier] = MemberResolver{ + Kind: member.DeclarationKind, + Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { + return member + }, + } + } + return resolvers +} + func isNumericSuperType(typ Type) bool { if numberType, ok := typ.(IntegerRangedType); ok { return numberType.IsSuperType() From a5b4e2876a4aec6f1b9ed7ad2f9ea4baa7975988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 12:47:22 -0700 Subject: [PATCH 097/173] generated members for nested composite types --- runtime/sema/authaccount_contracts.go | 4 +- runtime/sema/authaccount_type.go | 12 +- runtime/sema/crypto_algorithm_types.go | 4 +- runtime/sema/gen/main.go | 151 +++++++++++++++------ runtime/sema/gen/testdata/nested.golden.go | 22 +++ runtime/sema/public_account_contracts.go | 4 +- runtime/sema/publicaccount_type.go | 8 +- runtime/sema/type.go | 12 +- runtime/stdlib/bls.go | 2 +- runtime/stdlib/crypto.go | 2 +- runtime/stdlib/rlp.go | 2 +- runtime/stdlib/test.go | 4 +- 12 files changed, 157 insertions(+), 70 deletions(-) diff --git a/runtime/sema/authaccount_contracts.go b/runtime/sema/authaccount_contracts.go index 8fd3eb3979..f0134a81ab 100644 --- a/runtime/sema/authaccount_contracts.go +++ b/runtime/sema/authaccount_contracts.go @@ -75,8 +75,8 @@ var AuthAccountContractsType = func() *CompositeType { ), } - authAccountContractsType.Members = GetMembersAsMap(members) - authAccountContractsType.Fields = GetFieldNames(members) + authAccountContractsType.Members = MembersAsMap(members) + authAccountContractsType.Fields = MembersFieldNames(members) return authAccountContractsType }() diff --git a/runtime/sema/authaccount_type.go b/runtime/sema/authaccount_type.go index 0b6922a186..fd0bae1bd9 100644 --- a/runtime/sema/authaccount_type.go +++ b/runtime/sema/authaccount_type.go @@ -254,8 +254,8 @@ var AuthAccountType = func() *CompositeType { ), } - authAccountType.Members = GetMembersAsMap(members) - authAccountType.Fields = GetFieldNames(members) + authAccountType.Members = MembersAsMap(members) + authAccountType.Fields = MembersFieldNames(members) return authAccountType }() @@ -712,8 +712,8 @@ var AuthAccountKeysType = func() *CompositeType { ), } - accountKeys.Members = GetMembersAsMap(members) - accountKeys.Fields = GetFieldNames(members) + accountKeys.Members = MembersAsMap(members) + accountKeys.Fields = MembersFieldNames(members) return accountKeys }() @@ -975,7 +975,7 @@ var AuthAccountInboxType = func() *CompositeType { ), } - accountInbox.Members = GetMembersAsMap(members) - accountInbox.Fields = GetFieldNames(members) + accountInbox.Members = MembersAsMap(members) + accountInbox.Fields = MembersFieldNames(members) return accountInbox }() diff --git a/runtime/sema/crypto_algorithm_types.go b/runtime/sema/crypto_algorithm_types.go index b8f70b5c88..26cc5ef4d3 100644 --- a/runtime/sema/crypto_algorithm_types.go +++ b/runtime/sema/crypto_algorithm_types.go @@ -303,8 +303,8 @@ func newNativeEnumType( ), ) - ty.Members = GetMembersAsMap(members) - ty.Fields = GetFieldNames(members) + ty.Members = MembersAsMap(members) + ty.Fields = MembersFieldNames(members) return ty } diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 9544905a9d..f1ad850ba2 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -995,16 +995,61 @@ func simpleTypeLiteral(ty *typeDecl) dst.Expr { func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst.Expr { // func(t *SimpleType) map[string]MemberResolver { - // return MembersAsResolvers([]*Member{ - // ... - // }) + // return MembersAsResolvers(...) // } const typeVarName = "t" - elements := make([]dst.Expr, 0, len(declarations)) + returnStatement := &dst.ReturnStmt{ + Results: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsResolvers"), + Args: []dst.Expr{ + membersExpr(fullTypeName, typeVarName, declarations), + }, + }, + }, + } + returnStatement.Decorations().Before = dst.NewLine + returnStatement.Decorations().After = dst.NewLine + + return &dst.FuncLit{ + Type: &dst.FuncType{ + Func: true, + Params: &dst.FieldList{ + List: []*dst.Field{ + goField(typeVarName, simpleType()), + }, + }, + Results: &dst.FieldList{ + List: []*dst.Field{ + { + Type: stringMemberResolverMapType(), + }, + }, + }, + }, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + returnStatement, + }, + }, + } +} + +func membersExpr( + fullTypeName string, + typeVarName string, + memberDeclarations []ast.Declaration, +) dst.Expr { - for _, declaration := range declarations { + // []*Member{ + // ... + // } + + elements := make([]dst.Expr, 0, len(memberDeclarations)) + + for _, declaration := range memberDeclarations { var memberVarName string memberName := declaration.DeclarationIdentifier().Identifier @@ -1043,45 +1088,11 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. elements = append(elements, element) } - returnStatement := &dst.ReturnStmt{ - Results: []dst.Expr{ - &dst.CallExpr{ - Fun: dst.NewIdent("MembersAsResolvers"), - Args: []dst.Expr{ - &dst.CompositeLit{ - Type: &dst.ArrayType{ - Elt: &dst.StarExpr{X: dst.NewIdent("Member")}, - }, - Elts: elements, - }, - }, - }, - }, - } - returnStatement.Decorations().Before = dst.NewLine - returnStatement.Decorations().After = dst.NewLine - - return &dst.FuncLit{ - Type: &dst.FuncType{ - Func: true, - Params: &dst.FieldList{ - List: []*dst.Field{ - goField(typeVarName, simpleType()), - }, - }, - Results: &dst.FieldList{ - List: []*dst.Field{ - { - Type: stringMemberResolverMapType(), - }, - }, - }, - }, - Body: &dst.BlockStmt{ - List: []dst.Stmt{ - returnStatement, - }, + return &dst.CompositeLit{ + Type: &dst.ArrayType{ + Elt: &dst.StarExpr{X: dst.NewIdent("Member")}, }, + Elts: elements, } } @@ -1177,6 +1188,60 @@ func compositeTypeExpr(ty *typeDecl) dst.Expr { }, } + if len(ty.memberDeclarations) > 0 { + // members := []*Member{...} + // t.Members = MembersAsMap(members) + // t.Fields = MembersFieldNames(members) + + members := membersExpr(ty.fullTypeName, typeVarName, ty.memberDeclarations) + + const membersVariableIdentifier = "members" + + statements = append( + statements, + &dst.DeclStmt{ + Decl: goVarDecl( + membersVariableIdentifier, + members, + ), + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(typeVarName), + Sel: dst.NewIdent("Members"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsMap"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(typeVarName), + Sel: dst.NewIdent("Fields"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersFieldNames"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + ) + } + for _, nestedType := range ty.nestedTypes { statements = append( statements, diff --git a/runtime/sema/gen/testdata/nested.golden.go b/runtime/sema/gen/testdata/nested.golden.go index e432214fb2..93017be81b 100644 --- a/runtime/sema/gen/testdata/nested.golden.go +++ b/runtime/sema/gen/testdata/nested.golden.go @@ -55,6 +55,17 @@ var FooBarType = func() *CompositeType { hasComputedMembers: true, } + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + t, + FooBarTypeBarFunctionName, + FooBarTypeBarFunctionType, + FooBarTypeBarFunctionDocString, + ), + } + + t.Members = MembersAsMap(members) + t.Fields = MembersFieldNames(members) return t }() @@ -68,6 +79,17 @@ var FooType = func() *CompositeType { hasComputedMembers: true, } + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + t, + FooTypeFooFunctionName, + FooTypeFooFunctionType, + FooTypeFooFunctionDocString, + ), + } + + t.Members = MembersAsMap(members) + t.Fields = MembersFieldNames(members) t.SetNestedType(FooBarTypeName, FooBarType) return t }() diff --git a/runtime/sema/public_account_contracts.go b/runtime/sema/public_account_contracts.go index 0732c35fc9..410a6fcc88 100644 --- a/runtime/sema/public_account_contracts.go +++ b/runtime/sema/public_account_contracts.go @@ -54,7 +54,7 @@ var PublicAccountContractsType = func() *CompositeType { ), } - publicAccountContractsType.Members = GetMembersAsMap(members) - publicAccountContractsType.Fields = GetFieldNames(members) + publicAccountContractsType.Members = MembersAsMap(members) + publicAccountContractsType.Fields = MembersFieldNames(members) return publicAccountContractsType }() diff --git a/runtime/sema/publicaccount_type.go b/runtime/sema/publicaccount_type.go index 63ac88e696..fcdea49842 100644 --- a/runtime/sema/publicaccount_type.go +++ b/runtime/sema/publicaccount_type.go @@ -117,8 +117,8 @@ var PublicAccountType = func() *CompositeType { ), } - publicAccountType.Members = GetMembersAsMap(members) - publicAccountType.Fields = GetFieldNames(members) + publicAccountType.Members = MembersAsMap(members) + publicAccountType.Fields = MembersFieldNames(members) return publicAccountType }() @@ -199,8 +199,8 @@ var PublicAccountKeysType = func() *CompositeType { ), } - accountKeys.Members = GetMembersAsMap(members) - accountKeys.Fields = GetFieldNames(members) + accountKeys.Members = MembersAsMap(members) + accountKeys.Fields = MembersFieldNames(members) return accountKeys }() diff --git a/runtime/sema/type.go b/runtime/sema/type.go index ab180d2126..e4cb462ef6 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6641,8 +6641,8 @@ var AccountKeyType = func() *CompositeType { ), } - accountKeyType.Members = GetMembersAsMap(members) - accountKeyType.Fields = GetFieldNames(members) + accountKeyType.Members = MembersAsMap(members) + accountKeyType.Fields = MembersFieldNames(members) return accountKeyType }() @@ -6709,8 +6709,8 @@ var PublicKeyType = func() *CompositeType { ), } - publicKeyType.Members = GetMembersAsMap(members) - publicKeyType.Fields = GetFieldNames(members) + publicKeyType.Members = MembersAsMap(members) + publicKeyType.Fields = MembersFieldNames(members) return publicKeyType }() @@ -6766,7 +6766,7 @@ type CryptoAlgorithm interface { DocString() string } -func GetMembersAsMap(members []*Member) *StringMemberOrderedMap { +func MembersAsMap(members []*Member) *StringMemberOrderedMap { membersMap := &StringMemberOrderedMap{} for _, member := range members { name := member.Identifier.Identifier @@ -6779,7 +6779,7 @@ func GetMembersAsMap(members []*Member) *StringMemberOrderedMap { return membersMap } -func GetFieldNames(members []*Member) []string { +func MembersFieldNames(members []*Member) []string { fields := make([]string, 0) for _, member := range members { if member.DeclarationKind == common.DeclarationKindField { diff --git a/runtime/stdlib/bls.go b/runtime/stdlib/bls.go index 9aa6960529..38cf21a5a4 100644 --- a/runtime/stdlib/bls.go +++ b/runtime/stdlib/bls.go @@ -31,7 +31,7 @@ var blsContractType = func() *sema.CompositeType { Kind: common.CompositeKindContract, } - ty.Members = sema.GetMembersAsMap([]*sema.Member{ + ty.Members = sema.MembersAsMap([]*sema.Member{ sema.NewUnmeteredPublicFunctionMember( ty, blsAggregatePublicKeysFunctionName, diff --git a/runtime/stdlib/crypto.go b/runtime/stdlib/crypto.go index 24fd92ebd2..17ea1638b4 100644 --- a/runtime/stdlib/crypto.go +++ b/runtime/stdlib/crypto.go @@ -151,7 +151,7 @@ func cryptoAlgorithmEnumConstructorType[T sema.CryptoAlgorithm]( Type: enumType, }, ), - Members: sema.GetMembersAsMap(members), + Members: sema.MembersAsMap(members), } return constructorType diff --git a/runtime/stdlib/rlp.go b/runtime/stdlib/rlp.go index 99fb5f4bea..5051768bda 100644 --- a/runtime/stdlib/rlp.go +++ b/runtime/stdlib/rlp.go @@ -34,7 +34,7 @@ var rlpContractType = func() *sema.CompositeType { Kind: common.CompositeKindContract, } - ty.Members = sema.GetMembersAsMap([]*sema.Member{ + ty.Members = sema.MembersAsMap([]*sema.Member{ sema.NewUnmeteredPublicFunctionMember( ty, rlpDecodeListFunctionName, diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index dfafd07860..c6c7d70cf1 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -774,8 +774,8 @@ var EmulatorBackendType = func() *sema.CompositeType { ), } - ty.Members = sema.GetMembersAsMap(members) - ty.Fields = sema.GetFieldNames(members) + ty.Members = sema.MembersAsMap(members) + ty.Fields = sema.MembersFieldNames(members) return ty }() From d546be3ab78dc9ce5b12df2e74561fcc3d25b14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 12:57:29 -0700 Subject: [PATCH 098/173] lint --- runtime/sema/gen/main.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index f1ad850ba2..15b142fce5 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -388,7 +388,7 @@ func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ case "Storable": if !canGenerateSimpleType { panic(fmt.Errorf( - "composite types cannot be explictly marked as storable: %s", + "composite types cannot be explicitly marked as storable: %s", g.currentTypeID(), )) } @@ -397,7 +397,7 @@ func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ case "Equatable": if !canGenerateSimpleType { panic(fmt.Errorf( - "composite types cannot be explictly marked as equatable: %s", + "composite types cannot be explicitly marked as equatable: %s", g.currentTypeID(), )) } @@ -406,7 +406,7 @@ func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ case "Exportable": if !canGenerateSimpleType { panic(fmt.Errorf( - "composite types cannot be explictly marked as exportable: %s", + "composite types cannot be explicitly marked as exportable: %s", g.currentTypeID(), )) } @@ -876,13 +876,6 @@ func goBoolLit(b bool) dst.Expr { return dst.NewIdent(strconv.FormatBool(false)) } -func declarationKindExpr(kind string) *dst.Ident { - return &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/common", - Name: fmt.Sprintf("DeclarationKind%s", kind), - } -} - func compositeKindExpr(compositeKind common.CompositeKind) *dst.Ident { return &dst.Ident{ Path: "github.com/onflow/cadence/runtime/common", From 1a14892476494040f44d2b62233aece0ac85399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 13:24:06 -0700 Subject: [PATCH 099/173] move composite type member initialization to init function to allow for self-referencing types --- runtime/sema/gen/main.go | 127 ++++++++++++--------- runtime/sema/gen/testdata/nested.golden.go | 28 +++-- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 15b142fce5..428c7ef61a 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -161,7 +161,7 @@ type generator struct { var _ ast.DeclarationVisitor[struct{}] = &generator{} -func (g *generator) addDecls(decls ...*dst.GenDecl) { +func (g *generator) addDecls(decls ...dst.Decl) { for _, decl := range decls { g.decls = append(g.decls, decl) } @@ -424,17 +424,86 @@ func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ typeVarDecl = compositeTypeExpr(typeDecl) } + tyVarName := typeVarName(typeDecl.fullTypeName) + g.addDecls( goConstDecl( typeNameVarName(typeDecl.fullTypeName), goStringLit(typeName), ), goVarDecl( - typeVarName(typeDecl.fullTypeName), + tyVarName, typeVarDecl, ), ) + memberDeclarations := typeDecl.memberDeclarations + + if !canGenerateSimpleType && + len(memberDeclarations) > 0 { + + // func init() { + // members := []*Member{...} + // t.Members = MembersAsMap(members) + // t.Fields = MembersFieldNames(members) + // } + + members := membersExpr(typeDecl.fullTypeName, tyVarName, memberDeclarations) + + const membersVariableIdentifier = "members" + + g.addDecls( + &dst.FuncDecl{ + Name: dst.NewIdent("init"), + Type: &dst.FuncType{}, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + &dst.DeclStmt{ + Decl: goVarDecl( + membersVariableIdentifier, + members, + ), + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Members"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsMap"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Fields"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersFieldNames"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + }, + }, + }, + ) + } + return } @@ -1181,60 +1250,6 @@ func compositeTypeExpr(ty *typeDecl) dst.Expr { }, } - if len(ty.memberDeclarations) > 0 { - // members := []*Member{...} - // t.Members = MembersAsMap(members) - // t.Fields = MembersFieldNames(members) - - members := membersExpr(ty.fullTypeName, typeVarName, ty.memberDeclarations) - - const membersVariableIdentifier = "members" - - statements = append( - statements, - &dst.DeclStmt{ - Decl: goVarDecl( - membersVariableIdentifier, - members, - ), - }, - &dst.AssignStmt{ - Lhs: []dst.Expr{ - &dst.SelectorExpr{ - X: dst.NewIdent(typeVarName), - Sel: dst.NewIdent("Members"), - }, - }, - Tok: token.ASSIGN, - Rhs: []dst.Expr{ - &dst.CallExpr{ - Fun: dst.NewIdent("MembersAsMap"), - Args: []dst.Expr{ - dst.NewIdent(membersVariableIdentifier), - }, - }, - }, - }, - &dst.AssignStmt{ - Lhs: []dst.Expr{ - &dst.SelectorExpr{ - X: dst.NewIdent(typeVarName), - Sel: dst.NewIdent("Fields"), - }, - }, - Tok: token.ASSIGN, - Rhs: []dst.Expr{ - &dst.CallExpr{ - Fun: dst.NewIdent("MembersFieldNames"), - Args: []dst.Expr{ - dst.NewIdent(membersVariableIdentifier), - }, - }, - }, - }, - ) - } - for _, nestedType := range ty.nestedTypes { statements = append( statements, diff --git a/runtime/sema/gen/testdata/nested.golden.go b/runtime/sema/gen/testdata/nested.golden.go index 93017be81b..1786ae185d 100644 --- a/runtime/sema/gen/testdata/nested.golden.go +++ b/runtime/sema/gen/testdata/nested.golden.go @@ -55,19 +55,22 @@ var FooBarType = func() *CompositeType { hasComputedMembers: true, } + return t +}() + +func init() { var members = []*Member{ NewUnmeteredPublicFunctionMember( - t, + FooBarType, FooBarTypeBarFunctionName, FooBarTypeBarFunctionType, FooBarTypeBarFunctionDocString, ), } - t.Members = MembersAsMap(members) - t.Fields = MembersFieldNames(members) - return t -}() + FooBarType.Members = MembersAsMap(members) + FooBarType.Fields = MembersFieldNames(members) +} const FooTypeName = "Foo" @@ -79,17 +82,20 @@ var FooType = func() *CompositeType { hasComputedMembers: true, } + t.SetNestedType(FooBarTypeName, FooBarType) + return t +}() + +func init() { var members = []*Member{ NewUnmeteredPublicFunctionMember( - t, + FooType, FooTypeFooFunctionName, FooTypeFooFunctionType, FooTypeFooFunctionDocString, ), } - t.Members = MembersAsMap(members) - t.Fields = MembersFieldNames(members) - t.SetNestedType(FooBarTypeName, FooBarType) - return t -}() + FooType.Members = MembersAsMap(members) + FooType.Fields = MembersFieldNames(members) +} From 2a45bfe5ecd3278034b919e8fb73e543ea1277aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 13:34:39 -0700 Subject: [PATCH 100/173] handle nested identifiers --- runtime/sema/gen/main.go | 4 ++++ runtime/sema/gen/testdata/nested.cdc | 3 +++ runtime/sema/gen/testdata/nested.golden.go | 14 ++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index 428c7ef61a..a74d7c898e 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -578,6 +578,10 @@ func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { Type: dst.NewIdent("CapabilityType"), }, } + default: + for _, nestedIdentifier := range t.NestedIdentifiers { + identifier += nestedIdentifier.Identifier + } } return typeVarIdent(identifier) diff --git a/runtime/sema/gen/testdata/nested.cdc b/runtime/sema/gen/testdata/nested.cdc index a03088106d..5b6175f88a 100644 --- a/runtime/sema/gen/testdata/nested.cdc +++ b/runtime/sema/gen/testdata/nested.cdc @@ -2,6 +2,9 @@ struct Foo { /// foo fun foo() + /// Bar + let bar: Foo.Bar + struct Bar { /// bar fun bar() diff --git a/runtime/sema/gen/testdata/nested.golden.go b/runtime/sema/gen/testdata/nested.golden.go index 1786ae185d..dda9006a39 100644 --- a/runtime/sema/gen/testdata/nested.golden.go +++ b/runtime/sema/gen/testdata/nested.golden.go @@ -33,6 +33,14 @@ const FooTypeFooFunctionDocString = ` foo ` +const FooTypeBarFieldName = "bar" + +var FooTypeBarFieldType = FooBarType + +const FooTypeBarFieldDocString = ` +Bar +` + const FooBarTypeBarFunctionName = "bar" var FooBarTypeBarFunctionType = &FunctionType{ @@ -94,6 +102,12 @@ func init() { FooTypeFooFunctionType, FooTypeFooFunctionDocString, ), + NewUnmeteredPublicConstantFieldMember( + FooType, + FooTypeBarFieldName, + FooTypeBarFieldType, + FooTypeBarFieldDocString, + ), } FooType.Members = MembersAsMap(members) From c77f9af4a8f30c6c7320cba1be7c38f74bcd5e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 13:46:54 -0700 Subject: [PATCH 101/173] lint --- runtime/sema/gen/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index a74d7c898e..de3505412a 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -162,9 +162,7 @@ type generator struct { var _ ast.DeclarationVisitor[struct{}] = &generator{} func (g *generator) addDecls(decls ...dst.Decl) { - for _, decl := range decls { - g.decls = append(g.decls, decl) - } + g.decls = append(g.decls, decls...) } func (*generator) VisitVariableDeclaration(_ *ast.VariableDeclaration) struct{} { From b18e260d1100b4164171ac577ae019f136a56cb6 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:47:34 -0500 Subject: [PATCH 102/173] Allow only 0 or 1 composite type initializers in CCF Prior to this change, more initializers were allowed. Currently, Cadence doesn't support more than one initializer and adding this limit to CCF removes the need to sort initializers. Thanks Bastian for suggesting this! --- encoding/ccf/ccf_test.go | 162 +++++++++++++++++++-------------------- encoding/ccf/decode.go | 8 +- encoding/ccf/encode.go | 8 +- 3 files changed, 91 insertions(+), 87 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 2a15f5a775..26f22aab86 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7257,16 +7257,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "Struct", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7303,10 +7305,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7321,8 +7323,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7490,16 +7490,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "Resource", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7536,10 +7538,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7554,8 +7556,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7586,16 +7586,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "Contract", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"Contract","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7632,10 +7634,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7650,8 +7652,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7682,16 +7682,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "StructInterface", "type" : "", "typeID" : "S.test.S", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"StructInterface","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7728,10 +7730,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7746,8 +7748,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7778,16 +7778,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "ResourceInterface", "type" : "", "typeID" : "S.test.R", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"ResourceInterface","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7824,10 +7826,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7842,8 +7844,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7874,16 +7874,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "ContractInterface", "type" : "", "typeID" : "S.test.C", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":"","kind":"ContractInterface","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7920,10 +7922,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -7938,8 +7940,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -8065,16 +8065,18 @@ func TestEncodeType(t *testing.T) { {Identifier: "foo", Type: cadence.IntType{}}, }, Initializers: [][]cadence.Parameter{ - {{Label: "foo", Identifier: "bar", Type: cadence.IntType{}}}, - {{Label: "qux", Identifier: "baz", Type: cadence.StringType{}}}, + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "Enum", "type" : {"kind" : "String"}, "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [ [{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}], [{"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}] ] } } } + // {"value":{"staticType":{"type":{"kind":"String"},"kind":"Enum","typeID":"S.test.E","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -8113,10 +8115,10 @@ func TestEncodeType(t *testing.T) { // Int type (4) 0x04, // initializers - // array, 2 elements follow - 0x82, - // array, 1 element follows + // array, 1 elements follow 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -8131,8 +8133,6 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, - // array, 1 element follows - 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow @@ -9465,15 +9465,13 @@ func TestExportTypeValueRecursiveType(t *testing.T) { { cadence.Parameter{ Type: &cadence.OptionalType{Type: fooTy}, - Label: "aaa", - Identifier: "aaa", + Label: "bbb", + Identifier: "bbb", }, - }, - { cadence.Parameter{ Type: fooTy2, - Label: "bbb", - Identifier: "bbb", + Label: "aaa", + Identifier: "aaa", }, }, }, @@ -9485,10 +9483,10 @@ func TestExportTypeValueRecursiveType(t *testing.T) { StaticType: barTy, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"bbb","id":"bbb"},{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"aaa","id":"aaa"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], [[["aaa", "aaa", 186(184(h'01'))]], [["bbb", "bbb", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], [[["bbb", "bbb", 186(184(h'01'))], ["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) // // language=cbor, format=ccf // tag @@ -9553,20 +9551,20 @@ func TestExportTypeValueRecursiveType(t *testing.T) { // 1 0x01, // initializers + // array, 1 element follow + 0x81, // array, 2 elements follow 0x82, - // array, 1 elements follow - 0x81, // array, 3 elements follow 0x83, // text, 3 bytes follow 0x63, - // aaa - 0x61, 0x61, 0x61, + // bbb + 0x62, 0x62, 0x62, // text, 3 bytes follow 0x63, - // aaa - 0x61, 0x61, 0x61, + // bbb + 0x62, 0x62, 0x62, // tag 0xd8, ccf.CBORTagOptionalTypeValue, // tag @@ -9575,18 +9573,16 @@ func TestExportTypeValueRecursiveType(t *testing.T) { 0x41, // 1 0x01, - // array, 1 elements follow - 0x81, // array, 3 elements follow 0x83, // text, 3 bytes follow 0x63, - // bbb - 0x62, 0x62, 0x62, + // aaa + 0x61, 0x61, 0x61, // text, 3 bytes follow 0x63, - // bbb - 0x62, 0x62, 0x62, + // aaa + 0x61, 0x61, 0x61, // tag 0xd8, ccf.CBORTagResourceTypeValue, // array, 5 elements follow diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 8b27fe9470..fdf2c246d5 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1649,7 +1649,7 @@ func (d *Decoder) decodeCompositeTypeValue( // ] // ] // initializers: [ -// * [ +// ? [ // * [ // label: tstr, // identifier: tstr, @@ -1710,7 +1710,7 @@ func (d *Decoder) _decodeCompositeTypeValue(visited *cadenceTypeByCCFTypeID) (*c // language=CDDL // // initializers: [ -// * [ +// ? [ // * [ // label: tstr, // identifier: tstr, @@ -1725,6 +1725,10 @@ func (d *Decoder) decodeInitializerTypeValues(visited *cadenceTypeByCCFTypeID) ( return nil, err } + if count > 1 { + return nil, fmt.Errorf("expect 0 or 1 initializer, got %d initializers", count) + } + // Unmetered because this is created as an array of nil arrays, not Parameter structs. initializerTypes := make([][]cadence.Parameter, count) for i := 0; i < int(count); i++ { diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 35608ce0e0..26b180e770 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1442,7 +1442,7 @@ func (e *Encoder) encodeContractInterfaceTypeValue(typ *cadence.ContractInterfac // ] // ] // initializers: [ -// * [ +// ? [ // * [ // label: tstr, // identifier: tstr, @@ -1591,7 +1591,7 @@ func (e *Encoder) encodeFieldTypeValue(fieldType cadence.Field, visited ccfTypeI // language=CDDL // // initializers: [ -// * [ +// ? [ // * [ // label: tstr, // identifier: tstr, @@ -1600,6 +1600,10 @@ func (e *Encoder) encodeFieldTypeValue(fieldType cadence.Field, visited ccfTypeI // ] // ] func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Parameter, visited ccfTypeIDByCadenceType) error { + if len(initializerTypes) > 1 { + return fmt.Errorf("got %d initializers, want 0 or 1 initializer", len(initializerTypes)) + } + // Encode CBOR array head with number of initializers. err := e.enc.EncodeArrayHead(uint64(len(initializerTypes))) if err != nil { From e23dfc770aea5d4e2972f266a9689712b5c86ea1 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:54:30 -0500 Subject: [PATCH 103/173] Remove a redundant comment in CCF test --- encoding/ccf/ccf_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 26f22aab86..955b713dc6 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -11754,7 +11754,7 @@ func TestDecodeInvalidData(t *testing.T) { // language=edn, format=ccf // 130([138(null), null]) // - // language=cbor, format=ccf, format=ccf + // language=cbor, format=ccf // tag 0xd8, ccf.CBORTagTypeAndValue, // array, 2 items follow From 32e8af8f2e168392d1364ce5ef55b495a8415fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 14:00:43 -0700 Subject: [PATCH 104/173] refactor AuthAccount and PublicAccount types, as well as their nested types, to Cadence --- runtime/contract_update_validation_test.go | 2 +- runtime/interpreter/account.go | 22 +- runtime/interpreter/accountcontracts.go | 10 +- runtime/interpreter/accountinbox.go | 6 +- runtime/interpreter/accountkeys.go | 16 +- runtime/interpreter/interpreter.go | 10 +- runtime/sema/authaccount.cdc | 297 +++++ runtime/sema/authaccount.gen.go | 1355 ++++++++++++++++++++ runtime/sema/authaccount.go | 29 + runtime/sema/authaccount_contracts.go | 181 --- runtime/sema/authaccount_type.go | 981 -------------- runtime/sema/public_account_contracts.go | 60 - runtime/sema/publicaccount.cdc | 82 ++ runtime/sema/publicaccount.cdc.go | 21 + runtime/sema/publicaccount.gen.go | 481 +++++++ runtime/sema/publicaccount_type.go | 240 ---- runtime/stdlib/account.go | 22 +- runtime/stdlib/flow.go | 6 +- 18 files changed, 2323 insertions(+), 1498 deletions(-) create mode 100644 runtime/sema/authaccount.cdc create mode 100644 runtime/sema/authaccount.gen.go create mode 100644 runtime/sema/authaccount.go delete mode 100644 runtime/sema/authaccount_contracts.go delete mode 100644 runtime/sema/authaccount_type.go delete mode 100644 runtime/sema/public_account_contracts.go create mode 100644 runtime/sema/publicaccount.cdc create mode 100644 runtime/sema/publicaccount.cdc.go create mode 100644 runtime/sema/publicaccount.gen.go delete mode 100644 runtime/sema/publicaccount_type.go diff --git a/runtime/contract_update_validation_test.go b/runtime/contract_update_validation_test.go index e66282d3f5..4384dca6a6 100644 --- a/runtime/contract_update_validation_test.go +++ b/runtime/contract_update_validation_test.go @@ -59,7 +59,7 @@ func newContractAddTransaction(name string, code string) string { func newContractUpdateTransaction(name string, code string) string { return newContractDeployTransaction( - sema.AuthAccountContractsTypeUpdateExperimentalFunctionName, + sema.AuthAccountContractsTypeUpdate__experimentalFunctionName, name, code, ) diff --git a/runtime/interpreter/account.go b/runtime/interpreter/account.go index c96d119ec8..5c5cc1279a 100644 --- a/runtime/interpreter/account.go +++ b/runtime/interpreter/account.go @@ -109,6 +109,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachPublicFunctionName: if forEachPublicFunction == nil { forEachPublicFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachPublicFunctionType, address, common.PathDomainPublic, sema.PublicPathType, @@ -119,6 +120,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachPrivateFunctionName: if forEachPrivateFunction == nil { forEachPrivateFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachPrivateFunctionType, address, common.PathDomainPrivate, sema.PrivatePathType, @@ -129,6 +131,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachStoredFunctionName: if forEachStoredFunction == nil { forEachStoredFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachStoredFunctionType, address, common.PathDomainStorage, sema.StoragePathType, @@ -198,7 +201,10 @@ func NewAuthAccountValue( case sema.AuthAccountTypeGetLinkTargetFunctionName: if getLinkTargetFunction == nil { - getLinkTargetFunction = inter.accountGetLinkTargetFunction(address) + getLinkTargetFunction = inter.accountGetLinkTargetFunction( + sema.AuthAccountTypeGetLinkTargetFunctionType, + address, + ) } return getLinkTargetFunction @@ -253,7 +259,7 @@ func NewPublicAccountValue( fields := map[string]Value{ sema.PublicAccountTypeAddressFieldName: address, - sema.PublicAccountTypeGetCapabilityFieldName: accountGetCapabilityFunction( + sema.PublicAccountTypeGetCapabilityFunctionName: accountGetCapabilityFunction( gauge, address, sema.PublicPathType, @@ -280,12 +286,13 @@ func NewPublicAccountValue( } return contracts - case sema.PublicAccountTypePathsFieldName: + case sema.PublicAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) - case sema.PublicAccountTypeForEachPublicFieldName: + case sema.PublicAccountTypeForEachPublicFunctionName: if forEachPublicFunction == nil { forEachPublicFunction = inter.newStorageIterationFunction( + sema.PublicAccountTypeForEachPublicFunctionType, address, common.PathDomainPublic, sema.PublicPathType, @@ -305,9 +312,12 @@ func NewPublicAccountValue( case sema.PublicAccountTypeStorageCapacityFieldName: return storageCapacityGet(inter) - case sema.PublicAccountTypeGetTargetLinkFieldName: + case sema.PublicAccountTypeGetLinkTargetFunctionName: if getLinkTargetFunction == nil { - getLinkTargetFunction = inter.accountGetLinkTargetFunction(address) + getLinkTargetFunction = inter.accountGetLinkTargetFunction( + sema.PublicAccountTypeGetLinkTargetFunctionType, + address, + ) } return getLinkTargetFunction } diff --git a/runtime/interpreter/accountcontracts.go b/runtime/interpreter/accountcontracts.go index 94e5228680..00f8c45171 100644 --- a/runtime/interpreter/accountcontracts.go +++ b/runtime/interpreter/accountcontracts.go @@ -45,11 +45,11 @@ func NewAuthAccountContractsValue( ) Value { fields := map[string]Value{ - sema.AuthAccountContractsTypeAddFunctionName: addFunction, - sema.AccountContractsTypeGetFunctionName: getFunction, - sema.AccountContractsTypeBorrowFunctionName: borrowFunction, - sema.AuthAccountContractsTypeRemoveFunctionName: removeFunction, - sema.AuthAccountContractsTypeUpdateExperimentalFunctionName: updateFunction, + sema.AuthAccountContractsTypeAddFunctionName: addFunction, + sema.AccountContractsTypeGetFunctionName: getFunction, + sema.AccountContractsTypeBorrowFunctionName: borrowFunction, + sema.AuthAccountContractsTypeRemoveFunctionName: removeFunction, + sema.AuthAccountContractsTypeUpdate__experimentalFunctionName: updateFunction, } computeField := func( diff --git a/runtime/interpreter/accountinbox.go b/runtime/interpreter/accountinbox.go index 1f48bb7356..61bfa7c89d 100644 --- a/runtime/interpreter/accountinbox.go +++ b/runtime/interpreter/accountinbox.go @@ -40,9 +40,9 @@ func NewAuthAccountInboxValue( ) Value { fields := map[string]Value{ - sema.AuthAccountTypeInboxPublishFunctionName: publishFunction, - sema.AuthAccountTypeInboxUnpublishFunctionName: unpublishFunction, - sema.AuthAccountTypeInboxClaimFunctionName: claimFunction, + sema.AuthAccountInboxTypePublishFunctionName: publishFunction, + sema.AuthAccountInboxTypeUnpublishFunctionName: unpublishFunction, + sema.AuthAccountInboxTypeClaimFunctionName: claimFunction, } var str string diff --git a/runtime/interpreter/accountkeys.go b/runtime/interpreter/accountkeys.go index ea6dd30d7d..8f5bbe52e1 100644 --- a/runtime/interpreter/accountkeys.go +++ b/runtime/interpreter/accountkeys.go @@ -42,15 +42,15 @@ func NewAuthAccountKeysValue( ) Value { fields := map[string]Value{ - sema.AccountKeysTypeAddFunctionName: addFunction, - sema.AccountKeysTypeGetFunctionName: getFunction, - sema.AccountKeysTypeRevokeFunctionName: revokeFunction, - sema.AccountKeysTypeForEachFunctionName: forEachFunction, + sema.AuthAccountKeysTypeAddFunctionName: addFunction, + sema.AuthAccountKeysTypeGetFunctionName: getFunction, + sema.AuthAccountKeysTypeRevokeFunctionName: revokeFunction, + sema.AuthAccountKeysTypeForEachFunctionName: forEachFunction, } computeField := func(name string, _ *Interpreter, _ LocationRange) Value { switch name { - case sema.AccountKeysTypeCountFieldName: + case sema.AuthAccountKeysTypeCountFieldName: return getKeysCount() } return nil @@ -93,13 +93,13 @@ func NewPublicAccountKeysValue( ) Value { fields := map[string]Value{ - sema.AccountKeysTypeGetFunctionName: getFunction, - sema.AccountKeysTypeForEachFunctionName: forEachFunction, + sema.PublicAccountKeysTypeGetFunctionName: getFunction, + sema.PublicAccountKeysTypeForEachFunctionName: forEachFunction, } computeField := func(name string, _ *Interpreter, _ LocationRange) Value { switch name { - case sema.AccountKeysTypeCountFieldName: + case sema.PublicAccountKeysTypeCountFieldName: return getKeysCount() } return nil diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index bf9b6121fa..3100d93505 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3341,6 +3341,7 @@ func (interpreter *Interpreter) recordStorageMutation() { } func (interpreter *Interpreter) newStorageIterationFunction( + functionType *sema.FunctionType, addressValue AddressValue, domain common.PathDomain, pathType sema.Type, @@ -3351,7 +3352,7 @@ func (interpreter *Interpreter) newStorageIterationFunction( return NewHostFunctionValue( interpreter, - sema.AccountForEachFunctionType(pathType), + functionType, func(invocation Invocation) Value { interpreter := invocation.Interpreter @@ -3834,14 +3835,17 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr ) } -func (interpreter *Interpreter) accountGetLinkTargetFunction(addressValue AddressValue) *HostFunctionValue { +func (interpreter *Interpreter) accountGetLinkTargetFunction( + functionType *sema.FunctionType, + addressValue AddressValue, +) *HostFunctionValue { // Converted addresses can be cached and don't have to be recomputed on each function invocation address := addressValue.ToAddress() return NewHostFunctionValue( interpreter, - sema.AccountTypeGetLinkTargetFunctionType, + functionType, func(invocation Invocation) Value { interpreter := invocation.Interpreter diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc new file mode 100644 index 0000000000..df666a5507 --- /dev/null +++ b/runtime/sema/authaccount.cdc @@ -0,0 +1,297 @@ + +pub struct AuthAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: AuthAccount.Contracts + + /// The keys assigned to the account. + pub let keys: AuthAccount.Keys + + /// The inbox allows bootstrapping (sending and receiving) capabilities. + pub let inbox: AuthAccount.Inbox + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// All private paths of this account. + pub let privatePaths: [PrivatePath] + + /// All storage paths of this account. + pub let storagePaths: [StoragePath] + + /// **DEPRECATED**: Use `keys.add` instead. + /// + /// Adds a public key to the account. + /// + /// The public key must be encoded together with their signature algorithm, hashing algorithm and weight. + pub fun addPublicKey(_ publicKey: [UInt8]) + + /// **DEPRECATED**: Use `keys.revoke` instead. + /// + /// Revokes the key at the given index. + pub fun removePublicKey(_ index: Int) + + /// Saves the given object into the account's storage at the given path. + /// + /// Resources are moved into storage, and structures are copied. + /// + /// If there is already an object stored under the given path, the program aborts. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun save(_ value: T, to: StoragePath) + + /// Reads the type of an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, the type of the object is returned without modifying the stored object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun type(at path: StoragePath): Type? + + /// Loads an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, + /// the stored resource or structure is moved out of storage and returned as an optional. + /// + /// When the function returns, the storage no longer contains an object under the given path. + /// + /// The given type must be a supertype of the type of the loaded object. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the loaded object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun load(from: StoragePath): T? + + /// Returns a copy of a structure stored in account storage under the given path, + /// without removing it from storage, + /// or nil if no object is stored under the given path. + /// + /// If there is a structure stored, it is copied. + /// The structure stays stored in storage after the function returns. + /// + /// The given type must be a supertype of the type of the copied structure. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the copied structure. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun copy(from: StoragePath): T? + + /// Returns a reference to an object in storage without removing it from storage. + /// + /// If no object is stored under the given path, the function returns nil. + /// If there is an object stored, a reference is returned as an optional, + /// provided it can be borrowed using the given type. + /// If the stored object cannot be borrowed using the given type, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the borrowed object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed + pub fun borrow(from: StoragePath): T? + + /// Creates a capability at the given public or private path, + /// which targets the given public, private, or storage path. + /// + /// The target path leads to the object that will provide the functionality defined by this capability. + /// + /// The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + /// + /// It is not necessary for the target path to lead to a valid object; the target path could be empty, + /// or could lead to an object which does not provide the necessary type interface: + /// 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. + /// + /// The link is latent. + /// + /// The target value might be stored after the link is created, + /// and the target value might be moved out after the link has been created. + pub fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + + /// 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>? + + /// Returns the capability at the given private or public path. + pub fun getCapability(_ path: CapabilityPath): Capability + + /// 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? + + /// Removes the capability at the given public or private path. + pub fun unlink(_ path: CapabilityPath) + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + + /// Iterate over all the private paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPrivate(_ function: ((PrivatePath, Type): Bool)) + + /// Iterate over all the stored paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachStored(_ function: ((StoragePath, Type): Bool)) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Adds the given contract to the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// All additional arguments that are given are passed further to the initializer + /// of the contract that is being deployed. + /// + /// The function fails if a contract/contract interface with the given name already exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract. + pub fun add( + name: String, + code: [UInt8] + ): DeployedContract + + /// **Experimental** + /// + /// Updates the code for the contract/contract interface in the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// Does **not** run the initializer of the contract/contract interface again. + /// The contract instance in the world state stays as is. + /// + /// Fails if no contract/contract interface with the given name exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract for the updated contract. + pub fun update__experimental(name: String, code: [UInt8]): DeployedContract + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Removes the contract/contract interface from the account which has the given name, if any. + /// + /// Returns the removed deployed contract, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun remove(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } + + pub struct Keys { + + /// Adds a new key with the given hashing algorithm and a weight. + /// + /// Returns the added key. + pub fun add( + publicKey: PublicKey, + hashAlgorithm: HashAlgorithm, + weight: UFix64 + ): AccountKey + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Marks the key at the given index revoked, but does not delete it. + /// + /// Returns the revoked key if it exists, or nil otherwise. + pub fun revoke(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: ((AccountKey): Bool)) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } + + pub struct Inbox { + + /// Publishes a new Capability under the given name, + /// to be claimed by the specified recipient. + pub fun publish(_ value: Capability, name: String, recipient: Address) + + /// Unpublishes a Capability previously published by this account. + /// + /// Returns `nil` if no Capability is published under the given name. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun unpublish(_ name: String): Capability? + + /// Claims a Capability previously published by the specified provider. + /// + /// Returns `nil` if no Capability is published under the given name, + /// or if this account is not its intended recipient. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun claim(_ name: String, provider: Address): Capability? + } +} diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go new file mode 100644 index 0000000000..a67ab0ad7f --- /dev/null +++ b/runtime/sema/authaccount.gen.go @@ -0,0 +1,1355 @@ +// Code generated from authaccount.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/common" + +const AuthAccountTypeAddressFieldName = "address" + +var AuthAccountTypeAddressFieldType = TheAddressType + +const AuthAccountTypeAddressFieldDocString = ` +The address of the account. +` + +const AuthAccountTypeBalanceFieldName = "balance" + +var AuthAccountTypeBalanceFieldType = UFix64Type + +const AuthAccountTypeBalanceFieldDocString = ` +The FLOW balance of the default vault of this account. +` + +const AuthAccountTypeAvailableBalanceFieldName = "availableBalance" + +var AuthAccountTypeAvailableBalanceFieldType = UFix64Type + +const AuthAccountTypeAvailableBalanceFieldDocString = ` +The FLOW balance of the default vault of this account that is available to be moved. +` + +const AuthAccountTypeStorageUsedFieldName = "storageUsed" + +var AuthAccountTypeStorageUsedFieldType = UInt64Type + +const AuthAccountTypeStorageUsedFieldDocString = ` +The current amount of storage used by the account in bytes. +` + +const AuthAccountTypeStorageCapacityFieldName = "storageCapacity" + +var AuthAccountTypeStorageCapacityFieldType = UInt64Type + +const AuthAccountTypeStorageCapacityFieldDocString = ` +The storage capacity of the account in bytes. +` + +const AuthAccountTypeContractsFieldName = "contracts" + +var AuthAccountTypeContractsFieldType = AuthAccountContractsType + +const AuthAccountTypeContractsFieldDocString = ` +The contracts deployed to the account. +` + +const AuthAccountTypeKeysFieldName = "keys" + +var AuthAccountTypeKeysFieldType = AuthAccountKeysType + +const AuthAccountTypeKeysFieldDocString = ` +The keys assigned to the account. +` + +const AuthAccountTypeInboxFieldName = "inbox" + +var AuthAccountTypeInboxFieldType = AuthAccountInboxType + +const AuthAccountTypeInboxFieldDocString = ` +The inbox allows bootstrapping (sending and receiving) capabilities. +` + +const AuthAccountTypePublicPathsFieldName = "publicPaths" + +var AuthAccountTypePublicPathsFieldType = &VariableSizedType{ + Type: PublicPathType, +} + +const AuthAccountTypePublicPathsFieldDocString = ` +All public paths of this account. +` + +const AuthAccountTypePrivatePathsFieldName = "privatePaths" + +var AuthAccountTypePrivatePathsFieldType = &VariableSizedType{ + Type: PrivatePathType, +} + +const AuthAccountTypePrivatePathsFieldDocString = ` +All private paths of this account. +` + +const AuthAccountTypeStoragePathsFieldName = "storagePaths" + +var AuthAccountTypeStoragePathsFieldType = &VariableSizedType{ + Type: StoragePathType, +} + +const AuthAccountTypeStoragePathsFieldDocString = ` +All storage paths of this account. +` + +const AuthAccountTypeAddPublicKeyFunctionName = "addPublicKey" + +var AuthAccountTypeAddPublicKeyFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "publicKey", + TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ + Type: UInt8Type, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeAddPublicKeyFunctionDocString = ` +**DEPRECATED**: Use ` + "`keys.add`" + ` instead. + +Adds a public key to the account. + +The public key must be encoded together with their signature algorithm, hashing algorithm and weight. +` + +const AuthAccountTypeRemovePublicKeyFunctionName = "removePublicKey" + +var AuthAccountTypeRemovePublicKeyFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "index", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeRemovePublicKeyFunctionDocString = ` +**DEPRECATED**: Use ` + "`keys.revoke`" + ` instead. + +Revokes the key at the given index. +` + +const AuthAccountTypeSaveFunctionName = "save" + +var AuthAccountTypeSaveFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: StorableType, +} + +var AuthAccountTypeSaveFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeSaveFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: NewTypeAnnotation(&GenericType{ + TypeParameter: AuthAccountTypeSaveFunctionTypeParameterT, + }), + }, + { + Identifier: "to", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeSaveFunctionDocString = ` +Saves the given object into the account's storage at the given path. + +Resources are moved into storage, and structures are copied. + +If there is already an object stored under the given path, the program aborts. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeTypeFunctionName = "type" + +var AuthAccountTypeTypeFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: "at", + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MetaType, + }, + ), +} + +const AuthAccountTypeTypeFunctionDocString = ` +Reads the type of an object from the account's storage which is stored under the given path, +or nil if no object is stored under the given path. + +If there is an object stored, the type of the object is returned without modifying the stored object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeLoadFunctionName = "load" + +var AuthAccountTypeLoadFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: StorableType, +} + +var AuthAccountTypeLoadFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeLoadFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeLoadFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeLoadFunctionDocString = ` +Loads an object from the account's storage which is stored under the given path, +or nil if no object is stored under the given path. + +If there is an object stored, +the stored resource or structure is moved out of storage and returned as an optional. + +When the function returns, the storage no longer contains an object under the given path. + +The given type must be a supertype of the type of the loaded object. +If it is not, the function panics. + +The given type must not necessarily be exactly the same as the type of the loaded object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeCopyFunctionName = "copy" + +var AuthAccountTypeCopyFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: AnyStructType, +} + +var AuthAccountTypeCopyFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeCopyFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeCopyFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeCopyFunctionDocString = ` +Returns a copy of a structure stored in account storage under the given path, +without removing it from storage, +or nil if no object is stored under the given path. + +If there is a structure stored, it is copied. +The structure stays stored in storage after the function returns. + +The given type must be a supertype of the type of the copied structure. +If it is not, the function panics. + +The given type must not necessarily be exactly the same as the type of the copied structure. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeBorrowFunctionName = "borrow" + +var AuthAccountTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeBorrowFunctionDocString = ` +Returns a reference to an object in storage without removing it from storage. + +If no object is stored under the given path, the function returns nil. +If there is an object stored, a reference is returned as an optional, +provided it can be borrowed using the given type. +If the stored object cannot be borrowed using the given type, the function panics. + +The given type must not necessarily be exactly the same as the type of the borrowed object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed +` + +const AuthAccountTypeLinkFunctionName = "link" + +var AuthAccountTypeLinkFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeLinkFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeLinkFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "newCapabilityPath", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + { + Identifier: "target", + TypeAnnotation: NewTypeAnnotation(PathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountTypeLinkFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountTypeLinkFunctionDocString = ` +Creates a capability at the given public or private path, +which targets the given public, private, or storage path. + +The target path leads to the object that will provide the functionality defined by this capability. + +The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + +Returns nil if a link for the given capability path already exists, or the newly created capability if not. + +It is not necessary for the target path to lead to a valid object; the target path could be empty, +or could lead to an object which does not provide the necessary type interface: +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. + +The link is latent. + +The target value might be stored after the link is created, +and the target value might be moved out after the link has been created. +` + +const AuthAccountTypeLinkAccountFunctionName = "linkAccount" + +var AuthAccountTypeLinkAccountFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "newCapabilityPath", + TypeAnnotation: NewTypeAnnotation(PrivatePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &ReferenceType{ + Type: AuthAccountType, + }, + ), + }, + ), +} + +const AuthAccountTypeLinkAccountFunctionDocString = ` +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. +` + +const AuthAccountTypeGetCapabilityFunctionName = "getCapability" + +var AuthAccountTypeGetCapabilityFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeGetCapabilityFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeGetCapabilityFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountTypeGetCapabilityFunctionTypeParameterT, + }, + ), + ), +} + +const AuthAccountTypeGetCapabilityFunctionDocString = ` +Returns the capability at the given private or public path. +` + +const AuthAccountTypeGetLinkTargetFunctionName = "getLinkTarget" + +var AuthAccountTypeGetLinkTargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: PathType, + }, + ), +} + +const AuthAccountTypeGetLinkTargetFunctionDocString = ` +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. +` + +const AuthAccountTypeUnlinkFunctionName = "unlink" + +var AuthAccountTypeUnlinkFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeUnlinkFunctionDocString = ` +Removes the capability at the given public or private path. +` + +const AuthAccountTypeForEachPublicFunctionName = "forEachPublic" + +var AuthAccountTypeForEachPublicFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachPublicFunctionDocString = ` +Iterate over all the public paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountTypeForEachPrivateFunctionName = "forEachPrivate" + +var AuthAccountTypeForEachPrivateFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PrivatePathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachPrivateFunctionDocString = ` +Iterate over all the private paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountTypeForEachStoredFunctionName = "forEachStored" + +var AuthAccountTypeForEachStoredFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachStoredFunctionDocString = ` +Iterate over all the stored paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountContractsTypeNamesFieldName = "names" + +var AuthAccountContractsTypeNamesFieldType = &VariableSizedType{ + Type: StringType, +} + +const AuthAccountContractsTypeNamesFieldDocString = ` +The names of all contracts deployed in the account. +` + +const AuthAccountContractsTypeAddFunctionName = "add" + +var AuthAccountContractsTypeAddFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "code", + TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ + Type: UInt8Type, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + DeployedContractType, + ), +} + +const AuthAccountContractsTypeAddFunctionDocString = ` +Adds the given contract to the account. + +The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. +The code must contain exactly one contract or contract interface, +which must have the same name as the ` + "`name`" + ` parameter. + +All additional arguments that are given are passed further to the initializer +of the contract that is being deployed. + +The function fails if a contract/contract interface with the given name already exists in the account, +if the given code does not declare exactly one contract or contract interface, +or if the given name does not match the name of the contract/contract interface declaration in the code. + +Returns the deployed contract. +` + +const AuthAccountContractsTypeUpdate__experimentalFunctionName = "update__experimental" + +var AuthAccountContractsTypeUpdate__experimentalFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "code", + TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ + Type: UInt8Type, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + DeployedContractType, + ), +} + +const AuthAccountContractsTypeUpdate__experimentalFunctionDocString = ` +**Experimental** + +Updates the code for the contract/contract interface in the account. + +The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. +The code must contain exactly one contract or contract interface, +which must have the same name as the ` + "`name`" + ` parameter. + +Does **not** run the initializer of the contract/contract interface again. +The contract instance in the world state stays as is. + +Fails if no contract/contract interface with the given name exists in the account, +if the given code does not declare exactly one contract or contract interface, +or if the given name does not match the name of the contract/contract interface declaration in the code. + +Returns the deployed contract for the updated contract. +` + +const AuthAccountContractsTypeGetFunctionName = "get" + +var AuthAccountContractsTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const AuthAccountContractsTypeGetFunctionDocString = ` +Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const AuthAccountContractsTypeRemoveFunctionName = "remove" + +var AuthAccountContractsTypeRemoveFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const AuthAccountContractsTypeRemoveFunctionDocString = ` +Removes the contract/contract interface from the account which has the given name, if any. + +Returns the removed deployed contract, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const AuthAccountContractsTypeBorrowFunctionName = "borrow" + +var AuthAccountContractsTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountContractsTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountContractsTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountContractsTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountContractsTypeBorrowFunctionDocString = ` +Returns a reference of the given type to the contract with the given name in the account, if any. + +Returns nil if no contract with the given name exists in the account, +or if the contract does not conform to the given type. +` + +const AuthAccountContractsTypeName = "Contracts" + +var AuthAccountContractsType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountContractsTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + AuthAccountContractsType, + AuthAccountContractsTypeNamesFieldName, + AuthAccountContractsTypeNamesFieldType, + AuthAccountContractsTypeNamesFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeAddFunctionName, + AuthAccountContractsTypeAddFunctionType, + AuthAccountContractsTypeAddFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeUpdate__experimentalFunctionName, + AuthAccountContractsTypeUpdate__experimentalFunctionType, + AuthAccountContractsTypeUpdate__experimentalFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeGetFunctionName, + AuthAccountContractsTypeGetFunctionType, + AuthAccountContractsTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeRemoveFunctionName, + AuthAccountContractsTypeRemoveFunctionType, + AuthAccountContractsTypeRemoveFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeBorrowFunctionName, + AuthAccountContractsTypeBorrowFunctionType, + AuthAccountContractsTypeBorrowFunctionDocString, + ), + } + + AuthAccountContractsType.Members = MembersAsMap(members) + AuthAccountContractsType.Fields = MembersFieldNames(members) +} + +const AuthAccountKeysTypeAddFunctionName = "add" + +var AuthAccountKeysTypeAddFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "publicKey", + TypeAnnotation: NewTypeAnnotation(PublicKeyType), + }, + { + Identifier: "hashAlgorithm", + TypeAnnotation: NewTypeAnnotation(HashAlgorithmType), + }, + { + Identifier: "weight", + TypeAnnotation: NewTypeAnnotation(UFix64Type), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + AccountKeyType, + ), +} + +const AuthAccountKeysTypeAddFunctionDocString = ` +Adds a new key with the given hashing algorithm and a weight. + +Returns the added key. +` + +const AuthAccountKeysTypeGetFunctionName = "get" + +var AuthAccountKeysTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const AuthAccountKeysTypeGetFunctionDocString = ` +Returns the key at the given index, if it exists, or nil otherwise. + +Revoked keys are always returned, but they have ` + "`isRevoked`" + ` field set to true. +` + +const AuthAccountKeysTypeRevokeFunctionName = "revoke" + +var AuthAccountKeysTypeRevokeFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const AuthAccountKeysTypeRevokeFunctionDocString = ` +Marks the key at the given index revoked, but does not delete it. + +Returns the revoked key if it exists, or nil otherwise. +` + +const AuthAccountKeysTypeForEachFunctionName = "forEach" + +var AuthAccountKeysTypeForEachFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(AccountKeyType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountKeysTypeForEachFunctionDocString = ` +Iterate over all unrevoked keys in this account, +passing each key in turn to the provided function. + +Iteration is stopped early if the function returns ` + "`false`" + `. +The order of iteration is undefined. +` + +const AuthAccountKeysTypeCountFieldName = "count" + +var AuthAccountKeysTypeCountFieldType = UInt64Type + +const AuthAccountKeysTypeCountFieldDocString = ` +The total number of unrevoked keys in this account. +` + +const AuthAccountKeysTypeName = "Keys" + +var AuthAccountKeysType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountKeysTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeAddFunctionName, + AuthAccountKeysTypeAddFunctionType, + AuthAccountKeysTypeAddFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeGetFunctionName, + AuthAccountKeysTypeGetFunctionType, + AuthAccountKeysTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeRevokeFunctionName, + AuthAccountKeysTypeRevokeFunctionType, + AuthAccountKeysTypeRevokeFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeForEachFunctionName, + AuthAccountKeysTypeForEachFunctionType, + AuthAccountKeysTypeForEachFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountKeysType, + AuthAccountKeysTypeCountFieldName, + AuthAccountKeysTypeCountFieldType, + AuthAccountKeysTypeCountFieldDocString, + ), + } + + AuthAccountKeysType.Members = MembersAsMap(members) + AuthAccountKeysType.Fields = MembersFieldNames(members) +} + +const AuthAccountInboxTypePublishFunctionName = "publish" + +var AuthAccountInboxTypePublishFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: NewTypeAnnotation(&CapabilityType{}), + }, + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "recipient", + TypeAnnotation: NewTypeAnnotation(TheAddressType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountInboxTypePublishFunctionDocString = ` +Publishes a new Capability under the given name, +to be claimed by the specified recipient. +` + +const AuthAccountInboxTypeUnpublishFunctionName = "unpublish" + +var AuthAccountInboxTypeUnpublishFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountInboxTypeUnpublishFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountInboxTypeUnpublishFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountInboxTypeUnpublishFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountInboxTypeUnpublishFunctionDocString = ` +Unpublishes a Capability previously published by this account. + +Returns ` + "`nil`" + ` if no Capability is published under the given name. + +Errors if the Capability under that name does not match the provided type. +` + +const AuthAccountInboxTypeClaimFunctionName = "claim" + +var AuthAccountInboxTypeClaimFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountInboxTypeClaimFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountInboxTypeClaimFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "provider", + TypeAnnotation: NewTypeAnnotation(TheAddressType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountInboxTypeClaimFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountInboxTypeClaimFunctionDocString = ` +Claims a Capability previously published by the specified provider. + +Returns ` + "`nil`" + ` if no Capability is published under the given name, +or if this account is not its intended recipient. + +Errors if the Capability under that name does not match the provided type. +` + +const AuthAccountInboxTypeName = "Inbox" + +var AuthAccountInboxType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountInboxTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypePublishFunctionName, + AuthAccountInboxTypePublishFunctionType, + AuthAccountInboxTypePublishFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypeUnpublishFunctionName, + AuthAccountInboxTypeUnpublishFunctionType, + AuthAccountInboxTypeUnpublishFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypeClaimFunctionName, + AuthAccountInboxTypeClaimFunctionType, + AuthAccountInboxTypeClaimFunctionDocString, + ), + } + + AuthAccountInboxType.Members = MembersAsMap(members) + AuthAccountInboxType.Fields = MembersFieldNames(members) +} + +const AuthAccountTypeName = "AuthAccount" + +var AuthAccountType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) + t.SetNestedType(AuthAccountKeysTypeName, AuthAccountKeysType) + t.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeAddressFieldName, + AuthAccountTypeAddressFieldType, + AuthAccountTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeBalanceFieldName, + AuthAccountTypeBalanceFieldType, + AuthAccountTypeBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeAvailableBalanceFieldName, + AuthAccountTypeAvailableBalanceFieldType, + AuthAccountTypeAvailableBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStorageUsedFieldName, + AuthAccountTypeStorageUsedFieldType, + AuthAccountTypeStorageUsedFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStorageCapacityFieldName, + AuthAccountTypeStorageCapacityFieldType, + AuthAccountTypeStorageCapacityFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeContractsFieldName, + AuthAccountTypeContractsFieldType, + AuthAccountTypeContractsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeKeysFieldName, + AuthAccountTypeKeysFieldType, + AuthAccountTypeKeysFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeInboxFieldName, + AuthAccountTypeInboxFieldType, + AuthAccountTypeInboxFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypePublicPathsFieldName, + AuthAccountTypePublicPathsFieldType, + AuthAccountTypePublicPathsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypePrivatePathsFieldName, + AuthAccountTypePrivatePathsFieldType, + AuthAccountTypePrivatePathsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStoragePathsFieldName, + AuthAccountTypeStoragePathsFieldType, + AuthAccountTypeStoragePathsFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeAddPublicKeyFunctionName, + AuthAccountTypeAddPublicKeyFunctionType, + AuthAccountTypeAddPublicKeyFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeRemovePublicKeyFunctionName, + AuthAccountTypeRemovePublicKeyFunctionType, + AuthAccountTypeRemovePublicKeyFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeSaveFunctionName, + AuthAccountTypeSaveFunctionType, + AuthAccountTypeSaveFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeTypeFunctionName, + AuthAccountTypeTypeFunctionType, + AuthAccountTypeTypeFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLoadFunctionName, + AuthAccountTypeLoadFunctionType, + AuthAccountTypeLoadFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeCopyFunctionName, + AuthAccountTypeCopyFunctionType, + AuthAccountTypeCopyFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeBorrowFunctionName, + AuthAccountTypeBorrowFunctionType, + AuthAccountTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLinkFunctionName, + AuthAccountTypeLinkFunctionType, + AuthAccountTypeLinkFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLinkAccountFunctionName, + AuthAccountTypeLinkAccountFunctionType, + AuthAccountTypeLinkAccountFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeGetCapabilityFunctionName, + AuthAccountTypeGetCapabilityFunctionType, + AuthAccountTypeGetCapabilityFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeGetLinkTargetFunctionName, + AuthAccountTypeGetLinkTargetFunctionType, + AuthAccountTypeGetLinkTargetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeUnlinkFunctionName, + AuthAccountTypeUnlinkFunctionType, + AuthAccountTypeUnlinkFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachPublicFunctionName, + AuthAccountTypeForEachPublicFunctionType, + AuthAccountTypeForEachPublicFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachPrivateFunctionName, + AuthAccountTypeForEachPrivateFunctionType, + AuthAccountTypeForEachPrivateFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachStoredFunctionName, + AuthAccountTypeForEachStoredFunctionType, + AuthAccountTypeForEachStoredFunctionDocString, + ), + } + + AuthAccountType.Members = MembersAsMap(members) + AuthAccountType.Fields = MembersFieldNames(members) +} diff --git a/runtime/sema/authaccount.go b/runtime/sema/authaccount.go new file mode 100644 index 0000000000..d2518c40b0 --- /dev/null +++ b/runtime/sema/authaccount.go @@ -0,0 +1,29 @@ +/* + * 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 authaccount.cdc authaccount.gen.go + +var AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation = AuthAccountTypeLinkAccountFunctionType.Parameters[0].TypeAnnotation + +func init() { + AuthAccountContractsTypeAddFunctionType.RequiredArgumentCount = RequiredArgumentCount(2) + AuthAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true + PublicAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true +} diff --git a/runtime/sema/authaccount_contracts.go b/runtime/sema/authaccount_contracts.go deleted file mode 100644 index f0134a81ab..0000000000 --- a/runtime/sema/authaccount_contracts.go +++ /dev/null @@ -1,181 +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 sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const AuthAccountContractsTypeName = "Contracts" -const AuthAccountContractsTypeAddFunctionName = "add" -const AuthAccountContractsTypeUpdateExperimentalFunctionName = "update__experimental" -const AuthAccountContractsTypeRemoveFunctionName = "remove" - -// AuthAccountContractsType represents the type `AuthAccount.Contracts` -var AuthAccountContractsType = func() *CompositeType { - - authAccountContractsType := &CompositeType{ - Identifier: AuthAccountContractsTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeAddFunctionName, - AuthAccountContractsTypeAddFunctionType, - authAccountContractsTypeAddFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeUpdateExperimentalFunctionName, - AuthAccountContractsTypeUpdateExperimentalFunctionType, - authAccountContractsTypeUpdateExperimentalFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AccountContractsTypeGetFunctionName, - AccountContractsTypeGetFunctionType, - accountContractsTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AccountContractsTypeBorrowFunctionName, - AccountContractsTypeBorrowFunctionType, - accountContractsTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeRemoveFunctionName, - AuthAccountContractsTypeRemoveFunctionType, - authAccountContractsTypeRemoveFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountContractsType, - AccountContractsTypeNamesFieldName, - accountContractsTypeNamesFieldType, - accountContractsTypeNamesFieldDocString, - ), - } - - authAccountContractsType.Members = MembersAsMap(members) - authAccountContractsType.Fields = MembersFieldNames(members) - return authAccountContractsType -}() - -const authAccountContractsTypeAddFunctionDocString = ` -Adds the given contract to the account. - -The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. -The code must contain exactly one contract or contract interface, -which must have the same name as the ` + "`name`" + ` parameter. - -All additional arguments that are given are passed further to the initializer -of the contract that is being deployed. - -Fails if a contract/contract interface with the given name already exists in the account, -if the given code does not declare exactly one contract or contract interface, -or if the given name does not match the name of the contract/contract interface declaration in the code. - -Returns the deployed contract. -` - -var AuthAccountContractsTypeAddFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation( - StringType, - ), - }, - { - Identifier: "code", - TypeAnnotation: NewTypeAnnotation( - ByteArrayType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - DeployedContractType, - ), - // additional arguments are passed to the contract initializer - RequiredArgumentCount: RequiredArgumentCount(2), -} - -const authAccountContractsTypeUpdateExperimentalFunctionDocString = ` -**Experimental** - -Updates the code for the contract/contract interface in the account. - -The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. -The code must contain exactly one contract or contract interface, -which must have the same name as the ` + "`name`" + ` parameter. - -Does **not** run the initializer of the contract/contract interface again. -The contract instance in the world state stays as is. - -Fails if no contract/contract interface with the given name exists in the account, -if the given code does not declare exactly one contract or contract interface, -or if the given name does not match the name of the contract/contract interface declaration in the code. - -Returns the deployed contract for the updated contract. -` - -var AuthAccountContractsTypeUpdateExperimentalFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation( - StringType, - ), - }, - { - Identifier: "code", - TypeAnnotation: NewTypeAnnotation( - ByteArrayType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - DeployedContractType, - ), -} - -const authAccountContractsTypeRemoveFunctionDocString = ` -Removes the contract/contract interface from the account which has the given name, if any. - -Returns the removed deployed contract, if any. - -Returns nil if no contract/contract interface with the given name exists in the account. -` - -var AuthAccountContractsTypeRemoveFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation(StringType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: DeployedContractType, - }, - ), -} diff --git a/runtime/sema/authaccount_type.go b/runtime/sema/authaccount_type.go deleted file mode 100644 index fd0bae1bd9..0000000000 --- a/runtime/sema/authaccount_type.go +++ /dev/null @@ -1,981 +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 sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const AuthAccountTypeName = "AuthAccount" - -const AuthAccountTypeAddressFieldName = "address" -const AuthAccountTypeBalanceFieldName = "balance" -const AuthAccountTypeAvailableBalanceFieldName = "availableBalance" -const AuthAccountTypeStorageUsedFieldName = "storageUsed" -const AuthAccountTypeStorageCapacityFieldName = "storageCapacity" -const AuthAccountTypeAddPublicKeyFunctionName = "addPublicKey" -const AuthAccountTypeRemovePublicKeyFunctionName = "removePublicKey" -const AuthAccountTypeSaveFunctionName = "save" -const AuthAccountTypeLoadFunctionName = "load" -const AuthAccountTypeTypeFunctionName = "type" -const AuthAccountTypeCopyFunctionName = "copy" -const AuthAccountTypeBorrowFunctionName = "borrow" -const AuthAccountTypeLinkFunctionName = "link" -const AuthAccountTypeLinkAccountFunctionName = "linkAccount" -const AuthAccountTypeUnlinkFunctionName = "unlink" -const AuthAccountTypeGetCapabilityFunctionName = "getCapability" -const AuthAccountTypeGetLinkTargetFunctionName = "getLinkTarget" -const AuthAccountTypeForEachPublicFunctionName = "forEachPublic" -const AuthAccountTypeForEachPrivateFunctionName = "forEachPrivate" -const AuthAccountTypeForEachStoredFunctionName = "forEachStored" -const AuthAccountTypeContractsFieldName = "contracts" -const AuthAccountTypeKeysFieldName = "keys" -const AuthAccountTypeInboxFieldName = "inbox" -const AuthAccountTypePublicPathsFieldName = "publicPaths" -const AuthAccountTypePrivatePathsFieldName = "privatePaths" -const AuthAccountTypeStoragePathsFieldName = "storagePaths" -const AuthAccountTypeInboxPublishFunctionName = "publish" -const AuthAccountTypeInboxUnpublishFunctionName = "unpublish" -const AuthAccountTypeInboxClaimFunctionName = "claim" - -var AuthAccountTypeLinkAccountFunctionType *FunctionType - -var AuthAccountTypeLinkAccountFunctionTypePathParameterType = PrivatePathType - -// AuthAccountType represents the authorized access to an account. -// Access to an AuthAccount means having full access to its storage, public keys, and code. -// Only signed transactions can get the AuthAccount for an account. -var AuthAccountType = func() *CompositeType { - - authAccountType := &CompositeType{ - Identifier: AuthAccountTypeName, - Kind: common.CompositeKindStructure, - hasComputedMembers: true, - importable: false, - } - - authAccountType.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) - authAccountType.SetNestedType(AccountKeysTypeName, AuthAccountKeysType) - authAccountType.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) - - AuthAccountTypeLinkAccountFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "newCapabilityPath", - TypeAnnotation: NewTypeAnnotation( - AuthAccountTypeLinkAccountFunctionTypePathParameterType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &ReferenceType{ - Type: authAccountType, - }, - }, - }, - ), - } - - var members = []*Member{ - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeAddressFieldName, - TheAddressType, - accountTypeAddressFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeBalanceFieldName, - UFix64Type, - accountTypeAccountBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeAvailableBalanceFieldName, - UFix64Type, - accountTypeAccountAvailableBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStorageUsedFieldName, - UInt64Type, - accountTypeStorageUsedFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStorageCapacityFieldName, - UInt64Type, - accountTypeStorageCapacityFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeAddPublicKeyFunctionName, - AuthAccountTypeAddPublicKeyFunctionType, - authAccountTypeAddPublicKeyFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeRemovePublicKeyFunctionName, - AuthAccountTypeRemovePublicKeyFunctionType, - authAccountTypeRemovePublicKeyFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeSaveFunctionName, - AuthAccountTypeSaveFunctionType, - authAccountTypeSaveFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeTypeFunctionName, - AuthAccountTypeTypeFunctionType, - authAccountTypeTypeFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLoadFunctionName, - AuthAccountTypeLoadFunctionType, - authAccountTypeLoadFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeCopyFunctionName, - AuthAccountTypeCopyFunctionType, - authAccountTypeCopyFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeBorrowFunctionName, - AuthAccountTypeBorrowFunctionType, - authAccountTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLinkFunctionName, - AuthAccountTypeLinkFunctionType, - authAccountTypeLinkFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLinkAccountFunctionName, - AuthAccountTypeLinkAccountFunctionType, - authAccountTypeLinkAccountFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeUnlinkFunctionName, - AuthAccountTypeUnlinkFunctionType, - authAccountTypeUnlinkFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeGetCapabilityFunctionName, - AuthAccountTypeGetCapabilityFunctionType, - authAccountTypeGetCapabilityFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeGetLinkTargetFunctionName, - AccountTypeGetLinkTargetFunctionType, - accountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeContractsFieldName, - AuthAccountContractsType, - accountTypeContractsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeKeysFieldName, - AuthAccountKeysType, - accountTypeKeysFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeInboxFieldName, - AuthAccountInboxType, - accountInboxDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypePublicPathsFieldName, - AuthAccountPublicPathsType, - authAccountTypePublicPathsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypePrivatePathsFieldName, - AuthAccountPrivatePathsType, - authAccountTypePrivatePathsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStoragePathsFieldName, - AuthAccountStoragePathsType, - authAccountTypeStoragePathsFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachPublicFunctionName, - AuthAccountForEachPublicFunctionType, - authAccountForEachPublicDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachPrivateFunctionName, - AuthAccountForEachPrivateFunctionType, - authAccountForEachPrivateDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachStoredFunctionName, - AuthAccountForEachStoredFunctionType, - authAccountForEachStoredDocString, - ), - } - - authAccountType.Members = MembersAsMap(members) - authAccountType.Fields = MembersFieldNames(members) - return authAccountType -}() - -var AuthAccountPublicPathsType = &VariableSizedType{ - Type: PublicPathType, -} - -var AuthAccountPrivatePathsType = &VariableSizedType{ - Type: PrivatePathType, -} - -var AuthAccountStoragePathsType = &VariableSizedType{ - Type: StoragePathType, -} - -const authAccountTypeStoragePathsFieldDocString = ` -All the storage paths of an account -` - -const authAccountTypePublicPathsFieldDocString = ` -All the public paths of an account -` - -const authAccountTypePrivatePathsFieldDocString = ` -All the private paths of an account -` - -const authAccountForEachPublicDocString = ` -Iterate over all the public paths of an account. Takes one argument: the function to be applied to each public path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -const authAccountForEachPrivateDocString = ` -Iterate over all the private paths of an account. Takes one argument: the function to be applied to each private path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -const authAccountForEachStoredDocString = ` -Iterate over all the storage paths of an account. Takes one argument: the function to be applied to each storage path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -var AuthAccountForEachPublicFunctionType = AccountForEachFunctionType(PublicPathType) - -var AuthAccountForEachPrivateFunctionType = AccountForEachFunctionType(PrivatePathType) - -var AuthAccountForEachStoredFunctionType = AccountForEachFunctionType(StoragePathType) - -var AuthAccountTypeAddPublicKeyFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "key", - TypeAnnotation: NewTypeAnnotation( - ByteArrayType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const authAccountTypeAddPublicKeyFunctionDocString = ` -Adds the given byte representation of a public key to the account's keys -` - -var AuthAccountTypeRemovePublicKeyFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "index", - TypeAnnotation: NewTypeAnnotation( - IntType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const authAccountTypeRemovePublicKeyFunctionDocString = ` -Removes the public key at the given index from the account's keys -` - -var AuthAccountTypeSaveFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: StorableType, - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: NewTypeAnnotation( - &GenericType{ - TypeParameter: typeParameter, - }, - ), - }, - { - Label: "to", - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), - } -}() - -const authAccountTypeSaveFunctionDocString = ` -Saves the given object into the account's storage at the given path. -Resources are moved into storage, and structures are copied. - -If there is already an object stored under the given path, the program aborts. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeLoadFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: StorableType, - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeTypeFunctionDocString = ` -Reads the type of an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. - -If there is an object stored, the type of the object is returned without modifying the stored object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeTypeFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: "at", - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: MetaType, - }, - ), -} - -const authAccountTypeLoadFunctionDocString = ` -Loads an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. - -If there is an object stored, the stored resource or structure is moved out of storage and returned as an optional. - -When the function returns, the storage no longer contains an object under the given path. - -The given type must be a supertype of the type of the loaded object. -If it is not, the function panics. - -The given type must not necessarily be exactly the same as the type of the loaded object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeCopyFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: AnyStructType, - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeCopyFunctionDocString = ` -Returns a copy of a structure stored in account storage under the given path, without removing it from storage, or nil if no object is stored under the given path. - -If there is a structure stored, it is copied. -The structure stays stored in storage after the function returns. - -The given type must be a supertype of the type of the copied structure. -If it is not, the function panics. - -The given type must not necessarily be exactly the same as the type of the copied structure. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeBorrowFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(StoragePathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeBorrowFunctionDocString = ` -Returns a reference to an object in storage without removing it from storage. - -If no object is stored under the given path, the function returns nil. -If there is an object stored, a reference is returned as an optional, provided it can be borrowed using the given type. -If the stored object cannot be borrowed using the given type, the function panics. - -The given type must not necessarily be exactly the same as the type of the borrowed object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeLinkFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "newCapabilityPath", - TypeAnnotation: NewTypeAnnotation(CapabilityPathType), - }, - { - Identifier: "target", - TypeAnnotation: NewTypeAnnotation(PathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -const authAccountTypeLinkFunctionDocString = ` -Creates a capability at the given public or private path which targets the given public, private, or storage path. -The target path leads to the object that will provide the functionality defined by this capability. - -The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. - -Returns nil if a link for the given capability path already exists, or the newly created capability if not. - -It is not necessary for the target path to lead to a valid object; the target path could be empty, or could lead to an object which does not provide the necessary type interface: -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. -The link is latent. The target value might be stored after the link is created, and the target value might be moved out after the link has been created. -` - -const authAccountTypeLinkAccountFunctionDocString = ` -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. -` - -var AuthAccountTypeUnlinkFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: NewTypeAnnotation(CapabilityPathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), -} - -const authAccountTypeUnlinkFunctionDocString = ` -Removes the capability at the given public or private path -` - -var AuthAccountTypeGetCapabilityFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - Optional: true, - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: NewTypeAnnotation(CapabilityPathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeGetCapabilityFunctionDocString = ` -Returns the capability at the given private or public path, or nil if it does not exist -` - -var AccountTypeGetLinkTargetFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: NewTypeAnnotation(CapabilityPathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: PathType, - }, - ), -} - -// AuthAccountKeysType represents the keys associated with an auth account. -var AuthAccountKeysType = func() *CompositeType { - - accountKeys := &CompositeType{ - Identifier: AccountKeysTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeAddFunctionName, - AuthAccountKeysTypeAddFunctionType, - authAccountKeysTypeAddFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeGetFunctionName, - AccountKeysTypeGetFunctionType, - accountKeysTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeRevokeFunctionName, - AuthAccountKeysTypeRevokeFunctionType, - authAccountKeysTypeRevokeFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeForEachFunctionName, - AccountKeysTypeForEachFunctionType, - accountKeysTypeForEachFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - accountKeys, - AccountKeysTypeCountFieldName, - AccountKeysTypeCountFieldType, - accountKeysTypeCountFieldDocString, - ), - } - - accountKeys.Members = MembersAsMap(members) - accountKeys.Fields = MembersFieldNames(members) - return accountKeys -}() - -var AuthAccountKeysTypeAddFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: AccountKeyPublicKeyFieldName, - TypeAnnotation: NewTypeAnnotation(PublicKeyType), - }, - { - Identifier: AccountKeyHashAlgoFieldName, - TypeAnnotation: NewTypeAnnotation(HashAlgorithmType), - }, - { - Identifier: AccountKeyWeightFieldName, - TypeAnnotation: NewTypeAnnotation(UFix64Type), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(AccountKeyType), - RequiredArgumentCount: RequiredArgumentCount(3), -} - -var AccountKeysTypeGetFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: AccountKeyKeyIndexFieldName, - TypeAnnotation: NewTypeAnnotation(IntType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(&OptionalType{Type: AccountKeyType}), - RequiredArgumentCount: RequiredArgumentCount(1), -} - -// fun keys.forEach(_ function: ((AccountKey): Bool)): Void -var AccountKeysTypeForEachFunctionType = func() *FunctionType { - // ((AccountKey): Bool) - iterFunctionType := &FunctionType{ - Parameters: []Parameter{ - { - TypeAnnotation: NewTypeAnnotation(AccountKeyType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(BoolType), - } - - return &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "function", - TypeAnnotation: NewTypeAnnotation(iterFunctionType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), - } -}() - -var AccountKeysTypeCountFieldType = UInt64Type - -var AuthAccountKeysTypeRevokeFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: AccountKeyKeyIndexFieldName, - TypeAnnotation: NewTypeAnnotation(IntType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(&OptionalType{Type: AccountKeyType}), - RequiredArgumentCount: RequiredArgumentCount(1), -} - -const AccountKeysTypeName = "Keys" -const AccountKeysTypeAddFunctionName = "add" -const AccountKeysTypeGetFunctionName = "get" -const AccountKeysTypeForEachFunctionName = "forEach" -const AccountKeysTypeRevokeFunctionName = "revoke" -const AccountKeysTypeCountFieldName = "count" - -const accountTypeGetLinkTargetFunctionDocString = ` -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. -` - -const accountTypeAddressFieldDocString = ` -The address of the account -` - -const accountTypeContractsFieldDocString = ` -The contracts of the account -` - -const accountTypeAccountBalanceFieldDocString = ` -The FLOW balance of the default vault of this account -` - -const accountTypeAccountAvailableBalanceFieldDocString = ` -The FLOW balance of the default vault of this account that is available to be moved -` - -const accountTypeStorageUsedFieldDocString = ` -The current amount of storage used by the account in bytes -` - -const accountTypeStorageCapacityFieldDocString = ` -The storage capacity of the account in bytes -` - -const accountTypeKeysFieldDocString = ` -The keys associated with the account -` - -const authAccountKeysTypeAddFunctionDocString = ` -Adds the given key to the keys list of the account. -` - -const accountKeysTypeGetFunctionDocString = ` -Retrieves the key at the given index of the account. -` - -const authAccountKeysTypeRevokeFunctionDocString = ` -Revokes the key at the given index of the account. -` -const accountKeysTypeForEachFunctionDocString = ` -Iterates through all the keys of this account, passing each key to the provided function and short-circuiting if the function returns false. - -The order of iteration is undefined. -` - -const accountKeysTypeCountFieldDocString = ` -The number of keys associated with this account. -` - -const authAccountTypeInboxPublishFunctionDocString = ` -Publishes the argument value under the given name, to be later claimed by the specified recipient -` - -var AuthAccountTypeInboxPublishFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: NewTypeAnnotation(&CapabilityType{}), - }, - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation(StringType), - }, - { - Identifier: "recipient", - TypeAnnotation: NewTypeAnnotation(TheAddressType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const authAccountTypeInboxUnpublishFunctionDocString = ` -Unpublishes the value specified by the argument string -` - -var AuthAccountTypeInboxUnpublishFunctionType = func() *FunctionType { - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - }, - } - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "name", - TypeAnnotation: NewTypeAnnotation(StringType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -const authAccountTypeInboxClaimFunctionDocString = ` -Claims the value specified by the argument string from the account specified as the provider -` - -var AuthAccountTypeInboxClaimFunctionType = func() *FunctionType { - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - }, - } - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "name", - TypeAnnotation: NewTypeAnnotation(StringType), - }, - { - Identifier: "provider", - TypeAnnotation: NewTypeAnnotation(TheAddressType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -var AuthAccountInboxTypeName = "Inbox" - -var accountInboxDocString = "an inbox for sending and receiving capabilities" - -// AuthAccountInboxType represents the account's inbox. -var AuthAccountInboxType = func() *CompositeType { - - accountInbox := &CompositeType{ - Identifier: AuthAccountInboxTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxClaimFunctionName, - AuthAccountTypeInboxClaimFunctionType, - authAccountTypeInboxClaimFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxPublishFunctionName, - AuthAccountTypeInboxPublishFunctionType, - authAccountTypeInboxPublishFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxUnpublishFunctionName, - AuthAccountTypeInboxUnpublishFunctionType, - authAccountTypeInboxUnpublishFunctionDocString, - ), - } - - accountInbox.Members = MembersAsMap(members) - accountInbox.Fields = MembersFieldNames(members) - return accountInbox -}() diff --git a/runtime/sema/public_account_contracts.go b/runtime/sema/public_account_contracts.go deleted file mode 100644 index 410a6fcc88..0000000000 --- a/runtime/sema/public_account_contracts.go +++ /dev/null @@ -1,60 +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 sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const PublicAccountContractsTypeName = "Contracts" - -// PublicAccountContractsType represents the type `PublicAccount.Contracts` -var PublicAccountContractsType = func() *CompositeType { - - publicAccountContractsType := &CompositeType{ - Identifier: PublicAccountContractsTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - publicAccountContractsType, - AccountContractsTypeGetFunctionName, - AccountContractsTypeGetFunctionType, - accountContractsTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountContractsType, - AccountContractsTypeBorrowFunctionName, - AccountContractsTypeBorrowFunctionType, - accountContractsTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountContractsType, - AccountContractsTypeNamesFieldName, - accountContractsTypeNamesFieldType, - accountContractsTypeNamesFieldDocString, - ), - } - - publicAccountContractsType.Members = MembersAsMap(members) - publicAccountContractsType.Fields = MembersFieldNames(members) - return publicAccountContractsType -}() diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc new file mode 100644 index 0000000000..940b88b5d4 --- /dev/null +++ b/runtime/sema/publicaccount.cdc @@ -0,0 +1,82 @@ + +pub struct PublicAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: PublicAccount.Contracts + + /// The keys assigned to the account. + pub let keys: PublicAccount.Keys + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// Returns the capability at the given public path. + pub fun getCapability(_ path: PublicPath): Capability + + /// 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? + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } + + pub struct Keys { + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: ((AccountKey): Bool)) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } +} diff --git a/runtime/sema/publicaccount.cdc.go b/runtime/sema/publicaccount.cdc.go new file mode 100644 index 0000000000..e774d2d294 --- /dev/null +++ b/runtime/sema/publicaccount.cdc.go @@ -0,0 +1,21 @@ +/* + * 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 publicaccount.cdc publicaccount.gen.go diff --git a/runtime/sema/publicaccount.gen.go b/runtime/sema/publicaccount.gen.go new file mode 100644 index 0000000000..a06fc53e78 --- /dev/null +++ b/runtime/sema/publicaccount.gen.go @@ -0,0 +1,481 @@ +// Code generated from publicaccount.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/common" + +const PublicAccountTypeAddressFieldName = "address" + +var PublicAccountTypeAddressFieldType = TheAddressType + +const PublicAccountTypeAddressFieldDocString = ` +The address of the account. +` + +const PublicAccountTypeBalanceFieldName = "balance" + +var PublicAccountTypeBalanceFieldType = UFix64Type + +const PublicAccountTypeBalanceFieldDocString = ` +The FLOW balance of the default vault of this account. +` + +const PublicAccountTypeAvailableBalanceFieldName = "availableBalance" + +var PublicAccountTypeAvailableBalanceFieldType = UFix64Type + +const PublicAccountTypeAvailableBalanceFieldDocString = ` +The FLOW balance of the default vault of this account that is available to be moved. +` + +const PublicAccountTypeStorageUsedFieldName = "storageUsed" + +var PublicAccountTypeStorageUsedFieldType = UInt64Type + +const PublicAccountTypeStorageUsedFieldDocString = ` +The current amount of storage used by the account in bytes. +` + +const PublicAccountTypeStorageCapacityFieldName = "storageCapacity" + +var PublicAccountTypeStorageCapacityFieldType = UInt64Type + +const PublicAccountTypeStorageCapacityFieldDocString = ` +The storage capacity of the account in bytes. +` + +const PublicAccountTypeContractsFieldName = "contracts" + +var PublicAccountTypeContractsFieldType = PublicAccountContractsType + +const PublicAccountTypeContractsFieldDocString = ` +The contracts deployed to the account. +` + +const PublicAccountTypeKeysFieldName = "keys" + +var PublicAccountTypeKeysFieldType = PublicAccountKeysType + +const PublicAccountTypeKeysFieldDocString = ` +The keys assigned to the account. +` + +const PublicAccountTypePublicPathsFieldName = "publicPaths" + +var PublicAccountTypePublicPathsFieldType = &VariableSizedType{ + Type: PublicPathType, +} + +const PublicAccountTypePublicPathsFieldDocString = ` +All public paths of this account. +` + +const PublicAccountTypeGetCapabilityFunctionName = "getCapability" + +var PublicAccountTypeGetCapabilityFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountTypeGetCapabilityFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountTypeGetCapabilityFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: PublicAccountTypeGetCapabilityFunctionTypeParameterT, + }, + ), + ), +} + +const PublicAccountTypeGetCapabilityFunctionDocString = ` +Returns the capability at the given public path. +` + +const PublicAccountTypeGetLinkTargetFunctionName = "getLinkTarget" + +var PublicAccountTypeGetLinkTargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: PathType, + }, + ), +} + +const PublicAccountTypeGetLinkTargetFunctionDocString = ` +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. +` + +const PublicAccountTypeForEachPublicFunctionName = "forEachPublic" + +var PublicAccountTypeForEachPublicFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const PublicAccountTypeForEachPublicFunctionDocString = ` +Iterate over all the public paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const PublicAccountContractsTypeNamesFieldName = "names" + +var PublicAccountContractsTypeNamesFieldType = &VariableSizedType{ + Type: StringType, +} + +const PublicAccountContractsTypeNamesFieldDocString = ` +The names of all contracts deployed in the account. +` + +const PublicAccountContractsTypeGetFunctionName = "get" + +var PublicAccountContractsTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const PublicAccountContractsTypeGetFunctionDocString = ` +Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const PublicAccountContractsTypeBorrowFunctionName = "borrow" + +var PublicAccountContractsTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountContractsTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountContractsTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: PublicAccountContractsTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const PublicAccountContractsTypeBorrowFunctionDocString = ` +Returns a reference of the given type to the contract with the given name in the account, if any. + +Returns nil if no contract with the given name exists in the account, +or if the contract does not conform to the given type. +` + +const PublicAccountContractsTypeName = "Contracts" + +var PublicAccountContractsType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountContractsTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + PublicAccountContractsType, + PublicAccountContractsTypeNamesFieldName, + PublicAccountContractsTypeNamesFieldType, + PublicAccountContractsTypeNamesFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountContractsType, + PublicAccountContractsTypeGetFunctionName, + PublicAccountContractsTypeGetFunctionType, + PublicAccountContractsTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountContractsType, + PublicAccountContractsTypeBorrowFunctionName, + PublicAccountContractsTypeBorrowFunctionType, + PublicAccountContractsTypeBorrowFunctionDocString, + ), + } + + PublicAccountContractsType.Members = MembersAsMap(members) + PublicAccountContractsType.Fields = MembersFieldNames(members) +} + +const PublicAccountKeysTypeGetFunctionName = "get" + +var PublicAccountKeysTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const PublicAccountKeysTypeGetFunctionDocString = ` +Returns the key at the given index, if it exists, or nil otherwise. + +Revoked keys are always returned, but they have ` + "`isRevoked`" + ` field set to true. +` + +const PublicAccountKeysTypeForEachFunctionName = "forEach" + +var PublicAccountKeysTypeForEachFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(AccountKeyType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const PublicAccountKeysTypeForEachFunctionDocString = ` +Iterate over all unrevoked keys in this account, +passing each key in turn to the provided function. + +Iteration is stopped early if the function returns ` + "`false`" + `. +The order of iteration is undefined. +` + +const PublicAccountKeysTypeCountFieldName = "count" + +var PublicAccountKeysTypeCountFieldType = UInt64Type + +const PublicAccountKeysTypeCountFieldDocString = ` +The total number of unrevoked keys in this account. +` + +const PublicAccountKeysTypeName = "Keys" + +var PublicAccountKeysType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountKeysTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + PublicAccountKeysType, + PublicAccountKeysTypeGetFunctionName, + PublicAccountKeysTypeGetFunctionType, + PublicAccountKeysTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountKeysType, + PublicAccountKeysTypeForEachFunctionName, + PublicAccountKeysTypeForEachFunctionType, + PublicAccountKeysTypeForEachFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountKeysType, + PublicAccountKeysTypeCountFieldName, + PublicAccountKeysTypeCountFieldType, + PublicAccountKeysTypeCountFieldDocString, + ), + } + + PublicAccountKeysType.Members = MembersAsMap(members) + PublicAccountKeysType.Fields = MembersFieldNames(members) +} + +const PublicAccountTypeName = "PublicAccount" + +var PublicAccountType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) + t.SetNestedType(PublicAccountKeysTypeName, PublicAccountKeysType) + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeAddressFieldName, + PublicAccountTypeAddressFieldType, + PublicAccountTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeBalanceFieldName, + PublicAccountTypeBalanceFieldType, + PublicAccountTypeBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeAvailableBalanceFieldName, + PublicAccountTypeAvailableBalanceFieldType, + PublicAccountTypeAvailableBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeStorageUsedFieldName, + PublicAccountTypeStorageUsedFieldType, + PublicAccountTypeStorageUsedFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeStorageCapacityFieldName, + PublicAccountTypeStorageCapacityFieldType, + PublicAccountTypeStorageCapacityFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeContractsFieldName, + PublicAccountTypeContractsFieldType, + PublicAccountTypeContractsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeKeysFieldName, + PublicAccountTypeKeysFieldType, + PublicAccountTypeKeysFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypePublicPathsFieldName, + PublicAccountTypePublicPathsFieldType, + PublicAccountTypePublicPathsFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeGetCapabilityFunctionName, + PublicAccountTypeGetCapabilityFunctionType, + PublicAccountTypeGetCapabilityFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeGetLinkTargetFunctionName, + PublicAccountTypeGetLinkTargetFunctionType, + PublicAccountTypeGetLinkTargetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeForEachPublicFunctionName, + PublicAccountTypeForEachPublicFunctionType, + PublicAccountTypeForEachPublicFunctionDocString, + ), + } + + PublicAccountType.Members = MembersAsMap(members) + PublicAccountType.Fields = MembersFieldNames(members) +} diff --git a/runtime/sema/publicaccount_type.go b/runtime/sema/publicaccount_type.go deleted file mode 100644 index fcdea49842..0000000000 --- a/runtime/sema/publicaccount_type.go +++ /dev/null @@ -1,240 +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 sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const PublicAccountTypeName = "PublicAccount" -const PublicAccountTypeAddressFieldName = "address" -const PublicAccountTypeBalanceFieldName = "balance" -const PublicAccountTypeAvailableBalanceFieldName = "availableBalance" -const PublicAccountTypeStorageUsedFieldName = "storageUsed" -const PublicAccountTypeStorageCapacityFieldName = "storageCapacity" -const PublicAccountTypeGetCapabilityFieldName = "getCapability" -const PublicAccountTypeGetTargetLinkFieldName = "getLinkTarget" -const PublicAccountTypeForEachPublicFieldName = "forEachPublic" -const PublicAccountTypeKeysFieldName = "keys" -const PublicAccountTypeContractsFieldName = "contracts" -const PublicAccountTypePathsFieldName = "publicPaths" - -// PublicAccountType represents the publicly accessible portion of an account. -var PublicAccountType = func() *CompositeType { - - publicAccountType := &CompositeType{ - Identifier: PublicAccountTypeName, - Kind: common.CompositeKindStructure, - hasComputedMembers: true, - importable: false, - } - - publicAccountType.SetNestedType(AccountKeysTypeName, PublicAccountKeysType) - publicAccountType.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) - - var members = []*Member{ - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeAddressFieldName, - TheAddressType, - accountTypeAddressFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeBalanceFieldName, - UFix64Type, - accountTypeAccountBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeAvailableBalanceFieldName, - UFix64Type, - accountTypeAccountAvailableBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeStorageUsedFieldName, - UInt64Type, - accountTypeStorageUsedFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeStorageCapacityFieldName, - UInt64Type, - accountTypeStorageCapacityFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeGetCapabilityFieldName, - PublicAccountTypeGetCapabilityFunctionType, - publicAccountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeGetTargetLinkFieldName, - AccountTypeGetLinkTargetFunctionType, - accountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeKeysFieldName, - PublicAccountKeysType, - accountTypeKeysFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeContractsFieldName, - PublicAccountContractsType, - accountTypeContractsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypePathsFieldName, - PublicAccountPathsType, - publicAccountTypePathsFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeForEachPublicFieldName, - PublicAccountForEachPublicFunctionType, - publicAccountForEachPublicDocString, - ), - } - - publicAccountType.Members = MembersAsMap(members) - publicAccountType.Fields = MembersFieldNames(members) - return publicAccountType -}() - -var PublicAccountPathsType = &VariableSizedType{ - Type: PublicPathType, -} - -const publicAccountTypePathsFieldDocString = ` -All the public paths of an account -` - -func AccountForEachFunctionType(pathType Type) *FunctionType { - iterFunctionType := &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(pathType), - }, - { - Label: ArgumentLabelNotRequired, - Identifier: "type", - TypeAnnotation: NewTypeAnnotation(MetaType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(BoolType), - } - return &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "function", - TypeAnnotation: NewTypeAnnotation(iterFunctionType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), - } -} - -const publicAccountForEachPublicDocString = ` -Iterate over all the public paths in an account. - -Takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object - -The returned boolean of the supplied function indicates whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. -` - -var PublicAccountForEachPublicFunctionType = AccountForEachFunctionType(PublicPathType) - -// PublicAccountKeysType represents the keys associated with a public account. -var PublicAccountKeysType = func() *CompositeType { - - accountKeys := &CompositeType{ - Identifier: AccountKeysTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeGetFunctionName, - AccountKeysTypeGetFunctionType, - accountKeysTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeForEachFunctionName, - AccountKeysTypeForEachFunctionType, - accountKeysTypeForEachFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - accountKeys, - AccountKeysTypeCountFieldName, - AccountKeysTypeCountFieldType, - accountKeysTypeCountFieldDocString, - ), - } - - accountKeys.Members = MembersAsMap(members) - accountKeys.Fields = MembersFieldNames(members) - return accountKeys -}() - -var PublicAccountTypeGetCapabilityFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - Optional: true, - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: NewTypeAnnotation(PublicPathType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const publicAccountTypeGetLinkTargetFunctionDocString = ` -Returns the capability at the given public path, or nil if it does not exist -` diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index f3abb9e74b..a99356b977 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -287,6 +287,7 @@ func newAuthAccountKeysValue( addressValue, ), newAccountKeysGetFunction( + sema.AuthAccountKeysTypeGetFunctionType, gauge, handler, addressValue, @@ -296,7 +297,12 @@ func newAuthAccountKeysValue( handler, addressValue, ), - newAccountKeysForEachFunction(gauge, handler, addressValue), + newAccountKeysForEachFunction( + sema.AuthAccountKeysTypeForEachFunctionType, + gauge, + handler, + addressValue, + ), newAccountKeysCountGetter(gauge, handler, addressValue), ) } @@ -640,6 +646,7 @@ type AccountKeyProvider interface { } func newAccountKeysGetFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountKeyProvider, addressValue interpreter.AddressValue, @@ -650,7 +657,7 @@ func newAccountKeysGetFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountKeysTypeGetFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { indexValue, ok := invocation.Arguments[0].(interpreter.IntValue) if !ok { @@ -697,6 +704,7 @@ func newAccountKeysGetFunction( var accountKeysForEachCallbackTypeParams = []sema.Type{sema.AccountKeyType} func newAccountKeysForEachFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountKeyProvider, addressValue interpreter.AddressValue, @@ -705,7 +713,7 @@ func newAccountKeysForEachFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountKeysTypeForEachFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { fnValue, ok := invocation.Arguments[0].(interpreter.FunctionValue) @@ -905,11 +913,13 @@ func newPublicAccountKeysValue( gauge, addressValue, newAccountKeysGetFunction( + sema.PublicAccountKeysTypeGetFunctionType, gauge, handler, addressValue, ), newAccountKeysForEachFunction( + sema.PublicAccountKeysTypeForEachFunctionType, gauge, handler, addressValue, @@ -962,7 +972,7 @@ func accountInboxPublishFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { value, ok := invocation.Arguments[0].(*interpreter.StorageCapabilityValue) if !ok { @@ -1017,7 +1027,7 @@ func accountInboxUnpublishFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { @@ -1083,7 +1093,7 @@ func accountInboxClaimFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { diff --git a/runtime/stdlib/flow.go b/runtime/stdlib/flow.go index 15d431b93b..040e9e1629 100644 --- a/runtime/stdlib/flow.go +++ b/runtime/stdlib/flow.go @@ -279,9 +279,7 @@ var AccountLinkedEventType = newFlowEventType( "AccountLinked", AccountEventAddressParameter, sema.Parameter{ - Identifier: "path", - TypeAnnotation: sema.NewTypeAnnotation( - sema.AuthAccountTypeLinkAccountFunctionTypePathParameterType, - ), + Identifier: "path", + TypeAnnotation: sema.AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation, }, ) From 6fa530eaa3b9e6b773507af04e1d5d36a7c515e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 14:44:31 -0700 Subject: [PATCH 105/173] update documentation --- docs/language/accounts.mdx | 569 +++++++++++++++++++++++++------------ 1 file changed, 385 insertions(+), 184 deletions(-) diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index 5e5ad01a98..077d01b589 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -10,57 +10,85 @@ Every account can be accessed through two types, `PublicAccount` and `AuthAccoun which represents the publicly available portion of an account. ```cadence -struct PublicAccount { +pub struct PublicAccount { - let address: Address - // The FLOW balance of the default vault of this account - let balance: UFix64 - // The FLOW balance of the default vault of this account that is available to be moved - let availableBalance: UFix64 - // Amount of storage used by the account, in bytes - let storageUsed: UInt64 - // storage capacity of the account, in bytes - let storageCapacity: UInt64 + /// The address of the account. + pub let address: Address - // Contracts deployed to the account - let contracts: PublicAccount.Contracts + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 - // Keys assigned to the account - let keys: PublicAccount.Keys + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 - // The public paths associated with this account - let publicPaths: [PublicPath] + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 - // Storage operations + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 - fun getCapability(_ path: PublicPath): Capability - fun getLinkTarget(_ path: CapabilityPath): Path? + /// The contracts deployed to the account. + pub let contracts: PublicAccount.Contracts - // Storage iteration - fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + /// The keys assigned to the account. + pub let keys: PublicAccount.Keys - struct Contracts { + /// All public paths of this account. + pub let publicPaths: [PublicPath] - let names: [String] + /// Returns the capability at the given public path. + pub fun getCapability(_ path: PublicPath): Capability - fun get(name: String): DeployedContract? + /// 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? - fun borrow(name: String): T? + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? } - struct Keys { - // Returns the key at the given index, if it exists. - // Revoked keys are always returned, but they have \`isRevoked\` field set to true. - fun get(keyIndex: Int): AccountKey? + pub struct Keys { + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? - // Iterate over all unrevoked keys in this account, - // passing each key in turn to the provided function. - // Iteration is stopped early if the function returns `false`. - // The order of iteration is undefined. - fun forEach(function: ((AccountKey): Bool)): Void + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: ((AccountKey): Bool)) - // The total number of unrevoked keys in this account. - let count: UInt64 + /// The total number of unrevoked keys in this account. + pub let count: UInt64 } } ``` @@ -84,160 +112,333 @@ Only [signed transactions](transactions) can get the `AuthAccount` for an accoun For each signer of the transaction that signs as an authorizer, the corresponding `AuthAccount` object is passed to the `prepare` phase of the transaction. - ```cadence - struct AuthAccount { - - let address: Address - // The FLOW balance of the default vault of this account - let balance: UFix64 - // The FLOW balance of the default vault of this account that is available to be moved - let availableBalance: UFix64 - // Amount of storage used by the account, in bytes - let storageUsed: UInt64 - // storage capacity of the account, in bytes - let storageCapacity: UInt64 - - // Contracts deployed to the account - - let contracts: AuthAccount.Contracts - - // Keys assigned to the account - - let keys: AuthAccount.Keys - - // Provides a API for bootstrapping (sending and receiving) capabilities - - let inbox: AuthAccount.Inbox - - // All the paths associated with this account - let publicPaths: [PublicPath] - let privatePaths: [PrivatePath] - let storagePaths: [StoragePath] - - // Key management - - // Adds a public key to the account. - // The public key must be encoded together with their signature algorithm, hashing algorithm and weight. - // This method is currently deprecated and is available only for the backward compatibility. - // `keys.add` method can be use instead. - fun addPublicKey(_ publicKey: [UInt8]) - - // Revokes the key at the given index. - // This method is currently deprecated and is available only for the backward compatibility. - // `keys.revoke` method can be use instead. - fun removePublicKey(_ index: Int) - - // Account storage API (see the section below for documentation) - - fun save(_ value: T, to: StoragePath) - fun type(at path: StoragePath): Type? - fun load(from: StoragePath): T? - fun copy(from: StoragePath): T? - - fun borrow(from: StoragePath): T? - - fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? - fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>? - fun getCapability(_ path: CapabilityPath): Capability - fun getLinkTarget(_ path: CapabilityPath): Path? - fun unlink(_ path: CapabilityPath) - - // Storage iteration - fun forEachPublic(_ function: ((PublicPath, Type): Bool)) - fun forEachPrivate(_ function: ((PrivatePath, Type): Bool)) - fun forEachStored(_ function: ((StoragePath, Type): Bool)) - - struct Contracts { - - // The names of each contract deployed to the account - let names: [String] - - fun add( - name: String, - code: [UInt8], - ... contractInitializerArguments - ): DeployedContract - - fun update__experimental(name: String, code: [UInt8]): DeployedContract - - fun get(name: String): DeployedContract? - - fun remove(name: String): DeployedContract? - - fun borrow(name: String): T? - } - - struct Keys { - // Adds a new key with the given hashing algorithm and a weight, and returns the added key. - fun add( - publicKey: PublicKey, - hashAlgorithm: HashAlgorithm, - weight: UFix64 - ): AccountKey - - // Returns the key at the given index, if it exists, or nil otherwise. - // Revoked keys are always returned, but they have `isRevoked` field set to true. - fun get(keyIndex: Int): AccountKey? - - // Marks the key at the given index revoked, but does not delete it. - // Returns the revoked key if it exists, or nil otherwise. - fun revoke(keyIndex: Int): AccountKey? - - // Iterate over all unrevoked keys in this account, - // passing each key in turn to the provided function. - // Iteration is stopped early if the function returns `false`. - // The order of iteration is undefined. - fun forEach(function: ((AccountKey): Bool)): Void - - // The total number of unrevoked keys in this account. - let count: UInt64 - } - - struct Inbox { - // Publishes a new Capability under the given name, to be claimed by the specified recipient - fun publish(_ value: Capability, name: String, recipient: Address) +```cadence +pub struct AuthAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: AuthAccount.Contracts + + /// The keys assigned to the account. + pub let keys: AuthAccount.Keys + + /// The inbox allows bootstrapping (sending and receiving) capabilities. + pub let inbox: AuthAccount.Inbox + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// All private paths of this account. + pub let privatePaths: [PrivatePath] + + /// All storage paths of this account. + pub let storagePaths: [StoragePath] + + /// **DEPRECATED**: Use `keys.add` instead. + /// + /// Adds a public key to the account. + /// + /// The public key must be encoded together with their signature algorithm, hashing algorithm and weight. + pub fun addPublicKey(_ publicKey: [UInt8]) + + /// **DEPRECATED**: Use `keys.revoke` instead. + /// + /// Revokes the key at the given index. + pub fun removePublicKey(_ index: Int) + + /// Saves the given object into the account's storage at the given path. + /// + /// Resources are moved into storage, and structures are copied. + /// + /// If there is already an object stored under the given path, the program aborts. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun save(_ value: T, to: StoragePath) + + /// Reads the type of an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, the type of the object is returned without modifying the stored object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun type(at path: StoragePath): Type? + + /// Loads an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, + /// the stored resource or structure is moved out of storage and returned as an optional. + /// + /// When the function returns, the storage no longer contains an object under the given path. + /// + /// The given type must be a supertype of the type of the loaded object. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the loaded object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun load(from: StoragePath): T? + + /// Returns a copy of a structure stored in account storage under the given path, + /// without removing it from storage, + /// or nil if no object is stored under the given path. + /// + /// If there is a structure stored, it is copied. + /// The structure stays stored in storage after the function returns. + /// + /// The given type must be a supertype of the type of the copied structure. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the copied structure. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun copy(from: StoragePath): T? + + /// Returns a reference to an object in storage without removing it from storage. + /// + /// If no object is stored under the given path, the function returns nil. + /// If there is an object stored, a reference is returned as an optional, + /// provided it can be borrowed using the given type. + /// If the stored object cannot be borrowed using the given type, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the borrowed object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed + pub fun borrow(from: StoragePath): T? + + /// Creates a capability at the given public or private path, + /// which targets the given public, private, or storage path. + /// + /// The target path leads to the object that will provide the functionality defined by this capability. + /// + /// The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + /// + /// It is not necessary for the target path to lead to a valid object; the target path could be empty, + /// or could lead to an object which does not provide the necessary type interface: + /// 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. + /// + /// The link is latent. + /// + /// The target value might be stored after the link is created, + /// and the target value might be moved out after the link has been created. + pub fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + + /// 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>? + + /// Returns the capability at the given private or public path. + pub fun getCapability(_ path: CapabilityPath): Capability + + /// 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? + + /// Removes the capability at the given public or private path. + pub fun unlink(_ path: CapabilityPath) + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + + /// Iterate over all the private paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPrivate(_ function: ((PrivatePath, Type): Bool)) + + /// Iterate over all the stored paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachStored(_ function: ((StoragePath, Type): Bool)) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Adds the given contract to the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// All additional arguments that are given are passed further to the initializer + /// of the contract that is being deployed. + /// + /// The function fails if a contract/contract interface with the given name already exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract. + pub fun add( + name: String, + code: [UInt8] + ): DeployedContract + + /// **Experimental** + /// + /// Updates the code for the contract/contract interface in the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// Does **not** run the initializer of the contract/contract interface again. + /// The contract instance in the world state stays as is. + /// + /// Fails if no contract/contract interface with the given name exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract for the updated contract. + pub fun update__experimental(name: String, code: [UInt8]): DeployedContract + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Removes the contract/contract interface from the account which has the given name, if any. + /// + /// Returns the removed deployed contract, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun remove(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } - // Unpublishes a Capability previously published by this account. - // Returns `nil` if no Capability is published under the given name. - // Errors if the Capability under that name does not match the provided type. - fun unpublish(_ name: String): Capability? + pub struct Keys { + + /// Adds a new key with the given hashing algorithm and a weight. + /// + /// Returns the added key. + pub fun add( + publicKey: PublicKey, + hashAlgorithm: HashAlgorithm, + weight: UFix64 + ): AccountKey + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Marks the key at the given index revoked, but does not delete it. + /// + /// Returns the revoked key if it exists, or nil otherwise. + pub fun revoke(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: ((AccountKey): Bool)) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } - // Claims a Capability previously published by the specified provider. - // Returns `nil` if no Capability is published under the given name, or if this account is not its intended recipient. - // Errors if the Capability under that name does not match the provided type. - fun claim(_ name: String, provider: Address): Capability? + pub struct Inbox { + + /// Publishes a new Capability under the given name, + /// to be claimed by the specified recipient. + pub fun publish(_ value: Capability, name: String, recipient: Address) + + /// Unpublishes a Capability previously published by this account. + /// + /// Returns `nil` if no Capability is published under the given name. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun unpublish(_ name: String): Capability? + + /// Claims a Capability previously published by the specified provider. + /// + /// Returns `nil` if no Capability is published under the given name, + /// or if this account is not its intended recipient. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun claim(_ name: String, provider: Address): Capability? } - } - - struct DeployedContract { - let name: String - let code: [UInt8] - - // Returns an array of `Type` objects representing all the public type declarations in this contract (e.g. structs, resources, enums) - // - // For example, given a contract - // ``` - // contract Foo { - // pub struct Bar {...} - // pub resource Qux {...} - // } - // ``` - // then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` - - fun publicTypes(): [Type] - } - ``` +} - A script can get the `AuthAccount` for an account address using the built-in `getAuthAccount` function: +pub struct DeployedContract { + pub let name: String + pub let code: [UInt8] + + // Returns an array of `Type` objects representing all the public type declarations in this contract (e.g. structs, resources, enums) + // + // For example, given a contract + // ``` + // contract Foo { + // pub struct Bar {...} + // pub resource Qux {...} + // } + // ``` + // then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + + pub fun publicTypes(): [Type] +} +``` - ```cadence - fun getAuthAccount(_ address: Address): AuthAccount - ``` +A script can get the `AuthAccount` for an account address using the built-in `getAuthAccount` function: + +```cadence +fun getAuthAccount(_ address: Address): AuthAccount +``` - This `AuthAccount` object can perform all operations associated with authorized accounts, - and as such this function is only available in scripts, - which discard their changes upon completion. - Attempting to use this function outside of a script will cause a type error. +This `AuthAccount` object can perform all operations associated with authorized accounts, +and as such this function is only available in scripts, +which discard their changes upon completion. +Attempting to use this function outside of a script will cause a type error. ## Account Creation From 9f7dfedd2b128a694263245668aa6b7004e36616 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:26:39 -0500 Subject: [PATCH 106/173] Support FunctionType.TypeParameters in CCF codec Cadence recently added FunctionType.TypeParameters, so add support for it in CCF codec. --- encoding/ccf/ccf_test.go | 41 +++++++++------- encoding/ccf/decode.go | 103 ++++++++++++++++++++++++++++++++++++--- encoding/ccf/encode.go | 95 ++++++++++++++++++++++++++++++------ 3 files changed, 202 insertions(+), 37 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 955b713dc6..2aa51dec4e 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -8194,10 +8194,13 @@ func TestEncodeType(t *testing.T) { t.Run("with static function", func(t *testing.T) { - testEncodeAndDecodeEx( + testEncodeAndDecode( t, cadence.TypeValue{ StaticType: (&cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.AnyStructType{}}, + }, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, @@ -8205,10 +8208,10 @@ func TestEncodeType(t *testing.T) { }), }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType": { "kind" : "Function", "typeID":"((String):Int)", "return" : {"kind" : "Int"}, "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} } } + // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":{"kind":"AnyStruct"}}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 193([[["qux", "baz", 185(1)]], 185(4)])]) + // 130([137(41), 193([[["T", 185(39)]], [["qux", "baz", 185(1)]], 185(4)])]) // // language=cbor, format=ccf // tag @@ -8221,8 +8224,20 @@ func TestEncodeType(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, + // array, 1 elements follow + 0x81, // array, 2 elements follow 0x82, + // string, 1 byte follows + 0x61, + // "T" + 0x54, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type (39) + 0x18, 0x27, // array, 1 elements follow 0x81, // array, 3 elements follow @@ -8244,17 +8259,7 @@ func TestEncodeType(t *testing.T) { // Int type ID (4) 0x04, }, - // Expected decoded FunctionType doesn't have type ID. - cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ - Parameters: []cadence.Parameter{ - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, - ReturnType: cadence.IntType{}, - }), - }, ) - }) t.Run("with static Capability", func(t *testing.T) { @@ -10268,10 +10273,10 @@ func TestExportFunctionValue(t *testing.T) { }), }, []byte{ // language=json, format=json-cdc - // { "type": "Function", "value": { "functionType": { "kind": "Function", "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" } } } } + // {"value":{"functionType":{"kind":"Function","typeParameters":[],"parameters":[],"return":{"kind":"Void"}}},"type":"Function"} // // language=edn, format=ccf - // 130([137(51), [[], 185(50)]]) + // 130([137(51), [[], [], 185(50)]]) // // language=cbor, format=ccf // tag @@ -10282,8 +10287,10 @@ func TestExportFunctionValue(t *testing.T) { 0xd8, ccf.CBORTagSimpleType, // Function type ID (51) 0x18, 0x33, - // array, 2 elements follow - 0x82, + // array, 3 elements follow + 0x83, + // element 0: type parameters + 0x80, // element 1: parameters // array, 0 element 0x80, diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index fdf2c246d5..853c55b2ae 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1741,6 +1741,86 @@ func (d *Decoder) decodeInitializerTypeValues(visited *cadenceTypeByCCFTypeID) ( return initializerTypes, nil } +// decodeTypeParameterTypeValues decodes type parameters as +// language=CDDL +// +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value +// ] +// ] +func (d *Decoder) decodeTypeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([]cadence.TypeParameter, error) { + // Decode number of parameters. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count == 0 { + return []cadence.TypeParameter{}, nil + } + + typeParameterTypes := make([]cadence.TypeParameter, count) + typeParameterNames := make(map[string]struct{}, count) + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceTypeParameter, + Amount: count, + }) + + for i := 0; i < int(count); i++ { + // Decode type parameter. + typeParam, err := d.decodeTypeParameterTypeValue(visited) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "All parameter lists MUST have unique identifier" + if _, ok := typeParameterNames[typeParam.Name]; ok { + return nil, fmt.Errorf("found duplicate type parameter name %s", typeParam.Name) + } + + typeParameterNames[typeParam.Name] = struct{}{} + typeParameterTypes[i] = typeParam + } + + return typeParameterTypes, nil +} + +// decodeTypeParameterTypeValue decodes type parameter as +// language=CDDL +// +// [ +// name: tstr, +// type-bound: type-value +// ] +func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.TypeParameter, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return cadence.TypeParameter{}, err + } + + // element 0: name + name, err := d.dec.DecodeString() + if err != nil { + return cadence.TypeParameter{}, err + } + + // element 2: type + t, err := d._decodeTypeValue(visited) + if err != nil { + return cadence.TypeParameter{}, err + } + + // Unmetered because decodeTypeParamTypeValue is metered in decodeTypeParamTypeValues and called nowhere else + // Type is metered. + return cadence.NewTypeParameter(name, t), nil +} + // decodeParameterTypeValues decodes composite initializer parameter types as // language=CDDL // @@ -1844,6 +1924,12 @@ func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cad // language=CDDL // function-value = [ // +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value +// ] +// ] // parameters: [ // * [ // label: tstr, @@ -1854,21 +1940,26 @@ func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cad // return-type: type-value // // ] -// TODO: handle function type's type parameters func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { - // Decode array head of length 2 - err := decodeCBORArrayWithKnownSize(d.dec, 2) + // Decode array head of length 3 + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return nil, err + } + + // element 0: type parameters + typeParameters, err := d.decodeTypeParameterTypeValues(visited) if err != nil { return nil, err } - // element 0: parameters + // element 1: parameters parameters, err := d.decodeParameterTypeValues(visited) if err != nil { return nil, err } - // element 1: return-type + // element 2: return-type returnType, err := d._decodeTypeValue(visited) if err != nil { return nil, err @@ -1880,7 +1971,7 @@ func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cade return cadence.NewMeteredFunctionType( d.gauge, - nil, + typeParameters, parameters, returnType, ), nil diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 26b180e770..2b12b2e73b 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -998,34 +998,45 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { // language=CDDL // function-value = [ // -// parameters: [ -// * [ -// label: tstr, -// identifier: tstr, -// type: type-value -// ] -// ] -// return-type: type-value +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value +// ] +// ] +// parameters: [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// return-type: type-value // // ] -// TODO: handle function type's type parameters func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { - // Encode array head of length 2. + // Encode array head of length 3. err := e.enc.EncodeRawBytes([]byte{ - // array, 2 items follow - 0x82, + // array, 3 items follow + 0x83, }) if err != nil { return err } - // element 0: parameters as array. + // element 0: type parameters as array. + err = e.encodeTypeParameterTypeValues(typ.TypeParameters, visited) + if err != nil { + return err + } + + // element 1: parameters as array. err = e.encodeParameterTypeValues(typ.Parameters, visited) if err != nil { return err } - // element 1: return type as type-value. + // element 2: return type as type-value. return e.encodeTypeValue(typ.ReturnType, visited) } @@ -1621,6 +1632,62 @@ func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Param return nil } +// encodeTypeParameterTypeValues encodes type parameters as +// language=CDDL +// +// type-parameters: [ +// +// *[ +// name: tstr, +// type-bound: type-value +// ] +// +// ] +func (e *Encoder) encodeTypeParameterTypeValues(typeParameters []cadence.TypeParameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of type parameters. + err := e.enc.EncodeArrayHead(uint64(len(typeParameters))) + if err != nil { + return err + } + + // Encode type parameters. + for _, param := range typeParameters { + err = e.encodeTypeParameterTypeValue(param, visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeTypeParameterTypeValue encodes type parameter as +// language=CDDL +// +// [ +// name: tstr, +// type-bound: type-value +// ] +func (e *Encoder) encodeTypeParameterTypeValue(typeParameter cadence.TypeParameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 2 + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: name as tstr. + err = e.enc.EncodeString(typeParameter.Name) + if err != nil { + return err + } + + // element 1: type as type-bound. + return e.encodeTypeValue(typeParameter.TypeBound, visited) +} + // encodeParameterTypeValues encodes composite initializer parameter types as // language=CDDL // From 15227095c19ff28594731d0134fd9489e3f361d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 15:47:17 -0700 Subject: [PATCH 107/173] generate simple type members in init function to allow for cycles --- runtime/sema/gen/main.go | 148 ++++++++++-------- .../sema/gen/testdata/docstrings.golden.go | 7 +- runtime/sema/gen/testdata/fields.golden.go | 7 +- runtime/sema/gen/testdata/functions.golden.go | 7 +- 4 files changed, 102 insertions(+), 67 deletions(-) diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index de3505412a..d63a622061 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -437,69 +437,104 @@ func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ memberDeclarations := typeDecl.memberDeclarations - if !canGenerateSimpleType && - len(memberDeclarations) > 0 { - - // func init() { - // members := []*Member{...} - // t.Members = MembersAsMap(members) - // t.Fields = MembersFieldNames(members) - // } - - members := membersExpr(typeDecl.fullTypeName, tyVarName, memberDeclarations) - - const membersVariableIdentifier = "members" - - g.addDecls( - &dst.FuncDecl{ - Name: dst.NewIdent("init"), - Type: &dst.FuncType{}, - Body: &dst.BlockStmt{ - List: []dst.Stmt{ - &dst.DeclStmt{ - Decl: goVarDecl( - membersVariableIdentifier, - members, - ), - }, - &dst.AssignStmt{ - Lhs: []dst.Expr{ - &dst.SelectorExpr{ - X: dst.NewIdent(tyVarName), - Sel: dst.NewIdent("Members"), - }, - }, - Tok: token.ASSIGN, - Rhs: []dst.Expr{ - &dst.CallExpr{ - Fun: dst.NewIdent("MembersAsMap"), - Args: []dst.Expr{ - dst.NewIdent(membersVariableIdentifier), + if len(memberDeclarations) > 0 { + + if canGenerateSimpleType { + + // func init() { + // t.Members = func(t *SimpleType) map[string]MemberResolver { + // return MembersAsResolvers(...) + // } + // } + + memberResolversFunc := simpleTypeMemberResolversFunc(typeDecl.fullTypeName, memberDeclarations) + + g.addDecls( + &dst.FuncDecl{ + Name: dst.NewIdent("init"), + Type: &dst.FuncType{}, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Members"), }, }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + memberResolversFunc, + }, }, }, - &dst.AssignStmt{ - Lhs: []dst.Expr{ - &dst.SelectorExpr{ - X: dst.NewIdent(tyVarName), - Sel: dst.NewIdent("Fields"), + }, + }, + ) + + } else { + + // func init() { + // members := []*Member{...} + // t.Members = MembersAsMap(members) + // t.Fields = MembersFieldNames(members) + // } + + members := membersExpr(typeDecl.fullTypeName, tyVarName, memberDeclarations) + + const membersVariableIdentifier = "members" + + g.addDecls( + &dst.FuncDecl{ + Name: dst.NewIdent("init"), + Type: &dst.FuncType{}, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + &dst.DeclStmt{ + Decl: goVarDecl( + membersVariableIdentifier, + members, + ), + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Members"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsMap"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, }, }, - Tok: token.ASSIGN, - Rhs: []dst.Expr{ - &dst.CallExpr{ - Fun: dst.NewIdent("MembersFieldNames"), - Args: []dst.Expr{ - dst.NewIdent(membersVariableIdentifier), + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Fields"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersFieldNames"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, }, }, }, }, }, }, - }, - ) + ) + } } return @@ -1039,15 +1074,6 @@ func simpleTypeLiteral(ty *typeDecl) dst.Expr { goKeyValue("Importable", goBoolLit(ty.importable)), } - if len(ty.memberDeclarations) > 0 { - members := simpleTypeMembers(ty.fullTypeName, ty.memberDeclarations) - - elements = append( - elements, - goKeyValue("Members", members), - ) - } - return &dst.UnaryExpr{ Op: token.AND, X: &dst.CompositeLit{ @@ -1057,7 +1083,7 @@ func simpleTypeLiteral(ty *typeDecl) dst.Expr { } } -func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst.Expr { +func simpleTypeMemberResolversFunc(fullTypeName string, declarations []ast.Declaration) dst.Expr { // func(t *SimpleType) map[string]MemberResolver { // return MembersAsResolvers(...) // } diff --git a/runtime/sema/gen/testdata/docstrings.golden.go b/runtime/sema/gen/testdata/docstrings.golden.go index 2a8142da28..254880bd11 100644 --- a/runtime/sema/gen/testdata/docstrings.golden.go +++ b/runtime/sema/gen/testdata/docstrings.golden.go @@ -111,7 +111,10 @@ var DocstringsType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + DocstringsType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicConstantFieldMember( t, @@ -150,5 +153,5 @@ var DocstringsType = &SimpleType{ DocstringsTypeRunningOutOfIdeasFunctionDocString, ), }) - }, + } } diff --git a/runtime/sema/gen/testdata/fields.golden.go b/runtime/sema/gen/testdata/fields.golden.go index f0ba9b5f77..a747746953 100644 --- a/runtime/sema/gen/testdata/fields.golden.go +++ b/runtime/sema/gen/testdata/fields.golden.go @@ -126,7 +126,10 @@ var TestType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + TestType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicConstantFieldMember( t, @@ -189,5 +192,5 @@ var TestType = &SimpleType{ TestTypeTestCapIntFieldDocString, ), }) - }, + } } diff --git a/runtime/sema/gen/testdata/functions.golden.go b/runtime/sema/gen/testdata/functions.golden.go index 20ed76576f..6f8035625c 100644 --- a/runtime/sema/gen/testdata/functions.golden.go +++ b/runtime/sema/gen/testdata/functions.golden.go @@ -169,7 +169,10 @@ var TestType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + TestType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicFunctionMember( t, @@ -214,5 +217,5 @@ var TestType = &SimpleType{ TestTypeTypeParamWithBoundAndParamFunctionDocString, ), }) - }, + } } From 011a1cedca90337960e422aec5fc63853d15e5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 15:48:33 -0700 Subject: [PATCH 108/173] refactor DeployedContract definition to Cadence --- docs/language/accounts.mdx | 18 ---- docs/language/contracts.mdx | 25 +++++- runtime/sema/deployed_contract.go | 120 --------------------------- runtime/sema/deployedcontract.cdc | 24 ++++++ runtime/sema/deployedcontract.gen.go | 115 +++++++++++++++++++++++++ runtime/sema/deployedcontract.go | 21 +++++ 6 files changed, 182 insertions(+), 141 deletions(-) delete mode 100644 runtime/sema/deployed_contract.go create mode 100644 runtime/sema/deployedcontract.cdc create mode 100644 runtime/sema/deployedcontract.gen.go create mode 100644 runtime/sema/deployedcontract.go diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index 5e5ad01a98..93f1ef1fbd 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -208,24 +208,6 @@ to the `prepare` phase of the transaction. fun claim(_ name: String, provider: Address): Capability? } } - - struct DeployedContract { - let name: String - let code: [UInt8] - - // Returns an array of `Type` objects representing all the public type declarations in this contract (e.g. structs, resources, enums) - // - // For example, given a contract - // ``` - // contract Foo { - // pub struct Bar {...} - // pub resource Qux {...} - // } - // ``` - // then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` - - fun publicTypes(): [Type] - } ``` A script can get the `AuthAccount` for an account address using the built-in `getAuthAccount` function: diff --git a/docs/language/contracts.mdx b/docs/language/contracts.mdx index 32977536fe..55cbbc5481 100644 --- a/docs/language/contracts.mdx +++ b/docs/language/contracts.mdx @@ -237,9 +237,28 @@ The deployed contracts of an account can be accessed through the `contracts` obj Accounts store "deployed contracts", that is, the code of the contract: ```cadence -struct DeployedContract { - let name: String - let code: [UInt8] +pub struct DeployedContract { + /// The address of the account where the contract is deployed at. + pub let address: Address + + /// The name of the contract. + pub let name: String + + /// The code of the contract. + pub let code: [UInt8] + + /// Returns an array of `Type` objects representing all the public type declarations in this contract + /// (e.g. structs, resources, enums). + /// + /// For example, given a contract + /// ``` + /// contract Foo { + /// pub struct Bar {...} + /// pub resource Qux {...} + /// } + /// ``` + /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + pub fun publicTypes(): [Type] } ``` diff --git a/runtime/sema/deployed_contract.go b/runtime/sema/deployed_contract.go deleted file mode 100644 index 62f0d71159..0000000000 --- a/runtime/sema/deployed_contract.go +++ /dev/null @@ -1,120 +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 sema - -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - -// DeployedContractType represents the type `DeployedContract` -var DeployedContractType = &SimpleType{ - Name: "DeployedContract", - QualifiedName: "DeployedContract", - TypeID: "DeployedContract", - tag: DeployedContractTypeTag, - IsResource: false, - Storable: false, - Equatable: false, - Exportable: false, - Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - DeployedContractTypeAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TheAddressType, - deployedContractTypeAddressFieldDocString, - ) - }, - }, - DeployedContractTypeNameFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringType, - deployedContractTypeNameFieldDocString, - ) - }, - }, - DeployedContractTypeCodeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - ByteArrayType, - deployedContractTypeCodeFieldDocString, - ) - }, - }, - DeployedContractTypePublicTypesFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, report func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DeployedContractTypePublicTypesFunctionType, - DeployedContractTypePublicTypesFunctionDocString, - ) - }, - }, - } - }, -} - -const DeployedContractTypeAddressFieldName = "address" - -const deployedContractTypeAddressFieldDocString = ` -The address of the account where the contract is deployed at -` - -const DeployedContractTypeNameFieldName = "name" - -const deployedContractTypeNameFieldDocString = ` -The name of the contract -` - -const DeployedContractTypeCodeFieldName = "code" - -const deployedContractTypeCodeFieldDocString = ` -The code of the contract -` - -const DeployedContractTypePublicTypesFunctionName = "publicTypes" -const DeployedContractTypePublicTypesFunctionDocString = ` -Returns an array of Type objects representing all public type declarations in this contract. -` - -var DeployedContractTypePublicTypesFunctionType = &FunctionType{ - ReturnTypeAnnotation: NewTypeAnnotation( - &VariableSizedType{ - Type: MetaType, - }, - ), -} diff --git a/runtime/sema/deployedcontract.cdc b/runtime/sema/deployedcontract.cdc new file mode 100644 index 0000000000..ba9553c197 --- /dev/null +++ b/runtime/sema/deployedcontract.cdc @@ -0,0 +1,24 @@ + +pub struct DeployedContract { + /// The address of the account where the contract is deployed at. + pub let address: Address + + /// The name of the contract. + pub let name: String + + /// The code of the contract. + pub let code: [UInt8] + + /// Returns an array of `Type` objects representing all the public type declarations in this contract + /// (e.g. structs, resources, enums). + /// + /// For example, given a contract + /// ``` + /// contract Foo { + /// pub struct Bar {...} + /// pub resource Qux {...} + /// } + /// ``` + /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + pub fun publicTypes(): [Type] +} diff --git a/runtime/sema/deployedcontract.gen.go b/runtime/sema/deployedcontract.gen.go new file mode 100644 index 0000000000..24ea7d79b9 --- /dev/null +++ b/runtime/sema/deployedcontract.gen.go @@ -0,0 +1,115 @@ +// Code generated from deployedcontract.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 + +const DeployedContractTypeAddressFieldName = "address" + +var DeployedContractTypeAddressFieldType = TheAddressType + +const DeployedContractTypeAddressFieldDocString = ` +The address of the account where the contract is deployed at. +` + +const DeployedContractTypeNameFieldName = "name" + +var DeployedContractTypeNameFieldType = StringType + +const DeployedContractTypeNameFieldDocString = ` +The name of the contract. +` + +const DeployedContractTypeCodeFieldName = "code" + +var DeployedContractTypeCodeFieldType = &VariableSizedType{ + Type: UInt8Type, +} + +const DeployedContractTypeCodeFieldDocString = ` +The code of the contract. +` + +const DeployedContractTypePublicTypesFunctionName = "publicTypes" + +var DeployedContractTypePublicTypesFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + &VariableSizedType{ + Type: MetaType, + }, + ), +} + +const DeployedContractTypePublicTypesFunctionDocString = ` +Returns an array of ` + "`Type`" + ` objects representing all the public type declarations in this contract +(e.g. structs, resources, enums). + +For example, given a contract +` + ` +contract Foo { +pub struct Bar {...} +pub resource Qux {...} +} +` + ` +then ` + "`.publicTypes()`" + ` will return an array equivalent to the expression ` + "`[Type(), Type()]`" + ` +` + +const DeployedContractTypeName = "DeployedContract" + +var DeployedContractType = &SimpleType{ + Name: DeployedContractTypeName, + QualifiedName: DeployedContractTypeName, + TypeID: DeployedContractTypeName, + tag: DeployedContractTypeTag, + IsResource: false, + Storable: false, + Equatable: false, + Exportable: false, + Importable: false, +} + +func init() { + DeployedContractType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeAddressFieldName, + DeployedContractTypeAddressFieldType, + DeployedContractTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeNameFieldName, + DeployedContractTypeNameFieldType, + DeployedContractTypeNameFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeCodeFieldName, + DeployedContractTypeCodeFieldType, + DeployedContractTypeCodeFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DeployedContractTypePublicTypesFunctionName, + DeployedContractTypePublicTypesFunctionType, + DeployedContractTypePublicTypesFunctionDocString, + ), + }) + } +} diff --git a/runtime/sema/deployedcontract.go b/runtime/sema/deployedcontract.go new file mode 100644 index 0000000000..2581d4298f --- /dev/null +++ b/runtime/sema/deployedcontract.go @@ -0,0 +1,21 @@ +/* + * 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 deployedcontract.cdc deployedcontract.gen.go From 1546ddd6115c3bcf7a57de52064dfc3770291769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 15:49:01 -0700 Subject: [PATCH 109/173] optimize String type members: avoid allocation on access --- runtime/interpreter/value.go | 17 ++--- runtime/sema/string_type.go | 126 ++++++++++++++--------------------- 2 files changed, 59 insertions(+), 84 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index b092e6bf05..b2d75c2ea7 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -350,7 +350,7 @@ func (v TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { - case "identifier": + case sema.MetaTypeIdentifierFieldName: var typeID string staticType := v.Type if staticType != nil { @@ -363,7 +363,8 @@ func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name str return NewStringValue(interpreter, memoryUsage, func() string { return typeID }) - case "isSubtype": + + case sema.MetaTypeIsSubtypeFunctionName: return NewHostFunctionValue( interpreter, sema.MetaTypeIsSubtypeFunctionType, @@ -1121,14 +1122,14 @@ func (*StringValue) RemoveKey(_ *Interpreter, _ LocationRange, _ Value) Value { func (v *StringValue) GetMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { switch name { - case "length": + case sema.StringTypeLengthFieldName: length := v.Length() return NewIntValueFromInt64(interpreter, int64(length)) - case "utf8": + case sema.StringTypeUtf8FieldName: return ByteSliceToByteArrayValue(interpreter, []byte(v.Str)) - case "concat": + case sema.StringTypeConcatFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeConcatFunctionType, @@ -1142,7 +1143,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "slice": + case sema.StringTypeSliceFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeSliceFunctionType, @@ -1161,7 +1162,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "decodeHex": + case sema.StringTypeDecodeHexFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeDecodeHexFunctionType, @@ -1173,7 +1174,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "toLower": + case sema.StringTypeToLowerFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeToLowerFunctionType, diff --git a/runtime/sema/string_type.go b/runtime/sema/string_type.go index b871364743..75084c3a98 100644 --- a/runtime/sema/string_type.go +++ b/runtime/sema/string_type.go @@ -19,8 +19,6 @@ package sema import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" ) @@ -62,80 +60,44 @@ var StringType = &SimpleType{ func init() { StringType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - "concat": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeConcatFunctionType, - stringTypeConcatFunctionDocString, - ) - }, - }, - "slice": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeSliceFunctionType, - stringTypeSliceFunctionDocString, - ) - }, - }, - "decodeHex": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeDecodeHexFunctionType, - stringTypeDecodeHexFunctionDocString, - ) - }, - }, - "utf8": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - ByteArrayType, - stringTypeUtf8FieldDocString, - ) - }, - }, - "length": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - IntType, - stringTypeLengthFieldDocString, - ) - }, - }, - "toLower": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringTypeToLowerFunctionType, - stringTypeToLowerFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + StringTypeConcatFunctionName, + StringTypeConcatFunctionType, + stringTypeConcatFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StringTypeSliceFunctionName, + StringTypeSliceFunctionType, + stringTypeSliceFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StringTypeDecodeHexFunctionName, + StringTypeDecodeHexFunctionType, + stringTypeDecodeHexFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeUtf8FieldName, + ByteArrayType, + stringTypeUtf8FieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeLengthFieldName, + IntType, + stringTypeLengthFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeToLowerFunctionName, + StringTypeToLowerFunctionType, + stringTypeToLowerFunctionDocString, + ), + }) } } @@ -152,6 +114,8 @@ var StringTypeConcatFunctionType = &FunctionType{ ), } +const StringTypeConcatFunctionName = "concat" + const stringTypeConcatFunctionDocString = ` Returns a new string which contains the given string concatenated to the end of the original string, but does not modify the original string ` @@ -172,6 +136,8 @@ var StringTypeSliceFunctionType = &FunctionType{ ), } +const StringTypeSliceFunctionName = "slice" + const stringTypeSliceFunctionDocString = ` Returns a new string containing the slice of the characters in the given string from start index ` + "`from`" + ` up to, but not including, the end index ` + "`upTo`" + `. @@ -194,6 +160,8 @@ var StringTypeDecodeHexFunctionType = &FunctionType{ ReturnTypeAnnotation: NewTypeAnnotation(ByteArrayType), } +const StringTypeDecodeHexFunctionName = "decodeHex" + const stringTypeDecodeHexFunctionDocString = ` Returns an array containing the bytes represented by the given hexadecimal string. @@ -201,10 +169,14 @@ The given string must only contain hexadecimal characters and must have an even If the string is malformed, the program aborts ` +const StringTypeLengthFieldName = "length" + const stringTypeLengthFieldDocString = ` The number of characters in the string ` +const StringTypeUtf8FieldName = "utf8" + const stringTypeUtf8FieldDocString = ` The byte array of the UTF-8 encoding ` @@ -213,6 +185,8 @@ var StringTypeToLowerFunctionType = &FunctionType{ ReturnTypeAnnotation: NewTypeAnnotation(StringType), } +const StringTypeToLowerFunctionName = "toLower" + const stringTypeToLowerFunctionDocString = ` Returns the string with upper case letters replaced with lowercase ` From 4aaaacd1d1a53d5b757b91c0022333fcd5a43666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 15:49:20 -0700 Subject: [PATCH 110/173] refactor Character type to Cadence --- runtime/sema/character.cdc | 6 +++ .../{character_type.go => character.gen.go} | 53 +++++++++---------- runtime/sema/character.go | 29 ++++++++++ 3 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 runtime/sema/character.cdc rename runtime/sema/{character_type.go => character.gen.go} (53%) create mode 100644 runtime/sema/character.go diff --git a/runtime/sema/character.cdc b/runtime/sema/character.cdc new file mode 100644 index 0000000000..4339c7bbde --- /dev/null +++ b/runtime/sema/character.cdc @@ -0,0 +1,6 @@ + +pub struct Character: Storable, Equatable, Exportable, Importable { + + /// Returns this character as a String + pub fun toString(): String +} diff --git a/runtime/sema/character_type.go b/runtime/sema/character.gen.go similarity index 53% rename from runtime/sema/character_type.go rename to runtime/sema/character.gen.go index f46bd021c3..b7ea58dce6 100644 --- a/runtime/sema/character_type.go +++ b/runtime/sema/character.gen.go @@ -1,3 +1,4 @@ +// Code generated from character.cdc. DO NOT EDIT. /* * Cadence - The resource-oriented smart contract programming language * @@ -18,18 +19,24 @@ package sema -import ( - "github.com/rivo/uniseg" +const CharacterTypeToStringFunctionName = "toString" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) +var CharacterTypeToStringFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + StringType, + ), +} + +const CharacterTypeToStringFunctionDocString = ` +Returns this character as a String +` + +const CharacterTypeName = "Character" -// CharacterType represents the character type var CharacterType = &SimpleType{ - Name: "Character", - QualifiedName: "Character", - TypeID: "Character", + Name: CharacterTypeName, + QualifiedName: CharacterTypeName, + TypeID: CharacterTypeName, tag: CharacterTypeTag, IsResource: false, Storable: true, @@ -38,27 +45,15 @@ var CharacterType = &SimpleType{ Importable: true, } -func IsValidCharacter(s string) bool { - graphemes := uniseg.NewGraphemes(s) - // a valid character must have exactly one grapheme cluster - return graphemes.Next() && !graphemes.Next() -} - func init() { CharacterType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - ToStringFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - ToStringFunctionType, - toStringFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + CharacterTypeToStringFunctionName, + CharacterTypeToStringFunctionType, + CharacterTypeToStringFunctionDocString, + ), + }) } } diff --git a/runtime/sema/character.go b/runtime/sema/character.go new file mode 100644 index 0000000000..7e8475cb25 --- /dev/null +++ b/runtime/sema/character.go @@ -0,0 +1,29 @@ +/* + * 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 character.cdc character.gen.go + +import "github.com/rivo/uniseg" + +func IsValidCharacter(s string) bool { + graphemes := uniseg.NewGraphemes(s) + // a valid character must have exactly one grapheme cluster + return graphemes.Next() && !graphemes.Next() +} From 0e532e44376b4aaee3221efedcc58c0ad2f4f52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 15:49:45 -0700 Subject: [PATCH 111/173] optimize MetaType members: avoid allocation on access --- runtime/sema/block.go | 2 +- runtime/sema/meta_type.go | 51 +++++++++++++++------------------------ 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/runtime/sema/block.go b/runtime/sema/block.go index ea4c683094..ebbfa6bc10 100644 --- a/runtime/sema/block.go +++ b/runtime/sema/block.go @@ -18,4 +18,4 @@ package sema -//go:generate go run ./gen/main.go block.cdc block.gen.go +//go:generate go run ./gen block.cdc block.gen.go diff --git a/runtime/sema/meta_type.go b/runtime/sema/meta_type.go index e60966f948..c0f72e14f6 100644 --- a/runtime/sema/meta_type.go +++ b/runtime/sema/meta_type.go @@ -18,16 +18,15 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) +const MetaTypeIdentifierFieldName = "identifier" -const metaTypeIdentifierDocString = ` +const metaTypeIdentifierFieldDocString = ` The fully-qualified identifier of the type ` -const metaTypeSubtypeDocString = ` +const MetaTypeIsSubtypeFunctionName = "isSubtype" + +const metaTypeIsSubtypeFunctionDocString = ` Returns true if this type is a subtype of the given type at run-time ` @@ -61,31 +60,19 @@ var MetaTypeIsSubtypeFunctionType = &FunctionType{ func init() { MetaType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - "identifier": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringType, - metaTypeIdentifierDocString, - ) - }, - }, - "isSubtype": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - MetaTypeIsSubtypeFunctionType, - metaTypeSubtypeDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + MetaTypeIdentifierFieldName, + StringType, + metaTypeIdentifierFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + MetaTypeIsSubtypeFunctionName, + MetaTypeIsSubtypeFunctionType, + metaTypeIsSubtypeFunctionDocString, + ), + }) } } From d4af6614adae661191ba99e0e22b26202b406a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 3 Apr 2023 16:11:54 -0700 Subject: [PATCH 112/173] optimize member resolvers of some more types --- runtime/sema/type.go | 80 ++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index e4cb462ef6..05be7698c3 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2554,6 +2554,8 @@ type FunctionType struct { Members *StringMemberOrderedMap TypeParameters []*TypeParameter Parameters []Parameter + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once IsConstructor bool } @@ -2999,22 +3001,18 @@ func (t *FunctionType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } func (t *FunctionType) GetMembers() map[string]MemberResolver { - // TODO: optimize - var members map[string]MemberResolver - if t.Members != nil { - members = make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) - } - return withBuiltinMembers(t, members) + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *FunctionType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + var memberResolvers map[string]MemberResolver + if t.Members != nil { + memberResolvers = MembersMapAsResolvers(t.Members) + } + t.memberResolvers = withBuiltinMembers(t, memberResolvers) + }) } type ArgumentExpressionsCheck func( @@ -4207,17 +4205,7 @@ func (t *InterfaceType) GetMembers() map[string]MemberResolver { func (t *InterfaceType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - members := make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) + members := MembersMapAsResolvers(t.Members) t.memberResolvers = withBuiltinMembers(t, members) }) @@ -5860,10 +5848,12 @@ func IsNilType(ty Type) bool { } type TransactionType struct { - Members *StringMemberOrderedMap - Fields []string - PrepareParameters []Parameter - Parameters []Parameter + Fields []string + PrepareParameters []Parameter + Parameters []Parameter + Members *StringMemberOrderedMap + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ Type = &TransactionType{} @@ -5947,22 +5937,18 @@ func (t *TransactionType) RewriteWithRestrictedTypes() (Type, bool) { } func (t *TransactionType) GetMembers() map[string]MemberResolver { - // TODO: optimize - var members map[string]MemberResolver - if t.Members != nil { - members = make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) - } - return withBuiltinMembers(t, members) + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *TransactionType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + var memberResolvers map[string]MemberResolver + if t.Members != nil { + memberResolvers = MembersMapAsResolvers(t.Members) + } + t.memberResolvers = withBuiltinMembers(t, memberResolvers) + }) } func (*TransactionType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { From 7801f01c9e35fe127627ffa320650a5b44ab9c9c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 15:26:06 +0530 Subject: [PATCH 113/173] Resolve review comments: 1. Add test case of equality of dictionary where inner types are unequatable 2. Fix formatting --- .../tests/checker/arrays_dictionaries_test.go | 194 ++++++++++++------ runtime/tests/interpreter/interpreter_test.go | 114 +++++++--- 2 files changed, 216 insertions(+), 92 deletions(-) diff --git a/runtime/tests/checker/arrays_dictionaries_test.go b/runtime/tests/checker/arrays_dictionaries_test.go index c67f33af98..9bcf610093 100644 --- a/runtime/tests/checker/arrays_dictionaries_test.go +++ b/runtime/tests/checker/arrays_dictionaries_test.go @@ -340,65 +340,141 @@ func TestCheckDictionaryEqual(t *testing.T) { } for _, opStr := range []string{"==", "!="} { - testValid("self_dict_equality", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": 1, "def": 2} - return d %s d - }`, opStr)) - - testValid("self_dict_equality_nested_1", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": {1: 100, 2: 200}, "def": {4: 400, 5: 500}} - return d %s d - }`, opStr)) - - testValid("self_dict_equality_nested_2", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d - }`, opStr)) - - testValid("dict_equality_true", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": 1, "def": 2} - let d2 = {"abc": 1, "def": 2} - return d %s d2 - }`, opStr)) - - testValid("dict_equality_true_nested", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d2 - }`, opStr)) - - testValid("dict_equality_false", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": 1, "def": 2} - let d2 = {"abc": 1, "def": 2, "xyz": 4} - return d %s d2 - }`, opStr)) - - testValid("dict_equality_false_nested", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d2 - }`, opStr)) - - assertInvalid("dict_equality_invalid", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": 1, "def": 2} - let d2 = {1: "abc", 2: "def"} - return d %s d2 - }`, opStr)) - - assertInvalid("dict_equality_invalid_nested", fmt.Sprintf(` - fun test(): Bool { - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - let d2 = {"abc": {1: {1000: "a"}, 2: {2000: "b"}}, "def": {4: {1000: "c"}, 5: {2000: "d"}}} - return d %s d2 - }`, opStr)) + testValid( + "self_dict_equality", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "self_dict_equality_nested_1", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: 100, 2: 200}, "def": {4: 400, 5: 500}} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "self_dict_equality_nested_2", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_true", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_true_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_false", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_false_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid("dict_equality_invalid", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {1: "abc", 2: "def"} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid( + "dict_equality_invalid_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {1000: "a"}, 2: {2000: "b"}}, "def": {4: {1000: "c"}, 5: {2000: "d"}}} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid( + "dict_equality_invalid_inner_type_unequatable", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": fun (): Void {}} + let d2 = {"abc": fun (): Void {}} + return d %s d2 + } + `, + opStr, + ), + ) } } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 10aaa38111..10cd1d411d 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -4597,39 +4597,87 @@ func TestInterpretDictionaryEquality(t *testing.T) { } for _, opStr := range []string{"==", "!="} { - testBooleanFunction(t, "dictionary should be equal to itself", opStr == "==", fmt.Sprintf(` - let d = {"abc": 1, "def": 2} - return d %s d - `, opStr)) - - testBooleanFunction(t, "nested dictionary should be equal to itself", opStr == "==", fmt.Sprintf(` - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d - `, opStr)) - - testBooleanFunction(t, "simple dictionary equality", opStr == "==", fmt.Sprintf(` - let d = {"abc": 1, "def": 2} - let d2 = {"abc": 1, "def": 2} - return d %s d2 - `, opStr)) - - testBooleanFunction(t, "nested dictionary equality check", opStr == "==", fmt.Sprintf(` - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d2 - `, opStr)) - - testBooleanFunction(t, "simple dictionary unequal", opStr == "!=", fmt.Sprintf(` - let d = {"abc": 1, "def": 2} - let d2 = {"abc": 1, "def": 2, "xyz": 4} - return d %s d2 - `, opStr)) - - testBooleanFunction(t, "nested dictionary unequal", opStr == "!=", fmt.Sprintf(` - let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} - return d %s d2 - `, opStr)) + testBooleanFunction( + t, + "dictionary should be equal to itself", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + return d %s d + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary should be equal to itself", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "simple dictionary equality", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary equality check", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "simple dictionary unequal", + opStr == "!=", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary unequal", + opStr == "!=", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, + opStr, + ), + ) } } From 828842146c181824c004c98af4d627f94746f261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 10:45:33 -0700 Subject: [PATCH 114/173] remove old code --- runtime/interpreter/accountcontracts.go | 12 ++-- runtime/sema/account_contracts.go | 88 ------------------------- runtime/stdlib/account.go | 15 ++++- 3 files changed, 18 insertions(+), 97 deletions(-) delete mode 100644 runtime/sema/account_contracts.go diff --git a/runtime/interpreter/accountcontracts.go b/runtime/interpreter/accountcontracts.go index 00f8c45171..57a58ad01d 100644 --- a/runtime/interpreter/accountcontracts.go +++ b/runtime/interpreter/accountcontracts.go @@ -46,8 +46,8 @@ func NewAuthAccountContractsValue( fields := map[string]Value{ sema.AuthAccountContractsTypeAddFunctionName: addFunction, - sema.AccountContractsTypeGetFunctionName: getFunction, - sema.AccountContractsTypeBorrowFunctionName: borrowFunction, + sema.AuthAccountContractsTypeGetFunctionName: getFunction, + sema.AuthAccountContractsTypeBorrowFunctionName: borrowFunction, sema.AuthAccountContractsTypeRemoveFunctionName: removeFunction, sema.AuthAccountContractsTypeUpdate__experimentalFunctionName: updateFunction, } @@ -58,7 +58,7 @@ func NewAuthAccountContractsValue( locationRange LocationRange, ) Value { switch name { - case sema.AccountContractsTypeNamesFieldName: + case sema.AuthAccountContractsTypeNamesFieldName: return namesGetter(interpreter, locationRange) } return nil @@ -100,8 +100,8 @@ func NewPublicAccountContractsValue( ) Value { fields := map[string]Value{ - sema.AccountContractsTypeGetFunctionName: getFunction, - sema.AccountContractsTypeBorrowFunctionName: borrowFunction, + sema.PublicAccountContractsTypeGetFunctionName: getFunction, + sema.PublicAccountContractsTypeBorrowFunctionName: borrowFunction, } computeField := func( @@ -110,7 +110,7 @@ func NewPublicAccountContractsValue( locationRange LocationRange, ) Value { switch name { - case sema.AccountContractsTypeNamesFieldName: + case sema.PublicAccountContractsTypeNamesFieldName: return namesGetter(interpreter, locationRange) } return nil diff --git a/runtime/sema/account_contracts.go b/runtime/sema/account_contracts.go deleted file mode 100644 index 5215435e75..0000000000 --- a/runtime/sema/account_contracts.go +++ /dev/null @@ -1,88 +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 sema - -const AccountContractsTypeGetFunctionName = "get" -const AccountContractsTypeBorrowFunctionName = "borrow" -const AccountContractsTypeNamesFieldName = "names" - -const accountContractsTypeGetFunctionDocString = ` -Returns the deployed contract for the contract/contract interface with the given name in the account, if any. - -Returns nil if no contract/contract interface with the given name exists in the account. -` - -var AccountContractsTypeGetFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation( - StringType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: DeployedContractType, - }, - ), -} - -const accountContractsTypeBorrowFunctionDocString = ` -Returns a reference of the given type to the contract with the given name in the account, if any. - -Returns nil if no contract with the given name exists in the account, or if the contract does not conform to the given type. -` - -var AccountContractsTypeBorrowFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: NewTypeAnnotation(StringType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -var accountContractsTypeNamesFieldType = &VariableSizedType{ - Type: StringType, -} - -const accountContractsTypeNamesFieldDocString = ` -Names of all contracts deployed in the account. -` diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index a99356b977..38b25e0c23 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -234,23 +234,27 @@ func newAuthAccountContractsValue( gauge, addressValue, newAuthAccountContractsChangeFunction( + sema.AuthAccountContractsTypeAddFunctionType, gauge, handler, addressValue, false, ), newAuthAccountContractsChangeFunction( + sema.AuthAccountContractsTypeUpdate__experimentalFunctionType, gauge, handler, addressValue, true, ), newAccountContractsGetFunction( + sema.AuthAccountContractsTypeGetFunctionType, gauge, handler, addressValue, ), newAccountContractsBorrowFunction( + sema.AuthAccountContractsTypeBorrowFunctionType, gauge, handler, addressValue, @@ -946,11 +950,13 @@ func newPublicAccountContractsValue( gauge, addressValue, newAccountContractsGetFunction( + sema.PublicAccountContractsTypeGetFunctionType, gauge, handler, addressValue, ), newAccountContractsBorrowFunction( + sema.PublicAccountContractsTypeBorrowFunctionType, gauge, handler, addressValue, @@ -1245,6 +1251,7 @@ type AccountContractProvider interface { } func newAccountContractsGetFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountContractProvider, addressValue interpreter.AddressValue, @@ -1255,7 +1262,7 @@ func newAccountContractsGetFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountContractsTypeGetFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { @@ -1294,6 +1301,7 @@ func newAccountContractsGetFunction( } func newAccountContractsBorrowFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, handler PublicAccountContractsHandler, addressValue interpreter.AddressValue, @@ -1304,7 +1312,7 @@ func newAccountContractsBorrowFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountContractsTypeBorrowFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter @@ -1400,6 +1408,7 @@ type AccountContractAdditionHandler interface { // - adding: `AuthAccount.contracts.add(name: "Foo", code: [...])` (isUpdate = false) // - updating: `AuthAccount.contracts.update__experimental(name: "Foo", code: [...])` (isUpdate = true) func newAuthAccountContractsChangeFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, handler AccountContractAdditionHandler, addressValue interpreter.AddressValue, @@ -1407,7 +1416,7 @@ func newAuthAccountContractsChangeFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountContractsTypeAddFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { locationRange := invocation.LocationRange From 2d623c952897af7be0080b874dd26e9763506d5e Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:40:16 -0500 Subject: [PATCH 115/173] Support nullable types in CCF codec Add support for nullable types: - type bound in FunctionType.TypeParameters - type of RestrictedType - borrow type of CapabilityType - add more tests --- encoding/ccf/ccf_test.go | 2853 +++++++++++++++++++++-------------- encoding/ccf/decode.go | 71 +- encoding/ccf/decode_type.go | 38 +- encoding/ccf/encode.go | 39 +- encoding/ccf/encode_type.go | 17 +- types.go | 14 +- 6 files changed, 1856 insertions(+), 1176 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 2aa51dec4e..aa8d190c78 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -6240,224 +6240,446 @@ func TestEncodeEnum(t *testing.T) { } func TestEncodeValueOfRestrictedType(t *testing.T) { - hasCountInterfaceType := cadence.NewResourceInterfaceType( - common.NewStringLocation(nil, "test"), - "HasCount", - nil, - nil, - ) - hasSumInterfaceType := cadence.NewResourceInterfaceType( - common.NewStringLocation(nil, "test"), - "HasSum", - nil, - nil, - ) + t.Run("nil restricted type", func(t *testing.T) { + hasCountInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasCount", + nil, + nil, + ) - statsType := cadence.NewResourceType( - common.NewStringLocation(nil, "test"), - "Stats", - []cadence.Field{ - cadence.NewField("count", cadence.NewIntType()), - cadence.NewField("sum", cadence.NewIntType()), - }, - nil, - ) + hasSumInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasSum", + nil, + nil, + ) - countSumRestrictedType := cadence.NewRestrictedType( - statsType, - []cadence.Type{ - hasCountInterfaceType, - hasSumInterfaceType, - }, - ) + statsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("count", cadence.NewIntType()), + cadence.NewField("sum", cadence.NewIntType()), + }, + nil, + ) - val := cadence.NewArray([]cadence.Value{ - cadence.NewResource( - []cadence.Value{ - cadence.NewInt(1), - cadence.NewInt(2), + countSumRestrictedType := cadence.NewRestrictedType( + nil, + []cadence.Type{ + hasCountInterfaceType, + hasSumInterfaceType, }, - ).WithType(statsType), - }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) + ) - expectedStatsType := cadence.NewResourceType( - common.NewStringLocation(nil, "test"), - "Stats", - []cadence.Field{ - cadence.NewField("sum", cadence.NewIntType()), - cadence.NewField("count", cadence.NewIntType()), - }, - nil, - ) + val := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + }, + ).WithType(statsType), + }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) - expectedCountSumRestrictedType := cadence.NewRestrictedType( - expectedStatsType, - []cadence.Type{ - hasSumInterfaceType, - hasCountInterfaceType, - }, - ) + expectedStatsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("sum", cadence.NewIntType()), + cadence.NewField("count", cadence.NewIntType()), + }, + nil, + ) - expectedVal := cadence.NewArray([]cadence.Value{ - cadence.NewResource( - []cadence.Value{ - cadence.NewInt(2), - cadence.NewInt(1), + expectedCountSumRestrictedType := cadence.NewRestrictedType( + nil, + []cadence.Type{ + hasSumInterfaceType, + hasCountInterfaceType, }, - ).WithType(expectedStatsType), - }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + ) - testEncodeAndDecodeEx( - t, - val, - []byte{ - // language=json, format=json-cdc - // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"sum"},{"value":{"value":"2","type":"Int"},"name":"count"}]},"type":"Resource"}],"type":"Array"} - // - // language=edn, format=ccf - // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) - // - // language=cbor, format=ccf - // tag - 0xd8, ccf.CBORTagTypeDefAndValue, - // array, 2 items follow - 0x82, - // element 0: type definitions - // array, 3 items follow - 0x83, - // resource type: - // id: []byte{} - // cadence-type-id: "S.test.Stats" - // 2 fields: [["sum", type(int)], ["count", type(int)]] - // tag - 0xd8, ccf.CBORTagResourceType, - // array, 3 items follow - 0x83, - // id - // bytes, 0 bytes follow - 0x40, - // cadence-type-id - // string, 12 bytes follow - 0x6c, - // S.test.Stats - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, - // fields - // array, 2 items follow - 0x82, - // field 0 - // array, 2 items follow - 0x82, - // text, 3 bytes follow - 0x63, - // sum - 0x73, 0x75, 0x6d, - // tag - 0xd8, ccf.CBORTagSimpleType, - // Int type ID (4) - 0x04, - // field 1 - // array, 2 items follow - 0x82, - // text, 5 bytes follow - 0x65, - // count - 0x63, 0x6f, 0x75, 0x6e, 0x74, - // tag - 0xd8, ccf.CBORTagSimpleType, - // Int type ID (4) - 0x04, - // resource interface type: - // id: []byte{1} - // cadence-type-id: "S.test.HasSum" - // tag - 0xd8, ccf.CBORTagResourceInterfaceType, - // array, 2 items follow - 0x82, - // id - // bytes, 1 bytes follow - 0x41, - // 1 - 0x01, - // cadence-type-id - // string, 13 bytes follow - 0x6d, - // S.test.HasSum - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, - // resource interface type: - // id: []byte{2} - // cadence-type-id: "S.test.HasCount" - // tag - 0xd8, ccf.CBORTagResourceInterfaceType, - // array, 2 items follow - 0x82, - // id - // bytes, 1 bytes follow - 0x41, - // 2 - 0x02, - // cadence-type-id - // string, 15 bytes follow - 0x6f, - // S.test.HasCount - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + expectedVal := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(2), + cadence.NewInt(1), + }, + ).WithType(expectedStatsType), + }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + + testEncodeAndDecodeEx( + t, + val, + []byte{ + // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"count"},{"value":{"value":"2","type":"Int"},"name":"sum"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([null, [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Stats" + // 2 fields: [["sum", type(int)], ["count", type(int)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x6c, + // S.test.Stats + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // sum + 0x73, 0x75, 0x6d, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 5 bytes follow + 0x65, + // count + 0x63, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // resource interface type: + // id: []byte{1} + // cadence-type-id: "S.test.HasSum" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 13 bytes follow + 0x6d, + // S.test.HasSum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, + // resource interface type: + // id: []byte{2} + // cadence-type-id: "S.test.HasCount" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.HasCount + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 items follow + 0x82, + // type + // null + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + }, + expectedVal, + ) + }) + + t.Run("resource restricted type", func(t *testing.T) { + hasCountInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasCount", + nil, + nil, + ) + + hasSumInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasSum", + nil, + nil, + ) + + statsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("count", cadence.NewIntType()), + cadence.NewField("sum", cadence.NewIntType()), + }, + nil, + ) + + countSumRestrictedType := cadence.NewRestrictedType( + statsType, + []cadence.Type{ + hasCountInterfaceType, + hasSumInterfaceType, + }, + ) + + val := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + }, + ).WithType(statsType), + }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) + + expectedStatsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("sum", cadence.NewIntType()), + cadence.NewField("count", cadence.NewIntType()), + }, + nil, + ) + + expectedCountSumRestrictedType := cadence.NewRestrictedType( + expectedStatsType, + []cadence.Type{ + hasSumInterfaceType, + hasCountInterfaceType, + }, + ) + + expectedVal := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(2), + cadence.NewInt(1), + }, + ).WithType(expectedStatsType), + }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + + testEncodeAndDecodeEx( + t, + val, + []byte{ + // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"sum"},{"value":{"value":"2","type":"Int"},"name":"count"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Stats" + // 2 fields: [["sum", type(int)], ["count", type(int)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x6c, + // S.test.Stats + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // sum + 0x73, 0x75, 0x6d, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 5 bytes follow + 0x65, + // count + 0x63, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // resource interface type: + // id: []byte{1} + // cadence-type-id: "S.test.HasSum" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 13 bytes follow + 0x6d, + // S.test.HasSum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, + // resource interface type: + // id: []byte{2} + // cadence-type-id: "S.test.HasCount" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.HasCount + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - // element 1: type and value - // array, 2 items follow - 0x82, - // tag - 0xd8, ccf.CBORTagVarsizedArrayType, - // tag - 0xd8, ccf.CBORTagRestrictedType, - // array, 2 items follow - 0x82, - // type - // tag - 0xd8, ccf.CBORTagTypeRef, - // bytes, 0 byte follows - 0x40, - // array, 2 items follow - 0x82, - // tag - 0xd8, ccf.CBORTagTypeRef, - // bytes, 1 byte follows - 0x41, - // 1 - 0x01, - // tag - 0xd8, ccf.CBORTagTypeRef, - // bytes, 1 byte follows - 0x41, - // 2 - 0x02, + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 items follow + 0x82, + // type + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, - // array, 1 item follows - 0x81, - // tag - 0xd8, ccf.CBORTagTypeAndValue, - // array, 2 items follow - 0x82, - // tag - 0xd8, ccf.CBORTagTypeRef, - // bytes, 0 byte follows - 0x40, - // array, 2 items follow - 0x82, - // tag (big num) - 0xc2, - // bytes, 1 byte follows - 0x41, - // 2 - 0x02, - // tag (big num) - 0xc2, - // bytes, 1 byte follows - 0x41, - // 1 - 0x01, - }, - expectedVal, - ) + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + }, + expectedVal, + ) + }) } func TestEncodeValueOfReferenceType(t *testing.T) { @@ -6991,41 +7213,184 @@ func TestEncodeSimpleTypes(t *testing.T) { err = encoder.EncodeInt(ty.cborSimpleTypeID) require.NoError(t, err) - encoder.Flush() + encoder.Flush() + + tests = append(tests, encodeTest{ + name: fmt.Sprintf("with static %s", ty.typ.ID()), + val: cadence.TypeValue{ + StaticType: ty.typ, + }, + expected: w.Bytes(), + // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"[ty.ID()]"}}} + // + // language=edn, format=ccf + // 130([137(41), 185(simple_type_id)]) + }) + } + + testAllEncodeAndDecode(t, tests...) +} + +func TestEncodeType(t *testing.T) { + + t.Parallel() + + t.Run("with static int?", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: cadence.IntType{}}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static int??", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: &cadence.OptionalType{Type: cadence.IntType{}}}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(186(185(4)))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + t.Run("with static [int]", func(t *testing.T) { - tests = append(tests, encodeTest{ - name: fmt.Sprintf("with static %s", ty.typ.ID()), - val: cadence.TypeValue{ - StaticType: ty.typ, + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.VariableSizedArrayType{ElementType: cadence.IntType{}}, }, - expected: w.Bytes(), - // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"[ty.ID()]"}}} - // - // language=edn, format=ccf - // 130([137(41), 185(simple_type_id)]) - }) - } + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"VariableSizedArray", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 187(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) - testAllEncodeAndDecode(t, tests...) -} + }) -func TestEncodeType(t *testing.T) { + t.Run("with static [int; 3]", func(t *testing.T) { - t.Parallel() + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ConstantSizedArrayType{ + ElementType: cadence.IntType{}, + Size: 3, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"ConstantSizedArray", "type" : {"kind" : "Int"}, "size" : 3}}} + // + // language=edn, format=ccf + // 130([137(41), 188([3, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) - t.Run("with static int?", func(t *testing.T) { + }) + + t.Run("with static {int:string}", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.OptionalType{Type: cadence.IntType{}}, + StaticType: &cadence.DictionaryType{ + ElementType: cadence.StringType{}, + KeyType: cadence.IntType{}, + }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // {"type":"Type","value":{"staticType":{"kind":"Dictionary", "key" : {"kind" : "Int"}, "value" : {"kind" : "String"}}}} // // language=edn, format=ccf - // 130([137(41), 186(185(4))]) + // 130([137(41), 189([185(4), 185(1)])]) // // language=cbor, format=ccf // tag @@ -7037,28 +7402,39 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagOptionalTypeValue, + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, }, ) }) - t.Run("with static int??", func(t *testing.T) { + t.Run("with static struct with no field", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.OptionalType{Type: &cadence.OptionalType{Type: cadence.IntType{}}}, + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[],"initializers":[]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 186(186(185(4)))]) + // 130([137(41), 208([h'', "S.test.S", null, [], []])]) // // language=cbor, format=ccf // tag @@ -7070,29 +7446,171 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagOptionalTypeValue, + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + }) + + t.Run("with static struct", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf // tag - 0xd8, ccf.CBORTagOptionalTypeValue, + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static resource of composite fields and initializers", func(t *testing.T) { + + t.Parallel() + + fooTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + fooTy2 := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo2", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + barTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + Fields: []cadence.Field{ + { + Identifier: "foo1", + Type: fooTy, + }, }, - ) - - }) - t.Run("with static [int]", func(t *testing.T) { + Initializers: [][]cadence.Parameter{ + { + cadence.Parameter{ + Type: fooTy2, + Label: "aaa", + Identifier: "aaa", + }, + }, + }, + } testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.VariableSizedArrayType{ElementType: cadence.IntType{}}, + StaticType: barTy, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"VariableSizedArray", "type" : {"kind" : "Int"}}}} + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 187(185(4))]) + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])]], [[["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) // // language=cbor, format=ccf // tag @@ -7104,31 +7622,108 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo1 + 0x66, 0x6f, 0x6f, 0x31, // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 elements follow + 0x80, + // initializer + // array, 0 elements follow + 0x80, + // initializers + // array, 1 elements follow + 0x81, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // text, 11 bytes follow + 0x6b, + // S.test.Foo2 + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, }, ) - }) - t.Run("with static [int; 3]", func(t *testing.T) { + t.Run("with static resource", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.ConstantSizedArrayType{ - ElementType: cadence.IntType{}, - Size: 3, + StaticType: &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "R", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"ConstantSizedArray", "type" : {"kind" : "Int"}, "size" : 3}}} + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 188([3, 185(4)])]) + // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7140,35 +7735,91 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, // array, 2 elements follow 0x82, - // 3 - 0x03, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, }, ) - }) - t.Run("with static {int:string}", func(t *testing.T) { + t.Run("with static contract", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.DictionaryType{ - ElementType: cadence.StringType{}, - KeyType: cadence.IntType{}, + StaticType: &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Dictionary", "key" : {"kind" : "Int"}, "value" : {"kind" : "String"}}}} + // {"value":{"staticType":{"type":"","kind":"Contract","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 189([185(4), 185(1)])]) + // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7177,42 +7828,94 @@ func TestEncodeType(t *testing.T) { 0x82, // tag 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) + // Meta type ID (42) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagDictTypeValue, + 0xd8, ccf.CBORTagContractTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, // array, 2 elements follow 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // String type (1) 0x01, }, ) - }) - t.Run("with static struct with no field", func(t *testing.T) { + t.Run("with static struct interface", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.StructType{ + StaticType: &cadence.StructInterfaceType{ Location: utils.TestLocation, QualifiedIdentifier: "S", - Fields: []cadence.Field{}, - Initializers: [][]cadence.Parameter{}, + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, }, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[],"initializers":[]}},"type":"Type"} + // {"value":{"staticType":{"type":"","kind":"StructInterface","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 208([h'', "S.test.S", null, [], []])]) + // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7224,35 +7927,75 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagStructTypeValue, + 0xd8, ccf.CBORTagStructInterfaceTypeValue, // array, 5 elements follow 0x85, // bytes, 0 bytes follow 0x40, // string, 8 bytes follow 0x68, - // S.test.So + // S.test.S 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, // type (nil for struct) 0xf6, // fields - // array, 0 element follows - 0x80, + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, // initializers - // array, 0 elements follow - 0x80, + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, }, ) }) - t.Run("with static struct", func(t *testing.T) { + t.Run("with static resource interface", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.StructType{ + StaticType: &cadence.ResourceInterfaceType{ Location: utils.TestLocation, - QualifiedIdentifier: "S", + QualifiedIdentifier: "R", Fields: []cadence.Field{ {Identifier: "foo", Type: cadence.IntType{}}, }, @@ -7265,10 +8008,10 @@ func TestEncodeType(t *testing.T) { }, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"type":"","kind":"ResourceInterface","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7280,15 +8023,15 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagStructTypeValue, + 0xd8, ccf.CBORTagResourceInterfaceTypeValue, // array, 5 elements follow 0x85, // bytes, 0 bytes follow 0x40, // string, 8 bytes follow 0x68, - // S.test.So - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, // type (nil for struct) 0xf6, // fields @@ -7341,54 +8084,30 @@ func TestEncodeType(t *testing.T) { ) }) - t.Run("with static resource of composite fields and initializers", func(t *testing.T) { - - t.Parallel() - - fooTy := &cadence.ResourceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Foo", - Fields: []cadence.Field{}, - Initializers: [][]cadence.Parameter{}, - } - - fooTy2 := &cadence.ResourceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Foo2", - Fields: []cadence.Field{}, - Initializers: [][]cadence.Parameter{}, - } - - barTy := &cadence.ResourceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "Bar", - Fields: []cadence.Field{ - { - Identifier: "foo1", - Type: fooTy, - }, - }, - Initializers: [][]cadence.Parameter{ - { - cadence.Parameter{ - Type: fooTy2, - Label: "aaa", - Identifier: "aaa", - }, - }, - }, - } + t.Run("with static contract interface", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: barTy, + StaticType: &cadence.ContractInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} + // {"value":{"staticType":{"type":"","kind":"ContractInterface","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])]], [[["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) + // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7400,108 +8119,89 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagResourceTypeValue, + 0xd8, ccf.CBORTagContractInterfaceTypeValue, // array, 5 elements follow 0x85, // bytes, 0 bytes follow 0x40, - // string, 10 bytes follow - 0x6a, - // S.test.Bar - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, - // type (nil for struct) + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for contract interface) 0xf6, // fields // array, 1 element follows 0x81, // array, 2 elements follow 0x82, - // string, 4 bytes follow - 0x64, - // foo1 - 0x66, 0x6f, 0x6f, 0x31, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, // tag - 0xd8, ccf.CBORTagResourceTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 1 bytes follow - 0x41, - // 1 - 0x01, - // string, 10 bytes follow - 0x6a, - // S.test.Foo - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, - // type (nil for struct) - 0xf6, - // fields - // array, 0 elements follow - 0x80, - // initializer - // array, 0 elements follow - 0x80, + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, // initializers // array, 1 elements follow 0x81, - // array, 1 elements follow - 0x81, + // array, 2 element follows + 0x82, // array, 3 elements follow 0x83, - // text, 3 bytes follow + // string, 3 bytes follow 0x63, - // aaa - 0x61, 0x61, 0x61, - // text, 3 bytes follow + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow 0x63, - // aaa - 0x61, 0x61, 0x61, + // bar + 0x62, 0x61, 0x72, // tag - 0xd8, ccf.CBORTagResourceTypeValue, - // array, 5 elements follow - 0x85, - // CCF type ID - // bytes, 1 byte follows - 0x41, - // 2 - 0x02, - // text, 11 bytes follow - 0x6b, - // S.test.Foo2 - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, - // null - 0xf6, - // array, 0 element follows - 0x80, - // array, 0 element follows - 0x80, + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, }, ) }) - t.Run("with static resource", func(t *testing.T) { + t.Run("with static event", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.ResourceType{ + StaticType: &cadence.EventType{ Location: utils.TestLocation, - QualifiedIdentifier: "R", + QualifiedIdentifier: "E", Fields: []cadence.Field{ {Identifier: "foo", Type: cadence.IntType{}}, }, - Initializers: [][]cadence.Parameter{ - { - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, + Initializer: []cadence.Parameter{ + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, }, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"type":"Type", "value": {"staticType": {"kind": "Event", "type" : "", "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [[{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}, {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}]] } } } // // language=edn, format=ccf - // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 210([h'', "S.test.E", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7513,16 +8213,16 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagResourceTypeValue, + 0xd8, ccf.CBORTagEventTypeValue, // array, 5 elements follow 0x85, // bytes, 0 bytes follow 0x40, // string, 8 bytes follow 0x68, - // S.test.R - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, - // type (nil for struct) + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // type (nil for event) 0xf6, // fields // array, 1 element follows @@ -7564,7 +8264,7 @@ func TestEncodeType(t *testing.T) { 0x71, 0x75, 0x78, // string, 3 bytes follow 0x63, - // bax + // baz 0x62, 0x61, 0x7a, // tag 0xd8, ccf.CBORTagSimpleTypeValue, @@ -7574,14 +8274,15 @@ func TestEncodeType(t *testing.T) { ) }) - t.Run("with static contract", func(t *testing.T) { + t.Run("with static enum", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.ContractType{ + StaticType: &cadence.EnumType{ Location: utils.TestLocation, - QualifiedIdentifier: "C", + QualifiedIdentifier: "E", + RawType: cadence.StringType{}, Fields: []cadence.Field{ {Identifier: "foo", Type: cadence.IntType{}}, }, @@ -7594,10 +8295,10 @@ func TestEncodeType(t *testing.T) { }, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"Contract","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"type":{"kind":"String"},"kind":"Enum","typeID":"S.test.E","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) // // language=cbor, format=ccf // tag @@ -7606,20 +8307,22 @@ func TestEncodeType(t *testing.T) { 0x82, // tag 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (42) + // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagContractTypeValue, + 0xd8, ccf.CBORTagEnumTypeValue, // array, 5 elements follow 0x85, // bytes, 0 bytes follow 0x40, // string, 8 bytes follow 0x68, - // S.test.C - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, - // type (nil for struct) - 0xf6, + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, // fields // array, 1 element follows 0x81, @@ -7670,30 +8373,67 @@ func TestEncodeType(t *testing.T) { ) }) - t.Run("with static struct interface", func(t *testing.T) { + t.Run("with static &int", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.StructInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "S", - Fields: []cadence.Field{ - {Identifier: "foo", Type: cadence.IntType{}}, + StaticType: &cadence.ReferenceType{ + Authorized: false, + Type: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Reference", "type" : {"kind" : "Int"}, "authorized" : false}}}` + // + // language=edn, format=ccf + // 130([137(41), 190([false, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagReferenceTypeValue, + // array, 2 elements follow + 0x82, + // authorized + // bool + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + + }) + + t.Run("with static function", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.AnyStructType{}}, }, - Initializers: [][]cadence.Parameter{ - { - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, - }, + ReturnType: cadence.IntType{}, + }), }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"StructInterface","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":{"kind":"AnyStruct"}}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 193([[["T", 185(39)]], [["qux", "baz", 185(1)]], 185(4)])]) // // language=cbor, format=ccf // tag @@ -7705,91 +8445,195 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagStructInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 0 bytes follow - 0x40, - // string, 8 bytes follow - 0x68, - // S.test.S - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, - // type (nil for struct) - 0xf6, - // fields - // array, 1 element follows + 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, + // array, 1 elements follow 0x81, // array, 2 elements follow 0x82, + // string, 1 byte follows + 0x61, + // "T" + 0x54, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type (39) + 0x18, 0x27, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, // string, 3 bytes follow 0x63, - // foo - 0x66, 0x6f, 0x6f, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) + // String type (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) 0x04, - // initializers + }, + ) + }) + + t.Run("with static function nil type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T"}, + }, + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + ReturnType: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":null}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 193([[["T", null]], [["qux", "baz", 185(1)]], 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, // array, 1 elements follow 0x81, - // array, 2 element follows + // array, 2 elements follow 0x82, + // string, 1 byte follows + 0x61, + // "T" + 0x54, + // null + 0xf6, + // array, 1 elements follow + 0x81, // array, 3 elements follow 0x83, // string, 3 bytes follow 0x63, - // foo - 0x66, 0x6f, 0x6f, + // qux + 0x71, 0x75, 0x78, // string, 3 bytes follow 0x63, - // bar - 0x62, 0x61, 0x72, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + }) + + t.Run("with static Capability", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.CapabilityType{}, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Capability"}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 192([null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagCapabilityTypeValue, + // array, 1 element follows + 0x81, + // null + 0xf6, + }, + ) + }) + + t.Run("with static Capability", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.CapabilityType{ + BorrowType: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Capability", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 192([185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagCapabilityTypeValue, + // array, 1 element follows + 0x81, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) + // Int type ID (4) 0x04, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // bax - 0x62, 0x61, 0x7a, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) - 0x01, }, ) }) - t.Run("with static resource interface", func(t *testing.T) { + t.Run("with static nil restricted type", func(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: &cadence.ResourceInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "R", - Fields: []cadence.Field{ - {Identifier: "foo", Type: cadence.IntType{}}, - }, - Initializers: [][]cadence.Parameter{ - { - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, - }, - }, + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + }), }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"ResourceInterface","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"kind":"Restriction","type":"","restrictions":[]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 191([null, []])]) // // language=cbor, format=ccf // tag @@ -7801,91 +8645,32 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagResourceInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 0 bytes follow - 0x40, - // string, 8 bytes follow - 0x68, - // S.test.R - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, - // type (nil for struct) - 0xf6, - // fields - // array, 1 element follows - 0x81, + 0xd8, ccf.CBORTagRestrictedTypeValue, // array, 2 elements follow 0x82, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, - // initializers - // array, 1 elements follow - 0x81, - // array, 2 element follows - 0x82, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // string, 3 bytes follow - 0x63, - // bar - 0x62, 0x61, 0x72, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // bax - 0x62, 0x61, 0x7a, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) - 0x01, + // null + 0xf6, + // array, 0 element follows + 0x80, }, ) }) - t.Run("with static contract interface", func(t *testing.T) { + t.Run("with static no restricted type", func(t *testing.T) { - testEncodeAndDecode( + testEncodeAndDecodeEx( t, cadence.TypeValue{ - StaticType: &cadence.ContractInterfaceType{ - Location: utils.TestLocation, - QualifiedIdentifier: "C", - Fields: []cadence.Field{ - {Identifier: "foo", Type: cadence.IntType{}}, - }, - Initializers: [][]cadence.Parameter{ - { - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, - }, - }, + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }), }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":"","kind":"ContractInterface","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{String}","type":{"kind":"Int"},"restrictions":[]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 191([185(4), []])]) // // language=cbor, format=ccf // tag @@ -7897,89 +8682,43 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagContractInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 0 bytes follow - 0x40, - // string, 8 bytes follow - 0x68, - // S.test.C - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, - // type (nil for contract interface) - 0xf6, - // fields - // array, 1 element follows - 0x81, + 0xd8, ccf.CBORTagRestrictedTypeValue, // array, 2 elements follow 0x82, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, - // initializers - // array, 1 elements follow - 0x81, - // array, 2 element follows - 0x82, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // string, 3 bytes follow - 0x63, - // bar - 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) + // Int type ID (4) 0x04, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // bax - 0x62, 0x61, 0x7a, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) - 0x01, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }), }, ) }) - t.Run("with static event", func(t *testing.T) { + t.Run("with static restricted type", func(t *testing.T) { - testEncodeAndDecode( + testEncodeAndDecodeEx( t, cadence.TypeValue{ - StaticType: &cadence.EventType{ - Location: utils.TestLocation, - QualifiedIdentifier: "E", - Fields: []cadence.Field{ - {Identifier: "foo", Type: cadence.IntType{}}, - }, - Initializer: []cadence.Parameter{ - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, }, - }, + Type: cadence.IntType{}, + }), }, []byte{ // language=json, format=json-cdc - // {"type":"Type", "value": {"staticType": {"kind": "Event", "type" : "", "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [[{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}, {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}]] } } } + // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } // // language=edn, format=ccf - // 130([137(41), 210([h'', "S.test.E", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 191([185(4), [185(1)]])]) // // language=cbor, format=ccf // tag @@ -7991,92 +8730,51 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagEventTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 0 bytes follow - 0x40, - // string, 8 bytes follow - 0x68, - // S.test.E - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, - // type (nil for event) - 0xf6, - // fields - // array, 1 element follows - 0x81, + 0xd8, ccf.CBORTagRestrictedTypeValue, // array, 2 elements follow 0x82, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, - // initializers - // array, 1 elements follow - 0x81, - // array, 2 element follows - 0x82, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // string, 3 bytes follow - 0x63, - // bar - 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) + // Int type ID (4) 0x04, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // baz - 0x62, 0x61, 0x7a, + // array, 1 element follows + 0x81, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) + // String type ID (1) 0x01, }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }), + }, ) + }) - t.Run("with static enum", func(t *testing.T) { + t.Run("with static 2 restricted types", func(t *testing.T) { - testEncodeAndDecode( + testEncodeAndDecodeEx( t, cadence.TypeValue{ - StaticType: &cadence.EnumType{ - Location: utils.TestLocation, - QualifiedIdentifier: "E", - RawType: cadence.StringType{}, - Fields: []cadence.Field{ - {Identifier: "foo", Type: cadence.IntType{}}, - }, - Initializers: [][]cadence.Parameter{ - { - {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.NewAnyStructType(), + cadence.StringType{}, }, - }, + Type: cadence.IntType{}, + }), }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"type":{"kind":"String"},"kind":"Enum","typeID":"S.test.E","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // 130([137(41), 191([185(4), [185(1), 185(39)]])]) // // language=cbor, format=ccf // tag @@ -8088,84 +8786,72 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagEnumTypeValue, - // array, 5 elements follow - 0x85, - // bytes, 0 bytes follow - 0x40, - // string, 8 bytes follow - 0x68, - // S.test.E - 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type ID (1) - 0x01, - // fields - // array, 1 element follows - 0x81, + 0xd8, ccf.CBORTagRestrictedTypeValue, // array, 2 elements follow 0x82, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) + // Int type ID (4) 0x04, - // initializers - // array, 1 elements follow - 0x81, // array, 2 element follows 0x82, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - // string, 3 bytes follow - 0x63, - // bar - 0x62, 0x61, 0x72, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type (4) - 0x04, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // bax - 0x62, 0x61, 0x7a, + // String type ID (1) + 0x01, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) - 0x01, + // AnyStruct type ID (39) + 0x18, 0x27, + }, + // Expected decoded RestrictedType has sorted restrictions and no type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + cadence.NewAnyStructType(), + }, + Type: cadence.IntType{}, + }), }, ) }) - t.Run("with static &int", func(t *testing.T) { + t.Run("with static 3 restricted types", func(t *testing.T) { - testEncodeAndDecode( + // restrictedType is generated by fuzzer. + testEncodeAndDecodeEx( t, cadence.TypeValue{ - StaticType: &cadence.ReferenceType{ - Authorized: false, - Type: cadence.IntType{}, + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, }, }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Reference", "type" : {"kind" : "Int"}, "authorized" : false}}}` + // {"value":{"staticType":{"kind":"Restriction","typeID":"","type":{"kind":"AnyStruct"},"restrictions":[{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeA","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeB","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"I.LocationC.TypeC","fields":[],"initializers":[]}]}},"type":"Type"} // // language=edn, format=ccf - // 130([137(41), 190([false, 185(4)])]) + // 130([137(41), 191([185(39), [224([h'', "I.LocationC.TypeC", null, [], []]), 224([h'01', "A.0100000000000000.TypeA", null, [], []]), 224([h'02', "A.0100000000000000.TypeB", null, [], []])]])]) // // language=cbor, format=ccf // tag @@ -8177,41 +8863,119 @@ func TestEncodeType(t *testing.T) { // Meta type ID (41) 0x18, 0x29, // tag - 0xd8, ccf.CBORTagReferenceTypeValue, + 0xd8, ccf.CBORTagRestrictedTypeValue, // array, 2 elements follow 0x82, - // authorized - // bool - 0xf4, // tag 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type ID (4) - 0x04, + // AnyStruct type ID (39) + 0x18, 0x27, + // 3 sorted restrictions + // array, 3 element follows + 0x83, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 17 bytes follow + 0x71, + // "I.LocationC.TypeC" + 0x49, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x43, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeA" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x41, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeB" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x42, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType has sorted restrictions and no type ID. + cadence.TypeValue{ + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, + }, }, ) - }) - t.Run("with static function", func(t *testing.T) { + t.Run("without static type", func(t *testing.T) { - testEncodeAndDecode( - t, - cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ - TypeParameters: []cadence.TypeParameter{ - {Name: "T", TypeBound: cadence.AnyStructType{}}, - }, - Parameters: []cadence.Parameter{ - {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, - }, - ReturnType: cadence.IntType{}, - }), - }, + t.Parallel() + + testEncodeAndDecode( + t, + cadence.TypeValue{}, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":{"kind":"AnyStruct"}}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} + // {"type":"Type","value":{"staticType":""}} // // language=edn, format=ccf - // 130([137(41), 193([[["T", 185(39)]], [["qux", "baz", 185(1)]], 185(4)])]) + // 130([137(41), null]) // // language=cbor, format=ccf // tag @@ -8222,60 +8986,30 @@ func TestEncodeType(t *testing.T) { 0xd8, ccf.CBORTagSimpleType, // Meta type ID (41) 0x18, 0x29, - // tag - 0xd8, ccf.CBORTagFunctionTypeValue, - // array, 3 elements follow - 0x83, - // array, 1 elements follow - 0x81, - // array, 2 elements follow - 0x82, - // string, 1 byte follows - 0x61, - // "T" - 0x54, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // AnyStruct type (39) - 0x18, 0x27, - // array, 1 elements follow - 0x81, - // array, 3 elements follow - 0x83, - // string, 3 bytes follow - 0x63, - // qux - 0x71, 0x75, 0x78, - // string, 3 bytes follow - 0x63, - // bax - 0x62, 0x61, 0x7a, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type (1) - 0x01, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type ID (4) - 0x04, + // nil + 0xf6, }, ) }) +} - t.Run("with static Capability", func(t *testing.T) { +func TestEncodeCapability(t *testing.T) { + + t.Parallel() + + t.Run("Capability", func(t *testing.T) { testEncodeAndDecode( t, - cadence.TypeValue{ - StaticType: &cadence.CapabilityType{ - BorrowType: cadence.IntType{}, - }, + cadence.StorageCapability{ + Path: cadence.NewPath("storage", "foo"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), }, []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":{"kind":"Capability", "type" : {"kind" : "Int"}}}} + // {"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":"","address":"0x0000000102030405"},"type":"Capability"} // // language=edn, format=ccf - // 130([137(41), 192([185(4)])]) + // 130([144([null]), [h'0000000102030405', [1, "foo"]]]) // // language=cbor, format=ccf // tag @@ -8283,217 +9017,189 @@ func TestEncodeType(t *testing.T) { // array, 2 elements follow 0x82, // tag - 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, - // tag - 0xd8, ccf.CBORTagCapabilityTypeValue, + 0xd8, ccf.CBORTagCapabilityType, // array, 1 element follows 0x81, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type ID (4) - 0x04, + // null + 0xf6, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, }, ) }) - t.Run("with static no restricted type", func(t *testing.T) { + t.Run("array of Capability", func(t *testing.T) { + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, + } - testEncodeAndDecodeEx( + capability1 := cadence.StorageCapability{ + Path: cadence.NewPath("storage", "foo"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + + capability2 := cadence.StorageCapability{ + Path: cadence.NewPath("storage", "bar"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: simpleStructType, + } + + testEncodeAndDecode( t, - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{}, - Type: cadence.IntType{}, - }), - }, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(nil))), []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{String}","type":{"kind":"Int"},"restrictions":[]}},"type":"Type"} + // {"value":[{"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"},{"value":{"path":{"value":{"domain":"storage","identifier":"bar"},"type":"Path"},"borrowType":{"type":"","kind":"Struct","typeID":"S.test.FooStruct","fields":[{"type":{"kind":"Int"},"id":"bar"}],"initializers":[]},"address":"0x0000000102030405"},"type":"Capability"}],"type":"Array"} // // language=edn, format=ccf - // 130([137(41), 191([185(4), []])]) + // 129([[160([h'', "S.test.FooStruct", [["bar", 137(4)]]])], [139(144([null])), [130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]), 130([144([136(h'')]), [h'0000000102030405', [1, "bar"]]])]]]) // // language=cbor, format=ccf // tag - 0xd8, ccf.CBORTagTypeAndValue, - // array, 2 elements follow + 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.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, - // tag - 0xd8, ccf.CBORTagRestrictedTypeValue, - // array, 2 elements follow + 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.CBORTagSimpleTypeValue, + 0xd8, ccf.CBORTagSimpleType, // Int type ID (4) 0x04, - // array, 0 element follows - 0x80, - }, - // Expected decoded RestrictedType doesn't have type ID. - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{}, - Type: cadence.IntType{}, - }), - }, - ) - }) - - t.Run("with static restricted type", func(t *testing.T) { - testEncodeAndDecodeEx( - t, - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{ - cadence.StringType{}, - }, - Type: cadence.IntType{}, - }), - }, - []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } - // - // language=edn, format=ccf - // 130([137(41), 191([185(4), [185(1)]])]) - // - // language=cbor, format=ccf - // tag - 0xd8, ccf.CBORTagTypeAndValue, // array, 2 elements follow 0x82, // tag - 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, + 0xd8, ccf.CBORTagVarsizedArrayType, // tag - 0xd8, ccf.CBORTagRestrictedTypeValue, + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // null + 0xf6, // array, 2 elements follow 0x82, // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type ID (4) - 0x04, - // array, 1 element follows + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow 0x81, // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type ID (1) + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 0x01, - }, - // Expected decoded RestrictedType doesn't have type ID. - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{ - cadence.StringType{}, - }, - Type: cadence.IntType{}, - }), - }, - ) - - }) - - t.Run("with static 2 restricted types", func(t *testing.T) { + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, - testEncodeAndDecodeEx( - t, - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{ - cadence.NewAnyStructType(), - cadence.StringType{}, - }, - Type: cadence.IntType{}, - }), - }, - []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} - // - // language=edn, format=ccf - // 130([137(41), 191([185(4), [185(1), 185(39)]])]) - // - // language=cbor, format=ccf // tag 0xd8, ccf.CBORTagTypeAndValue, // array, 2 elements follow 0x82, // tag - 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow + 0x81, // tag - 0xd8, ccf.CBORTagRestrictedTypeValue, + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // array, 2 elements follow 0x82, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // Int type ID (4) - 0x04, - // array, 2 element follows - 0x82, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // String type ID (1) + // 1 0x01, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // AnyStruct type ID (39) - 0x18, 0x27, - }, - // Expected decoded RestrictedType has sorted restrictions and no type ID. - cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ - Restrictions: []cadence.Type{ - cadence.StringType{}, - cadence.NewAnyStructType(), - }, - Type: cadence.IntType{}, - }), + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, }, ) }) - t.Run("with static 3 restricted types", func(t *testing.T) { - - // restrictedType is generated by fuzzer. - testEncodeAndDecodeEx( + t.Run("Capability", func(t *testing.T) { + testEncodeAndDecode( t, - cadence.TypeValue{ - StaticType: &cadence.RestrictedType{ - Type: cadence.TheAnyStructType, - Restrictions: []cadence.Type{ - cadence.NewStructInterfaceType( - common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), - "TypeA", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - cadence.NewStructInterfaceType( - common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), - "TypeB", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - cadence.NewStructInterfaceType( - common.IdentifierLocation("LocationC"), - "TypeC", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - }, - }, + cadence.StorageCapability{ + Path: cadence.NewPath("storage", "foo"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, }, []byte{ // language=json, format=json-cdc - // {"value":{"staticType":{"kind":"Restriction","typeID":"","type":{"kind":"AnyStruct"},"restrictions":[{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeA","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeB","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"I.LocationC.TypeC","fields":[],"initializers":[]}]}},"type":"Type"} + // {"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"}} // // language=edn, format=ccf - // 130([137(41), 191([185(39), [224([h'', "I.LocationC.TypeC", null, [], []]), 224([h'01', "A.0100000000000000.TypeA", null, [], []]), 224([h'02', "A.0100000000000000.TypeB", null, [], []])]])]) + // 130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]) // // language=cbor, format=ccf // tag @@ -8501,123 +9207,55 @@ func TestEncodeType(t *testing.T) { // array, 2 elements follow 0x82, // tag - 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, // tag - 0xd8, ccf.CBORTagRestrictedTypeValue, + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // array, 2 elements follow 0x82, - // tag - 0xd8, ccf.CBORTagSimpleTypeValue, - // AnyStruct type ID (39) - 0x18, 0x27, - // 3 sorted restrictions - // array, 3 element follows - 0x83, - // tag - 0xd8, ccf.CBORTagStructInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // CCF type ID - // bytes, 0 byte follows - 0x40, - // cadence type ID - // text, 17 bytes follow - 0x71, - // "I.LocationC.TypeC" - 0x49, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x43, - // type - // null - 0xf6, - // array, 0 element follows - 0x80, - // array, 0 element follows - 0x80, - // tag - 0xd8, ccf.CBORTagStructInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // CCF type ID - // bytes, 1 byte follows - 0x41, // 1 0x01, - // cadence type ID - // text, 24 bytes follow - 0x78, 0x18, - // "A.0100000000000000.TypeA" - 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x41, - // type - // null - 0xf6, - // array, 0 element follows - 0x80, - // array, 0 element follows - 0x80, - // tag - 0xd8, ccf.CBORTagStructInterfaceTypeValue, - // array, 5 elements follow - 0x85, - // CCF type ID - // bytes, 1 byte follows - 0x41, - // 2 - 0x02, - // cadence type ID - // text, 24 bytes follow - 0x78, 0x18, - // "A.0100000000000000.TypeB" - 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x42, - // type - // null - 0xf6, - // array, 0 element follows - 0x80, - // array, 0 element follows - 0x80, - }, - // Expected decoded RestrictedType has sorted restrictions and no type ID. - cadence.TypeValue{ - StaticType: &cadence.RestrictedType{ - Type: cadence.TheAnyStructType, - Restrictions: []cadence.Type{ - cadence.NewStructInterfaceType( - common.IdentifierLocation("LocationC"), - "TypeC", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - cadence.NewStructInterfaceType( - common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), - "TypeA", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - cadence.NewStructInterfaceType( - common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), - "TypeB", - []cadence.Field{}, - [][]cadence.Parameter{}, - ), - }, - }, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, }, ) }) - t.Run("without static type", func(t *testing.T) { - - t.Parallel() + t.Run("array of Capability", func(t *testing.T) { + capability1 := cadence.StorageCapability{ + Path: cadence.NewPath("storage", "foo"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + capability2 := cadence.StorageCapability{ + Path: cadence.NewPath("storage", "bar"), + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } testEncodeAndDecode( t, - cadence.TypeValue{}, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(cadence.NewIntType()))), []byte{ // language=json, format=json-cdc - // {"type":"Type","value":{"staticType":""}} + // {"value":[{"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"},{"value":{"path":{"value":{"domain":"storage","identifier":"bar"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"}],"type":"Array"} // // language=edn, format=ccf - // 130([137(41), null]) + // 130([139(144([137(4)])), [[h'0000000102030405', [1, "foo"]], [h'0000000102030405', [1, "bar"]]]]) // // language=cbor, format=ccf // tag @@ -8625,65 +9263,52 @@ func TestEncodeType(t *testing.T) { // array, 2 elements follow 0x82, // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // tag 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, - // nil - 0xf6, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, }, ) }) } -func TestEncodeCapability(t *testing.T) { - - t.Parallel() - - testEncodeAndDecode( - t, - cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), - Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), - BorrowType: cadence.IntType{}, - }, - []byte{ // language=json, format=json-cdc - // {"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"}} - // - // language=edn, format=ccf - // 130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]) - // - // 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 folloow - 0x48, - // {1,2,3,4,5} - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - // array, 2 elements follow - 0x82, - // 1 - 0x01, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - }, - ) -} - func TestDecodeFix64(t *testing.T) { t.Parallel() diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 853c55b2ae..965e0f5368 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -369,10 +369,10 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca return d.decodePath() case cadence.MetaType: - return d.decodeTypeValue() + return d.decodeNullableTypeValue() case *cadence.CapabilityType: - return d.decodeCapability(typ) + return d.decodeCapability(typ, types) case *cadence.EnumType: return d.decodeEnum(typ, types) @@ -1150,9 +1150,28 @@ func (d *Decoder) decodePath() (cadence.Value, error) { // path: path-value // // ] -func (d *Decoder) decodeCapability(typ *cadence.CapabilityType) (cadence.Value, error) { +func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // typ can be different from runtime CapabilityType because borrow type can be nil. + // In this case, runtime type is encoded with the value (as tag content for tag CBORTagTypeAndValue). + + // Check next encoded CBOR type. + nextType, err := d.dec.NextType() + if err != nil { + return nil, err + } + + if nextType == cbor.TagType { + err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) + if err != nil { + return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", typ.ID(), typ, err.Error()) + } + + // Decode ccf-type-and-value-message. + return d.decodeTypeAndValue(types) + } + // Decode array head of length 2. - err := decodeCBORArrayWithKnownSize(d.dec, 2) + err = decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { return nil, err } @@ -1176,10 +1195,10 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType) (cadence.Value, typ.BorrowType), nil } -// decodeTypeValue decodes encoded type-value. -// See _decodeTypeValue() for details. -func (d *Decoder) decodeTypeValue() (cadence.Value, error) { - t, err := d._decodeTypeValue(newCadenceTypeByCCFTypeID()) +// decodeNullableTypeValue decodes encoded type-value / nil. +// See _decodeNullableTypeValue() for details. +func (d *Decoder) decodeNullableTypeValue() (cadence.Value, error) { + t, err := d._decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) if err != nil { return nil, err } @@ -1188,10 +1207,8 @@ func (d *Decoder) decodeTypeValue() (cadence.Value, error) { // _decodeTypeValue decodes encoded type-value as // language=CDDL -// type-value = +// type-value = simple-type-value // -// nil -// / simple-type-value // / optional-type-value // / varsized-array-type-value // / constsized-array-type-value @@ -1213,11 +1230,6 @@ func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Typ // Decode tag number. tagNum, err := d.dec.DecodeTagNumber() if err != nil { - if _, ok := err.(*cbor.WrongTypeError); ok { - // Decode nil. - err = d.dec.DecodeNil() - return nil, err - } return nil, err } @@ -1242,13 +1254,13 @@ func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Typ return d.decodeDictType(visited, d._decodeTypeValue) case CBORTagCapabilityTypeValue: - return d.decodeCapabilityType(visited, d._decodeTypeValue) + return d.decodeCapabilityType(visited, d._decodeNullableTypeValue) case CBORTagReferenceTypeValue: return d.decodeReferenceType(visited, d._decodeTypeValue) case CBORTagRestrictedTypeValue: - return d.decodeRestrictedType(visited, d._decodeTypeValue) + return d.decodeRestrictedType(visited, d._decodeNullableTypeValue, d._decodeTypeValue) case CBORTagFunctionTypeValue: return d.decodeFunctionTypeValue(visited) @@ -1282,6 +1294,19 @@ func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Typ } } +// _decodeNullableTypeValue decodes encoded type-value or nil. +func (d *Decoder) _decodeNullableTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + cborType, err := d.dec.NextType() + if err != nil { + return nil, err + } + if cborType == cbor.NilType { + err = d.dec.DecodeNil() + return nil, err + } + return d._decodeTypeValue(visited) +} + // decodeStructTypeValue decodes struct-type-value as // language=CDDL // struct-type-value = @@ -1679,7 +1704,7 @@ func (d *Decoder) _decodeCompositeTypeValue(visited *cadenceTypeByCCFTypeID) (*c } // element 2: type (only used by enum type value) - typ, err := d._decodeTypeValue(visited) + typ, err := d._decodeNullableTypeValue(visited) if err != nil { return nil, err } @@ -1747,7 +1772,7 @@ func (d *Decoder) decodeInitializerTypeValues(visited *cadenceTypeByCCFTypeID) ( // type-parameters: [ // * [ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] // ] func (d *Decoder) decodeTypeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([]cadence.TypeParameter, error) { @@ -1795,7 +1820,7 @@ func (d *Decoder) decodeTypeParameterTypeValues(visited *cadenceTypeByCCFTypeID) // // [ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.TypeParameter, error) { // Decode array head of length 2 @@ -1811,7 +1836,7 @@ func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) } // element 2: type - t, err := d._decodeTypeValue(visited) + t, err := d._decodeNullableTypeValue(visited) if err != nil { return cadence.TypeParameter{}, err } @@ -1927,7 +1952,7 @@ func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cad // type-parameters: [ // * [ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] // ] // parameters: [ diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 29334c93f2..6ab8e674bf 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -22,6 +22,8 @@ import ( "errors" "fmt" + "github.com/fxamacker/cbor/v2" + "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" @@ -73,10 +75,10 @@ func (d *Decoder) decodeInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, return d.decodeReferenceType(types, d.decodeInlineType) case CBORTagRestrictedType: - return d.decodeRestrictedType(types, d.decodeInlineType) + return d.decodeRestrictedType(types, d.decodeNullableInlineType, d.decodeInlineType) case CBORTagCapabilityType: - return d.decodeCapabilityType(types, d.decodeInlineType) + return d.decodeCapabilityType(types, d.decodeNullableInlineType) case CBORTagTypeRef: return d.decodeTypeRef(types) @@ -86,6 +88,19 @@ func (d *Decoder) decodeInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, } } +// decodeNullableInlineType decodes encoded inline-type or nil. +func (d *Decoder) decodeNullableInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { + cborType, err := d.dec.NextType() + if err != nil { + return nil, err + } + if cborType == cbor.NilType { + err = d.dec.DecodeNil() + return nil, err + } + return d.decodeInlineType(types) +} + // decodeSimpleTypeID decodes encoded simple-type-id. // See CCF specification for complete list of simple-type-id. func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { @@ -417,7 +432,7 @@ func (d *Decoder) decodeDictType( // ; use an array as an extension point // #6.144([ // ; borrow-type -// inline-type +// inline-type / nil // ]) // // capability-type-value = @@ -426,7 +441,7 @@ func (d *Decoder) decodeDictType( // ; use an array as an extension point // #6.192([ // ; borrow-type -// type-value +// type-value / nil // ]) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. @@ -446,10 +461,6 @@ func (d *Decoder) decodeCapabilityType( return nil, err } - if borrowType == nil { - return nil, errors.New("unexpected nil type as capability borrow type") - } - return cadence.NewMeteredCapabilityType(d.gauge, borrowType), nil } @@ -507,7 +518,7 @@ func (d *Decoder) decodeReferenceType( // // ; cbor-tag-restricted-type // #6.143([ -// type: inline-type, +// type: inline-type / nil, // restrictions: [* inline-type] // ]) // @@ -515,7 +526,7 @@ func (d *Decoder) decodeReferenceType( // // ; cbor-tag-restricted-type-value // #6.191([ -// type: type-value, +// type: type-value / nil, // restrictions: [* type-value] // ]) // @@ -523,6 +534,7 @@ func (d *Decoder) decodeReferenceType( func (d *Decoder) decodeRestrictedType( types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, + decodeRestrictionTypeFn decodeTypeFn, ) (cadence.Type, error) { // Decode array of length 2. err := decodeCBORArrayWithKnownSize(d.dec, 2) @@ -536,10 +548,6 @@ func (d *Decoder) decodeRestrictedType( return nil, err } - if typ == nil { - return nil, errors.New("unexpected nil type as restricted type") - } - // element 1: restrictions restrictionCount, err := d.dec.DecodeArrayHead() if err != nil { @@ -552,7 +560,7 @@ func (d *Decoder) decodeRestrictedType( restrictions := make([]cadence.Type, restrictionCount) for i := 0; i < int(restrictionCount); i++ { // Decode restriction. - restrictedType, err := decodeTypeFn(types) + restrictedType, err := decodeRestrictionTypeFn(types) if err != nil { return nil, err } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 2b12b2e73b..3437287bde 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -482,7 +482,9 @@ func (e *Encoder) encodeValue( // it is only encoded once and is subsequently represented by its CCF ID. // For type value encoding, CCF type ID is sequentially generated by // traversal order. - return e.encodeTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) + // + // If x.StaticType is nil, type value is encoded as nil. + return e.encodeNullableTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) case cadence.StorageCapability: return e.encodeCapability(x) @@ -1001,7 +1003,7 @@ func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { // type-parameters: [ // * [ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] // ] // parameters: [ @@ -1044,8 +1046,7 @@ func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByC // language=CDDL // type-value = // -// nil -// / simple-type-value +// simple-type-value // / optional-type-value // / varsized-array-type-value // / constsized-array-type-value @@ -1136,14 +1137,19 @@ func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceTy case *cadence.EnumType: return e.encodeEnumTypeValue(typ, visited) - case nil: - return e.encodeNilTypeValue() - default: panic(cadenceErrors.NewUnexpectedError("unsupported type value %s (%T)", typ.ID(), typ)) } } +// encodeNullableTypeValue encodes cadence.Type as type-value or nil. +func (e *Encoder) encodeNullableTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceType) error { + if typ == nil { + return e.encodeNilTypeValue() + } + return e.encodeTypeValue(typ, visited) +} + // encodeTypeValueRef encodes type value ID as // language=CDDL // type-value-ref = @@ -1261,7 +1267,7 @@ func (e *Encoder) encodeReferenceTypeValue(typ *cadence.ReferenceType, visited c // // ; cbor-tag-restricted-type-value // #6.191([ -// type: type-value, +// type: type-value / nil, // restrictions: [* type-value] // ]) func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited ccfTypeIDByCadenceType) error { @@ -1269,6 +1275,7 @@ func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited return e.encodeRestrictedTypeWithRawTag( typ, visited, + e.encodeNullableTypeValue, e.encodeTypeValue, rawTagNum, ) @@ -1282,14 +1289,14 @@ func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited // ; use an array as an extension point // #6.192([ // ; borrow-type -// type-value +// type-value / nil // ]) func (e *Encoder) encodeCapabilityTypeValue(typ *cadence.CapabilityType, visited ccfTypeIDByCadenceType) error { rawTagNum := []byte{0xd8, CBORTagCapabilityTypeValue} return e.encodeCapabilityTypeWithRawTag( typ, visited, - e.encodeTypeValue, + e.encodeNullableTypeValue, rawTagNum, ) } @@ -1503,11 +1510,7 @@ func (e *Encoder) encodeCompositeTypeValue( // element 2: type as nil or type-value. // Type is only used by enum type value. - if typ == nil { - err = e.enc.EncodeNil() - } else { - err = e.encodeTypeValue(typ, visited) - } + err = e.encodeNullableTypeValue(typ, visited) if err != nil { return err } @@ -1639,7 +1642,7 @@ func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Param // // *[ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] // // ] @@ -1666,7 +1669,7 @@ func (e *Encoder) encodeTypeParameterTypeValues(typeParameters []cadence.TypePar // // [ // name: tstr, -// type-bound: type-value +// type-bound: type-value / nil // ] func (e *Encoder) encodeTypeParameterTypeValue(typeParameter cadence.TypeParameter, visited ccfTypeIDByCadenceType) error { // Encode CBOR array head with length 2 @@ -1685,7 +1688,7 @@ func (e *Encoder) encodeTypeParameterTypeValue(typeParameter cadence.TypeParamet } // element 1: type as type-bound. - return e.encodeTypeValue(typeParameter.TypeBound, visited) + return e.encodeNullableTypeValue(typeParameter.TypeBound, visited) } // encodeParameterTypeValues encodes composite initializer parameter types as diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index e1abc5f3ba..5b800d55d1 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -86,6 +86,13 @@ func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType } } +func (e *Encoder) encodeNullableInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType) error { + if typ == nil { + return e.enc.EncodeNil() + } + return e.encodeInlineType(typ, tids) +} + // encodeSimpleType encodes cadence simple type as // language=CDDL // simple-type = @@ -347,7 +354,7 @@ func (e *Encoder) encodeReferenceTypeWithRawTag( // // ; cbor-tag-restricted-type // #6.143([ -// type: inline-type, +// type: inline-type / nil, // restrictions: [* inline-type] // ]) func (e *Encoder) encodeRestrictedType(typ *cadence.RestrictedType, tids ccfTypeIDByCadenceType) error { @@ -355,6 +362,7 @@ func (e *Encoder) encodeRestrictedType(typ *cadence.RestrictedType, tids ccfType return e.encodeRestrictedTypeWithRawTag( typ, tids, + e.encodeNullableInlineType, e.encodeInlineType, rawTagNum, ) @@ -366,6 +374,7 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( typ *cadence.RestrictedType, tids ccfTypeIDByCadenceType, encodeTypeFn encodeTypeFn, + encodeRestrictionTypeFn encodeTypeFn, rawTagNumber []byte, ) error { // Encode CBOR tag number. @@ -416,7 +425,7 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( for _, index := range sorter.indexes { // Encode restriction type with given encodeTypeFn. - err = encodeTypeFn(restrictions[index], tids) + err = encodeRestrictionTypeFn(restrictions[index], tids) if err != nil { return err } @@ -434,7 +443,7 @@ func (e *Encoder) encodeRestrictedTypeWithRawTag( // ; use an array as an extension point // #6.144([ // ; borrow-type -// inline-type +// inline-type / nil // ]) func (e *Encoder) encodeCapabilityType( typ *cadence.CapabilityType, @@ -444,7 +453,7 @@ func (e *Encoder) encodeCapabilityType( return e.encodeCapabilityTypeWithRawTag( typ, tids, - e.encodeInlineType, + e.encodeNullableInlineType, rawTagNum, ) } diff --git a/types.go b/types.go index d604d816cc..0b5842d9a8 100644 --- a/types.go +++ b/types.go @@ -1908,7 +1908,11 @@ func (t *RestrictedType) ID() string { restrictionStrings = append(restrictionStrings, restriction.ID()) } } - t.typeID = sema.FormatRestrictedTypeID(t.Type.ID(), restrictionStrings) + var typeString string + if t.Type != nil { + typeString = t.Type.ID() + } + t.typeID = sema.FormatRestrictedTypeID(typeString, restrictionStrings) } return t.typeID } @@ -1919,7 +1923,13 @@ func (t *RestrictedType) Equal(other Type) bool { return false } - if !t.Type.Equal(otherType.Type) { + if t.Type == nil && otherType.Type != nil { + return false + } + if t.Type != nil && otherType.Type == nil { + return false + } + if t.Type != nil && !t.Type.Equal(otherType.Type) { return false } From ac8154e2db36130503b99226779d5b141faa5819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 14:57:47 -0700 Subject: [PATCH 116/173] optimize Address type member resolvers --- runtime/sema/type.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 05be7698c3..7ee49267d9 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5062,20 +5062,15 @@ func (t *AddressType) GetMembers() map[string]MemberResolver { func (t *AddressType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - t.memberResolvers = withBuiltinMembers(t, map[string]MemberResolver{ - AddressTypeToBytesFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - AddressTypeToBytesFunctionType, - addressTypeToBytesFunctionDocString, - ) - }, - }, + memberResolvers := MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + AddressTypeToBytesFunctionName, + AddressTypeToBytesFunctionType, + addressTypeToBytesFunctionDocString, + ), }) + t.memberResolvers = withBuiltinMembers(t, memberResolvers) }) } From 63237bff58ef05113fba8c8e67973e5430d5f3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 14:58:11 -0700 Subject: [PATCH 117/173] optimize restricted type member resolvers --- runtime/sema/type.go | 80 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7ee49267d9..d85bbb0eb1 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6155,54 +6155,66 @@ func (t *RestrictedType) RewriteWithRestrictedTypes() (Type, bool) { } func (t *RestrictedType) GetMembers() map[string]MemberResolver { + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *RestrictedType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { - members := map[string]MemberResolver{} + memberResolvers := map[string]MemberResolver{} - // Return the members of all restrictions. - // The invariant that restrictions may not have overlapping members is not checked here, - // but implicitly when the resource declaration's conformances are checked. + // Return the members of all restrictions. + // The invariant that restrictions may not have overlapping members is not checked here, + // but implicitly when the resource declaration's conformances are checked. - for _, restriction := range t.Restrictions { - for name, resolver := range restriction.GetMembers() { //nolint:maprange - if _, ok := members[name]; !ok { - members[name] = resolver + for _, restriction := range t.Restrictions { + for name, resolver := range restriction.GetMembers() { //nolint:maprange + if _, ok := memberResolvers[name]; !ok { + memberResolvers[name] = resolver + } } } - } - // Also include members of the restricted type for convenience, - // to help check the rest of the program and improve the developer experience, - // *but* also report an error that this access is invalid when the entry is resolved. - // - // The restricted type may be `AnyResource`, in which case there are no members. + // Also include members of the restricted type for convenience, + // to help check the rest of the program and improve the developer experience, + // *but* also report an error that this access is invalid when the entry is resolved. + // + // The restricted type may be `AnyResource`, in which case there are no members. - for name, loopResolver := range t.Type.GetMembers() { //nolint:maprange + for name, loopResolver := range t.Type.GetMembers() { //nolint:maprange - if _, ok := members[name]; ok { - continue - } + if _, ok := memberResolvers[name]; ok { + continue + } - // NOTE: don't capture loop variable - resolver := loopResolver + // NOTE: don't capture loop variable + resolver := loopResolver - members[name] = MemberResolver{ - Kind: resolver.Kind, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { - member := resolver.Resolve(memoryGauge, identifier, targetRange, report) + memberResolvers[name] = MemberResolver{ + Kind: resolver.Kind, + Resolve: func( + memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error), + ) *Member { + member := resolver.Resolve(memoryGauge, identifier, targetRange, report) - report( - &InvalidRestrictedTypeMemberAccessError{ - Name: identifier, - Range: targetRange, - }, - ) + report( + &InvalidRestrictedTypeMemberAccessError{ + Name: identifier, + Range: targetRange, + }, + ) - return member - }, + return member + }, + } } - } - return members + t.memberResolvers = memberResolvers + }) } func (*RestrictedType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { From f62e2620862f5b20cf5947646ace96b4f5b82c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 14:58:22 -0700 Subject: [PATCH 118/173] optimize Capability type member resolvers --- runtime/sema/type.go | 65 +++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index d85bbb0eb1..6f31dc75b2 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5961,9 +5961,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 + restrictionSet *InterfaceSet + Restrictions []*InterfaceType + restrictionSetOnce sync.Once + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ Type = &RestrictedType{} @@ -6517,44 +6519,27 @@ const CapabilityTypeAddressFieldName = "address" func (t *CapabilityType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - t.memberResolvers = withBuiltinMembers(t, map[string]MemberResolver{ - CapabilityTypeBorrowFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityTypeBorrowFunctionType(t.BorrowType), - capabilityTypeBorrowFunctionDocString, - ) - }, - }, - CapabilityTypeCheckFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityTypeCheckFunctionType(t.BorrowType), - capabilityTypeCheckFunctionDocString, - ) - }, - }, - CapabilityTypeAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TheAddressType, - capabilityTypeAddressFieldDocString, - ) - }, - }, + members := MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + CapabilityTypeBorrowFunctionName, + CapabilityTypeBorrowFunctionType(t.BorrowType), + capabilityTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + CapabilityTypeCheckFunctionName, + CapabilityTypeCheckFunctionType(t.BorrowType), + capabilityTypeCheckFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + CapabilityTypeAddressFieldName, + TheAddressType, + capabilityTypeAddressFieldDocString, + ), }) + t.memberResolvers = withBuiltinMembers(t, members) }) } From ecc3efd15a474c420bbf2d1f74b1fec9ca7a94a6 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:14:46 -0500 Subject: [PATCH 119/173] Update handling of atree errors --- go.mod | 2 +- go.sum | 4 ++-- runtime/interpreter/storagemap.go | 11 ++++++++--- runtime/interpreter/value.go | 33 ++++++++++++++++++++----------- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 4c9c09b263..7b00b6698c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f github.com/go-test/deep v1.1.0 github.com/leanovate/gopter v0.2.9 - github.com/onflow/atree v0.5.0 + github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b github.com/rivo/uniseg v0.4.4 github.com/schollz/progressbar/v3 v3.13.1 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 54b15f2013..59ca966de3 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3px github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/onflow/atree v0.5.0 h1:y3lh8hY2fUo8KVE2ALVcz0EiNTq0tXJ6YTXKYVDA+3E= -github.com/onflow/atree v0.5.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= +github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b h1:H6hKC/kqP0EDmmM0fqvO4bfquqpyuvQSoXfuF/jz/jU= +github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/runtime/interpreter/storagemap.go b/runtime/interpreter/storagemap.go index f6e33edc9d..8c329ffb22 100644 --- a/runtime/interpreter/storagemap.go +++ b/runtime/interpreter/storagemap.go @@ -19,6 +19,8 @@ package interpreter import ( + goerrors "errors" + "github.com/onflow/atree" "github.com/onflow/cadence/runtime/common" @@ -71,7 +73,8 @@ func (s StorageMap) ValueExists(key string) bool { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return false } panic(errors.NewExternalError(err)) @@ -89,7 +92,8 @@ func (s StorageMap) ReadValue(gauge common.MemoryGauge, key string) Value { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -139,7 +143,8 @@ func (s StorageMap) RemoveValue(interpreter *Interpreter, key string) { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return } panic(errors.NewExternalError(err)) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index b092e6bf05..3227cb5bc5 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -21,6 +21,7 @@ package interpreter import ( "encoding/binary" "encoding/hex" + goerrors "errors" "fmt" "math" "math/big" @@ -1728,7 +1729,8 @@ func (v *ArrayValue) GetKey(interpreter *Interpreter, locationRange LocationRang } func (v *ArrayValue) handleIndexOutOfBoundsError(err error, index int, locationRange LocationRange) { - if _, ok := err.(*atree.IndexOutOfBoundsError); ok { + var indexOutOfBoundsError *atree.IndexOutOfBoundsError + if goerrors.As(err, &indexOutOfBoundsError) { panic(ArrayIndexOutOfBoundsError{ Index: index, Size: v.Count(), @@ -2629,16 +2631,18 @@ func (v *ArrayValue) Slice( iterator, err := v.array.RangeIterator(uint64(fromIndex), uint64(toIndex)) if err != nil { - switch err.(type) { - case *atree.SliceOutOfBoundsError: + var sliceOutOfBoundsError *atree.SliceOutOfBoundsError + if goerrors.As(err, &sliceOutOfBoundsError) { panic(ArraySliceIndicesError{ FromIndex: fromIndex, UpToIndex: toIndex, Size: v.Count(), LocationRange: locationRange, }) + } - case *atree.InvalidSliceIndexError: + var invalidSliceIndexError *atree.InvalidSliceIndexError + if goerrors.As(err, &invalidSliceIndexError) { panic(InvalidSliceIndexError{ FromIndex: fromIndex, UpToIndex: toIndex, @@ -14185,7 +14189,8 @@ func (v *CompositeValue) GetMember(interpreter *Interpreter, locationRange Locat StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); !ok { + var keyNotFoundError *atree.KeyNotFoundError + if !goerrors.As(err, &keyNotFoundError) { panic(errors.NewExternalError(err)) } } @@ -14328,7 +14333,8 @@ func (v *CompositeValue) RemoveMember( StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -14506,7 +14512,8 @@ func (v *CompositeValue) GetField(interpreter *Interpreter, locationRange Locati StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -15061,7 +15068,8 @@ func (v *CompositeValue) RemoveField( StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return } panic(errors.NewExternalError(err)) @@ -15569,7 +15577,8 @@ func (v *DictionaryValue) ContainsKey( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return FalseValue } panic(errors.NewExternalError(err)) @@ -15593,7 +15602,8 @@ func (v *DictionaryValue) Get( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil, false } panic(errors.NewExternalError(err)) @@ -15918,7 +15928,8 @@ func (v *DictionaryValue) Remove( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return NilOptionalValue } panic(errors.NewExternalError(err)) From c6699892872e66820cf5fc9e6712b52d9ec9d8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 15:26:12 -0700 Subject: [PATCH 120/173] improve type of cadence.Path.domain, ensure it is not unknown --- encoding/json/decode.go | 20 ++---- encoding/json/encode.go | 2 +- encoding/json/encoding_test.go | 68 ++++++++++++++++--- runtime/account_test.go | 15 +++- runtime/convertValues.go | 42 ++++++------ runtime/convertValues_test.go | 42 ++++++------ .../imported_values_memory_metering_test.go | 14 ++-- runtime/literal.go | 22 ++++-- runtime/literal_test.go | 17 ++--- runtime/runtime_test.go | 6 +- runtime/storage_test.go | 12 ++-- values.go | 14 ++-- values_test.go | 12 ++-- 13 files changed, 182 insertions(+), 104 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index e6492216a3..8fab5938a1 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -826,26 +826,20 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum { func (d *Decoder) decodePath(valueJSON any) cadence.Path { obj := toObject(valueJSON) - domain := obj.GetString(domainKey) - - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(domain)), - }) + domain := common.PathDomainFromIdentifier(obj.GetString(domainKey)) identifier := obj.GetString(identifierKey) - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(identifier)), - }) + common.UseMemory(d.gauge, common.NewRawStringMemoryUsage(len(identifier))) - return cadence.NewMeteredPath( + path, err := cadence.NewMeteredPath( d.gauge, domain, identifier, ) + if err != nil { + panic(errors.NewDefaultUserError("failed to decode path: %w", err)) + } + return path } func (d *Decoder) decodeTypeParameter(valueJSON any, results typeDecodingResults) cadence.TypeParameter { diff --git a/encoding/json/encode.go b/encoding/json/encode.go index dc46bcf7fe..8bfa47c34f 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -627,7 +627,7 @@ func preparePath(x cadence.Path) jsonValue { return jsonValueObject{ Type: pathTypeStr, Value: jsonPathValue{ - Domain: x.Domain, + Domain: x.Domain.Identifier(), Identifier: x.Identifier, }, } diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 419bb9acb9..72b709ce0a 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/tests/checker" @@ -1652,7 +1653,10 @@ func TestEncodePathLink(t *testing.T) { testEncode( t, cadence.NewPathLink( - cadence.NewPath("storage", "foo"), + cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, "Bar", ), // language=json @@ -2526,7 +2530,10 @@ func TestEncodeCapability(t *testing.T) { testEncodeAndDecode( t, cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, }, @@ -2950,12 +2957,57 @@ func TestEncodePath(t *testing.T) { t.Parallel() - testEncodeAndDecode( - t, - cadence.NewPath("storage", "foo"), - // language=json - `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, - ) + t.Run("storage", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, + ) + }) + + t.Run("private", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"private","identifier":"foo"}}`, + ) + }) + + t.Run("public", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainPublic, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"public","identifier":"foo"}}`, + ) + }) + + t.Run("invalid", func(t *testing.T) { + t.Parallel() + + _, err := json.Decode(nil, []byte( + // language=json + `{"type":"Path","value":{"domain":"Storage","identifier":"foo"}}`, + )) + require.ErrorContains(t, err, "unknown domain in path") + }) } func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { diff --git a/runtime/account_test.go b/runtime/account_test.go index 7072fee3d7..0273f8636e 100644 --- a/runtime/account_test.go +++ b/runtime/account_test.go @@ -2645,7 +2645,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) @@ -2886,7 +2889,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) @@ -2993,7 +2999,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 51018e2a56..31264f8266 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -212,15 +212,15 @@ func exportValueWithInterpreter( case interpreter.AddressValue: return cadence.NewMeteredAddress(inter, v), nil case interpreter.PathLinkValue: - return exportPathLinkValue(v, inter), nil + return exportPathLinkValue(v, inter) case interpreter.AccountLinkValue: return exportAccountLinkValue(inter), nil case interpreter.PathValue: - return exportPathValue(inter, v), nil + return exportPathValue(inter, v) case interpreter.TypeValue: return exportTypeValue(v, inter), nil case *interpreter.StorageCapabilityValue: - return exportStorageCapabilityValue(v, inter), nil + return exportStorageCapabilityValue(v, inter) case *interpreter.EphemeralReferenceValue: // Break recursion through references if _, ok := seenReferences[v]; ok { @@ -594,27 +594,23 @@ func exportDictionaryValue( return dictionary.WithType(exportType), err } -func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) cadence.PathLink { - path := exportPathValue(inter, v.TargetPath) +func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) (cadence.PathLink, error) { + path, err := exportPathValue(inter, v.TargetPath) + if err != nil { + return cadence.PathLink{}, err + } ty := string(inter.MustConvertStaticToSemaType(v.Type).ID()) - return cadence.NewMeteredPathLink(inter, path, ty) + return cadence.NewMeteredPathLink(inter, path, ty), nil } func exportAccountLinkValue(inter *interpreter.Interpreter) cadence.AccountLink { return cadence.NewMeteredAccountLink(inter) } -func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) cadence.Path { - domain := v.Domain.Identifier() - common.UseMemory(gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(domain)), - }) - +func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) (cadence.Path, error) { return cadence.NewMeteredPath( gauge, - domain, + v.Domain, v.Identifier, ) } @@ -630,18 +626,26 @@ 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, error) { var borrowType sema.Type if v.BorrowType != nil { borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) } + path, err := exportPathValue(inter, v.Path) + if err != nil { + return cadence.StorageCapability{}, err + } + return cadence.NewMeteredStorageCapability( inter, - exportPathValue(inter, v.Path), + path, cadence.NewMeteredAddress(inter, v.Address), ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), - ) + ), nil } // exportEvent converts a runtime event to its native Go representation. @@ -1059,7 +1063,7 @@ func (i valueImporter) importPathValue(v cadence.Path) interpreter.PathValue { return interpreter.NewPathValue( inter, - common.PathDomainFromIdentifier(v.Domain), + v.Domain, v.Identifier, ) } diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 495807409b..d89956fd37 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -383,7 +383,7 @@ func TestExportValue(t *testing.T) { Identifier: "foo", }, expected: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, }, @@ -813,7 +813,7 @@ func TestImportValue(t *testing.T) { { label: "Path", value: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, expected: interpreter.PathValue{ @@ -825,7 +825,7 @@ func TestImportValue(t *testing.T) { label: "Path Link (invalid)", value: cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, BorrowType: "Int", @@ -841,7 +841,7 @@ func TestImportValue(t *testing.T) { label: "Capability (invalid)", value: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "test", }, BorrowType: cadence.IntType{}, @@ -2081,7 +2081,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2135,7 +2135,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2169,7 +2169,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2203,7 +2203,7 @@ func TestExportPathLinkValue(t *testing.T) { expected := cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, BorrowType: "Int", @@ -2254,7 +2254,7 @@ func TestExportPathLinkValue(t *testing.T) { expected := cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, BorrowType: "S.test.S", @@ -2684,7 +2684,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "StoragePath", typeSignature: "StoragePath", exportedValue: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, skipExport: true, @@ -2693,7 +2693,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "PrivatePath", typeSignature: "PrivatePath", exportedValue: cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, skipExport: true, @@ -2702,7 +2702,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "PublicPath", typeSignature: "PublicPath", exportedValue: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, skipExport: true, @@ -2849,15 +2849,15 @@ func TestRuntimeComplexStructArgumentPassing(t *testing.T) { cadence.NewAddress([8]byte{0, 0, 0, 0, 0, 1, 0, 2}), cadence.NewBool(true), cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, cadence.String("foo"), @@ -2980,7 +2980,7 @@ func TestRuntimeComplexStructWithAnyStructFields(t *testing.T) { Size: 2, }), cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, }, @@ -4015,7 +4015,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } @@ -4069,7 +4069,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: cadence.IntType{}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } @@ -4116,7 +4116,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPrivate.Identifier(), + Domain: common.PathDomainPrivate, Identifier: "foo", }, } @@ -4163,7 +4163,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainStorage.Identifier(), + Domain: common.PathDomainStorage, Identifier: "foo", }, } @@ -4219,7 +4219,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: borrowType, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index d527152c86..7349b3c08f 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -419,16 +419,16 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { MemoryKind: common.MemoryKindPathValue, Weight: 1, TypeInstance: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "id3", }, }, { TypeName: "Path", MemoryKind: common.MemoryKindRawString, - Weight: 3 + 1 + 10, + Weight: (1 + 3) + (1 + 3), TypeInstance: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "id3", }, }, @@ -440,7 +440,7 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { Weight: 1, TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, @@ -456,7 +456,7 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { Weight: 1, TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, @@ -469,10 +469,10 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { { TypeName: "Capability", MemoryKind: common.MemoryKindRawString, - Weight: 13 + 1 + 19, + Weight: (1 + 13) + (1 + 13), TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, diff --git a/runtime/literal.go b/runtime/literal.go index 99e95aa891..ba9f87e9c0 100644 --- a/runtime/literal.go +++ b/runtime/literal.go @@ -126,15 +126,25 @@ func arrayLiteralValue(inter *interpreter.Interpreter, elements []ast.Expression }) } -func pathLiteralValue(memoryGauge common.MemoryGauge, expression ast.Expression, ty sema.Type) (result cadence.Value, errResult error) { +func pathLiteralValue( + memoryGauge common.MemoryGauge, + expression ast.Expression, + ty sema.Type, +) ( + cadence.Value, + error, +) { pathExpression, ok := expression.(*ast.PathExpression) if !ok { return nil, LiteralExpressionTypeError } + pathDomain := pathExpression.Domain.Identifier + pathIdentifier := pathExpression.Identifier.Identifier + pathType, err := sema.CheckPathLiteral( - pathExpression.Domain.Identifier, - pathExpression.Identifier.Identifier, + pathDomain, + pathIdentifier, func() ast.Range { return ast.NewRangeFromPositioned(memoryGauge, pathExpression.Domain) }, @@ -155,9 +165,9 @@ func pathLiteralValue(memoryGauge common.MemoryGauge, expression ast.Expression, return cadence.NewMeteredPath( memoryGauge, - pathExpression.Domain.Identifier, - pathExpression.Identifier.Identifier, - ), nil + common.PathDomainFromIdentifier(pathDomain), + pathIdentifier, + ) } func integerLiteralValue( diff --git a/runtime/literal_test.go b/runtime/literal_test.go index ff4349aa83..9c86ce73fc 100644 --- a/runtime/literal_test.go +++ b/runtime/literal_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" . "github.com/onflow/cadence/runtime/tests/utils" ) @@ -240,7 +241,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, value, @@ -256,7 +257,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, @@ -272,7 +273,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -299,7 +300,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, value, @@ -348,7 +349,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, @@ -360,7 +361,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -386,7 +387,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -419,7 +420,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index c6ecec9733..b83c2d4f84 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -7025,7 +7025,7 @@ func TestRuntimeGetCapability(t *testing.T) { cadence.StorageCapability{ Address: cadence.BytesToAddress([]byte{0x1}), Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "xxx", }, }, @@ -7355,7 +7355,7 @@ func TestRuntimeInternalErrors(t *testing.T) { _, err = runtime.ReadStored( address, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ @@ -7388,7 +7388,7 @@ func TestRuntimeInternalErrors(t *testing.T) { _, err = runtime.ReadLinked( address, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 72b1e69753..7a87b2bf6a 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -664,7 +664,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadStored( signer, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ @@ -681,7 +681,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadStored( signer, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "other", }, Context{ @@ -698,7 +698,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadLinked( signer, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "test", }, Context{ @@ -715,7 +715,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadLinked( signer, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "other", }, Context{ @@ -1402,7 +1402,7 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { t.Run(fmt.Sprintf("%s %s", domain.Identifier(), typeDescription), func(t *testing.T) { storagePath := cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: fmt.Sprintf( "test%s%s", typeDescription, @@ -1445,7 +1445,7 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: domain.Identifier(), + Domain: domain, Identifier: "test", }, Address: cadence.Address(signer), diff --git a/values.go b/values.go index dbea97a266..1e9cd3dfe6 100644 --- a/values.go +++ b/values.go @@ -1924,20 +1924,24 @@ func (v AccountLink) String() string { // Path type Path struct { - Domain string + Domain common.PathDomain Identifier string } var _ Value = Path{} -func NewPath(domain, identifier string) Path { +func NewPath(domain common.PathDomain, identifier string) (Path, error) { + if domain == common.PathDomainUnknown { + return Path{}, errors.NewDefaultUserError("unknown domain in path") + } + return Path{ Domain: domain, Identifier: identifier, - } + }, nil } -func NewMeteredPath(gauge common.MemoryGauge, domain, identifier string) Path { +func NewMeteredPath(gauge common.MemoryGauge, domain common.PathDomain, identifier string) (Path, error) { common.UseMemory(gauge, common.CadencePathValueMemoryUsage) return NewPath(domain, identifier) } @@ -1958,7 +1962,7 @@ func (Path) ToGoValue() any { func (v Path) String() string { return format.Path( - v.Domain, + v.Domain.Identifier(), v.Identifier, ) } diff --git a/values_test.go b/values_test.go index 28f1d54ffb..299fbadf97 100644 --- a/values_test.go +++ b/values_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/tests/utils" ) @@ -333,7 +334,7 @@ func newValueTestCases() map[string]valueTestCase { "PathLink": { value: NewPathLink( Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, "Int", @@ -346,9 +347,9 @@ func newValueTestCases() map[string]valueTestCase { string: "AccountLink()", noType: true, }, - "Path": { + "StoragePath": { value: Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, expectedType: PathType{}, @@ -361,7 +362,10 @@ func newValueTestCases() map[string]valueTestCase { }, "Capability": { value: StorageCapability{ - Path: Path{Domain: "storage", Identifier: "foo"}, + Path: Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, Address: BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: IntType{}, }, From 2923c9c6d9bf6a421dbbd9073df60caa9c9d6741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 15:26:36 -0700 Subject: [PATCH 121/173] return path subtype --- values.go | 13 +++++++++++-- values_test.go | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/values.go b/values.go index 1e9cd3dfe6..e03bdbd6f3 100644 --- a/values.go +++ b/values.go @@ -1948,8 +1948,17 @@ func NewMeteredPath(gauge common.MemoryGauge, domain common.PathDomain, identifi func (Path) isValue() {} -func (Path) Type() Type { - return ThePathType +func (v Path) Type() Type { + switch v.Domain { + case common.PathDomainStorage: + return TheStoragePathType + case common.PathDomainPrivate: + return ThePrivatePathType + case common.PathDomainPublic: + return ThePublicPathType + } + + panic(errors.NewUnreachableError()) } func (v Path) MeteredType(common.MemoryGauge) Type { diff --git a/values_test.go b/values_test.go index 299fbadf97..e242587021 100644 --- a/values_test.go +++ b/values_test.go @@ -352,9 +352,25 @@ func newValueTestCases() map[string]valueTestCase { Domain: common.PathDomainStorage, Identifier: "foo", }, - expectedType: PathType{}, + expectedType: TheStoragePathType, string: "/storage/foo", }, + "PrivatePath": { + value: Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, + expectedType: ThePrivatePathType, + string: "/private/foo", + }, + "PublicPath": { + value: Path{ + Domain: common.PathDomainPublic, + Identifier: "foo", + }, + expectedType: ThePublicPathType, + string: "/public/foo", + }, "Type": { value: TypeValue{StaticType: IntType{}}, expectedType: NewMetaType(), From 5658ba106203cf50a1322d16fc8bd9d99d1771fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 15:26:58 -0700 Subject: [PATCH 122/173] test script with storage path parameter --- runtime/runtime_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index b83c2d4f84..052341db67 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -1706,6 +1706,23 @@ func TestRuntimeScriptArguments(t *testing.T) { }, expectedLogs: []string{`"bar"`}, }, + { + name: "Path subtype", + script: ` + pub fun main(x: StoragePath) { + log(x) + } + `, + args: [][]byte{ + jsoncdc.MustEncode(cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }), + }, + expectedLogs: []string{ + "/storage/foo", + }, + }, } test := func(tt testCase) { From c3507cb3085f6fabbe220e6029673b9e9929fd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 15:27:33 -0700 Subject: [PATCH 123/173] test contract deployment with storage path contract initializer parameter --- runtime/deployment_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runtime/deployment_test.go b/runtime/deployment_test.go index e29292c155..783dc04da8 100644 --- a/runtime/deployment_test.go +++ b/runtime/deployment_test.go @@ -338,4 +338,17 @@ func TestRuntimeTransactionWithContractDeployment(t *testing.T) { }) }) + t.Run("Path subtype", func(t *testing.T) { + test(t, testCase{ + contract: ` + pub contract Test { + init(_ path: StoragePath) {} + } + `, + arguments: []argument{ + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "test"), + }, + check: expectSuccess, + }) + }) } From dc93a8706f23c4d27a8050f0b3639d4bc745a57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Apr 2023 17:18:51 -0700 Subject: [PATCH 124/173] regenerate files --- runtime/sema/block.gen.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/sema/block.gen.go b/runtime/sema/block.gen.go index f4e5192412..ed587a3007 100644 --- a/runtime/sema/block.gen.go +++ b/runtime/sema/block.gen.go @@ -73,7 +73,10 @@ var BlockType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { +} + +func init() { + BlockType.Members = func(t *SimpleType) map[string]MemberResolver { return MembersAsResolvers([]*Member{ NewUnmeteredPublicConstantFieldMember( t, @@ -100,5 +103,5 @@ var BlockType = &SimpleType{ BlockTypeIdFieldDocString, ), }) - }, + } } From d5753f9fbe0aff6eac5ddc5bccc46314f908606a Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:19:26 -0500 Subject: [PATCH 125/173] Bump onflow/atree to v0.6.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7b00b6698c..75846df285 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f github.com/go-test/deep v1.1.0 github.com/leanovate/gopter v0.2.9 - github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b + github.com/onflow/atree v0.6.0 github.com/rivo/uniseg v0.4.4 github.com/schollz/progressbar/v3 v3.13.1 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 59ca966de3..296e82c68e 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3px github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b h1:H6hKC/kqP0EDmmM0fqvO4bfquqpyuvQSoXfuF/jz/jU= -github.com/onflow/atree v0.5.1-0.20230323185112-76ac577acc1b/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= +github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= +github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From caacb28e833dd196f7c618811216afea078383e4 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:39:52 +0300 Subject: [PATCH 126/173] Add the 'not' built-in matcher combinator --- runtime/stdlib/contracts/test.cdc | 9 ++++++++ runtime/stdlib/test_test.go | 35 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 2693597379..53628c95dc 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -250,4 +250,13 @@ pub contract Test { /// pub fun useConfiguration(_ configuration: Configuration) } + + /// Returns a new matcher that negates the test of the given matcher. + /// + pub fun not(_ matcher: Matcher): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return !matcher.test(value) + }) + } + } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 34542071a9..3d38fd33ea 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -508,6 +508,41 @@ func TestTestEqualMatcher(t *testing.T) { assert.Equal(t, interpreter.FalseValue, result) }) + t.Run("matcher not", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let one = Test.equal(1) + + let notOne = Test.not(one) + + return notOne.test(2) + } + + pub fun testNoMatch(): Bool { + let one = Test.equal(1) + + let notOne = Test.not(one) + + return notOne.test(1) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + t.Run("chained matchers", func(t *testing.T) { t.Parallel() From 9a54823c18a49627e0b6af2d507ab37864f6ecd7 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:44:30 +0300 Subject: [PATCH 127/173] Add the 'beSucceeded' built-in matcher function --- runtime/stdlib/contracts/test.cdc | 18 ++++- runtime/stdlib/test_test.go | 110 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 53628c95dc..cd0f5bf617 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -138,7 +138,7 @@ pub contract Test { pub let status: ResultStatus pub let error: Error? - init(status: ResultStatus, error: Error) { + init(status: ResultStatus, error: Error?) { self.status = status self.error = error } @@ -259,4 +259,20 @@ pub contract Test { }) } + /// Returns a new matcher that checks if the given test value is either + /// a ScriptResult or TransactionResult and the ResultStatus is succeeded. + /// Returns false in any other case. + /// + pub fun beSucceeded(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + if let result = value as? TransactionResult { + return result.status == ResultStatus.succeeded + } else if let result = value as? ScriptResult { + return result.status == ResultStatus.succeeded + } else { + return false + } + }) + } + } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 3d38fd33ea..8335664b8c 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -631,6 +631,116 @@ func TestTestEqualMatcher(t *testing.T) { }) } +func TestTestBeSucceededMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beSucceeded with ScriptResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let successful = Test.beSucceeded() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.succeeded, + returnValue: 42, + error: nil + ) + + return successful.test(scriptResult) + } + + pub fun testNoMatch(): Bool { + let successful = Test.beSucceeded() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.failed, + returnValue: nil, + error: Test.Error("Exceeding limit") + ) + + return successful.test(scriptResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beSucceeded with TransactionResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let successful = Test.beSucceeded() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.succeeded, + error: nil + ) + + return successful.test(transactionResult) + } + + pub fun testNoMatch(): Bool { + let successful = Test.beSucceeded() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.failed, + error: Test.Error("Exceeded Limit") + ) + + return successful.test(transactionResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beSucceeded with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let successful = Test.beSucceeded() + + return successful.test("hello") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("test") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From d725d77b48202274d90b966e13738052ee655fc3 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:45:20 +0300 Subject: [PATCH 128/173] Add the 'beFailed' built-in matcher function --- runtime/stdlib/contracts/test.cdc | 16 +++++ runtime/stdlib/test_test.go | 110 ++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index cd0f5bf617..428c5e9c31 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -275,4 +275,20 @@ pub contract Test { }) } + /// Returns a new matcher that checks if the given test value is either + /// a ScriptResult or TransactionResult and the ResultStatus is failed. + /// Returns false in any other case. + /// + pub fun beFailed(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + if let result = value as? TransactionResult { + return result.status == ResultStatus.failed + } else if let result = value as? ScriptResult { + return result.status == ResultStatus.failed + } else { + return false + } + }) + } + } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 8335664b8c..c385eab794 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -741,6 +741,116 @@ func TestTestBeSucceededMatcher(t *testing.T) { }) } +func TestTestBeFailedMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beFailed with ScriptResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let failed = Test.beFailed() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.failed, + returnValue: nil, + error: Test.Error("Exceeding limit") + ) + + return failed.test(scriptResult) + } + + pub fun testNoMatch(): Bool { + let failed = Test.beFailed() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.succeeded, + returnValue: 42, + error: nil + ) + + return failed.test(scriptResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beFailed with TransactionResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let failed = Test.beFailed() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.failed, + error: Test.Error("Exceeding limit") + ) + + return failed.test(transactionResult) + } + + pub fun testNoMatch(): Bool { + let failed = Test.beFailed() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.succeeded, + error: nil + ) + + return failed.test(transactionResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beFailed with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let failed = Test.beFailed() + + return failed.test([]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("test") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From 54475e2dfebf477038eb5a80037f673da66d3d5b Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:46:10 +0300 Subject: [PATCH 129/173] Add the 'beNil' built-in matcher function --- runtime/stdlib/contracts/test.cdc | 8 +++++++ runtime/stdlib/test_test.go | 36 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 428c5e9c31..ba624830f5 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -291,4 +291,12 @@ pub contract Test { }) } + /// Returns a new matcher that checks if the given test value is nil. + /// + pub fun beNil(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return value == nil + }) + } + } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index c385eab794..f88761571c 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -851,6 +851,42 @@ func TestTestBeFailedMatcher(t *testing.T) { }) } +func TestTestBeNilMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beNil", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let isNil = Test.beNil() + + return isNil.test(nil) + } + + pub fun testNoMatch(): Bool { + let isNil = Test.beNil() + + return isNil.test([1, 2]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From 7f7fb4479ebfb75cc337ac7b9ac6a64d30f3f1c5 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:54:50 +0300 Subject: [PATCH 130/173] Add the 'beEmpty' built-in matcher function --- runtime/stdlib/test.go | 57 +++++++++++++++++++++++- runtime/stdlib/test_test.go | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index dfafd07860..ad63ae4923 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -124,6 +124,7 @@ func NewTestContract( // Inject natively implemented matchers compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction + compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction return compositeValue, nil } @@ -277,7 +278,7 @@ func init() { ), ) - // Matcher functions + // Test.equal() testContractType.Members.Set( equalMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -288,6 +289,17 @@ func init() { ), ) + // Test.beEmpty() + testContractType.Members.Set( + beEmptyMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beEmptyMatcherFunctionName, + beEmptyMatcherFunctionType, + beEmptyMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -316,7 +328,7 @@ var blockchainType = func() sema.Type { return typ }() -// Functions belong to the 'Test' contract +// Functions belonging to the 'Test' contract // 'Test.assert' function @@ -1367,6 +1379,47 @@ var equalMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +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. +` + +var beEmptyMatcherFunctionType = func() *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.NewUnreachableError()) + } + + return interpreter.AsBoolValue(isEmpty) + }, + ) + + return newMatcherWithGenericTestFunction(invocation, beEmptyTestFunc) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index f88761571c..bc2ad671bd 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -887,6 +887,95 @@ func TestTestBeNilMatcher(t *testing.T) { }) } +func TestTestBeEmptyMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beEmpty with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let emptyArray = Test.beEmpty() + + return emptyArray.test([]) + } + + pub fun testNoMatch(): Bool { + let emptyArray = Test.beEmpty() + + return emptyArray.test([42, 23, 31]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beEmpty with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let emptyDict = Test.beEmpty() + let dict: {Bool: Int} = {} + + return emptyDict.test(dict) + } + + pub fun testNoMatch(): Bool { + let emptyDict = Test.beEmpty() + let dict: {Bool: Int} = {true: 1, false: 0} + + return emptyDict.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beEmpty with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let emptyDict = Test.beEmpty() + + return emptyDict.test("empty") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("testMatch") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From 1f938a731c7621822cfde557c2fa3403aa7eb7be Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:56:55 +0300 Subject: [PATCH 131/173] Add the 'haveElementCount' built-in matcher function --- runtime/stdlib/test.go | 66 +++++++++++++++++++++++++++ runtime/stdlib/test_test.go | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index ad63ae4923..d2c14eb72e 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -125,6 +125,7 @@ func NewTestContract( compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction + compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction return compositeValue, nil } @@ -300,6 +301,17 @@ func init() { ), ) + // Test.haveElementCount() + testContractType.Members.Set( + haveElementCountMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + haveElementCountMatcherFunctionName, + haveElementCountMatcherFunctionType, + haveElementCountMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -1420,6 +1432,60 @@ var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +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. +` + +var haveElementCountMatcherFunctionType = func() *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), + } +}() + +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.NewUnreachableError()) + } + + return interpreter.AsBoolValue(matchingCount) + }, + ) + + return newMatcherWithGenericTestFunction(invocation, haveElementCountTestFunc) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index bc2ad671bd..070b6b5c29 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -976,6 +976,95 @@ func TestTestBeEmptyMatcher(t *testing.T) { }) } +func TestTestHaveElementCountMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher haveElementCount with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let hasThreeElements = Test.haveElementCount(3) + + return hasThreeElements.test([42, 23, 31]) + } + + pub fun testNoMatch(): Bool { + let hasThreeElements = Test.haveElementCount(3) + + return hasThreeElements.test([42]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher haveElementCount with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let hasTwoElements = Test.haveElementCount(2) + let dict: {Bool: Int} = {true: 1, false: 0} + + return hasTwoElements.test(dict) + } + + pub fun testNoMatch(): Bool { + let hasTwoElements = Test.haveElementCount(2) + let dict: {Bool: Int} = {} + + return hasTwoElements.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher haveElementCount with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let hasTwoElements = Test.haveElementCount(2) + + return hasTwoElements.test("two") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From 6786bb8d62a73b569781b52140813c54b090312b Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 12:58:56 +0300 Subject: [PATCH 132/173] Add the 'contain' built-in matcher function --- runtime/stdlib/test.go | 77 ++++++++++++++++++++++++++++++++ runtime/stdlib/test_test.go | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index d2c14eb72e..d8a62b3715 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -126,6 +126,7 @@ func NewTestContract( compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction + compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction return compositeValue, nil } @@ -312,6 +313,17 @@ func init() { ), ) + // Test.contain() + testContractType.Members.Set( + containMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + containMatcherFunctionName, + containMatcherFunctionType, + containMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -1486,6 +1498,71 @@ var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +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 value is equal to the given value. +` + +var containMatcherFunctionType = func() *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), + } +}() + +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 + + 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.NewUnreachableError()) + } + + return elementFound + }, + ) + + return newMatcherWithGenericTestFunction(invocation, containTestFunc) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 070b6b5c29..e4c6bab3dd 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -1065,6 +1065,95 @@ func TestTestHaveElementCountMatcher(t *testing.T) { }) } +func TestTestContainMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher contain with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let containsTwenty = Test.contain(20) + + return containsTwenty.test([42, 20, 31]) + } + + pub fun testNoMatch(): Bool { + let containsTwenty = Test.contain(20) + + return containsTwenty.test([42]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher contain with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let containsFalse = Test.contain(false) + let dict: {Bool: Int} = {true: 1, false: 0} + + return containsFalse.test(dict) + } + + pub fun testNoMatch(): Bool { + let containsFive = Test.contain(5) + let dict: {Int: Bool} = {1: true, 0: false} + + return containsFive.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher contain with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let containsFalse = Test.contain(false) + + return containsFalse.test("false") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From f595f72a2e5e82ee4ff708d0f6db5070e9d32204 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 13:00:31 +0300 Subject: [PATCH 133/173] Add the 'beGreaterThan' built-in matcher function --- runtime/stdlib/test.go | 69 +++++++++++++++++++++++++++++++++++++ runtime/stdlib/test_test.go | 56 ++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index d8a62b3715..1c1b42aadb 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -127,6 +127,7 @@ func NewTestContract( compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction + compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction return compositeValue, nil } @@ -324,6 +325,17 @@ func init() { ), ) + // Test.beGreaterThan() + testContractType.Members.Set( + beGreaterThanMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beGreaterThanMatcherFunctionName, + beGreaterThanMatcherFunctionType, + beGreaterThanMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -1563,6 +1575,63 @@ var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +const beGreaterThanMatcherFunctionName = "beGreaterThan" + +const beGreaterThanMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +greater than the given number. +` + +var beGreaterThanMatcherFunctionType = func() *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), + } +}() + +var 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) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index e4c6bab3dd..f5e1128bd4 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -1154,6 +1154,62 @@ func TestTestContainMatcher(t *testing.T) { }) } +func TestTestBeGreaterThanMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beGreaterThan", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test(7) + } + + pub fun testNoMatch(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test(2) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beGreaterThan with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test("7") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From c67f456dd6342360d00b394398030f03c4fd344d Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 4 Apr 2023 13:01:51 +0300 Subject: [PATCH 134/173] Add the 'beLessThan' built-in matcher function --- runtime/stdlib/test.go | 69 +++++++++++++++++++++++++++++++++++++ runtime/stdlib/test_test.go | 56 ++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 1c1b42aadb..6a6de22b00 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -128,6 +128,7 @@ func NewTestContract( compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction + compositeValue.Functions[beLessThanMatcherFunctionName] = beLessThanMatcherFunction return compositeValue, nil } @@ -336,6 +337,17 @@ func init() { ), ) + // Test.beLessThan() + testContractType.Members.Set( + beLessThanMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beLessThanMatcherFunctionName, + beLessThanMatcherFunctionType, + beLessThanMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -1632,6 +1644,63 @@ var beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +const beLessThanMatcherFunctionName = "beLessThan" + +const beLessThanMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +less than the given number. +` + +var beLessThanMatcherFunctionType = func() *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), + } +}() + +var 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) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index f5e1128bd4..08968daa61 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -1210,6 +1210,62 @@ func TestTestBeGreaterThanMatcher(t *testing.T) { }) } +func TestTestBeLessThanMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beLessThan", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(5) + } + + pub fun testNoMatch(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(9) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beLessThan with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(true) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() From 24bb27bdaa32c9aaa5465995c9587fb6421572e1 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 5 Apr 2023 11:46:43 +0300 Subject: [PATCH 135/173] Declare the 'panic' function in the TestContractChecker This way it can be utilized in the test.cdc contract. --- runtime/stdlib/contracts/test.cdc | 6 ++++-- runtime/stdlib/test.go | 1 + runtime/stdlib/test_test.go | 17 ++++++++++------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index ba624830f5..fb508cec1e 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -270,8 +270,9 @@ pub contract Test { } else if let result = value as? ScriptResult { return result.status == ResultStatus.succeeded } else { - return false + panic("Only TransactionResult & ScriptResult types are supported.") } + return false }) } @@ -286,8 +287,9 @@ pub contract Test { } else if let result = value as? ScriptResult { return result.status == ResultStatus.failed } else { - return false + panic("Only TransactionResult & ScriptResult types are supported.") } + return false }) } diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 6a6de22b00..21fcc91177 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -69,6 +69,7 @@ var TestContractChecker = func() *sema.Checker { activation := sema.NewVariableActivation(sema.BaseValueActivation) activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) var checker *sema.Checker checker, err = sema.NewChecker( diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 08968daa61..468281fc81 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -42,12 +42,17 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr ) require.NoError(t, err) + activation := sema.NewVariableActivation(sema.BaseValueActivation) + activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) + checker, err := sema.NewChecker( program, utils.TestLocation, nil, &sema.Config{ - AccessCheckMode: sema.AccessCheckModeStrict, + BaseValueActivation: activation, + AccessCheckMode: sema.AccessCheckModeStrict, ImportHandler: func( checker *sema.Checker, importedLocation common.Location, @@ -735,9 +740,8 @@ func TestTestBeSucceededMatcher(t *testing.T) { inter, err := newTestContractInterpreter(t, script) require.NoError(t, err) - result, err := inter.Invoke("test") - require.NoError(t, err) - assert.Equal(t, interpreter.FalseValue, result) + _, err = inter.Invoke("test") + require.Error(t, err) }) } @@ -845,9 +849,8 @@ func TestTestBeFailedMatcher(t *testing.T) { inter, err := newTestContractInterpreter(t, script) require.NoError(t, err) - result, err := inter.Invoke("test") - require.NoError(t, err) - assert.Equal(t, interpreter.FalseValue, result) + _, err = inter.Invoke("test") + require.Error(t, err) }) } From dc31ba34ac6e1f632568039a623cfeb95b147425 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 5 Apr 2023 13:24:11 +0300 Subject: [PATCH 136/173] Improve error handling and error messages for the new matchers --- runtime/stdlib/contracts/test.cdc | 4 ++-- runtime/stdlib/test.go | 6 +++--- runtime/stdlib/test_test.go | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index fb508cec1e..e68ef73090 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -270,7 +270,7 @@ pub contract Test { } else if let result = value as? ScriptResult { return result.status == ResultStatus.succeeded } else { - panic("Only TransactionResult & ScriptResult types are supported.") + panic("expected TransactionResult or ScriptResult argument") } return false }) @@ -287,7 +287,7 @@ pub contract Test { } else if let result = value as? ScriptResult { return result.status == ResultStatus.failed } else { - panic("Only TransactionResult & ScriptResult types are supported.") + panic("expected TransactionResult or ScriptResult argument") } return false }) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 21fcc91177..b8dfc9a320 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -1458,7 +1458,7 @@ var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( case *interpreter.DictionaryValue: isEmpty = value.Count() == 0 default: - panic(errors.NewUnreachableError()) + panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) } return interpreter.AsBoolValue(isEmpty) @@ -1512,7 +1512,7 @@ var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( case *interpreter.DictionaryValue: matchingCount = value.Count() == count.ToInt(invocation.LocationRange) default: - panic(errors.NewUnreachableError()) + panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) } return interpreter.AsBoolValue(matchingCount) @@ -1577,7 +1577,7 @@ var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( element, ) default: - panic(errors.NewUnreachableError()) + panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) } return elementFound diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 468281fc81..232ba38f46 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -974,8 +974,9 @@ func TestTestBeEmptyMatcher(t *testing.T) { inter, err := newTestContractInterpreter(t, script) require.NoError(t, err) - _, err = inter.Invoke("testMatch") + _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } @@ -1065,6 +1066,7 @@ func TestTestHaveElementCountMatcher(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } @@ -1154,6 +1156,7 @@ func TestTestContainMatcher(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } From bad630c3380fe45bb2076e6c53b2f67b32c8ce63 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 7 Apr 2023 12:17:44 +0300 Subject: [PATCH 137/173] Add a struct interface to abstract the result of an executed operation --- runtime/stdlib/contracts/test.cdc | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index e68ef73090..59c74b0968 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -132,9 +132,18 @@ pub contract Test { pub case failed } + /// Result is the interface to be implemented by the various execution + /// operations, such as transactions and scripts. + /// + pub struct interface Result { + /// The resulted status of an executed operation. + /// + pub let status: ResultStatus + } + /// The result of a transaction execution. /// - pub struct TransactionResult { + pub struct TransactionResult: Result { pub let status: ResultStatus pub let error: Error? @@ -146,7 +155,7 @@ pub contract Test { /// The result of a script execution. /// - pub struct ScriptResult { + pub struct ScriptResult: Result { pub let status: ResultStatus pub let returnValue: AnyStruct? pub let error: Error? @@ -265,14 +274,7 @@ pub contract Test { /// pub fun beSucceeded(): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { - if let result = value as? TransactionResult { - return result.status == ResultStatus.succeeded - } else if let result = value as? ScriptResult { - return result.status == ResultStatus.succeeded - } else { - panic("expected TransactionResult or ScriptResult argument") - } - return false + return (value as! {Result}).status == ResultStatus.succeeded }) } @@ -282,14 +284,7 @@ pub contract Test { /// pub fun beFailed(): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { - if let result = value as? TransactionResult { - return result.status == ResultStatus.failed - } else if let result = value as? ScriptResult { - return result.status == ResultStatus.failed - } else { - panic("expected TransactionResult or ScriptResult argument") - } - return false + return (value as! {Result}).status == ResultStatus.failed }) } From 3428082350091c7f1dd0caddd98689332ba05ab1 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Sat, 8 Apr 2023 16:14:50 +0300 Subject: [PATCH 138/173] Panic with DefaultUserError instead of UnexpectedError --- runtime/stdlib/test.go | 6 +++--- runtime/stdlib/test_test.go | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index b8dfc9a320..41c5ac7583 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -1458,7 +1458,7 @@ var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( case *interpreter.DictionaryValue: isEmpty = value.Count() == 0 default: - panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) } return interpreter.AsBoolValue(isEmpty) @@ -1512,7 +1512,7 @@ var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( case *interpreter.DictionaryValue: matchingCount = value.Count() == count.ToInt(invocation.LocationRange) default: - panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) } return interpreter.AsBoolValue(matchingCount) @@ -1577,7 +1577,7 @@ var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( element, ) default: - panic(errors.NewUnexpectedError("expected Array or Dictionary argument")) + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) } return elementFound diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 232ba38f46..393d4c1825 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -27,6 +27,7 @@ import ( "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" + cdcErrors "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/sema" @@ -976,6 +977,7 @@ func TestTestBeEmptyMatcher(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } @@ -1066,6 +1068,7 @@ func TestTestHaveElementCountMatcher(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } @@ -1156,6 +1159,7 @@ func TestTestContainMatcher(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) assert.ErrorContains(t, err, "expected Array or Dictionary argument") }) } From 74dc043c8fc00e1f3bfb70bff816bf1a9512a9bd Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 10 Apr 2023 22:47:43 +0300 Subject: [PATCH 139/173] Update description for contain matcher function Co-authored-by: Supun Setunga --- runtime/stdlib/test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 41c5ac7583..7e4723f07e 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -1528,7 +1528,7 @@ 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 value is equal to the given value. +that contains an entry where the key is equal to the given value. ` var containMatcherFunctionType = func() *sema.FunctionType { From 2f4cb0d199d15aafa4bbf3645cb3825382e75103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 11 Apr 2023 08:39:28 -0700 Subject: [PATCH 140/173] add basic support for multiline input --- runtime/parser/parser.go | 18 +++++++++++++ runtime/repl.go | 56 +++++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/runtime/parser/parser.go b/runtime/parser/parser.go index 8f2c7ec97f..869e53c69c 100644 --- a/runtime/parser/parser.go +++ b/runtime/parser/parser.go @@ -556,6 +556,24 @@ func ParseStatements( ) } +func ParseStatementsFromTokenStream( + memoryGauge common.MemoryGauge, + tokens lexer.TokenStream, + config Config, +) ( + statements []ast.Statement, + errs []error, +) { + return ParseTokenStream( + memoryGauge, + tokens, + func(p *parser) ([]ast.Statement, error) { + return parseStatements(p, nil) + }, + config, + ) +} + func ParseType(memoryGauge common.MemoryGauge, input []byte, config Config) (ty ast.Type, errs []error) { return Parse( memoryGauge, diff --git a/runtime/repl.go b/runtime/repl.go index e0b0f528fd..90c0b8b4a9 100644 --- a/runtime/repl.go +++ b/runtime/repl.go @@ -32,6 +32,7 @@ import ( "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/parser" + "github.com/onflow/cadence/runtime/parser/lexer" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -113,6 +114,45 @@ func (r *REPL) handleCheckerError() error { return err } +func isInputComplete(tokens lexer.TokenStream) bool { + var unmatchedBrackets, unmatchedParens, unmatchedBraces int + + for { + + token := tokens.Next() + + switch token.Type { + case lexer.TokenBracketOpen: + unmatchedBrackets++ + + case lexer.TokenBracketClose: + unmatchedBrackets-- + + case lexer.TokenParenOpen: + unmatchedParens++ + + case lexer.TokenParenClose: + unmatchedParens-- + + case lexer.TokenBraceOpen: + unmatchedBraces++ + + case lexer.TokenBraceClose: + unmatchedBraces-- + } + + if token.Is(lexer.TokenEOF) { + break + } + } + + tokens.Revert(0) + + return unmatchedBrackets <= 0 && + unmatchedParens <= 0 && + unmatchedBraces <= 0 +} + var lineSep = []byte{'\n'} func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { @@ -188,10 +228,16 @@ func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { code = prefixedCode } - // TODO: detect if the input is complete - inputIsComplete = true + tokens := lexer.Lex(code, nil) + defer tokens.Reclaim() + + inputIsComplete = isInputComplete(tokens) + + if !inputIsComplete { + return + } - result, errs := parser.ParseStatements(nil, code, parser.Config{}) + result, errs := parser.ParseStatementsFromTokenStream(nil, tokens, parser.Config{}) if len(errs) > 0 { err = parser.Error{ Code: code, @@ -199,10 +245,6 @@ func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { } } - if !inputIsComplete { - return - } - if err != nil { r.onError(err, r.checker.Location, r.codes) return From 19f829fd4ca3401237844db355ed247b6321d166 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:27:06 -0500 Subject: [PATCH 141/173] Add more comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/ccf_type_id.go | 2 ++ encoding/ccf/decode_typedef.go | 7 ++++++- encoding/ccf/encode.go | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go index 3335562d03..7459624ed8 100644 --- a/encoding/ccf/ccf_type_id.go +++ b/encoding/ccf/ccf_type_id.go @@ -44,6 +44,8 @@ func (id ccfTypeID) Equal(other ccfTypeID) bool { return id == other } +// ccfTypeIDByCadenceType maps a Cadence type ID to a CCF type ID +// // IMPORTANT: Don't use cadence.Type as map key because all Cadence composite/interface // types are pointers, and different instance of the same type will be treated as // different map key. diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 0b25153217..aee50dc740 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -30,7 +30,10 @@ import ( // language=CDDL // composite-typedef = [ // -// +( +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// + ( // struct-type // / resource-type // / contract-type @@ -339,6 +342,7 @@ func (d *Decoder) decodeCompositeType( return ccfTypeID(0), cadenceTypeID(""), err } + // The return value can be ignored, because its non-existence was already checked above _ = types.add(ccfID, constructor(location, identifier)) rawFields[ccfID] = rawField return ccfID, cadenceID, nil @@ -472,6 +476,7 @@ func (d *Decoder) decodeInterfaceType( return ccfTypeID(0), cadenceTypeID(""), err } + // The return value can be ignored, because its non-existence was already checked above _ = types.add(ccfID, constructor(location, identifier)) return ccfID, cadenceID, nil } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 3437287bde..07ad21787e 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -229,7 +229,10 @@ func (e *Encoder) encodeInlineTypeAndValue(value cadence.Value, tids ccfTypeIDBy // language=CDDL // composite-typedef = [ // -// +( +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// + ( // struct-type // / resource-type // / contract-type From b8faff0b0103eeced0d547fcc1f04b44946fe663 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:23:28 -0500 Subject: [PATCH 142/173] Update CCF codec to use newer PathValue in PR 2427 Cadence recently improved PathValue in PR 2427, so the CCF codec was modified to use the updated PathValue. Also added more tests to encode different types of PathValue. --- encoding/ccf/ccf_test.go | 322 +++++++++++++++++++++++++++++---- encoding/ccf/decode.go | 20 +- encoding/ccf/decode_typedef.go | 26 +-- encoding/ccf/encode.go | 10 +- encoding/ccf/traverse_value.go | 3 + 5 files changed, 316 insertions(+), 65 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index aa8d190c78..73b239b689 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -8999,10 +8999,13 @@ func TestEncodeCapability(t *testing.T) { t.Run("Capability", func(t *testing.T) { + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + testEncodeAndDecode( t, cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: path, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), }, []byte{ // language=json, format=json-cdc @@ -9053,14 +9056,20 @@ func TestEncodeCapability(t *testing.T) { }, } + path1, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + path2, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + capability1 := cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: path1, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, } capability2 := cadence.StorageCapability{ - Path: cadence.NewPath("storage", "bar"), + Path: path2, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: simpleStructType, } @@ -9188,10 +9197,13 @@ func TestEncodeCapability(t *testing.T) { }) t.Run("Capability", func(t *testing.T) { + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + testEncodeAndDecode( t, cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: path, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, }, @@ -9234,13 +9246,20 @@ func TestEncodeCapability(t *testing.T) { }) t.Run("array of Capability", func(t *testing.T) { + + path1, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + path2, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + capability1 := cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: path1, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, } capability2 := cadence.StorageCapability{ - Path: cadence.NewPath("storage", "bar"), + Path: path2, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, } @@ -10241,34 +10260,269 @@ func TestEncodePath(t *testing.T) { t.Parallel() - testEncodeAndDecode( - t, - cadence.NewPath("storage", "foo"), - []byte{ // language=json, format=json-cdc - // {"type":"Path","value":{"domain":"storage","identifier":"foo"}} - // - // language=edn, format=ccf - // 130([137(24), [1, "foo"]]) - // - // language=cbor, format=ccf - // tag - 0xd8, ccf.CBORTagTypeAndValue, - // array, 2 elements follow - 0x82, - // tag - 0xd8, ccf.CBORTagSimpleType, - // Path type ID (24) - 0x18, 0x18, - // array, 2 elements follow - 0x82, - // 1 - 0x01, - // string, 3 bytes follow - 0x63, - // foo - 0x66, 0x6f, 0x6f, - }, - ) + t.Run("Storage", func(t *testing.T) { + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"value":{"domain":"storage","identifier":"foo"},"type":"Path"} + // + // language=edn, format=ccf + // 130([137(26), [1, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Private", func(t *testing.T) { + path, err := cadence.NewPath(2, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"type":"Path","value":{"domain":"private","identifier":"foo"}} + // + // language=edn, format=ccf + // 130([137(28), [2, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PrivatePath type ID (28) + 0x18, 0x1c, + // array, 2 elements follow + 0x82, + // 2 + 0x02, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Public", func(t *testing.T) { + path, err := cadence.NewPath(3, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"type":"Path","value":{"domain":"public","identifier":"foo"}} + // + // language=edn, format=ccf + // 130([137(27), [3, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PublicPath type ID (27) + 0x18, 0x1b, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Array of StoragePath", func(t *testing.T) { + storagePath, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + privatePath, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + + publicPath, err := cadence.NewPath(1, "baz") + require.NoError(t, err) + + arrayOfPaths := cadence.NewArray([]cadence.Value{ + storagePath, + privatePath, + publicPath, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewStoragePathType())) + + testEncodeAndDecode( + t, + arrayOfPaths, + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},{"value":{"domain":"private","identifier":"bar"},"type":"Path"},{"value":{"domain":"public","identifier":"baz"},"type":"Path"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(26)), [[1, "foo"], [1, "bar"], [1, "baz"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 3 elements follow + 0x83, + // element 0: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // element 1: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // element 2: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + }, + ) + }) + + t.Run("Array of Path", func(t *testing.T) { + storagePath, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + privatePath, err := cadence.NewPath(2, "bar") + require.NoError(t, err) + + publicPath, err := cadence.NewPath(3, "baz") + require.NoError(t, err) + + arrayOfPaths := cadence.NewArray([]cadence.Value{ + storagePath, + privatePath, + publicPath, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewPathType())) + + testEncodeAndDecode( + t, + arrayOfPaths, + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},{"value":{"domain":"private","identifier":"bar"},"type":"Path"},{"value":{"domain":"public","identifier":"baz"},"type":"Path"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(24)), [130([137(26), [1, "foo"]]), 130([137(28), [2, "bar"]]), 130([137(27), [3, "baz"]])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Path type ID (24) + 0x18, 0x18, + // array, 3 elements follow + 0x83, + // element 0: storage path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // element 1: private path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PrivatePath type ID (28) + 0x18, 0x1c, + // array, 2 elements follow + 0x82, + // 2 + 0x02, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // element 2: public path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PublicPath type ID (27) + 0x18, 0x1b, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + }, + ) + }) } func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 965e0f5368..68e776cf1b 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -365,7 +365,13 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca case *cadence.ContractType: return d.decodeContract(typ, types) - case cadence.PathType: + case cadence.StoragePathType: + return d.decodePath() + + case cadence.PublicPathType: + return d.decodePath() + + case cadence.PrivatePathType: return d.decodePath() case cadence.MetaType: @@ -1117,16 +1123,6 @@ func (d *Decoder) decodePath() (cadence.Value, error) { return nil, err } - // Get domain identifier. - // Identifier() panics if pathDomain is invalid. - domain := common.PathDomain(pathDomain).Identifier() - - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // No need to add 1 to account for empty string: string is metered in Path struct. - Amount: uint64(len(domain)), - }) - // Decode identifier. identifier, err := d.dec.DecodeString() if err != nil { @@ -1139,7 +1135,7 @@ func (d *Decoder) decodePath() (cadence.Value, error) { Amount: uint64(len(identifier)), }) - return cadence.NewMeteredPath(d.gauge, domain, identifier), nil + return cadence.NewMeteredPath(d.gauge, common.PathDomain(pathDomain), identifier) } // decodeCapability decodes encoded capability-value as diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index aee50dc740..dfc55bab75 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -30,19 +30,19 @@ import ( // language=CDDL // composite-typedef = [ // -// ; one-or-more instead of zero-or-more because: -// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) -// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` -// + ( -// struct-type -// / resource-type -// / contract-type -// / event-type -// / enum-type -// / struct-interface-type -// / resource-interface-type -// / contract-interface-type -// )] +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// + ( +// struct-type +// / resource-type +// / contract-type +// / event-type +// / enum-type +// / struct-interface-type +// / resource-interface-type +// / contract-interface-type +// )] func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { // Decode number of type definitions. count, err := d.dec.DecodeArrayHead() diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 07ad21787e..c9dfdcab5a 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -29,7 +29,6 @@ import ( "github.com/fxamacker/cbor/v2" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" cadenceErrors "github.com/onflow/cadence/runtime/errors" ) @@ -229,9 +228,9 @@ func (e *Encoder) encodeInlineTypeAndValue(value cadence.Value, tids ccfTypeIDBy // language=CDDL // composite-typedef = [ // -// ; one-or-more instead of zero-or-more because: -// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) -// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` // + ( // struct-type // / resource-type @@ -961,8 +960,7 @@ func (e *Encoder) encodePath(x cadence.Path) error { } // element 0: domain as CBOR uint. - domain := common.PathDomainFromIdentifier(x.Domain) - err = e.enc.EncodeUint8(uint8(domain)) + err = e.enc.EncodeUint8(uint8(x.Domain)) if err != nil { return err } diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 5f4a012e82..6d7ec4dc16 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -204,6 +204,9 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) cadence.Fix64Type, cadence.UFix64Type, cadence.PathType, + cadence.StoragePathType, + cadence.PublicPathType, + cadence.PrivatePathType, cadence.MetaType, *cadence.FunctionType, cadence.NumberType, From 804555889ba00a8b6399280e57fddd677f30a4f5 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:47:28 -0500 Subject: [PATCH 143/173] Parallelize some CCF tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/ccf_type_id_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/encoding/ccf/ccf_type_id_test.go b/encoding/ccf/ccf_type_id_test.go index 2ffbaa9584..dd5c2fb08f 100644 --- a/encoding/ccf/ccf_type_id_test.go +++ b/encoding/ccf/ccf_type_id_test.go @@ -29,6 +29,9 @@ import ( ) func TestCCFTypeID(t *testing.T) { + + t.Parallel() + testCases := []struct { name string input uint64 @@ -66,6 +69,9 @@ func TestCCFTypeID(t *testing.T) { } func TestCCFTypeIDByCadenceType(t *testing.T) { + + t.Parallel() + // Create ccfTypeIDByCadenceType map ccfIDs := make(ccfTypeIDByCadenceType) @@ -84,6 +90,9 @@ func TestCCFTypeIDByCadenceType(t *testing.T) { } func TestCadenceTypeByCCFTypeID(t *testing.T) { + + t.Parallel() + cadenceTypes := newCadenceTypeByCCFTypeID() // Add new entry. From dbbbbd9927652d2c628a05868fee0f7db865d205 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 17:17:24 -0500 Subject: [PATCH 144/173] Refactor and rename functions --- encoding/ccf/decode.go | 54 +++++++++++++++++++----------------------- values.go | 4 ++-- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 68e776cf1b..a8cc4555a6 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -375,7 +375,11 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca return d.decodePath() case cadence.MetaType: - return d.decodeNullableTypeValue() + t, err := d.decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) + if err != nil { + return nil, err + } + return cadence.NewMeteredTypeValue(d.gauge, t), nil case *cadence.CapabilityType: return d.decodeCapability(typ, types) @@ -783,7 +787,7 @@ func (d *Decoder) decodeFix64() (cadence.Value, error) { if err != nil { return nil, err } - return cadence.NewMeteredFix64FromInt64(d.gauge, i) + return cadence.NewMeteredFix64FromRawFixedPointNumber(d.gauge, i) } // decodeUFix64 decodes ufix64-value as @@ -794,7 +798,7 @@ func (d *Decoder) decodeUFix64() (cadence.Value, error) { if err != nil { return nil, err } - return cadence.NewMeteredUFix64FromUint64(d.gauge, i) + return cadence.NewMeteredUFix64FromRawFixedPointNumber(d.gauge, i) } // decodeOptional decodes encoded optional-value as @@ -1191,17 +1195,7 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTy typ.BorrowType), nil } -// decodeNullableTypeValue decodes encoded type-value / nil. -// See _decodeNullableTypeValue() for details. -func (d *Decoder) decodeNullableTypeValue() (cadence.Value, error) { - t, err := d._decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) - if err != nil { - return nil, err - } - return cadence.NewMeteredTypeValue(d.gauge, t), nil -} - -// _decodeTypeValue decodes encoded type-value as +// decodeTypeValue decodes encoded type-value as // language=CDDL // type-value = simple-type-value // @@ -1222,7 +1216,7 @@ func (d *Decoder) decodeNullableTypeValue() (cadence.Value, error) { // / restricted-type-value // / capability-type-value // / type-value-ref -func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { +func (d *Decoder) decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { // Decode tag number. tagNum, err := d.dec.DecodeTagNumber() if err != nil { @@ -1238,25 +1232,25 @@ func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Typ return d.decodeSimpleTypeID() case CBORTagOptionalTypeValue: - return d.decodeOptionalType(visited, d._decodeTypeValue) + return d.decodeOptionalType(visited, d.decodeTypeValue) case CBORTagVarsizedArrayTypeValue: - return d.decodeVarSizedArrayType(visited, d._decodeTypeValue) + return d.decodeVarSizedArrayType(visited, d.decodeTypeValue) case CBORTagConstsizedArrayTypeValue: - return d.decodeConstantSizedArrayType(visited, d._decodeTypeValue) + return d.decodeConstantSizedArrayType(visited, d.decodeTypeValue) case CBORTagDictTypeValue: - return d.decodeDictType(visited, d._decodeTypeValue) + return d.decodeDictType(visited, d.decodeTypeValue) case CBORTagCapabilityTypeValue: - return d.decodeCapabilityType(visited, d._decodeNullableTypeValue) + return d.decodeCapabilityType(visited, d.decodeNullableTypeValue) case CBORTagReferenceTypeValue: - return d.decodeReferenceType(visited, d._decodeTypeValue) + return d.decodeReferenceType(visited, d.decodeTypeValue) case CBORTagRestrictedTypeValue: - return d.decodeRestrictedType(visited, d._decodeNullableTypeValue, d._decodeTypeValue) + return d.decodeRestrictedType(visited, d.decodeNullableTypeValue, d.decodeTypeValue) case CBORTagFunctionTypeValue: return d.decodeFunctionTypeValue(visited) @@ -1290,8 +1284,8 @@ func (d *Decoder) _decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Typ } } -// _decodeNullableTypeValue decodes encoded type-value or nil. -func (d *Decoder) _decodeNullableTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { +// decodeNullableTypeValue decodes encoded type-value or nil. +func (d *Decoder) decodeNullableTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { cborType, err := d.dec.NextType() if err != nil { return nil, err @@ -1300,7 +1294,7 @@ func (d *Decoder) _decodeNullableTypeValue(visited *cadenceTypeByCCFTypeID) (cad err = d.dec.DecodeNil() return nil, err } - return d._decodeTypeValue(visited) + return d.decodeTypeValue(visited) } // decodeStructTypeValue decodes struct-type-value as @@ -1600,7 +1594,7 @@ func (d *Decoder) decodeCompositeTypeValue( // Decode fields after type is resolved to handle recursive types. dec := NewDecoder(d.gauge, compTypeValue.rawFields) - fields, err := dec.decodeCompositeFields(visited, dec._decodeTypeValue) + fields, err := dec.decodeCompositeFields(visited, dec.decodeTypeValue) if err != nil { return nil, err } @@ -1700,7 +1694,7 @@ func (d *Decoder) _decodeCompositeTypeValue(visited *cadenceTypeByCCFTypeID) (*c } // element 2: type (only used by enum type value) - typ, err := d._decodeNullableTypeValue(visited) + typ, err := d.decodeNullableTypeValue(visited) if err != nil { return nil, err } @@ -1832,7 +1826,7 @@ func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) } // element 2: type - t, err := d._decodeNullableTypeValue(visited) + t, err := d.decodeNullableTypeValue(visited) if err != nil { return cadence.TypeParameter{}, err } @@ -1927,7 +1921,7 @@ func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cad } // element 2: type - t, err := d._decodeTypeValue(visited) + t, err := d.decodeTypeValue(visited) if err != nil { return cadence.Parameter{}, err } @@ -1981,7 +1975,7 @@ func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cade } // element 2: return-type - returnType, err := d._decodeTypeValue(visited) + returnType, err := d.decodeTypeValue(visited) if err != nil { return nil, err } diff --git a/values.go b/values.go index de16f6ff25..bdd0b40c6d 100644 --- a/values.go +++ b/values.go @@ -1267,7 +1267,7 @@ func NewMeteredFix64(gauge common.MemoryGauge, constructor func() (string, error return NewFix64(value) } -func NewMeteredFix64FromInt64(gauge common.MemoryGauge, n int64) (Fix64, error) { +func NewMeteredFix64FromRawFixedPointNumber(gauge common.MemoryGauge, n int64) (Fix64, error) { common.UseMemory(gauge, fix64MemoryUsage) return Fix64(n), nil } @@ -1341,7 +1341,7 @@ func ParseUFix64(s string) (uint64, error) { return v.Uint64(), nil } -func NewMeteredUFix64FromUint64(gauge common.MemoryGauge, n uint64) (UFix64, error) { +func NewMeteredUFix64FromRawFixedPointNumber(gauge common.MemoryGauge, n uint64) (UFix64, error) { common.UseMemory(gauge, ufix64MemoryUsage) return UFix64(n), nil } From 926661cbc672ec8711015c5a097586a81e2f3d2b Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:06:38 -0500 Subject: [PATCH 145/173] Refactor to use same variable name in type switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/decode.go | 73 +++++++++++------------ encoding/ccf/decode_typedef.go | 6 +- encoding/ccf/encode.go | 103 ++++++++++++++++----------------- encoding/ccf/encode_typedef.go | 8 +-- encoding/ccf/traverse_value.go | 44 +++++++------- 5 files changed, 117 insertions(+), 117 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index a8cc4555a6..e9b7358106 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -265,12 +265,12 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca // If type t for the value to be decoded is a concrete type (e.g. IntType), // value MUST NOT be ccf-type-and-value-message. - switch typ := t.(type) { + switch t := t.(type) { case cadence.VoidType: return d.decodeVoid() case *cadence.OptionalType: - return d.decodeOptional(typ, types) + return d.decodeOptional(t, types) case cadence.BoolType: return d.decodeBool() @@ -345,25 +345,25 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca return d.decodeUFix64() case *cadence.VariableSizedArrayType: - return d.decodeArray(typ, false, 0, types) + return d.decodeArray(t, false, 0, types) case *cadence.ConstantSizedArrayType: - return d.decodeArray(typ, true, uint64(typ.Size), types) + return d.decodeArray(t, true, uint64(t.Size), types) case *cadence.DictionaryType: - return d.decodeDictionary(typ, types) + return d.decodeDictionary(t, types) case *cadence.ResourceType: - return d.decodeResource(typ, types) + return d.decodeResource(t, types) case *cadence.StructType: - return d.decodeStruct(typ, types) + return d.decodeStruct(t, types) case *cadence.EventType: - return d.decodeEvent(typ, types) + return d.decodeEvent(t, types) case *cadence.ContractType: - return d.decodeContract(typ, types) + return d.decodeContract(t, types) case cadence.StoragePathType: return d.decodePath() @@ -375,26 +375,26 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca return d.decodePath() case cadence.MetaType: - t, err := d.decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) + typeValue, err := d.decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) if err != nil { return nil, err } - return cadence.NewMeteredTypeValue(d.gauge, t), nil + return cadence.NewMeteredTypeValue(d.gauge, typeValue), nil case *cadence.CapabilityType: - return d.decodeCapability(typ, types) + return d.decodeCapability(t, types) case *cadence.EnumType: - return d.decodeEnum(typ, types) + return d.decodeEnum(t, types) case *cadence.ReferenceType: // When static type is a reference type, encoded value is its deferenced type. - return d.decodeValue(typ.Type, types) + return d.decodeValue(t.Type, types) default: err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) if err != nil { - return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", typ.ID(), typ, err.Error()) + return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", t.ID(), t, err.Error()) } // Decode ccf-type-and-value-message. @@ -1189,10 +1189,11 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTy } return cadence.NewMeteredStorageCapability( - d.gauge, - path.(cadence.Path), - address.(cadence.Address), - typ.BorrowType), nil + d.gauge, + path.(cadence.Path), + address.(cadence.Address), + typ.BorrowType), + nil } // decodeTypeValue decodes encoded type-value as @@ -1606,14 +1607,14 @@ func (d *Decoder) decodeCompositeTypeValue( return nil, err } - switch typ := compositeType.(type) { + switch compositeType := compositeType.(type) { case *cadence.StructType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.ResourceType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.EventType: if len(initializers) != 1 { @@ -1622,28 +1623,28 @@ func (d *Decoder) decodeCompositeTypeValue( len(initializers), ) } - typ.Fields = fields - typ.Initializer = initializers[0] + compositeType.Fields = fields + compositeType.Initializer = initializers[0] case *cadence.ContractType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.EnumType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.StructInterfaceType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.ResourceInterfaceType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers case *cadence.ContractInterfaceType: - typ.Fields = fields - typ.Initializers = initializers + compositeType.Fields = fields + compositeType.Initializers = initializers } return compositeType, nil diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index dfc55bab75..8bc239ceff 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -120,12 +120,12 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { return nil, err } - switch t := typ.(type) { + switch typ := typ.(type) { case cadence.CompositeType: - t.SetCompositeFields(fields) + typ.SetCompositeFields(fields) default: - return nil, fmt.Errorf("unsupported type %s (%T) in composite-typedef", t.ID(), t) + return nil, fmt.Errorf("unsupported type %s (%T) in composite-typedef", typ.ID(), typ) } } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index c9dfdcab5a..4b7e5fa3bb 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -250,17 +250,17 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy for _, typ := range types { - switch x := typ.(type) { + switch typ := typ.(type) { case cadence.CompositeType: // Encode struct-type, resource-type, contract-type, event-type, or enum-type. - err = e.encodeCompositeType(x, tids) + err = e.encodeCompositeType(typ, tids) if err != nil { return err } case cadence.InterfaceType: // Encode struct-interface-type, resource-interface-type, or contract-interface-type. - err = e.encodeInterfaceType(x, tids) + err = e.encodeInterfaceType(typ, tids) if err != nil { return err } @@ -373,105 +373,105 @@ func (e *Encoder) encodeValue( // element 1: value } - switch x := v.(type) { + switch v := v.(type) { case cadence.Void: - return e.encodeVoid(x) + return e.encodeVoid(v) case cadence.Optional: - return e.encodeOptional(x, tids) + return e.encodeOptional(v, tids) case cadence.Bool: - return e.encodeBool(x) + return e.encodeBool(v) case cadence.Character: - return e.encodeCharacter(x) + return e.encodeCharacter(v) case cadence.String: - return e.encodeString(x) + return e.encodeString(v) case cadence.Address: - return e.encodeAddress(x) + return e.encodeAddress(v) case cadence.Int: - return e.encodeInt(x) + return e.encodeInt(v) case cadence.Int8: - return e.encodeInt8(x) + return e.encodeInt8(v) case cadence.Int16: - return e.encodeInt16(x) + return e.encodeInt16(v) case cadence.Int32: - return e.encodeInt32(x) + return e.encodeInt32(v) case cadence.Int64: - return e.encodeInt64(x) + return e.encodeInt64(v) case cadence.Int128: - return e.encodeInt128(x) + return e.encodeInt128(v) case cadence.Int256: - return e.encodeInt256(x) + return e.encodeInt256(v) case cadence.UInt: - return e.encodeUInt(x) + return e.encodeUInt(v) case cadence.UInt8: - return e.encodeUInt8(x) + return e.encodeUInt8(v) case cadence.UInt16: - return e.encodeUInt16(x) + return e.encodeUInt16(v) case cadence.UInt32: - return e.encodeUInt32(x) + return e.encodeUInt32(v) case cadence.UInt64: - return e.encodeUInt64(x) + return e.encodeUInt64(v) case cadence.UInt128: - return e.encodeUInt128(x) + return e.encodeUInt128(v) case cadence.UInt256: - return e.encodeUInt256(x) + return e.encodeUInt256(v) case cadence.Word8: - return e.encodeWord8(x) + return e.encodeWord8(v) case cadence.Word16: - return e.encodeWord16(x) + return e.encodeWord16(v) case cadence.Word32: - return e.encodeWord32(x) + return e.encodeWord32(v) case cadence.Word64: - return e.encodeWord64(x) + return e.encodeWord64(v) case cadence.Fix64: - return e.encodeFix64(x) + return e.encodeFix64(v) case cadence.UFix64: - return e.encodeUFix64(x) + return e.encodeUFix64(v) case cadence.Array: - return e.encodeArray(x, tids) + return e.encodeArray(v, tids) case cadence.Dictionary: - return e.encodeDictionary(x, tids) + return e.encodeDictionary(v, tids) case cadence.Struct: - return e.encodeStruct(x, tids) + return e.encodeStruct(v, tids) case cadence.Resource: - return e.encodeResource(x, tids) + return e.encodeResource(v, tids) case cadence.Event: - return e.encodeEvent(x, tids) + return e.encodeEvent(v, tids) case cadence.Contract: - return e.encodeContract(x, tids) + return e.encodeContract(v, tids) case cadence.Path: - return e.encodePath(x) + return e.encodePath(v) case cadence.TypeValue: // cadence.TypeValue is encoded as self-contained, without any @@ -486,13 +486,13 @@ func (e *Encoder) encodeValue( // traversal order. // // If x.StaticType is nil, type value is encoded as nil. - return e.encodeNullableTypeValue(x.StaticType, ccfTypeIDByCadenceType{}) + return e.encodeNullableTypeValue(v.StaticType, ccfTypeIDByCadenceType{}) case cadence.StorageCapability: - return e.encodeCapability(x) + return e.encodeCapability(v) case cadence.Enum: - return e.encodeEnum(x, tids) + return e.encodeEnum(v, tids) case cadence.Function: // cadence.Function is encoded as self-contained, without any @@ -505,7 +505,7 @@ func (e *Encoder) encodeValue( // it is only encoded once and is subsequently represented by its CCF ID. // For function value encoding, CCF type ID is sequentially generated by // traversal order of sorted parameters and return type. - return e.encodeFunction(x.FunctionType, ccfTypeIDByCadenceType{}) + return e.encodeFunction(v.FunctionType, ccfTypeIDByCadenceType{}) default: panic(cadenceErrors.NewUnexpectedError("cannot encode unsupported value (%T)", v)) @@ -1792,7 +1792,7 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) // Here, static type is different from runtime type. - switch typ := staticType.(type) { + switch staticType := staticType.(type) { case *cadence.OptionalType: // Handle special case of runtime type being OptionalType{NeverType}. // We handle special case of Optional{nil} because its runtime type is OptionalType{NeverType} @@ -1808,13 +1808,13 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) // Handle special case of static type being OptionalType{ReferenceType} // since runtime type is optional type of the deferenced type. if or, ok := runtimeType.(*cadence.OptionalType); ok { - return needToEncodeRuntimeType(typ.Type, or.Type) + return needToEncodeRuntimeType(staticType.Type, or.Type) } case *cadence.ReferenceType: // Handle special case of static type being ReferenceType. // Encoder doesn't need to encode runtime type if runtime type is the deferenced type of static type. - return needToEncodeRuntimeType(typ.Type, runtimeType) + return needToEncodeRuntimeType(staticType.Type, runtimeType) } return true @@ -1852,16 +1852,15 @@ func getOptionalInnerTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtim // isOptionalNeverType returns true if t is (nested) optional never type. func isOptionalNeverType(t cadence.Type) bool { for { - switch ot := t.(type) { - case *cadence.OptionalType: - if ot.Type.Equal(cadence.NewNeverType()) { - return true - } - t = ot.Type - - default: + ot, ok := t.(*cadence.OptionalType) + if !ok { return false } + + if ot.Type.Equal(cadence.NewNeverType()) { + return true + } + t = ot.Type } } diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 075676117b..7bf3889455 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -72,7 +72,7 @@ func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDB var cborTagNum uint64 - switch t := typ.(type) { + switch typ := typ.(type) { case *cadence.StructType: cborTagNum = CBORTagStructType @@ -89,7 +89,7 @@ func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDB cborTagNum = CBORTagEnumType default: - panic(cadenceErrors.NewUnexpectedError("unexpected composite type %s (%T)", t.ID(), t)) + panic(cadenceErrors.NewUnexpectedError("unexpected composite type %s (%T)", typ.ID(), typ)) } // Encode tag number indicating composite type. @@ -220,7 +220,7 @@ func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDB var cborTagNum uint64 - switch t := typ.(type) { + switch typ := typ.(type) { case *cadence.StructInterfaceType: cborTagNum = CBORTagStructInterfaceType @@ -231,7 +231,7 @@ func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDB cborTagNum = CBORTagContractInterfaceType default: - panic(cadenceErrors.NewUnexpectedError("unexpected interface type %s (%T)", t.ID(), t)) + panic(cadenceErrors.NewUnexpectedError("unexpected interface type %s (%T)", typ.ID(), typ)) } // Encode tag number indicating interface type. diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index 6d7ec4dc16..bc8e3ec500 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -76,39 +76,39 @@ func (ct *compositeTypes) traverseValue(v cadence.Value) { // Traverse v's elements for runtime types. // Note: don't need to traverse fields of cadence.Enum // because enum's field is an integer subtype. - switch x := v.(type) { + switch v := v.(type) { case cadence.Optional: - ct.traverseValue(x.Value) + ct.traverseValue(v.Value) case cadence.Array: - for _, element := range x.Values { + for _, element := range v.Values { ct.traverseValue(element) } case cadence.Dictionary: - for _, pair := range x.Pairs { + for _, pair := range v.Pairs { ct.traverseValue(pair.Key) ct.traverseValue(pair.Value) } case cadence.Struct: - for _, field := range x.Fields { + for _, field := range v.Fields { ct.traverseValue(field) } case cadence.Resource: - for _, field := range x.Fields { + for _, field := range v.Fields { ct.traverseValue(field) } case cadence.Event: - for _, field := range x.Fields { + for _, field := range v.Fields { ct.traverseValue(field) } case cadence.Contract: - for _, field := range x.Fields { + for _, field := range v.Fields { ct.traverseValue(field) } @@ -121,41 +121,41 @@ func (ct *compositeTypes) traverseValue(v cadence.Value) { // such as OptionalType. // Runtime needs to be checked when typ contains any abstract type. func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) { - switch t := typ.(type) { + switch typ := typ.(type) { case *cadence.OptionalType: - return ct.traverseType(t.Type) + return ct.traverseType(typ.Type) case cadence.ArrayType: - return ct.traverseType(t.Element()) + return ct.traverseType(typ.Element()) case *cadence.DictionaryType: - checkKeyRuntimeType := ct.traverseType(t.KeyType) - checkValueRuntimeType := ct.traverseType(t.ElementType) + checkKeyRuntimeType := ct.traverseType(typ.KeyType) + checkValueRuntimeType := ct.traverseType(typ.ElementType) return checkKeyRuntimeType || checkValueRuntimeType case *cadence.CapabilityType: - return ct.traverseType(t.BorrowType) + return ct.traverseType(typ.BorrowType) case *cadence.ReferenceType: - return ct.traverseType(t.Type) + return ct.traverseType(typ.Type) case *cadence.RestrictedType: - check := ct.traverseType(t.Type) - for _, restriction := range t.Restrictions { + check := ct.traverseType(typ.Type) + for _, restriction := range typ.Restrictions { checkRestriction := ct.traverseType(restriction) check = check || checkRestriction } return check case cadence.CompositeType: // struct, resource, event, contract, enum - newType := ct.add(t) + newType := ct.add(typ) if !newType { - return ct.abstractTypes[t.ID()] + return ct.abstractTypes[typ.ID()] } check := false - fields := t.CompositeFields() + fields := typ.CompositeFields() for _, field := range fields { checkField := ct.traverseType(field.Type) check = check || checkField @@ -164,12 +164,12 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) // Don't need to traverse initializers because // they are not encoded and their types aren't needed. - ct.abstractTypes[t.ID()] = check + ct.abstractTypes[typ.ID()] = check return check case cadence.InterfaceType: // struct interface, resource interface, contract interface - ct.add(t) + ct.add(typ) // Don't need to traverse fields or initializers because // they are not encoded and their types aren't needed. From 80c9f49b88b0bae9598fd1fa47bfabd5cbcc9821 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:47:16 -0500 Subject: [PATCH 146/173] Make CCF codec panic on implementation errors Thanks @turbolent for spotting this! --- encoding/ccf/decode_typedef.go | 3 ++- encoding/ccf/encode.go | 2 +- encoding/ccf/encode_typedef.go | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 8bc239ceff..086f4f9992 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -24,6 +24,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/runtime/common" + cadenceErrors "github.com/onflow/cadence/runtime/errors" ) // decodeTypeDefs decodes composite/interface type definitions as @@ -111,7 +112,7 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { for id, raw := range rawFields { //nolint:maprange typ, err := types.typ(id) if err != nil { - return nil, err + panic(cadenceErrors.NewUnexpectedErrorFromCause(err)) } dec := NewDecoder(d.gauge, raw) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 4b7e5fa3bb..eda79f95d0 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1481,7 +1481,7 @@ func (e *Encoder) encodeCompositeTypeValue( ) error { ccfID, ok := visited[cadenceTypeID] if !ok { - return fmt.Errorf("CCF type ID not found for composite type value %s", cadenceTypeID) + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for composite type value %s", cadenceTypeID)) } // Encode given tag number indicating cadence type value. diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go index 7bf3889455..a0fc21f206 100644 --- a/encoding/ccf/encode_typedef.go +++ b/encoding/ccf/encode_typedef.go @@ -19,8 +19,6 @@ package ccf import ( - "fmt" - "github.com/onflow/cadence" cadenceErrors "github.com/onflow/cadence/runtime/errors" ) @@ -67,7 +65,7 @@ import ( func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { ccfID, err := tids.id(typ) if err != nil { - return fmt.Errorf("CCF type ID not found for composite type %s (%T)", typ.ID(), typ) + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for composite type %s (%T)", typ.ID(), typ)) } var cborTagNum uint64 @@ -215,7 +213,7 @@ func (e *Encoder) encodeCompositeTypeField(typ cadence.Field, tids ccfTypeIDByCa func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDByCadenceType) error { ccfID, err := tids.id(typ) if err != nil { - return fmt.Errorf("CCF type ID not found for interface type %s (%T)", typ.ID(), typ) + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for interface type %s (%T)", typ.ID(), typ)) } var cborTagNum uint64 From f6f08eed16c991f320a671801762f8f79ba30b29 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:52:41 -0500 Subject: [PATCH 147/173] Replace map of raw fields with slice for CCF decoder Thanks @turbolent for identifying potential coding changes in the future that could require determinism inside a function when metering is added. "Even though I can't currently see a problem, i.e. the function only has local side-effects, this might not stay true forever (e.g. we are going to add metering)." --- encoding/ccf/decode_typedef.go | 74 +++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go index 086f4f9992..d7eacfc52a 100644 --- a/encoding/ccf/decode_typedef.go +++ b/encoding/ccf/decode_typedef.go @@ -27,6 +27,11 @@ import ( cadenceErrors "github.com/onflow/cadence/runtime/errors" ) +type rawFieldsWithCCFTypeID struct { + typeID ccfTypeID + rawFields []byte +} + // decodeTypeDefs decodes composite/interface type definitions as // language=CDDL // composite-typedef = [ @@ -59,7 +64,7 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { // NOTE: composite fields are not decoded while composite types are decoded // because field type might reference composite type that hasn't decoded yet. - rawFields := make(map[ccfTypeID][]byte, count) + rawFieldsOfTypes := make([]rawFieldsWithCCFTypeID, 0, count) // cadenceTypeIDs is used to check if cadence type IDs are unique in type definitions. cadenceTypeIDs := make(map[cadenceTypeID]struct{}, count) @@ -68,7 +73,7 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { var previousCadenceID cadenceTypeID for i := uint64(0); i < count; i++ { - ccfID, cadenceID, err := d.decodeTypeDef(types, rawFields) + ccfID, cadenceID, rawFields, err := d.decodeTypeDef(types) if err != nil { return nil, err } @@ -104,18 +109,22 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { ) } + if len(rawFields) > 0 { + rawFieldsOfTypes = append(rawFieldsOfTypes, rawFieldsWithCCFTypeID{typeID: ccfID, rawFields: rawFields}) + } + cadenceTypeIDs[cadenceID] = struct{}{} previousCadenceID = cadenceID } // Decode fields after all high-level type definitions are resolved. - for id, raw := range rawFields { //nolint:maprange - typ, err := types.typ(id) + for _, rawFields := range rawFieldsOfTypes { + typ, err := types.typ(rawFields.typeID) if err != nil { panic(cadenceErrors.NewUnexpectedErrorFromCause(err)) } - dec := NewDecoder(d.gauge, raw) + dec := NewDecoder(d.gauge, rawFields.rawFields) fields, err := dec.decodeCompositeFields(types, dec.decodeInlineType) if err != nil { return nil, err @@ -176,15 +185,15 @@ func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { // #6.178(interface-type) func (d *Decoder) decodeTypeDef( types *cadenceTypeByCCFTypeID, - rawFields map[ccfTypeID][]byte, ) ( ccfTypeID, cadenceTypeID, + []byte, error, ) { tagNum, err := d.dec.DecodeTagNumber() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } switch tagNum { @@ -198,7 +207,7 @@ func (d *Decoder) decodeTypeDef( nil, ) } - return d.decodeCompositeType(types, rawFields, ctr) + return d.decodeCompositeType(types, ctr) case CBORTagResourceType: ctr := func(location common.Location, identifier string) cadence.Type { @@ -210,7 +219,7 @@ func (d *Decoder) decodeTypeDef( nil, ) } - return d.decodeCompositeType(types, rawFields, ctr) + return d.decodeCompositeType(types, ctr) case CBORTagEventType: ctr := func(location common.Location, identifier string) cadence.Type { @@ -222,7 +231,7 @@ func (d *Decoder) decodeTypeDef( nil, ) } - return d.decodeCompositeType(types, rawFields, ctr) + return d.decodeCompositeType(types, ctr) case CBORTagContractType: ctr := func(location common.Location, identifier string) cadence.Type { @@ -234,7 +243,7 @@ func (d *Decoder) decodeTypeDef( nil, ) } - return d.decodeCompositeType(types, rawFields, ctr) + return d.decodeCompositeType(types, ctr) case CBORTagEnumType: ctr := func(location common.Location, identifier string) cadence.Type { @@ -247,7 +256,7 @@ func (d *Decoder) decodeTypeDef( nil, ) } - return d.decodeCompositeType(types, rawFields, ctr) + return d.decodeCompositeType(types, ctr) case CBORTagStructInterfaceType: ctr := func(location common.Location, identifier string) cadence.Type { @@ -288,6 +297,7 @@ func (d *Decoder) decodeTypeDef( default: return ccfTypeID(0), cadenceTypeID(""), + nil, fmt.Errorf("unsupported type definition with CBOR tag number %d", tagNum) } } @@ -308,45 +318,49 @@ func (d *Decoder) decodeTypeDef( // ] func (d *Decoder) decodeCompositeType( types *cadenceTypeByCCFTypeID, - rawFields map[ccfTypeID][]byte, constructor func(common.Location, string) cadence.Type, -) (ccfTypeID, cadenceTypeID, error) { +) ( + ccfTypeID, + cadenceTypeID, + []byte, + error, +) { // Decode array head of length 3. err := decodeCBORArrayWithKnownSize(d.dec, 3) if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // element 0: id ccfID, err := d.decodeCCFTypeID() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // "Valid CCF Encoding Requirements" in CCF specs: // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." if types.has(ccfID) { - return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in composite-type", ccfID) + return ccfTypeID(0), cadenceTypeID(""), nil, fmt.Errorf("found duplicate CCF type ID %d in composite-type", ccfID) } // element 1: cadence-type-id cadenceID, location, identifier, err := d.decodeCadenceTypeID() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // element 2: fields rawField, err := d.dec.DecodeRawBytes() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // The return value can be ignored, because its non-existence was already checked above _ = types.add(ccfID, constructor(location, identifier)) - rawFields[ccfID] = rawField - return ccfID, cadenceID, nil + + return ccfID, cadenceID, rawField, nil } // decodeCompositeFields decodes field types as @@ -450,34 +464,40 @@ func (d *Decoder) decodeCompositeField(types *cadenceTypeByCCFTypeID, decodeType func (d *Decoder) decodeInterfaceType( types *cadenceTypeByCCFTypeID, constructor func(common.Location, string) cadence.Type, -) (ccfTypeID, cadenceTypeID, error) { +) ( + ccfTypeID, + cadenceTypeID, + []byte, // always nil since interface type definition doesn't contain fields + error, +) { // Decode array head of length 2. err := decodeCBORArrayWithKnownSize(d.dec, 2) if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // element 0: id ccfID, err := d.decodeCCFTypeID() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // "Valid CCF Encoding Requirements" in CCF specs: // // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." if types.has(ccfID) { - return ccfTypeID(0), cadenceTypeID(""), fmt.Errorf("found duplicate CCF type ID %d in interface-type", ccfID) + return ccfTypeID(0), cadenceTypeID(""), nil, fmt.Errorf("found duplicate CCF type ID %d in interface-type", ccfID) } // element 1: cadence-type-id cadenceID, location, identifier, err := d.decodeCadenceTypeID() if err != nil { - return ccfTypeID(0), cadenceTypeID(""), err + return ccfTypeID(0), cadenceTypeID(""), nil, err } // The return value can be ignored, because its non-existence was already checked above _ = types.add(ccfID, constructor(location, identifier)) - return ccfID, cadenceID, nil + + return ccfID, cadenceID, nil, nil } From 565f84e6576a6148f6b76d77cd2e5f68b752bd57 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 12 Apr 2023 09:32:30 -0500 Subject: [PATCH 148/173] Reduce CCF decoder limits for array and map elems Reduce default max elements limit to 20_000_000 for arrays and maps. These limits are large (and can be reduced more if needed): - Current grpc limit is 20 MB and these limits are large enough to support unrealistic CCF message with zero-overhead and elements of 1 byte size using up entire 20 MB grpc limit. - It would typically take many thousands of "normal" CCF-encoded events to get near the 20 MB grpc limit with one transaction. Also added comments explaining the security considerations for having limits. Thanks @turbolent for reminder to document limits! --- encoding/ccf/decode.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index e9b7358106..a76bdb66ed 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -36,11 +36,20 @@ import ( // // See https://github.com/fxamacker/cbor: // "For best performance, reuse EncMode and DecMode after creating them." +// +// Security Considerations in Section 10 of RFC 8949 states: +// +// "Hostile input may be constructed to overrun buffers, to overflow or underflow integer arithmetic, +// or to cause other decoding disruption. CBOR data items might have lengths or sizes that are +// intentionally extremely large or too short. Resource exhaustion attacks might attempt to lure a +// decoder into allocating very big data items (strings, arrays, maps, or even arbitrary precision numbers) +// or exhaust the stack depth by setting up deeply nested items. Decoders need to have appropriate resource +// management to mitigate these attacks." var CBORDecMode = func() cbor.DecMode { decMode, err := cbor.DecOptions{ IntDec: cbor.IntDecConvertNone, - MaxArrayElements: math.MaxInt64, - MaxMapPairs: math.MaxInt64, + MaxArrayElements: 20_000_000, // 20 MB is current grpc size limit so this is more than enough + MaxMapPairs: 20_000_000, // 20 MB is current grpc size limit so this is more than enough MaxNestedLevels: math.MaxInt16, }.DecMode() if err != nil { @@ -49,7 +58,10 @@ var CBORDecMode = func() cbor.DecMode { return decMode }() -// A Decoder decodes CCF-encoded representations of Cadence values. +// Decoder decodes CCF-encoded representations of Cadence values. +// Since CBOR security considerations apply to CCF, the CBOR +// codec used by CCF Decoder uses limits (e.g. MaxArrayElements, +// MaxMapPairs, MaxNestedLevels) specified by CBORDecMode. type Decoder struct { // CCF codec uses CBOR codec under the hood. dec *cbor.StreamDecoder From 12b7116000c8f9f6900f009d775c6dfa636e8f6e Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed, 12 Apr 2023 09:51:46 -0500 Subject: [PATCH 149/173] Deny indef length CBOR data items in CCF As a CBOR security consideration, make CCF decoder reject messages containing indefinite length CBOR byte string, text string, arrays, and maps. --- encoding/ccf/decode.go | 1 + 1 file changed, 1 insertion(+) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index a76bdb66ed..a1677f86a9 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -47,6 +47,7 @@ import ( // management to mitigate these attacks." var CBORDecMode = func() cbor.DecMode { decMode, err := cbor.DecOptions{ + IndefLength: cbor.IndefLengthForbidden, IntDec: cbor.IntDecConvertNone, MaxArrayElements: 20_000_000, // 20 MB is current grpc size limit so this is more than enough MaxMapPairs: 20_000_000, // 20 MB is current grpc size limit so this is more than enough From 093955907ce5711c5880fa8dacb38fe87433484f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 12 Apr 2023 09:05:28 -0700 Subject: [PATCH 150/173] separate breaking changes --- .github/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 481646c565..7c2ce51c56 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,8 +1,12 @@ changelog: categories: - - title: 💥 Breaking Changes + - title: 💥 Language Breaking Changes labels: - - Breaking Change + - Language Breaking Change + - Storage Breaking Change + - title: 💥 Go API Breaking Chance + labels: + - Go API Breaking Change - title: ⭐ Features labels: - Feature From c49628b5acbe66c3b62ecf7641ffbb4e66492645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Apr 2023 17:27:34 -0700 Subject: [PATCH 151/173] check for potential resource loss in reference expression --- runtime/sema/check_reference_expression.go | 4 +++- runtime/tests/checker/reference_test.go | 21 +++++++++++++++++++++ runtime/tests/checker/resources_test.go | 5 +++-- runtime/tests/interpreter/resources_test.go | 5 +++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index 1300b6f960..36c6e1e4bc 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -71,12 +71,14 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere referencedExpression := referenceExpression.Expression - _ = checker.VisitExpression(referencedExpression, targetType) + referencedType := checker.VisitExpression(referencedExpression, targetType) if referenceType == nil { return InvalidType } + checker.checkUnusedExpressionResourceLoss(referencedType, referencedExpression) + checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, returnType) return returnType diff --git a/runtime/tests/checker/reference_test.go b/runtime/tests/checker/reference_test.go index c38f87d691..34a5555215 100644 --- a/runtime/tests/checker/reference_test.go +++ b/runtime/tests/checker/reference_test.go @@ -721,6 +721,27 @@ func TestCheckInvalidReferenceResourceLoss(t *testing.T) { assert.IsType(t, &sema.ResourceLossError{}, errs[0]) } +func TestCheckInvalidReferenceResourceLoss2(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R {} + + fun f(): @R { + return <- create R() + } + + fun test() { + let ref = &f() as &R + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) +} + func TestCheckInvalidReferenceIndexingIfReferencedNotIndexable(t *testing.T) { t.Parallel() diff --git a/runtime/tests/checker/resources_test.go b/runtime/tests/checker/resources_test.go index 321147a559..f67489c989 100644 --- a/runtime/tests/checker/resources_test.go +++ b/runtime/tests/checker/resources_test.go @@ -9003,8 +9003,9 @@ func TestCheckResourceInvalidationWithMove(t *testing.T) { } `) - errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[0]) + errs := RequireCheckerErrors(t, err, 2) + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) + assert.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[1]) }) t.Run("in casting expression", func(t *testing.T) { diff --git a/runtime/tests/interpreter/resources_test.go b/runtime/tests/interpreter/resources_test.go index cc462eeba1..82268b2dd0 100644 --- a/runtime/tests/interpreter/resources_test.go +++ b/runtime/tests/interpreter/resources_test.go @@ -1748,8 +1748,9 @@ func TestInterpretInvalidatedResourceValidation(t *testing.T) { }`, ParseCheckAndInterpretOptions{ HandleCheckerError: func(err error) { - errs := checker.RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[0]) + errs := checker.RequireCheckerErrors(t, err, 2) + require.IsType(t, &sema.ResourceLossError{}, errs[0]) + require.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[1]) }, }, ) From 3a8f62240a996025a290fd2fbe38514fe6a59244 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 12 Apr 2023 14:05:54 -0600 Subject: [PATCH 152/173] update doc with regards to identity BLS public keys --- docs/language/crypto.mdx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/language/crypto.mdx b/docs/language/crypto.mdx index 13a7a75c1f..a7a21bf654 100644 --- a/docs/language/crypto.mdx +++ b/docs/language/crypto.mdx @@ -157,7 +157,7 @@ The raw key value depends on the supported signature scheme: The raw public key is 64-bytes long. - `BLS_BLS_12_381`: - The public key is a G_2 (curve over the prime field extension) element. + The public key is a G_2 element (on the curve over the prime field extension). The encoding follows the compressed serialization defined in the [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). A public key is 96-bytes long. @@ -173,9 +173,10 @@ The validation of the public key depends on the supported signature scheme: - `BLS_BLS_12_381`: The given key is correctly serialized following the compressed serialization in [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). - The coordinates represent valid prime field extension elemnents. The resulting point is on the curve, and is on the correct subgroup G_2. + The coordinates represent valid prime field extension elements. The resulting point is on the curve, and is on the correct subgroup G_2. + Note that the point at infinity is accepted and yields the identity public key. Such identity key can be useful when aggregating multiple keys. -Since the validation happen only at the time of creation, public keys are immutable. +Since the validation happens only at the time of creation, public keys are immutable. ```cadence publicKey.signatureAlgorithm = SignatureAlgorithm.ECDSA_secp256k1 // Not allowed @@ -225,15 +226,15 @@ ECDSA verification is implemented as defined in ANS X9.62 (also referred by [FIP A valid signature would be generated using the expected `signedData`, `domainSeparationTag` and `hashAlgorithm` used to verify. - BLS (`BLS_BLS_12_381`): - - `signature` expects a G_1 (subgroup of the curve over the prime field) point. + - `signature` expects a G_1 point (on the curve over the prime field). The encoding follows the compressed serialization defined in the [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). A signature is 48-bytes long. - `signedData` is the arbitrary message to verify the signature against. - `domainSeparationTag` is the expected domain tag. All tags are accepted (check [KMAC128 for BLS](#KMAC128-for-BLS)). - `hashAlgorithm` only accepts `KMAC128_BLS_BLS12_381`. It is the algorithm used to hash the message along with the given tag (check [KMAC128 for BLS](#KMAC128-for-BLS)). -BLS verification performs the necessary membership check of the signature while the membership check of the public key is performed at the creation of the `PublicKey` object -and not repeated during the signature verification. +BLS verification performs the necessary membership check on the signature while the membership check of the public key is performed at the creation of the `PublicKey` object +and is not repeated during the signature verification. In order to prevent equivocation issues, a verification under the identity public key always returns `false`. The verificaction uses a hash-to-curve algorithm to hash the `signedData` into a `G_1` point, following the `hash_to_curve` method described in the [draft-irtf-cfrg-hash-to-curve-14](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-3). While KMAC128 is used as a hash-to-field method resulting in two field elements, the mapping to curve is implemented using the [simplified SWU](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-6.6.3). @@ -252,7 +253,7 @@ These tools are defined in the built-in `BLS` contract, which does not need to b ### Proof of Possession (PoP) Multi-signature verification in BLS requires a defense against rogue public-key attacks. Multiple ways are -available to protect BLS verification. The language provides the proof of possession of private key as a defense tool. +available to protect BLS verification. Cadence provides the proof of possession of private key as a defense tool. The proof of possession of private key is a BLS signature over the public key itself. The PoP signature follows the same requirements of a BLS signature (detailed in [Signature verification](#Signature-verification)), except it uses a special domain separation tag. The key expected to be used in KMAC128 is the UTF-8 encoding of `"BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"`. @@ -270,7 +271,7 @@ Signatures could be generated from the same or distinct messages, they could also be the aggregation of other signatures. The order of the signatures in the slice does not matter since the aggregation is commutative. There is no subgroup membership check performed on the input signatures. -If the array is empty or if decoding one of the signature fails, the program aborts +If the array is empty or if decoding one of the signatures fails, the program aborts. The output signature can be verified against an aggregated public key to authenticate multiple signers at once. Since the `verify` method accepts a single data to verify against, it is only possible to @@ -287,11 +288,18 @@ Aggregates multiple BLS public keys into one. The order of the public keys in the slice does not matter since the aggregation is commutative. The input keys are guaranteed to be in the correct subgroup since subgroup membership is checked at the key creation time. -If the array is empty or any of the input keys is not a BLS key, the program aborts +If the array is empty or any of the input keys is not a BLS key, the program aborts. +Note that the identity public key is a valid input to this function and it represents the +identity element of aggregation. The output public key can be used to verify aggregated signatures to authenticate multiple signers at once. Since the `verify` method accepts a single data to verify against, it is only possible to verfiy multiple signatures of the same message. +The identity public key is a possible output of the function, though signature verifications +against identity result in `false`. + +In order to prevent rogue key attacks when verifying aggregated signatures, it is important to verfiy the +PoP of each individual key involved in the aggregation process. ## Crypto Contract From 0d227f8dd5139aa8d4ffd2bc087c5705ee047755 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 12 Apr 2023 14:17:23 -0600 Subject: [PATCH 153/173] add callouts --- docs/language/crypto.mdx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/language/crypto.mdx b/docs/language/crypto.mdx index a7a21bf654..b48fc1370f 100644 --- a/docs/language/crypto.mdx +++ b/docs/language/crypto.mdx @@ -176,6 +176,12 @@ The validation of the public key depends on the supported signature scheme: The coordinates represent valid prime field extension elements. The resulting point is on the curve, and is on the correct subgroup G_2. Note that the point at infinity is accepted and yields the identity public key. Such identity key can be useful when aggregating multiple keys. + + +🚧 Status: Accepting the BLS identity key is going to be available in the upcoming release of Cadence on Mainnet. + + + Since the validation happens only at the time of creation, public keys are immutable. ```cadence @@ -236,6 +242,13 @@ A valid signature would be generated using the expected `signedData`, `domainSep BLS verification performs the necessary membership check on the signature while the membership check of the public key is performed at the creation of the `PublicKey` object and is not repeated during the signature verification. In order to prevent equivocation issues, a verification under the identity public key always returns `false`. + + +🚧 Status: Returning `false` when verifying against a BLS identity key is going to be available in the upcoming release of Cadence on Mainnet. +Currently, BLS identity keys can only be constructed as an output of `aggregatePublicKeys`. + + + The verificaction uses a hash-to-curve algorithm to hash the `signedData` into a `G_1` point, following the `hash_to_curve` method described in the [draft-irtf-cfrg-hash-to-curve-14](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-3). While KMAC128 is used as a hash-to-field method resulting in two field elements, the mapping to curve is implemented using the [simplified SWU](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-6.6.3). From 4a6aa577f9b69074f1dfc0e20743031e73e1486a Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 13 Apr 2023 10:40:27 -0500 Subject: [PATCH 154/173] Add more comments for CCF codec --- encoding/ccf/consts.go | 6 ++++++ encoding/ccf/decode.go | 5 +++++ encoding/ccf/encode.go | 10 +++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go index a19e5aa4b9..ee01a62912 100644 --- a/encoding/ccf/consts.go +++ b/encoding/ccf/consts.go @@ -35,6 +35,12 @@ package ccf // DO *NOT* REPLACE EXISTING TAG NUMBERS! // DO *NOT* ADD NEW TAG NUMBERS IN BETWEEN! // DO *NOT* APPEND NEW TAG NUMBERS AT END! +// +// By not appending tag numbers to the end, we have larger block of +// unused tag numbers if needed. Tag numbers in 128-255 are +// unassigned in CBOR, and we currently use 128-231. Since each +// group of tags in this range have reserved space available, +// there is no need to append new tag numbers in 232-255. const ( // CBOR tag numbers (128-135) for root objects (131-135 are reserved) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index a1677f86a9..9dc9ef22ef 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -388,6 +388,11 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca return d.decodePath() case cadence.MetaType: + // cadenceTypeByCCFTypeID uses a map with CCF type ID as keys. + // CCF type ID can collide if we reuse types (the variable) + // for type values because: + // - CCF type IDs are zero-based for composite TypeValue. + // - CCF type IDs are zero-based for composite type definitions. typeValue, err := d.decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) if err != nil { return nil, err diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index eda79f95d0..7a493a2c48 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1779,7 +1779,7 @@ func (e *Encoder) encodeNilTypeValue() error { return e.enc.EncodeNil() } -// needToEncodeType returns true if runtimeType needs to be encoded because: +// needToEncodeRuntimeType returns true if runtimeType needs to be encoded because: // - static type is missing (top level value doesn't have static type) // - static type is different from runtime type (static type is abstract type) func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) bool { @@ -1805,8 +1805,12 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) return false } - // Handle special case of static type being OptionalType{ReferenceType} - // since runtime type is optional type of the deferenced type. + // If both staticType and runtimeType are optional types, check again + // with unwrapped inner types. For example, runtimeType shouldn't be + // encoded if staticType is optional reference to string type and + // runtimeType is optional string type. After unwrapping optional + // types, needToEncodeRuntimeType returns false because staticType is + // reference to string type and runtimeType is string type. if or, ok := runtimeType.(*cadence.OptionalType); ok { return needToEncodeRuntimeType(staticType.Type, or.Type) } From 51552f153326be5a493682fcd68ae381d9899013 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 13 Apr 2023 11:40:02 -0500 Subject: [PATCH 155/173] Unwrap ref type when reducing inline type for CCF This prevents an edge case from encoding more data than necessary. getTypeToEncodeAsCCFInlineType() returns runtime type to be encoded after removing redundant type info that is present in staticType because staticType is already encoded at higher level. This applies to optional type container that is present in both static type and runtime type. This commit unwraps reference type from static type if present and tries again because reference type is only present in static type. --- encoding/ccf/ccf_test.go | 221 ++++++++++++++++++++++++++++++++++++++- encoding/ccf/encode.go | 33 +++--- 2 files changed, 235 insertions(+), 19 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 73b239b689..4c74ade063 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7120,13 +7120,232 @@ func TestEncodeValueOfReferenceType(t *testing.T) { }, } + // ["a", "b", nil] with type array of reference to optional AnyStruct + referenceToOptionalAnyStructType := encodeTest{ + name: "array of reference to optional AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(cadence.NewOptional(cadence.String("b"))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType( + false, + cadence.NewOptionalType(cadence.NewAnyStructType()), + ))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 138(137(39))])), [130([137(1), "a"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + + optionalReferenceToAnyStructType := encodeTest{ + name: "array of optional reference to AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(cadence.NewOptional(cadence.String("b"))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewOptionalType( + cadence.NewReferenceType( + false, + cadence.NewAnyStructType(), + )))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(138(142([false, 137(39)]))), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + + optionalReferenceToOptionalAnyStructType := encodeTest{ + name: "array of optional reference to optional AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.NewOptional(cadence.String("a"))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.String("b")))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewOptionalType( + cadence.NewReferenceType( + false, + cadence.NewOptionalType( + cadence.NewAnyStructType(), + ))))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":{"value":"a","type":"String"},"type":"Optional"},"type":"Optional"},{"value":{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(138(142([false, 138(137(39))]))), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + testAllEncodeAndDecode(t, referenceToSimpleType, referenceToOptionalSimpleType, - optionalReferenceToSimpleType, referenceToStructType, referenceToAnyStructWithSimpleTypes, referenceToAnyStructWithStructType, + referenceToOptionalAnyStructType, + optionalReferenceToSimpleType, + optionalReferenceToAnyStructType, + optionalReferenceToOptionalAnyStructType, ) } diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 7a493a2c48..06eef301ea 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1824,30 +1824,27 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) return true } +// getTypeToEncodeAsCCFInlineType returns runtime type to be encoded after +// removing redundant type info that is present in staticType because +// staticType is already encoded at higher level. func getTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { - if _, ok := staticType.(*cadence.OptionalType); ok { - return getOptionalInnerTypeToEncodeAsCCFInlineType(staticType, runtimeType) - } - return runtimeType -} - -// getOptionalInnerTypeToEncodeAsCCFInlineType returns cadence.Type that needs to be encoded as CCF inline type. -// Since static type is encoded at higher level, inline type shouldn't repeat encoded static type. -// So inline type is runtime type after removing OptionalType wrappers that are present in static type. -func getOptionalInnerTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { - for { - sot, ok := staticType.(*cadence.OptionalType) - if !ok { - break - } + switch staticType := staticType.(type) { + case *cadence.OptionalType: rot, ok := runtimeType.(*cadence.OptionalType) if !ok { - // static type is optional type while runtime type isn't. + // staticType is optional type while runtime type isn't. panic(cadenceErrors.NewUnexpectedError("static type (%T) is optional type while runtime type (%T) isn't", staticType, runtimeType)) } - staticType = sot.Type - runtimeType = rot.Type + // Unwrap optional type container from both staticType and runtimeType. + // Static type is encoded at higher level, so encoded runtime type shouldn't repeat + // the same info. Here, inline type is runtime type after unwrapping optional type + // that is present in both static and runtime types. + return getTypeToEncodeAsCCFInlineType(staticType.Type, rot.Type) + + case *cadence.ReferenceType: + // Unwrap reference type from static type and try again. + return getTypeToEncodeAsCCFInlineType(staticType.Type, runtimeType) } return runtimeType From b6b29f23c0e264b300c1bf485363be351d3d2b53 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:30:07 -0500 Subject: [PATCH 156/173] Fix comment in CCF test Thanks @turbolent for spotting this! --- encoding/ccf/ccf_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 4c74ade063..1855fe52a6 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -7136,7 +7136,7 @@ func TestEncodeValueOfReferenceType(t *testing.T) { // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} // // language=edn, format=ccf - // 130([139(142([false, 138(137(39))])), [130([137(1), "a"]), null]]) + // 130([139(142([false, 138(137(39))])), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) // // language=cbor, format=ccf // tag From 6e5cb150599a9d21c333a38766b98d12ae08518e Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:38:51 -0500 Subject: [PATCH 157/173] Replace recursion in getTypeToEncodeAsCCFInlineType Replaced recursive call with for-loop. Thanks @turbolent for suggesting this! --- encoding/ccf/encode.go | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 06eef301ea..9f71212e3d 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1828,26 +1828,30 @@ func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) // removing redundant type info that is present in staticType because // staticType is already encoded at higher level. func getTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { - switch staticType := staticType.(type) { - case *cadence.OptionalType: - rot, ok := runtimeType.(*cadence.OptionalType) - if !ok { - // staticType is optional type while runtime type isn't. - panic(cadenceErrors.NewUnexpectedError("static type (%T) is optional type while runtime type (%T) isn't", staticType, runtimeType)) - } + for { + switch st := staticType.(type) { + case *cadence.OptionalType: + rot, ok := runtimeType.(*cadence.OptionalType) + if !ok { + // staticType is optional type while runtime type isn't. + panic(cadenceErrors.NewUnexpectedError("static type (%T) is optional type while runtime type (%T) isn't", staticType, runtimeType)) + } - // Unwrap optional type container from both staticType and runtimeType. - // Static type is encoded at higher level, so encoded runtime type shouldn't repeat - // the same info. Here, inline type is runtime type after unwrapping optional type - // that is present in both static and runtime types. - return getTypeToEncodeAsCCFInlineType(staticType.Type, rot.Type) + // Unwrap optional type container from both staticType and runtimeType. + // Static type is encoded at higher level, so encoded runtime type shouldn't repeat + // the same info. Here, inline type is runtime type after unwrapping optional type + // that is present in both static and runtime types. + staticType = st.Type + runtimeType = rot.Type - case *cadence.ReferenceType: - // Unwrap reference type from static type and try again. - return getTypeToEncodeAsCCFInlineType(staticType.Type, runtimeType) - } + case *cadence.ReferenceType: + // Unwrap reference type from static type and try again. + staticType = st.Type - return runtimeType + default: + return runtimeType + } + } } // isOptionalNeverType returns true if t is (nested) optional never type. From adb8d5f3870a6680a7853a4bfc25768e9872af60 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Apr 2023 15:54:33 -0700 Subject: [PATCH 158/173] Fix built-in result variable for optional-typed returns --- runtime/interpreter/interpreter.go | 20 +++- runtime/sema/check_function.go | 14 ++- runtime/tests/checker/function_test.go | 49 ++++++++ runtime/tests/interpreter/function_test.go | 130 +++++++++++++++++++++ 4 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 runtime/tests/interpreter/function_test.go diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 3100d93505..c1e974b5ac 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -755,7 +755,25 @@ func (interpreter *Interpreter) visitFunctionBody( if returnType != sema.VoidType { var resultValue Value if returnType.IsResourceType() { - resultValue = NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) + switch returnValue := returnValue.(type) { + // If this value is an optional value (T?), then transform it into an optional reference (&T)?. + case *SomeValue: + optionalType, ok := returnType.(*sema.OptionalType) + if !ok { + panic(errors.NewUnreachableError()) + } + innerValue := NewEphemeralReferenceValue( + interpreter, + false, + returnValue.value, + optionalType.Type, + ) + resultValue = NewSomeValueNonCopying(interpreter, innerValue) + case NilValue: + resultValue = NilValue{} + default: + resultValue = NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) + } } else { resultValue = returnValue } diff --git a/runtime/sema/check_function.go b/runtime/sema/check_function.go index b4faff9cbd..8797b50f37 100644 --- a/runtime/sema/check_function.go +++ b/runtime/sema/check_function.go @@ -344,8 +344,18 @@ func (checker *Checker) visitWithPostConditions(postConditions *ast.Conditions, if returnType != VoidType { var resultType Type if returnType.IsResourceType() { - resultType = &ReferenceType{ - Type: returnType, + optType, isOptional := returnType.(*OptionalType) + if isOptional { + // If the return type is an optional type T?, then create an optional reference (&T)?. + resultType = &OptionalType{ + Type: &ReferenceType{ + Type: optType.Type, + }, + } + } else { + resultType = &ReferenceType{ + Type: returnType, + } } } else { resultType = returnType diff --git a/runtime/tests/checker/function_test.go b/runtime/tests/checker/function_test.go index 998e687f8b..659b6b0b93 100644 --- a/runtime/tests/checker/function_test.go +++ b/runtime/tests/checker/function_test.go @@ -456,3 +456,52 @@ func TestCheckNativeFunctionDeclaration(t *testing.T) { assert.IsType(t, &sema.InvalidNativeModifierError{}, errs[0]) } + +func TestCheckResultVariable(t *testing.T) { + + t.Parallel() + + t.Run("resource", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R { + post { + result.id == 1234: "Invalid id" + } + return <- create R() + }`, + ) + + require.NoError(t, err) + }) + + t.Run("optional resource", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result!.id == 1234: "invalid id" + } + return nil + }`, + ) + + require.NoError(t, err) + }) +} diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go new file mode 100644 index 0000000000..1f39e2b773 --- /dev/null +++ b/runtime/tests/interpreter/function_test.go @@ -0,0 +1,130 @@ +/* + * 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_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/tests/utils" +) + +func TestInterpretResultVariable(t *testing.T) { + + t.Parallel() + + t.Run("resource", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R { + post { + result.id == 1: "invalid id" + } + return <- create R() + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.CompositeValue{}, result) + resource := result.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt64Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("optional resource", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result!.id == 1: "invalid id" + } + return <- create R() + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.SomeValue{}, result) + someValue := result.(*interpreter.SomeValue) + + innerValue := someValue.InnerValue(inter, interpreter.EmptyLocationRange) + require.IsType(t, &interpreter.CompositeValue{}, innerValue) + + resource := innerValue.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt64Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("optional nil resource", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result == nil: "invalid result" + } + return nil + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + require.Equal(t, interpreter.NilValue{}, result) + }) +} From 65ce61c21e6e160cfc96ecf61d3c641aabc1b45a Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Apr 2023 17:09:28 -0700 Subject: [PATCH 159/173] Refactor code --- runtime/interpreter/interpreter.go | 59 ++++++++++++---------- runtime/tests/interpreter/function_test.go | 46 +++++++++++++++-- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index c1e974b5ac..3a2da353f7 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -749,34 +749,9 @@ func (interpreter *Interpreter) visitFunctionBody( } // If there is a return type, declare the constant `result`. - // If it is a resource type, the constant has the same type as a reference to the return type. - // If it is not a resource type, the constant has the same type as the return type. if returnType != sema.VoidType { - var resultValue Value - if returnType.IsResourceType() { - switch returnValue := returnValue.(type) { - // If this value is an optional value (T?), then transform it into an optional reference (&T)?. - case *SomeValue: - optionalType, ok := returnType.(*sema.OptionalType) - if !ok { - panic(errors.NewUnreachableError()) - } - innerValue := NewEphemeralReferenceValue( - interpreter, - false, - returnValue.value, - optionalType.Type, - ) - resultValue = NewSomeValueNonCopying(interpreter, innerValue) - case NilValue: - resultValue = NilValue{} - default: - resultValue = NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) - } - } else { - resultValue = returnValue - } + resultValue := interpreter.resultValue(returnValue, returnType) interpreter.declareVariable( sema.ResultIdentifier, resultValue, @@ -788,6 +763,38 @@ func (interpreter *Interpreter) visitFunctionBody( return returnValue } +// resultValue returns the value for the `result` constant. +// If the return type is not a resource: +// - The constant has the same type as the return type. +// - `result` value is the same as the return value. +// +// If the return type is a resource: +// - The constant has the same type as a reference to the return type. +// - `result` value is a reference to the return value. +func (interpreter *Interpreter) resultValue(returnValue Value, returnType sema.Type) Value { + if !returnType.IsResourceType() { + return returnValue + } + + if optionalType, ok := returnType.(*sema.OptionalType); ok { + switch returnValue := returnValue.(type) { + // If this value is an optional value (T?), then transform it into an optional reference (&T)?. + case *SomeValue: + innerValue := NewEphemeralReferenceValue( + interpreter, + false, + returnValue.value, + optionalType.Type, + ) + return NewSomeValueNonCopying(interpreter, innerValue) + case NilValue: + return NilValue{} + } + } + + return NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) +} + func (interpreter *Interpreter) visitConditions(conditions []*ast.Condition) { for _, condition := range conditions { interpreter.visitCondition(condition) diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go index 1f39e2b773..5293c77711 100644 --- a/runtime/tests/interpreter/function_test.go +++ b/runtime/tests/interpreter/function_test.go @@ -33,7 +33,7 @@ func TestInterpretResultVariable(t *testing.T) { t.Parallel() - t.Run("resource", func(t *testing.T) { + t.Run("resource type, resource value", func(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` @@ -66,7 +66,7 @@ func TestInterpretResultVariable(t *testing.T) { ) }) - t.Run("optional resource", func(t *testing.T) { + t.Run("optional resource type, resource value", func(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` @@ -104,7 +104,7 @@ func TestInterpretResultVariable(t *testing.T) { ) }) - t.Run("optional nil resource", func(t *testing.T) { + t.Run("optional resource type, nil value", func(t *testing.T) { t.Parallel() inter := parseCheckAndInterpret(t, ` @@ -127,4 +127,44 @@ func TestInterpretResultVariable(t *testing.T) { require.NoError(t, err) require.Equal(t, interpreter.NilValue{}, result) }) + + t.Run("any resource type, optional value", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @AnyResource { + post { + result != nil: "invalid value" + } + + var value: @R? <- create R() + return <- value + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.SomeValue{}, result) + someValue := result.(*interpreter.SomeValue) + + innerValue := someValue.InnerValue(inter, interpreter.EmptyLocationRange) + require.IsType(t, &interpreter.CompositeValue{}, innerValue) + + resource := innerValue.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt64Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) } From d2262a40e90502aee934c5a38ab5ac1dc6eea101 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 14 Apr 2023 13:30:50 -0700 Subject: [PATCH 160/173] Add reference tracking for the result variable --- runtime/interpreter/interpreter.go | 9 ++ runtime/interpreter/interpreter_expression.go | 8 +- runtime/tests/interpreter/function_test.go | 112 ++++++++++++++++++ 3 files changed, 123 insertions(+), 6 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 3a2da353f7..146cae7b3d 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -786,12 +786,15 @@ func (interpreter *Interpreter) resultValue(returnValue Value, returnType sema.T returnValue.value, optionalType.Type, ) + + interpreter.maybeTrackReferencedResourceKindedValue(returnValue.value) return NewSomeValueNonCopying(interpreter, innerValue) case NilValue: return NilValue{} } } + interpreter.maybeTrackReferencedResourceKindedValue(returnValue) return NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) } @@ -4605,6 +4608,12 @@ func (interpreter *Interpreter) ValidateAtreeValue(value atree.Value) { } } +func (interpreter *Interpreter) maybeTrackReferencedResourceKindedValue(value Value) { + if value, ok := value.(ReferenceTrackedResourceKindedValue); ok { + interpreter.trackReferencedResourceKindedValue(value.StorageID(), value) + } +} + func (interpreter *Interpreter) trackReferencedResourceKindedValue( id atree.StorageID, value ReferenceTrackedResourceKindedValue, diff --git a/runtime/interpreter/interpreter_expression.go b/runtime/interpreter/interpreter_expression.go index 0772112698..5e0f3f2f5d 100644 --- a/runtime/interpreter/interpreter_expression.go +++ b/runtime/interpreter/interpreter_expression.go @@ -1108,9 +1108,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as result := interpreter.evalExpression(referenceExpression.Expression) - if result, ok := result.(ReferenceTrackedResourceKindedValue); ok { - interpreter.trackReferencedResourceKindedValue(result.StorageID(), result) - } + interpreter.maybeTrackReferencedResourceKindedValue(result) switch typ := borrowType.(type) { case *sema.OptionalType: @@ -1131,9 +1129,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as } innerValue := result.InnerValue(interpreter, locationRange) - if result, ok := innerValue.(ReferenceTrackedResourceKindedValue); ok { - interpreter.trackReferencedResourceKindedValue(result.StorageID(), result) - } + interpreter.maybeTrackReferencedResourceKindedValue(innerValue) return NewSomeValueNonCopying( interpreter, diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go index 5293c77711..4add97e057 100644 --- a/runtime/tests/interpreter/function_test.go +++ b/runtime/tests/interpreter/function_test.go @@ -167,4 +167,116 @@ func TestInterpretResultVariable(t *testing.T) { resource.GetField(inter, interpreter.EmptyLocationRange, "id"), ) }) + + t.Run("reference invalidation, optional type", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub(set) var id: UInt64 + init() { + self.id = 1 + } + } + + var ref: &R? = nil + + pub fun main(): @R? { + var r <- createAndStoreRef() + if var r <- r { + r.id = 2 + return <- r + } + + return <- r + } + + pub fun createAndStoreRef(): @R? { + post { + storeRef(result) + } + return <- create R() + } + + pub fun storeRef(_ r: &R?): Bool { + ref = r + return r != nil + } + + pub fun getRef(): &R { + return ref! + }`, + ) + + _, err := inter.Invoke("main") + require.NoError(t, err) + + result, err := inter.Invoke("getRef") + require.NoError(t, err) + + require.IsType(t, &interpreter.EphemeralReferenceValue{}, result) + referenceValue := result.(*interpreter.EphemeralReferenceValue) + + // Get the field via the reference. Must have the updated field value. + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt64Value(2), + referenceValue.GetMember(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("reference invalidation, non optional", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub(set) var id: UInt64 + init() { + self.id = 1 + } + } + + var ref: &R? = nil + + pub fun main(): @R { + var r <- createAndStoreRef() + r.id = 2 + return <- r + } + + pub fun createAndStoreRef(): @R { + post { + storeRef(result) + } + return <- create R() + } + + pub fun storeRef(_ r: &R): Bool { + ref = r + return r != nil + } + + pub fun getRef(): &R { + return ref! + }`, + ) + + _, err := inter.Invoke("main") + require.NoError(t, err) + + result, err := inter.Invoke("getRef") + require.NoError(t, err) + + require.IsType(t, &interpreter.EphemeralReferenceValue{}, result) + referenceValue := result.(*interpreter.EphemeralReferenceValue) + + // Get the field via the reference. Must have the updated field value. + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt64Value(2), + referenceValue.GetMember(inter, interpreter.EmptyLocationRange, "id"), + ) + }) } From 53dad69e5eda2847db807b37e5138604d307b1ad Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 14 Apr 2023 14:11:58 -0700 Subject: [PATCH 161/173] Check for other places where references are created internally --- runtime/stdlib/account.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 38b25e0c23..8ff0008204 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -1365,6 +1365,8 @@ func newAccountContractsBorrowFunction( return interpreter.Nil } + // No need to track the referenced value, since the reference is taken to a contract value. + // A contract value would never be moved or destroyed, within the execution of a program. reference := interpreter.NewEphemeralReferenceValue( inter, false, From 6c22a63c5d73b5e2959833d931bfb4e6fe05c1c5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 17 Apr 2023 15:14:01 -0700 Subject: [PATCH 162/173] ADd resource reference tracking for attachments --- runtime/interpreter/interpreter_expression.go | 6 +- runtime/interpreter/value.go | 9 +- runtime/tests/interpreter/attachments_test.go | 147 ++++++++++++++++-- 3 files changed, 141 insertions(+), 21 deletions(-) diff --git a/runtime/interpreter/interpreter_expression.go b/runtime/interpreter/interpreter_expression.go index 5e0f3f2f5d..d4f6fb109f 100644 --- a/runtime/interpreter/interpreter_expression.go +++ b/runtime/interpreter/interpreter_expression.go @@ -1221,7 +1221,7 @@ func (interpreter *Interpreter) VisitAttachExpression(attachExpression *ast.Atta attachTarget := interpreter.evalExpression(attachExpression.Base) base, ok := attachTarget.(*CompositeValue) - // we enforce this in the checker, but check defensively anyways + // we enforce this in the checker, but check defensively anyway if !ok || !base.Kind.SupportsAttachments() { panic(InvalidAttachmentOperationTargetError{ Value: attachTarget, @@ -1237,8 +1237,8 @@ func (interpreter *Interpreter) VisitAttachExpression(attachExpression *ast.Atta } // the `base` value must be accessible during the attachment's constructor, but we cannot - // set it on the attachment's `CompositeValue` yet, because the value does not exist. Instead - // we create an implicit constructor argument containing a reference to the base + // set it on the attachment's `CompositeValue` yet, because the value does not exist. + // Instead, we create an implicit constructor argument containing a reference to the base. var baseValue Value = NewEphemeralReferenceValue( interpreter, false, diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 9c63a582c0..ff71785397 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -15144,6 +15144,8 @@ func (v *CompositeValue) setBaseValue(interpreter *Interpreter, base *CompositeV // the base reference can only be borrowed with the declared type of the attachment's base v.base = NewEphemeralReferenceValue(interpreter, false, base, baseType) + + interpreter.trackReferencedResourceKindedValue(base.StorageID(), base) } func attachmentMemberName(ty sema.Type) string { @@ -15173,6 +15175,7 @@ func attachmentBaseAndSelfValues( base = v.getBaseValue(interpreter, locationRange) // in attachment functions, self is a reference value self = NewEphemeralReferenceValue(interpreter, false, v, interpreter.MustSemaTypeOfValue(v)) + interpreter.trackReferencedResourceKindedValue(v.StorageID(), v) return } @@ -15221,7 +15224,11 @@ func (v *CompositeValue) GetTypeKey( } // dynamically set the attachment's base to this composite attachment.setBaseValue(interpreter, v) - return NewSomeValueNonCopying(interpreter, NewEphemeralReferenceValue(interpreter, false, attachment, ty)) + + attachmentRef := NewEphemeralReferenceValue(interpreter, false, attachment, ty) + interpreter.trackReferencedResourceKindedValue(attachment.StorageID(), attachment) + + return NewSomeValueNonCopying(interpreter, attachmentRef) } func (v *CompositeValue) SetTypeKey( diff --git a/runtime/tests/interpreter/attachments_test.go b/runtime/tests/interpreter/attachments_test.go index 6c5725facc..fafbdb9693 100644 --- a/runtime/tests/interpreter/attachments_test.go +++ b/runtime/tests/interpreter/attachments_test.go @@ -1599,24 +1599,37 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { inter, _ := testAccount(t, address, true, ` resource R {} attachment A for R { - fun foo(): Int { return 3 } + pub(set) var id: UInt8 + init() { + self.id = 1 + } } - fun test() { + fun test(): UInt8 { let r <- create R() let r2 <- attach A() to <-r let a = r2[A]! - authAccount.save(<-r2, to: /storage/foo) - let i = a.foo() - } - `, sema.Config{ - AttachmentsEnabled: true, - }, + + + // Move the resource after the taking a reference to the attachment. + // Then update the field of the attachment. + var r3 <- r2 + let a2 = r3[A]! + a2.id = 5 + authAccount.save(<-r3, to: /storage/foo) + + // Access the attachment filed from the previous reference. + return a.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, ) // TODO: in the stable cadence branch, with the new resource reference invalidation, // this should be an error, as `a` should be invalidated after the save - _, err := inter.Invoke("test") + result, err := inter.Invoke("test") require.NoError(t, err) + AssertValuesEqual(t, inter, interpreter.UInt8Value(5), result) }) t.Run("nested", func(t *testing.T) { @@ -1637,24 +1650,124 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { } } attachment A for R { - fun foo(): Int { return 3 } + pub(set) var id: UInt8 + init() { + self.id = 1 + } } - fun test() { + fun test(): UInt8 { let r2 <- create R2(r: <-attach A() to <-create R()) let a = r2.r[A]! + + // Move the resource after the taking a reference to the attachment. + // Then update the field of the attachment. + var r3 <- r2 + let a2 = r3.r[A]! + a2.id = 5 + authAccount.save(<-r3, to: /storage/foo) + + // Access the attachment filed from the previous reference. + return a.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, + ) + + // TODO: in the stable cadence branch, with the new resource reference invalidation, + // this should be an error, as `a` should be invalidated after the save + result, err := inter.Invoke("test") + require.NoError(t, err) + AssertValuesEqual(t, inter, interpreter.UInt8Value(5), result) + }) + + t.Run("base reference", func(t *testing.T) { + + t.Parallel() + + address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) + + inter, _ := testAccount(t, address, true, ` + pub resource R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } + } + + var ref: &R? = nil + + attachment A for R { + fun saveBaseRef() { + ref = base + } + } + + fun test(): UInt8 { + let r <- attach A() to <-create R() + let a = r[A]! + + a.saveBaseRef() + + var r2 <- r + r2.id = 5 authAccount.save(<-r2, to: /storage/foo) - let i = a.foo() + return ref!.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, + ) + + // TODO: in the stable cadence branch, with the new resource reference invalidation, + // this should be an error, as `a` should be invalidated after the save + result, err := inter.Invoke("test") + require.NoError(t, err) + AssertValuesEqual(t, inter, interpreter.UInt8Value(5), result) + }) + + t.Run("self reference", func(t *testing.T) { + + t.Parallel() + + address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) + + inter, _ := testAccount(t, address, true, ` + pub resource R {} + + var ref: &A? = nil + + attachment A for R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } + + fun saveSelfRef() { + ref = self as &A + } } - - `, sema.Config{ - AttachmentsEnabled: true, - }, + + fun test(): UInt8 { + let r <- attach A() to <-create R() + r[A]!.saveSelfRef() + + var r2 <- r + let a = r2[A]! + a.id = 5 + authAccount.save(<-r2, to: /storage/foo) + return ref!.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, ) // TODO: in the stable cadence branch, with the new resource reference invalidation, // this should be an error, as `a` should be invalidated after the save - _, err := inter.Invoke("test") + result, err := inter.Invoke("test") require.NoError(t, err) + AssertValuesEqual(t, inter, interpreter.UInt8Value(5), result) }) } From b7430040b0ac79c54ec22ebd9272399f0db964d4 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 17 Apr 2023 15:21:07 -0700 Subject: [PATCH 163/173] Update tests --- runtime/tests/interpreter/attachments_test.go | 4 +- runtime/tests/interpreter/function_test.go | 54 ++++++------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/runtime/tests/interpreter/attachments_test.go b/runtime/tests/interpreter/attachments_test.go index fafbdb9693..f4662b95bb 100644 --- a/runtime/tests/interpreter/attachments_test.go +++ b/runtime/tests/interpreter/attachments_test.go @@ -1610,7 +1610,7 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { let a = r2[A]! - // Move the resource after the taking a reference to the attachment. + // Move the resource after taking a reference to the attachment. // Then update the field of the attachment. var r3 <- r2 let a2 = r3[A]! @@ -1659,7 +1659,7 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { let r2 <- create R2(r: <-attach A() to <-create R()) let a = r2.r[A]! - // Move the resource after the taking a reference to the attachment. + // Move the resource after taking a reference to the attachment. // Then update the field of the attachment. var r3 <- r2 let a2 = r3.r[A]! diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go index 4add97e057..81199d95ad 100644 --- a/runtime/tests/interpreter/function_test.go +++ b/runtime/tests/interpreter/function_test.go @@ -38,7 +38,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub let id: UInt64 + pub let id: UInt8 init() { self.id = 1 } @@ -61,7 +61,7 @@ func TestInterpretResultVariable(t *testing.T) { utils.AssertValuesEqual( t, inter, - interpreter.UInt64Value(1), + interpreter.UInt8Value(1), resource.GetField(inter, interpreter.EmptyLocationRange, "id"), ) }) @@ -71,7 +71,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub let id: UInt64 + pub let id: UInt8 init() { self.id = 1 } @@ -99,7 +99,7 @@ func TestInterpretResultVariable(t *testing.T) { utils.AssertValuesEqual( t, inter, - interpreter.UInt64Value(1), + interpreter.UInt8Value(1), resource.GetField(inter, interpreter.EmptyLocationRange, "id"), ) }) @@ -109,7 +109,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub let id: UInt64 + pub let id: UInt8 init() { self.id = 1 } @@ -133,7 +133,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub let id: UInt64 + pub let id: UInt8 init() { self.id = 1 } @@ -163,7 +163,7 @@ func TestInterpretResultVariable(t *testing.T) { utils.AssertValuesEqual( t, inter, - interpreter.UInt64Value(1), + interpreter.UInt8Value(1), resource.GetField(inter, interpreter.EmptyLocationRange, "id"), ) }) @@ -173,7 +173,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub(set) var id: UInt64 + pub(set) var id: UInt8 init() { self.id = 1 } @@ -203,27 +203,17 @@ func TestInterpretResultVariable(t *testing.T) { return r != nil } - pub fun getRef(): &R { - return ref! + pub fun getID(): UInt8 { + return ref!.id }`, ) _, err := inter.Invoke("main") require.NoError(t, err) - result, err := inter.Invoke("getRef") + result, err := inter.Invoke("getID") require.NoError(t, err) - - require.IsType(t, &interpreter.EphemeralReferenceValue{}, result) - referenceValue := result.(*interpreter.EphemeralReferenceValue) - - // Get the field via the reference. Must have the updated field value. - utils.AssertValuesEqual( - t, - inter, - interpreter.UInt64Value(2), - referenceValue.GetMember(inter, interpreter.EmptyLocationRange, "id"), - ) + utils.AssertValuesEqual(t, inter, interpreter.UInt8Value(2), result) }) t.Run("reference invalidation, non optional", func(t *testing.T) { @@ -231,7 +221,7 @@ func TestInterpretResultVariable(t *testing.T) { inter := parseCheckAndInterpret(t, ` pub resource R { - pub(set) var id: UInt64 + pub(set) var id: UInt8 init() { self.id = 1 } @@ -257,26 +247,16 @@ func TestInterpretResultVariable(t *testing.T) { return r != nil } - pub fun getRef(): &R { - return ref! + pub fun getID(): UInt8 { + return ref!.id }`, ) _, err := inter.Invoke("main") require.NoError(t, err) - result, err := inter.Invoke("getRef") + result, err := inter.Invoke("getID") require.NoError(t, err) - - require.IsType(t, &interpreter.EphemeralReferenceValue{}, result) - referenceValue := result.(*interpreter.EphemeralReferenceValue) - - // Get the field via the reference. Must have the updated field value. - utils.AssertValuesEqual( - t, - inter, - interpreter.UInt64Value(2), - referenceValue.GetMember(inter, interpreter.EmptyLocationRange, "id"), - ) + utils.AssertValuesEqual(t, inter, interpreter.UInt8Value(2), result) }) } From bda1a19f4e24b258f5a7f7629fa77f519bc130b5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 18 Apr 2023 16:14:51 -0700 Subject: [PATCH 164/173] Add validation for exporting references to destroyed resources --- runtime/convertValues.go | 5 ++++- runtime/convertValues_test.go | 32 ++++++++++++++++++++++++++++++++ runtime/interpreter/value.go | 22 +++++++++++----------- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 51018e2a56..2558bd6b0c 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -228,8 +228,11 @@ func exportValueWithInterpreter( } defer delete(seenReferences, v) seenReferences[v] = struct{}{} + + referencedValue := v.MustReferencedValue(inter, locationRange) + return exportValueWithInterpreter( - v.Value, + referencedValue, inter, locationRange, seenReferences, diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 495807409b..02b59f5a13 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -5326,3 +5326,35 @@ func TestNestedStructArgPassing(t *testing.T) { require.ErrorAs(t, err, &argErr) }) } + +func TestDestroyedResourceReferenceExport(t *testing.T) { + t.Parallel() + + rt := newTestInterpreterRuntimeWithAttachments() + + script := []byte(` + pub resource S {} + + pub fun main(): &S { + var s <- create S() + var ref = &s as &S + destroy s + return ref + } + `) + + runtimeInterface := &testRuntimeInterface{} + + nextScriptLocation := newScriptLocationGenerator() + _, err := rt.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: nextScriptLocation(), + }, + ) + require.Error(t, err) + require.ErrorAs(t, err, &interpreter.DestroyedResourceError{}) +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index b092e6bf05..e7b9646bcd 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17427,7 +17427,7 @@ func (v *EphemeralReferenceValue) ReferencedValue( } } -func (v *EphemeralReferenceValue) mustReferencedValue( +func (v *EphemeralReferenceValue) MustReferencedValue( interpreter *Interpreter, locationRange LocationRange, ) Value { @@ -17450,7 +17450,7 @@ func (v *EphemeralReferenceValue) GetMember( locationRange LocationRange, name string, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return interpreter.getMember(self, locationRange, name) } @@ -17460,7 +17460,7 @@ func (v *EphemeralReferenceValue) RemoveMember( locationRange LocationRange, identifier string, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) if memberAccessibleValue, ok := self.(MemberAccessibleValue); ok { return memberAccessibleValue.RemoveMember(interpreter, locationRange, identifier) @@ -17475,7 +17475,7 @@ func (v *EphemeralReferenceValue) SetMember( name string, value Value, ) bool { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return interpreter.setMember(self, locationRange, name, value) } @@ -17485,7 +17485,7 @@ func (v *EphemeralReferenceValue) GetKey( locationRange LocationRange, key Value, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(ValueIndexableValue). GetKey(interpreter, locationRange, key) @@ -17497,7 +17497,7 @@ func (v *EphemeralReferenceValue) SetKey( key Value, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(ValueIndexableValue). SetKey(interpreter, locationRange, key, value) @@ -17509,7 +17509,7 @@ func (v *EphemeralReferenceValue) InsertKey( key Value, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(ValueIndexableValue). InsertKey(interpreter, locationRange, key, value) @@ -17520,7 +17520,7 @@ func (v *EphemeralReferenceValue) RemoveKey( locationRange LocationRange, key Value, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(ValueIndexableValue). RemoveKey(interpreter, locationRange, key) @@ -17531,7 +17531,7 @@ func (v *EphemeralReferenceValue) GetTypeKey( locationRange LocationRange, key sema.Type, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(TypeIndexableValue). GetTypeKey(interpreter, locationRange, key) @@ -17543,7 +17543,7 @@ func (v *EphemeralReferenceValue) SetTypeKey( key sema.Type, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(TypeIndexableValue). SetTypeKey(interpreter, locationRange, key, value) @@ -17554,7 +17554,7 @@ func (v *EphemeralReferenceValue) RemoveTypeKey( locationRange LocationRange, key sema.Type, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(TypeIndexableValue). RemoveTypeKey(interpreter, locationRange, key) From 30589cbbe57044169141d3c1ed22fb4cf086f0cf Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 18 Apr 2023 16:25:52 -0700 Subject: [PATCH 165/173] Update attachment export test --- runtime/attachments_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/attachments_test.go b/runtime/attachments_test.go index 9a857be79e..809e777932 100644 --- a/runtime/attachments_test.go +++ b/runtime/attachments_test.go @@ -169,8 +169,12 @@ func TestAccountAttachmentExport(t *testing.T) { import Test from 0x1 pub fun main(): &Test.A? { let r <- Test.makeRWithA() - let a = r[Test.A] - destroy r + + let acc = getAuthAccount(0x1) + acc.save(<-r, to: /storage/r) + var rRef = acc.borrow<&Test.R>(from: /storage/r)! + + let a = rRef[Test.A] return a } `) From a4acb79d21eac0ca5912cf0fa6c90fd91cd09b15 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 19 Apr 2023 15:53:34 -0700 Subject: [PATCH 166/173] Fix matchers --- runtime/stdlib/test.go | 62 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index cfd17d0b5c..50b31e1fe1 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -533,7 +533,9 @@ var testExpectFunction = interpreter.NewUnmeteredHostFunctionValue( ) if !result { - panic(AssertionError{}) + panic(AssertionError{ + LocationRange: locationRange, + }) } return interpreter.Void @@ -1456,7 +1458,7 @@ var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) - return newMatcherWithGenericTestFunction(invocation, beEmptyTestFunc) + return newMatcherWithAnyStructTestFunction(invocation, beEmptyTestFunc) }, ) @@ -1510,7 +1512,7 @@ var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) - return newMatcherWithGenericTestFunction(invocation, haveElementCountTestFunc) + return newMatcherWithAnyStructTestFunction(invocation, haveElementCountTestFunc) }, ) @@ -1575,7 +1577,7 @@ var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) - return newMatcherWithGenericTestFunction(invocation, containTestFunc) + return newMatcherWithAnyStructTestFunction(invocation, containTestFunc) }, ) @@ -1632,7 +1634,7 @@ var beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) - return newMatcherWithGenericTestFunction(invocation, beGreaterThanTestFunc) + return newMatcherWithAnyStructTestFunction(invocation, beGreaterThanTestFunc) }, ) @@ -1689,7 +1691,7 @@ var beLessThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) - return newMatcherWithGenericTestFunction(invocation, beLessThanTestFunc) + return newMatcherWithAnyStructTestFunction(invocation, beLessThanTestFunc) }, ) @@ -1828,12 +1830,38 @@ func (e TestFailedError) Error() string { return fmt.Sprintf("test failed: %s", e.Err.Error()) } -func newMatcherWithGenericTestFunction( +// Creates a matcher using a function that accepts an `AnyStruct` typed parameter. +// i.e: invokes `newMatcher(fun (value: AnyStruct): Bool)`. +func newMatcherWithAnyStructTestFunction( invocation interpreter.Invocation, testFunc interpreter.FunctionValue, ) interpreter.Value { - inter := invocation.Interpreter + matcherConstructor := getNestedTypeConstructorValue( + *invocation.Self, + matcherTypeName, + ) + matcher, err := invocation.Interpreter.InvokeExternally( + matcherConstructor, + matcherConstructor.Type, + []interpreter.Value{ + testFunc, + }, + ) + + if err != nil { + panic(err) + } + + return matcher +} + +// Creates a matcher using a function that accepts a generic `T` typed parameter. +// NOTE: Use this function only if the matcher function has a generic type. +func newMatcherWithGenericTestFunction( + invocation interpreter.Invocation, + testFunc interpreter.FunctionValue, +) interpreter.Value { typeParameterPair := invocation.TypeParameterTypes.Oldest() if typeParameterPair == nil { @@ -1882,23 +1910,7 @@ func newMatcherWithGenericTestFunction( }, ) - matcherConstructor := getNestedTypeConstructorValue( - *invocation.Self, - matcherTypeName, - ) - matcher, err := inter.InvokeExternally( - matcherConstructor, - matcherConstructor.Type, - []interpreter.Value{ - matcherTestFunction, - }, - ) - - if err != nil { - panic(err) - } - - return matcher + return newMatcherWithAnyStructTestFunction(invocation, matcherTestFunction) } func TestCheckerContractValueHandler( From cdeeabd0f13f9328642c04efba7027c7cce7b60b Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 19 Apr 2023 15:53:53 -0700 Subject: [PATCH 167/173] Update json encoding --- encoding/ccf/decode.go | 4 ++++ encoding/ccf/encode.go | 1 - encoding/json/encoding_test.go | 14 ++++++++------ types_test.go | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 9dc9ef22ef..7d5dd0ba20 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -2003,8 +2003,12 @@ func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cade return nil, errors.New("unexpected nil function return type") } + // TODO: + purity := cadence.FunctionPurityUnspecified + return cadence.NewMeteredFunctionType( d.gauge, + purity, typeParameters, parameters, returnType, diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 9f71212e3d..b97d8dc417 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -61,7 +61,6 @@ func Encode(value cadence.Value) ([]byte, error) { var w bytes.Buffer enc := NewEncoder(&w) - defer enc.enc.Close() err := enc.Encode(value) if err != nil { diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 9b8c7d748f..8c9db9cf76 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2459,15 +2459,16 @@ func TestEncodeType(t *testing.T) { Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, - ReturnType: cadence.IntType{}, + ReturnType: cadence.IntType{}, + TypeParameters: []cadence.TypeParameter{}, }, }, `{"type":"Type","value":{"staticType": { "kind" : "Function", - "typeID":"Foo", "purity": "view", - "return" : {"kind" : "Int"}, + "return" : {"kind" : "Int"}, + "typeParameters": [], "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} @@ -2482,8 +2483,8 @@ func TestEncodeType(t *testing.T) { encodedValue := `{"type":"Type","value":{"staticType": { "kind" : "Function", - "typeID":"Foo", - "return" : {"kind" : "Int"}, + "return" : {"kind" : "Int"}, + "typeParameters": [], "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} @@ -2495,7 +2496,8 @@ func TestEncodeType(t *testing.T) { Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, - ReturnType: cadence.IntType{}, + ReturnType: cadence.IntType{}, + TypeParameters: []cadence.TypeParameter{}, }, } diff --git a/types_test.go b/types_test.go index d927e17230..16414a2faf 100644 --- a/types_test.go +++ b/types_test.go @@ -190,7 +190,7 @@ func TestType_ID(t *testing.T) { }, ReturnType: StringType{}, }, - "((Int):String)", + "fun(Int):String", }, { &EventType{ From 19019ff876329d84dd982fdd2e8d15151b1619fa Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 19 Apr 2023 16:22:12 -0700 Subject: [PATCH 168/173] Fix auth_account sema type generation --- runtime/sema/authaccount.cdc | 10 +++++----- runtime/sema/authaccount.gen.go | 1 + runtime/sema/gen/main.go | 18 ++++++++++++++++++ runtime/sema/gen/testdata/functions.cdc | 3 +++ runtime/sema/gen/testdata/functions.golden.go | 19 +++++++++++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index df666a5507..1e8bb2d5a7 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -61,7 +61,7 @@ pub struct AuthAccount { /// If there is an object stored, the type of the object is returned without modifying the stored object. /// /// The path must be a storage path, i.e., only the domain `storage` is allowed. - pub fun type(at path: StoragePath): Type? + pub view fun type(at path: StoragePath): Type? /// Loads an object from the account's storage which is stored under the given path, /// or nil if no object is stored under the given path. @@ -152,7 +152,7 @@ pub struct AuthAccount { /// /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, /// is undefined. - pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + pub fun forEachPublic(_ function: fun(PublicPath, Type): Bool) /// Iterate over all the private paths of an account. /// passing each path and type in turn to the provided callback function. @@ -165,7 +165,7 @@ pub struct AuthAccount { /// /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, /// is undefined. - pub fun forEachPrivate(_ function: ((PrivatePath, Type): Bool)) + pub fun forEachPrivate(_ function: fun(PrivatePath, Type): Bool) /// Iterate over all the stored paths of an account. /// passing each path and type in turn to the provided callback function. @@ -178,7 +178,7 @@ pub struct AuthAccount { /// /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, /// is undefined. - pub fun forEachStored(_ function: ((StoragePath, Type): Bool)) + pub fun forEachStored(_ function: fun(StoragePath, Type): Bool) pub struct Contracts { @@ -267,7 +267,7 @@ pub struct AuthAccount { /// /// Iteration is stopped early if the function returns `false`. /// The order of iteration is undefined. - pub fun forEach(_ function: ((AccountKey): Bool)) + pub fun forEach(_ function: fun(AccountKey): Bool) /// The total number of unrevoked keys in this account. pub let count: UInt64 diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index a67ab0ad7f..7fc63b28e4 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -203,6 +203,7 @@ The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is al const AuthAccountTypeTypeFunctionName = "type" var AuthAccountTypeTypeFunctionType = &FunctionType{ + Purity: FunctionPurityView, Parameters: []Parameter{ { Label: "at", diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index d63a622061..543613d887 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -272,6 +272,7 @@ func (g *generator) addFunctionTypeDeclaration( functionTypeVarName(fullTypeName, functionName), functionTypeExpr( &ast.FunctionType{ + PurityAnnotation: decl.Purity, ReturnTypeAnnotation: decl.ReturnTypeAnnotation, ParameterTypeAnnotations: parameterTypeAnnotations, }, @@ -708,6 +709,13 @@ func functionTypeExpr( typeParams map[string]string, ) dst.Expr { + // Function purity + + var purityExpr dst.Expr + if t.PurityAnnotation == ast.FunctionPurityView { + purityExpr = dst.NewIdent("FunctionPurityView") + } + // Type parameters var typeParameterTypeAnnotations []*ast.TypeParameter @@ -831,6 +839,16 @@ func functionTypeExpr( var compositeElements []dst.Expr + if purityExpr != nil { + compositeElements = append( + compositeElements, + goKeyValue( + "Purity", + purityExpr, + ), + ) + } + if typeParametersExpr != nil { compositeElements = append( compositeElements, diff --git a/runtime/sema/gen/testdata/functions.cdc b/runtime/sema/gen/testdata/functions.cdc index 6ef3af288f..376ad84682 100644 --- a/runtime/sema/gen/testdata/functions.cdc +++ b/runtime/sema/gen/testdata/functions.cdc @@ -19,4 +19,7 @@ pub struct Test { /// This is a test function with a type parameter and a parameter using it. pub fun typeParamWithBoundAndParam(t: T) {} + + /// This is a function with 'view' modifier + pub view fun viewFunction() {} } diff --git a/runtime/sema/gen/testdata/functions.golden.go b/runtime/sema/gen/testdata/functions.golden.go index 247f4ccb25..b0dfc3b327 100644 --- a/runtime/sema/gen/testdata/functions.golden.go +++ b/runtime/sema/gen/testdata/functions.golden.go @@ -157,6 +157,19 @@ const TestTypeTypeParamWithBoundAndParamFunctionDocString = ` This is a test function with a type parameter and a parameter using it. ` +const TestTypeViewFunctionFunctionName = "viewFunction" + +var TestTypeViewFunctionFunctionType = &FunctionType{ + Purity: FunctionPurityView, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const TestTypeViewFunctionFunctionDocString = ` +This is a function with 'view' modifier +` + const TestTypeName = "Test" var TestType = &SimpleType{ @@ -216,6 +229,12 @@ func init() { TestTypeTypeParamWithBoundAndParamFunctionType, TestTypeTypeParamWithBoundAndParamFunctionDocString, ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeViewFunctionFunctionName, + TestTypeViewFunctionFunctionType, + TestTypeViewFunctionFunctionDocString, + ), }) } } From 46b69964b3ac019907b6e4f44d475e6f3410d301 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 19 Apr 2023 19:04:29 -0700 Subject: [PATCH 169/173] Fix reference invalidation --- encoding/json/decode.go | 2 +- encoding/json/encode.go | 2 +- runtime/attachments_test.go | 10 +++------- runtime/convertValues_test.go | 9 +++++++++ runtime/tests/interpreter/function_test.go | 4 ++-- runtime/tests/interpreter/uuid_test.go | 3 ++- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index cd38617479..9f6c2d912b 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -135,7 +135,7 @@ const ( typeParametersKey = "typeParameters" returnKey = "return" typeBoundKey = "typeBound" - purityKey = "purity" + purityKey = "purity" ) func (d *Decoder) decodeJSON(v any) cadence.Value { diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 85497a5d41..9b33a040f1 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -849,7 +849,7 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { Initializers: prepareInitializers(typ.Initializers, results), } case *cadence.FunctionType: - typeJson := jsonFunctionType{ + typeJson := jsonFunctionType{ Kind: "Function", TypeParameters: prepareTypeParameters(typ.TypeParameters, results), Parameters: prepareParameters(typ.Parameters, results), diff --git a/runtime/attachments_test.go b/runtime/attachments_test.go index 2017e18032..2b807cfc0e 100644 --- a/runtime/attachments_test.go +++ b/runtime/attachments_test.go @@ -170,12 +170,8 @@ func TestAccountAttachmentExportFailure(t *testing.T) { import Test from 0x1 pub fun main(): &Test.A? { let r <- Test.makeRWithA() - - let acc = getAuthAccount(0x1) - acc.save(<-r, to: /storage/r) - var rRef = acc.borrow<&Test.R>(from: /storage/r)! - - let a = rRef[Test.A] + let a = r[Test.A] + destroy r return a } `) @@ -227,7 +223,7 @@ func TestAccountAttachmentExportFailure(t *testing.T) { }, ) require.Error(t, err) - require.ErrorAs(t, err, &interpreter.InvalidatedResourceError{}) + require.ErrorAs(t, err, &interpreter.DestroyedResourceError{}) } func TestAccountAttachmentExport(t *testing.T) { diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 8b69e8da9a..731aa9f525 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -5341,7 +5341,16 @@ func TestDestroyedResourceReferenceExport(t *testing.T) { pub fun main(): &S { var s <- create S() var ref = &s as &S + + // Just to trick the checker, + // and get pass the static referenced resource invalidation analysis. + var ref2 = getRef(ref) + destroy s + return ref2! + } + + pub fun getRef(_ ref: &S): &S { return ref } `) diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go index f558b02093..29d10857fa 100644 --- a/runtime/tests/interpreter/function_test.go +++ b/runtime/tests/interpreter/function_test.go @@ -19,8 +19,6 @@ package interpreter_test import ( - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/tests/checker" "testing" "github.com/stretchr/testify/assert" @@ -28,6 +26,8 @@ import ( "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/tests/checker" "github.com/onflow/cadence/runtime/tests/utils" ) diff --git a/runtime/tests/interpreter/uuid_test.go b/runtime/tests/interpreter/uuid_test.go index 8ef6adca9c..d1aaf27a11 100644 --- a/runtime/tests/interpreter/uuid_test.go +++ b/runtime/tests/interpreter/uuid_test.go @@ -88,7 +88,8 @@ func TestInterpretResourceUUID(t *testing.T) { interpreter.ProgramFromChecker(importingChecker), importingChecker.Location, &interpreter.Config{ - Storage: storage, + InvalidatedResourceValidationEnabled: true, + Storage: storage, UUIDHandler: func() (uint64, error) { defer func() { uuid++ }() return uuid, nil From a8adeac02a0a916ad7a70e310492e0f274f11c74 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 19 Apr 2023 19:38:05 -0700 Subject: [PATCH 170/173] Update publicaccount.cdc with new cadence syntax --- runtime/sema/publicaccount.cdc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc index 940b88b5d4..e69f186589 100644 --- a/runtime/sema/publicaccount.cdc +++ b/runtime/sema/publicaccount.cdc @@ -43,7 +43,7 @@ pub struct PublicAccount { /// /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, /// is undefined. - pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + pub fun forEachPublic(_ function: fun(PublicPath, Type): Bool) pub struct Contracts { @@ -74,7 +74,7 @@ pub struct PublicAccount { /// /// Iteration is stopped early if the function returns `false`. /// The order of iteration is undefined. - pub fun forEach(_ function: ((AccountKey): Bool)) + pub fun forEach(_ function: fun(AccountKey): Bool) /// The total number of unrevoked keys in this account. pub let count: UInt64 From 55510a901f70afe7682e34c17648db45773fd6c8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 20 Apr 2023 15:58:53 -0700 Subject: [PATCH 171/173] Update docs --- docs/language/accounts.mdx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index 2911b87383..a8726c68d8 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -148,19 +148,17 @@ pub struct AuthAccount { /// All storage paths of this account. pub let storagePaths: [StoragePath] - // Key management API - /// **DEPRECATED**: Use `keys.add` instead. /// /// Adds a public key to the account. /// /// The public key must be encoded together with their signature algorithm, hashing algorithm and weight. - fun addPublicKey(_ publicKey: [UInt8]) + pub fun addPublicKey(_ publicKey: [UInt8]) /// **DEPRECATED**: Use `keys.revoke` instead. /// /// Revokes the key at the given index. - fun removePublicKey(_ index: Int) + pub fun removePublicKey(_ index: Int) // Account storage API (see the section below for documentation) From 3d57ec19028c88e752ffb8fff1ce0a4dce49b48f Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 24 Apr 2023 09:24:01 -0700 Subject: [PATCH 172/173] Remove depricated APIs --- docs/language/accounts.mdx | 12 ------- runtime/sema/authaccount.cdc | 12 ------- runtime/sema/authaccount.gen.go | 58 --------------------------------- 3 files changed, 82 deletions(-) diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index a8726c68d8..f5c45963ce 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -148,18 +148,6 @@ pub struct AuthAccount { /// All storage paths of this account. pub let storagePaths: [StoragePath] - /// **DEPRECATED**: Use `keys.add` instead. - /// - /// Adds a public key to the account. - /// - /// The public key must be encoded together with their signature algorithm, hashing algorithm and weight. - pub fun addPublicKey(_ publicKey: [UInt8]) - - /// **DEPRECATED**: Use `keys.revoke` instead. - /// - /// Revokes the key at the given index. - pub fun removePublicKey(_ index: Int) - // Account storage API (see the section below for documentation) // Provides a API for bootstrapping (sending and receiving) capabilities diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc index 1e8bb2d5a7..3b2ca673ad 100644 --- a/runtime/sema/authaccount.cdc +++ b/runtime/sema/authaccount.cdc @@ -34,18 +34,6 @@ pub struct AuthAccount { /// All storage paths of this account. pub let storagePaths: [StoragePath] - /// **DEPRECATED**: Use `keys.add` instead. - /// - /// Adds a public key to the account. - /// - /// The public key must be encoded together with their signature algorithm, hashing algorithm and weight. - pub fun addPublicKey(_ publicKey: [UInt8]) - - /// **DEPRECATED**: Use `keys.revoke` instead. - /// - /// Revokes the key at the given index. - pub fun removePublicKey(_ index: Int) - /// Saves the given object into the account's storage at the given path. /// /// Resources are moved into storage, and structures are copied. diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go index 7fc63b28e4..71d82a8e18 100644 --- a/runtime/sema/authaccount.gen.go +++ b/runtime/sema/authaccount.gen.go @@ -115,52 +115,6 @@ const AuthAccountTypeStoragePathsFieldDocString = ` All storage paths of this account. ` -const AuthAccountTypeAddPublicKeyFunctionName = "addPublicKey" - -var AuthAccountTypeAddPublicKeyFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "publicKey", - TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ - Type: UInt8Type, - }), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const AuthAccountTypeAddPublicKeyFunctionDocString = ` -**DEPRECATED**: Use ` + "`keys.add`" + ` instead. - -Adds a public key to the account. - -The public key must be encoded together with their signature algorithm, hashing algorithm and weight. -` - -const AuthAccountTypeRemovePublicKeyFunctionName = "removePublicKey" - -var AuthAccountTypeRemovePublicKeyFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "index", - TypeAnnotation: NewTypeAnnotation(IntType), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - VoidType, - ), -} - -const AuthAccountTypeRemovePublicKeyFunctionDocString = ` -**DEPRECATED**: Use ` + "`keys.revoke`" + ` instead. - -Revokes the key at the given index. -` - const AuthAccountTypeSaveFunctionName = "save" var AuthAccountTypeSaveFunctionTypeParameterT = &TypeParameter{ @@ -1259,18 +1213,6 @@ func init() { AuthAccountTypeStoragePathsFieldType, AuthAccountTypeStoragePathsFieldDocString, ), - NewUnmeteredPublicFunctionMember( - AuthAccountType, - AuthAccountTypeAddPublicKeyFunctionName, - AuthAccountTypeAddPublicKeyFunctionType, - AuthAccountTypeAddPublicKeyFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - AuthAccountType, - AuthAccountTypeRemovePublicKeyFunctionName, - AuthAccountTypeRemovePublicKeyFunctionType, - AuthAccountTypeRemovePublicKeyFunctionDocString, - ), NewUnmeteredPublicFunctionMember( AuthAccountType, AuthAccountTypeSaveFunctionName, From 891935956430045008ec9cb4d3073931f23a1fc8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 24 Apr 2023 09:25:35 -0700 Subject: [PATCH 173/173] Bump CBOR version --- encoding/ccf/encode.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index b97d8dc417..9f71212e3d 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -61,6 +61,7 @@ func Encode(value cadence.Value) ([]byte, error) { var w bytes.Buffer enc := NewEncoder(&w) + defer enc.enc.Close() err := enc.Encode(value) if err != nil { diff --git a/go.mod b/go.mod index 49fd1eb1cb..de4ec6fead 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/bits-and-blooms/bitset v1.2.2 github.com/c-bata/go-prompt v0.2.5 - github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f + github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c github.com/go-test/deep v1.0.5 github.com/google/go-cmp v0.5.9 // indirect github.com/leanovate/gopter v0.2.9 diff --git a/go.sum b/go.sum index 9cc149e2ee..1036526b32 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f h1:dxTR4AaxCwuQv9LAVTAC2r1szlS+epeuPT5ClLKT6ZY= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc=