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 }