Compare commits

..

69 Commits

Author SHA1 Message Date
Daniel Martí
373e25552c 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-07 16:49:03 +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
Steven Allen
3da5bbbe45 Merge pull request #92 from b5/chore_remove_gx
docs(readme): remove reference to gx
2019-12-09 06:47:25 -06:00
Steven Allen
9d598bbdbd Merge pull request #98 from ipfs/dependabot/go_modules/github.com/multiformats/go-multihash-0.0.10
Bump github.com/multiformats/go-multihash from 0.0.9 to 0.0.10
2019-12-09 06:24:09 -06:00
dependabot-preview[bot]
b420452400 Bump github.com/multiformats/go-multihash from 0.0.9 to 0.0.10
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.0.9 to 0.0.10.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.0.9...v0.0.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-09 11:21:09 +00:00
Steven Allen
ed7dab1755 Merge pull request #97 from ipfs/fix/parse-panic
fix parsing issues and nits
2019-12-03 09:50:46 -05:00
Steven Allen
5df89959a0 test: test parsing non-sha256 hashes 2019-12-02 20:41:59 -05:00
Steven Allen
60ab0f84f0 chore: avoid magic numbers 2019-12-02 20:35:36 -05:00
Steven Allen
08e15f8a6c chore: avoid re-validated already validated CIDs 2019-12-02 20:35:27 -05:00
Steven Allen
58b483a841 fix: avoid panicing if we try to parse a non-sha multihash as a CIDv0
I'm pretty sure we never call `Parse` multihashes anyways, but we should be careful.
2019-12-02 20:34:30 -05:00
Whyrusleeping
79e75dffeb Merge pull request #95 from ipfs/feat/cid-from-bytes
Add a function to read a cid from an array of bytes
2019-11-12 10:17:18 +09:00
whyrusleeping
d93d4baeab add a simple test for reading cids from a buffer 2019-11-11 16:48:19 -08:00
whyrusleeping
cf8cf8856e Use optimized multihash method for parsing 2019-11-11 16:33:49 -08:00
Steven Allen
aa8072eeb5 Merge pull request #96 from ipfs/fix/verif-v0
Verify that prefix is correct v0 prefix
2019-11-11 11:29:02 -08:00
Jakub Sztandera
51871ccaa9 Disable gx builds
License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2019-11-11 20:04:40 +01:00
Jakub Sztandera
9238d1a533 Verify that prefix is correct v0 prefix
License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2019-11-11 20:00:31 +01:00
whyrusleeping
16bbe1cf14 Add a command to read a cid from an array of bytes 2019-11-10 22:17:41 -08:00
b5
c91a795815 docs(readme): remove reference to gx 2019-10-24 10:06:43 -04:00
Steven Allen
64acce09e1 Merge pull request #91 from ipfs/dependabot/go_modules/github.com/multiformats/go-multihash-0.0.8
Bump github.com/multiformats/go-multihash from 0.0.1 to 0.0.8
2019-09-24 16:11:26 -07:00
dependabot-preview[bot]
2a2f67d2c4 Bump github.com/multiformats/go-multihash from 0.0.1 to 0.0.8
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.0.1 to 0.0.8.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.0.1...v0.0.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-24 23:01:51 +00:00
Steven Allen
86ca2a8300 Merge pull request #90 from ipfs/feat/lead-maintainer
doc: add a lead maintainer
2019-09-18 22:09:20 -07:00
Steven Allen
e0a778be45 doc: add a lead maintainer 2019-09-18 22:08:21 -07:00
20 changed files with 1107 additions and 323 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)
-->

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: ""

53
.github/workflows/automerge.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
# Automatically merge pull requests opened by web3-bot, as soon as (and only if) all tests pass.
# This reduces the friction associated with updating with our workflows.
on: [ pull_request ]
name: Automerge
jobs:
automerge-check:
if: github.event.pull_request.user.login == 'web3-bot'
runs-on: ubuntu-latest
outputs:
status: ${{ steps.should-automerge.outputs.status }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check if we should automerge
id: should-automerge
run: |
for commit in $(git rev-list --first-parent origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}); do
committer=$(git show --format=$'%ce' -s $commit)
echo "Committer: $committer"
if [[ "$committer" != "web3-bot@users.noreply.github.com" ]]; then
echo "Commit $commit wasn't committed by web3-bot, but by $committer."
echo "::set-output name=status::false"
exit
fi
done
echo "::set-output name=status::true"
automerge:
needs: automerge-check
runs-on: ubuntu-latest
# The check for the user is redundant here, as this job depends on the automerge-check job,
# but it prevents this job from spinning up, just to be skipped shortly after.
if: github.event.pull_request.user.login == 'web3-bot' && needs.automerge-check.outputs.status == 'true'
steps:
- name: Wait on tests
uses: lewagon/wait-on-check-action@bafe56a6863672c681c3cf671f5e10b20abf2eaa # v0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
running-workflow-name: 'automerge' # the name of this job
- name: Merge PR
uses: pascalgn/automerge-action@741c311a47881be9625932b0a0de1b0937aab1ae # v0.13.1
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: ""
MERGE_METHOD: "squash"
MERGE_DELETE_BRANCH: true

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

@@ -0,0 +1,74 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
on: [push, pull_request]
name: Go Checks
jobs:
unit:
runs-on: ubuntu-latest
name: All
env:
RUNGOGENERATE: false
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-go@v2
with:
go-version: "1.17.x"
- name: Run repo-specific setup
uses: ./.github/actions/go-check-setup
if: hashFiles('./.github/actions/go-check-setup') != ''
- name: Read config
if: hashFiles('./.github/workflows/go-check-config.json') != ''
run: |
if jq -re .gogenerate ./.github/workflows/go-check-config.json; then
echo "RUNGOGENERATE=true" >> $GITHUB_ENV
fi
- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@df71e5d0e0ed317ebf43e6e59cf919430fa4b8f2 # 2021.1.1 (v0.2.1)
- name: Check that go.mod is tidy
uses: protocol/multiple-go-modules@v1.2
with:
run: |
go mod tidy
if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then
echo "go.sum was added by go mod tidy"
exit 1
fi
git diff --exit-code -- go.sum go.mod
- name: gofmt
if: ${{ success() || failure() }} # run this step even if the previous one failed
run: |
out=$(gofmt -s -l .)
if [[ -n "$out" ]]; then
echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}'
exit 1
fi
- name: go vet
if: ${{ success() || failure() }} # run this step even if the previous one failed
uses: protocol/multiple-go-modules@v1.2
with:
run: go vet ./...
- name: staticcheck
if: ${{ success() || failure() }} # run this step even if the previous one failed
uses: protocol/multiple-go-modules@v1.2
with:
run: |
set -o pipefail
staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g'
- name: go generate
uses: protocol/multiple-go-modules@v1.2
if: (success() || failure()) && env.RUNGOGENERATE == 'true'
with:
run: |
git clean -fd # make sure there aren't untracked files / directories
go generate ./...
# check if go generate modified or added any files
if ! $(git add . && git diff-index HEAD --exit-code --quiet); then
echo "go generated caused changes to the repository:"
git status --short
exit 1
fi

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

@@ -0,0 +1,55 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
on: [push, pull_request]
name: Go Test
jobs:
unit:
strategy:
fail-fast: false
matrix:
os: [ "ubuntu", "windows", "macos" ]
go: [ "1.16.x", "1.17.x" ]
env:
COVERAGES: ""
runs-on: ${{ matrix.os }}-latest
name: ${{ matrix.os}} (go ${{ matrix.go }})
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- name: Go information
run: |
go version
go env
- name: Run repo-specific setup
uses: ./.github/actions/go-test-setup
if: hashFiles('./.github/actions/go-test-setup') != ''
- name: Run tests
uses: protocol/multiple-go-modules@v1.2
with:
run: go test -v -coverprofile module-coverage.txt ./...
- name: Run tests (32 bit)
if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX.
uses: protocol/multiple-go-modules@v1.2
env:
GOARCH: 386
with:
run: go test -v ./...
- name: Run tests with race detector
if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow
uses: protocol/multiple-go-modules@v1.2
with:
run: go test -v -race ./...
- name: Collect coverage files
shell: bash
run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV
- name: Upload coverage to Codecov
uses: codecov/codecov-action@51d810878be5422784e86451c0e7c14e5860ec47 # v2.0.2
with:
files: '${{ env.COVERAGES }}'
env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }}

View File

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

View File

@@ -1,32 +0,0 @@
os:
- linux
language: go
go:
- 1.11.x
env:
global:
- GOTFLAGS="-race"
matrix:
- BUILD_DEPTYPE=gx
- 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
gx:
go get github.com/whyrusleeping/gx
go get github.com/whyrusleeping/gx-go
covertools:
deps:
go get github.com/mattn/goveralls
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

@@ -14,6 +14,9 @@ go-cid
This is an implementation in Go of the [CID spec](https://github.com/ipld/cid).
It is used in `go-ipfs` and related packages to refer to a typed hunk of data.
## Lead Maintainer
[Eric Myhre](https://github.com/warpfork)
## Table of Contents
@@ -31,36 +34,16 @@ It is used in `go-ipfs` and related packages to refer to a typed hunk of data.
go get github.com/ipfs/go-cid
```
Note that `go-cid` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
## Usage
### Using Gx and Gx-go
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project it is recommended that you:
```sh
go get -u github.com/whyrusleeping/gx
go get -u github.com/whyrusleeping/gx-go
cd <your-project-repository>
gx init
gx import github.com/ipfs/go-cid
gx install --global
gx-go --rewrite
```
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
### Running tests
Before running tests, please run:
Run tests with `go test` from the directory root
```sh
make deps
go test
```
This will make sure that dependencies are rewritten to known working versions.
### Examples
#### Parsing string input from users

View File

@@ -38,7 +38,7 @@ func (p V0Builder) Sum(data []byte) (Cid, error) {
if err != nil {
return Undef, err
}
return NewCidV0(hash), nil
return Cid{string(hash)}, nil
}
func (p V0Builder) GetCodec() uint64 {

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]}
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) {
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) {
testCodecChange(t, V1Builder{Codec: DagProtobuf, MhType: mh.SHA2_256})
})

520
cid.go
View File

@@ -26,25 +26,18 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"strings"
mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash"
varint "github.com/multiformats/go-varint"
)
// UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>"
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
// enough to be a valid Cid
ErrCidTooShort = errors.New("cid too short")
@@ -66,114 +59,147 @@ const (
GitRaw = 0x78
EthBlock = 0x90
EthBlockList = 0x91
EthTxTrie = 0x92
EthTx = 0x93
EthTxReceiptTrie = 0x94
EthTxReceipt = 0x95
EthStateTrie = 0x96
EthAccountSnapshot = 0x97
EthStorageTrie = 0x98
BitcoinBlock = 0xb0
BitcoinTx = 0xb1
ZcashBlock = 0xc0
ZcashTx = 0xc1
DecredBlock = 0xe0
DecredTx = 0xe1
DashBlock = 0xf0
DashTx = 0xf1
DagJOSE = 0x85
EthBlock = 0x90
EthBlockList = 0x91
EthTxTrie = 0x92
EthTx = 0x93
EthTxReceiptTrie = 0x94
EthTxReceipt = 0x95
EthStateTrie = 0x96
EthAccountSnapshot = 0x97
EthStorageTrie = 0x98
BitcoinBlock = 0xb0
BitcoinTx = 0xb1
ZcashBlock = 0xc0
ZcashTx = 0xc1
DecredBlock = 0xe0
DecredTx = 0xe1
DashBlock = 0xf0
DashTx = 0xf1
FilCommitmentUnsealed = 0xf101
FilCommitmentSealed = 0xf102
)
// 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,
"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,
"fil-commitment-unsealed": FilCommitmentUnsealed,
"fil-commitment-sealed": FilCommitmentSealed,
"dag-jose": DagJOSE,
}
// 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",
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",
FilCommitmentUnsealed: "fil-commitment-unsealed",
FilCommitmentSealed: "fil-commitment-sealed",
DagJOSE: "dag-jose",
}
// tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an
// error on failure.
func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
// Need to make sure hash is valid for CidV0 otherwise we will
// incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash)
if err != nil {
return Undef, err
}
if dec.Code != mh.SHA2_256 || dec.Length != 32 {
return Undef, fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)
}
return Cid{string(mhash)}, nil
}
// NewCidV0 returns a Cid-wrapped multihash.
// They exist to allow IPFS to work with Cids while keeping
// compatibility with the plain-multihash format used used in IPFS.
// NewCidV1 should be used preferentially.
//
// Panics if the multihash isn't sha2-256.
func NewCidV0(mhash mh.Multihash) Cid {
// Need to make sure hash is valid for CidV0 otherwise we will
// incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash)
c, err := tryNewCidV0(mhash)
if err != nil {
panic(err)
}
if dec.Code != mh.SHA2_256 || dec.Length != 32 {
panic("invalid hash for cidv0")
}
return Cid{string(mhash)}
return c
}
// NewCidV1 returns a new Cid using the given multicodec-packed
// content type.
//
// Panics if the multihash is invalid.
func NewCidV1(codecType uint64, mhash mh.Multihash) Cid {
hashlen := len(mhash)
// two 8 bytes (max) numbers plus hash
buf := make([]byte, 2*binary.MaxVarintLen64+hashlen)
n := binary.PutUvarint(buf, 1)
n += binary.PutUvarint(buf[n:], codecType)
cn := copy(buf[n:], mhash)
// Two 8 bytes (max) numbers plus hash.
// We use strings.Builder to only allocate once.
var b strings.Builder
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 {
panic("copy hash length is inconsistent")
}
return Cid{string(buf[:n+hashlen])}
return Cid{b.String()}
}
var _ encoding.BinaryMarshaler = Cid{}
var _ encoding.BinaryUnmarshaler = (*Cid)(nil)
var _ encoding.TextMarshaler = Cid{}
var _ encoding.TextUnmarshaler = (*Cid)(nil)
var (
_ encoding.BinaryMarshaler = Cid{}
_ encoding.BinaryUnmarshaler = (*Cid)(nil)
_ encoding.TextMarshaler = Cid{}
_ encoding.TextUnmarshaler = (*Cid)(nil)
)
// Cid represents a self-describing content addressed
// identifier. It is formed by a Version, a Codec (which indicates
@@ -203,7 +229,7 @@ func Parse(v interface{}) (Cid, error) {
case []byte:
return Cast(v2)
case mh.Multihash:
return NewCidV0(v2), nil
return tryNewCidV0(v2)
case Cid:
return v2, nil
default:
@@ -234,7 +260,7 @@ func Decode(v string) (Cid, error) {
return Undef, err
}
return NewCidV0(hash), nil
return tryNewCidV0(hash)
}
_, data, err := mbase.Decode(v)
@@ -267,17 +293,6 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
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.
// For CidV1, the data buffer is in the form:
//
@@ -290,36 +305,16 @@ func uvError(read int) error {
// Please use decode when parsing a regular Cid string, as Cast does not
// expect multibase-encoded data. Cast accepts the output of Cid.Bytes().
func Cast(data []byte) (Cid, error) {
if len(data) == 34 && data[0] == 18 && data[1] == 32 {
h, err := mh.Cast(data)
if err != nil {
return Undef, err
}
return NewCidV0(h), nil
}
vers, n := binary.Uvarint(data)
if err := uvError(n); err != nil {
return Undef, err
}
if vers != 1 {
return Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
}
_, cn := binary.Uvarint(data[n:])
if err := uvError(cn); err != nil {
return Undef, err
}
rest := data[n+cn:]
h, err := mh.Cast(rest)
nr, c, err := CidFromBytes(data)
if err != nil {
return Undef, err
}
return Cid{string(data[0 : n+cn+len(h)])}, nil
if nr != len(data) {
return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast")
}
return c, nil
}
// UnmarshalBinary is equivalent to Cast(). It implements the
@@ -357,14 +352,14 @@ func (c Cid) Type() uint64 {
if c.Version() == 0 {
return DagProtobuf
}
_, n := uvarint(c.str)
codec, _ := uvarint(c.str[n:])
_, n, _ := uvarint(c.str)
codec, _, _ := uvarint(c.str[n:])
return codec
}
// String returns the default string representation of a
// Cid. Currently, Base58 is used as the encoding for the
// multibase string.
// Cid. Currently, Base32 is used for CIDV1 as the encoding for the
// multibase string, Base58 is used for CIDV0.
func (c Cid) String() string {
switch c.Version() {
case 0:
@@ -420,9 +415,9 @@ func (c Cid) Hash() mh.Multihash {
}
// skip version length
_, n1 := binary.Uvarint(bytes)
_, n1, _ := varint.FromUvarint(bytes)
// skip codec length
_, n2 := binary.Uvarint(bytes[n1:])
_, n2, _ := varint.FromUvarint(bytes[n1:])
return mh.Multihash(bytes[n1+n2:])
}
@@ -434,6 +429,30 @@ func (c Cid) Bytes() []byte {
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
// encoding.BinaryMarshaler interface.
func (c Cid) MarshalBinary() ([]byte, error) {
@@ -513,12 +532,29 @@ func (c Cid) Loggable() map[string]interface{} {
// Prefix builds and returns a Prefix out of a Cid.
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{
MhType: dec.Code,
MhLength: dec.Length,
Version: c.Version(),
Codec: c.Type(),
MhType: mhtype,
MhLength: int(mhlen),
Version: version,
Codec: codec,
}
}
@@ -539,10 +575,16 @@ type Prefix struct {
// and return a newly constructed Cid with the resulting multihash.
func (p Prefix) Sum(data []byte) (Cid, error) {
length := p.MhLength
if p.MhType == mh.ID {
if p.MhType == mh.IDENTITY {
length = -1
}
if p.Version == 0 && (p.MhType != mh.SHA2_256 ||
(p.MhLength != 32 && p.MhLength != -1)) {
return Undef, fmt.Errorf("invalid v0 prefix")
}
hash, err := mh.Sum(data, p.MhType, length)
if err != nil {
return Undef, err
@@ -562,34 +604,42 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
//
// <version><codec><mh-type><mh-length>
func (p Prefix) Bytes() []byte {
buf := make([]byte, 4*binary.MaxVarintLen64)
n := binary.PutUvarint(buf, p.Version)
n += binary.PutUvarint(buf[n:], p.Codec)
n += binary.PutUvarint(buf[n:], uint64(p.MhType))
n += binary.PutUvarint(buf[n:], uint64(p.MhLength))
return buf[:n]
size := varint.UvarintSize(p.Version)
size += varint.UvarintSize(p.Codec)
size += varint.UvarintSize(p.MhType)
size += varint.UvarintSize(uint64(p.MhLength))
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
// Prefix.
func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf)
vers, err := binary.ReadUvarint(r)
vers, err := varint.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
codec, err := binary.ReadUvarint(r)
codec, err := varint.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhtype, err := binary.ReadUvarint(r)
mhtype, err := varint.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhlen, err := binary.ReadUvarint(r)
mhlen, err := varint.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
@@ -601,3 +651,177 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
MhLength: int(mhlen),
}, nil
}
func CidFromBytes(data []byte) (int, Cid, error) {
if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 {
if len(data) < 34 {
return 0, Undef, fmt.Errorf("not enough bytes for cid v0")
}
h, err := mh.Cast(data[:34])
if err != nil {
return 0, Undef, err
}
return 34, Cid{string(h)}, nil
}
vers, n, err := varint.FromUvarint(data)
if err != nil {
return 0, Undef, err
}
if vers != 1 {
return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
}
_, cn, err := varint.FromUvarint(data[n:])
if err != nil {
return 0, Undef, err
}
mhnr, _, err := mh.MHFromBytes(data[n+cn:])
if err != nil {
return 0, Undef, err
}
l := n + cn + mhnr
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.
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 {
return len(br.dst), Undef, 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, err
}
br.dst = br.dst[:34]
h, err := mh.Cast(br.dst)
if err != nil {
return len(br.dst), Undef, err
}
return len(br.dst), Cid{string(h)}, nil
}
if vers != 1 {
return len(br.dst), Undef, 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, 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, err
}
// Multihash digest length.
mhl, err := varint.ReadUvarint(br)
if err != nil {
return len(br.dst), Undef, 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, 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, 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, err
}
return len(br.dst), Cid{string(br.dst)}, nil
}

View File

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

View File

@@ -4,9 +4,12 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"math/rand"
"reflect"
"strings"
"testing"
"testing/iotest"
mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash"
@@ -16,28 +19,31 @@ import (
// 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",
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",
FilCommitmentUnsealed: "fil-commitment-unsealed",
FilCommitmentSealed: "fil-commitment-sealed",
DagJOSE: "dag-jose",
}
func assertEqual(t *testing.T, a, b Cid) {
@@ -78,7 +84,7 @@ func TestPrefixSum(t *testing.T) {
// Test creating CIDs both manually and with Prefix.
// Tests: https://github.com/ipfs/go-cid/issues/83
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)
if err != nil {
@@ -182,8 +188,32 @@ func TestBasesMarshaling(t *testing.T) {
}
s2 := cid.Encode(encoder)
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 ee != -1 {
t.Fatal("expected -1 from too-short ExtractEncoding")
}
}
@@ -192,18 +222,32 @@ func TestBinaryMarshaling(t *testing.T) {
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash)
var c2 Cid
var c3 Cid
data, err := c.MarshalBinary()
if err != nil {
t.Fatal(err)
}
err = c2.UnmarshalBinary(data)
if err != nil {
if err = c2.UnmarshalBinary(data); err != nil {
t.Fatal(err)
}
if !c.Equals(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) {
@@ -216,13 +260,16 @@ func TestTextMarshaling(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = c2.UnmarshalText(data)
if err != nil {
if err = c2.UnmarshalText(data); err != nil {
t.Fatal(err)
}
if !c.Equals(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) {
@@ -252,6 +299,11 @@ func TestV0Handling(t *testing.T) {
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)
if err != nil {
t.Fatal(err)
@@ -267,6 +319,11 @@ func TestV0Handling(t *testing.T) {
if cid.Encode(encoder) != old {
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) {
@@ -335,6 +392,47 @@ func TestNewPrefixV0(t *testing.T) {
}
}
func TestInvalidV0Prefix(t *testing.T) {
tests := []Prefix{
{
MhType: mh.SHA2_256,
MhLength: 31,
},
{
MhType: mh.SHA2_256,
MhLength: 33,
},
{
MhType: mh.SHA2_256,
MhLength: -2,
},
{
MhType: mh.SHA2_512,
MhLength: 32,
},
{
MhType: mh.SHA2_512,
MhLength: -1,
},
}
for i, p := range tests {
t.Log(i)
_, err := p.Sum([]byte("testdata"))
if err == nil {
t.Fatalf("should error (index %d)", i)
}
}
}
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")
}
}
func TestPrefixRoundtrip(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
@@ -364,6 +462,25 @@ 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")
}
_, err = PrefixFromBytes([]byte{0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 1")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 2")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x01, 0x80})
if err == nil {
t.Fatal("expected error for bad byte 3")
}
}
func Test16BytesVarint(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
@@ -396,13 +513,13 @@ func TestParse(t *testing.T) {
}
assertions := [][]interface{}{
[]interface{}{NewCidV0(h), theHash},
[]interface{}{NewCidV0(h).Bytes(), theHash},
[]interface{}{h, theHash},
[]interface{}{theHash, theHash},
[]interface{}{"/ipfs/" + theHash, theHash},
[]interface{}{"https://ipfs.io/ipfs/" + theHash, theHash},
[]interface{}{"http://localhost:8080/ipfs/" + theHash, theHash},
{NewCidV0(h), theHash},
{NewCidV0(h).Bytes(), theHash},
{h, theHash},
{theHash, theHash},
{"/ipfs/" + theHash, theHash},
{"https://ipfs.io/ipfs/" + theHash, theHash},
{"http://localhost:8080/ipfs/" + theHash, theHash},
}
assert := func(arg interface{}, expected string) error {
@@ -411,7 +528,7 @@ func TestParse(t *testing.T) {
return err
}
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()
if actual != expected {
@@ -425,8 +542,7 @@ func TestParse(t *testing.T) {
}
for _, args := range assertions {
err := assert(args[0], args[1].(string))
if err != nil {
if err := assert(args[0], args[1].(string)); err != nil {
t.Fatal(err)
}
}
@@ -460,8 +576,7 @@ func TestFromJson(t *testing.T) {
cval := "bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm"
jsoncid := []byte(`{"/":"` + cval + `"}`)
var c Cid
err := json.Unmarshal(jsoncid, &c)
if err != nil {
if err := json.Unmarshal(jsoncid, &c); err != nil {
t.Fatal(err)
}
@@ -471,6 +586,7 @@ func TestFromJson(t *testing.T) {
}
func TestJsonRoundTrip(t *testing.T) {
expectedJSON := `{"/":"bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm"}`
exp, err := Decode("bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm")
if err != nil {
t.Fatal(err)
@@ -482,21 +598,50 @@ func TestJsonRoundTrip(t *testing.T) {
t.Fatal(err)
}
var actual Cid
err = json.Unmarshal(enc, &actual)
if err = json.Unmarshal(enc, &actual); err != nil {
t.Fatal(err)
}
if !exp.Equals(actual) {
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.
enc, err = json.Marshal(exp)
if err != nil {
t.Fatal(err)
}
var actual2 Cid
err = json.Unmarshal(enc, &actual2)
if err = json.Unmarshal(enc, &actual2); err != nil {
t.Fatal(err)
}
if !exp.Equals(actual2) {
t.Fatal("cids not equal for Cid")
}
if err = actual2.UnmarshalJSON([]byte("1")); err == nil {
t.Fatal("expected error for too-short JSON")
}
if err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`)); err == nil {
t.Fatal("expected error for bad CID JSON")
}
if err = actual2.UnmarshalJSON([]byte(`bad "" json!`)); err == nil {
t.Fatal("expected error for bad JSON")
}
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) {
@@ -515,3 +660,156 @@ func BenchmarkStringV1(b *testing.B) {
b.FailNow()
}
}
func TestReadCidsFromBuffer(t *testing.T) {
cidstr := []string{
"bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm",
"k2cwueckqkibutvhkr4p2ln2pjcaxaakpd9db0e7j7ax1lxhhxy3ekpv",
"Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893",
"zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz",
"bafkqarjpmzuwyzltorxxezjpkvcfgqkfjfbfcvslivje2vchkzdu6rckjjcfgtkolaze6mssjqzeyn2ekrcfatkjku2vowseky3fswkfkm2deqkrju3e2",
}
var cids []Cid
var buf []byte
for _, cs := range cidstr {
c, err := Decode(cs)
if err != nil {
t.Fatal(err)
}
cids = append(cids, c)
buf = append(buf, c.Bytes()...)
}
var cur int
for _, expc := range cids {
n, c, err := CidFromBytes(buf[cur:])
if err != nil {
t.Fatal(err)
}
if c != expc {
t.Fatal("cids mismatched")
}
cur += n
}
if cur != len(buf) {
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 TestBadParse(t *testing.T) {
hash, err := mh.Sum([]byte("foobar"), mh.SHA3_256, -1)
if err != nil {
t.Fatal(err)
}
_, err = Parse(hash)
if err == nil {
t.Fatal("expected to fail to parse an invalid CIDv1 CID")
}
}
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)
}
}

8
go.mod
View File

@@ -1,6 +1,10 @@
module github.com/ipfs/go-cid
require (
github.com/multiformats/go-multibase v0.0.1
github.com/multiformats/go-multihash v0.0.1
github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multihash v0.0.15
github.com/multiformats/go-varint v0.0.6
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
)
go 1.16

45
go.sum
View File

@@ -1,20 +1,35 @@
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
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.2.0/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/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multihash v0.0.15 h1:hWOPdrNqDjwHDx82vsYGSDZNyktOJJ2dzZJzFkOV1jM=
github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg=
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

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
// 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
// Copyright 2011 The Go Authors. All rights reserved.
@@ -8,27 +12,26 @@ package cid
// license as given at https://golang.org/LICENSE
// 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
// and the number of bytes n is <= 0 meaning:
//
// n == 0: buf too small
// n < 0: value larger than 64 bits (overflow)
// and -n is the number of bytes read
//
func uvarint(buf string) (uint64, int) {
// number of bytes read (> 0). If an error occurred, then 0 is
// returned for both the value and the number of bytes read, and an
// error is returned.
func uvarint(buf string) (uint64, int, error) {
var x uint64
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++ {
b := buf[i]
if b < 0x80 {
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
s += 7
}
return 0, 0
return 0, 0, varint.ErrUnderflow
}

View File

@@ -1,17 +1,25 @@
package cid
import (
"encoding/binary"
"testing"
"github.com/multiformats/go-varint"
)
func TestUvarintRoundTrip(t *testing.T) {
testCases := []uint64{0, 1, 2, 127, 128, 129, 255, 256, 257, 1<<63 - 1}
for _, tc := range testCases {
t.Log("testing", tc)
buf := make([]byte, 16)
binary.PutUvarint(buf, tc)
v, l1 := uvarint(string(buf))
_, l2 := binary.Uvarint(buf)
varint.PutUvarint(buf, tc)
v, l1, err := uvarint(string(buf))
if err != nil {
t.Fatalf("%v: %s", buf, err)
}
_, l2, err := varint.FromUvarint(buf)
if err != nil {
t.Fatal(err)
}
if 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)
}
})
}
}