gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/proto/contract.go (about) 1 package proto 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "sync" 8 9 "gitlab.com/NebulousLabs/errors" 10 11 "gitlab.com/SiaPrime/SiaPrime/build" 12 "gitlab.com/SiaPrime/SiaPrime/crypto" 13 "gitlab.com/SiaPrime/SiaPrime/encoding" 14 "gitlab.com/SiaPrime/SiaPrime/modules" 15 "gitlab.com/SiaPrime/SiaPrime/types" 16 "gitlab.com/SiaPrime/writeaheadlog" 17 ) 18 19 const ( 20 // contractHeaderSize is the maximum amount of space that the non-Merkle-root 21 // portion of a contract can consume. 22 contractHeaderSize = writeaheadlog.MaxPayloadSize // TODO: test this 23 24 updateNameSetHeader = "setHeader" 25 updateNameSetRoot = "setRoot" 26 ) 27 28 type updateSetHeader struct { 29 ID types.FileContractID 30 Header contractHeader 31 } 32 33 // v132UpdateHeader was introduced due to backwards compatibility reasons after 34 // changing the format of the contractHeader. It contains the legacy 35 // v132ContractHeader. 36 type v132UpdateSetHeader struct { 37 ID types.FileContractID 38 Header v132ContractHeader 39 } 40 41 type updateSetRoot struct { 42 ID types.FileContractID 43 Root crypto.Hash 44 Index int 45 } 46 47 type contractHeader struct { 48 // transaction is the signed transaction containing the most recent 49 // revision of the file contract. 50 Transaction types.Transaction 51 52 // secretKey is the key used by the renter to sign the file contract 53 // transaction. 54 SecretKey crypto.SecretKey 55 56 // Same as modules.RenterContract. 57 StartHeight types.BlockHeight 58 DownloadSpending types.Currency 59 StorageSpending types.Currency 60 UploadSpending types.Currency 61 TotalCost types.Currency 62 ContractFee types.Currency 63 TxnFee types.Currency 64 SiafundFee types.Currency 65 Utility modules.ContractUtility 66 } 67 68 // v132ContractHeader is a contractHeader without the Utility field. This field 69 // was added after v132 to be able to persist contract utilities. 70 type v132ContractHeader struct { 71 // transaction is the signed transaction containing the most recent 72 // revision of the file contract. 73 Transaction types.Transaction 74 75 // secretKey is the key used by the renter to sign the file contract 76 // transaction. 77 SecretKey crypto.SecretKey 78 79 // Same as modules.RenterContract. 80 StartHeight types.BlockHeight 81 DownloadSpending types.Currency 82 StorageSpending types.Currency 83 UploadSpending types.Currency 84 TotalCost types.Currency 85 ContractFee types.Currency 86 TxnFee types.Currency 87 SiafundFee types.Currency 88 } 89 90 // validate returns an error if the contractHeader is invalid. 91 func (h *contractHeader) validate() error { 92 if len(h.Transaction.FileContractRevisions) > 0 && 93 len(h.Transaction.FileContractRevisions[0].NewValidProofOutputs) > 0 && 94 len(h.Transaction.FileContractRevisions[0].UnlockConditions.PublicKeys) == 2 { 95 return nil 96 } 97 return errors.New("invalid contract") 98 } 99 100 func (h *contractHeader) copyTransaction() (txn types.Transaction) { 101 encoding.Unmarshal(encoding.Marshal(h.Transaction), &txn) 102 return 103 } 104 105 func (h *contractHeader) LastRevision() types.FileContractRevision { 106 return h.Transaction.FileContractRevisions[0] 107 } 108 109 func (h *contractHeader) ID() types.FileContractID { 110 return h.LastRevision().ID() 111 } 112 113 func (h *contractHeader) HostPublicKey() types.SiaPublicKey { 114 return h.LastRevision().HostPublicKey() 115 } 116 117 func (h *contractHeader) RenterFunds() types.Currency { 118 return h.LastRevision().RenterFunds() 119 } 120 121 func (h *contractHeader) EndHeight() types.BlockHeight { 122 return h.LastRevision().EndHeight() 123 } 124 125 // A SafeContract contains the most recent revision transaction negotiated 126 // with a host, and the secret key used to sign it. 127 type SafeContract struct { 128 header contractHeader 129 130 // merkleRoots are the sector roots covered by this contract. 131 merkleRoots *merkleRoots 132 133 // unappliedTxns are the transactions that were written to the WAL but not 134 // applied to the contract file. 135 unappliedTxns []*writeaheadlog.Transaction 136 137 headerFile *fileSection 138 wal *writeaheadlog.WAL 139 mu sync.Mutex 140 141 // revisionMu serializes revisions to the contract. It is acquired by 142 // (ContractSet).Acquire and released by (ContractSet).Return. When holding 143 // revisionMu, it is still necessary to lock mu when modifying fields of the 144 // SafeContract. 145 revisionMu sync.Mutex 146 } 147 148 // Metadata returns the metadata of a renter contract 149 func (c *SafeContract) Metadata() modules.RenterContract { 150 c.mu.Lock() 151 defer c.mu.Unlock() 152 h := c.header 153 return modules.RenterContract{ 154 ID: h.ID(), 155 Transaction: h.copyTransaction(), 156 HostPublicKey: h.HostPublicKey(), 157 StartHeight: h.StartHeight, 158 EndHeight: h.EndHeight(), 159 RenterFunds: h.RenterFunds(), 160 DownloadSpending: h.DownloadSpending, 161 StorageSpending: h.StorageSpending, 162 UploadSpending: h.UploadSpending, 163 TotalCost: h.TotalCost, 164 ContractFee: h.ContractFee, 165 TxnFee: h.TxnFee, 166 SiafundFee: h.SiafundFee, 167 Utility: h.Utility, 168 } 169 } 170 171 // UpdateUtility updates the utility field of a contract. 172 func (c *SafeContract) UpdateUtility(utility modules.ContractUtility) error { 173 c.mu.Lock() 174 defer c.mu.Unlock() 175 // Construct new header 176 newHeader := c.header 177 newHeader.Utility = utility 178 179 // Record the intent to change the header in the wal. 180 t, err := c.wal.NewTransaction([]writeaheadlog.Update{ 181 c.makeUpdateSetHeader(newHeader), 182 }) 183 if err != nil { 184 return err 185 } 186 // Signal that the setup is completed. 187 if err := <-t.SignalSetupComplete(); err != nil { 188 return err 189 } 190 // Apply the change. 191 if err := c.applySetHeader(newHeader); err != nil { 192 return err 193 } 194 // Sync the change to disk. 195 if err := c.headerFile.Sync(); err != nil { 196 return err 197 } 198 // Signal that the update has been applied. 199 if err := t.SignalUpdatesApplied(); err != nil { 200 return err 201 } 202 return nil 203 } 204 205 // Utility returns the contract utility for the contract. 206 func (c *SafeContract) Utility() modules.ContractUtility { 207 c.mu.Lock() 208 defer c.mu.Unlock() 209 return c.header.Utility 210 } 211 212 func (c *SafeContract) makeUpdateSetHeader(h contractHeader) writeaheadlog.Update { 213 id := c.header.ID() 214 return writeaheadlog.Update{ 215 Name: updateNameSetHeader, 216 Instructions: encoding.Marshal(updateSetHeader{ 217 ID: id, 218 Header: h, 219 }), 220 } 221 } 222 223 func (c *SafeContract) makeUpdateSetRoot(root crypto.Hash, index int) writeaheadlog.Update { 224 id := c.header.ID() 225 return writeaheadlog.Update{ 226 Name: updateNameSetRoot, 227 Instructions: encoding.Marshal(updateSetRoot{ 228 ID: id, 229 Root: root, 230 Index: index, 231 }), 232 } 233 } 234 235 func (c *SafeContract) applySetHeader(h contractHeader) error { 236 if build.DEBUG { 237 // read the existing header on disk, to make sure we aren't overwriting 238 // it with an older revision 239 var oldHeader contractHeader 240 headerBytes := make([]byte, contractHeaderSize) 241 if _, err := c.headerFile.ReadAt(headerBytes, 0); err == nil { 242 if err := encoding.Unmarshal(headerBytes, &oldHeader); err == nil { 243 if oldHeader.LastRevision().NewRevisionNumber > h.LastRevision().NewRevisionNumber { 244 build.Critical("overwriting a newer revision:", oldHeader.LastRevision().NewRevisionNumber, h.LastRevision().NewRevisionNumber) 245 } 246 } 247 } 248 } 249 250 headerBytes := make([]byte, contractHeaderSize) 251 copy(headerBytes, encoding.Marshal(h)) 252 if _, err := c.headerFile.WriteAt(headerBytes, 0); err != nil { 253 return err 254 } 255 c.header = h 256 return nil 257 } 258 259 func (c *SafeContract) applySetRoot(root crypto.Hash, index int) error { 260 return c.merkleRoots.insert(index, root) 261 } 262 263 func (c *SafeContract) managedRecordUploadIntent(rev types.FileContractRevision, root crypto.Hash, storageCost, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) { 264 c.mu.Lock() 265 defer c.mu.Unlock() 266 // construct new header 267 // NOTE: this header will not include the host signature 268 newHeader := c.header 269 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 270 newHeader.Transaction.TransactionSignatures = nil 271 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 272 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 273 274 t, err := c.wal.NewTransaction([]writeaheadlog.Update{ 275 c.makeUpdateSetHeader(newHeader), 276 c.makeUpdateSetRoot(root, c.merkleRoots.len()), 277 }) 278 if err != nil { 279 return nil, err 280 } 281 if err := <-t.SignalSetupComplete(); err != nil { 282 return nil, err 283 } 284 c.unappliedTxns = append(c.unappliedTxns, t) 285 return t, nil 286 } 287 288 func (c *SafeContract) managedCommitUpload(t *writeaheadlog.Transaction, signedTxn types.Transaction, root crypto.Hash, storageCost, bandwidthCost types.Currency) error { 289 c.mu.Lock() 290 defer c.mu.Unlock() 291 // construct new header 292 newHeader := c.header 293 newHeader.Transaction = signedTxn 294 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 295 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 296 297 if err := c.applySetHeader(newHeader); err != nil { 298 return err 299 } 300 if err := c.applySetRoot(root, c.merkleRoots.len()); err != nil { 301 return err 302 } 303 if err := c.headerFile.Sync(); err != nil { 304 return err 305 } 306 if err := t.SignalUpdatesApplied(); err != nil { 307 return err 308 } 309 c.unappliedTxns = nil 310 return nil 311 } 312 313 func (c *SafeContract) managedRecordDownloadIntent(rev types.FileContractRevision, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) { 314 c.mu.Lock() 315 defer c.mu.Unlock() 316 // construct new header 317 // NOTE: this header will not include the host signature 318 newHeader := c.header 319 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 320 newHeader.Transaction.TransactionSignatures = nil 321 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 322 323 t, err := c.wal.NewTransaction([]writeaheadlog.Update{ 324 c.makeUpdateSetHeader(newHeader), 325 }) 326 if err != nil { 327 return nil, err 328 } 329 if err := <-t.SignalSetupComplete(); err != nil { 330 return nil, err 331 } 332 c.unappliedTxns = append(c.unappliedTxns, t) 333 return t, nil 334 } 335 336 func (c *SafeContract) managedCommitDownload(t *writeaheadlog.Transaction, signedTxn types.Transaction, bandwidthCost types.Currency) error { 337 c.mu.Lock() 338 defer c.mu.Unlock() 339 // construct new header 340 newHeader := c.header 341 newHeader.Transaction = signedTxn 342 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 343 344 if err := c.applySetHeader(newHeader); err != nil { 345 return err 346 } 347 if err := c.headerFile.Sync(); err != nil { 348 return err 349 } 350 if err := t.SignalUpdatesApplied(); err != nil { 351 return err 352 } 353 c.unappliedTxns = nil 354 return nil 355 } 356 357 // managedCommitTxns commits the unapplied transactions to the contract file and marks 358 // the transactions as applied. 359 func (c *SafeContract) managedCommitTxns() error { 360 c.mu.Lock() 361 defer c.mu.Unlock() 362 for _, t := range c.unappliedTxns { 363 for _, update := range t.Updates { 364 switch update.Name { 365 case updateNameSetHeader: 366 var u updateSetHeader 367 if err := unmarshalHeader(update.Instructions, &u); err != nil { 368 return err 369 } 370 if err := c.applySetHeader(u.Header); err != nil { 371 return err 372 } 373 case updateNameSetRoot: 374 var u updateSetRoot 375 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 376 return err 377 } 378 if err := c.applySetRoot(u.Root, u.Index); err != nil { 379 return err 380 } 381 } 382 } 383 if err := c.headerFile.Sync(); err != nil { 384 return err 385 } 386 if err := t.SignalUpdatesApplied(); err != nil { 387 return err 388 } 389 } 390 c.unappliedTxns = nil 391 return nil 392 } 393 394 // managedSyncRevision checks whether rev accords with the SafeContract's most 395 // recent revision; if it does not, managedSyncRevision attempts to synchronize 396 // with rev by committing any uncommitted WAL transactions. If the revisions 397 // still do not match, and the host's revision is ahead of the renter's, 398 // managedSyncRevision uses the host's revision. 399 func (c *SafeContract) managedSyncRevision(rev types.FileContractRevision, sigs []types.TransactionSignature) error { 400 c.mu.Lock() 401 defer c.mu.Unlock() 402 403 // Our current revision should always be signed. If it isn't, we have no 404 // choice but to accept the host's revision. 405 if len(c.header.Transaction.TransactionSignatures) == 0 { 406 c.header.Transaction.FileContractRevisions[0] = rev 407 c.header.Transaction.TransactionSignatures = sigs 408 return nil 409 } 410 411 ourRev := c.header.LastRevision() 412 413 // If the revision number and Merkle root match, we don't need to do anything. 414 if rev.NewRevisionNumber == ourRev.NewRevisionNumber && rev.NewFileMerkleRoot == ourRev.NewFileMerkleRoot { 415 // If any other fields mismatch, it must be our fault, since we signed 416 // the revision reported by the host. So, to ensure things are 417 // consistent, we blindly overwrite our revision with the host's. 418 c.header.Transaction.FileContractRevisions[0] = rev 419 c.header.Transaction.TransactionSignatures = sigs 420 return nil 421 } 422 423 // The host should never report a lower revision number than ours. If they 424 // do, it may mean they are intentionally (and maliciously) trying to 425 // "rewind" the contract to an earlier state. Even if the host does not have 426 // ill intent, this would mean that they failed to commit one or more 427 // revisions to durable storage, which reflects very poorly on them. 428 if rev.NewRevisionNumber < ourRev.NewRevisionNumber { 429 return &revisionNumberMismatchError{ourRev.NewRevisionNumber, rev.NewRevisionNumber} 430 } 431 432 // At this point, we know that either the host's revision number is above 433 // ours, or their Merkle root differs. Search our unapplied WAL transactions 434 // for one that might synchronize us with the host. 435 for _, t := range c.unappliedTxns { 436 for _, update := range t.Updates { 437 if update.Name == updateNameSetHeader { 438 var u updateSetHeader 439 if err := unmarshalHeader(update.Instructions, &u); err != nil { 440 return err 441 } 442 unappliedRev := u.Header.LastRevision() 443 if unappliedRev.NewRevisionNumber != rev.NewRevisionNumber || unappliedRev.NewFileMerkleRoot != rev.NewFileMerkleRoot { 444 continue 445 } 446 // found a matching header, but it still won't have the host's 447 // signatures, since those aren't added until the transaction is 448 // committed. Add the signatures supplied by the host and commit 449 // the header. 450 u.Header.Transaction.TransactionSignatures = sigs 451 if err := c.applySetHeader(u.Header); err != nil { 452 return err 453 } 454 if err := c.headerFile.Sync(); err != nil { 455 return err 456 } 457 // drop all unapplied transactions 458 for _, t := range c.unappliedTxns { 459 if err := t.SignalUpdatesApplied(); err != nil { 460 return err 461 } 462 } 463 c.unappliedTxns = nil 464 return nil 465 } 466 } 467 } 468 469 // The host's revision is still different, and we have no unapplied 470 // transactions containing their revision. At this point, the best we can do 471 // is accept their revision. This isn't ideal, but at least there's no 472 // security risk, since we *did* sign the revision that the host is 473 // claiming. Worst case, certain contract metadata (e.g. UploadSpending) 474 // will be incorrect. 475 c.header.Transaction.FileContractRevisions[0] = rev 476 c.header.Transaction.TransactionSignatures = sigs 477 // Drop the WAL transactions, since they can't conceivably help us. 478 for _, t := range c.unappliedTxns { 479 if err := t.SignalUpdatesApplied(); err != nil { 480 return err 481 } 482 } 483 c.unappliedTxns = nil 484 return nil 485 } 486 487 func (cs *ContractSet) managedInsertContract(h contractHeader, roots []crypto.Hash) (modules.RenterContract, error) { 488 if err := h.validate(); err != nil { 489 return modules.RenterContract{}, err 490 } 491 f, err := os.Create(filepath.Join(cs.dir, h.ID().String()+contractExtension)) 492 if err != nil { 493 return modules.RenterContract{}, err 494 } 495 // create fileSections 496 headerSection := newFileSection(f, 0, contractHeaderSize) 497 rootsSection := newFileSection(f, contractHeaderSize, -1) 498 // write header 499 if _, err := headerSection.WriteAt(encoding.Marshal(h), 0); err != nil { 500 return modules.RenterContract{}, err 501 } 502 // write roots 503 merkleRoots := newMerkleRoots(rootsSection) 504 for _, root := range roots { 505 if err := merkleRoots.push(root); err != nil { 506 return modules.RenterContract{}, err 507 } 508 } 509 if err := f.Sync(); err != nil { 510 return modules.RenterContract{}, err 511 } 512 sc := &SafeContract{ 513 header: h, 514 merkleRoots: merkleRoots, 515 headerFile: headerSection, 516 wal: cs.wal, 517 } 518 cs.mu.Lock() 519 cs.contracts[sc.header.ID()] = sc 520 cs.pubKeys[h.HostPublicKey().String()] = sc.header.ID() 521 cs.mu.Unlock() 522 return sc.Metadata(), nil 523 } 524 525 // loadSafeContract loads a contract from disk and adds it to the contractset 526 // if it is valid. 527 func (cs *ContractSet) loadSafeContract(filename string, walTxns []*writeaheadlog.Transaction) (err error) { 528 f, err := os.OpenFile(filename, os.O_RDWR, 0600) 529 if err != nil { 530 return err 531 } 532 defer func() { 533 if err != nil { 534 f.Close() 535 } 536 }() 537 stat, err := f.Stat() 538 if err != nil { 539 return err 540 } 541 542 headerSection := newFileSection(f, 0, contractHeaderSize) 543 rootsSection := newFileSection(f, contractHeaderSize, remainingFile) 544 545 // read header 546 var header contractHeader 547 if err := encoding.NewDecoder(f, int(stat.Size()*3)).Decode(&header); err != nil { 548 return err 549 } else if err := header.validate(); err != nil { 550 return err 551 } 552 553 // read merkleRoots 554 merkleRoots, applyTxns, err := loadExistingMerkleRoots(rootsSection) 555 if err != nil { 556 return err 557 } 558 // add relevant unapplied transactions 559 var unappliedTxns []*writeaheadlog.Transaction 560 for _, t := range walTxns { 561 // NOTE: we assume here that if any of the updates apply to the 562 // contract, the whole transaction applies to the contract. 563 if len(t.Updates) == 0 { 564 continue 565 } 566 var id types.FileContractID 567 switch update := t.Updates[0]; update.Name { 568 case updateNameSetHeader: 569 var u updateSetHeader 570 if err := unmarshalHeader(update.Instructions, &u); err != nil { 571 return err 572 } 573 id = u.ID 574 case updateNameSetRoot: 575 var u updateSetRoot 576 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 577 return err 578 } 579 id = u.ID 580 } 581 if id == header.ID() { 582 unappliedTxns = append(unappliedTxns, t) 583 } 584 } 585 // add to set 586 sc := &SafeContract{ 587 header: header, 588 merkleRoots: merkleRoots, 589 unappliedTxns: unappliedTxns, 590 headerFile: headerSection, 591 wal: cs.wal, 592 } 593 594 // apply the wal txns if necessary. 595 if applyTxns { 596 if err := sc.managedCommitTxns(); err != nil { 597 return err 598 } 599 } 600 cs.contracts[sc.header.ID()] = sc 601 cs.pubKeys[header.HostPublicKey().String()] = sc.header.ID() 602 return nil 603 } 604 605 // ConvertV130Contract creates a contract file for a v130 contract. 606 func (cs *ContractSet) ConvertV130Contract(c V130Contract, cr V130CachedRevision) error { 607 m, err := cs.managedInsertContract(contractHeader{ 608 Transaction: c.LastRevisionTxn, 609 SecretKey: c.SecretKey, 610 StartHeight: c.StartHeight, 611 DownloadSpending: c.DownloadSpending, 612 StorageSpending: c.StorageSpending, 613 UploadSpending: c.UploadSpending, 614 TotalCost: c.TotalCost, 615 ContractFee: c.ContractFee, 616 TxnFee: c.TxnFee, 617 SiafundFee: c.SiafundFee, 618 }, c.MerkleRoots) 619 if err != nil { 620 return err 621 } 622 // if there is a cached revision, store it as an unapplied WAL transaction 623 if cr.Revision.NewRevisionNumber != 0 { 624 sc, ok := cs.Acquire(m.ID) 625 if !ok { 626 return errors.New("contract set is missing contract that was just added") 627 } 628 defer cs.Return(sc) 629 if len(cr.MerkleRoots) == sc.merkleRoots.len()+1 { 630 root := cr.MerkleRoots[len(cr.MerkleRoots)-1] 631 _, err = sc.managedRecordUploadIntent(cr.Revision, root, types.ZeroCurrency, types.ZeroCurrency) 632 } else { 633 _, err = sc.managedRecordDownloadIntent(cr.Revision, types.ZeroCurrency) 634 } 635 if err != nil { 636 return err 637 } 638 } 639 return nil 640 } 641 642 // A V130Contract specifies the v130 contract format. 643 type V130Contract struct { 644 HostPublicKey types.SiaPublicKey `json:"hostpublickey"` 645 ID types.FileContractID `json:"id"` 646 LastRevision types.FileContractRevision `json:"lastrevision"` 647 LastRevisionTxn types.Transaction `json:"lastrevisiontxn"` 648 MerkleRoots MerkleRootSet `json:"merkleroots"` 649 SecretKey crypto.SecretKey `json:"secretkey"` 650 StartHeight types.BlockHeight `json:"startheight"` 651 DownloadSpending types.Currency `json:"downloadspending"` 652 StorageSpending types.Currency `json:"storagespending"` 653 UploadSpending types.Currency `json:"uploadspending"` 654 TotalCost types.Currency `json:"totalcost"` 655 ContractFee types.Currency `json:"contractfee"` 656 TxnFee types.Currency `json:"txnfee"` 657 SiafundFee types.Currency `json:"siafundfee"` 658 } 659 660 // EndHeight returns the height at which the host is no longer obligated to 661 // store contract data. 662 func (c *V130Contract) EndHeight() types.BlockHeight { 663 return c.LastRevision.NewWindowStart 664 } 665 666 // RenterFunds returns the funds remaining in the contract's Renter payout as 667 // of the most recent revision. 668 func (c *V130Contract) RenterFunds() types.Currency { 669 if len(c.LastRevision.NewValidProofOutputs) < 2 { 670 return types.ZeroCurrency 671 } 672 return c.LastRevision.NewValidProofOutputs[0].Value 673 } 674 675 // A V130CachedRevision contains changes that would be applied to a 676 // RenterContract if a contract revision succeeded. 677 type V130CachedRevision struct { 678 Revision types.FileContractRevision `json:"revision"` 679 MerkleRoots modules.MerkleRootSet `json:"merkleroots"` 680 } 681 682 // MerkleRootSet is a set of Merkle roots, and gets encoded more efficiently. 683 type MerkleRootSet []crypto.Hash 684 685 // MarshalJSON defines a JSON encoding for a MerkleRootSet. 686 func (mrs MerkleRootSet) MarshalJSON() ([]byte, error) { 687 // Copy the whole array into a giant byte slice and then encode that. 688 fullBytes := make([]byte, crypto.HashSize*len(mrs)) 689 for i := range mrs { 690 copy(fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize], mrs[i][:]) 691 } 692 return json.Marshal(fullBytes) 693 } 694 695 // UnmarshalJSON attempts to decode a MerkleRootSet, falling back on the legacy 696 // decoding of a []crypto.Hash if that fails. 697 func (mrs *MerkleRootSet) UnmarshalJSON(b []byte) error { 698 // Decode the giant byte slice, and then split it into separate arrays. 699 var fullBytes []byte 700 err := json.Unmarshal(b, &fullBytes) 701 if err != nil { 702 // Encoding the byte slice has failed, try decoding it as a []crypto.Hash. 703 var hashes []crypto.Hash 704 err := json.Unmarshal(b, &hashes) 705 if err != nil { 706 return err 707 } 708 *mrs = MerkleRootSet(hashes) 709 return nil 710 } 711 712 umrs := make(MerkleRootSet, len(fullBytes)/32) 713 for i := range umrs { 714 copy(umrs[i][:], fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize]) 715 } 716 *mrs = umrs 717 return nil 718 } 719 720 func unmarshalHeader(b []byte, u *updateSetHeader) error { 721 // Try unmarshaling the header. 722 if err := encoding.Unmarshal(b, u); err != nil { 723 // COMPATv132 try unmarshaling the header the old way. 724 var oldHeader v132UpdateSetHeader 725 if err2 := encoding.Unmarshal(b, &oldHeader); err2 != nil { 726 // If unmarshaling the header the old way also doesn't work we 727 // return the original error. 728 return err 729 } 730 // If unmarshaling it the old way was successful we convert it to a new 731 // header. 732 u.Header = contractHeader{ 733 Transaction: oldHeader.Header.Transaction, 734 SecretKey: oldHeader.Header.SecretKey, 735 StartHeight: oldHeader.Header.StartHeight, 736 DownloadSpending: oldHeader.Header.DownloadSpending, 737 StorageSpending: oldHeader.Header.StorageSpending, 738 UploadSpending: oldHeader.Header.UploadSpending, 739 TotalCost: oldHeader.Header.TotalCost, 740 ContractFee: oldHeader.Header.ContractFee, 741 TxnFee: oldHeader.Header.TxnFee, 742 SiafundFee: oldHeader.Header.SiafundFee, 743 } 744 } 745 return nil 746 }