github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/chain.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 in-memory chain representation with marshaling, & validation
     6  
     7  package holochain
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  	"time"
    21  
    22  	ic "github.com/libp2p/go-libp2p-crypto"
    23  
    24  	. "github.com/holochain/holochain-proto/hash"
    25  )
    26  
    27  // WalkerFn a function type for call Walk
    28  type WalkerFn func(key *Hash, header *Header, entry Entry) error
    29  
    30  var ErrHashNotFound = errors.New("hash not found")
    31  var ErrIncompleteChain = errors.New("operation not allowed on incomplete chain")
    32  var ErrChainLockedForBundle = errors.New("chain locked for bundle")
    33  var ErrBundleNotStarted = errors.New("bundle not started")
    34  
    35  const (
    36  	ChainMarshalFlagsNone            = 0x00
    37  	ChainMarshalFlagsNoHeaders       = 0x01
    38  	ChainMarshalFlagsNoEntries       = 0x02
    39  	ChainMarshalFlagsOmitDNA         = 0x04
    40  	ChainMarshalFlagsNoPrivate       = 0x08
    41  	ChainMarshalPrivateEntryRedacted = "%%PRIVATE ENTRY REDACTED%%"
    42  )
    43  
    44  type Bundle struct {
    45  	idx       int
    46  	userParam string
    47  	chain     *Chain
    48  	sharing   []CommittingAction
    49  }
    50  
    51  // Chain structure for providing in-memory access to chain data, entries headers and hashes
    52  type Chain struct {
    53  	Hashes   []Hash
    54  	Headers  []*Header
    55  	Entries  []Entry
    56  	TypeTops map[string]int // pointer to index of top of a given type
    57  	Hmap     map[Hash]int   // map header hashes to index number
    58  	Emap     map[Hash]int   // map entry hashes to index number
    59  
    60  	//---
    61  
    62  	s        *os.File // if this stream is not nil, new entries will get marshaled to it
    63  	hashSpec HashSpec
    64  	lk       sync.RWMutex
    65  	bundle   *Bundle // non-nil when this chain has a bundle in progress
    66  	bundleOf *Chain  // non-nil if this chain is a bundle of a different chain
    67  }
    68  
    69  // NewChain creates and empty chain
    70  func NewChain(hashSpec HashSpec) (chain *Chain) {
    71  	c := Chain{
    72  		Headers:  make([]*Header, 0),
    73  		Entries:  make([]Entry, 0),
    74  		Hashes:   make([]Hash, 0),
    75  		TypeTops: make(map[string]int),
    76  		Hmap:     make(map[Hash]int),
    77  		Emap:     make(map[Hash]int),
    78  		hashSpec: hashSpec,
    79  	}
    80  	chain = &c
    81  	return
    82  }
    83  
    84  // NewChainFromFile creates a chain from a file, loading any data there,
    85  // and setting it to be persisted to. If no file exists it will be created.
    86  func NewChainFromFile(spec HashSpec, path string) (c *Chain, err error) {
    87  	defer func() {
    88  		if err != nil {
    89  			Debugf("error loading chain :%s", err.Error())
    90  		}
    91  	}()
    92  	c = NewChain(spec)
    93  
    94  	var f *os.File
    95  	if FileExists(path) {
    96  		f, err = os.Open(path)
    97  		if err != nil {
    98  			return
    99  		}
   100  		var i int
   101  		for {
   102  			var header *Header
   103  			var e Entry
   104  			header, e, err = readPair(ChainMarshalFlagsNone, f)
   105  			if err != nil && err.Error() == "EOF" {
   106  				err = nil
   107  				break
   108  			}
   109  			if err != nil {
   110  				Debugf("error reading pair:%s", err.Error())
   111  				return
   112  			}
   113  			c.addPair(header, e, i)
   114  			i++
   115  		}
   116  		f.Close()
   117  		i--
   118  		// if we read anything then we have to calculate the final hash and add it
   119  		if i >= 0 {
   120  			hd := c.Headers[i]
   121  			var hash Hash
   122  
   123  			// hash the header
   124  			hash, _, err = hd.Sum(spec)
   125  			if err != nil {
   126  				return
   127  			}
   128  
   129  			c.Hashes = append(c.Hashes, hash)
   130  			c.Hmap[hash] = i
   131  
   132  			// finally validate that it all hashes out correctly
   133  			/*			err = c.Validate(h)
   134  						if err != nil {
   135  							return
   136  						}
   137  			*/
   138  		}
   139  
   140  		f, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0600)
   141  		if err != nil {
   142  			return
   143  		}
   144  	} else {
   145  		f, err = os.Create(path)
   146  		if err != nil {
   147  			return
   148  		}
   149  	}
   150  	c.s = f
   151  	return
   152  }
   153  
   154  // Top returns the latest header
   155  func (c *Chain) Top() (header *Header) {
   156  	return c.Nth(0)
   157  }
   158  
   159  // Nth returns the nth latest header
   160  func (c *Chain) Nth(n int) (header *Header) {
   161  	c.lk.RLock()
   162  	defer c.lk.RUnlock()
   163  	l := len(c.Headers)
   164  	if l-n > 0 {
   165  		header = c.Headers[l-n-1]
   166  	}
   167  	return
   168  }
   169  
   170  // TopType returns the latest header of a given type
   171  func (c *Chain) TopType(entryType string) (hash *Hash, header *Header) {
   172  	c.lk.RLock()
   173  	defer c.lk.RUnlock()
   174  	i, ok := c.TypeTops[entryType]
   175  	if ok {
   176  		header = c.Headers[i]
   177  		var hs = c.Hashes[i].Clone()
   178  		hash = &hs
   179  	}
   180  	return
   181  }
   182  
   183  // AddEntry creates a new header and adds it to a chain
   184  func (c *Chain) AddEntry(now time.Time, entryType string, e Entry, privKey ic.PrivKey) (hash Hash, err error) {
   185  	c.lk.Lock()
   186  	defer c.lk.Unlock()
   187  	var l int
   188  	var header *Header
   189  	now = now.Round(0)
   190  	l, hash, header, err = c.prepareHeader(now, entryType, e, privKey, NullHash())
   191  	if err == nil {
   192  		err = c.addEntry(l, hash, header, e)
   193  	}
   194  	return
   195  }
   196  
   197  // prepareHeader builds a header that could be added to the chain.
   198  // Not thread safe, this must be called with the chain locked for writing so something else
   199  // doesn't get inserted
   200  func (c *Chain) prepareHeader(now time.Time, entryType string, e Entry, privKey ic.PrivKey, change Hash) (entryIdx int, hash Hash, header *Header, err error) {
   201  
   202  	if c.BundleStarted() != nil {
   203  		err = ErrChainLockedForBundle
   204  		return
   205  	}
   206  	// get the previous hashes
   207  	var ph, pth Hash
   208  
   209  	l := len(c.Hashes)
   210  	if l == 0 {
   211  		if c.bundleOf != nil {
   212  			l := len(c.bundleOf.Hashes)
   213  			if l == 0 {
   214  				ph = NullHash()
   215  			} else {
   216  				ph = c.bundleOf.Hashes[l-1]
   217  			}
   218  		} else {
   219  			ph = NullHash()
   220  		}
   221  	} else {
   222  		ph = c.Hashes[l-1]
   223  	}
   224  
   225  	i, ok := c.TypeTops[entryType]
   226  	if !ok {
   227  		if c.bundleOf != nil {
   228  			i, ok = c.bundleOf.TypeTops[entryType]
   229  			if !ok {
   230  				pth = NullHash()
   231  			} else {
   232  				pth = c.bundleOf.Hashes[i]
   233  			}
   234  		} else {
   235  			pth = NullHash()
   236  		}
   237  	} else {
   238  		pth = c.Hashes[i]
   239  	}
   240  
   241  	hash, header, err = newHeader(c.hashSpec, now, entryType, e, privKey, ph, pth, change)
   242  	if err != nil {
   243  		return
   244  	}
   245  	entryIdx = l
   246  	return
   247  }
   248  
   249  // addEntry, low level entry add, not thread safe, must call c.lock in the calling funciton
   250  func (c *Chain) addEntry(entryIdx int, hash Hash, header *Header, e Entry) (err error) {
   251  	if c.BundleStarted() != nil {
   252  		err = ErrChainLockedForBundle
   253  		return
   254  	}
   255  	l := len(c.Hashes)
   256  	if l != entryIdx {
   257  		err = errors.New("entry indexes don't match can't create new entry")
   258  		return
   259  	}
   260  
   261  	if l != len(c.Entries) {
   262  		err = ErrIncompleteChain
   263  		return
   264  	}
   265  
   266  	var g GobEntry
   267  	g = *e.(*GobEntry)
   268  
   269  	c.Hashes = append(c.Hashes, hash)
   270  	c.Headers = append(c.Headers, header)
   271  	c.Entries = append(c.Entries, &g)
   272  	c.TypeTops[header.Type] = entryIdx
   273  	c.Emap[header.EntryLink] = entryIdx
   274  	c.Hmap[hash] = entryIdx
   275  
   276  	if c.s != nil {
   277  		err = writePair(c.s, header, &g)
   278  	}
   279  
   280  	return
   281  }
   282  
   283  // Get returns the header of a given hash
   284  func (c *Chain) Get(h Hash) (header *Header, err error) {
   285  	c.lk.RLock()
   286  	defer c.lk.RUnlock()
   287  	i, ok := c.Hmap[h]
   288  	if ok {
   289  		header = c.Headers[i]
   290  	} else {
   291  		err = ErrHashNotFound
   292  	}
   293  	return
   294  }
   295  
   296  // GetEntry returns the entry of a given entry hash
   297  func (c *Chain) GetEntry(h Hash) (entry Entry, entryType string, err error) {
   298  	c.lk.RLock()
   299  	defer c.lk.RUnlock()
   300  	i, ok := c.Emap[h]
   301  	if ok {
   302  		entry = c.Entries[i]
   303  		entryType = c.Headers[i].Type
   304  	} else {
   305  		err = ErrHashNotFound
   306  	}
   307  	return
   308  }
   309  
   310  // GetEntryHeader returns the header of a given entry hash
   311  func (c *Chain) GetEntryHeader(h Hash) (header *Header, err error) {
   312  	c.lk.RLock()
   313  	defer c.lk.RUnlock()
   314  	i, ok := c.Emap[h]
   315  	if ok {
   316  		header = c.Headers[i]
   317  	} else {
   318  		err = ErrHashNotFound
   319  	}
   320  	return
   321  }
   322  
   323  func writePair(writer io.Writer, header *Header, entry Entry) (err error) {
   324  	if header != nil {
   325  		err = MarshalHeader(writer, header)
   326  		if err != nil {
   327  			return
   328  		}
   329  	}
   330  	if entry != nil {
   331  		err = MarshalEntry(writer, entry)
   332  	}
   333  	return
   334  }
   335  
   336  func readPair(flags int64, reader io.Reader) (header *Header, entry Entry, err error) {
   337  	if (flags & ChainMarshalFlagsNoHeaders) == 0 {
   338  		var hd Header
   339  		err = UnmarshalHeader(reader, &hd, 34)
   340  		if err != nil {
   341  			return
   342  		}
   343  		header = &hd
   344  	}
   345  	if (flags & ChainMarshalFlagsNoEntries) == 0 {
   346  		entry, err = UnmarshalEntry(reader)
   347  	}
   348  	return
   349  }
   350  
   351  func contains(s []string, e string) bool {
   352  	for _, a := range s {
   353  		if a == e {
   354  			return true
   355  		}
   356  	}
   357  	return false
   358  }
   359  
   360  func filterPass(index int, header *Header, whitelistTypes []string, blacklistTypes []string) bool {
   361  	pass := true
   362  	if len(whitelistTypes) > 0 {
   363  		pass = contains(whitelistTypes, header.Type)
   364  	}
   365  
   366  	if len(blacklistTypes) > 0 {
   367  		pass = pass && !contains(blacklistTypes, header.Type)
   368  	}
   369  	return pass
   370  }
   371  
   372  type ChainPair struct {
   373  	Header *Header
   374  	Entry  Entry
   375  }
   376  
   377  // MarshalChain serializes a chain data to a writer
   378  func (c *Chain) MarshalChain(writer io.Writer, flags int64, whitelistTypes []string, privateTypes []string) (err error) {
   379  	c.lk.RLock()
   380  	defer c.lk.RUnlock()
   381  
   382  	if len(c.Headers) != len(c.Entries) {
   383  		err = ErrIncompleteChain
   384  		return
   385  	}
   386  
   387  	err = binary.Write(writer, binary.LittleEndian, flags)
   388  	if err != nil {
   389  		return err
   390  	}
   391  
   392  	var pairsToWrite []ChainPair
   393  	var lastHeaderToWrite int
   394  
   395  	for i, hdr := range c.Headers {
   396  		var empty []string
   397  		var e Entry
   398  
   399  		if i == 0 || filterPass(i, hdr, whitelistTypes, empty) {
   400  			e = c.Entries[i]
   401  
   402  			if (i == 0) && ((flags & ChainMarshalFlagsOmitDNA) != 0) {
   403  				e = &GobEntry{C: ""}
   404  			}
   405  
   406  			if (flags & ChainMarshalFlagsNoEntries) != 0 {
   407  				e = nil
   408  			}
   409  
   410  			if !filterPass(i, hdr, whitelistTypes, privateTypes) {
   411  				e = &GobEntry{C: ChainMarshalPrivateEntryRedacted}
   412  			}
   413  
   414  			if (flags & ChainMarshalFlagsNoHeaders) != 0 {
   415  				hdr = nil
   416  			} else {
   417  				lastHeaderToWrite = i
   418  			}
   419  
   420  			pairsToWrite = append(pairsToWrite, ChainPair{Header: hdr, Entry: e})
   421  		}
   422  	}
   423  
   424  	err = binary.Write(writer, binary.LittleEndian, int64(len(pairsToWrite)))
   425  	if err != nil {
   426  		return err
   427  	}
   428  
   429  	for _, pair := range pairsToWrite {
   430  		err = writePair(writer, pair.Header, pair.Entry)
   431  		if err != nil {
   432  			return
   433  		}
   434  	}
   435  
   436  	if (flags & ChainMarshalFlagsNoHeaders) == 0 {
   437  		hash := c.Hashes[lastHeaderToWrite]
   438  		err = hash.MarshalHash(writer)
   439  	}
   440  	return
   441  }
   442  
   443  // addPair adds header and entry pairs to the chain during unmarshaling
   444  // This call assumes that Hashes array is one element behind the Headers and Entries
   445  // because for each pair (except the 0th) it adds the hash of the previous entry
   446  // thus it also means that you must add the last Hash after you have finished calling addPair
   447  func (c *Chain) addPair(header *Header, entry Entry, i int) {
   448  	if header != nil {
   449  		if i > 0 {
   450  			h := header.HeaderLink
   451  			c.Hashes = append(c.Hashes, h)
   452  			c.Hmap[h] = i - 1
   453  		}
   454  		c.Headers = append(c.Headers, header)
   455  		c.TypeTops[header.Type] = i
   456  		c.Emap[header.EntryLink] = i
   457  	}
   458  	if entry != nil {
   459  		c.Entries = append(c.Entries, entry)
   460  	}
   461  }
   462  
   463  // UnmarshalChain unserializes a chain from a reader
   464  func UnmarshalChain(hashSpec HashSpec, reader io.Reader) (flags int64, c *Chain, err error) {
   465  	defer func() {
   466  		if err != nil {
   467  			Debugf("error unmarshaling chain:%s", err.Error())
   468  		}
   469  	}()
   470  	c = NewChain(hashSpec)
   471  	err = binary.Read(reader, binary.LittleEndian, &flags)
   472  	if err != nil {
   473  		return
   474  	}
   475  	var l, i int64
   476  	err = binary.Read(reader, binary.LittleEndian, &l)
   477  	if err != nil {
   478  		return
   479  	}
   480  	for i = 0; i < l; i++ {
   481  		var header *Header
   482  		var e Entry
   483  		header, e, err = readPair(flags, reader)
   484  		if err != nil {
   485  			return
   486  		}
   487  		c.addPair(header, e, int(i))
   488  	}
   489  
   490  	if (flags & ChainMarshalFlagsNoHeaders) == 0 {
   491  		// decode final hash
   492  		var h Hash
   493  		h, err = UnmarshalHash(reader)
   494  		if err != nil {
   495  			return
   496  		}
   497  		c.Hashes = append(c.Hashes, h)
   498  		c.Hmap[h] = int(i - 1)
   499  	}
   500  	return
   501  }
   502  
   503  // Walk traverses chain from most recent to first entry calling fn on each one
   504  func (c *Chain) Walk(fn WalkerFn) (err error) {
   505  	l := len(c.Headers)
   506  	for i := l - 1; i >= 0; i-- {
   507  		err = fn(&c.Hashes[i], c.Headers[i], c.Entries[i])
   508  		if err != nil {
   509  			return
   510  		}
   511  	}
   512  	return
   513  }
   514  
   515  // Validate traverses chain confirming the hashes
   516  // @TODO confirm that TypeLinks are also correct
   517  // @TODO confirm signatures
   518  func (c *Chain) Validate(skipEntries bool) (err error) {
   519  	c.lk.RLock()
   520  	defer c.lk.RUnlock()
   521  	l := len(c.Headers)
   522  	for i := 0; i < l; i++ {
   523  		hd := c.Headers[i]
   524  
   525  		var hash, nexth Hash
   526  		// hash the header
   527  		hash, _, err = hd.Sum(c.hashSpec)
   528  		if err != nil {
   529  			return
   530  		}
   531  		// we can't compare top hash to next link, because it doesn't exist yet!
   532  		if i < l-2 {
   533  			nexth = c.Headers[i+1].HeaderLink
   534  		} else {
   535  			// so get it from the Hashes (even though this could be cheated)
   536  			nexth = c.Hashes[i]
   537  		}
   538  
   539  		if !hash.Equal(nexth) {
   540  			err = fmt.Errorf("header hash mismatch at link %d", i)
   541  			return
   542  		}
   543  
   544  		if !skipEntries {
   545  			var b []byte
   546  			b, err = c.Entries[i].Marshal()
   547  			if err != nil {
   548  				return
   549  			}
   550  			hash, err = Sum(c.hashSpec, b)
   551  			if err != nil {
   552  				return
   553  			}
   554  
   555  			if !hash.Equal(hd.EntryLink) {
   556  				err = fmt.Errorf("entry hash mismatch at link %d", i)
   557  				return
   558  			}
   559  		}
   560  	}
   561  	return
   562  }
   563  
   564  // String converts a chain to a textual dump of the headers and entries
   565  func (c *Chain) String() string {
   566  	return c.Dump(0)
   567  }
   568  
   569  // Dump converts a chain to a textual dump of the headers and entries from a starting index
   570  func (c *Chain) Dump(start int) string {
   571  	c.lk.RLock()
   572  	defer c.lk.RUnlock()
   573  	l := len(c.Headers)
   574  	r := ""
   575  	for i := start; i < l; i++ {
   576  		hdr := c.Headers[i]
   577  		hash := c.Hashes[i]
   578  		r += fmt.Sprintf("%s:%s @ %v\n", hdr.Type, hash, hdr.Time)
   579  		r += fmt.Sprintf("    Sig: %v\n", hdr.Sig)
   580  		r += fmt.Sprintf("    Next Header: %v\n", hdr.HeaderLink)
   581  		r += fmt.Sprintf("    Next %s: %v\n", hdr.Type, hdr.TypeLink)
   582  		r += fmt.Sprintf("    Entry: %v\n", hdr.EntryLink)
   583  		e := c.Entries[i]
   584  		switch hdr.Type {
   585  		case KeyEntryType:
   586  			r += fmt.Sprintf("       %v\n", e.(*GobEntry).C)
   587  		case DNAEntryType:
   588  			r += fmt.Sprintf("       %s\n", e.(*GobEntry).C)
   589  		case AgentEntryType:
   590  			r += fmt.Sprintf("       %v\n", e.(*GobEntry).C)
   591  		case MigrateEntryType:
   592  			r += fmt.Sprintf("       %v\n", e.(*GobEntry).C)
   593  		default:
   594  			r += fmt.Sprintf("       %v\n", e)
   595  		}
   596  		r += "\n"
   597  	}
   598  	return r
   599  }
   600  
   601  // JSON converts a chain to a json string dump of the headers and entries
   602  func (c *Chain) JSON(start int) (string, error) {
   603  	c.lk.RLock()
   604  	defer c.lk.RUnlock()
   605  	l := len(c.Headers)
   606  	firstEntry := false
   607  	lastEntry := false
   608  
   609  	var buffer bytes.Buffer
   610  
   611  	buffer.WriteString("{")
   612  
   613  	for i := start; i < l; i++ {
   614  		hdr := c.Headers[i]
   615  		hash := c.Hashes[i]
   616  
   617  		e := c.Entries[i]
   618  		lastEntry = (i == l-1)
   619  
   620  		switch hdr.Type {
   621  		case KeyEntryType, AgentEntryType, DNAEntryType:
   622  			buffer.WriteString("\"" + hdr.Type + "\":")
   623  			appendEntryAsJSON(&buffer, hdr, &hash, e.(*GobEntry))
   624  			if !lastEntry {
   625  				buffer.WriteString(",")
   626  			}
   627  		default:
   628  			if !firstEntry {
   629  				buffer.WriteString("\"entries\":[")
   630  				firstEntry = true
   631  			}
   632  
   633  			appendEntryAsJSON(&buffer, hdr, &hash, e.(*GobEntry))
   634  
   635  			if lastEntry {
   636  				buffer.WriteString("]")
   637  			} else {
   638  				buffer.WriteString(",")
   639  			}
   640  		}
   641  	}
   642  
   643  	buffer.WriteString("}")
   644  	return PrettyPrintJSON(buffer.Bytes())
   645  }
   646  
   647  // Dot converts a chain to a GraphViz 'dot' format dump of the headers and entries
   648  func (c *Chain) Dot(start int) (dump string, err error) {
   649  	c.lk.RLock()
   650  	defer c.lk.RUnlock()
   651  	l := len(c.Headers)
   652  
   653  	var buffer bytes.Buffer
   654  
   655  	buffer.WriteString("digraph chain {\n")
   656  	buffer.WriteString("graph [splines=line];\n")
   657  	buffer.WriteString(`node [shape=record fontname="Arial",fontsize="10",style="rounded, filled",penwidth=2,fontcolor="#c5c5c5",color="#8d00ff",fillcolor="#181818"];` + "\n")
   658  	buffer.WriteString(`edge [penwidth=2, color="#8d00ff"];` + "\n")
   659  
   660  	for i := start; i < l; i++ {
   661  		hdr := c.Headers[i]
   662  		hash := c.Hashes[i]
   663  		headerLabel := ""
   664  		contentLabel := ""
   665  		contentBody := ""
   666  
   667  		if i == 0 {
   668  			headerLabel = ": GENESIS"
   669  		}
   670  
   671  		// header
   672  		buffer.WriteString(fmt.Sprintf("header%d [label=<{HEADER %d%s|\n", i, i, headerLabel))
   673  		buffer.WriteString(fmt.Sprintf("{Type|%s}|\n", hdr.Type))
   674  		buffer.WriteString(fmt.Sprintf("{Hash|%s}|\n", hash))
   675  		buffer.WriteString(fmt.Sprintf("{Timestamp|%v}|\n", hdr.Time))
   676  		buffer.WriteString(fmt.Sprintf("{Next Header|%v}|\n", hdr.HeaderLink))
   677  		buffer.WriteString(fmt.Sprintf("{Next|%s: %v}|\n", hdr.Type, hdr.TypeLink))
   678  		buffer.WriteString(fmt.Sprintf("{Entry|%v}\n", hdr.EntryLink))
   679  		buffer.WriteString("}>];\n")
   680  
   681  		if i == 0 {
   682  			contentLabel = "HOLOCHAIN DNA"
   683  		} else if i == 1 {
   684  			contentLabel = "AGENT ID"
   685  		} else {
   686  			contentLabel = fmt.Sprintf("ENTRY %d", i)
   687  		}
   688  
   689  		if i == 0 {
   690  			contentBody = "See dna.json"
   691  		} else {
   692  			e := c.Entries[i]
   693  			contentBody = fmt.Sprintf("%s", e.(*GobEntry).C)
   694  			contentBody = strings.Replace(contentBody, `{"`, `\{"`, -1)
   695  			contentBody = strings.Replace(contentBody, `"}`, `"\}`, -1)
   696  			contentBody = strings.Replace(contentBody, `:[`, `:[<br/>`, -1)
   697  			contentBody = strings.Replace(contentBody, `]}`, `]\}`, -1)
   698  			contentBody = strings.Replace(contentBody, `,`, `,<br/>`, -1)
   699  		}
   700  
   701  		buffer.WriteString(fmt.Sprintf("content%d [label=<{%s|%s}>];\n", i, contentLabel, contentBody))
   702  
   703  		// arrows
   704  		buffer.WriteString(fmt.Sprintf("header%d->content%d;\n", i, i))
   705  		if i < l-1 {
   706  			buffer.WriteString(fmt.Sprintf("header%d->header%d;\n", i, i+1))
   707  		}
   708  	}
   709  
   710  	buffer.WriteString("}")
   711  	return buffer.String(), nil
   712  }
   713  
   714  // Length returns the number of entries in the chain
   715  func (c *Chain) Length() int {
   716  	return len(c.Headers)
   717  }
   718  
   719  // BundleStarted returns the index of the chain item before the bundle or 0 if no bundle is active
   720  func (c *Chain) BundleStarted() *Bundle {
   721  	return c.bundle
   722  }
   723  
   724  // StartBundle marks a bundle start point and returns an error if already started
   725  func (c *Chain) StartBundle(userParam interface{}) (err error) {
   726  	j, err := json.Marshal(userParam)
   727  	if err != nil {
   728  		return
   729  	}
   730  	c.lk.RLock()
   731  	defer c.lk.RUnlock()
   732  	if c.BundleStarted() != nil {
   733  		err = errors.New("Bundle already started")
   734  		return
   735  	}
   736  	bundle := Bundle{
   737  		idx:       c.Length() - 1,
   738  		chain:     NewChain(c.hashSpec),
   739  		userParam: string(j),
   740  	}
   741  	bundle.sharing = make([]CommittingAction, 0)
   742  	bundle.chain.bundleOf = c
   743  	c.bundle = &bundle
   744  	return
   745  }
   746  
   747  // CloseBundle closes a started bundle and if commit
   748  // copies entries from the bundle onto the chain
   749  func (c *Chain) CloseBundle(commit bool) (err error) {
   750  	c.lk.RLock()
   751  	defer c.lk.RUnlock()
   752  	if c.bundle == nil {
   753  		err = ErrBundleNotStarted
   754  		return
   755  	}
   756  	bundle := c.bundle
   757  	c.bundle = nil
   758  	if commit {
   759  		l := c.Length()
   760  		for i, header := range bundle.chain.Headers {
   761  			err = c.addEntry(i+l, bundle.chain.Hashes[i], header, bundle.chain.Entries[i])
   762  			if err != nil {
   763  				return
   764  			}
   765  		}
   766  	}
   767  	return
   768  }
   769  
   770  // Close the chain's file
   771  func (c *Chain) Close() {
   772  	c.s.Close()
   773  	c.s = nil
   774  }
   775  
   776  func appendEntryAsJSON(buffer *bytes.Buffer, hdr *Header, hash *Hash, g *GobEntry) {
   777  	buffer.WriteString("{")
   778  	appendEntryHeaderAsJSON(buffer, hdr, hash)
   779  	buffer.WriteString(",")
   780  	appendEntryContentAsJSON(buffer, hdr, g)
   781  	buffer.WriteString("}")
   782  }
   783  
   784  func appendEntryHeaderAsJSON(buffer *bytes.Buffer, hdr *Header, hash *Hash) {
   785  	buffer.WriteString("\"header\":{")
   786  	buffer.WriteString("\"type\":" + "\"" + hdr.Type + "\",")
   787  	buffer.WriteString(fmt.Sprintf("\"signature\":\"%v\",", hdr.Sig))
   788  	buffer.WriteString(fmt.Sprintf("\"hash\":\"%v\",", hash))
   789  	buffer.WriteString(fmt.Sprintf("\"time\":\"%v\",", hdr.Time))
   790  	buffer.WriteString(fmt.Sprintf("\"nextHeader\":\"%v\",", hdr.HeaderLink))
   791  	buffer.WriteString(fmt.Sprintf("\"next\":\"%v: %v\",", hdr.Type, hdr.TypeLink))
   792  	buffer.WriteString(fmt.Sprintf("\"entry\":\"%v\"", hdr.EntryLink))
   793  	buffer.WriteString("}")
   794  }
   795  
   796  func appendEntryContentAsJSON(buffer *bytes.Buffer, hdr *Header, g *GobEntry) {
   797  	buffer.WriteString("\"content\":")
   798  	buffer.WriteString(jsonEncode(g))
   799  }
   800  
   801  func jsonEncode(g *GobEntry) (encodedValue string) {
   802  	var err error
   803  	switch g.C.(type) {
   804  	case []byte:
   805  		var decoded map[string]interface{}
   806  		content := fmt.Sprintf("%s", g.C)
   807  		buffer := bytes.NewBufferString(content)
   808  		err = Decode(buffer, "json", &decoded)
   809  
   810  		if err != nil {
   811  			// DNA content may be TOML or YAML encoded, so escape it to make it JSON safe.
   812  			// (an improvement could be to convert from TOML/YAML to JSON)
   813  			encodedValue = strconv.Quote(content)
   814  		} else {
   815  			// DNA content is already in JSON so just use it.
   816  			encodedValue = content
   817  		}
   818  	default:
   819  		var result []byte
   820  		result, err = json.Marshal(g.C)
   821  		if err != nil {
   822  			encodedValue = strconv.Quote(err.Error())
   823  			return
   824  		}
   825  		encodedValue = string(result)
   826  	}
   827  	return
   828  }