Skip to content

Commit 32d4a96

Browse files
authored
feat: support bigs ints (#18)
Required by fil-forge/libforge#28 + fil-forge/piri-signing-service#3
1 parent d526651 commit 32d4a96

4 files changed

Lines changed: 134 additions & 36 deletions

File tree

go.mod

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
module github.com/fil-forge/ucantone
22

3-
go 1.25
3+
go 1.25.0
44

55
// replace "github.com/alanshaw/dag-json-gen" => ../dag-json-gen
66

77
require (
88
github.com/alanshaw/dag-json-gen v0.0.4
9+
github.com/filecoin-project/go-state-types v0.18.0
910
github.com/gobwas/glob v0.2.3
10-
github.com/ipfs/go-cid v0.5.0
11+
github.com/ipfs/go-cid v0.6.0
1112
github.com/multiformats/go-multibase v0.2.0
1213
github.com/multiformats/go-multihash v0.2.3
13-
github.com/multiformats/go-varint v0.0.7
14+
github.com/multiformats/go-varint v0.1.0
1415
github.com/stretchr/testify v1.11.1
1516
github.com/whyrusleeping/cbor-gen v0.3.1
1617
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b
17-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
18+
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da
1819
)
1920

2021
require (
2122
github.com/davecgh/go-spew v1.1.1 // indirect
22-
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
23+
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
2324
github.com/kr/pretty v0.3.1 // indirect
24-
github.com/minio/sha256-simd v1.0.0 // indirect
25+
github.com/minio/sha256-simd v1.0.1 // indirect
2526
github.com/mr-tron/base58 v1.2.0 // indirect
26-
github.com/multiformats/go-base32 v0.0.3 // indirect
27-
github.com/multiformats/go-base36 v0.1.0 // indirect
27+
github.com/multiformats/go-base32 v0.1.0 // indirect
28+
github.com/multiformats/go-base36 v0.2.0 // indirect
2829
github.com/pmezard/go-difflib v1.0.0 // indirect
2930
github.com/rogpeppe/go-internal v1.14.1 // indirect
3031
github.com/spaolacci/murmur3 v1.1.0 // indirect
3132
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
32-
golang.org/x/crypto v0.44.0 // indirect
33-
golang.org/x/sys v0.40.0 // indirect
34-
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
33+
golang.org/x/crypto v0.49.0 // indirect
34+
golang.org/x/sys v0.42.0 // indirect
3535
gopkg.in/yaml.v3 v3.0.1 // indirect
36-
lukechampine.com/blake3 v1.1.6 // indirect
36+
lukechampine.com/blake3 v1.4.1 // indirect
3737
pitr.ca/jsontokenizer v0.3.0 // indirect
3838
)

go.sum

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,34 @@ github.com/alanshaw/dag-json-gen v0.0.4/go.mod h1:rXxWw0SItP9QjxpRMpkju66h0KumF7
33
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
44
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
55
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/filecoin-project/go-state-types v0.18.0 h1:oDcjihXRlf2cM176atZzllp79Zc+kcbiuQM9DPL/1a4=
7+
github.com/filecoin-project/go-state-types v0.18.0/go.mod h1:CcyG4ZQRDWW+QUY2WDf1KtVDRN7W4twjsfgnGbQfJVI=
68
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
79
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
8-
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
9-
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
10-
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
11-
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
12-
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
13-
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
10+
github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30=
11+
github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ=
12+
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
13+
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
1414
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1515
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
16-
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
17-
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1816
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1917
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
2018
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
2119
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
22-
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
23-
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
20+
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
21+
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
2422
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
2523
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
26-
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
27-
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
28-
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
29-
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
24+
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
25+
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
26+
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
27+
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
3028
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
3129
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
3230
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
3331
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
34-
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
35-
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
32+
github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI=
33+
github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI=
3634
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
3735
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3836
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -49,18 +47,18 @@ gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRyS
4947
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=
5048
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=
5149
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=
52-
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
53-
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
54-
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
55-
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
56-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
57-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
50+
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
51+
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
52+
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
53+
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
54+
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
55+
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
5856
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5957
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
6058
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
6159
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6260
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
63-
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
64-
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
61+
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
62+
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
6563
pitr.ca/jsontokenizer v0.3.0 h1:Qr70hk4/wcpFEgu/6aJ+nvYQ6x/xS0WOkC627ceiI/M=
6664
pitr.ca/jsontokenizer v0.3.0/go.mod h1:3DJdA2QNOU6cI0XkH6pRKZ4Oe8G5SDRUQ6PFAwaQ3YY=

ipld/datamodel/any.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
jsg "github.com/alanshaw/dag-json-gen"
1414
"github.com/fil-forge/ucantone/ipld"
15+
"github.com/filecoin-project/go-state-types/big"
1516
"github.com/ipfs/go-cid"
1617
cbg "github.com/whyrusleeping/cbor-gen"
1718
)
@@ -22,6 +23,9 @@ import (
2223
// - Null (nil)
2324
// - Boolean (bool)
2425
// - Integer (int64, int)
26+
// - BigInteger (go-state-types/big.Int) — encoded as a CBOR bignum: tag 2
27+
// for non-negative values and tag 3 for negative values (RFC 8949).
28+
// CBOR only; big integers are not supported in DAG-JSON.
2529
// - String (string)
2630
// - Bytes ([]byte)
2731
// - List ([]Any)
@@ -40,6 +44,7 @@ type Any struct {
4044
// - bool
4145
// - int
4246
// - int64
47+
// - big.Int (go-state-types/big; CBOR only)
4348
// - string
4449
// - []byte
4550
// - slice
@@ -66,6 +71,14 @@ func (a *Any) MarshalCBOR(w io.Writer) error {
6671
return cbg.CborInt(v).MarshalCBOR(w)
6772
case int:
6873
return cbg.CborInt(v).MarshalCBOR(w)
74+
case big.Int:
75+
return marshalCborBigInt(w, v)
76+
case *big.Int:
77+
if v == nil {
78+
_, err := w.Write(cbg.CborNull)
79+
return err
80+
}
81+
return marshalCborBigInt(w, *v)
6982
case bool:
7083
return cbg.CborBool(v).MarshalCBOR(w)
7184
case cid.Cid:
@@ -153,6 +166,20 @@ func (a *Any) UnmarshalCBOR(r io.Reader) (err error) {
153166
}
154167
case cbg.MajTag:
155168
switch extra {
169+
case 2: // CBOR positive bignum (tag 2 + byte string): value = n
170+
b, err := readBignumBytes(pr)
171+
if err != nil {
172+
return err
173+
}
174+
a.Value = big.PositiveFromUnsignedBytes(b)
175+
return nil
176+
case 3: // CBOR negative bignum (tag 3 + byte string): value = -1 - n
177+
b, err := readBignumBytes(pr)
178+
if err != nil {
179+
return err
180+
}
181+
a.Value = big.Sub(big.NewInt(-1), big.PositiveFromUnsignedBytes(b))
182+
return nil
156183
case 42:
157184
cbc := cbg.CborCid{}
158185
if err = cbc.UnmarshalCBOR(pr); err != nil {
@@ -246,6 +273,8 @@ func (a *Any) MarshalDagJSON(w io.Writer) error {
246273
return jw.WriteInt64(v)
247274
case int:
248275
return jw.WriteInt64(int64(v))
276+
case big.Int, *big.Int:
277+
return fmt.Errorf("big integers are not supported in DAG-JSON")
249278
case bool:
250279
return jw.WriteBool(v)
251280
case cid.Cid:
@@ -419,6 +448,43 @@ func (a *Any) UnmarshalDagJSON(r io.Reader) (err error) {
419448
return nil
420449
}
421450

451+
// marshalCborBigInt writes a big.Int as a CBOR bignum (RFC 8949): tag 2 for
452+
// non-negative values and tag 3 for negative values, each followed by a byte
453+
// string holding the big-endian magnitude. A negative value v is encoded as
454+
// the magnitude of n = -1 - v, so it round-trips via value = -1 - n on decode.
455+
func marshalCborBigInt(w io.Writer, v big.Int) error {
456+
if v.Nil() {
457+
_, err := w.Write(cbg.CborNull)
458+
return err
459+
}
460+
tag := uint64(2)
461+
mag := v // non-negative: encode the magnitude directly
462+
if v.Sign() < 0 {
463+
tag = 3
464+
mag = big.Sub(big.NewInt(-1), v) // n = -1 - v
465+
}
466+
cw := cbg.NewCborWriter(w)
467+
if err := cw.WriteMajorTypeHeader(cbg.MajTag, tag); err != nil {
468+
return err
469+
}
470+
b := mag.Int.Bytes() // raw big-endian magnitude (no sign prefix)
471+
if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(b))); err != nil {
472+
return err
473+
}
474+
_, err := cw.Write(b)
475+
return err
476+
}
477+
478+
// readBignumBytes consumes a CBOR bignum tag header and returns the bytes of
479+
// the following byte string (the big-endian magnitude).
480+
func readBignumBytes(r io.Reader) ([]byte, error) {
481+
cr := cbg.NewCborReader(r)
482+
if _, _, err := cr.ReadHeader(); err != nil { // consume the bignum tag header
483+
return nil, err
484+
}
485+
return cbg.ReadByteArray(cr, 256)
486+
}
487+
422488
func peekCborHeader(r io.Reader) (byte, uint64, io.Reader, error) {
423489
cr := cbg.NewCborReader(r)
424490
maj, extra, err := cr.ReadHeader()

ipld/datamodel/any_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/fil-forge/ucantone/ipld"
99
"github.com/fil-forge/ucantone/ipld/datamodel"
1010
"github.com/fil-forge/ucantone/testutil"
11+
"github.com/filecoin-project/go-state-types/big"
1112
"github.com/stretchr/testify/require"
1213
)
1314

@@ -61,3 +62,36 @@ func TestAny(t *testing.T) {
6162
})
6263
}
6364
}
65+
66+
// big.Int is CBOR only (not supported in DAG-JSON), so it round-trips through
67+
// its own test rather than the shared list above.
68+
func TestAnyBigInt(t *testing.T) {
69+
values := []big.Int{
70+
big.NewInt(0),
71+
big.NewInt(138),
72+
big.NewInt(-138),
73+
big.MustFromString("340282366920938463463374607431768211457"), // > maxUint64, positive
74+
big.MustFromString("-340282366920938463463374607431768211457"), // < minInt64, negative
75+
}
76+
77+
for _, v := range values {
78+
t.Run(fmt.Sprintf("dag-cbor %s", v), func(t *testing.T) {
79+
initial := datamodel.NewAny(v)
80+
81+
var buf bytes.Buffer
82+
err := initial.MarshalCBOR(&buf)
83+
require.NoError(t, err)
84+
85+
var decoded datamodel.Any
86+
err = decoded.UnmarshalCBOR(&buf)
87+
require.NoError(t, err)
88+
require.Equal(t, v, decoded.Value)
89+
})
90+
91+
t.Run(fmt.Sprintf("dag-json unsupported %s", v), func(t *testing.T) {
92+
var buf bytes.Buffer
93+
err := datamodel.NewAny(v).MarshalDagJSON(&buf)
94+
require.Error(t, err)
95+
})
96+
}
97+
}

0 commit comments

Comments
 (0)