github.com/koko1123/flow-go-1@v0.29.6/model/flow/identifier.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package flow 4 5 import ( 6 "encoding/binary" 7 "encoding/hex" 8 "fmt" 9 "math/rand" 10 "reflect" 11 12 "github.com/ipfs/go-cid" 13 mh "github.com/multiformats/go-multihash" 14 15 "github.com/koko1123/flow-go-1/model/fingerprint" 16 "github.com/koko1123/flow-go-1/storage/merkle" 17 "github.com/onflow/flow-go/crypto" 18 "github.com/onflow/flow-go/crypto/hash" 19 ) 20 21 const IdentifierLen = 32 22 23 // Identifier represents a 32-byte unique identifier for an entity. 24 type Identifier [IdentifierLen]byte 25 26 // IdentifierFilter is a filter on identifiers. 27 type IdentifierFilter func(Identifier) bool 28 29 // IdentifierOrder is a sort for identifier 30 type IdentifierOrder func(Identifier, Identifier) bool 31 32 var ( 33 // ZeroID is the lowest value in the 32-byte ID space. 34 ZeroID = Identifier{} 35 ) 36 37 // HexStringToIdentifier converts a hex string to an identifier. The input 38 // must be 64 characters long and contain only valid hex characters. 39 func HexStringToIdentifier(hexString string) (Identifier, error) { 40 var identifier Identifier 41 i, err := hex.Decode(identifier[:], []byte(hexString)) 42 if err != nil { 43 return identifier, err 44 } 45 if i != 32 { 46 return identifier, fmt.Errorf("malformed input, expected 32 bytes (64 characters), decoded %d", i) 47 } 48 return identifier, nil 49 } 50 51 func MustHexStringToIdentifier(hexString string) Identifier { 52 id, err := HexStringToIdentifier(hexString) 53 if err != nil { 54 panic(err) 55 } 56 return id 57 } 58 59 // String returns the hex string representation of the identifier. 60 func (id Identifier) String() string { 61 return hex.EncodeToString(id[:]) 62 } 63 64 // Format handles formatting of id for different verbs. This is called when 65 // formatting an identifier with fmt. 66 func (id Identifier) Format(state fmt.State, verb rune) { 67 switch verb { 68 case 'x', 's', 'v': 69 _, _ = state.Write([]byte(id.String())) 70 default: 71 _, _ = state.Write([]byte(fmt.Sprintf("%%!%c(%s=%s)", verb, reflect.TypeOf(id), id))) 72 } 73 } 74 75 // IsSampled is a utility method to sample entities based on their ids 76 // the range is from [0, 64]. 77 // 0 is 100% (all data will be collected) 78 // 1 is ~50% 79 // 2 is ~25% 80 // 3 is ~12.5% 81 // ... 82 // >64 is 0% (no data will be collected) 83 func (id Identifier) IsSampled(sensitivity uint) bool { 84 if sensitivity > 64 { 85 return false 86 } 87 // take the first 8 bytes and check the first few bits based on sensitivity 88 // higher sensitivity means more bits has to be zero, means less number of samples 89 // sensitivity of zero, means everything is sampled 90 return binary.BigEndian.Uint64(id[:8])>>uint64(64-sensitivity) == 0 91 } 92 93 func (id Identifier) MarshalText() ([]byte, error) { 94 return []byte(id.String()), nil 95 } 96 97 func (id *Identifier) UnmarshalText(text []byte) error { 98 var err error 99 *id, err = HexStringToIdentifier(string(text)) 100 return err 101 } 102 103 func HashToID(hash []byte) Identifier { 104 var id Identifier 105 copy(id[:], hash) 106 return id 107 } 108 109 // MakeID creates an ID from a hash of encoded data. MakeID uses `model.Fingerprint() []byte` to get the byte 110 // representation of the entity, which uses RLP to encode the data. If the input defines its own canonical encoding by 111 // implementing Fingerprinter, it uses that instead. That allows removal of non-unique fields from structs or 112 // overwriting of the used encoder. We are using Fingerprint instead of the default encoding for two reasons: a) JSON 113 // (the default encoding) does not specify an order for the elements of arrays and objects, which could lead to 114 // different hashes depending on the JSON implementation and b) the Fingerprinter interface allows to exclude fields not 115 // needed in the pre-image of the hash that comprises the Identifier, which could be different from the encoding for 116 // sending entities in messages or for storing them. 117 func MakeID(entity interface{}) Identifier { 118 // collect fingerprint of the entity 119 data := fingerprint.Fingerprint(entity) 120 // make ID from fingerprint 121 return MakeIDFromFingerPrint(data) 122 } 123 124 // MakeIDFromFingerPrint is similar to MakeID but skipping fingerprinting step. 125 func MakeIDFromFingerPrint(fingerPrint []byte) Identifier { 126 var id Identifier 127 hash.ComputeSHA3_256((*[hash.HashLenSHA3_256]byte)(&id), fingerPrint) 128 return id 129 } 130 131 // PublicKeyToID creates an ID from a public key. 132 func PublicKeyToID(pk crypto.PublicKey) (Identifier, error) { 133 var id Identifier 134 pkBytes := pk.Encode() 135 hash.ComputeSHA3_256((*[32]byte)(&id), pkBytes) 136 return id, nil 137 } 138 139 // GetIDs gets the IDs for a slice of entities. 140 func GetIDs[T Entity](entities []T) IdentifierList { 141 ids := make([]Identifier, 0, len(entities)) 142 for _, entity := range entities { 143 ids = append(ids, entity.ID()) 144 } 145 return ids 146 } 147 148 func MerkleRoot(ids ...Identifier) Identifier { 149 var root Identifier 150 tree, _ := merkle.NewTree(IdentifierLen) // we verify in a unit test that constructor does not error for this paramter 151 for i, id := range ids { 152 val := make([]byte, 8) 153 binary.BigEndian.PutUint64(val, uint64(i)) 154 _, _ = tree.Put(id[:], val) // Tree copies keys and values internally 155 // `Put` only errors for keys whose length does not conform to the pre-configured length. As 156 // Identifiers are fixed-sized arrays, errors are impossible here, which we also verify in a unit test. 157 } 158 hash := tree.Hash() 159 copy(root[:], hash) 160 return root 161 } 162 163 func CheckMerkleRoot(root Identifier, ids ...Identifier) bool { 164 computed := MerkleRoot(ids...) 165 return root == computed 166 } 167 168 func ConcatSum(ids ...Identifier) Identifier { 169 hasher := hash.NewSHA3_256() 170 for _, id := range ids { 171 _, _ = hasher.Write(id[:]) 172 } 173 hash := hasher.SumHash() 174 return HashToID(hash) 175 } 176 177 func CheckConcatSum(sum Identifier, fps ...Identifier) bool { 178 computed := ConcatSum(fps...) 179 return sum == computed 180 } 181 182 // Sample returns random sample of length 'size' of the ids 183 // [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). 184 func Sample(size uint, ids ...Identifier) []Identifier { 185 n := uint(len(ids)) 186 dup := make([]Identifier, 0, n) 187 dup = append(dup, ids...) 188 // if sample size is greater than total size, return all the elements 189 if n <= size { 190 return dup 191 } 192 for i := uint(0); i < size; i++ { 193 j := uint(rand.Intn(int(n - i))) 194 dup[i], dup[j+i] = dup[j+i], dup[i] 195 } 196 return dup[:size] 197 } 198 199 func CidToId(c cid.Cid) (Identifier, error) { 200 decoded, err := mh.Decode(c.Hash()) 201 if err != nil { 202 return ZeroID, fmt.Errorf("failed to decode CID: %w", err) 203 } 204 205 if decoded.Code != mh.SHA2_256 { 206 return ZeroID, fmt.Errorf("unsupported CID hash function: %v", decoded.Name) 207 } 208 if decoded.Length != IdentifierLen { 209 return ZeroID, fmt.Errorf("invalid CID length: %d", decoded.Length) 210 } 211 212 return HashToID(decoded.Digest), nil 213 } 214 215 func IdToCid(f Identifier) cid.Cid { 216 hash, _ := mh.Encode(f[:], mh.SHA2_256) 217 return cid.NewCidV0(hash) 218 } 219 220 func ByteSliceToId(b []byte) (Identifier, error) { 221 var id Identifier 222 if len(b) != IdentifierLen { 223 return id, fmt.Errorf("illegal length for a flow identifier %x: got: %d, expected: %d", b, len(b), IdentifierLen) 224 } 225 226 copy(id[:], b[:]) 227 228 return id, nil 229 } 230 231 func ByteSlicesToIds(b [][]byte) (IdentifierList, error) { 232 total := len(b) 233 ids := make(IdentifierList, total) 234 235 for i := 0; i < total; i++ { 236 id, err := ByteSliceToId(b[i]) 237 if err != nil { 238 return nil, err 239 } 240 241 ids[i] = id 242 } 243 244 return ids, nil 245 } 246 247 func IdsToBytes(identifiers []Identifier) [][]byte { 248 var byteIds [][]byte 249 for _, id := range identifiers { 250 tempID := id // avoid capturing loop variable 251 byteIds = append(byteIds, tempID[:]) 252 } 253 254 return byteIds 255 }