OSDN Git Service

Merge pull request #27 from Bytom/dev_modify_code
[bytom/vapor.git] / protocol / bc / entry.go
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/vapor/crypto/sha3pool"
12         "github.com/vapor/encoding/blockchain"
13         "github.com/vapor/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 uint32:
92                 buf := [8]byte{}
93                 binary.LittleEndian.PutUint32(buf[:], v)
94                 _, err := w.Write(buf[:])
95                 return errors.Wrapf(err, "writing uint64 (%d) for hash", v)
96         case []byte:
97                 _, err := blockchain.WriteVarstr31(w, v)
98                 return errors.Wrapf(err, "writing []byte (len %d) for hash", len(v))
99         case [][]byte:
100                 _, err := blockchain.WriteVarstrList(w, v)
101                 return errors.Wrapf(err, "writing [][]byte (len %d) for hash", len(v))
102         case string:
103                 _, err := blockchain.WriteVarstr31(w, []byte(v))
104                 return errors.Wrapf(err, "writing string (len %d) for hash", len(v))
105         case *Hash:
106                 if v == nil {
107                         _, err := w.Write(byte32zero[:])
108                         return errors.Wrap(err, "writing nil *Hash for hash")
109                 }
110                 _, err := w.Write(v.Bytes())
111                 return errors.Wrap(err, "writing *Hash for hash")
112         case *AssetID:
113                 if v == nil {
114                         _, err := w.Write(byte32zero[:])
115                         return errors.Wrap(err, "writing nil *AssetID for hash")
116                 }
117                 _, err := w.Write(v.Bytes())
118                 return errors.Wrap(err, "writing *AssetID for hash")
119         case Hash:
120                 _, err := v.WriteTo(w)
121                 return errors.Wrap(err, "writing Hash for hash")
122         case AssetID:
123                 _, err := v.WriteTo(w)
124                 return errors.Wrap(err, "writing AssetID for hash")
125         }
126
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() {
131         case reflect.Ptr:
132                 if v.IsNil() {
133                         return nil
134                 }
135                 elem := v.Elem()
136                 return writeForHash(w, elem.Interface())
137         case reflect.Slice:
138                 l := v.Len()
139                 if _, err := blockchain.WriteVarint31(w, uint64(l)); err != nil {
140                         return errors.Wrapf(err, "writing slice (len %d) for hash", l)
141                 }
142                 for i := 0; i < l; i++ {
143                         c := v.Index(i)
144                         if !c.CanInterface() {
145                                 return errInvalidValue
146                         }
147                         if err := writeForHash(w, c.Interface()); err != nil {
148                                 return errors.Wrapf(err, "writing slice element %d for hash", i)
149                         }
150                 }
151                 return nil
152
153         case reflect.Struct:
154                 typ := v.Type()
155                 for i := 0; i < typ.NumField(); i++ {
156                         c := v.Field(i)
157                         if !c.CanInterface() {
158                                 return errInvalidValue
159                         }
160                         if err := writeForHash(w, c.Interface()); err != nil {
161                                 t := v.Type()
162                                 f := t.Field(i)
163                                 return errors.Wrapf(err, "writing struct field %d (%s.%s) for hash", i, t.Name(), f.Name)
164                         }
165                 }
166                 return nil
167         }
168
169         return errors.Wrap(fmt.Errorf("bad type %T", c))
170 }