Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Introduce Word128 integer type #2497

Merged
merged 17 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions encoding/ccf/ccf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,98 @@ func TestEncodeWord64(t *testing.T) {
}...)
}

func TestEncodeWord128(t *testing.T) {

t.Parallel()

testAllEncodeAndDecode(t, []encodeTest{
{
name: "Zero",
val: cadence.NewWord128(0),
expected: []byte{
// language=json, format=json-cdc
// {"type":"Word128","value":"0"}
//
// language=edn, format=ccf
// 130([137(52), 0])
//
// language=cbor, format=ccf
// tag
0xd8, ccf.CBORTagTypeAndValue,
// array, 2 items follow
0x82,
// tag
0xd8, ccf.CBORTagSimpleType,
// Word128 type ID (52)
0x18, 0x34,
// tag (big num)
0xc2,
// bytes, 0 bytes follow
0x40,
},
},
{
name: "Max",
val: cadence.Word128{Value: sema.Word128TypeMaxIntBig},
expected: []byte{
// language=json, format=json-cdc
// {"type":"Word128","value":"340282366920938463463374607431768211455"}
//
// language=edn, format=ccf
// 130([137(52), 340282366920938463463374607431768211455])
//
// language=cbor, format=ccf
// tag
0xd8, ccf.CBORTagTypeAndValue,
// array, 2 items follow
0x82,
// tag
0xd8, ccf.CBORTagSimpleType,
// Word128 type ID (52)
0x18, 0x34,
// 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 TestDecodeWord128Invalid(t *testing.T) {
t.Parallel()

_, err := ccf.Decode(nil, []byte{
// language=json, format=json-cdc
// {"type":"Word128","value":"0"}
//
// language=edn, format=ccf
// 130([137(52), 0])
//
// language=cbor, format=ccf
// tag
0xd8, ccf.CBORTagTypeAndValue,
// array, 2 items follow
0x82,
// tag
0xd8, ccf.CBORTagSimpleType,
// Word128 type ID (52)
0x18, 0x34,
// Invalid type
0xd7,
// bytes, 16 bytes follow
0x50,
// 340282366920938463463374607431768211455
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
})
require.Error(t, err)
assert.Equal(t, "ccf: failed to decode: failed to decode Word128: cbor: cannot decode CBOR tag type to big.Int", err.Error())
}

func TestEncodeFix64(t *testing.T) {

t.Parallel()
Expand Down Expand Up @@ -7589,6 +7681,7 @@ func TestEncodeSimpleTypes(t *testing.T) {
{cadence.Word16Type{}, ccf.TypeWord16},
{cadence.Word32Type{}, ccf.TypeWord32},
{cadence.Word64Type{}, ccf.TypeWord64},
{cadence.Word128Type{}, ccf.TypeWord128},
{cadence.Fix64Type{}, ccf.TypeFix64},
{cadence.UFix64Type{}, ccf.TypeUFix64},
{cadence.BlockType{}, ccf.TypeBlock},
Expand Down
21 changes: 21 additions & 0 deletions encoding/ccf/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ func (d *Decoder) decodeTypeAndValue(types *cadenceTypeByCCFTypeID) (cadence.Val
// / word16-value
// / word32-value
// / word64-value
// / word128-value
// / fix64-value
// / ufix64-value
func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (cadence.Value, error) {
Expand Down Expand Up @@ -352,6 +353,9 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca
case cadence.Word64Type:
return d.decodeWord64()

case cadence.Word128Type:
return d.decodeWord128()

case cadence.Fix64Type:
return d.decodeFix64()

Expand Down Expand Up @@ -798,6 +802,23 @@ func (d *Decoder) decodeWord64() (cadence.Value, error) {
return cadence.NewMeteredWord64(d.gauge, i), nil
}

// decodeWord128 decodes word128-value as
// language=CDDL
// word128-value = bigint .ge 0
func (d *Decoder) decodeWord128() (cadence.Value, error) {
// NewMeteredWord128FromBig checks if decoded big.Int is positive.
return cadence.NewMeteredWord128FromBig(
d.gauge,
func() *big.Int {
bigInt, err := d.dec.DecodeBigInt()
if err != nil {
panic(fmt.Errorf("failed to decode Word128: %s", err))
turbolent marked this conversation as resolved.
Show resolved Hide resolved
}
return bigInt
},
)
}

// decodeFix64 decodes fix64-value as
// language=CDDL
// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807
Expand Down
3 changes: 3 additions & 0 deletions encoding/ccf/decode_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) {
case TypeWord64:
return cadence.TheWord64Type, nil

case TypeWord128:
return cadence.TheWord128Type, nil

case TypeFix64:
return cadence.TheFix64Type, nil

Expand Down
11 changes: 11 additions & 0 deletions encoding/ccf/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy
// / word16-value
// / word32-value
// / word64-value
// / word128-value
// / fix64-value
// / ufix64-value
//
Expand Down Expand Up @@ -447,6 +448,9 @@ func (e *Encoder) encodeValue(
case cadence.Word64:
return e.encodeWord64(v)

case cadence.Word128:
return e.encodeWord128(v)

case cadence.Fix64:
return e.encodeFix64(v)

Expand Down Expand Up @@ -690,6 +694,13 @@ func (e *Encoder) encodeWord64(v cadence.Word64) error {
return e.enc.EncodeUint64(uint64(v))
}

// encodeWord128 encodes cadence.Word128 as
// language=CDDL
// word128-value = uint .ge 0
func (e *Encoder) encodeWord128(v cadence.Word128) error {
return e.enc.EncodeBigInt(v.Big())
}

// encodeFix64 encodes cadence.Fix64 as
// language=CDDL
// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807
Expand Down
4 changes: 4 additions & 0 deletions encoding/ccf/simple_type_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const ( // Cadence simple type IDs
TypeBytes
TypeVoid
TypeFunction
TypeWord128
)

// NOTE: cadence.FunctionType isn't included in simpleTypeIDByType
Expand Down Expand Up @@ -194,6 +195,9 @@ func simpleTypeIDByType(typ cadence.Type) (uint64, bool) {
case cadence.Word64Type:
return TypeWord64, true

case cadence.Word128Type:
return TypeWord128, true

case cadence.Fix64Type:
return TypeFix64, true

Expand Down
1 change: 1 addition & 0 deletions encoding/ccf/traverse_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool)
cadence.Word16Type,
cadence.Word32Type,
cadence.Word64Type,
cadence.Word128Type,
cadence.Fix64Type,
cadence.UFix64Type,
cadence.PathType,
Expand Down
22 changes: 22 additions & 0 deletions encoding/json/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ func (d *Decoder) decodeJSON(v any) cadence.Value {
return d.decodeWord32(valueJSON)
case word64TypeStr:
return d.decodeWord64(valueJSON)
case word128TypeStr:
return d.decodeWord128(valueJSON)
case fix64TypeStr:
return d.decodeFix64(valueJSON)
case ufix64TypeStr:
Expand Down Expand Up @@ -565,6 +567,24 @@ func (d *Decoder) decodeWord64(valueJSON any) cadence.Word64 {
return cadence.NewMeteredWord64(d.gauge, i)
}

func (d *Decoder) decodeWord128(valueJSON any) cadence.Word128 {
value, err := cadence.NewMeteredWord128FromBig(
d.gauge,
func() *big.Int {
bigInt := d.decodeBigInt(valueJSON)
if bigInt == nil {
// TODO: propagate toString error from decodeBigInt
panic(errors.NewDefaultUserError("invalid Word128: %s", valueJSON))
turbolent marked this conversation as resolved.
Show resolved Hide resolved
}
return bigInt
},
)
if err != nil {
panic(errors.NewDefaultUserError("invalid Word128: %w", err))
}
return value
}

func (d *Decoder) decodeFix64(valueJSON any) cadence.Fix64 {
v, err := cadence.NewMeteredFix64(d.gauge, func() (string, error) {
return toString(valueJSON), nil
Expand Down Expand Up @@ -1213,6 +1233,8 @@ func (d *Decoder) decodeType(valueJSON any, results typeDecodingResults) cadence
return cadence.TheWord32Type
case "Word64":
return cadence.TheWord64Type
case "Word128":
return cadence.TheWord128Type
case "Fix64":
return cadence.TheFix64Type
case "UFix64":
Expand Down
11 changes: 11 additions & 0 deletions encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ const (
word16TypeStr = "Word16"
word32TypeStr = "Word32"
word64TypeStr = "Word64"
word128TypeStr = "Word128"
fix64TypeStr = "Fix64"
ufix64TypeStr = "UFix64"
arrayTypeStr = "Array"
Expand Down Expand Up @@ -314,6 +315,8 @@ func Prepare(v cadence.Value) jsonValue {
return prepareWord32(v)
case cadence.Word64:
return prepareWord64(v)
case cadence.Word128:
return prepareWord128(v)
case cadence.Fix64:
return prepareFix64(v)
case cadence.UFix64:
Expand Down Expand Up @@ -522,6 +525,13 @@ func prepareWord64(v cadence.Word64) jsonValue {
}
}

func prepareWord128(v cadence.Word128) jsonValue {
return jsonValueObject{
Type: word128TypeStr,
Value: encodeBig(v.Big()),
}
}

func prepareFix64(v cadence.Fix64) jsonValue {
return jsonValueObject{
Type: fix64TypeStr,
Expand Down Expand Up @@ -758,6 +768,7 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue {
cadence.Word16Type,
cadence.Word32Type,
cadence.Word64Type,
cadence.Word128Type,
cadence.Fix64Type,
cadence.UFix64Type,
cadence.BlockType,
Expand Down
21 changes: 21 additions & 0 deletions encoding/json/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,26 @@ func TestEncodeWord64(t *testing.T) {
}...)
}

func TestEncodeWord128(t *testing.T) {

t.Parallel()

testAllEncodeAndDecode(t, []encodeTest{
{
"Zero",
cadence.NewWord128(0),
// language=json
`{"type":"Word128","value":"0"}`,
},
{
"Max",
cadence.Word128{Value: sema.Word128TypeMaxIntBig},
// language=json
`{"type":"Word128","value":"340282366920938463463374607431768211455"}`,
},
}...)
}

func TestEncodeFix64(t *testing.T) {

t.Parallel()
Expand Down Expand Up @@ -1736,6 +1756,7 @@ func TestEncodeSimpleTypes(t *testing.T) {
cadence.Word16Type{},
cadence.Word32Type{},
cadence.Word64Type{},
cadence.Word128Type{},
cadence.Fix64Type{},
cadence.UFix64Type{},
cadence.BlockType{},
Expand Down
4 changes: 4 additions & 0 deletions runtime/convertTypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ func ExportMeteredType(
return cadence.TheWord32Type
case sema.Word64Type:
return cadence.TheWord64Type
case sema.Word128Type:
return cadence.TheWord128Type
case sema.Fix64Type:
return cadence.TheFix64Type
case sema.UFix64Type:
Expand Down Expand Up @@ -602,6 +604,8 @@ func ImportType(memoryGauge common.MemoryGauge, t cadence.Type) interpreter.Stat
return interpreter.NewPrimitiveStaticType(memoryGauge, interpreter.PrimitiveStaticTypeWord32)
case cadence.Word64Type:
return interpreter.NewPrimitiveStaticType(memoryGauge, interpreter.PrimitiveStaticTypeWord64)
case cadence.Word128Type:
return interpreter.NewPrimitiveStaticType(memoryGauge, interpreter.PrimitiveStaticTypeWord128)
case cadence.Fix64Type:
return interpreter.NewPrimitiveStaticType(memoryGauge, interpreter.PrimitiveStaticTypeFix64)
case cadence.UFix64Type:
Expand Down
18 changes: 18 additions & 0 deletions runtime/convertValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ func exportValueWithInterpreter(
return cadence.NewMeteredWord32(inter, uint32(v)), nil
case interpreter.Word64Value:
return cadence.NewMeteredWord64(inter, uint64(v)), nil
case interpreter.Word128Value:
return cadence.NewMeteredWord128FromBig(
inter,
func() *big.Int {
return v.ToBigInt(inter)
},
)
case interpreter.Fix64Value:
return cadence.Fix64(v), nil
case interpreter.UFix64Value:
Expand Down Expand Up @@ -793,6 +800,8 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type)
return i.importWord32(v), nil
case cadence.Word64:
return i.importWord64(v), nil
case cadence.Word128:
return i.importWord128(v), nil
case cadence.Fix64:
return i.importFix64(v), nil
case cadence.UFix64:
Expand Down Expand Up @@ -1033,6 +1042,15 @@ func (i valueImporter) importWord64(v cadence.Word64) interpreter.Word64Value {
)
}

func (i valueImporter) importWord128(v cadence.Word128) interpreter.Word128Value {
return interpreter.NewWord128ValueFromBigInt(
i.inter,
func() *big.Int {
return v.Value
},
)
}

func (i valueImporter) importFix64(v cadence.Fix64) interpreter.Fix64Value {
return interpreter.NewFix64Value(
i.inter,
Expand Down
Loading