Compare commits

..

137 Commits

Author SHA1 Message Date
cf0ccd2f6e refactor(benchmark_test): migrate from github.com/ipfs/go-cid to code.sonr.org/go/cid
Some checks failed
Close Stale Issues / stale (push) Has been cancelled
Close Generated PRs / stale (push) Has been cancelled
Go Checks / go-check (push) Has been cancelled
Go Test / go-test (push) Has been cancelled
Releaser / releaser (push) Has been cancelled
Tag Push Checker / releaser (push) Has been cancelled
2026-01-08 15:46:16 -05:00
a7a1f319b3 docs(readme): update readme to reflect new package path 2026-01-08 15:46:14 -05:00
8c8b22eab1 docs(core): update deprecated comment with new pull request link 2026-01-08 15:46:13 -05:00
af732567fd chore(config): add Repo.toml and update go.mod for config changes 2026-01-08 15:46:12 -05:00
dependabot[bot]
1b3fc58457 chore(deps): bump golang.org/x/crypto from 0.35.0 to 0.45.0 (#179)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.35.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.35.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 14:57:43 +13:00
Rod Vagg
594b41a340 v0.6.0 bump (#178) 2025-10-28 12:15:36 +11:00
dependabot[bot]
7942a87de8 chore(deps): bump github.com/multiformats/go-varint from 0.0.7 to 0.1.0 (#177)
Bumps [github.com/multiformats/go-varint](https://github.com/multiformats/go-varint) from 0.0.7 to 0.1.0.
- [Release notes](https://github.com/multiformats/go-varint/releases)
- [Commits](https://github.com/multiformats/go-varint/compare/v0.0.7...v0.1.0)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-varint
  dependency-version: 0.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 12:12:14 +11:00
Andrew Gillis
6365bffd65 Merge pull request #176 from ipfs/uci/update-go
ci: uci/update-go
2025-08-20 22:10:32 -07:00
web3-bot
1022508918 chore!: bump go.mod to Go 1.24 and run go fix
BREAKING CHANGE: Updating the Go version to 1.24
2025-08-20 16:22:55 +00:00
dependabot[bot]
1afa3fef92 chore(deps): bump golang.org/x/crypto from 0.31.0 to 0.35.0 (#175)
* chore(deps): bump golang.org/x/crypto from 0.31.0 to 0.35.0

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.35.0.
- [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.35.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update go.mod

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rod Vagg <rod@vagg.org>
2025-04-15 10:32:11 +10:00
web3-bot
3b830eb57c ci: uci/copy-templates (#174)
* chore: add or force update .github/workflows/stale.yml

* chore: add or force update .github/workflows/generated-pr.yml
2025-03-28 19:15:37 +01:00
web3-bot
4d43496971 chore!: bump go.mod to Go 1.23 and run go fix (#173)
BREAKING CHANGE: Updating the Go version to 1.23
2025-02-16 22:33:16 +01:00
Rod Vagg
5cddba5d53 v0.5.0 bump (#172) 2025-01-22 13:02:05 +11:00
dependabot[bot]
c38d1ef3f4 chore(deps): bump golang.org/x/crypto from 0.17.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 16:18:17 +11:00
Michael Muré
7f61b44eb9 move _rsrch/cidiface into an internal package
_rsrch/cidiface is an engineering experiment package to explore the right form for a CID. As such, it's not meant to be actually used, and could result in conflict if used.

To avoid accidentally importing it (or even getting suggestions to do so), this package is moved into an "internal" folder, hinting tooling that it's not meant for external consumption.
2024-09-27 09:13:31 +10:00
web3-bot
13ed68d2d7 chore!: bump go.mod to Go 1.22 and run go fix (#167) 2024-08-26 11:01:52 +10:00
web3-bot
b655a59a8a Merge pull request #166 from ipfs/uci/copy-templates
ci: uci/copy-templates
2024-03-22 12:54:37 +00:00
web3-bot
81bd42a55c chore: add or force update .github/workflows/tagpush.yml 2024-03-21 15:36:17 +00:00
web3-bot
23af06e490 chore: add or force update .github/workflows/release-check.yml 2024-03-21 15:36:17 +00:00
web3-bot
52653019a7 chore: add or force update .github/workflows/releaser.yml 2024-03-21 15:36:17 +00:00
web3-bot
11e146d86d chore: add or force update .github/workflows/go-check.yml 2024-03-21 15:36:17 +00:00
web3-bot
91f1b23adf chore: add or force update .github/workflows/go-test.yml 2024-03-21 15:36:17 +00:00
web3-bot
607591de0b chore: add or force update .github/workflows/tagpush.yml 2024-03-21 14:55:31 +00:00
web3-bot
93d2928381 chore: add or force update .github/workflows/release-check.yml 2024-03-21 14:55:31 +00:00
web3-bot
1f9bd88a8e chore: add or force update .github/workflows/releaser.yml 2024-03-21 14:55:31 +00:00
web3-bot
f6805b41a0 chore: add or force update .github/workflows/go-check.yml 2024-03-21 14:55:31 +00:00
web3-bot
01aca4cad3 chore: add or force update .github/workflows/go-test.yml 2024-03-21 14:55:31 +00:00
web3-bot
9b08623b01 chore: add or force update .github/workflows/tagpush.yml 2024-03-21 14:13:35 +00:00
web3-bot
1399b513f9 chore: add or force update .github/workflows/release-check.yml 2024-03-21 14:13:35 +00:00
web3-bot
00c494db15 chore: add or force update .github/workflows/releaser.yml 2024-03-21 14:13:35 +00:00
web3-bot
d1e16392a7 chore: add or force update .github/workflows/go-check.yml 2024-03-21 14:13:35 +00:00
web3-bot
af4cf3c53a chore: add or force update .github/workflows/go-test.yml 2024-03-21 14:13:35 +00:00
web3-bot
01fcc60a8c Merge pull request #165 from ipfs/uci/update-go
ci: uci/update-go
2024-03-14 11:40:58 +00:00
web3-bot
663c9b30cf chore: run go mod tidy 2024-03-13 14:58:09 +00:00
web3-bot
4a2ad46e4c chore: bump go.mod to Go 1.21 and run go fix 2024-03-13 14:58:09 +00:00
GitHub
a126b93d54 chore: Update .github/workflows/stale.yml [skip ci] 2024-01-31 17:37:03 +00:00
dependabot[bot]
7db2df1954 chore(deps): bump golang.org/x/crypto from 0.1.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-02 10:46:28 +11:00
web3-bot
d900d766af chore: bump go.mod to Go 1.20 and run go fix (#163) 2023-08-14 15:37:26 +02:00
web3-bot
aea9f4a20c chore: add or force update .github/workflows/tagpush.yml 2023-08-14 12:00:07 +10:00
web3-bot
6bf5ab9148 chore: add or force update .github/workflows/release-check.yml 2023-08-14 12:00:07 +10:00
web3-bot
ef021a6454 chore: add or force update .github/workflows/releaser.yml 2023-08-14 12:00:07 +10:00
web3-bot
13f45bfa17 chore: add or force update .github/workflows/go-check.yml 2023-08-14 12:00:07 +10:00
web3-bot
4828462368 chore: add or force update .github/workflows/go-test.yml 2023-08-14 12:00:07 +10:00
web3-bot
e12272217c chore: delete templates [skip ci] (#161) 2023-08-13 18:52:13 +02:00
GitHub
3863e676fd chore: Update .github/workflows/stale.yml [skip ci] 2023-06-28 08:49:10 +00:00
GitHub
9a3cbeb6a0 chore: Update .github/workflows/stale.yml [skip ci] 2023-06-14 11:15:02 +00:00
dependabot[bot]
f834302da4 chore(deps): bump github.com/multiformats/go-multihash
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.2.2 to 0.2.3.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.2.2...v0.2.3)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multihash
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 14:45:14 +10:00
dependabot[bot]
6b3d27fb29 chore(deps): bump github.com/multiformats/go-multihash
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.2.1 to 0.2.2.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.2.1...v0.2.2)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multihash
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 11:04:36 +10:00
dependabot[bot]
829c826f6b chore(deps): bump github.com/multiformats/go-varint from 0.0.6 to 0.0.7
Bumps [github.com/multiformats/go-varint](https://github.com/multiformats/go-varint) from 0.0.6 to 0.0.7.
- [Release notes](https://github.com/multiformats/go-varint/releases)
- [Commits](https://github.com/multiformats/go-varint/compare/v0.0.6...v0.0.7)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-varint
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 13:13:25 +10:00
dependabot[bot]
8bb6c55e54 chore(deps): bump github.com/multiformats/go-multihash
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.0.15 to 0.2.1.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.0.15...v0.2.1)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multihash
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 12:53:59 +10:00
dependabot[bot]
72c7f21dcd chore(deps): bump github.com/multiformats/go-multibase
Bumps [github.com/multiformats/go-multibase](https://github.com/multiformats/go-multibase) from 0.0.3 to 0.2.0.
- [Release notes](https://github.com/multiformats/go-multibase/releases)
- [Commits](https://github.com/multiformats/go-multibase/compare/v0.0.3...v0.2.0)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multibase
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 12:50:34 +10:00
dependabot-preview[bot]
93a8da769f Upgrade to GitHub-native Dependabot 2023-04-04 14:13:57 +10:00
Rod Vagg
d46e7f2866 v0.4.1 2023-04-04 14:08:15 +10:00
gammazero
83a0e939a4 Add unit test for unexpected eof 2023-04-04 14:08:15 +10:00
Andrew Gillis
0981f8566c Update cid.go
Co-authored-by: Rod Vagg <rod@vagg.org>
2023-04-04 14:08:15 +10:00
gammazero
166a3a6880 CidFromReader should not wrap valid EOF return.
When reading from an io.Reader that has no data, the io.EOF error should not be wrapped in ErrInvalidCid. This is not an invalid CID, and is not the same as a partial read which is indicated by io.ErrUnexpectedEOF.

This fix is needed because existing code that uses CidFromReader may check for the end of an input stream by `if err == io.EOF` instead of the preferred `if errors.Is(err, io.EOF)`, and that code break at runtime after upgrading to go-cid v0.4.0.
2023-04-04 14:08:15 +10:00
Henrique Dias
8098d66787 chore: version 0.4.0 2023-03-20 09:29:34 +01:00
Henrique Dias
b98e249130 feat: wrap parsing errors into ErrInvalidCid 2023-03-20 09:29:34 +01:00
dependabot[bot]
85c423677c build(deps): bump golang.org/x/crypto
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20210506145944-38f3c27a63bf to 0.1.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/commits/v0.1.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 17:42:17 +11:00
dependabot[bot]
197da0b712 build(deps): bump golang.org/x/sys
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.0.0-20210309074719-68d13333faf2 to 0.1.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/commits/v0.1.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 16:12:56 +11:00
Rod Vagg
06fe289a6a fix: use crypto/rand.Read 2023-02-09 15:30:24 +11:00
web3-bot
a1630a3bd9 update .github/workflows/release-check.yml 2023-02-09 15:30:24 +11:00
web3-bot
225f5db116 update .github/workflows/go-check.yml 2023-02-09 15:30:24 +11:00
web3-bot
c2c040dac7 update .github/workflows/go-test.yml 2023-02-09 15:30:24 +11:00
web3-bot
b729e38c6e bump go.mod to Go 1.19 and run go fix 2023-02-09 15:30:24 +11:00
Nikhilesh Susarla
823c6b8a59 Fix README.md example error (#146) 2022-10-19 17:17:54 +11:00
Jorropo
350d6afb62 chore: release v0.3.2 2022-09-04 16:00:09 +02:00
Jorropo
68090a8273 Revert "fix: bring back, but deprecate CodecToStr and Codecs"
This reverts commit 548365f3a7.
2022-09-04 16:00:09 +02:00
Jorropo
69784e9dd7 chore: release v0.2.1 2022-09-04 13:03:33 +02:00
Rod Vagg
548365f3a7 fix: bring back, but deprecate CodecToStr and Codecs
use go-multicodec as the source of truth
2022-09-04 13:03:33 +02:00
web3-bot
5da28005a9 update .github/workflows/go-check.yml 2022-08-25 15:55:13 +10:00
web3-bot
7f6771bc54 update .github/workflows/go-test.yml 2022-08-25 15:55:13 +10:00
web3-bot
5b6a9dce73 run gofmt -s 2022-08-25 15:55:13 +10:00
web3-bot
890f55d9f3 bump go.mod to Go 1.18 and run go fix 2022-08-25 15:55:13 +10:00
Jorropo
085afa84d0 chore: release v0.3.0 2022-08-25 05:34:36 +02:00
Jorropo
0052a62190 fix: return nil Bytes() if the Cid in undef 2022-08-25 05:34:36 +02:00
GitHub
802b45594e chore: Update .github/workflows/stale.yml [skip ci] 2022-07-21 09:54:55 +00:00
Steven Allen
8f7d7ac18e Merge pull request #139 from mg98/feat/must-parse
Add MustParse
2022-06-27 08:55:37 -07:00
GitHub
b106e0883a Update .github/workflows/stale.yml 2022-06-27 10:45:49 +00:00
Marcel Gregoriadis
386c6cc18a Add MustParse 2022-06-24 17:35:23 +02:00
ipfs-mgmt-read-write[bot]
f4b3e66993 Add .github/workflows/stale.yml 2022-06-13 12:53:24 +00:00
Marcin Rataj
b2064d74a8 fix: remove invalid multicodec2string mappings (#137)
* refactor: remove Codecs table
* chore: go-cid 0.2.0

Codec table was missing dag-json and it had invalid code for dag-cbor.
It also had invalid string representation of dag-pb -- it was using
'protobuf' which is a totally different code.

This removes invalid mappings entirely.
https://github.com/multiformats/go-multicodec should be used instead.
2022-04-21 22:39:31 +02:00
web3-bot
ddd9ef7248 update .github/workflows/go-check.yml 2022-04-05 09:32:38 +10:00
web3-bot
6be8b631d0 update .github/workflows/go-test.yml 2022-04-05 09:32:38 +10:00
web3-bot
395d392889 update .github/workflows/automerge.yml 2022-04-05 09:32:38 +10:00
web3-bot
1d11990417 bump go.mod to Go 1.17 and run go fix 2022-04-05 09:32:38 +10:00
web3-bot
dc3bb41324 sync: update CI config files (#136)
* add version.json file

* update .github/workflows/go-test.yml

* update .github/workflows/go-check.yml

* add .github/workflows/releaser.yml

* add .github/workflows/release-check.yml

* add .github/workflows/tagpush.yml

Co-authored-by: web3-bot <web3-bot@users.noreply.github.com>
2021-12-13 12:26:14 +01:00
Masih H. Derkani
28f4a5eab6 Benchmark existing ways to check for IDENTITY CIDs
Add benchmarks that compare two ways of checking for
`multihash.IDENTITY` code:
1. `Cid.Prefix().MhType`
2. Decode of `Cid.Hash()`

This benchmark illustrates that using Cid.Prefix is more efficient than
`multihash.Decode`. Users wishing to perform such a check should use
`Cid.Prefix`.

Consider that `Cid.Prefix` is already efficient enough and introducing a
dedicated API for performing this check will likely result in small
gains.

Relates to #133
2021-11-08 14:52:45 +00:00
Daniel Martí
5640b0169f avoid double alloc in NewCidV1
We allocate once via "make([]byte, len)",
and again when that buffer is converted to a string.

Thankfully, since Go 1.10 we have strings.Builder,
designed specifically for this use case.

In a downstream benchmark in go-car,
which needs to reconstruct many CID values,
we see small but nice gains:

    name           old time/op    new time/op    delta
    ReadBlocks-16    1.09ms ± 4%    1.06ms ± 5%   -3.33%  (p=0.007 n=11+11)

    name           old speed      new speed      delta
    ReadBlocks-16   478MB/s ± 4%   494MB/s ± 5%   +3.46%  (p=0.007 n=11+11)

    name           old alloc/op   new alloc/op   delta
    ReadBlocks-16    1.30MB ± 0%    1.25MB ± 0%   -3.86%  (p=0.000 n=12+12)

    name           old allocs/op  new allocs/op  delta
    ReadBlocks-16     9.50k ± 0%     8.45k ± 0%  -11.05%  (p=0.000 n=12+12)
2021-09-12 14:08:59 +02:00
Steven Allen
cf76220258 Merge pull request #131 from ipfs/web3-bot/sync
sync: update CI config files
2021-08-30 08:37:22 -07:00
web3-bot
9e2855d9ff update .github/workflows/go-check.yml 2021-08-17 13:32:06 +00:00
web3-bot
b8eba8ea35 update .github/workflows/go-test.yml 2021-08-17 13:32:06 +00:00
web3-bot
2628583977 update .github/workflows/automerge.yml 2021-08-17 13:32:06 +00:00
web3-bot
6e96c56557 run gofmt -s 2021-08-17 13:32:05 +00:00
web3-bot
44cccd62db bump go.mod to Go 1.16 and run go fix 2021-08-17 13:32:05 +00:00
Daniel Martí
de6c03deae amend the CidFromReader slice extension math
The append+make slice extension idiom works, but note that append uses
the slice's length as its base. We need to append the number of bytes
required for length to reach cidLength, not the capacity.

The added test case panicked before this change, and works now:

	--- FAIL: TestReadCidsFromBuffer (0.00s)
	panic: runtime error: slice bounds out of range [:73] with capacity 64 [recovered]
		panic: runtime error: slice bounds out of range [:73] with capacity 64

	goroutine 37 [running]:
	testing.tRunner.func1.2({0x570d60, 0xc000016438})
		testing/testing.go:1203 +0x24e
	testing.tRunner.func1()
		testing/testing.go:1206 +0x218
	panic({0x570d60, 0xc000016438})
		runtime/panic.go:1038 +0x215
	github.com/ipfs/go-cid.CidFromReader({0x5b0e20, 0xc000010900})
		github.com/ipfs/go-cid/cid.go:803 +0x75f
	github.com/ipfs/go-cid.TestReadCidsFromBuffer(0xc00014ba00)
		github.com/ipfs/go-cid/cid_test.go:710 +0x625
	testing.tRunner(0xc00014ba00, 0x58af38)
		testing/testing.go:1253 +0x102
	created by testing.(*T).Run
		testing/testing.go:1300 +0x35a
	exit status 2
	FAIL	github.com/ipfs/go-cid	0.004s
2021-07-16 10:10:50 +01:00
Daniel Martí
c4c8760a80 implement CidFromReader
And reuse two CidFromBytes tests for it, which includes both CIDv0 and
CIDv1 cases as inputs, as well as some inputs that should error.

Fixes #126.
2021-07-15 01:01:22 +01:00
Steven Allen
8e9280df3d Merge pull request #118 from ipfs/multihash-update
Update go-multihash.  Now uses registry system.
2021-06-25 17:23:48 -07:00
Eric Myhre
b4bcfe45c9 Update go-multihash.
You can't see it from here, but go-mulithash now uses registry system,
so it's reasonably possible to introduce new hashers, and to use
(some parts of!) go-multihash without bringing in lots of transitive
dependencies.

The main package of go-multihash still brings in everything
transitively that it did before, so go-cid's transitives aren't
shrinking, and no code is changing here... but it's closer.
If we did cut over to the new go-mulithash/core, we could make
many transitive dependencies become optional.
2021-06-25 16:05:47 -07:00
Marten Seemann
979bf3fb85 Merge pull request #125 from ipfs/web3-bot/sync
sync: update CI config files
2021-06-01 12:40:31 -07:00
web3-bot
95ec98a9cc update .github/workflows/go-check.yml 2021-06-01 16:53:30 +00:00
web3-bot
1c23d3b694 update .github/workflows/go-test.yml 2021-06-01 16:53:30 +00:00
web3-bot
5b5d1329c5 update .github/workflows/automerge.yml 2021-06-01 16:53:30 +00:00
web3-bot
a42c04af93 sync: update CI config files (#124) 2021-05-12 15:13:58 +00:00
Steven Allen
0582f6b9b2 Merge pull request #122 from iand/chore/repo-health
chore: fixups from running go vet, go fmt and staticcheck
2021-05-10 08:32:22 -07:00
Ian Davis
0717510dfb chore: fixups from running go vet, go fmt and staticcheck 2021-05-07 15:43:00 +01:00
dependabot-preview[bot]
e530276a70 Bump github.com/multiformats/go-varint from 0.0.5 to 0.0.6
Bumps [github.com/multiformats/go-varint](https://github.com/multiformats/go-varint) from 0.0.5 to 0.0.6.
- [Release notes](https://github.com/multiformats/go-varint/releases)
- [Commits](https://github.com/multiformats/go-varint/compare/v0.0.5...v0.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-15 14:27:46 +11:00
dependabot-preview[bot]
45f4147a06 Bump github.com/multiformats/go-multihash from 0.0.13 to 0.0.14
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.0.13 to 0.0.14.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.0.13...v0.0.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-15 14:12:37 +11:00
Rod Vagg
b34ad3cfa5 s/characters/bytes 2020-10-15 14:05:52 +11:00
gammazero
ec089b8a53 Fix inaccurate comment for uvarint
This fixes the comment to be consistent with the code, and fixes spelling.

Fixes Issue #111
2020-10-15 14:05:52 +11:00
Rod Vagg
efe2d2de45 coverage: more tests for cid 2020-10-15 14:00:36 +11:00
Rod Vagg
46aac88838 coverage: more tests for varint 2020-10-15 14:00:36 +11:00
Rod Vagg
f60e346b24 coverage: more tests for builder 2020-10-15 14:00:36 +11:00
Rod Vagg
8647a1d84b fix: make tests run with Go 1.15
> ./cid_test.go:451:52: conversion from uint64 to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)
2020-10-15 14:00:36 +11:00
Alex Good
de49849130 Add the dagjose multiformat 2020-10-15 14:00:36 +11:00
Whyrusleeping
8b9ff3906e Merge pull request #109 from ipfs/feat/optimize-prefix
feat: optimize cid.Prefix
2020-07-23 09:15:54 -07:00
Steven Allen
f7cb4c91eb feat: optimize cid.Prefix
We call this frequently and having to allocate here is really unfortunate.
2020-07-22 19:45:20 -07:00
Peter Rabbitson
85cd30874e Merge pull request #107 from ipfs/chore/base36_support
Chore/base36 support
2020-05-25 18:15:04 +02:00
Peter Rabbitson
12e2623f35 gx-era file no longer relevant 2020-05-25 18:08:48 +02:00
Marcin Rataj
d683e9243c test: lowercase base36
Base36 was introduced mainly for use in DNS, and various user agents
force lowercase, so tests should use that instead.

License: MIT
Signed-off-by: Marcin Rataj <lidel@lidel.org>
2020-05-25 17:46:16 +02:00
Peter Rabbitson
18af217de6 Add explicit base36 test 2020-05-25 14:06:26 +02:00
Peter Rabbitson
9830f823b5 Base36 support 2020-05-25 13:51:07 +02:00
Peter Rabbitson
d110f73c43 Remove gx remnants 2020-05-25 13:50:43 +02:00
Steven Allen
95a7ed4233 Merge pull request #104 from rvagg/rvagg/filecoin-codecs
feat: add Filecoin multicodecs
2020-05-25 00:46:19 -07:00
Rod Vagg
8dd6fe2778 feat: add Filecoin multicodecs
* fil-commitment-unsealed
* fil-commitment-sealed

Ref: https://github.com/multiformats/multicodec/pull/161
Ref: https://github.com/multiformats/multicodec/pull/172
2020-05-13 11:22:42 +10:00
Hector Sanjuan
a25d68f3e4 Add autocomment configuration 2020-05-04 12:05:06 +02:00
Whyrusleeping
7c82f3b81c Merge pull request #103 from ipfs/fix/write-to-interface
avoid calling the method WriteTo if we don't satisfy its contract
2020-05-01 16:06:55 -07:00
Jeromy
75caa6bdbd avoid calling the method WriteTo if we don't satisfy its contract 2020-05-01 16:04:36 -07:00
Whyrusleeping
c1c89c20c1 Merge pull request #102 from ipfs/feat/add-methods-for-using-less-memory
add a couple useful methods
2020-05-01 15:54:35 -07:00
Eric Myhre
628a0123ed Slightly more documentation comment. 2020-05-02 00:52:07 +02:00
Jeromy
266e76d591 properly satisfy writerTo interface 2020-05-01 15:44:06 -07:00
Jeromy
7d7cb88d78 add a couple useful methods 2020-05-01 15:01:28 -07:00
Steven Allen
723b4ab105 Merge pull request #101 from vmx/patch-1
chore: make comment reflect the code
2020-04-29 09:27:52 -07:00
Volker Mische
9cb3848e51 chore: make comment reflect the code
During the change to default CIDV1 to Base32, the corresponding code
comment wasn't updated.
2020-04-28 16:02:21 +02:00
Hector Sanjuan
f458e04107 Add standard issue template 2020-04-27 11:27:08 +02:00
Steven Allen
72cd3d39d7 Merge pull request #99 from ipfs/fix/minimal-encoding
fix: enforce minimal encoding
2020-02-04 12:12:58 -08:00
Steven Allen
c1b740035a fix: enforce minimal encoding
Note: this removes two errors that don't appear to be used by anyone.
2020-02-03 20:55:45 -08:00
35 changed files with 1156 additions and 371 deletions

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Getting Help on IPFS
url: https://ipfs.io/help
about: All information about how and where to get help on IPFS.
- name: IPFS Official Forum
url: https://discuss.ipfs.io
about: Please post general questions, support requests, and discussions here.

19
.github/ISSUE_TEMPLATE/open_an_issue.md vendored Normal file
View File

@@ -0,0 +1,19 @@
---
name: Open an issue
about: Only for actionable issues relevant to this repository.
title: ''
labels: need/triage
assignees: ''
---
<!--
Hello! To ensure this issue is correctly addressed as soon as possible by the IPFS team, please try to make sure:
- This issue is relevant to this repository's topic or codebase.
- A clear description is provided. It should includes as much relevant information as possible and clear scope for the issue to be actionable.
FOR GENERAL DISCUSSION, HELP OR QUESTIONS, please see the options at https://ipfs.io/help or head directly to https://discuss.ipfs.io.
(you can delete this section after reading)
-->

7
.github/Repo.toml vendored Normal file
View File

@@ -0,0 +1,7 @@
[scopes]
ci = [".github/workflows"]
config = [".github", "codecov.yml", "go.mod", "go.sum", "Makefile", "version.json"]
docs = ["README.md", "LICENSE"]
tests = ["*_test.go", "fuzz-data"]
research = ["_rsrch"]
core = ["cid.go", "builder.go", "set.go", "varint.go", "cid_fuzz.go", "deprecated.go"]

68
.github/config.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
# Configuration for welcome - https://github.com/behaviorbot/welcome
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
# Comment to be posted to on first time issues
newIssueWelcomeComment: >
Thank you for submitting your first issue to this repository! A maintainer
will be here shortly to triage and review.
In the meantime, please double-check that you have provided all the
necessary information to make this process easy! Any information that can
help save additional round trips is useful! We currently aim to give
initial feedback within **two business days**. If this does not happen, feel
free to leave a comment.
Please keep an eye on how this issue will be labeled, as labels give an
overview of priorities, assignments and additional actions requested by the
maintainers:
- "Priority" labels will show how urgent this is for the team.
- "Status" labels will show if this is ready to be worked on, blocked, or in progress.
- "Need" labels will indicate if additional input or analysis is required.
Finally, remember to use https://discuss.ipfs.io if you just need general
support.
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: >
Thank you for submitting this PR!
A maintainer will be here shortly to review it.
We are super grateful, but we are also overloaded! Help us by making sure
that:
* The context for this PR is clear, with relevant discussion, decisions
and stakeholders linked/mentioned.
* Your contribution itself is clear (code comments, self-review for the
rest) and in its best form. Follow the [code contribution
guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines)
if they apply.
Getting other community members to do a review would be great help too on
complex PRs (you can ask in the chats/forums). If you are unsure about
something, just leave us a comment.
Next steps:
* A maintainer will triage and assign priority to this PR, commenting on
any missing things and potentially assigning a reviewer for high
priority items.
* The PR gets reviews, discussed and approvals as needed.
* The PR is merged by maintainers when it has been approved and comments addressed.
We currently aim to provide initial feedback/triaging within **two business
days**. Please keep an eye on any labelling actions, as these will indicate
priorities and status of your contribution.
We are very grateful for your contribution!
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
# Currently disabled
#firstPRMergeComment: ""

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
schedule:
interval: weekly
time: "11:00"
open-pull-requests-limit: 10

14
.github/workflows/generated-pr.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Close Generated PRs
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1

18
.github/workflows/go-check.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Go Checks
on:
pull_request:
push:
branches: ["master"]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
jobs:
go-check:
uses: ipdxco/unified-github-workflows/.github/workflows/go-check.yml@v1.0

20
.github/workflows/go-test.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Go Test
on:
pull_request:
push:
branches: ["master"]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
jobs:
go-test:
uses: ipdxco/unified-github-workflows/.github/workflows/go-test.yml@v1.0
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

19
.github/workflows/release-check.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Release Checker
on:
pull_request_target:
paths: [ 'version.json' ]
types: [ opened, synchronize, reopened, labeled, unlabeled ]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
release-check:
uses: ipdxco/unified-github-workflows/.github/workflows/release-check.yml@v1.0

17
.github/workflows/releaser.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: Releaser
on:
push:
paths: [ 'version.json' ]
workflow_dispatch:
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.sha }}
cancel-in-progress: true
jobs:
releaser:
uses: ipdxco/unified-github-workflows/.github/workflows/releaser.yml@v1.0

14
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Close Stale Issues
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1

18
.github/workflows/tagpush.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Tag Push Checker
on:
push:
tags:
- v*
permissions:
contents: read
issues: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
releaser:
uses: ipdxco/unified-github-workflows/.github/workflows/tagpush.yml@v1.0

View File

@@ -1 +0,0 @@
0.9.3: QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN

View File

@@ -1,31 +0,0 @@
os:
- linux
language: go
go:
- 1.11.x
env:
global:
- GOTFLAGS="-race"
matrix:
- BUILD_DEPTYPE=gomod
# disable travis install
install:
- true
script:
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
cache:
directories:
- $GOPATH/src/gx
- $GOPATH/pkg/mod
- $HOME/.cache/go-build
notifications:
email: false

View File

@@ -1,16 +1,5 @@
all: deps all: deps
gx: deps:
go get github.com/whyrusleeping/gx
go get github.com/whyrusleeping/gx-go
covertools:
go get github.com/mattn/goveralls go get github.com/mattn/goveralls
go get golang.org/x/tools/cmd/cover go get golang.org/x/tools/cmd/cover
deps: gx covertools
gx --verbose install --global
gx-go rewrite
publish:
gx-go rewrite --undo

View File

@@ -5,7 +5,7 @@ go-cid
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![GoDoc](https://godoc.org/github.com/ipfs/go-cid?status.svg)](https://godoc.org/github.com/ipfs/go-cid) [![GoDoc](https://godoc.org/code.sonr.org/go/cid?status.svg)](https://godoc.org/code.sonr.org/go/cid)
[![Coverage Status](https://coveralls.io/repos/github/ipfs/go-cid/badge.svg?branch=master)](https://coveralls.io/github/ipfs/go-cid?branch=master) [![Coverage Status](https://coveralls.io/repos/github/ipfs/go-cid/badge.svg?branch=master)](https://coveralls.io/github/ipfs/go-cid?branch=master)
[![Travis CI](https://travis-ci.org/ipfs/go-cid.svg?branch=master)](https://travis-ci.org/ipfs/go-cid) [![Travis CI](https://travis-ci.org/ipfs/go-cid.svg?branch=master)](https://travis-ci.org/ipfs/go-cid)
@@ -31,7 +31,7 @@ It is used in `go-ipfs` and related packages to refer to a typed hunk of data.
`go-cid` is a standard Go module which can be installed with: `go-cid` is a standard Go module which can be installed with:
```sh ```sh
go get github.com/ipfs/go-cid go get code.sonr.org/go/cid
``` ```
## Usage ## Usage
@@ -59,10 +59,17 @@ fmt.Println("Got CID: ", c)
#### Creating a CID from scratch #### Creating a CID from scratch
```go ```go
import (
cid "code.sonr.org/go/cid"
mc "github.com/multiformats/go-multicodec"
mh "github.com/multiformats/go-multihash"
)
// Create a cid manually by specifying the 'prefix' parameters // Create a cid manually by specifying the 'prefix' parameters
pref := cid.Prefix{ pref := cid.Prefix{
Version: 1, Version: 1,
Codec: cid.Raw, Codec: uint64(mc.Raw),
MhType: mh.SHA2_256, MhType: mh.SHA2_256,
MhLength: -1, // default length MhLength: -1, // default length
} }

View File

@@ -39,7 +39,8 @@ type Cid interface {
// and the Multihash length. It does not contains // and the Multihash length. It does not contains
// any actual content information. // any actual content information.
// NOTE: The use -1 in MhLength to mean default length is deprecated, // NOTE: The use -1 in MhLength to mean default length is deprecated,
// use the V0Builder or V1Builder structures instead //
// use the V0Builder or V1Builder structures instead
type Prefix struct { type Prefix struct {
Version uint64 Version uint64
Codec uint64 Codec uint64

View File

@@ -12,14 +12,14 @@ import (
// //
// Sample results on linux amd64 go1.11beta: // Sample results on linux amd64 go1.11beta:
// //
// BenchmarkCidMap_CidStr-8 100000 16317 ns/op // BenchmarkCidMap_CidStr-8 100000 16317 ns/op
// BenchmarkCidMap_CidIface-8 100000 20516 ns/op // BenchmarkCidMap_CidIface-8 100000 20516 ns/op
// //
// With benchmem on: // With benchmem on:
// //
// BenchmarkCidMap_CidStr-8 100000 15579 ns/op 11223 B/op 207 allocs/op // BenchmarkCidMap_CidStr-8 100000 15579 ns/op 11223 B/op 207 allocs/op
// BenchmarkCidMap_CidIface-8 100000 19500 ns/op 12824 B/op 307 allocs/op // BenchmarkCidMap_CidIface-8 100000 19500 ns/op 12824 B/op 307 allocs/op
// BenchmarkCidMap_StrPlusHax-8 200000 10451 ns/op 7589 B/op 202 allocs/op // BenchmarkCidMap_StrPlusHax-8 200000 10451 ns/op 7589 B/op 202 allocs/op
// //
// We can see here that the impact of interface boxing is significant: // We can see here that the impact of interface boxing is significant:
// it increases the time taken to do the inserts to 133%, largely because // it increases the time taken to do the inserts to 133%, largely because
@@ -36,7 +36,6 @@ import (
// re-arranges itself, it involves more or less an O(n) copy of the content // re-arranges itself, it involves more or less an O(n) copy of the content
// in addition to the alloc itself). This isn't topical to the question of // in addition to the alloc itself). This isn't topical to the question of
// whether or not interfaces are a good idea; just for contextualizing. // whether or not interfaces are a good idea; just for contextualizing.
//
func BenchmarkCidMap_CidStr(b *testing.B) { func BenchmarkCidMap_CidStr(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
mp := map[CidStr]int{} mp := map[CidStr]int{}

View File

@@ -114,7 +114,7 @@ func NewCidStr(version uint64, codecType uint64, mhash mh.Multihash) CidStr {
// //
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered

View File

@@ -110,7 +110,7 @@ func (c CidStruct) Prefix() Prefix {
// //
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered

61
benchmark_test.go Normal file
View File

@@ -0,0 +1,61 @@
package cid_test
import (
"math/rand"
"testing"
"code.sonr.org/go/cid"
"github.com/multiformats/go-multihash"
)
// BenchmarkIdentityCheck benchmarks two ways of checking whether a CIDv1 has multihash.IDENTITY
// code:
// 1. Cid.Prefix(), and
// 2. decoding the Cid.Hash().
//
// This benchmark illustrates that using Cid.Prefix is more efficient than multihash.Decode.
// Users wishing to perform such a check should use Cid.Prefix.
//
// Consider that `Cid.Prefix` is already efficient enough and introducing a dedicated API for
// performing this check will likely result in small gains.
func BenchmarkIdentityCheck(b *testing.B) {
rng := rand.New(rand.NewSource(1413))
data := make([]byte, rng.Intn(100)+1024)
if _, err := rng.Read(data); err != nil {
b.Fatal(err)
}
mh, err := multihash.Sum(data, multihash.IDENTITY, -1)
if err != nil {
b.Fatal(err)
}
cv1 := cid.NewCidV1(cid.Raw, mh)
b.SetBytes(int64(cv1.ByteLen()))
b.ReportAllocs()
b.ResetTimer()
b.Run("Prefix", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if cv1.Prefix().MhType != multihash.IDENTITY {
b.Fatal("expected IDENTITY CID")
}
}
})
})
b.Run("MultihashDecode", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
dmh, err := multihash.Decode(cv1.Hash())
if err != nil {
b.Fatal(err)
}
if dmh.Code != multihash.IDENTITY {
b.Fatal("expected IDENTITY CID")
}
}
})
})
}

View File

@@ -65,9 +65,29 @@ func TestCodecChange(t *testing.T) {
p := Prefix{Version: 1, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: mh.DefaultLengths[mh.SHA2_256]} p := Prefix{Version: 1, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: mh.DefaultLengths[mh.SHA2_256]}
testCodecChange(t, p) testCodecChange(t, p)
}) })
t.Run("Prefix-NoChange", func(t *testing.T) {
p := Prefix{Version: 0, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: mh.DefaultLengths[mh.SHA2_256]}
if p.GetCodec() != DagProtobuf {
t.Fatal("original builder not using Protobuf codec")
}
pn := p.WithCodec(DagProtobuf)
if pn != p {
t.Fatal("should have returned same builder")
}
})
t.Run("V0Builder", func(t *testing.T) { t.Run("V0Builder", func(t *testing.T) {
testCodecChange(t, V0Builder{}) testCodecChange(t, V0Builder{})
}) })
t.Run("V0Builder-NoChange", func(t *testing.T) {
b := V0Builder{}
if b.GetCodec() != DagProtobuf {
t.Fatal("original builder not using Protobuf codec")
}
bn := b.WithCodec(DagProtobuf)
if bn != b {
t.Fatal("should have returned same builder")
}
})
t.Run("V1Builder", func(t *testing.T) { t.Run("V1Builder", func(t *testing.T) {
testCodecChange(t, V1Builder{Codec: DagProtobuf, MhType: mh.SHA2_256}) testCodecChange(t, V1Builder{Codec: DagProtobuf, MhType: mh.SHA2_256})
}) })

510
cid.go
View File

@@ -10,7 +10,7 @@
// //
// A CIDv1 has four parts: // A CIDv1 has four parts:
// //
// <cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address> // <cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address>
// //
// As shown above, the CID implementation relies heavily on Multiformats, // As shown above, the CID implementation relies heavily on Multiformats,
// particularly Multibase // particularly Multibase
@@ -26,117 +26,85 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"strings" "strings"
mbase "github.com/multiformats/go-multibase" mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash" mh "github.com/multiformats/go-multihash"
varint "github.com/multiformats/go-varint"
) )
// UnsupportedVersionString just holds an error message // UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>" const UnsupportedVersionString = "<unsupported cid version>"
// ErrInvalidCid is an error that indicates that a CID is invalid.
type ErrInvalidCid struct {
Err error
}
func (e ErrInvalidCid) Error() string {
return fmt.Sprintf("invalid cid: %s", e.Err)
}
func (e ErrInvalidCid) Unwrap() error {
return e.Err
}
func (e ErrInvalidCid) Is(err error) bool {
switch err.(type) {
case ErrInvalidCid, *ErrInvalidCid:
return true
default:
return false
}
}
var ( var (
// ErrVarintBuffSmall means that a buffer passed to the cid parser was not
// long enough, or did not contain an invalid cid
ErrVarintBuffSmall = errors.New("reading varint: buffer too small")
// ErrVarintTooBig means that the varint in the given cid was above the
// limit of 2^64
ErrVarintTooBig = errors.New("reading varint: varint bigger than 64bits" +
" and not supported")
// ErrCidTooShort means that the cid passed to decode was not long // ErrCidTooShort means that the cid passed to decode was not long
// enough to be a valid Cid // enough to be a valid Cid
ErrCidTooShort = errors.New("cid too short") ErrCidTooShort = ErrInvalidCid{errors.New("cid too short")}
// ErrInvalidEncoding means that selected encoding is not supported // ErrInvalidEncoding means that selected encoding is not supported
// by this Cid version // by this Cid version
ErrInvalidEncoding = errors.New("invalid base encoding") ErrInvalidEncoding = errors.New("invalid base encoding")
) )
// These are multicodec-packed content types. The should match // Consts below are DEPRECATED and left only for legacy reasons:
// the codes described in the authoritative document: // <https://code.sonr.org/go/cid/pull/137>
// https://github.com/multiformats/multicodec/blob/master/table.csv // Modern code should use consts from go-multicodec instead:
// <https://github.com/multiformats/go-multicodec>
const ( const (
Raw = 0x55 // common ones
Raw = 0x55
DagProtobuf = 0x70 // https://ipld.io/docs/codecs/known/dag-pb/
DagCBOR = 0x71 // https://ipld.io/docs/codecs/known/dag-cbor/
DagJSON = 0x0129 // https://ipld.io/docs/codecs/known/dag-json/
Libp2pKey = 0x72 // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
DagProtobuf = 0x70 // other
DagCBOR = 0x71 GitRaw = 0x78
Libp2pKey = 0x72 DagJOSE = 0x85 // https://ipld.io/specs/codecs/dag-jose/spec/
EthBlock = 0x90
GitRaw = 0x78 EthBlockList = 0x91
EthTxTrie = 0x92
EthBlock = 0x90 EthTx = 0x93
EthBlockList = 0x91 EthTxReceiptTrie = 0x94
EthTxTrie = 0x92 EthTxReceipt = 0x95
EthTx = 0x93 EthStateTrie = 0x96
EthTxReceiptTrie = 0x94 EthAccountSnapshot = 0x97
EthTxReceipt = 0x95 EthStorageTrie = 0x98
EthStateTrie = 0x96 BitcoinBlock = 0xb0
EthAccountSnapshot = 0x97 BitcoinTx = 0xb1
EthStorageTrie = 0x98 ZcashBlock = 0xc0
BitcoinBlock = 0xb0 ZcashTx = 0xc1
BitcoinTx = 0xb1 DecredBlock = 0xe0
ZcashBlock = 0xc0 DecredTx = 0xe1
ZcashTx = 0xc1 DashBlock = 0xf0
DecredBlock = 0xe0 DashTx = 0xf1
DecredTx = 0xe1 FilCommitmentUnsealed = 0xf101
DashBlock = 0xf0 FilCommitmentSealed = 0xf102
DashTx = 0xf1
) )
// Codecs maps the name of a codec to its type
var Codecs = map[string]uint64{
"v0": DagProtobuf,
"raw": Raw,
"protobuf": DagProtobuf,
"cbor": DagCBOR,
"libp2p-key": Libp2pKey,
"git-raw": GitRaw,
"eth-block": EthBlock,
"eth-block-list": EthBlockList,
"eth-tx-trie": EthTxTrie,
"eth-tx": EthTx,
"eth-tx-receipt-trie": EthTxReceiptTrie,
"eth-tx-receipt": EthTxReceipt,
"eth-state-trie": EthStateTrie,
"eth-account-snapshot": EthAccountSnapshot,
"eth-storage-trie": EthStorageTrie,
"bitcoin-block": BitcoinBlock,
"bitcoin-tx": BitcoinTx,
"zcash-block": ZcashBlock,
"zcash-tx": ZcashTx,
"decred-block": DecredBlock,
"decred-tx": DecredTx,
"dash-block": DashBlock,
"dash-tx": DashTx,
}
// CodecToStr maps the numeric codec to its name
var CodecToStr = map[uint64]string{
Raw: "raw",
DagProtobuf: "protobuf",
DagCBOR: "cbor",
GitRaw: "git-raw",
EthBlock: "eth-block",
EthBlockList: "eth-block-list",
EthTxTrie: "eth-tx-trie",
EthTx: "eth-tx",
EthTxReceiptTrie: "eth-tx-receipt-trie",
EthTxReceipt: "eth-tx-receipt",
EthStateTrie: "eth-state-trie",
EthAccountSnapshot: "eth-account-snapshot",
EthStorageTrie: "eth-storage-trie",
BitcoinBlock: "bitcoin-block",
BitcoinTx: "bitcoin-tx",
ZcashBlock: "zcash-block",
ZcashTx: "zcash-tx",
DecredBlock: "decred-block",
DecredTx: "decred-tx",
DashBlock: "dash-block",
DashTx: "dash-tx",
}
// tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an // tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an
// error on failure. // error on failure.
func tryNewCidV0(mhash mh.Multihash) (Cid, error) { func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
@@ -144,10 +112,10 @@ func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
// incorrectly detect it as CidV1 in the Version() method // incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash) dec, err := mh.Decode(mhash)
if err != nil { if err != nil {
return Undef, err return Undef, ErrInvalidCid{err}
} }
if dec.Code != mh.SHA2_256 || dec.Length != 32 { if dec.Code != mh.SHA2_256 || dec.Length != 32 {
return Undef, fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length) return Undef, ErrInvalidCid{fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)}
} }
return Cid{string(mhash)}, nil return Cid{string(mhash)}, nil
} }
@@ -172,22 +140,32 @@ func NewCidV0(mhash mh.Multihash) Cid {
// Panics if the multihash is invalid. // Panics if the multihash is invalid.
func NewCidV1(codecType uint64, mhash mh.Multihash) Cid { func NewCidV1(codecType uint64, mhash mh.Multihash) Cid {
hashlen := len(mhash) hashlen := len(mhash)
// two 8 bytes (max) numbers plus hash
buf := make([]byte, 2*binary.MaxVarintLen64+hashlen) // Two 8 bytes (max) numbers plus hash.
n := binary.PutUvarint(buf, 1) // We use strings.Builder to only allocate once.
n += binary.PutUvarint(buf[n:], codecType) var b strings.Builder
cn := copy(buf[n:], mhash) b.Grow(1 + varint.UvarintSize(codecType) + hashlen)
b.WriteByte(1)
var buf [binary.MaxVarintLen64]byte
n := varint.PutUvarint(buf[:], codecType)
b.Write(buf[:n])
cn, _ := b.Write(mhash)
if cn != hashlen { if cn != hashlen {
panic("copy hash length is inconsistent") panic("copy hash length is inconsistent")
} }
return Cid{string(buf[:n+hashlen])} return Cid{b.String()}
} }
var _ encoding.BinaryMarshaler = Cid{} var (
var _ encoding.BinaryUnmarshaler = (*Cid)(nil) _ encoding.BinaryMarshaler = Cid{}
var _ encoding.TextMarshaler = Cid{} _ encoding.BinaryUnmarshaler = (*Cid)(nil)
var _ encoding.TextUnmarshaler = (*Cid)(nil) _ encoding.TextMarshaler = Cid{}
_ encoding.TextUnmarshaler = (*Cid)(nil)
)
// Cid represents a self-describing content addressed // Cid represents a self-describing content addressed
// identifier. It is formed by a Version, a Codec (which indicates // identifier. It is formed by a Version, a Codec (which indicates
@@ -221,14 +199,23 @@ func Parse(v interface{}) (Cid, error) {
case Cid: case Cid:
return v2, nil return v2, nil
default: default:
return Undef, fmt.Errorf("can't parse %+v as Cid", v2) return Undef, ErrInvalidCid{fmt.Errorf("can't parse %+v as Cid", v2)}
} }
} }
// MustParse calls Parse but will panic on error.
func MustParse(v interface{}) Cid {
c, err := Parse(v)
if err != nil {
panic(err)
}
return c
}
// Decode parses a Cid-encoded string and returns a Cid object. // Decode parses a Cid-encoded string and returns a Cid object.
// For CidV1, a Cid-encoded string is primarily a multibase string: // For CidV1, a Cid-encoded string is primarily a multibase string:
// //
// <multibase-type-code><base-encoded-string> // <multibase-type-code><base-encoded-string>
// //
// The base-encoded string represents a: // The base-encoded string represents a:
// //
@@ -245,7 +232,7 @@ func Decode(v string) (Cid, error) {
if len(v) == 46 && v[:2] == "Qm" { if len(v) == 46 && v[:2] == "Qm" {
hash, err := mh.FromB58String(v) hash, err := mh.FromB58String(v)
if err != nil { if err != nil {
return Undef, err return Undef, ErrInvalidCid{err}
} }
return tryNewCidV0(hash) return tryNewCidV0(hash)
@@ -253,7 +240,7 @@ func Decode(v string) (Cid, error) {
_, data, err := mbase.Decode(v) _, data, err := mbase.Decode(v)
if err != nil { if err != nil {
return Undef, err return Undef, ErrInvalidCid{err}
} }
return Cast(data) return Cast(data)
@@ -275,27 +262,16 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
// check encoding is valid // check encoding is valid
_, err := mbase.NewEncoder(encoding) _, err := mbase.NewEncoder(encoding)
if err != nil { if err != nil {
return -1, err return -1, ErrInvalidCid{err}
} }
return encoding, nil return encoding, nil
} }
func uvError(read int) error {
switch {
case read == 0:
return ErrVarintBuffSmall
case read < 0:
return ErrVarintTooBig
default:
return nil
}
}
// Cast takes a Cid data slice, parses it and returns a Cid. // Cast takes a Cid data slice, parses it and returns a Cid.
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered
@@ -306,11 +282,11 @@ func uvError(read int) error {
func Cast(data []byte) (Cid, error) { func Cast(data []byte) (Cid, error) {
nr, c, err := CidFromBytes(data) nr, c, err := CidFromBytes(data)
if err != nil { if err != nil {
return Undef, err return Undef, ErrInvalidCid{err}
} }
if nr != len(data) { if nr != len(data) {
return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast") return Undef, ErrInvalidCid{fmt.Errorf("trailing bytes in data buffer passed to cid Cast")}
} }
return c, nil return c, nil
@@ -351,14 +327,14 @@ func (c Cid) Type() uint64 {
if c.Version() == 0 { if c.Version() == 0 {
return DagProtobuf return DagProtobuf
} }
_, n := uvarint(c.str) _, n, _ := uvarint(c.str)
codec, _ := uvarint(c.str[n:]) codec, _, _ := uvarint(c.str[n:])
return codec return codec
} }
// String returns the default string representation of a // String returns the default string representation of a
// Cid. Currently, Base58 is used as the encoding for the // Cid. Currently, Base32 is used for CIDV1 as the encoding for the
// multibase string. // multibase string, Base58 is used for CIDV0.
func (c Cid) String() string { func (c Cid) String() string {
switch c.Version() { switch c.Version() {
case 0: case 0:
@@ -414,9 +390,9 @@ func (c Cid) Hash() mh.Multihash {
} }
// skip version length // skip version length
_, n1 := binary.Uvarint(bytes) _, n1, _ := varint.FromUvarint(bytes)
// skip codec length // skip codec length
_, n2 := binary.Uvarint(bytes[n1:]) _, n2, _ := varint.FromUvarint(bytes[n1:])
return mh.Multihash(bytes[n1+n2:]) return mh.Multihash(bytes[n1+n2:])
} }
@@ -424,10 +400,40 @@ func (c Cid) Hash() mh.Multihash {
// Bytes returns the byte representation of a Cid. // Bytes returns the byte representation of a Cid.
// The output of bytes can be parsed back into a Cid // The output of bytes can be parsed back into a Cid
// with Cast(). // with Cast().
//
// If c.Defined() == false, it return a nil slice and may not
// be parsable with Cast().
func (c Cid) Bytes() []byte { func (c Cid) Bytes() []byte {
if !c.Defined() {
return nil
}
return []byte(c.str) return []byte(c.str)
} }
// ByteLen returns the length of the CID in bytes.
// It's equivalent to `len(c.Bytes())`, but works without an allocation,
// and should therefore be preferred.
//
// (See also the WriteTo method for other important operations that work without allocation.)
func (c Cid) ByteLen() int {
return len(c.str)
}
// WriteBytes writes the CID bytes to the given writer.
// This method works without incurring any allocation.
//
// (See also the ByteLen method for other important operations that work without allocation.)
func (c Cid) WriteBytes(w io.Writer) (int, error) {
n, err := io.WriteString(w, c.str)
if err != nil {
return n, err
}
if n != len(c.str) {
return n, fmt.Errorf("failed to write entire cid string")
}
return n, nil
}
// MarshalBinary is equivalent to Bytes(). It implements the // MarshalBinary is equivalent to Bytes(). It implements the
// encoding.BinaryMarshaler interface. // encoding.BinaryMarshaler interface.
func (c Cid) MarshalBinary() ([]byte, error) { func (c Cid) MarshalBinary() ([]byte, error) {
@@ -450,7 +456,7 @@ func (c Cid) Equals(o Cid) bool {
// UnmarshalJSON parses the JSON representation of a Cid. // UnmarshalJSON parses the JSON representation of a Cid.
func (c *Cid) UnmarshalJSON(b []byte) error { func (c *Cid) UnmarshalJSON(b []byte) error {
if len(b) < 2 { if len(b) < 2 {
return fmt.Errorf("invalid cid json blob") return ErrInvalidCid{fmt.Errorf("invalid cid json blob")}
} }
obj := struct { obj := struct {
CidTarget string `json:"/"` CidTarget string `json:"/"`
@@ -458,7 +464,7 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
objptr := &obj objptr := &obj
err := json.Unmarshal(b, &objptr) err := json.Unmarshal(b, &objptr)
if err != nil { if err != nil {
return err return ErrInvalidCid{err}
} }
if objptr == nil { if objptr == nil {
*c = Cid{} *c = Cid{}
@@ -466,12 +472,12 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
} }
if obj.CidTarget == "" { if obj.CidTarget == "" {
return fmt.Errorf("cid was incorrectly formatted") return ErrInvalidCid{fmt.Errorf("cid was incorrectly formatted")}
} }
out, err := Decode(obj.CidTarget) out, err := Decode(obj.CidTarget)
if err != nil { if err != nil {
return err return ErrInvalidCid{err}
} }
*c = out *c = out
@@ -481,7 +487,7 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
// MarshalJSON procudes a JSON representation of a Cid, which looks as follows: // MarshalJSON procudes a JSON representation of a Cid, which looks as follows:
// //
// { "/": "<cid-string>" } // { "/": "<cid-string>" }
// //
// Note that this formatting comes from the IPLD specification // Note that this formatting comes from the IPLD specification
// (https://github.com/ipld/specs/tree/master/ipld) // (https://github.com/ipld/specs/tree/master/ipld)
@@ -507,12 +513,29 @@ func (c Cid) Loggable() map[string]interface{} {
// Prefix builds and returns a Prefix out of a Cid. // Prefix builds and returns a Prefix out of a Cid.
func (c Cid) Prefix() Prefix { func (c Cid) Prefix() Prefix {
dec, _ := mh.Decode(c.Hash()) // assuming we got a valid multiaddr, this will not error if c.Version() == 0 {
return Prefix{
MhType: mh.SHA2_256,
MhLength: 32,
Version: 0,
Codec: DagProtobuf,
}
}
offset := 0
version, n, _ := uvarint(c.str[offset:])
offset += n
codec, n, _ := uvarint(c.str[offset:])
offset += n
mhtype, n, _ := uvarint(c.str[offset:])
offset += n
mhlen, _, _ := uvarint(c.str[offset:])
return Prefix{ return Prefix{
MhType: dec.Code, MhType: mhtype,
MhLength: dec.Length, MhLength: int(mhlen),
Version: c.Version(), Version: version,
Codec: c.Type(), Codec: codec,
} }
} }
@@ -521,7 +544,8 @@ func (c Cid) Prefix() Prefix {
// and the Multihash length. It does not contains // and the Multihash length. It does not contains
// any actual content information. // any actual content information.
// NOTE: The use -1 in MhLength to mean default length is deprecated, // NOTE: The use -1 in MhLength to mean default length is deprecated,
// use the V0Builder or V1Builder structures instead //
// use the V0Builder or V1Builder structures instead
type Prefix struct { type Prefix struct {
Version uint64 Version uint64
Codec uint64 Codec uint64
@@ -533,19 +557,19 @@ type Prefix struct {
// and return a newly constructed Cid with the resulting multihash. // and return a newly constructed Cid with the resulting multihash.
func (p Prefix) Sum(data []byte) (Cid, error) { func (p Prefix) Sum(data []byte) (Cid, error) {
length := p.MhLength length := p.MhLength
if p.MhType == mh.ID { if p.MhType == mh.IDENTITY {
length = -1 length = -1
} }
if p.Version == 0 && (p.MhType != mh.SHA2_256 || if p.Version == 0 && (p.MhType != mh.SHA2_256 ||
(p.MhLength != 32 && p.MhLength != -1)) { (p.MhLength != 32 && p.MhLength != -1)) {
return Undef, fmt.Errorf("invalid v0 prefix") return Undef, ErrInvalidCid{fmt.Errorf("invalid v0 prefix")}
} }
hash, err := mh.Sum(data, p.MhType, length) hash, err := mh.Sum(data, p.MhType, length)
if err != nil { if err != nil {
return Undef, err return Undef, ErrInvalidCid{err}
} }
switch p.Version { switch p.Version {
@@ -554,44 +578,52 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
case 1: case 1:
return NewCidV1(p.Codec, hash), nil return NewCidV1(p.Codec, hash), nil
default: default:
return Undef, fmt.Errorf("invalid cid version") return Undef, ErrInvalidCid{fmt.Errorf("invalid cid version")}
} }
} }
// Bytes returns a byte representation of a Prefix. It looks like: // Bytes returns a byte representation of a Prefix. It looks like:
// //
// <version><codec><mh-type><mh-length> // <version><codec><mh-type><mh-length>
func (p Prefix) Bytes() []byte { func (p Prefix) Bytes() []byte {
buf := make([]byte, 4*binary.MaxVarintLen64) size := varint.UvarintSize(p.Version)
n := binary.PutUvarint(buf, p.Version) size += varint.UvarintSize(p.Codec)
n += binary.PutUvarint(buf[n:], p.Codec) size += varint.UvarintSize(p.MhType)
n += binary.PutUvarint(buf[n:], uint64(p.MhType)) size += varint.UvarintSize(uint64(p.MhLength))
n += binary.PutUvarint(buf[n:], uint64(p.MhLength))
return buf[:n] buf := make([]byte, size)
n := varint.PutUvarint(buf, p.Version)
n += varint.PutUvarint(buf[n:], p.Codec)
n += varint.PutUvarint(buf[n:], p.MhType)
n += varint.PutUvarint(buf[n:], uint64(p.MhLength))
if n != size {
panic("size mismatch")
}
return buf
} }
// PrefixFromBytes parses a Prefix-byte representation onto a // PrefixFromBytes parses a Prefix-byte representation onto a
// Prefix. // Prefix.
func PrefixFromBytes(buf []byte) (Prefix, error) { func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
vers, err := binary.ReadUvarint(r) vers, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
codec, err := binary.ReadUvarint(r) codec, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
mhtype, err := binary.ReadUvarint(r) mhtype, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
mhlen, err := binary.ReadUvarint(r) mhlen, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
return Prefix{ return Prefix{
@@ -605,37 +637,181 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
func CidFromBytes(data []byte) (int, Cid, error) { func CidFromBytes(data []byte) (int, Cid, error) {
if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 { if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 {
if len(data) < 34 { if len(data) < 34 {
return 0, Undef, fmt.Errorf("not enough bytes for cid v0") return 0, Undef, ErrInvalidCid{fmt.Errorf("not enough bytes for cid v0")}
} }
h, err := mh.Cast(data[:34]) h, err := mh.Cast(data[:34])
if err != nil { if err != nil {
return 0, Undef, err return 0, Undef, ErrInvalidCid{err}
} }
return 34, Cid{string(h)}, nil return 34, Cid{string(h)}, nil
} }
vers, n := binary.Uvarint(data) vers, n, err := varint.FromUvarint(data)
if err := uvError(n); err != nil { if err != nil {
return 0, Undef, err return 0, Undef, ErrInvalidCid{err}
} }
if vers != 1 { if vers != 1 {
return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers) return 0, Undef, ErrInvalidCid{fmt.Errorf("expected 1 as the cid version number, got: %d", vers)}
} }
_, cn := binary.Uvarint(data[n:]) _, cn, err := varint.FromUvarint(data[n:])
if err := uvError(cn); err != nil { if err != nil {
return 0, Undef, err return 0, Undef, ErrInvalidCid{err}
} }
mhnr, _, err := mh.MHFromBytes(data[n+cn:]) mhnr, _, err := mh.MHFromBytes(data[n+cn:])
if err != nil { if err != nil {
return 0, Undef, err return 0, Undef, ErrInvalidCid{err}
} }
l := n + cn + mhnr l := n + cn + mhnr
return l, Cid{string(data[0:l])}, nil return l, Cid{string(data[0:l])}, nil
} }
func toBufByteReader(r io.Reader, dst []byte) *bufByteReader {
// If the reader already implements ByteReader, use it directly.
// Otherwise, use a fallback that does 1-byte Reads.
if br, ok := r.(io.ByteReader); ok {
return &bufByteReader{direct: br, dst: dst}
}
return &bufByteReader{fallback: r, dst: dst}
}
type bufByteReader struct {
direct io.ByteReader
fallback io.Reader
dst []byte
}
func (r *bufByteReader) ReadByte() (byte, error) {
// The underlying reader has ReadByte; use it.
if br := r.direct; br != nil {
b, err := br.ReadByte()
if err != nil {
return 0, err
}
r.dst = append(r.dst, b)
return b, nil
}
// Fall back to a one-byte Read.
// TODO: consider reading straight into dst,
// once we have benchmarks and if they prove that to be faster.
var p [1]byte
if _, err := io.ReadFull(r.fallback, p[:]); err != nil {
return 0, err
}
r.dst = append(r.dst, p[0])
return p[0], nil
}
// CidFromReader reads a precise number of bytes for a CID from a given reader.
// It returns the number of bytes read, the CID, and any error encountered.
// The number of bytes read is accurate even if a non-nil error is returned.
//
// It's recommended to supply a reader that buffers and implements io.ByteReader,
// as CidFromReader has to do many single-byte reads to decode varints.
// If the argument only implements io.Reader, single-byte Read calls are used instead.
//
// If the Reader is found to yield zero bytes, an io.EOF error is returned directly, in all
// other error cases, an ErrInvalidCid, wrapping the original error, is returned.
func CidFromReader(r io.Reader) (int, Cid, error) {
// 64 bytes is enough for any CIDv0,
// and it's enough for most CIDv1s in practice.
// If the digest is too long, we'll allocate more.
br := toBufByteReader(r, make([]byte, 0, 64))
// We read the first varint, to tell if this is a CIDv0 or a CIDv1.
// The varint package wants a io.ByteReader, so we must wrap our io.Reader.
vers, err := varint.ReadUvarint(br)
if err != nil {
if err == io.EOF {
// First-byte read in ReadUvarint errors with io.EOF, so reader has no data.
// Subsequent reads with an EOF will return io.ErrUnexpectedEOF and be wrapped here.
return 0, Undef, err
}
return len(br.dst), Undef, ErrInvalidCid{err}
}
// If we have a CIDv0, read the rest of the bytes and cast the buffer.
if vers == mh.SHA2_256 {
if n, err := io.ReadFull(r, br.dst[1:34]); err != nil {
return len(br.dst) + n, Undef, ErrInvalidCid{err}
}
br.dst = br.dst[:34]
h, err := mh.Cast(br.dst)
if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err}
}
return len(br.dst), Cid{string(h)}, nil
}
if vers != 1 {
return len(br.dst), Undef, ErrInvalidCid{fmt.Errorf("expected 1 as the cid version number, got: %d", vers)}
}
// CID block encoding multicodec.
_, err = varint.ReadUvarint(br)
if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err}
}
// We could replace most of the code below with go-multihash's ReadMultihash.
// Note that it would save code, but prevent reusing buffers.
// Plus, we already have a ByteReader now.
mhStart := len(br.dst)
// Multihash hash function code.
_, err = varint.ReadUvarint(br)
if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err}
}
// Multihash digest length.
mhl, err := varint.ReadUvarint(br)
if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err}
}
// Refuse to make large allocations to prevent OOMs due to bugs.
const maxDigestAlloc = 32 << 20 // 32MiB
if mhl > maxDigestAlloc {
return len(br.dst), Undef, ErrInvalidCid{fmt.Errorf("refusing to allocate %d bytes for a digest", mhl)}
}
// Fine to convert mhl to int, given maxDigestAlloc.
prefixLength := len(br.dst)
cidLength := prefixLength + int(mhl)
if cidLength > cap(br.dst) {
// If the multihash digest doesn't fit in our initial 64 bytes,
// efficiently extend the slice via append+make.
br.dst = append(br.dst, make([]byte, cidLength-len(br.dst))...)
} else {
// The multihash digest fits inside our buffer,
// so just extend its capacity.
br.dst = br.dst[:cidLength]
}
if n, err := io.ReadFull(r, br.dst[prefixLength:cidLength]); err != nil {
// We can't use len(br.dst) here,
// as we've only read n bytes past prefixLength.
return prefixLength + n, Undef, ErrInvalidCid{err}
}
// This simply ensures the multihash is valid.
// TODO: consider removing this bit, as it's probably redundant;
// for now, it helps ensure consistency with CidFromBytes.
_, _, err = mh.MHFromBytes(br.dst[mhStart:])
if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err}
}
return len(br.dst), Cid{string(br.dst)}, nil
}

View File

@@ -1,10 +1,9 @@
// +build gofuzz //go:build gofuzz
package cid package cid
func Fuzz(data []byte) int { func Fuzz(data []byte) int {
cid, err := Cast(data) cid, err := Cast(data)
if err != nil { if err != nil {
return 0 return 0
} }

View File

@@ -2,44 +2,21 @@ package cid
import ( import (
"bytes" "bytes"
crand "crypto/rand"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io"
"math/rand" "math/rand"
"reflect"
"strings" "strings"
"testing" "testing"
"testing/iotest"
mbase "github.com/multiformats/go-multibase" mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash" mh "github.com/multiformats/go-multihash"
) )
// Copying the "silly test" idea from
// https://github.com/multiformats/go-multihash/blob/7aa9f26a231c6f34f4e9fad52bf580fd36627285/multihash_test.go#L13
// Makes it so changing the table accidentally has to happen twice.
var tCodecs = map[uint64]string{
Raw: "raw",
DagProtobuf: "protobuf",
DagCBOR: "cbor",
Libp2pKey: "libp2p-key",
GitRaw: "git-raw",
EthBlock: "eth-block",
EthBlockList: "eth-block-list",
EthTxTrie: "eth-tx-trie",
EthTx: "eth-tx",
EthTxReceiptTrie: "eth-tx-receipt-trie",
EthTxReceipt: "eth-tx-receipt",
EthStateTrie: "eth-state-trie",
EthAccountSnapshot: "eth-account-snapshot",
EthStorageTrie: "eth-storage-trie",
BitcoinBlock: "bitcoin-block",
BitcoinTx: "bitcoin-tx",
ZcashBlock: "zcash-block",
ZcashTx: "zcash-tx",
DecredBlock: "decred-block",
DecredTx: "decred-tx",
DashBlock: "dash-block",
DashTx: "dash-tx",
}
func assertEqual(t *testing.T, a, b Cid) { func assertEqual(t *testing.T, a, b Cid) {
if a.Type() != b.Type() { if a.Type() != b.Type() {
t.Fatal("mismatch on type") t.Fatal("mismatch on type")
@@ -54,31 +31,11 @@ func assertEqual(t *testing.T, a, b Cid) {
} }
} }
func TestTable(t *testing.T) {
if len(tCodecs) != len(Codecs)-1 {
t.Errorf("Item count mismatch in the Table of Codec. Should be %d, got %d", len(tCodecs)+1, len(Codecs))
}
for k, v := range tCodecs {
if Codecs[v] != k {
t.Errorf("Table mismatch: 0x%x %s", k, v)
}
}
}
// The table returns cid.DagProtobuf for "v0"
// so we test it apart
func TestTableForV0(t *testing.T) {
if Codecs["v0"] != DagProtobuf {
t.Error("Table mismatch: Codecs[\"v0\"] should resolve to DagProtobuf (0x70)")
}
}
func TestPrefixSum(t *testing.T) { func TestPrefixSum(t *testing.T) {
// Test creating CIDs both manually and with Prefix. // Test creating CIDs both manually and with Prefix.
// Tests: https://github.com/ipfs/go-cid/issues/83 // Tests: https://code.sonr.org/go/cid/issues/83
for _, hashfun := range []uint64{ for _, hashfun := range []uint64{
mh.ID, mh.SHA3, mh.SHA2_256, mh.IDENTITY, mh.SHA3, mh.SHA2_256,
} { } {
h1, err := mh.Sum([]byte("TEST"), hashfun, -1) h1, err := mh.Sum([]byte("TEST"), hashfun, -1)
if err != nil { if err != nil {
@@ -182,8 +139,35 @@ func TestBasesMarshaling(t *testing.T) {
} }
s2 := cid.Encode(encoder) s2 := cid.Encode(encoder)
if s != s2 { if s != s2 {
t.Fatalf("'%s' != '%s'", s, s2) t.Fatalf("%q != %q", s, s2)
} }
ee, err := ExtractEncoding(s)
if err != nil {
t.Fatal(err)
}
if ee != b {
t.Fatalf("could not properly determine base (got %v)", ee)
}
}
ee, err := ExtractEncoding("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n")
if err != nil {
t.Fatal(err)
}
if ee != mbase.Base58BTC {
t.Fatalf("expected Base58BTC from Qm string (got %v)", ee)
}
ee, err = ExtractEncoding("1")
if err == nil {
t.Fatal("expected too-short error from ExtractEncoding")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
if ee != -1 {
t.Fatal("expected -1 from too-short ExtractEncoding")
} }
} }
@@ -192,18 +176,32 @@ func TestBinaryMarshaling(t *testing.T) {
hash, _ := mh.Sum(data, mh.SHA2_256, -1) hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash) c := NewCidV1(DagCBOR, hash)
var c2 Cid var c2 Cid
var c3 Cid
data, err := c.MarshalBinary() data, err := c.MarshalBinary()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = c2.UnmarshalBinary(data) if err = c2.UnmarshalBinary(data); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !c.Equals(c2) { if !c.Equals(c2) {
t.Errorf("cids should be the same: %s %s", c, c2) t.Errorf("cids should be the same: %s %s", c, c2)
} }
var buf bytes.Buffer
wrote, err := c.WriteBytes(&buf)
if err != nil {
t.Fatal(err)
}
if wrote != 36 {
t.Fatalf("expected 36 bytes written (got %d)", wrote)
}
if err = c3.UnmarshalBinary(data); err != nil {
t.Fatal(err)
}
if !c.Equals(c3) {
t.Errorf("cids should be the same: %s %s", c, c3)
}
} }
func TestTextMarshaling(t *testing.T) { func TestTextMarshaling(t *testing.T) {
@@ -216,13 +214,16 @@ func TestTextMarshaling(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = c2.UnmarshalText(data) if err = c2.UnmarshalText(data); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !c.Equals(c2) { if !c.Equals(c2) {
t.Errorf("cids should be the same: %s %s", c, c2) t.Errorf("cids should be the same: %s %s", c, c2)
} }
if c.KeyString() != string(c.Bytes()) {
t.Errorf("got unexpected KeyString() result")
}
} }
func TestEmptyString(t *testing.T) { func TestEmptyString(t *testing.T) {
@@ -230,6 +231,9 @@ func TestEmptyString(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("shouldnt be able to parse an empty cid") t.Fatal("shouldnt be able to parse an empty cid")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
} }
func TestV0Handling(t *testing.T) { func TestV0Handling(t *testing.T) {
@@ -252,6 +256,11 @@ func TestV0Handling(t *testing.T) {
t.Fatal("marshaling roundtrip failed") t.Fatal("marshaling roundtrip failed")
} }
byteLen := cid.ByteLen()
if byteLen != 34 {
t.Fatalf("expected V0 ByteLen to be 34 (got %d)", byteLen)
}
new, err := cid.StringOfBase(mbase.Base58BTC) new, err := cid.StringOfBase(mbase.Base58BTC)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -267,6 +276,11 @@ func TestV0Handling(t *testing.T) {
if cid.Encode(encoder) != old { if cid.Encode(encoder) != old {
t.Fatal("Encode roundtrip failed") t.Fatal("Encode roundtrip failed")
} }
_, err = cid.StringOfBase(mbase.Base32)
if err != ErrInvalidEncoding {
t.Fatalf("expected ErrInvalidEncoding for V0 StringOfBase(Base32) (got %v)", err)
}
} }
func TestV0ErrorCases(t *testing.T) { func TestV0ErrorCases(t *testing.T) {
@@ -275,6 +289,9 @@ func TestV0ErrorCases(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("should have failed to decode that ref") t.Fatal("should have failed to decode that ref")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
} }
func TestNewPrefixV1(t *testing.T) { func TestNewPrefixV1(t *testing.T) {
@@ -333,7 +350,6 @@ func TestNewPrefixV0(t *testing.T) {
if c1.Prefix() != c2.Prefix() { if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch") t.Fatal("prefixes mismatch")
} }
} }
func TestInvalidV0Prefix(t *testing.T) { func TestInvalidV0Prefix(t *testing.T) {
@@ -366,8 +382,21 @@ func TestInvalidV0Prefix(t *testing.T) {
if err == nil { if err == nil {
t.Fatalf("should error (index %d)", i) t.Fatalf("should error (index %d)", i)
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
} }
}
func TestBadPrefix(t *testing.T) {
p := Prefix{Version: 3, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: 3}
_, err := p.Sum([]byte{0x00, 0x01, 0x03})
if err == nil {
t.Fatalf("expected error on v3 prefix Sum")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
} }
func TestPrefixRoundtrip(t *testing.T) { func TestPrefixRoundtrip(t *testing.T) {
@@ -399,6 +428,37 @@ func TestPrefixRoundtrip(t *testing.T) {
} }
} }
func TestBadPrefixFromBytes(t *testing.T) {
_, err := PrefixFromBytes([]byte{0x80})
if err == nil {
t.Fatal("expected error for bad byte 0")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 1")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 2")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 3")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
}
func Test16BytesVarint(t *testing.T) { func Test16BytesVarint(t *testing.T) {
data := []byte("this is some test content") data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1) hash, _ := mh.Sum(data, mh.SHA2_256, -1)
@@ -410,7 +470,7 @@ func TestFuzzCid(t *testing.T) {
buf := make([]byte, 128) buf := make([]byte, 128)
for i := 0; i < 200; i++ { for i := 0; i < 200; i++ {
s := rand.Intn(128) s := rand.Intn(128)
rand.Read(buf[:s]) crand.Read(buf[:s])
_, _ = Cast(buf[:s]) _, _ = Cast(buf[:s])
} }
} }
@@ -423,6 +483,9 @@ func TestParse(t *testing.T) {
if !strings.Contains(err.Error(), "can't parse 123 as Cid") { if !strings.Contains(err.Error(), "can't parse 123 as Cid") {
t.Fatalf("expected int error, got %s", err.Error()) t.Fatalf("expected int error, got %s", err.Error())
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatalf("expected ErrInvalidCid, got %s", err.Error())
}
theHash := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" theHash := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
h, err := mh.FromB58String(theHash) h, err := mh.FromB58String(theHash)
@@ -431,13 +494,13 @@ func TestParse(t *testing.T) {
} }
assertions := [][]interface{}{ assertions := [][]interface{}{
[]interface{}{NewCidV0(h), theHash}, {NewCidV0(h), theHash},
[]interface{}{NewCidV0(h).Bytes(), theHash}, {NewCidV0(h).Bytes(), theHash},
[]interface{}{h, theHash}, {h, theHash},
[]interface{}{theHash, theHash}, {theHash, theHash},
[]interface{}{"/ipfs/" + theHash, theHash}, {"/ipfs/" + theHash, theHash},
[]interface{}{"https://ipfs.io/ipfs/" + theHash, theHash}, {"https://ipfs.io/ipfs/" + theHash, theHash},
[]interface{}{"http://localhost:8080/ipfs/" + theHash, theHash}, {"http://localhost:8080/ipfs/" + theHash, theHash},
} }
assert := func(arg interface{}, expected string) error { assert := func(arg interface{}, expected string) error {
@@ -446,7 +509,7 @@ func TestParse(t *testing.T) {
return err return err
} }
if cid.Version() != 0 { if cid.Version() != 0 {
return fmt.Errorf("expected version 0, got %s", string(cid.Version())) return fmt.Errorf("expected version 0, got %d", cid.Version())
} }
actual := cid.Hash().B58String() actual := cid.Hash().B58String()
if actual != expected { if actual != expected {
@@ -460,8 +523,7 @@ func TestParse(t *testing.T) {
} }
for _, args := range assertions { for _, args := range assertions {
err := assert(args[0], args[1].(string)) if err := assert(args[0], args[1].(string)); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@@ -495,8 +557,7 @@ func TestFromJson(t *testing.T) {
cval := "bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm" cval := "bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm"
jsoncid := []byte(`{"/":"` + cval + `"}`) jsoncid := []byte(`{"/":"` + cval + `"}`)
var c Cid var c Cid
err := json.Unmarshal(jsoncid, &c) if err := json.Unmarshal(jsoncid, &c); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -506,6 +567,7 @@ func TestFromJson(t *testing.T) {
} }
func TestJsonRoundTrip(t *testing.T) { func TestJsonRoundTrip(t *testing.T) {
expectedJSON := `{"/":"bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm"}`
exp, err := Decode("bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm") exp, err := Decode("bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -517,21 +579,62 @@ func TestJsonRoundTrip(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
var actual Cid var actual Cid
err = json.Unmarshal(enc, &actual) if err = json.Unmarshal(enc, &actual); err != nil {
t.Fatal(err)
}
if !exp.Equals(actual) { if !exp.Equals(actual) {
t.Fatal("cids not equal for *Cid") t.Fatal("cids not equal for *Cid")
} }
if string(enc) != expectedJSON {
t.Fatalf("did not get expected JSON form (got %q)", string(enc))
}
// Verify it works for a Cid. // Verify it works for a Cid.
enc, err = json.Marshal(exp) enc, err = json.Marshal(exp)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var actual2 Cid var actual2 Cid
err = json.Unmarshal(enc, &actual2) if err = json.Unmarshal(enc, &actual2); err != nil {
t.Fatal(err)
}
if !exp.Equals(actual2) { if !exp.Equals(actual2) {
t.Fatal("cids not equal for Cid") t.Fatal("cids not equal for Cid")
} }
err = actual2.UnmarshalJSON([]byte("1"))
if err == nil {
t.Fatal("expected error for too-short JSON")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`))
if err == nil {
t.Fatal("expected error for bad CID JSON")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
err = actual2.UnmarshalJSON([]byte(`bad "" json!`))
if err == nil {
t.Fatal("expected error for bad JSON")
}
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
var actual3 Cid
enc, err = actual3.MarshalJSON()
if err != nil {
t.Fatal(err)
}
if string(enc) != "null" {
t.Fatalf("expected 'null' string for undefined CID (got %q)", string(enc))
}
} }
func BenchmarkStringV1(b *testing.B) { func BenchmarkStringV1(b *testing.B) {
@@ -554,8 +657,10 @@ func BenchmarkStringV1(b *testing.B) {
func TestReadCidsFromBuffer(t *testing.T) { func TestReadCidsFromBuffer(t *testing.T) {
cidstr := []string{ cidstr := []string{
"bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm", "bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm",
"k2cwueckqkibutvhkr4p2ln2pjcaxaakpd9db0e7j7ax1lxhhxy3ekpv",
"Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893", "Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893",
"zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz", "zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz",
"bafkqarjpmzuwyzltorxxezjpkvcfgqkfjfbfcvslivje2vchkzdu6rckjjcfgtkolaze6mssjqzeyn2ekrcfatkjku2vowseky3fswkfkm2deqkrju3e2",
} }
var cids []Cid var cids []Cid
@@ -583,6 +688,123 @@ func TestReadCidsFromBuffer(t *testing.T) {
if cur != len(buf) { if cur != len(buf) {
t.Fatal("had trailing bytes") t.Fatal("had trailing bytes")
} }
// The same, but now with CidFromReader.
// In multiple forms, to catch more io interface bugs.
for _, r := range []io.Reader{
// implements io.ByteReader
bytes.NewReader(buf),
// tiny reads, no io.ByteReader
iotest.OneByteReader(bytes.NewReader(buf)),
} {
cur = 0
for _, expc := range cids {
n, c, err := CidFromReader(r)
if err != nil {
t.Fatal(err)
}
if c != expc {
t.Fatal("cids mismatched")
}
cur += n
}
if cur != len(buf) {
t.Fatal("had trailing bytes")
}
}
}
func TestBadCidInput(t *testing.T) {
for _, name := range []string{
"FromBytes",
"FromReader",
} {
t.Run(name, func(t *testing.T) {
usingReader := name == "FromReader"
fromBytes := CidFromBytes
if usingReader {
fromBytes = func(data []byte) (int, Cid, error) {
return CidFromReader(bytes.NewReader(data))
}
}
l, c, err := fromBytes([]byte{mh.SHA2_256, 32, 0x00})
if err == nil {
t.Fatal("expected not-enough-bytes for V0 CID")
}
if !usingReader && l != 0 {
t.Fatal("expected length==0 from bad CID")
} else if usingReader && l == 0 {
t.Fatal("expected length!=0 from bad CID")
}
if c != Undef {
t.Fatal("expected Undef CID from bad CID")
}
c, err = Decode("bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm")
if err != nil {
t.Fatal(err)
}
byts := make([]byte, c.ByteLen())
copy(byts, c.Bytes())
byts[1] = 0x80 // bad codec varint
byts[2] = 0x00
l, c, err = fromBytes(byts)
if err == nil {
t.Fatal("expected not-enough-bytes for V1 CID")
}
if !usingReader && l != 0 {
t.Fatal("expected length==0 from bad CID")
} else if usingReader && l == 0 {
t.Fatal("expected length!=0 from bad CID")
}
if c != Undef {
t.Fatal("expected Undef CID from bad CID")
}
copy(byts, c.Bytes())
byts[2] = 0x80 // bad multihash varint
byts[3] = 0x00
l, c, err = fromBytes(byts)
if err == nil {
t.Fatal("expected not-enough-bytes for V1 CID")
}
if !usingReader && l != 0 {
t.Fatal("expected length==0 from bad CID")
} else if usingReader && l == 0 {
t.Fatal("expected length!=0 from bad CID")
}
if c != Undef {
t.Fatal("expected Undef CID from bad CidFromBytes")
}
})
}
}
func TestFromReaderNoData(t *testing.T) {
// Reading no data from io.Reader should return io.EOF, not ErrInvalidCid.
n, cid, err := CidFromReader(bytes.NewReader(nil))
if err != io.EOF {
t.Fatal("Expected io.EOF error")
}
if cid != Undef {
t.Fatal("Expected Undef CID")
}
if n != 0 {
t.Fatal("Expected 0 data")
}
// Read byte indicatiing more data to and check error is ErrInvalidCid.
_, _, err = CidFromReader(bytes.NewReader([]byte{0x80}))
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("Expected ErrInvalidCid error")
}
// Check for expected wrapped error.
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatal("Expected error", io.ErrUnexpectedEOF)
}
} }
func TestBadParse(t *testing.T) { func TestBadParse(t *testing.T) {
@@ -594,4 +816,97 @@ func TestBadParse(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected to fail to parse an invalid CIDv1 CID") t.Fatal("expected to fail to parse an invalid CIDv1 CID")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
}
func TestLoggable(t *testing.T) {
c, err := Decode("bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm")
if err != nil {
t.Fatal(err)
}
actual := c.Loggable()
expected := make(map[string]interface{})
expected["cid"] = c
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("did not get expected loggable form (got %v)", actual)
}
}
func TestErrInvalidCidIs(t *testing.T) {
for i, test := range []struct {
err error
target error
}{
{&ErrInvalidCid{}, ErrInvalidCid{}},
{ErrInvalidCid{}, &ErrInvalidCid{}},
{ErrInvalidCid{}, ErrInvalidCid{}},
{&ErrInvalidCid{}, &ErrInvalidCid{}},
} {
if !errors.Is(test.err, test.target) {
t.Fatalf("expected error to be ErrInvalidCid, case %d", i)
}
}
}
func TestErrInvalidCid(t *testing.T) {
run := func(err error) {
if err == nil {
t.Fatal("expected error")
}
if !strings.HasPrefix(err.Error(), "invalid cid: ") {
t.Fatal(`expected error message to contain "invalid cid: "`)
}
is := errors.Is(err, ErrInvalidCid{})
if !is {
t.Fatal("expected error to be ErrInvalidCid")
}
if !errors.Is(err, &ErrInvalidCid{}) {
t.Fatal("expected error to be &ErrInvalidCid")
}
}
_, err := Decode("")
run(err)
_, err = Decode("not-a-cid")
run(err)
_, err = Decode("bafyInvalid")
run(err)
_, err = Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
run(err)
_, err = Cast([]byte("invalid"))
run(err)
_, err = Parse("not-a-cid")
run(err)
_, err = Parse("bafyInvalid")
run(err)
_, err = Parse("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
run(err)
_, err = Parse(123)
run(err)
_, _, err = CidFromBytes([]byte("invalid"))
run(err)
_, err = Prefix{}.Sum([]byte("data"))
run(err)
_, err = PrefixFromBytes([]byte{0x80})
run(err)
_, err = ExtractEncoding("invalid ")
run(err)
} }

21
go.mod
View File

@@ -1,8 +1,21 @@
module github.com/ipfs/go-cid module code.sonr.org/go/cid
require ( require (
github.com/multiformats/go-multibase v0.0.1 github.com/multiformats/go-multibase v0.2.0
github.com/multiformats/go-multihash v0.0.10 github.com/multiformats/go-multihash v0.2.3
github.com/multiformats/go-varint v0.1.0
) )
go 1.13 require (
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/sys v0.38.0 // indirect
lukechampine.com/blake3 v1.1.6 // indirect
)
go 1.24.0

45
go.sum
View File

@@ -1,28 +1,25 @@
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multihash v0.0.9 h1:aoijQXYYl7Xtb2pUUP68R+ys1TlnlR3eX6wmozr0Hp4= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-multihash v0.0.10 h1:lMoNbh2Ssd9PUF74Nz008KGzGPlfeV6wH3rit5IIGCM= github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI=
github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -1,30 +0,0 @@
{
"author": "whyrusleeping",
"bugs": {
"url": "https://github.com/ipfs/go-cid"
},
"gx": {
"dvcsimport": "github.com/ipfs/go-cid"
},
"gxDependencies": [
{
"author": "whyrusleeping",
"hash": "QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW",
"name": "go-multihash",
"version": "1.0.9"
},
{
"author": "whyrusleeping",
"hash": "QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd",
"name": "go-multibase",
"version": "0.3.0"
}
],
"gxVersion": "0.8.0",
"language": "go",
"license": "MIT",
"name": "go-cid",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "0.9.3"
}

View File

@@ -1,6 +1,10 @@
package cid package cid
// Version of varint function that work with a string rather than import (
"github.com/multiformats/go-varint"
)
// Version of varint function that works with a string rather than
// []byte to avoid unnecessary allocation // []byte to avoid unnecessary allocation
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
@@ -8,27 +12,26 @@ package cid
// license as given at https://golang.org/LICENSE // license as given at https://golang.org/LICENSE
// uvarint decodes a uint64 from buf and returns that value and the // uvarint decodes a uint64 from buf and returns that value and the
// number of characters read (> 0). If an error occurred, the value is 0 // number of bytes read (> 0). If an error occurred, then 0 is
// and the number of bytes n is <= 0 meaning: // returned for both the value and the number of bytes read, and an
// // error is returned.
// n == 0: buf too small func uvarint(buf string) (uint64, int, error) {
// n < 0: value larger than 64 bits (overflow)
// and -n is the number of bytes read
//
func uvarint(buf string) (uint64, int) {
var x uint64 var x uint64
var s uint var s uint
// we have a binary string so we can't use a range loope // we have a binary string so we can't use a range loop
for i := 0; i < len(buf); i++ { for i := 0; i < len(buf); i++ {
b := buf[i] b := buf[i]
if b < 0x80 { if b < 0x80 {
if i > 9 || i == 9 && b > 1 { if i > 9 || i == 9 && b > 1 {
return 0, -(i + 1) // overflow return 0, 0, varint.ErrOverflow
} }
return x | uint64(b)<<s, i + 1 if b == 0 && i > 0 {
return 0, 0, varint.ErrNotMinimal
}
return x | uint64(b)<<s, i + 1, nil
} }
x |= uint64(b&0x7f) << s x |= uint64(b&0x7f) << s
s += 7 s += 7
} }
return 0, 0 return 0, 0, varint.ErrUnderflow
} }

View File

@@ -1,17 +1,25 @@
package cid package cid
import ( import (
"encoding/binary"
"testing" "testing"
"github.com/multiformats/go-varint"
) )
func TestUvarintRoundTrip(t *testing.T) { func TestUvarintRoundTrip(t *testing.T) {
testCases := []uint64{0, 1, 2, 127, 128, 129, 255, 256, 257, 1<<63 - 1} testCases := []uint64{0, 1, 2, 127, 128, 129, 255, 256, 257, 1<<63 - 1}
for _, tc := range testCases { for _, tc := range testCases {
t.Log("testing", tc)
buf := make([]byte, 16) buf := make([]byte, 16)
binary.PutUvarint(buf, tc) varint.PutUvarint(buf, tc)
v, l1 := uvarint(string(buf)) v, l1, err := uvarint(string(buf))
_, l2 := binary.Uvarint(buf) if err != nil {
t.Fatalf("%v: %s", buf, err)
}
_, l2, err := varint.FromUvarint(buf)
if err != nil {
t.Fatal(err)
}
if tc != v { if tc != v {
t.Errorf("roundtrip failed expected %d but got %d", tc, v) t.Errorf("roundtrip failed expected %d but got %d", tc, v)
} }
@@ -20,3 +28,29 @@ func TestUvarintRoundTrip(t *testing.T) {
} }
} }
} }
func TestUvarintEdges(t *testing.T) {
tests := []struct {
name string
input []byte
want error
}{
{"ErrNotMinimal", []byte{0x01 | 0x80, 0}, varint.ErrNotMinimal},
{"ErrOverflow", []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}, varint.ErrOverflow},
{"ErrUnderflow", []byte{0x80}, varint.ErrUnderflow},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
v, l1, err := uvarint(string(test.input))
if err != test.want {
t.Fatalf("error case (%v) should return varint.%s (got: %v)", test.input, test.name, err)
}
if v != 0 {
t.Fatalf("error case (%v) should return 0 value (got %d)", test.input, v)
}
if l1 != 0 {
t.Fatalf("error case (%v) should return 0 length (got %d)", test.input, l1)
}
})
}
}

3
version.json Normal file
View File

@@ -0,0 +1,3 @@
{
"version": "v0.6.0"
}