本文整理匯總了Golang中github.com/NebulousLabs/Sia/types.Transaction.ID方法的典型用法代碼示例。如果您正苦於以下問題:Golang Transaction.ID方法的具體用法?Golang Transaction.ID怎麽用?Golang Transaction.ID使用的例子?那麽, 這裏精選的方法代碼示例或許可以為您提供幫助。您也可以進一步了解該方法所在類github.com/NebulousLabs/Sia/types.Transaction
的用法示例。
在下文中一共展示了Transaction.ID方法的8個代碼示例,這些例子默認根據受歡迎程度排序。您可以為喜歡或者感覺有用的代碼點讚,您的評價將有助於係統推薦出更棒的Golang代碼示例。
示例1: negotiateRevision
// negotiateRevision sends the revision and new piece data to the host.
func negotiateRevision(conn net.Conn, rev types.FileContractRevision, piece []byte, secretKey crypto.SecretKey) (types.Transaction, error) {
conn.SetDeadline(time.Now().Add(5 * time.Minute)) // sufficient to transfer 4 MB over 100 kbps
defer conn.SetDeadline(time.Time{}) // reset timeout after each revision
// create transaction containing the revision
signedTxn := types.Transaction{
FileContractRevisions: []types.FileContractRevision{rev},
TransactionSignatures: []types.TransactionSignature{{
ParentID: crypto.Hash(rev.ParentID),
CoveredFields: types.CoveredFields{FileContractRevisions: []uint64{0}},
PublicKeyIndex: 0, // renter key is always first -- see negotiateContract
}},
}
// sign the transaction
encodedSig, _ := crypto.SignHash(signedTxn.SigHash(0), secretKey) // no error possible
signedTxn.TransactionSignatures[0].Signature = encodedSig[:]
// send the transaction
if err := encoding.WriteObject(conn, signedTxn); err != nil {
return types.Transaction{}, errors.New("couldn't send revision transaction: " + err.Error())
}
// host sends acceptance
var response string
if err := encoding.ReadObject(conn, &response, 128); err != nil {
return types.Transaction{}, errors.New("couldn't read host acceptance: " + err.Error())
}
if response != modules.AcceptResponse {
return types.Transaction{}, errors.New("host rejected revision: " + response)
}
// transfer piece
if _, err := conn.Write(piece); err != nil {
return types.Transaction{}, errors.New("couldn't transfer piece: " + err.Error())
}
// read txn signed by host
var signedHostTxn types.Transaction
if err := encoding.ReadObject(conn, &signedHostTxn, types.BlockSizeLimit); err != nil {
return types.Transaction{}, errors.New("couldn't read signed revision transaction: " + err.Error())
}
if signedHostTxn.ID() != signedTxn.ID() {
return types.Transaction{}, errors.New("host sent bad signed transaction")
}
return signedHostTxn, nil
}
示例2: managedRPCRevise
// managedRPCRevise is an RPC that allows a renter to revise a file contract. It will
// read new revisions in a loop until the renter sends a termination signal.
func (h *Host) managedRPCRevise(conn net.Conn) error {
// read ID of contract to be revised
var fcid types.FileContractID
if err := encoding.ReadObject(conn, &fcid, crypto.HashSize); err != nil {
return errors.New("couldn't read contract ID: " + err.Error())
}
// remove conn deadline while we wait for lock and rebuild the Merkle tree.
err := conn.SetDeadline(time.Now().Add(15 * time.Minute))
if err != nil {
return err
}
h.mu.RLock()
obligation, exists := h.obligationsByID[fcid]
h.mu.RUnlock()
if !exists {
return errors.New("no record of that contract")
}
// need to protect against two simultaneous revisions to the same
// contract; this can cause inconsistency and data loss, making storage
// proofs impossible
//
// TODO: DOS vector - the host has locked the obligation even though the
// renter has not proven themselves to be the owner of the file contract.
obligation.mu.Lock()
defer obligation.mu.Unlock()
// open the file in append mode
file, err := os.OpenFile(obligation.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
if err != nil {
return err
}
// rebuild current Merkle tree
tree := crypto.NewTree()
err = tree.ReadSegments(file)
if err != nil {
// Error does not need to be checked when closing the file, already
// there have been issues related to the filesystem.
_ = file.Close()
return err
}
// accept new revisions in a loop. The final good transaction will be
// submitted to the blockchain.
revisionErr := func() error {
for {
// allow 5 minutes between revisions
err := conn.SetDeadline(time.Now().Add(5 * time.Minute))
if err != nil {
return err
}
// read proposed revision
var revTxn types.Transaction
if err = encoding.ReadObject(conn, &revTxn, types.BlockSizeLimit); err != nil {
return errors.New("couldn't read revision: " + err.Error())
}
// an empty transaction indicates completion
if revTxn.ID() == (types.Transaction{}).ID() {
return nil
}
// allow 5 minutes for each revision
err = conn.SetDeadline(time.Now().Add(5 * time.Minute))
if err != nil {
return err
}
// check revision against original file contract
h.mu.RLock()
err = h.considerRevision(revTxn, obligation)
h.mu.RUnlock()
if err != nil {
// There is nothing that can be done if there is an error while
// writing to a connection.
_ = encoding.WriteObject(conn, err.Error())
return err
}
// indicate acceptance
if err := encoding.WriteObject(conn, modules.AcceptResponse); err != nil {
return errors.New("couldn't write acceptance: " + err.Error())
}
// read piece
// TODO: simultaneously read into tree and file
rev := revTxn.FileContractRevisions[0]
piece := make([]byte, rev.NewFileSize-obligation.fileSize())
_, err = io.ReadFull(conn, piece)
if err != nil {
return errors.New("couldn't read piece data: " + err.Error())
}
// verify Merkle root
err = tree.ReadSegments(bytes.NewReader(piece))
if err != nil {
//.........這裏部分代碼省略.........
示例3: buildExplorerTransaction
// buildExplorerTransaction takes a transaction and the height + id of the
// block it appears in an uses that to build an explorer transaction.
func (srv *Server) buildExplorerTransaction(height types.BlockHeight, parent types.BlockID, txn types.Transaction) (et ExplorerTransaction) {
// Get the header information for the transaction.
et.ID = txn.ID()
et.Height = height
et.Parent = parent
et.RawTransaction = txn
// Add the siacoin outputs that correspond with each siacoin input.
for _, sci := range txn.SiacoinInputs {
sco, exists := srv.explorer.SiacoinOutput(sci.ParentID)
if build.DEBUG && !exists {
panic("could not find corresponding siacoin output")
}
et.SiacoinInputOutputs = append(et.SiacoinInputOutputs, sco)
}
for i := range txn.SiacoinOutputs {
et.SiacoinOutputIDs = append(et.SiacoinOutputIDs, txn.SiacoinOutputID(uint64(i)))
}
// Add all of the valid and missed proof ids as extra data to the file
// contracts.
for i, fc := range txn.FileContracts {
fcid := txn.FileContractID(uint64(i))
var fcvpoids []types.SiacoinOutputID
var fcmpoids []types.SiacoinOutputID
for j := range fc.ValidProofOutputs {
fcvpoids = append(fcvpoids, fcid.StorageProofOutputID(types.ProofValid, uint64(j)))
}
for j := range fc.MissedProofOutputs {
fcmpoids = append(fcmpoids, fcid.StorageProofOutputID(types.ProofMissed, uint64(j)))
}
et.FileContractIDs = append(et.FileContractIDs, fcid)
et.FileContractValidProofOutputIDs = append(et.FileContractValidProofOutputIDs, fcvpoids)
et.FileContractMissedProofOutputIDs = append(et.FileContractMissedProofOutputIDs, fcmpoids)
}
// Add all of the valid and missed proof ids as extra data to the file
// contract revisions.
for _, fcr := range txn.FileContractRevisions {
var fcrvpoids []types.SiacoinOutputID
var fcrmpoids []types.SiacoinOutputID
for j := range fcr.NewValidProofOutputs {
fcrvpoids = append(fcrvpoids, fcr.ParentID.StorageProofOutputID(types.ProofValid, uint64(j)))
}
for j := range fcr.NewMissedProofOutputs {
fcrmpoids = append(fcrmpoids, fcr.ParentID.StorageProofOutputID(types.ProofMissed, uint64(j)))
}
et.FileContractValidProofOutputIDs = append(et.FileContractValidProofOutputIDs, fcrvpoids)
et.FileContractMissedProofOutputIDs = append(et.FileContractMissedProofOutputIDs, fcrmpoids)
}
// Add all of the output ids and outputs corresponding with each storage
// proof.
for _, sp := range txn.StorageProofs {
fileContract, fileContractRevisions, fileContractExists, _ := srv.explorer.FileContractHistory(sp.ParentID)
if !fileContractExists && build.DEBUG {
panic("could not find a file contract connected with a storage proof")
}
var storageProofOutputs []types.SiacoinOutput
if len(fileContractRevisions) > 0 {
storageProofOutputs = fileContractRevisions[len(fileContractRevisions)-1].NewValidProofOutputs
} else {
storageProofOutputs = fileContract.ValidProofOutputs
}
var storageProofOutputIDs []types.SiacoinOutputID
for i := range storageProofOutputs {
storageProofOutputIDs = append(storageProofOutputIDs, sp.ParentID.StorageProofOutputID(types.ProofValid, uint64(i)))
}
et.StorageProofOutputIDs = append(et.StorageProofOutputIDs, storageProofOutputIDs)
et.StorageProofOutputs = append(et.StorageProofOutputs, storageProofOutputs)
}
// Add the siafund outputs that correspond to each siacoin input.
for _, sci := range txn.SiafundInputs {
sco, exists := srv.explorer.SiafundOutput(sci.ParentID)
if build.DEBUG && !exists {
panic("could not find corresponding siafund output")
}
et.SiafundInputOutputs = append(et.SiafundInputOutputs, sco)
}
for i := range txn.SiafundOutputs {
et.SiafundOutputIDs = append(et.SiafundOutputIDs, txn.SiafundOutputID(uint64(i)))
}
for _, sfi := range txn.SiafundInputs {
et.SiaClaimOutputIDs = append(et.SiaClaimOutputIDs, sfi.ParentID.SiaClaimOutputID())
}
return et
}
示例4: revise
// revise revises fc to cover piece and uploads both the revision and the
// piece data to the host.
func (hu *hostUploader) revise(fc types.FileContract, piece []byte, height types.BlockHeight) error {
hu.conn.SetDeadline(time.Now().Add(5 * time.Minute)) // sufficient to transfer 4 MB over 100 kbps
defer hu.conn.SetDeadline(time.Time{}) // reset timeout after each revision
// calculate new merkle root
err := hu.tree.ReadSegments(bytes.NewReader(piece))
if err != nil {
return err
}
// create revision
rev := types.FileContractRevision{
ParentID: hu.contract.ID,
UnlockConditions: hu.unlockConditions,
NewRevisionNumber: fc.RevisionNumber + 1,
NewFileSize: fc.FileSize + uint64(len(piece)),
NewFileMerkleRoot: hu.tree.Root(),
NewWindowStart: fc.WindowStart,
NewWindowEnd: fc.WindowEnd,
NewValidProofOutputs: fc.ValidProofOutputs,
NewMissedProofOutputs: fc.MissedProofOutputs,
NewUnlockHash: fc.UnlockHash,
}
// transfer value of piece from renter to host
safeDuration := uint64(fc.WindowStart - height + 20) // buffer in case host is behind
piecePrice := types.NewCurrency64(uint64(len(piece))).Mul(types.NewCurrency64(safeDuration)).Mul(hu.settings.Price)
// prevent a negative currency panic
if piecePrice.Cmp(fc.ValidProofOutputs[0].Value) > 0 {
// probably not enough money, but the host might accept it anyway
piecePrice = fc.ValidProofOutputs[0].Value
}
rev.NewValidProofOutputs[0].Value = rev.NewValidProofOutputs[0].Value.Sub(piecePrice) // less returned to renter
rev.NewValidProofOutputs[1].Value = rev.NewValidProofOutputs[1].Value.Add(piecePrice) // more given to host
rev.NewMissedProofOutputs[0].Value = rev.NewMissedProofOutputs[0].Value.Sub(piecePrice) // less returned to renter
rev.NewMissedProofOutputs[1].Value = rev.NewMissedProofOutputs[1].Value.Add(piecePrice) // more given to void
// create transaction containing the revision
signedTxn := types.Transaction{
FileContractRevisions: []types.FileContractRevision{rev},
TransactionSignatures: []types.TransactionSignature{{
ParentID: crypto.Hash(hu.contract.ID),
CoveredFields: types.CoveredFields{FileContractRevisions: []uint64{0}},
PublicKeyIndex: 0, // renter key is always first -- see negotiateContract
}},
}
// sign the transaction
encodedSig, err := crypto.SignHash(signedTxn.SigHash(0), hu.secretKey)
if err != nil {
return err
}
signedTxn.TransactionSignatures[0].Signature = encodedSig[:]
// send the transaction
if err := encoding.WriteObject(hu.conn, signedTxn); err != nil {
return err
}
// host sends acceptance
var response string
if err := encoding.ReadObject(hu.conn, &response, 128); err != nil {
return err
}
if response != modules.AcceptResponse {
return errors.New("host rejected revision: " + response)
}
// transfer piece
if _, err := hu.conn.Write(piece); err != nil {
return err
}
// read txn signed by host
var signedHostTxn types.Transaction
if err := encoding.ReadObject(hu.conn, &signedHostTxn, types.BlockSizeLimit); err != nil {
return err
}
if signedHostTxn.ID() != signedTxn.ID() {
return errors.New("host sent bad signed transaction")
} else if err = signedHostTxn.StandaloneValid(height); err != nil {
return err
}
hu.lastTxn = signedHostTxn
return nil
}
示例5: addTransaction
// addTransaction is called from addBlockDB, and delegates the adding
// of information to the database to the functions defined above
func (tx *boltTx) addTransaction(txn types.Transaction) {
// Store this for quick lookup
txid := txn.ID()
// Append each input to the list of modifications
for _, input := range txn.SiacoinInputs {
tx.addAddress(input.UnlockConditions.UnlockHash(), txid)
tx.addSiacoinInput(input.ParentID, txid)
}
// Handle all the transaction outputs
for i, output := range txn.SiacoinOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewOutput(txn.SiacoinOutputID(uint64(i)), txid)
}
// Handle each file contract individually
for i, contract := range txn.FileContracts {
fcid := txn.FileContractID(uint64(i))
tx.addNewHash("FileContracts", hashFilecontract, crypto.Hash(fcid), fcInfo{
Contract: txid,
})
for j, output := range contract.ValidProofOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewOutput(fcid.StorageProofOutputID(true, uint64(j)), txid)
}
for j, output := range contract.MissedProofOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewOutput(fcid.StorageProofOutputID(false, uint64(j)), txid)
}
tx.addAddress(contract.UnlockHash, txid)
}
// Update the list of revisions
for _, revision := range txn.FileContractRevisions {
tx.addFcRevision(revision.ParentID, txid)
// Note the old outputs will still be there in the
// database. This is to provide information to the
// people who may just need it.
for i, output := range revision.NewValidProofOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewOutput(revision.ParentID.StorageProofOutputID(true, uint64(i)), txid)
}
for i, output := range revision.NewMissedProofOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewOutput(revision.ParentID.StorageProofOutputID(false, uint64(i)), txid)
}
tx.addAddress(revision.NewUnlockHash, txid)
}
// Update the list of storage proofs
for _, proof := range txn.StorageProofs {
tx.addFcProof(proof.ParentID, txid)
}
// Append all the siafund inputs to the modification list
for _, input := range txn.SiafundInputs {
tx.addSiafundInput(input.ParentID, txid)
}
// Handle all the siafund outputs
for i, output := range txn.SiafundOutputs {
tx.addAddress(output.UnlockHash, txid)
tx.addNewSFOutput(txn.SiafundOutputID(uint64(i)), txid)
}
tx.putObject("Hashes", txid, hashTransaction)
}
示例6: rpcRevise
// rpcRevise is an RPC that allows a renter to revise a file contract. It will
// read new revisions in a loop until the renter sends a termination signal.
func (h *Host) rpcRevise(conn net.Conn) error {
// read ID of contract to be revised
var fcid types.FileContractID
if err := encoding.ReadObject(conn, &fcid, crypto.HashSize); err != nil {
return err
}
lockID := h.mu.RLock()
obligation, exists := h.obligationsByID[fcid]
h.mu.RUnlock(lockID)
if !exists {
return errors.New("no record of that contract")
}
// need to protect against two simultaneous revisions to the same
// contract; this can cause inconsistency and data loss, making storage
// proofs impossible
obligation.mu.Lock()
defer obligation.mu.Unlock()
// open the file in append mode
file, err := os.OpenFile(obligation.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
if err != nil {
return err
}
defer func() {
// if a newly-created file was not updated, remove it
if stat, _ := file.Stat(); stat.Size() == 0 {
os.Remove(obligation.Path)
}
file.Close()
}()
// rebuild current Merkle tree
tree := crypto.NewTree()
buf := make([]byte, crypto.SegmentSize)
for {
_, err := io.ReadFull(file, buf)
if err == io.EOF {
break
} else if err != nil && err != io.ErrUnexpectedEOF {
return err
}
tree.Push(buf)
}
// accept new revisions in a loop. The final good transaction will be
// submitted to the blockchain.
var finalTxn types.Transaction
defer func() {
h.tpool.AcceptTransactionSet([]types.Transaction{finalTxn})
}()
for {
// read proposed revision
var revTxn types.Transaction
if err := encoding.ReadObject(conn, &revTxn, types.BlockSizeLimit); err != nil {
return err
}
// an empty transaction indicates completion
if revTxn.ID() == (types.Transaction{}).ID() {
break
}
// check revision against original file contract
lockID = h.mu.RLock()
err := h.considerRevision(revTxn, obligation)
h.mu.RUnlock(lockID)
if err != nil {
encoding.WriteObject(conn, err.Error())
continue // don't terminate loop; subsequent revisions may be okay
}
// indicate acceptance
if err := encoding.WriteObject(conn, modules.AcceptResponse); err != nil {
return err
}
// read piece
// TODO: simultaneously read into tree?
rev := revTxn.FileContractRevisions[0]
piece := make([]byte, rev.NewFileSize-obligation.FileContract.FileSize)
_, err = io.ReadFull(conn, piece)
if err != nil {
return err
}
// verify Merkle root
r := bytes.NewReader(piece)
for {
_, err := io.ReadFull(r, buf)
if err == io.EOF {
break
} else if err != nil && err != io.ErrUnexpectedEOF {
return err
}
tree.Push(buf)
}
if tree.Root() != rev.NewFileMerkleRoot {
return errors.New("revision has bad Merkle root")
//.........這裏部分代碼省略.........
示例7: rpcContract
// rpcContract is an RPC that negotiates a file contract. If the
// negotiation is successful, the file is downloaded and the host begins
// submitting proofs of storage.
func (h *Host) rpcContract(conn net.Conn) (err error) {
// Read the contract terms.
var terms modules.ContractTerms
err = encoding.ReadObject(conn, &terms, maxContractLen)
if err != nil {
return
}
// Consider the contract terms. If they are unacceptable, return an error
// describing why.
lockID := h.mu.RLock()
err = h.considerTerms(terms)
h.mu.RUnlock(lockID)
if err != nil {
err = encoding.WriteObject(conn, err.Error())
return
}
// terms are acceptable; allocate space for file
lockID = h.mu.Lock()
file, path, err := h.allocate(terms.FileSize)
h.mu.Unlock(lockID)
if err != nil {
return
}
defer file.Close()
// rollback everything if something goes wrong
defer func() {
lockID := h.mu.Lock()
defer h.mu.Unlock(lockID)
if err != nil {
h.deallocate(terms.FileSize, path)
}
}()
// signal that we are ready to download file
err = encoding.WriteObject(conn, modules.AcceptTermsResponse)
if err != nil {
return
}
// simultaneously download file and calculate its Merkle root.
tee := io.TeeReader(
// use a LimitedReader to ensure we don't read indefinitely
io.LimitReader(conn, int64(terms.FileSize)),
// each byte we read from tee will also be written to file
file,
)
merkleRoot, err := crypto.ReaderMerkleRoot(tee)
if err != nil {
return
}
// Data has been sent, read in the unsigned transaction with the file
// contract.
var unsignedTxn types.Transaction
err = encoding.ReadObject(conn, &unsignedTxn, maxContractLen)
if err != nil {
return
}
// Verify that the transaction matches the agreed upon terms, and that the
// Merkle root in the file contract matches our independently calculated
// Merkle root.
err = verifyTransaction(unsignedTxn, terms, merkleRoot)
if err != nil {
err = errors.New("transaction does not satisfy terms: " + err.Error())
return
}
// Add the collateral to the transaction, but do not sign the transaction.
collateralTxn, txnBuilder, err := h.addCollateral(unsignedTxn, terms)
if err != nil {
return
}
err = encoding.WriteObject(conn, collateralTxn)
if err != nil {
return
}
// Read in the renter-signed transaction and check that it matches the
// previously accepted transaction.
var signedTxn types.Transaction
err = encoding.ReadObject(conn, &signedTxn, maxContractLen)
if err != nil {
return
}
if collateralTxn.ID() != signedTxn.ID() {
err = errors.New("signed transaction does not match the transaction with collateral")
return
}
// Add the signatures from the renter signed transaction, and then sign the
// transaction, then submit the transaction.
for _, sig := range signedTxn.TransactionSignatures {
txnBuilder.AddTransactionSignature(sig)
//.........這裏部分代碼省略.........
示例8: rpcRevise
// rpcRevise is an RPC that allows a renter to revise a file contract. It will
// read new revisions in a loop until the renter sends a termination signal.
func (h *Host) rpcRevise(conn net.Conn) error {
// read ID of contract to be revised
var fcid types.FileContractID
if err := encoding.ReadObject(conn, &fcid, crypto.HashSize); err != nil {
return errors.New("couldn't read contract ID: " + err.Error())
}
// remove conn deadline while we wait for lock and rebuild the Merkle tree
conn.SetDeadline(time.Time{})
h.mu.RLock()
obligation, exists := h.obligationsByID[fcid]
h.mu.RUnlock()
if !exists {
return errors.New("no record of that contract")
}
// need to protect against two simultaneous revisions to the same
// contract; this can cause inconsistency and data loss, making storage
// proofs impossible
obligation.mu.Lock()
defer obligation.mu.Unlock()
// open the file in append mode
file, err := os.OpenFile(obligation.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
if err != nil {
return err
}
// rebuild current Merkle tree
tree := crypto.NewTree()
err = tree.ReadSegments(file)
if err != nil {
file.Close()
return err
}
// accept new revisions in a loop. The final good transaction will be
// submitted to the blockchain.
revisionErr := func() error {
for {
// allow 2 minutes between revisions
conn.SetDeadline(time.Now().Add(2 * time.Minute))
// read proposed revision
var revTxn types.Transaction
if err := encoding.ReadObject(conn, &revTxn, types.BlockSizeLimit); err != nil {
return errors.New("couldn't read revision: " + err.Error())
}
// an empty transaction indicates completion
if revTxn.ID() == (types.Transaction{}).ID() {
return nil
}
// allow 5 minutes for each revision
conn.SetDeadline(time.Now().Add(5 * time.Minute))
// check revision against original file contract
h.mu.RLock()
err := h.considerRevision(revTxn, *obligation)
h.mu.RUnlock()
if err != nil {
encoding.WriteObject(conn, err.Error())
continue // don't terminate loop; subsequent revisions may be okay
}
// indicate acceptance
if err := encoding.WriteObject(conn, modules.AcceptResponse); err != nil {
return errors.New("couldn't write acceptance: " + err.Error())
}
// read piece
// TODO: simultaneously read into tree and file
rev := revTxn.FileContractRevisions[0]
last := obligation.LastRevisionTxn.FileContractRevisions[0]
piece := make([]byte, rev.NewFileSize-last.NewFileSize)
_, err = io.ReadFull(conn, piece)
if err != nil {
return errors.New("couldn't read piece data: " + err.Error())
}
// verify Merkle root
err = tree.ReadSegments(bytes.NewReader(piece))
if err != nil {
return errors.New("couldn't verify Merkle root: " + err.Error())
}
if tree.Root() != rev.NewFileMerkleRoot {
return errors.New("revision has bad Merkle root")
}
// manually sign the transaction
revTxn.TransactionSignatures = append(revTxn.TransactionSignatures, types.TransactionSignature{
ParentID: crypto.Hash(fcid),
CoveredFields: types.CoveredFields{FileContractRevisions: []uint64{0}},
PublicKeyIndex: 1, // host key is always second
})
encodedSig, err := crypto.SignHash(revTxn.SigHash(1), h.secretKey)
if err != nil {
//.........這裏部分代碼省略.........