github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/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  }