github.com/clarenceb/holochain-proto@v0.0.1-alpha.0.20180918132555-dff351593ded/header.go (about)

     1  // Copyright (C) 2013-2017, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //----------------------------------------------------------------------------------------
     4  
     5  // implements chain header structures & coding
     6  
     7  package holochain
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"fmt"
    13  	. "github.com/holochain/holochain-proto/hash"
    14  	b58 "github.com/jbenet/go-base58"
    15  	ic "github.com/libp2p/go-libp2p-crypto"
    16  	"io"
    17  	"time"
    18  )
    19  
    20  type Signature struct {
    21  	S []byte
    22  }
    23  
    24  // StatusChange records change of status of an entry in the header
    25  type StatusChange struct {
    26  	Action string // either AddAction, ModAction, or DelAction
    27  	Hash   Hash
    28  }
    29  
    30  // Header holds chain links, type, timestamp and signature
    31  type Header struct {
    32  	Type       string
    33  	Time       time.Time
    34  	HeaderLink Hash // link to previous header
    35  	EntryLink  Hash // link to entry
    36  	TypeLink   Hash // link to header of previous header of this type
    37  	Sig        Signature
    38  	Change     StatusChange
    39  }
    40  
    41  // newHeader makes Header object linked to a previous Header by hash
    42  func newHeader(hashSpec HashSpec, now time.Time, t string, entry Entry, privKey ic.PrivKey, prev Hash, prevType Hash, change *StatusChange) (hash Hash, header *Header, err error) {
    43  	var hd Header
    44  	hd.Type = t
    45  	now = now.Round(0)
    46  	hd.Time = now
    47  	hd.HeaderLink = prev
    48  	hd.TypeLink = prevType
    49  	if change != nil {
    50  		hd.Change = *change
    51  	} else {
    52  		hd.Change.Hash = NullHash()
    53  	}
    54  
    55  	hd.EntryLink, err = entry.Sum(hashSpec)
    56  	if err != nil {
    57  		return
    58  	}
    59  
    60  	// sign the hash of the entry
    61  	sig, err := privKey.Sign(hd.EntryLink.H)
    62  	if err != nil {
    63  		return
    64  	}
    65  	hd.Sig = Signature{S: sig}
    66  
    67  	hash, _, err = (&hd).Sum(hashSpec)
    68  	if err != nil {
    69  		return
    70  	}
    71  
    72  	header = &hd
    73  	return
    74  }
    75  
    76  // Sum encodes and creates a hash digest of the header
    77  func (hd *Header) Sum(spec HashSpec) (hash Hash, b []byte, err error) {
    78  	b, err = hd.Marshal()
    79  	if err == nil {
    80  		err = hash.Sum(spec, b)
    81  	}
    82  	return
    83  }
    84  
    85  // B58String encodes a signature as a b58string
    86  func (sig Signature) B58String() (result string) {
    87  	return b58.Encode(sig.S)
    88  }
    89  
    90  // Equal tests signature equality
    91  func (sig1 Signature) Equal(sig2 Signature) bool {
    92  	return bytes.Equal(sig1.S, sig2.S)
    93  }
    94  
    95  // SignatureFromB58String encodes a signature as a b58string
    96  func SignatureFromB58String(encoded string) (sig Signature) {
    97  	sig.S = b58.Decode(encoded)
    98  	return
    99  }
   100  
   101  // ToJSON serializes a header to JSON
   102  func (hd *Header) ToJSON() (result string, err error) {
   103  	result = fmt.Sprintf(
   104  		`{"Type":"%s","Time":"%v","EntryLink":"%s","HeaderLink":"%s","TypeLink":"%s","Signature":"%s"}`,
   105  		jsSanitizeString(hd.Type),
   106  		hd.Time,
   107  		hd.EntryLink.String(),
   108  		hd.HeaderLink.String(),
   109  		hd.TypeLink.String(),
   110  		hd.Sig.B58String(),
   111  	)
   112  	return
   113  }
   114  
   115  // Marshal writes a header to bytes
   116  func (hd *Header) Marshal() (b []byte, err error) {
   117  	var s bytes.Buffer
   118  	err = MarshalHeader(&s, hd)
   119  	if err == nil {
   120  		b = s.Bytes()
   121  	}
   122  	return
   123  }
   124  
   125  func writeStr(writer io.Writer, str string) (err error) {
   126  	var b []byte
   127  	b = []byte(str)
   128  	l := uint8(len(b))
   129  	err = binary.Write(writer, binary.LittleEndian, l)
   130  	if err != nil {
   131  		return
   132  	}
   133  	err = binary.Write(writer, binary.LittleEndian, b)
   134  	return
   135  }
   136  
   137  // MarshalHeader writes a header to a binary stream
   138  func MarshalHeader(writer io.Writer, hd *Header) (err error) {
   139  	err = writeStr(writer, hd.Type)
   140  	if err != nil {
   141  		return
   142  	}
   143  
   144  	var b []byte
   145  	b, err = hd.Time.MarshalBinary()
   146  	err = binary.Write(writer, binary.LittleEndian, b)
   147  	if err != nil {
   148  		return
   149  	}
   150  
   151  	err = hd.HeaderLink.MarshalHash(writer)
   152  	if err != nil {
   153  		return
   154  	}
   155  
   156  	err = hd.EntryLink.MarshalHash(writer)
   157  	if err != nil {
   158  		return
   159  	}
   160  
   161  	err = hd.TypeLink.MarshalHash(writer)
   162  	if err != nil {
   163  		return
   164  	}
   165  	err = MarshalSignature(writer, &hd.Sig)
   166  	if err != nil {
   167  		return
   168  	}
   169  
   170  	err = writeStr(writer, hd.Change.Action)
   171  	if err != nil {
   172  		return
   173  	}
   174  
   175  	err = hd.Change.Hash.MarshalHash(writer)
   176  	if err != nil {
   177  		return
   178  	}
   179  
   180  	// write out 0 for future expansion (meta)
   181  	z := uint64(0)
   182  	err = binary.Write(writer, binary.LittleEndian, &z)
   183  	if err != nil {
   184  		return
   185  	}
   186  	return
   187  }
   188  
   189  // Unmarshal reads a header from bytes
   190  func (hd *Header) Unmarshal(b []byte, hashSize int) (err error) {
   191  	s := bytes.NewBuffer(b)
   192  	err = UnmarshalHeader(s, hd, hashSize)
   193  	return
   194  }
   195  
   196  func readStr(reader io.Reader) (str string, err error) {
   197  	var l uint8
   198  	err = binary.Read(reader, binary.LittleEndian, &l)
   199  	if err != nil {
   200  		return
   201  	}
   202  
   203  	var b = make([]byte, l)
   204  	err = binary.Read(reader, binary.LittleEndian, b)
   205  	if err != nil {
   206  		return
   207  	}
   208  	str = string(b)
   209  	return
   210  }
   211  
   212  // UnmarshalHeader reads a Header from a binary stream
   213  func UnmarshalHeader(reader io.Reader, hd *Header, hashSize int) (err error) {
   214  
   215  	hd.Type, err = readStr(reader)
   216  	if err != nil {
   217  		return
   218  	}
   219  
   220  	var b = make([]byte, 15)
   221  	err = binary.Read(reader, binary.LittleEndian, b)
   222  	if err != nil {
   223  		return
   224  	}
   225  	hd.Time.UnmarshalBinary(b)
   226  
   227  	err = hd.HeaderLink.UnmarshalHash(reader)
   228  	if err != nil {
   229  		return
   230  	}
   231  
   232  	err = hd.EntryLink.UnmarshalHash(reader)
   233  	if err != nil {
   234  		return
   235  	}
   236  
   237  	err = hd.TypeLink.UnmarshalHash(reader)
   238  	if err != nil {
   239  		return
   240  	}
   241  
   242  	err = UnmarshalSignature(reader, &hd.Sig)
   243  	if err != nil {
   244  		return
   245  	}
   246  
   247  	hd.Change.Action, err = readStr(reader)
   248  	if err != nil {
   249  		return
   250  	}
   251  
   252  	err = hd.Change.Hash.UnmarshalHash(reader)
   253  	if err != nil {
   254  		return
   255  	}
   256  
   257  	z := uint64(0)
   258  	err = binary.Read(reader, binary.LittleEndian, &z)
   259  	if err != nil {
   260  		return
   261  	}
   262  	return
   263  }
   264  
   265  // MarshalSignature writes a signature to a binary stream
   266  func MarshalSignature(writer io.Writer, s *Signature) (err error) {
   267  	l := uint8(len(s.S))
   268  	err = binary.Write(writer, binary.LittleEndian, l)
   269  	if err != nil {
   270  		return
   271  	}
   272  	err = binary.Write(writer, binary.LittleEndian, s.S)
   273  	if err != nil {
   274  		return
   275  	}
   276  	return
   277  }
   278  
   279  // UnmarshalSignature reads a Signature from a binary stream
   280  func UnmarshalSignature(reader io.Reader, s *Signature) (err error) {
   281  	var l uint8
   282  	err = binary.Read(reader, binary.LittleEndian, &l)
   283  	if err != nil {
   284  		return
   285  	}
   286  	var b = make([]byte, l)
   287  	err = binary.Read(reader, binary.LittleEndian, b)
   288  	if err != nil {
   289  		return
   290  	}
   291  	s.S = b
   292  	return
   293  }