diff --git a/consensus/merkle.go b/consensus/merkle.go index c650316b..24dc281d 100644 --- a/consensus/merkle.go +++ b/consensus/merkle.go @@ -75,94 +75,38 @@ func (l elementLeaf) proofRoot() types.Hash256 { // chainIndexLeaf returns the elementLeaf for a ChainIndexElement. func chainIndexLeaf(e *types.ChainIndexElement) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/chainindex") - e.StateElement.ID.EncodeTo(h.E) - e.ChainIndex.EncodeTo(h.E) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: false, - } + elemHash := hashAll("leaf/chainindex", e.ID, e.ChainIndex) + return elementLeaf{&e.StateElement, elemHash, false} } // siacoinLeaf returns the elementLeaf for a SiacoinElement. func siacoinLeaf(e *types.SiacoinElement, spent bool) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/siacoin") - e.ID.EncodeTo(h.E) - e.SiacoinOutput.EncodeTo(h.E) - h.E.WriteUint64(e.MaturityHeight) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: spent, - } + elemHash := hashAll("leaf/siacoin", e.ID, e.SiacoinOutput, e.MaturityHeight) + return elementLeaf{&e.StateElement, elemHash, spent} } // siafundLeaf returns the elementLeaf for a SiafundElement. func siafundLeaf(e *types.SiafundElement, spent bool) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/siafund") - e.ID.EncodeTo(h.E) - e.SiafundOutput.EncodeTo(h.E) - e.ClaimStart.EncodeTo(h.E) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: spent, - } + elemHash := hashAll("leaf/siafund", e.ID, e.SiafundOutput, e.ClaimStart) + return elementLeaf{&e.StateElement, elemHash, spent} } // fileContractLeaf returns the elementLeaf for a FileContractElement. func fileContractLeaf(e *types.FileContractElement, spent bool) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/filecontract") - e.ID.EncodeTo(h.E) - e.FileContract.EncodeTo(h.E) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: spent, - } + elemHash := hashAll("leaf/filecontract", e.ID, e.FileContract) + return elementLeaf{&e.StateElement, elemHash, spent} } // v2FileContractLeaf returns the elementLeaf for a V2FileContractElement. func v2FileContractLeaf(e *types.V2FileContractElement, spent bool) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/v2filecontract") - e.ID.EncodeTo(h.E) - e.V2FileContract.EncodeTo(h.E) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: spent, - } + elemHash := hashAll("leaf/v2filecontract", e.ID, e.V2FileContract) + return elementLeaf{&e.StateElement, elemHash, spent} } // attestationLeaf returns the elementLeaf for an AttestationElement. func attestationLeaf(e *types.AttestationElement) elementLeaf { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("leaf/attestation") - e.StateElement.ID.EncodeTo(h.E) - e.Attestation.EncodeTo(h.E) - return elementLeaf{ - StateElement: &e.StateElement, - ElementHash: h.Sum(), - Spent: false, - } + elemHash := hashAll("leaf/attestation", e.ID, e.Attestation) + return elementLeaf{&e.StateElement, elemHash, false} } // An ElementAccumulator tracks the state of an unbounded number of elements diff --git a/consensus/state.go b/consensus/state.go index 62d544ee..43b3326b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -3,6 +3,7 @@ package consensus import ( "encoding/binary" + "fmt" "math/big" "math/bits" "sort" @@ -19,6 +20,29 @@ import ( // implementation whose constructor returns a concrete type. var hasherPool = &sync.Pool{New: func() interface{} { return types.NewHasher() }} +func hashAll(elems ...interface{}) [32]byte { + h := hasherPool.Get().(*types.Hasher) + defer hasherPool.Put(h) + h.Reset() + for _, e := range elems { + if et, ok := e.(types.EncoderTo); ok { + et.EncodeTo(h.E) + } else { + switch e := e.(type) { + case string: + h.WriteDistinguisher(e) + case uint8: + h.E.WriteUint8(e) + case uint64: + h.E.WriteUint64(e) + default: + panic(fmt.Sprintf("unhandled type %T", e)) + } + } + } + return h.Sum() +} + // A Network specifies the fixed parameters of a Sia blockchain. type Network struct { Name string `json:"name"` @@ -334,7 +358,7 @@ func (s State) StorageProofLeafIndex(filesize uint64, windowID types.BlockID, fc if numLeaves == 0 { return 0 } - seed := types.HashBytes(append(windowID[:], fcid[:]...)) + seed := hashAll(windowID, fcid) var r uint64 for i := 0; i < len(seed); i += 8 { _, r = bits.Div64(r, binary.BigEndian.Uint64(seed[i:]), numLeaves) @@ -491,123 +515,35 @@ func (s *State) TransactionsCommitment(txns []types.Transaction, v2txns []types. // Commitment computes the commitment hash for a child block with the given // transactions and miner address. func (s State) Commitment(txnsHash types.Hash256, minerAddr types.Address) types.Hash256 { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - s.EncodeTo(h.E) - stateHash := h.Sum() - h.Reset() - h.WriteDistinguisher("commitment") - h.E.WriteUint8(s.v2ReplayPrefix()) - stateHash.EncodeTo(h.E) - minerAddr.EncodeTo(h.E) - txnsHash.EncodeTo(h.E) - return h.Sum() + // NOTE: The state is hashed separately so that miners don't have to rehash + // it every time the transaction set changes + stateHash := types.Hash256(hashAll(s)) + return hashAll("commitment", s.v2ReplayPrefix(), stateHash, minerAddr, txnsHash) } // InputSigHash returns the hash that must be signed for each v2 transaction input. func (s State) InputSigHash(txn types.V2Transaction) types.Hash256 { - // NOTE: This currently covers exactly the same fields as txn.ID(), for the - // same reasons. - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("sig/input") - h.E.WriteUint8(s.v2ReplayPrefix()) - h.E.WritePrefix(len(txn.SiacoinInputs)) - for _, in := range txn.SiacoinInputs { - in.Parent.ID.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiacoinOutputs)) - for _, out := range txn.SiacoinOutputs { - out.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiafundInputs)) - for _, in := range txn.SiafundInputs { - in.Parent.ID.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiafundOutputs)) - for _, out := range txn.SiafundOutputs { - out.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContracts)) - for _, fc := range txn.FileContracts { - fc.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContractRevisions)) - for _, fcr := range txn.FileContractRevisions { - fcr.Parent.ID.EncodeTo(h.E) - fcr.Revision.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContractResolutions)) - for _, fcr := range txn.FileContractResolutions { - fcr.Parent.ID.EncodeTo(h.E) - // normalize proof - if sp, ok := fcr.Resolution.(*types.V2StorageProof); ok { - c := *sp // don't modify original - c.ProofIndex.MerkleProof = nil - fcr.Resolution = &c - } - fcr.Resolution.(types.EncoderTo).EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.Attestations)) - for _, a := range txn.Attestations { - a.EncodeTo(h.E) - } - h.E.WriteBytes(txn.ArbitraryData) - h.E.WriteBool(txn.NewFoundationAddress != nil) - if txn.NewFoundationAddress != nil { - txn.NewFoundationAddress.EncodeTo(h.E) - } - txn.MinerFee.EncodeTo(h.E) - return h.Sum() + return hashAll("sig/input", s.v2ReplayPrefix(), types.V2TransactionSemantics(txn)) } // ContractSigHash returns the hash that must be signed for a v2 contract revision. func (s State) ContractSigHash(fc types.V2FileContract) types.Hash256 { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("sig/filecontract") - h.E.WriteUint8(s.v2ReplayPrefix()) - h.E.WriteUint64(fc.Filesize) - fc.FileMerkleRoot.EncodeTo(h.E) - h.E.WriteUint64(fc.ProofHeight) - h.E.WriteUint64(fc.ExpirationHeight) - fc.RenterOutput.EncodeTo(h.E) - fc.HostOutput.EncodeTo(h.E) - fc.MissedHostValue.EncodeTo(h.E) - fc.RenterPublicKey.EncodeTo(h.E) - fc.HostPublicKey.EncodeTo(h.E) - h.E.WriteUint64(fc.RevisionNumber) - return h.Sum() + fc.RenterSignature, fc.HostSignature = types.Signature{}, types.Signature{} + return hashAll("sig/filecontract", s.v2ReplayPrefix(), fc) } // RenewalSigHash returns the hash that must be signed for a file contract renewal. func (s State) RenewalSigHash(fcr types.V2FileContractRenewal) types.Hash256 { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("sig/filecontractrenewal") - h.E.WriteUint8(s.v2ReplayPrefix()) - fcr.FinalRevision.EncodeTo(h.E) - fcr.InitialRevision.EncodeTo(h.E) - fcr.RenterRollover.EncodeTo(h.E) - fcr.HostRollover.EncodeTo(h.E) - return h.Sum() + fcr.InitialRevision.RenterSignature, fcr.InitialRevision.HostSignature = types.Signature{}, types.Signature{} + fcr.FinalRevision.RenterSignature, fcr.FinalRevision.HostSignature = types.Signature{}, types.Signature{} + fcr.RenterSignature, fcr.HostSignature = types.Signature{}, types.Signature{} + return hashAll("sig/filecontractrenewal", s.v2ReplayPrefix(), fcr) } // AttestationSigHash returns the hash that must be signed for an attestation. func (s State) AttestationSigHash(a types.Attestation) types.Hash256 { - h := hasherPool.Get().(*types.Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("sig/attestation") - h.E.WriteUint8(s.v2ReplayPrefix()) - a.PublicKey.EncodeTo(h.E) - h.E.WriteString(a.Key) - h.E.WriteBytes(a.Value) - return h.Sum() + a.Signature = types.Signature{} + return hashAll("sig/attestation", s.v2ReplayPrefix(), a) } // A MidState represents the state of the chain within a block. diff --git a/types/encoding.go b/types/encoding.go index c807c058..c6a42a6f 100644 --- a/types/encoding.go +++ b/types/encoding.go @@ -385,16 +385,9 @@ func (ts TransactionSignature) EncodeTo(e *Encoder) { e.WriteBytes(ts.Signature) } -// EncodeTo implements types.EncoderTo. -func (txn Transaction) EncodeTo(e *Encoder) { - txn.encodeNoSignatures(e) - e.WritePrefix(len((txn.Signatures))) - for i := range txn.Signatures { - txn.Signatures[i].EncodeTo(e) - } -} +type txnSansSigs Transaction -func (txn *Transaction) encodeNoSignatures(e *Encoder) { +func (txn txnSansSigs) EncodeTo(e *Encoder) { e.WritePrefix(len((txn.SiacoinInputs))) for i := range txn.SiacoinInputs { txn.SiacoinInputs[i].EncodeTo(e) @@ -433,6 +426,15 @@ func (txn *Transaction) encodeNoSignatures(e *Encoder) { } } +// EncodeTo implements types.EncoderTo. +func (txn Transaction) EncodeTo(e *Encoder) { + txnSansSigs(txn).EncodeTo(e) + e.WritePrefix(len((txn.Signatures))) + for i := range txn.Signatures { + txn.Signatures[i].EncodeTo(e) + } +} + func (p SpendPolicy) encodePolicy(e *Encoder) { const ( opInvalid = iota @@ -721,6 +723,73 @@ func (txn V2Transaction) EncodeTo(e *Encoder) { } } +// V2TransactionSemantics is a helper type that provides a "semantic encoding" +// of a v2 transaction, for use in computing IDs and signature hashes. +type V2TransactionSemantics V2Transaction + +// EncodeTo implements types.EncoderTo. +func (txn V2TransactionSemantics) EncodeTo(e *Encoder) { + e.WritePrefix(len(txn.SiacoinInputs)) + for _, in := range txn.SiacoinInputs { + in.Parent.ID.EncodeTo(e) + } + e.WritePrefix(len(txn.SiacoinOutputs)) + for _, out := range txn.SiacoinOutputs { + out.EncodeTo(e) + } + e.WritePrefix(len(txn.SiafundInputs)) + for _, in := range txn.SiafundInputs { + in.Parent.ID.EncodeTo(e) + } + e.WritePrefix(len(txn.SiafundOutputs)) + for _, out := range txn.SiafundOutputs { + out.EncodeTo(e) + } + e.WritePrefix(len(txn.FileContracts)) + for _, fc := range txn.FileContracts { + fc.RenterSignature, fc.HostSignature = Signature{}, Signature{} + fc.EncodeTo(e) + } + e.WritePrefix(len(txn.FileContractRevisions)) + for _, fcr := range txn.FileContractRevisions { + fcr.Revision.RenterSignature, fcr.Revision.HostSignature = Signature{}, Signature{} + fcr.Parent.ID.EncodeTo(e) + fcr.Revision.EncodeTo(e) + } + e.WritePrefix(len(txn.FileContractResolutions)) + for _, fcr := range txn.FileContractResolutions { + fcr.Parent.ID.EncodeTo(e) + // normalize (being careful not to modify the original) + switch res := fcr.Resolution.(type) { + case *V2FileContractFinalization: + fc := *res + fc.RenterSignature, fc.HostSignature = Signature{}, Signature{} + fcr.Resolution = &fc + case *V2FileContractRenewal: + renewal := *res + renewal.InitialRevision.RenterSignature, renewal.InitialRevision.HostSignature = Signature{}, Signature{} + renewal.FinalRevision.RenterSignature, renewal.FinalRevision.HostSignature = Signature{}, Signature{} + renewal.RenterSignature, renewal.HostSignature = Signature{}, Signature{} + fcr.Resolution = &renewal + case *V2StorageProof: + sp := *res + sp.ProofIndex.MerkleProof = nil + fcr.Resolution = &sp + } + fcr.Resolution.(EncoderTo).EncodeTo(e) + } + e.WritePrefix(len(txn.Attestations)) + for _, a := range txn.Attestations { + a.EncodeTo(e) + } + e.WriteBytes(txn.ArbitraryData) + e.WriteBool(txn.NewFoundationAddress != nil) + if txn.NewFoundationAddress != nil { + txn.NewFoundationAddress.EncodeTo(e) + } + txn.MinerFee.EncodeTo(e) +} + // EncodeTo implements types.EncoderTo. func (b V2BlockData) EncodeTo(e *Encoder) { e.WriteUint64(b.Height) diff --git a/types/hash.go b/types/hash.go index c8b2a958..2005cfdd 100644 --- a/types/hash.go +++ b/types/hash.go @@ -51,6 +51,29 @@ func NewHasher() *Hasher { // implementation whose constructor returns a concrete type. var hasherPool = &sync.Pool{New: func() interface{} { return NewHasher() }} +func hashAll(elems ...interface{}) [32]byte { + h := hasherPool.Get().(*Hasher) + defer hasherPool.Put(h) + h.Reset() + for _, e := range elems { + if et, ok := e.(EncoderTo); ok { + et.EncodeTo(h.E) + } else { + switch e := e.(type) { + case string: + h.WriteDistinguisher(e) + case int: + h.E.WriteUint64(uint64(e)) + case bool: + h.E.WriteBool(e) + default: + panic("unhandled type") + } + } + } + return h.Sum() +} + const leafHashPrefix = 0 // from RFC 6962 // StandardAddress returns the standard v2 Address derived from pk. It is diff --git a/types/types.go b/types/types.go index a89b276b..59ca0864 100644 --- a/types/types.go +++ b/types/types.go @@ -165,18 +165,12 @@ func (bid BlockID) CmpWork(t BlockID) int { // MinerOutputID returns the ID of the block's i'th miner payout. func (bid BlockID) MinerOutputID(i int) SiacoinOutputID { - buf := make([]byte, 32+8) - copy(buf, bid[:]) - binary.LittleEndian.PutUint64(buf[32:], uint64(i)) - return SiacoinOutputID(HashBytes(buf)) + return hashAll(bid, i) } // FoundationOutputID returns the ID of the block's Foundation subsidy. func (bid BlockID) FoundationOutputID() SiacoinOutputID { - buf := make([]byte, 32+16) - copy(buf, bid[:]) - copy(buf[32:], SpecifierFoundation[:]) - return SiacoinOutputID(HashBytes(buf)) + return hashAll(bid, SpecifierFoundation) } // A TransactionID uniquely identifies a transaction. @@ -218,18 +212,13 @@ type SiafundOutputID Hash256 // ClaimOutputID returns the ID of the SiacoinOutput that is created when // the siafund output is spent. func (sfoid SiafundOutputID) ClaimOutputID() SiacoinOutputID { - return SiacoinOutputID(HashBytes(sfoid[:])) + return hashAll(sfoid) } // V2ClaimOutputID returns the ID of the SiacoinOutput that is created when the // siafund output is spent. func (sfoid SiafundOutputID) V2ClaimOutputID() SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/siacoinclaimoutput") - sfoid.EncodeTo(h.E) - return SiacoinOutputID(h.Sum()) + return hashAll("id/v2siacoinclaimoutput", sfoid) } // A SiafundInput spends an unspent SiafundOutput in the UTXO set by revealing @@ -307,58 +296,27 @@ type FileContractID Hash256 // ValidOutputID returns the ID of the valid proof output at index i. func (fcid FileContractID) ValidOutputID(i int) SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - SpecifierStorageProof.EncodeTo(h.E) - fcid.EncodeTo(h.E) - h.E.WriteBool(true) - h.E.WriteUint64(uint64(i)) - return SiacoinOutputID(h.Sum()) + return hashAll(SpecifierStorageProof, fcid, true, i) } // MissedOutputID returns the ID of the missed proof output at index i. func (fcid FileContractID) MissedOutputID(i int) SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - SpecifierStorageProof.EncodeTo(h.E) - fcid.EncodeTo(h.E) - h.E.WriteBool(false) - h.E.WriteUint64(uint64(i)) - return SiacoinOutputID(h.Sum()) + return hashAll(SpecifierStorageProof, fcid, false, i) } // V2RenterOutputID returns the ID of the renter output for a v2 contract. func (fcid FileContractID) V2RenterOutputID() SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/v2filecontractoutput") - fcid.EncodeTo(h.E) - h.E.WriteUint64(0) - return SiacoinOutputID(h.Sum()) + return hashAll("id/v2filecontractoutput", fcid, 0) } // V2HostOutputID returns the ID of the host output for a v2 contract. func (fcid FileContractID) V2HostOutputID() SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/v2filecontractoutput") - fcid.EncodeTo(h.E) - h.E.WriteUint64(1) - return SiacoinOutputID(h.Sum()) + return hashAll("id/v2filecontractoutput", fcid, 1) } // V2RenewalID returns the ID of the renewal of a v2 contract. func (fcid FileContractID) V2RenewalID() FileContractID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/v2filecontractrenewal") - fcid.EncodeTo(h.E) - return FileContractID(h.Sum()) + return hashAll("id/v2filecontractrenewal", fcid) } // A FileContractRevision updates the state of an existing file contract. @@ -434,61 +392,34 @@ type Transaction struct { // // To hash all of the data in a transaction, use the FullHash method. func (txn *Transaction) ID() TransactionID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - txn.encodeNoSignatures(h.E) - return TransactionID(h.Sum()) + return hashAll((*txnSansSigs)(txn)) } // FullHash returns the hash of the transaction's binary encoding. This hash is // only used in specific circumstances; generally, ID should be used instead. func (txn *Transaction) FullHash() Hash256 { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - txn.EncodeTo(h.E) - return h.Sum() + return hashAll(txn) } // SiacoinOutputID returns the ID of the siacoin output at index i. func (txn *Transaction) SiacoinOutputID(i int) SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - SpecifierSiacoinOutput.EncodeTo(h.E) - txn.encodeNoSignatures(h.E) - h.E.WriteUint64(uint64(i)) - return SiacoinOutputID(h.Sum()) + return hashAll(SpecifierSiacoinOutput, (*txnSansSigs)(txn), i) } // SiafundOutputID returns the ID of the siafund output at index i. func (txn *Transaction) SiafundOutputID(i int) SiafundOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - SpecifierSiafundOutput.EncodeTo(h.E) - txn.encodeNoSignatures(h.E) - h.E.WriteUint64(uint64(i)) - return SiafundOutputID(h.Sum()) + return hashAll(SpecifierSiafundOutput, (*txnSansSigs)(txn), i) } // SiafundClaimOutputID returns the ID of the siacoin claim output for the // siafund input at index i. func (txn *Transaction) SiafundClaimOutputID(i int) SiacoinOutputID { - sfid := txn.SiafundOutputID(i) - return SiacoinOutputID(HashBytes(sfid[:])) + return hashAll(txn.SiafundOutputID(i)) } // FileContractID returns the ID of the file contract at index i. func (txn *Transaction) FileContractID(i int) FileContractID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - SpecifierFileContract.EncodeTo(h.E) - txn.encodeNoSignatures(h.E) - h.E.WriteUint64(uint64(i)) - return FileContractID(h.Sum()) + return hashAll(SpecifierFileContract, (*txnSansSigs)(txn), i) } // TotalFees returns the sum of the transaction's miner fees. @@ -721,111 +652,33 @@ type V2Transaction struct { // // To hash all of the data in a transaction, use the FullHash method. func (txn *V2Transaction) ID() TransactionID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/transaction") - h.E.WritePrefix(len(txn.SiacoinInputs)) - for _, in := range txn.SiacoinInputs { - in.Parent.ID.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiacoinOutputs)) - for _, out := range txn.SiacoinOutputs { - out.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiafundInputs)) - for _, in := range txn.SiafundInputs { - in.Parent.ID.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.SiafundOutputs)) - for _, out := range txn.SiafundOutputs { - out.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContracts)) - for _, fc := range txn.FileContracts { - fc.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContractRevisions)) - for _, fcr := range txn.FileContractRevisions { - fcr.Parent.ID.EncodeTo(h.E) - fcr.Revision.EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.FileContractResolutions)) - for _, fcr := range txn.FileContractResolutions { - fcr.Parent.ID.EncodeTo(h.E) - // normalize proof - if sp, ok := fcr.Resolution.(*V2StorageProof); ok { - c := *sp // don't modify original - c.ProofIndex.MerkleProof = nil - fcr.Resolution = &c - } - fcr.Resolution.(EncoderTo).EncodeTo(h.E) - } - h.E.WritePrefix(len(txn.Attestations)) - for _, a := range txn.Attestations { - a.EncodeTo(h.E) - } - h.E.WriteBytes(txn.ArbitraryData) - h.E.WriteBool(txn.NewFoundationAddress != nil) - if txn.NewFoundationAddress != nil { - txn.NewFoundationAddress.EncodeTo(h.E) - } - txn.MinerFee.EncodeTo(h.E) - return TransactionID(h.Sum()) + return hashAll("id/transaction", (*V2TransactionSemantics)(txn)) } // FullHash returns the hash of the transaction's binary encoding. This hash is // only used in specific circumstances; generally, ID should be used instead. func (txn *V2Transaction) FullHash() Hash256 { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - txn.EncodeTo(h.E) - return h.Sum() + return hashAll(txn) } // SiacoinOutputID returns the ID for the siacoin output at index i. func (*V2Transaction) SiacoinOutputID(txid TransactionID, i int) SiacoinOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/siacoinoutput") - txid.EncodeTo(h.E) - h.E.WriteUint64(uint64(i)) - return SiacoinOutputID(h.Sum()) + return hashAll("id/siacoinoutput", txid, i) } // SiafundOutputID returns the ID for the siafund output at index i. func (*V2Transaction) SiafundOutputID(txid TransactionID, i int) SiafundOutputID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/siafundoutput") - txid.EncodeTo(h.E) - h.E.WriteUint64(uint64(i)) - return SiafundOutputID(h.Sum()) + return hashAll("id/siafundoutput", txid, i) } // V2FileContractID returns the ID for the v2 file contract at index i. func (*V2Transaction) V2FileContractID(txid TransactionID, i int) FileContractID { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/filecontract") - txid.EncodeTo(h.E) - h.E.WriteUint64(uint64(i)) - return FileContractID(h.Sum()) + return hashAll("id/filecontract", txid, i) } // AttestationID returns the ID for the attestation at index i. func (*V2Transaction) AttestationID(txid TransactionID, i int) Hash256 { - h := hasherPool.Get().(*Hasher) - defer hasherPool.Put(h) - h.Reset() - h.WriteDistinguisher("id/attestation") - txid.EncodeTo(h.E) - h.E.WriteUint64(uint64(i)) - return h.Sum() + return hashAll("id/attestation", txid, i) } // EphemeralSiacoinOutput returns a SiacoinElement for the siacoin output at