9 "github.com/golang/protobuf/proto"
11 "github.com/vapor/crypto/sha3pool"
12 "github.com/vapor/encoding/blockchain"
13 "github.com/vapor/errors"
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 {
22 // type produces a short human-readable string uniquely identifying
23 // the type of this entry.
26 // writeForHash writes the entry's body for hashing.
27 writeForHash(w io.Writer)
30 var errInvalidValue = errors.New("invalid value")
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) {
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() {
45 hasher := sha3pool.Get256()
46 defer sha3pool.Put256(hasher)
48 hasher.Write([]byte("entryid:"))
49 hasher.Write([]byte(e.typ()))
50 hasher.Write([]byte{':'})
52 bh := sha3pool.Get256()
53 defer sha3pool.Put256(bh)
57 var innerHash [32]byte
60 hasher.Write(innerHash[:])
66 var byte32zero [32]byte
68 // mustWriteForHash serializes the object c to the writer w, from which
69 // presumably a hash can be extracted.
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 {
81 func writeForHash(w io.Writer, c interface{}) error {
82 switch v := c.(type) {
84 _, err := w.Write([]byte{v})
85 return errors.Wrap(err, "writing byte for hash")
88 binary.LittleEndian.PutUint64(buf[:], v)
89 _, err := w.Write(buf[:])
90 return errors.Wrapf(err, "writing uint64 (%d) for hash", v)
93 binary.LittleEndian.PutUint32(buf[:], v)
94 _, err := w.Write(buf[:])
95 return errors.Wrapf(err, "writing uint64 (%d) for hash", v)
97 _, err := blockchain.WriteVarstr31(w, v)
98 return errors.Wrapf(err, "writing []byte (len %d) for hash", len(v))
100 _, err := blockchain.WriteVarstrList(w, v)
101 return errors.Wrapf(err, "writing [][]byte (len %d) for hash", len(v))
103 _, err := blockchain.WriteVarstr31(w, []byte(v))
104 return errors.Wrapf(err, "writing string (len %d) for hash", len(v))
107 _, err := w.Write(byte32zero[:])
108 return errors.Wrap(err, "writing nil *Hash for hash")
110 _, err := w.Write(v.Bytes())
111 return errors.Wrap(err, "writing *Hash for hash")
114 _, err := w.Write(byte32zero[:])
115 return errors.Wrap(err, "writing nil *AssetID for hash")
117 _, err := w.Write(v.Bytes())
118 return errors.Wrap(err, "writing *AssetID for hash")
120 _, err := v.WriteTo(w)
121 return errors.Wrap(err, "writing Hash for hash")
123 _, err := v.WriteTo(w)
124 return errors.Wrap(err, "writing AssetID for hash")
127 // The two container types in the spec (List and Struct)
128 // correspond to slices and structs in Go. They can't be
129 // handled with type assertions, so we must use reflect.
130 switch v := reflect.ValueOf(c); v.Kind() {
136 return writeForHash(w, elem.Interface())
139 if _, err := blockchain.WriteVarint31(w, uint64(l)); err != nil {
140 return errors.Wrapf(err, "writing slice (len %d) for hash", l)
142 for i := 0; i < l; i++ {
144 if !c.CanInterface() {
145 return errInvalidValue
147 if err := writeForHash(w, c.Interface()); err != nil {
148 return errors.Wrapf(err, "writing slice element %d for hash", i)
155 for i := 0; i < typ.NumField(); i++ {
157 if !c.CanInterface() {
158 return errInvalidValue
160 if err := writeForHash(w, c.Interface()); err != nil {
163 return errors.Wrapf(err, "writing struct field %d (%s.%s) for hash", i, t.Name(), f.Name)
169 return errors.Wrap(fmt.Errorf("bad type %T", c))