github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/bc/entry.go (about) 1 package bc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "reflect" 8 9 "github.com/golang/protobuf/proto" 10 11 "github.com/bytom/bytom/crypto/sha3pool" 12 "github.com/bytom/bytom/encoding/blockchain" 13 "github.com/bytom/bytom/errors" 14 ) 15 16 // Entry is the interface implemented by each addressable unit in a 17 // blockchain: transaction components such as spends, issuances, 18 // outputs, and retirements (among others), plus blockheaders. 19 type Entry interface { 20 proto.Message 21 22 // type produces a short human-readable string uniquely identifying 23 // the type of this entry. 24 typ() string 25 26 // writeForHash writes the entry's body for hashing. 27 writeForHash(w io.Writer) 28 } 29 30 var errInvalidValue = errors.New("invalid value") 31 32 // EntryID computes the identifier of an entry, as the hash of its 33 // body plus some metadata. 34 func EntryID(e Entry) (hash Hash) { 35 if e == nil { 36 return hash 37 } 38 39 // Nil pointer; not the same as nil interface above. (See 40 // https://golang.org/doc/faq#nil_error.) 41 if v := reflect.ValueOf(e); v.Kind() == reflect.Ptr && v.IsNil() { 42 return hash 43 } 44 45 hasher := sha3pool.Get256() 46 defer sha3pool.Put256(hasher) 47 48 hasher.Write([]byte("entryid:")) 49 hasher.Write([]byte(e.typ())) 50 hasher.Write([]byte{':'}) 51 52 bh := sha3pool.Get256() 53 defer sha3pool.Put256(bh) 54 55 e.writeForHash(bh) 56 57 var innerHash [32]byte 58 bh.Read(innerHash[:]) 59 60 hasher.Write(innerHash[:]) 61 62 hash.ReadFrom(hasher) 63 return hash 64 } 65 66 var byte32zero [32]byte 67 68 // mustWriteForHash serializes the object c to the writer w, from which 69 // presumably a hash can be extracted. 70 // 71 // This function may panic with an error from the underlying writer, 72 // and may produce errors of its own if passed objects whose 73 // hash-serialization formats are not specified. It MUST NOT produce 74 // errors in other cases. 75 func mustWriteForHash(w io.Writer, c interface{}) { 76 if err := writeForHash(w, c); err != nil { 77 panic(err) 78 } 79 } 80 81 func writeForHash(w io.Writer, c interface{}) error { 82 switch v := c.(type) { 83 case byte: 84 _, err := w.Write([]byte{v}) 85 return errors.Wrap(err, "writing byte for hash") 86 case uint64: 87 buf := [8]byte{} 88 binary.LittleEndian.PutUint64(buf[:], v) 89 _, err := w.Write(buf[:]) 90 return errors.Wrapf(err, "writing uint64 (%d) for hash", v) 91 case []byte: 92 _, err := blockchain.WriteVarstr31(w, v) 93 return errors.Wrapf(err, "writing []byte (len %d) for hash", len(v)) 94 case [][]byte: 95 _, err := blockchain.WriteVarstrList(w, v) 96 return errors.Wrapf(err, "writing [][]byte (len %d) for hash", len(v)) 97 case string: 98 _, err := blockchain.WriteVarstr31(w, []byte(v)) 99 return errors.Wrapf(err, "writing string (len %d) for hash", len(v)) 100 case *Hash: 101 if v == nil { 102 _, err := w.Write(byte32zero[:]) 103 return errors.Wrap(err, "writing nil *Hash for hash") 104 } 105 _, err := w.Write(v.Bytes()) 106 return errors.Wrap(err, "writing *Hash for hash") 107 case *AssetID: 108 if v == nil { 109 _, err := w.Write(byte32zero[:]) 110 return errors.Wrap(err, "writing nil *AssetID for hash") 111 } 112 _, err := w.Write(v.Bytes()) 113 return errors.Wrap(err, "writing *AssetID for hash") 114 case Hash: 115 _, err := v.WriteTo(w) 116 return errors.Wrap(err, "writing Hash for hash") 117 case AssetID: 118 _, err := v.WriteTo(w) 119 return errors.Wrap(err, "writing AssetID for hash") 120 } 121 122 // The two container types in the spec (List and Struct) 123 // correspond to slices and structs in Go. They can't be 124 // handled with type assertions, so we must use reflect. 125 switch v := reflect.ValueOf(c); v.Kind() { 126 case reflect.Ptr: 127 if v.IsNil() { 128 return nil 129 } 130 elem := v.Elem() 131 return writeForHash(w, elem.Interface()) 132 case reflect.Slice: 133 l := v.Len() 134 if _, err := blockchain.WriteVarint31(w, uint64(l)); err != nil { 135 return errors.Wrapf(err, "writing slice (len %d) for hash", l) 136 } 137 for i := 0; i < l; i++ { 138 c := v.Index(i) 139 if !c.CanInterface() { 140 return errInvalidValue 141 } 142 if err := writeForHash(w, c.Interface()); err != nil { 143 return errors.Wrapf(err, "writing slice element %d for hash", i) 144 } 145 } 146 return nil 147 148 case reflect.Struct: 149 typ := v.Type() 150 for i := 0; i < typ.NumField(); i++ { 151 c := v.Field(i) 152 if !c.CanInterface() { 153 return errInvalidValue 154 } 155 if err := writeForHash(w, c.Interface()); err != nil { 156 t := v.Type() 157 f := t.Field(i) 158 return errors.Wrapf(err, "writing struct field %d (%s.%s) for hash", i, t.Name(), f.Name) 159 } 160 } 161 return nil 162 } 163 164 return errors.Wrap(fmt.Errorf("bad type %T", c)) 165 }