transactions.go 4.5 KB
Newer Older
1
package consensus
2 3 4 5 6

import (
	"errors"
)

7 8 9 10
// validInput returns err = nil if the input is valid within the current state,
// otherwise returns an error explaining what wasn't valid.
func (s *State) validInput(input Input) (err error) {
	// Check the input spends an existing and valid output.
11
	_, exists := s.unspentOutputs[input.OutputID]
12 13 14 15 16 17
	if !exists {
		err = errors.New("transaction spends a nonexisting output")
		return
	}

	// Check that the spend conditions match the hash listed in the output.
18
	if input.SpendConditions.CoinAddress() != s.unspentOutputs[input.OutputID].SpendHash {
19 20 21 22 23
		err = errors.New("spend conditions do not match hash")
		return
	}

	// Check the timelock on the spend conditions is expired.
24
	if input.SpendConditions.TimeLock > s.height() {
25 26 27 28 29
		err = errors.New("output spent before timelock expiry.")
		return
	}

	return
30 31
}

32
// ValidTransaction returns err = nil if the transaction is valid, otherwise
33
// returns an error explaining what wasn't valid.
34
func (s *State) validTransaction(t Transaction) (err error) {
35 36 37 38 39 40
	// Check that the storage proof guidelines are followed.
	if !t.validStorageProofs() {
		err = errors.New("transaction contains storage proofs and conflicts")
		return
	}

41
	// Validate each input and get the total amount of Currency.
42
	inputSum := Currency(0)
43
	for _, input := range t.Inputs {
44 45 46
		// Check that the input is valid.
		err = s.validInput(input)
		if err != nil {
47 48 49
			return
		}

50
		// Add the input value to the coin sum.
51
		inputSum += s.unspentOutputs[input.OutputID].Value
52 53 54 55
	}

	// Verify the contracts and tally up the expenditures.
	for _, contract := range t.FileContracts {
56 57
		err = s.validContract(contract)
		if err != nil {
58 59 60 61
			return
		}
	}

62
	// Check that all provided proofs are valid.
63
	for _, proof := range t.StorageProofs {
64 65
		err = s.validProof(proof)
		if err != nil {
66 67 68 69
			return
		}
	}

David Vorick's avatar
David Vorick committed
70
	// Check that the inputs equal the outputs.
71
	if inputSum != t.OutputSum() {
David Vorick's avatar
David Vorick committed
72
		err = errors.New("inputs do not equal outputs for transaction.")
73 74 75
		return
	}

David Vorick's avatar
David Vorick committed
76
	// Check all of the signatures for validity.
77
	err = s.validSignatures(t)
78 79 80
	if err != nil {
		return
	}
81

82 83
	return
}
84

85 86 87 88 89 90 91 92 93 94 95 96
// applyTransaction() takes a transaction and adds it to the
// ConsensusState, updating the list of contracts, outputs, etc.
func (s *State) applyTransaction(t Transaction) (outputDiffs []OutputDiff, contractDiffs []ContractDiff) {
	// Remove all inputs from the unspent outputs list.
	for _, input := range t.Inputs {
		// Sanity check - the input must exist within the blockchain, should
		// have already been verified.
		if DEBUG {
			_, exists := s.unspentOutputs[input.OutputID]
			if !exists {
				panic("Applying a transaction with an invalid unspent output!")
			}
97
		}
David Vorick's avatar
David Vorick committed
98

99 100 101 102 103 104 105
		outputDiff := OutputDiff{
			New:    false,
			ID:     input.OutputID,
			Output: s.unspentOutputs[input.OutputID],
		}
		outputDiffs = append(outputDiffs, outputDiff)
		delete(s.unspentOutputs, input.OutputID)
David Vorick's avatar
David Vorick committed
106 107
	}

108 109 110 111 112 113 114 115
	// Add all finanacial outputs to the unspent outputs list.
	for i, output := range t.Outputs {
		// Sanity check - the output must not exist within the state, should
		// have already been verified.
		if DEBUG {
			_, exists := s.unspentOutputs[t.OutputID(i)]
			if exists {
				panic("applying a  transaction with an invalid new output")
David Vorick's avatar
David Vorick committed
116
			}
117
		}
David Vorick's avatar
David Vorick committed
118

119 120 121 122
		diff := OutputDiff{
			New:    true,
			ID:     t.OutputID(i),
			Output: output,
David Vorick's avatar
David Vorick committed
123
		}
124 125
		s.unspentOutputs[t.OutputID(i)] = output
		outputDiffs = append(outputDiffs, diff)
126 127
	}

128 129 130 131 132 133 134 135 136 137 138 139
	// Add all outputs created by storage proofs.
	for _, sp := range t.StorageProofs {
		outputDiff, contractDiff := s.applyStorageProof(sp)
		outputDiffs = append(outputDiffs, outputDiff)
		contractDiffs = append(contractDiffs, contractDiff)
	}

	// Add all new contracts to the OpenContracts list.
	for i, contract := range t.FileContracts {
		contractDiff := s.applyContract(contract, t.FileContractID(i))
		contractDiffs = append(contractDiffs, contractDiff)
	}
140 141 142
	return
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
// OutputSum returns the sum of all the outputs in the transaction, which must
// match the sum of all the inputs. Outputs created by storage proofs are not
// considered, as they were already considered when the contract was created.
func (t Transaction) OutputSum() (sum Currency) {
	// Add the miner fees.
	for _, fee := range t.MinerFees {
		sum += fee
	}

	// Add the contract payouts
	for _, contract := range t.FileContracts {
		sum += contract.Payout
	}

	// Add the outputs
	for _, output := range t.Outputs {
		sum += output.Value
	}

	return
}

165 166 167 168 169
func (s *State) ValidTransaction(t Transaction) (err error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.validTransaction(t)
}