github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/tstore/anchor.go (about)

     1  // Copyright (c) 2020-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package tstore
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/sha256"
    10  	"encoding/base64"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"time"
    16  
    17  	dcrtime "github.com/decred/dcrtime/api/v2"
    18  	"github.com/decred/dcrtime/merkle"
    19  	"github.com/decred/politeia/politeiad/backendv2/tstorebe/store"
    20  	"github.com/decred/politeia/politeiad/backendv2/tstorebe/tlog"
    21  	"github.com/decred/politeia/util"
    22  	"github.com/google/trillian"
    23  	"github.com/google/trillian/types"
    24  	"google.golang.org/grpc/codes"
    25  )
    26  
    27  const (
    28  	// anchorSchedule determines how often we anchor records. dcrtime
    29  	// currently drops an anchor on the hour mark so we submit new
    30  	// anchors a few minutes prior to that.
    31  	// Seconds Minutes Hours Days Months DayOfWeek
    32  	anchorSchedule = "0 56 * * * *" // At minute 56 of every hour
    33  
    34  	// anchorID is included in the timestamp and verify requests as a
    35  	// unique identifier.
    36  	anchorID = "tstorebe"
    37  )
    38  
    39  // anchor represents an anchor, i.e. timestamp, of a trillian tree at a
    40  // specific tree size. The LogRootV1.RootHash is the merkle root hash of a
    41  // trillian tree. This root hash is submitted to dcrtime to be anchored and is
    42  // the anchored digest in the VerifyDigest. Only the root hash is anchored, but
    43  // the full LogRootV1 struct is saved as part of an anchor record so that it
    44  // can be used to retrieve inclusion proofs for any leaves that were included
    45  // in the root hash.
    46  type anchor struct {
    47  	TreeID       int64                 `json:"treeid"`
    48  	LogRoot      *types.LogRootV1      `json:"logroot"`
    49  	VerifyDigest *dcrtime.VerifyDigest `json:"verifydigest"`
    50  }
    51  
    52  // droppingAnchorGet returns the dropping anchor boolean, which is used to
    53  // prevent reentrant anchor drops.
    54  func (t *Tstore) droppingAnchorGet() bool {
    55  	t.Lock()
    56  	defer t.Unlock()
    57  
    58  	return t.droppingAnchor
    59  }
    60  
    61  // droppingAnchorSet sets the dropping anchor boolean, which is used to prevent
    62  // reentrant anchor drops.
    63  func (t *Tstore) droppingAnchorSet(b bool) {
    64  	t.Lock()
    65  	defer t.Unlock()
    66  
    67  	t.droppingAnchor = b
    68  }
    69  
    70  var (
    71  	// errAnchorNotFound is returned when a anchor record does not
    72  	// exist for a leaf yet.
    73  	errAnchorNotFound = errors.New("anchor not found")
    74  )
    75  
    76  // anchorForLeaf returns the anchor for a specific merkle leaf hash.
    77  func (t *Tstore) anchorForLeaf(treeID int64, merkleLeafHash []byte, leaves []*trillian.LogLeaf) (*anchor, error) {
    78  	// Find the leaf for the provided merkle leaf hash
    79  	var l *trillian.LogLeaf
    80  	for i, v := range leaves {
    81  		if bytes.Equal(v.MerkleLeafHash, merkleLeafHash) {
    82  			l = v
    83  			// Sanity check
    84  			if l.LeafIndex != int64(i) {
    85  				return nil, fmt.Errorf("unexpected leaf index: got %v, want %v",
    86  					l.LeafIndex, i)
    87  			}
    88  			break
    89  		}
    90  	}
    91  	if l == nil {
    92  		return nil, fmt.Errorf("leaf not found")
    93  	}
    94  
    95  	// Find the first two anchor that occurs after the leaf. If the
    96  	// leaf was added in the middle of an anchor drop then it will not
    97  	// be part of the first anchor. It will be part of the second
    98  	// anchor.
    99  	keys := make([]string, 0, 2)
   100  	for i := int(l.LeafIndex); i < len(leaves); i++ {
   101  		l := leaves[i]
   102  		ed, err := extraDataDecode(l.ExtraData)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  		if ed.Desc == dataDescriptorAnchor {
   107  			keys = append(keys, ed.storeKey())
   108  			if len(keys) == 2 {
   109  				break
   110  			}
   111  		}
   112  	}
   113  	if len(keys) == 0 {
   114  		// This leaf has not been anchored yet
   115  		return nil, errAnchorNotFound
   116  	}
   117  
   118  	// Get the anchor records
   119  	blobs, err := t.store.Get(keys)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("store Get: %v", err)
   122  	}
   123  	if len(blobs) != len(keys) {
   124  		return nil, fmt.Errorf("unexpected blobs count: got %v, want %v",
   125  			len(blobs), len(keys))
   126  	}
   127  
   128  	// Find the correct anchor for the leaf
   129  	var leafAnchor *anchor
   130  	for _, v := range keys {
   131  		b, ok := blobs[v]
   132  		if !ok {
   133  			return nil, fmt.Errorf("blob not found %v", v)
   134  		}
   135  		be, err := store.Deblob(b)
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  		a, err := convertAnchorFromBlobEntry(*be)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  		if uint64(l.LeafIndex) < a.LogRoot.TreeSize {
   144  			// The leaf is included in this anchor. We're done.
   145  			leafAnchor = a
   146  			break
   147  		}
   148  	}
   149  	if leafAnchor == nil {
   150  		// This leaf has not been anchored yet
   151  		return nil, errAnchorNotFound
   152  	}
   153  
   154  	return leafAnchor, nil
   155  }
   156  
   157  // anchorLatest returns the most recent anchor for the provided tree. A
   158  // errAnchorNotFound is returned if no anchor is found.
   159  func (t *Tstore) anchorLatest(treeID int64) (*anchor, error) {
   160  	// Get tree leaves
   161  	leavesAll, err := t.tlog.LeavesAll(treeID)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	// Find the most recent anchor leaf
   167  	var key string
   168  	for i := len(leavesAll) - 1; i >= 0; i-- {
   169  		ed, err := extraDataDecode(leavesAll[i].ExtraData)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		if ed.Desc == dataDescriptorAnchor {
   174  			key = ed.storeKey()
   175  			break
   176  		}
   177  	}
   178  	if key == "" {
   179  		return nil, errAnchorNotFound
   180  	}
   181  
   182  	// Pull blob from key-value store
   183  	blobs, err := t.store.Get([]string{key})
   184  	if err != nil {
   185  		return nil, fmt.Errorf("store Get: %v", err)
   186  	}
   187  	if len(blobs) != 1 {
   188  		return nil, fmt.Errorf("unexpected blobs count: got %v, want 1",
   189  			len(blobs))
   190  	}
   191  	b, ok := blobs[key]
   192  	if !ok {
   193  		return nil, fmt.Errorf("blob not found %v", key)
   194  	}
   195  	be, err := store.Deblob(b)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	a, err := convertAnchorFromBlobEntry(*be)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	return a, nil
   205  }
   206  
   207  // anchorSave saves an anchor to the key-value store and appends a log leaf
   208  // to the trillian tree for the anchor.
   209  func (t *Tstore) anchorSave(a anchor) error {
   210  	// Sanity checks
   211  	switch {
   212  	case a.TreeID == 0:
   213  		return fmt.Errorf("invalid tree id of 0")
   214  	case a.LogRoot == nil:
   215  		return fmt.Errorf("log root not found")
   216  	case a.VerifyDigest == nil:
   217  		return fmt.Errorf("verify digest not found")
   218  	}
   219  
   220  	// Save anchor record to the kv store
   221  	be, err := convertBlobEntryFromAnchor(a)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	b, err := store.Blobify(*be)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	key := storeKeyNew(false)
   230  	kv := map[string][]byte{key: b}
   231  	err = t.store.Put(kv, false)
   232  	if err != nil {
   233  		return fmt.Errorf("store Put: %v", err)
   234  	}
   235  
   236  	// Append anchor leaf to tlog
   237  	d, err := hex.DecodeString(be.Digest)
   238  	if err != nil {
   239  		return err
   240  	}
   241  	extraData, err := extraDataEncode(key, dataDescriptorAnchor, 0)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	leaves := []*trillian.LogLeaf{
   246  		tlog.NewLogLeaf(d, extraData),
   247  	}
   248  	queued, _, err := t.tlog.LeavesAppend(a.TreeID, leaves)
   249  	if err != nil {
   250  		return fmt.Errorf("LeavesAppend: %v", err)
   251  	}
   252  	if len(queued) != 1 {
   253  		return fmt.Errorf("wrong number of queud leaves: got %v, want 1",
   254  			len(queued))
   255  	}
   256  	failed := make([]string, 0, len(queued))
   257  	for _, v := range queued {
   258  		c := codes.Code(v.QueuedLeaf.GetStatus().GetCode())
   259  		if c != codes.OK {
   260  			failed = append(failed, fmt.Sprintf("%v", c))
   261  		}
   262  	}
   263  	if len(failed) > 0 {
   264  		return fmt.Errorf("append leaves failed: %v", failed)
   265  	}
   266  
   267  	log.Debugf("Anchor saved for tree %v at height %v",
   268  		a.TreeID, a.LogRoot.TreeSize)
   269  
   270  	return nil
   271  }
   272  
   273  // anchorWait waits for the anchor to drop. The anchor is not considered
   274  // dropped until dcrtime returns the ChainTimestamp in the reply. dcrtime does
   275  // not return the ChainTimestamp until the timestamp transaction has 6
   276  // confirmations. Once the timestamp has been dropped, the anchor record is
   277  // saved to the tstore, which means that an anchor leaf will be appended onto
   278  // all trees that were anchored and the anchor records saved to the kv store.
   279  func (t *Tstore) anchorWait(anchors []anchor, digests []string) {
   280  	// Verify we are not reentrant
   281  	if t.droppingAnchorGet() {
   282  		log.Errorf("waitForAchor: called reentrantly")
   283  		return
   284  	}
   285  
   286  	// We are now condsidered to be dropping an anchor
   287  	t.droppingAnchorSet(true)
   288  
   289  	// Whatever happens in this function we must clear droppingAnchor
   290  	var exitErr error
   291  	defer func() {
   292  		t.droppingAnchorSet(false)
   293  
   294  		if exitErr != nil {
   295  			log.Errorf("anchorWait: %v", exitErr)
   296  		}
   297  	}()
   298  
   299  	// Wait for anchor to drop
   300  	log.Infof("Waiting for anchor to drop")
   301  
   302  	// Continually check with dcrtime if the anchor has been dropped.
   303  	// The anchor is not considered dropped until the ChainTimestamp
   304  	// field of the dcrtime reply has been populated. dcrtime only
   305  	// populates the ChainTimestamp field once the dcr transaction has
   306  	// 6 confirmations.
   307  	var (
   308  		// The max retry period is set to 180 minutes to ensure that
   309  		// enough time is given for the anchor transaction to receive 6
   310  		// confirmations. This is based on the fact that each block has
   311  		// a 99.75% chance of being mined within 30 minutes.
   312  		period  = 5 * time.Minute             // check every 5 minute
   313  		retries = 180 / int(period.Minutes()) // for up to 180 minutes
   314  		ticker  = time.NewTicker(period)
   315  	)
   316  	defer ticker.Stop()
   317  	for try := 0; try < retries; try++ {
   318  		<-ticker.C
   319  
   320  		log.Debugf("Verify anchor attempt %v/%v", try+1, retries)
   321  
   322  		vbr, err := t.dcrtime.verifyBatch(anchorID, digests)
   323  		if err != nil {
   324  			exitErr = fmt.Errorf("verifyBatch: %v", err)
   325  			return
   326  		}
   327  
   328  		// We must wait until all digests have been anchored. Under
   329  		// normal circumstances this will happen during the same dcrtime
   330  		// transaction, but its possible for some of the digests to have
   331  		// already been anchored in previous transactions if politeiad
   332  		// was shutdown in the middle of the anchoring process.
   333  		//
   334  		// Ex: politeiad submits a digest for treeA to dcrtime. politeiad
   335  		// gets shutdown before an anchor record is added to treeA.
   336  		// dcrtime timestamps the treeA digest into block 1000. politeiad
   337  		// gets turned back on and a new record, treeB, is submitted
   338  		// prior to an anchor drop attempt. On the next anchor drop,
   339  		// politeiad will try to drop an anchor for both treeA and treeB
   340  		// since treeA is still considered unachored, however, when this
   341  		// part of the code gets hit dcrtime will immediately return a
   342  		// valid timestamp for treeA since it was already timestamped
   343  		// into block 1000. In this situation, the verify loop must also
   344  		// wait for treeB to be timestamped by dcrtime before continuing.
   345  		anchored := make(map[string]struct{}, len(digests))
   346  		for _, v := range vbr.Digests {
   347  			if v.Result != dcrtime.ResultOK {
   348  				// Something is wrong. Log the error and retry.
   349  				log.Errorf("Digest %v: %v (%v)",
   350  					v.Digest, dcrtime.Result[v.Result], v.Result)
   351  				break
   352  			}
   353  
   354  			// Transaction will be populated once the tx has been sent,
   355  			// otherwise is will be a zeroed out SHA256 digest.
   356  			b := make([]byte, sha256.Size)
   357  			if v.ChainInformation.Transaction == hex.EncodeToString(b) {
   358  				log.Debugf("Anchor tx not sent yet; retry in %v", period)
   359  				break
   360  			}
   361  
   362  			// ChainTimestamp will be populated once the tx has 6
   363  			// confirmations.
   364  			if v.ChainInformation.ChainTimestamp == 0 {
   365  				log.Debugf("Anchor tx %v not enough confirmations; retry in %v",
   366  					v.ChainInformation.Transaction, period)
   367  				break
   368  			}
   369  
   370  			// This digest has been anchored
   371  			anchored[v.Digest] = struct{}{}
   372  		}
   373  		if len(anchored) != len(digests) {
   374  			// There are still digests that are waiting to be anchored.
   375  			// Retry again after the wait period.
   376  			continue
   377  		}
   378  
   379  		// Save anchor records
   380  		for k, v := range anchors {
   381  			var (
   382  				verifyDigest = vbr.Digests[k]
   383  				digest       = verifyDigest.Digest
   384  				merkleRoot   = verifyDigest.ChainInformation.MerkleRoot
   385  				merklePath   = verifyDigest.ChainInformation.MerklePath
   386  			)
   387  
   388  			// Verify the anchored digest matches the root hash
   389  			if digest != hex.EncodeToString(v.LogRoot.RootHash) {
   390  				log.Errorf("anchorWait: digest mismatch: got %x, want %v",
   391  					digest, v.LogRoot.RootHash)
   392  				continue
   393  			}
   394  
   395  			// Verify merkle path
   396  			mk, err := merkle.VerifyAuthPath(&merklePath)
   397  			if err != nil {
   398  				log.Errorf("anchorWait: VerifyAuthPath: %v", err)
   399  				continue
   400  			}
   401  			if hex.EncodeToString(mk[:]) != merkleRoot {
   402  				log.Errorf("anchorWait: merkle root invalid: got %x, want %v",
   403  					mk[:], merkleRoot)
   404  				continue
   405  			}
   406  
   407  			// Verify digest is in the merkle path
   408  			var found bool
   409  			for _, v := range merklePath.Hashes {
   410  				if hex.EncodeToString(v[:]) == digest {
   411  					found = true
   412  					break
   413  				}
   414  			}
   415  			if !found {
   416  				log.Errorf("anchorWait: digest %v not found in merkle path", digest)
   417  				continue
   418  			}
   419  
   420  			// Add VerifyDigest to the anchor record
   421  			v.VerifyDigest = &verifyDigest
   422  
   423  			// Save anchor
   424  			err = t.anchorSave(v)
   425  			if err != nil {
   426  				log.Errorf("anchorWait: anchorSave %v: %v", v.TreeID, err)
   427  				continue
   428  			}
   429  		}
   430  
   431  		log.Infof("Anchor dropped for %v records", len(vbr.Digests))
   432  		return
   433  	}
   434  
   435  	log.Errorf("Anchor drop timeout, waited for: %v",
   436  		int(period.Minutes())*retries)
   437  }
   438  
   439  // anchorTrees drops an anchor for any trees that have unanchored leaves at the
   440  // time of invocation. A SHA256 digest of the tree's log root at its current
   441  // height is timestamped onto the decred blockchain using the dcrtime service.
   442  // The anchor data is saved to the key-value store and the tlog tree is updated
   443  // with an anchor leaf.
   444  func (t *Tstore) anchorTrees() error {
   445  	log.Debugf("Start anchor process")
   446  
   447  	// Ensure we are not reentrant
   448  	if t.droppingAnchorGet() {
   449  		// An anchor is not considered dropped until dcrtime returns the
   450  		// ChainTimestamp in the VerifyReply. dcrtime does not do this
   451  		// until the anchor tx has 6 confirmations, therefor, this code
   452  		// path can be hit if 6 blocks are not mined within the period
   453  		// specified by the anchor schedule. Though rare, the probability
   454  		// of this happening is not zero and should not be considered an
   455  		// error. We simply exit and will drop a new anchor at the next
   456  		// anchor period.
   457  		log.Infof("Attempting to drop an anchor while previous anchor " +
   458  			"has not finished dropping; skipping current anchor period")
   459  		return nil
   460  	}
   461  
   462  	trees, err := t.tlog.TreesAll()
   463  	if err != nil {
   464  		return fmt.Errorf("TreesAll: %v", err)
   465  	}
   466  
   467  	// digests contains the SHA256 digests of the LogRootV1.RootHash
   468  	// for all trees that need to be anchored. These will be submitted
   469  	// to dcrtime to be included in a dcrtime timestamp.
   470  	digests := make([]string, 0, len(trees))
   471  
   472  	// anchors contains an anchor structure for each tree that is being
   473  	// anchored. Once the dcrtime timestamp is successful, these
   474  	// anchors will be updated with the timestamp data and saved to the
   475  	// key-value store.
   476  	anchors := make([]anchor, 0, len(trees))
   477  
   478  	// Find the trees that need to be anchored. This is done by pulling
   479  	// the most recent anchor from the tree and checking the anchored
   480  	// tree height against the current tree height. We cannot rely on
   481  	// the anchored being the last leaf in the tree since new leaves
   482  	// can be added while the anchor is waiting to be dropped.
   483  	for _, v := range trees {
   484  		// Get latest anchor
   485  		a, err := t.anchorLatest(v.TreeId)
   486  		switch {
   487  		case errors.Is(err, errAnchorNotFound):
   488  			// Tree has not been anchored yet. Verify that the tree has
   489  			// leaves. A tree with no leaves does not need to be anchored.
   490  			leavesAll, err := t.tlog.LeavesAll(v.TreeId)
   491  			if err != nil {
   492  				return fmt.Errorf("LeavesAll: %v", err)
   493  			}
   494  			if len(leavesAll) == 0 {
   495  				// Tree does not have any leaves. Nothing to do.
   496  				continue
   497  			}
   498  
   499  		case err != nil:
   500  			// All other errors
   501  			return fmt.Errorf("anchorLatest %v: %v", v.TreeId, err)
   502  
   503  		default:
   504  			// Anchor record found. If the anchor height differs from the
   505  			// current height then the tree needs to be anchored.
   506  			_, lr, err := t.tlog.SignedLogRoot(v)
   507  			if err != nil {
   508  				return fmt.Errorf("SignedLogRoot %v: %v", v.TreeId, err)
   509  			}
   510  			// Subtract one from the current height to account for the
   511  			// anchor leaf.
   512  			if a.LogRoot.TreeSize == lr.TreeSize-1 {
   513  				// Tree has already been anchored at this height. Nothing to
   514  				// do.
   515  				continue
   516  			}
   517  		}
   518  
   519  		// Tree has not been anchored at current height. Add it to the
   520  		// list of anchors.
   521  		_, lr, err := t.tlog.SignedLogRoot(v)
   522  		if err != nil {
   523  			return fmt.Errorf("SignedLogRoot %v: %v", v.TreeId, err)
   524  		}
   525  		anchors = append(anchors, anchor{
   526  			TreeID:  v.TreeId,
   527  			LogRoot: lr,
   528  		})
   529  
   530  		// Collate the tree's root hash. This is what gets submitted to
   531  		// dcrtime.
   532  		digests = append(digests, hex.EncodeToString(lr.RootHash))
   533  
   534  		log.Debugf("Anchoring tree %v at height %v",
   535  			v.TreeId, lr.TreeSize)
   536  	}
   537  	if len(anchors) == 0 {
   538  		log.Infof("No trees to to anchor")
   539  		return nil
   540  	}
   541  
   542  	// Submit dcrtime anchor request
   543  	log.Infof("Anchoring %v trees", len(anchors))
   544  
   545  	tbr, err := t.dcrtime.timestampBatch(anchorID, digests)
   546  	if err != nil {
   547  		return fmt.Errorf("timestampBatch: %v", err)
   548  	}
   549  	var failed bool
   550  	for i, v := range tbr.Results {
   551  		switch v {
   552  		case dcrtime.ResultOK:
   553  			// We're good; continue
   554  		case dcrtime.ResultExistsError:
   555  			// This can happen if politeiad was shutdown in the middle of
   556  			// an anchor process. This is ok. The anchor process will pick
   557  			// up where it left off.
   558  			log.Warnf("Digest already exists %v: %v (%v)",
   559  				tbr.Digests[i], dcrtime.Result[v], v)
   560  		default:
   561  			// Something went wrong; exit
   562  			log.Errorf("Digest failed %v: %v (%v)",
   563  				tbr.Digests[i], dcrtime.Result[v], v)
   564  			failed = true
   565  		}
   566  	}
   567  	if failed {
   568  		return fmt.Errorf("dcrtime failed to timestamp digests")
   569  	}
   570  
   571  	// Launch go routine that polls dcrtime for the anchor tx
   572  	go t.anchorWait(anchors, digests)
   573  
   574  	return nil
   575  }
   576  
   577  func convertBlobEntryFromAnchor(a anchor) (*store.BlobEntry, error) {
   578  	data, err := json.Marshal(a)
   579  	if err != nil {
   580  		return nil, err
   581  	}
   582  	hint, err := json.Marshal(
   583  		store.DataDescriptor{
   584  			Type:       store.DataTypeStructure,
   585  			Descriptor: dataDescriptorAnchor,
   586  		})
   587  	if err != nil {
   588  		return nil, err
   589  	}
   590  	be := store.NewBlobEntry(hint, data)
   591  	return &be, nil
   592  }
   593  
   594  func convertAnchorFromBlobEntry(be store.BlobEntry) (*anchor, error) {
   595  	// Decode and validate data hint
   596  	b, err := base64.StdEncoding.DecodeString(be.DataHint)
   597  	if err != nil {
   598  		return nil, fmt.Errorf("decode DataHint: %v", err)
   599  	}
   600  	var dd store.DataDescriptor
   601  	err = json.Unmarshal(b, &dd)
   602  	if err != nil {
   603  		return nil, fmt.Errorf("unmarshal DataHint: %v", err)
   604  	}
   605  	if dd.Descriptor != dataDescriptorAnchor {
   606  		return nil, fmt.Errorf("unexpected data descriptor: got %v, want %v",
   607  			dd.Descriptor, dataDescriptorAnchor)
   608  	}
   609  
   610  	// Decode data
   611  	b, err = base64.StdEncoding.DecodeString(be.Data)
   612  	if err != nil {
   613  		return nil, fmt.Errorf("decode Data: %v", err)
   614  	}
   615  	digest, err := hex.DecodeString(be.Digest)
   616  	if err != nil {
   617  		return nil, fmt.Errorf("decode digest: %v", err)
   618  	}
   619  	if !bytes.Equal(util.Digest(b), digest) {
   620  		return nil, fmt.Errorf("data is not coherent; got %x, want %x",
   621  			util.Digest(b), digest)
   622  	}
   623  	var a anchor
   624  	err = json.Unmarshal(b, &a)
   625  	if err != nil {
   626  		return nil, fmt.Errorf("unmarshal anchor: %v", err)
   627  	}
   628  
   629  	return &a, nil
   630  }