Commit 09802e3b authored by David Vorick's avatar David Vorick

implement contract terminations

parent adb71da2
......@@ -73,6 +73,7 @@ func (s *State) validProof(sp StorageProof) error {
// it to the state.
func (s *State) applyFileContracts(bn *blockNode, t Transaction) {
for i, fc := range t.FileContracts {
// TODO: Sanity check.
// Apply the contract.
fcid := t.FileContractID(i)
s.openFileContracts[fcid] = fc
......@@ -88,6 +89,38 @@ func (s *State) applyFileContracts(bn *blockNode, t Transaction) {
return
}
func (s *State) applyFileContractTerminations(bn *blockNode, t Transaction) {
for _, fct := range t.FileContractTerminations {
// Delete the contract.
fc, exists := s.openFileContracts[fct.ParentID]
// Sanity check - termination should be terminating an existing
// contract.
if !exists {
if DEBUG {
panic("file contract termination terminates a nonexisting contract")
} else {
return
}
}
delete(s.openFileContracts, fct.ParentID)
// Add the diff for the deletion to the block node.
fcd := FileContractDiff{
New: false,
ID: fct.ParentID,
FileContract: fc,
}
bn.fileContractDiffs = append(bn.fileContractDiffs, fcd)
// Add all of the payouts and diffs.
for i, payout := range fct.Payouts {
id := fct.ParentID.FileContractTerminationPayoutID(i)
s.delayedSiacoinOutputs[s.height()][id] = payout
bn.delayedSiacoinOutputs[id] = payout
}
}
}
// splitContractPayout takes a contract payout as input and returns the portion
// of the payout that goes to the pool, as well as the portion that goes to the
// siacoin output. They should add to the original payout.
......
......@@ -121,29 +121,43 @@ func (s *State) validSignatures(t Transaction) (err error) {
// Create the InputSignatures object for each input.
sigMap := make(map[string]*InputSignatures)
for i, input := range t.SiacoinInputs {
stringID := string(input.OutputID[:])
stringID := string(input.ParentID[:])
_, exists := sigMap[stringID]
if exists {
return errors.New("siacoin output spent twice in the same transaction.")
}
inSig := &InputSignatures{
RemainingSignatures: input.SpendConditions.NumSignatures,
PossibleKeys: input.SpendConditions.PublicKeys,
RemainingSignatures: input.UnlockConditions.NumSignatures,
PossibleKeys: input.UnlockConditions.PublicKeys,
Index: i,
}
sigMap[stringID] = inSig
}
for i, input := range t.SiafundInputs {
stringID := string(input.OutputID[:])
stringID := string(input.ParentID[:])
_, exists := sigMap[stringID]
if exists {
return errors.New("siafund output spent twice in the same transaction.")
}
inSig := &InputSignatures{
RemainingSignatures: input.SpendConditions.NumSignatures,
PossibleKeys: input.SpendConditions.PublicKeys,
RemainingSignatures: input.UnlockConditions.NumSignatures,
PossibleKeys: input.UnlockConditions.PublicKeys,
Index: i,
}
sigMap[stringID] = inSig
}
for i, termination := range t.FileContractTerminations {
stringID := string(termination.ParentID[:])
_, exists := sigMap[stringID]
if exists {
return errors.New("file contract terminated twice in the same transaction.")
}
inSig := &InputSignatures{
RemainingSignatures: termination.TerminationConditions.NumSignatures,
PossibleKeys: termination.TerminationConditions.PublicKeys,
Index: i,
}
sigMap[stringID] = inSig
......
......@@ -83,7 +83,43 @@ func (s *State) validSiafundInput(sfi SiafundInput) (sfo SiafundOutput, err erro
return
}
// TODO: Add validFileContractTermination
func (s *State) validFileContractTermination(fct FileContractTermination) (err error) {
// Check that the FileContractTermination terminates an existing
// FileContract.
fc, exists := s.openFileContracts[fct.ParentID]
if !exists {
err = ErrMissingFileContract
return
}
// Check that the spend conditions match the hash listed in the output.
if fct.TerminationConditions.UnlockHash() != fc.TerminationHash {
err = errors.New("spend conditions do not match hash")
return
}
// Check the timelock on the spend conditions is expired.
if fct.TerminationConditions.Timelock > s.height() {
err = errors.New("contract terminated before timelock expiry.")
return
}
// Check that the payouts in the termination add up to the payout of the
// contract.
var payoutSum Currency
for _, payout := range fct.Payouts {
err = payoutSum.Add(payout.Value)
if err != nil {
return
}
}
if payoutSum.Cmp(fc.Payout) != 0 {
err = errors.New("contract termination has incorrect payouts")
return
}
return
}
// validSiafunds checks that the transaction has valid siafund inputs and
// outputs, and that the sum of the inputs matches the sum of the outputs.
......@@ -162,6 +198,12 @@ func (s *State) validTransaction(t Transaction) (err error) {
return
}
}
for _, termination := range t.FileContractTerminations {
err = s.validFileContractTermination(termination)
if err != nil {
return
}
}
for _, proof := range t.StorageProofs {
err = s.validProof(proof)
if err != nil {
......@@ -331,6 +373,7 @@ func (s *State) applyTransaction(bn *blockNode, t Transaction) {
s.applySiacoinInputs(bn, t)
s.applySiacoinOutputs(bn, t)
s.applyFileContracts(bn, t)
s.applyFileContractTerminations(bn, t)
s.applyStorageProofs(bn, t)
s.applySiafundInputs(bn, t)
s.applySiafundOutputs(bn, t)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment