-
Notifications
You must be signed in to change notification settings - Fork 4
/
commcid.go
149 lines (125 loc) · 4.75 KB
/
commcid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Package commcid provides helpers to convert between Piece/Data/Replica
// Commitments and their CID representation
package commcid
import (
"errors"
"fmt"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
"github.com/multiformats/go-varint"
"golang.org/x/xerrors"
)
// FilMultiCodec is a uint64-sized type representing a Filecoin-specific codec
type FilMultiCodec uint64
// FilMultiHash is a uint64-sized type representing a Filecoin-specific multihash
type FilMultiHash uint64
// FILCODEC_UNDEFINED is just a signifier for "no codec determined
const FILCODEC_UNDEFINED = FilMultiCodec(0)
// FILMULTIHASH_UNDEFINED is a signifier for "no multihash etermined"
const FILMULTIHASH_UNDEFINED = FilMultiHash(0)
var (
// ErrIncorrectCodec means the codec for a CID is a block format that does not match
// a commitment hash
ErrIncorrectCodec = errors.New("unexpected commitment codec")
// ErrIncorrectHash means the hash function for this CID does not match the expected
// hash for this type of commitment
ErrIncorrectHash = errors.New("incorrect hashing function for data commitment")
)
// CommitmentToCID converts a raw commitment hash to a CID
// by adding:
// - the given filecoin codec type
// - the given filecoin hash type
func CommitmentToCID(mc FilMultiCodec, mh FilMultiHash, commX []byte) (cid.Cid, error) {
if err := validateFilecoinCidSegments(mc, mh, commX); err != nil {
return cid.Undef, err
}
mhBuf := make(
[]byte,
(varint.UvarintSize(uint64(mh)) + varint.UvarintSize(uint64(len(commX))) + len(commX)),
)
pos := varint.PutUvarint(mhBuf, uint64(mh))
pos += varint.PutUvarint(mhBuf[pos:], uint64(len(commX)))
copy(mhBuf[pos:], commX)
return cid.NewCidV1(uint64(mc), multihash.Multihash(mhBuf)), nil
}
// CIDToCommitment extracts the raw commitment bytes, the FilMultiCodec and
// FilMultiHash from a CID, after validating that the codec and hash type are
// consistent
func CIDToCommitment(c cid.Cid) (FilMultiCodec, FilMultiHash, []byte, error) {
decoded, err := multihash.Decode([]byte(c.Hash()))
if err != nil {
return FILCODEC_UNDEFINED, FILMULTIHASH_UNDEFINED, nil, xerrors.Errorf("Error decoding data commitment hash: %w", err)
}
filCodec := FilMultiCodec(c.Type())
filMh := FilMultiHash(decoded.Code)
if err := validateFilecoinCidSegments(filCodec, filMh, decoded.Digest); err != nil {
return FILCODEC_UNDEFINED, FILMULTIHASH_UNDEFINED, nil, err
}
return filCodec, filMh, decoded.Digest, nil
}
// DataCommitmentV1ToCID converts a raw data commitment to a CID
// by adding:
// - codec: cid.FilCommitmentUnsealed
// - hash type: multihash.SHA2_256_TRUNC254_PADDED
func DataCommitmentV1ToCID(commD []byte) (cid.Cid, error) {
return CommitmentToCID(cid.FilCommitmentUnsealed, multihash.SHA2_256_TRUNC254_PADDED, commD)
}
// CIDToDataCommitmentV1 extracts the raw data commitment from a CID
// after checking for the correct codec and hash types.
func CIDToDataCommitmentV1(c cid.Cid) ([]byte, error) {
codec, _, commD, err := CIDToCommitment(c)
if err != nil {
return nil, err
}
if codec != cid.FilCommitmentUnsealed {
return nil, ErrIncorrectCodec
}
return commD, nil
}
// ReplicaCommitmentV1ToCID converts a raw data commitment to a CID
// by adding:
// - codec: cid.FilCommitmentSealed
// - hash type: multihash.POSEIDON_BLS12_381_A1_FC1
func ReplicaCommitmentV1ToCID(commR []byte) (cid.Cid, error) {
return CommitmentToCID(cid.FilCommitmentSealed, multihash.POSEIDON_BLS12_381_A1_FC1, commR)
}
// CIDToReplicaCommitmentV1 extracts the raw replica commitment from a CID
// after checking for the correct codec and hash types.
func CIDToReplicaCommitmentV1(c cid.Cid) ([]byte, error) {
codec, _, commR, err := CIDToCommitment(c)
if err != nil {
return nil, err
}
if codec != cid.FilCommitmentSealed {
return nil, ErrIncorrectCodec
}
return commR, nil
}
// ValidateFilecoinCidSegments returns an error if the provided CID parts
// conflict with each other.
func validateFilecoinCidSegments(mc FilMultiCodec, mh FilMultiHash, commX []byte) error {
switch mc {
case cid.FilCommitmentUnsealed:
if mh != multihash.SHA2_256_TRUNC254_PADDED {
return ErrIncorrectHash
}
case cid.FilCommitmentSealed:
if mh != multihash.POSEIDON_BLS12_381_A1_FC1 {
return ErrIncorrectHash
}
default: // neither of the codecs above: we are not in Fil teritory
return ErrIncorrectCodec
}
if len(commX) != 32 {
return fmt.Errorf("commitments must be 32 bytes long")
}
return nil
}
// PieceCommitmentV1ToCID converts a commP to a CID
// -- it is just a helper function that is equivalent to
// DataCommitmentV1ToCID.
var PieceCommitmentV1ToCID = DataCommitmentV1ToCID
// CIDToPieceCommitmentV1 converts a CID to a commP
// -- it is just a helper function that is equivalent to
// CIDToDataCommitmentV1.
var CIDToPieceCommitmentV1 = CIDToDataCommitmentV1