diff --git a/consensus/state.go b/consensus/state.go index 9353634..85757d8 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -309,14 +309,14 @@ func (s State) V2TransactionWeight(txn types.V2Transaction) uint64 { var wc writeCounter e := types.NewEncoder(&wc) for _, sci := range txn.SiacoinInputs { - sci.Parent.MerkleProof = nil + sci.Parent.StateElement.MerkleProof = nil sci.EncodeTo(e) } for _, sco := range txn.SiacoinOutputs { types.V2SiacoinOutput(sco).EncodeTo(e) } for _, sfi := range txn.SiafundInputs { - sfi.Parent.MerkleProof = nil + sfi.Parent.StateElement.MerkleProof = nil sfi.EncodeTo(e) } for _, sfo := range txn.SiafundOutputs { @@ -326,14 +326,14 @@ func (s State) V2TransactionWeight(txn types.V2Transaction) uint64 { fc.EncodeTo(e) } for _, fcr := range txn.FileContractRevisions { - fcr.Parent.MerkleProof = nil + fcr.Parent.StateElement.MerkleProof = nil fcr.EncodeTo(e) } for _, fcr := range txn.FileContractResolutions { - fcr.Parent.MerkleProof = nil + fcr.Parent.StateElement.MerkleProof = nil if sp, ok := fcr.Resolution.(*types.V2StorageProof); ok { c := *sp // don't modify original - c.ProofIndex.MerkleProof = nil + c.ProofIndex.StateElement.MerkleProof = nil fcr.Resolution = &c } fcr.EncodeTo(e) @@ -589,12 +589,12 @@ func (s State) AttestationSigHash(a types.Attestation) types.Hash256 { // A MidState represents the state of the chain within a block. type MidState struct { base State - created map[types.Hash256]int // indices into element slices - spends map[types.Hash256]types.TransactionID - revs map[types.Hash256]*types.FileContractElement - res map[types.Hash256]bool - v2revs map[types.Hash256]*types.V2FileContractElement - v2res map[types.Hash256]types.V2FileContractResolutionType + created map[types.ElementID]int // indices into element slices + spends map[types.ElementID]types.TransactionID + revs map[types.FileContractID]*types.FileContractElement + res map[types.FileContractID]bool + v2revs map[types.FileContractID]*types.V2FileContractElement + v2res map[types.FileContractID]types.V2FileContractResolutionType siafundPool types.Currency foundationPrimary types.Address foundationFailsafe types.Address @@ -616,33 +616,33 @@ func (ms *MidState) siacoinElement(ts V1TransactionSupplement, id types.SiacoinO } func (ms *MidState) siafundElement(ts V1TransactionSupplement, id types.SiafundOutputID) (types.SiafundElement, bool) { - if i, ok := ms.created[types.Hash256(id)]; ok { + if i, ok := ms.created[id]; ok { return ms.sfes[i], true } return ts.siafundElement(id) } func (ms *MidState) fileContractElement(ts V1TransactionSupplement, id types.FileContractID) (types.FileContractElement, bool) { - if rev, ok := ms.revs[types.Hash256(id)]; ok { + if rev, ok := ms.revs[id]; ok { return *rev, true } - if i, ok := ms.created[types.Hash256(id)]; ok { + if i, ok := ms.created[id]; ok { return ms.fces[i], true } return ts.revision(id) } -func (ms *MidState) spent(id types.Hash256) (types.TransactionID, bool) { +func (ms *MidState) spent(id types.ElementID) (types.TransactionID, bool) { txid, ok := ms.spends[id] return txid, ok } -func (ms *MidState) isSpent(id types.Hash256) bool { +func (ms *MidState) isSpent(id types.ElementID) bool { _, ok := ms.spends[id] return ok } -func (ms *MidState) isCreated(id types.Hash256) bool { +func (ms *MidState) isCreated(id types.ElementID) bool { _, ok := ms.created[id] return ok || id == ms.cie.ID } @@ -651,12 +651,12 @@ func (ms *MidState) isCreated(id types.Hash256) bool { func NewMidState(s State) *MidState { return &MidState{ base: s, - created: make(map[types.Hash256]int), - spends: make(map[types.Hash256]types.TransactionID), - revs: make(map[types.Hash256]*types.FileContractElement), - res: make(map[types.Hash256]bool), - v2revs: make(map[types.Hash256]*types.V2FileContractElement), - v2res: make(map[types.Hash256]types.V2FileContractResolutionType), + created: make(map[types.ElementID]int), + spends: make(map[types.ElementID]types.TransactionID), + revs: make(map[types.FileContractID]*types.FileContractElement), + res: make(map[types.FileContractID]bool), + v2revs: make(map[types.FileContractID]*types.V2FileContractElement), + v2res: make(map[types.FileContractID]types.V2FileContractResolutionType), siafundPool: s.SiafundPool, foundationPrimary: s.FoundationPrimaryAddress, foundationFailsafe: s.FoundationFailsafeAddress, diff --git a/consensus/update.go b/consensus/update.go index f9aaa27..02fa1d1 100644 --- a/consensus/update.go +++ b/consensus/update.go @@ -335,9 +335,14 @@ func ApplyOrphan(s State, b types.Block, targetTimestamp time.Time) State { return next } +func dupProof(se *types.StateElement) { + se.MerkleProof = append([]types.Hash256(nil), se.MerkleProof...) +} + func (ms *MidState) addSiacoinElement(id types.SiacoinOutputID, sco types.SiacoinOutput) { sce := types.SiacoinElement{ - StateElement: types.StateElement{ID: types.Hash256(id)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: id, SiacoinOutput: sco, } ms.sces = append(ms.sces, sce) @@ -352,14 +357,15 @@ func (ms *MidState) addImmatureSiacoinElement(id types.SiacoinOutputID, sco type func (ms *MidState) spendSiacoinElement(sce types.SiacoinElement, txid types.TransactionID) { ms.spends[sce.ID] = txid if !ms.isCreated(sce.ID) { - sce.MerkleProof = append([]types.Hash256(nil), sce.MerkleProof...) + dupProof(&sce.StateElement) ms.sces = append(ms.sces, sce) } } func (ms *MidState) addSiafundElement(id types.SiafundOutputID, sfo types.SiafundOutput) { sfe := types.SiafundElement{ - StateElement: types.StateElement{ID: types.Hash256(id)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: id, SiafundOutput: sfo, ClaimStart: ms.siafundPool, } @@ -370,14 +376,15 @@ func (ms *MidState) addSiafundElement(id types.SiafundOutputID, sfo types.Siafun func (ms *MidState) spendSiafundElement(sfe types.SiafundElement, txid types.TransactionID) { ms.spends[sfe.ID] = txid if !ms.isCreated(sfe.ID) { - sfe.MerkleProof = append([]types.Hash256(nil), sfe.MerkleProof...) + dupProof(&sfe.StateElement) ms.sfes = append(ms.sfes, sfe) } } func (ms *MidState) addFileContractElement(id types.FileContractID, fc types.FileContract) { fce := types.FileContractElement{ - StateElement: types.StateElement{ID: types.Hash256(id)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: id, FileContract: fc, } ms.fces = append(ms.fces, fce) @@ -394,10 +401,10 @@ func (ms *MidState) reviseFileContractElement(fce types.FileContractElement, rev r.FileContract = rev } else { // store the original - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) ms.fces = append(ms.fces, fce) // store the revision - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) fce.FileContract = rev ms.revs[fce.ID] = &fce } @@ -407,13 +414,14 @@ func (ms *MidState) reviseFileContractElement(fce types.FileContractElement, rev func (ms *MidState) resolveFileContractElement(fce types.FileContractElement, valid bool, txid types.TransactionID) { ms.res[fce.ID] = valid ms.spends[fce.ID] = txid - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) ms.fces = append(ms.fces, fce) } func (ms *MidState) addV2FileContractElement(id types.FileContractID, fc types.V2FileContract) { fce := types.V2FileContractElement{ - StateElement: types.StateElement{ID: types.Hash256(id)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: id, V2FileContract: fc, } ms.v2fces = append(ms.v2fces, fce) @@ -429,10 +437,10 @@ func (ms *MidState) reviseV2FileContractElement(fce types.V2FileContractElement, r.V2FileContract = rev } else { // store the original - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) ms.v2fces = append(ms.v2fces, fce) // store the revision - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) fce.V2FileContract = rev ms.v2revs[fce.ID] = &fce } @@ -442,7 +450,7 @@ func (ms *MidState) reviseV2FileContractElement(fce types.V2FileContractElement, func (ms *MidState) resolveV2FileContractElement(fce types.V2FileContractElement, res types.V2FileContractResolutionType, txid types.TransactionID) { ms.v2res[fce.ID] = res ms.spends[fce.ID] = txid - fce.MerkleProof = append([]types.Hash256(nil), fce.MerkleProof...) + dupProof(&fce.StateElement) ms.v2fces = append(ms.v2fces, fce) } @@ -521,7 +529,7 @@ func (ms *MidState) ApplyV2Transaction(txn types.V2Transaction) { for _, sfi := range txn.SiafundInputs { ms.spendSiafundElement(sfi.Parent, txid) claimPortion := ms.siafundPool.Sub(sfi.Parent.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfi.Parent.SiafundOutput.Value) - ms.addImmatureSiacoinElement(types.SiafundOutputID(sfi.Parent.ID).V2ClaimOutputID(), types.SiacoinOutput{Value: claimPortion, Address: sfi.ClaimAddress}) + ms.addImmatureSiacoinElement(sfi.Parent.ID.V2ClaimOutputID(), types.SiacoinOutput{Value: claimPortion, Address: sfi.ClaimAddress}) } for i, sfo := range txn.SiafundOutputs { ms.addSiafundElement(txn.SiafundOutputID(txid, i), sfo) @@ -543,7 +551,7 @@ func (ms *MidState) ApplyV2Transaction(txn types.V2Transaction) { renter, host = r.FinalRevision.RenterOutput, r.FinalRevision.HostOutput renter.Value = renter.Value.Sub(r.RenterRollover) host.Value = host.Value.Sub(r.HostRollover) - ms.addV2FileContractElement(types.FileContractID(fce.ID).V2RenewalID(), r.NewContract) + ms.addV2FileContractElement(fce.ID.V2RenewalID(), r.NewContract) case *types.V2StorageProof: renter, host = fc.RenterOutput, fc.HostOutput case *types.V2FileContractFinalization: @@ -551,12 +559,13 @@ func (ms *MidState) ApplyV2Transaction(txn types.V2Transaction) { case *types.V2FileContractExpiration: renter, host = fc.RenterOutput, fc.MissedHostOutput() } - ms.addImmatureSiacoinElement(types.FileContractID(fce.ID).V2RenterOutputID(), renter) - ms.addImmatureSiacoinElement(types.FileContractID(fce.ID).V2HostOutputID(), host) + ms.addImmatureSiacoinElement(fce.ID.V2RenterOutputID(), renter) + ms.addImmatureSiacoinElement(fce.ID.V2HostOutputID(), host) } for i, a := range txn.Attestations { ms.addAttestationElement(types.AttestationElement{ - StateElement: types.StateElement{ID: txn.AttestationID(txid, i)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: txn.AttestationID(txid, i), Attestation: a, }) } @@ -587,12 +596,13 @@ func (ms *MidState) ApplyBlock(b types.Block, bs V1BlockSupplement) { } ms.resolveFileContractElement(fce, false, types.TransactionID(bid)) for i, sco := range fce.FileContract.MissedProofOutputs { - ms.addImmatureSiacoinElement(types.FileContractID(fce.ID).MissedOutputID(i), sco) + ms.addImmatureSiacoinElement(fce.ID.MissedOutputID(i), sco) } } ms.cie = types.ChainIndexElement{ - StateElement: types.StateElement{ID: types.Hash256(bid)}, + StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex}, + ID: bid, ChainIndex: types.ChainIndex{Height: ms.base.childHeight(), ID: bid}, } } @@ -716,7 +726,7 @@ func (au ApplyUpdate) ForEachTreeNode(fn func(row, col uint64, h types.Hash256)) // ChainIndexElement returns the chain index element for the applied block. func (au ApplyUpdate) ChainIndexElement() types.ChainIndexElement { cie := au.ms.cie - cie.MerkleProof = append([]types.Hash256(nil), cie.MerkleProof...) + dupProof(&cie.StateElement) return cie } @@ -736,7 +746,7 @@ func ApplyBlock(s State, b types.Block, bs V1BlockSupplement, targetTimestamp ti // compute updated and added elements var updated, added []elementLeaf ms.forEachAppliedElement(func(el elementLeaf) { - if ms.isCreated(el.ID) { + if el.LeafIndex == types.UnassignedLeafIndex { added = append(added, el) } else { updated = append(updated, el) @@ -834,10 +844,10 @@ func RevertBlock(s State, b types.Block, bs V1BlockSupplement) RevertUpdate { // compute updated elements var updated, added []elementLeaf ms.forEachRevertedElement(func(el elementLeaf) { - if !ms.isCreated(el.ID) { - updated = append(updated, el) - } else { + if el.LeafIndex == types.UnassignedLeafIndex { added = append(added, el) + } else { + updated = append(updated, el) } }) eru := s.Elements.revertBlock(updated, added) @@ -847,19 +857,19 @@ func RevertBlock(s State, b types.Block, bs V1BlockSupplement) RevertUpdate { // condensed representation of the update types for JSON marshaling type ( applyUpdateJSON struct { - Created []types.Hash256 `json:"created"` - Spent []types.Hash256 `json:"spent"` - ValidProof []types.Hash256 `json:"validProof"` - MissedProof []types.Hash256 `json:"missedProof"` - Revisions []types.FileContractElement `json:"revisions"` - V2Revisions []types.V2FileContractElement `json:"v2Revisions"` - V2Resolutions map[types.Hash256]types.V2FileContractResolutionType `json:"v2Resolutions"` - SiacoinElements []types.SiacoinElement `json:"siacoinElements"` - SiafundElements []types.SiafundElement `json:"siafundElements"` - FileContractElements []types.FileContractElement `json:"fileContractElements"` - V2FileContractElements []types.V2FileContractElement `json:"v2FileContractElements"` - AttestationElements []types.AttestationElement `json:"attestationElements"` - ChainIndexElement types.ChainIndexElement `json:"chainIndexElement"` + Created []types.Hash256 `json:"created"` + Spent []types.Hash256 `json:"spent"` + ValidProof []types.FileContractID `json:"validProof"` + MissedProof []types.FileContractID `json:"missedProof"` + Revisions []types.FileContractElement `json:"revisions"` + V2Revisions []types.V2FileContractElement `json:"v2Revisions"` + V2Resolutions map[types.FileContractID]types.V2FileContractResolutionType `json:"v2Resolutions"` + SiacoinElements []types.SiacoinElement `json:"siacoinElements"` + SiafundElements []types.SiafundElement `json:"siafundElements"` + FileContractElements []types.FileContractElement `json:"fileContractElements"` + V2FileContractElements []types.V2FileContractElement `json:"v2FileContractElements"` + AttestationElements []types.AttestationElement `json:"attestationElements"` + ChainIndexElement types.ChainIndexElement `json:"chainIndexElement"` UpdatedLeaves map[int][]elementLeaf `json:"updatedLeaves"` TreeGrowth map[int][]types.Hash256 `json:"treeGrowth"` @@ -868,19 +878,19 @@ type ( } revertUpdateJSON struct { - Created []types.Hash256 `json:"created"` - Spent []types.Hash256 `json:"spent"` - ValidProof []types.Hash256 `json:"validProof"` - MissedProof []types.Hash256 `json:"missedProof"` - Revisions []types.FileContractElement `json:"revisions"` - V2Revisions []types.V2FileContractElement `json:"v2Revisions"` - V2Resolutions map[types.Hash256]types.V2FileContractResolutionType `json:"v2Resolutions"` - SiacoinElements []types.SiacoinElement `json:"siacoinElements"` - SiafundElements []types.SiafundElement `json:"siafundElements"` - FileContractElements []types.FileContractElement `json:"fileContractElements"` - V2FileContractElements []types.V2FileContractElement `json:"v2FileContractElements"` - AttestationElements []types.AttestationElement `json:"attestationElements"` - ChainIndexElement types.ChainIndexElement `json:"chainIndexElement"` + Created []types.Hash256 `json:"created"` + Spent []types.Hash256 `json:"spent"` + ValidProof []types.FileContractID `json:"validProof"` + MissedProof []types.FileContractID `json:"missedProof"` + Revisions []types.FileContractElement `json:"revisions"` + V2Revisions []types.V2FileContractElement `json:"v2Revisions"` + V2Resolutions map[types.FileContractID]types.V2FileContractResolutionType `json:"v2Resolutions"` + SiacoinElements []types.SiacoinElement `json:"siacoinElements"` + SiafundElements []types.SiafundElement `json:"siafundElements"` + FileContractElements []types.FileContractElement `json:"fileContractElements"` + V2FileContractElements []types.V2FileContractElement `json:"v2FileContractElements"` + AttestationElements []types.AttestationElement `json:"attestationElements"` + ChainIndexElement types.ChainIndexElement `json:"chainIndexElement"` UpdatedLeaves map[int][]elementLeaf `json:"updatedLeaves"` NumLeaves uint64 `json:"numLeaves"` diff --git a/consensus/update_test.go b/consensus/update_test.go index 0d6b3c8..89a1db8 100644 --- a/consensus/update_test.go +++ b/consensus/update_test.go @@ -160,6 +160,7 @@ func TestApplyBlock(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[0]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[0]) } @@ -174,6 +175,7 @@ func TestApplyBlock(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[0]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[0]) } @@ -193,6 +195,7 @@ func TestApplyBlock(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[len(*sces)-1]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[len(*sces)-1]) } @@ -207,6 +210,7 @@ func TestApplyBlock(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[len(*sfes)-1]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[len(*sfes)-1]) } @@ -542,6 +546,7 @@ func TestApplyRevertBlockV1(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[0]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[0]) } @@ -556,6 +561,7 @@ func TestApplyRevertBlockV1(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[0]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[0]) } @@ -575,6 +581,7 @@ func TestApplyRevertBlockV1(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[len(*sces)-1]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[len(*sces)-1]) } @@ -589,6 +596,7 @@ func TestApplyRevertBlockV1(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[len(*sfes)-1]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[len(*sfes)-1]) } @@ -931,6 +939,7 @@ func TestApplyRevertBlockV2(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[0]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[0]) } @@ -945,6 +954,7 @@ func TestApplyRevertBlockV2(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[0]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[0]) } @@ -964,6 +974,7 @@ func TestApplyRevertBlockV2(t *testing.T) { t.Fatal("unexpected spent siacoin element") } sce.StateElement = types.StateElement{} + sce.ID = types.SiacoinOutputID{} if !reflect.DeepEqual(sce, (*sces)[len(*sces)-1]) { t.Fatalf("siacoin element doesn't match:\n%v\nvs\n%v\n", sce, (*sces)[len(*sces)-1]) } @@ -978,6 +989,7 @@ func TestApplyRevertBlockV2(t *testing.T) { t.Fatal("unexpected spent siafund element") } sfe.StateElement = types.StateElement{} + sfe.ID = types.SiafundOutputID{} if !reflect.DeepEqual(sfe, (*sfes)[len(*sfes)-1]) { t.Fatalf("siafund element doesn't match:\n%v\nvs\n%v\n", sfe, (*sfes)[len(*sfes)-1]) } diff --git a/consensus/validation.go b/consensus/validation.go index c446323..ba42f8d 100644 --- a/consensus/validation.go +++ b/consensus/validation.go @@ -559,7 +559,7 @@ func validateV2CurrencyOverflow(ms *MidState, txn types.V2Transaction) error { func validateV2Siacoins(ms *MidState, txn types.V2Transaction) error { sigHash := ms.base.InputSigHash(txn) - spent := make(map[types.Hash256]int) + spent := make(map[types.SiacoinOutputID]int) for i, sci := range txn.SiacoinInputs { if txid, ok := ms.spent(sci.Parent.ID); ok { return fmt.Errorf("siacoin input %v double-spends parent output (previously spent in %v)", i, txid) @@ -571,7 +571,7 @@ func validateV2Siacoins(ms *MidState, txn types.V2Transaction) error { spent[sci.Parent.ID] = i // check accumulator - if sci.Parent.LeafIndex == types.UnassignedLeafIndex { + if sci.Parent.StateElement.LeafIndex == types.UnassignedLeafIndex { if !ms.isCreated(sci.Parent.ID) { return fmt.Errorf("siacoin input %v spends nonexistent ephemeral output %v", i, sci.Parent.ID) } @@ -625,7 +625,7 @@ func validateV2Siacoins(ms *MidState, txn types.V2Transaction) error { func validateV2Siafunds(ms *MidState, txn types.V2Transaction) error { sigHash := ms.base.InputSigHash(txn) - spent := make(map[types.Hash256]int) + spent := make(map[types.SiafundOutputID]int) for i, sfi := range txn.SiafundInputs { if txid, ok := ms.spent(sfi.Parent.ID); ok { return fmt.Errorf("siafund input %v double-spends parent output (previously spent in %v)", i, txid) @@ -635,7 +635,7 @@ func validateV2Siafunds(ms *MidState, txn types.V2Transaction) error { spent[sfi.Parent.ID] = i // check accumulator - if sfi.Parent.LeafIndex == types.UnassignedLeafIndex { + if sfi.Parent.StateElement.LeafIndex == types.UnassignedLeafIndex { if !ms.isCreated(sfi.Parent.ID) { return fmt.Errorf("siafund input %v spends nonexistent ephemeral output %v", i, sfi.Parent.ID) } @@ -672,8 +672,8 @@ func validateV2Siafunds(ms *MidState, txn types.V2Transaction) error { } func validateV2FileContracts(ms *MidState, txn types.V2Transaction) error { - revised := make(map[types.Hash256]int) - resolved := make(map[types.Hash256]int) + revised := make(map[types.FileContractID]int) + resolved := make(map[types.FileContractID]int) validateParent := func(fce types.V2FileContractElement) error { if txid, ok := ms.spent(fce.ID); ok { return fmt.Errorf("has already been resolved in transaction %v", txid) diff --git a/consensus/validation_test.go b/consensus/validation_test.go index cb87e73..7a46289 100644 --- a/consensus/validation_test.go +++ b/consensus/validation_test.go @@ -1238,14 +1238,14 @@ func TestValidateV2Block(t *testing.T) { "spends siacoin output not in accumulator", func(b *types.Block) { txn := &b.V2.Transactions[0] - txn.SiacoinInputs[0].Parent.StateElement.ID[0] ^= 255 + txn.SiacoinInputs[0].Parent.ID[0] ^= 255 }, }, { "spends siafund output not in accumulator", func(b *types.Block) { txn := &b.V2.Transactions[0] - txn.SiafundInputs[0].Parent.StateElement.ID[0] ^= 255 + txn.SiafundInputs[0].Parent.ID[0] ^= 255 }, }, { @@ -1741,7 +1741,7 @@ func TestV2RevisionApply(t *testing.T) { Transactions: []types.V2Transaction{genesisTxn}, } contractID := genesisTxn.V2FileContractID(genesisTxn.ID(), 0) - fces := make(map[types.Hash256]types.V2FileContractElement) + fces := make(map[types.FileContractID]types.V2FileContractElement) applyContractChanges := func(au ApplyUpdate) { au.ForEachV2FileContractElement(func(fce types.V2FileContractElement, created bool, rev *types.V2FileContractElement, res types.V2FileContractResolutionType) { switch { @@ -1763,7 +1763,7 @@ func TestV2RevisionApply(t *testing.T) { checkRevision := func(t *testing.T, expected uint64) { t.Helper() - fce, ok := fces[types.Hash256(contractID)] + fce, ok := fces[contractID] if !ok { t.Fatal("missing revision") } else if fce.V2FileContract.RevisionNumber != expected { @@ -1785,7 +1785,7 @@ func TestV2RevisionApply(t *testing.T) { txn1 := types.V2Transaction{ FileContractRevisions: []types.V2FileContractRevision{ - {Parent: fces[types.Hash256(contractID)], Revision: rev1}, + {Parent: fces[contractID], Revision: rev1}, }, } @@ -1802,7 +1802,7 @@ func TestV2RevisionApply(t *testing.T) { txn2 := types.V2Transaction{ FileContractRevisions: []types.V2FileContractRevision{ - {Parent: fces[types.Hash256(contractID)], Revision: rev2}, + {Parent: fces[contractID], Revision: rev2}, }, } if err := ValidateV2Transaction(ms, txn2); err == nil { @@ -1862,7 +1862,7 @@ func TestV2RenewalResolution(t *testing.T) { Transactions: []types.V2Transaction{genesisTxn}, } contractID := genesisTxn.V2FileContractID(genesisTxn.ID(), 0) - fces := make(map[types.Hash256]types.V2FileContractElement) + fces := make(map[types.FileContractID]types.V2FileContractElement) genesisOutput := genesisTxn.EphemeralSiacoinOutput(0) applyChanges := func(au ApplyUpdate) { au.ForEachV2FileContractElement(func(fce types.V2FileContractElement, created bool, rev *types.V2FileContractElement, res types.V2FileContractResolutionType) { @@ -2020,7 +2020,7 @@ func TestV2RenewalResolution(t *testing.T) { renewTxn := types.V2Transaction{ FileContractResolutions: []types.V2FileContractResolution{ { - Parent: fces[types.Hash256(contractID)], + Parent: fces[contractID], Resolution: &types.V2FileContractRenewal{ FinalRevision: finalRevision, NewContract: fc, diff --git a/types/encoding.go b/types/encoding.go index 2586abc..9fb2486 100644 --- a/types/encoding.go +++ b/types/encoding.go @@ -611,7 +611,6 @@ func (sp SatisfiedPolicy) EncodeTo(e *Encoder) { // EncodeTo implements types.EncoderTo. func (se StateElement) EncodeTo(e *Encoder) { - se.ID.EncodeTo(e) e.WriteUint64(se.LeafIndex) EncodeSlice(e, se.MerkleProof) } @@ -625,12 +624,14 @@ func (in V2SiacoinInput) EncodeTo(e *Encoder) { // EncodeTo implements types.EncoderTo. func (cie ChainIndexElement) EncodeTo(e *Encoder) { cie.StateElement.EncodeTo(e) + cie.ID.EncodeTo(e) cie.ChainIndex.EncodeTo(e) } // EncodeTo implements types.EncoderTo. func (sce SiacoinElement) EncodeTo(e *Encoder) { sce.StateElement.EncodeTo(e) + sce.ID.EncodeTo(e) V2SiacoinOutput(sce.SiacoinOutput).EncodeTo(e) e.WriteUint64(sce.MaturityHeight) } @@ -645,6 +646,7 @@ func (in V2SiafundInput) EncodeTo(e *Encoder) { // EncodeTo implements types.EncoderTo. func (sfe SiafundElement) EncodeTo(e *Encoder) { sfe.StateElement.EncodeTo(e) + sfe.ID.EncodeTo(e) V2SiafundOutput(sfe.SiafundOutput).EncodeTo(e) V2Currency(sfe.ClaimStart).EncodeTo(e) } @@ -670,12 +672,14 @@ func (fc V2FileContract) EncodeTo(e *Encoder) { // EncodeTo implements types.EncoderTo. func (fce FileContractElement) EncodeTo(e *Encoder) { fce.StateElement.EncodeTo(e) + fce.ID.EncodeTo(e) fce.FileContract.EncodeTo(e) } // EncodeTo implements types.EncoderTo. func (fce V2FileContractElement) EncodeTo(e *Encoder) { fce.StateElement.EncodeTo(e) + fce.ID.EncodeTo(e) fce.V2FileContract.EncodeTo(e) } @@ -854,7 +858,7 @@ func (txn V2TransactionSemantics) EncodeTo(e *Encoder) { fcr.Resolution = &renewal case *V2StorageProof: sp := *res - sp.ProofIndex.MerkleProof = nil + sp.ProofIndex.StateElement.MerkleProof = nil fcr.Resolution = &sp } fcr.Resolution.(EncoderTo).EncodeTo(e) @@ -1207,7 +1211,6 @@ func (sp *SatisfiedPolicy) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (se *StateElement) DecodeFrom(d *Decoder) { - se.ID.DecodeFrom(d) se.LeafIndex = d.ReadUint64() DecodeSlice(d, &se.MerkleProof) } @@ -1221,12 +1224,14 @@ func (in *V2SiacoinInput) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (cie *ChainIndexElement) DecodeFrom(d *Decoder) { cie.StateElement.DecodeFrom(d) + cie.ID.DecodeFrom(d) cie.ChainIndex.DecodeFrom(d) } // DecodeFrom implements types.DecoderFrom. func (sce *SiacoinElement) DecodeFrom(d *Decoder) { sce.StateElement.DecodeFrom(d) + sce.ID.DecodeFrom(d) (*V2SiacoinOutput)(&sce.SiacoinOutput).DecodeFrom(d) sce.MaturityHeight = d.ReadUint64() } @@ -1241,6 +1246,7 @@ func (in *V2SiafundInput) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (sfe *SiafundElement) DecodeFrom(d *Decoder) { sfe.StateElement.DecodeFrom(d) + sfe.ID.DecodeFrom(d) (*V2SiafundOutput)(&sfe.SiafundOutput).DecodeFrom(d) (*V2Currency)(&sfe.ClaimStart).DecodeFrom(d) } @@ -1266,12 +1272,14 @@ func (fc *V2FileContract) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (fce *FileContractElement) DecodeFrom(d *Decoder) { fce.StateElement.DecodeFrom(d) + fce.ID.DecodeFrom(d) fce.FileContract.DecodeFrom(d) } // DecodeFrom implements types.DecoderFrom. func (fce *V2FileContractElement) DecodeFrom(d *Decoder) { fce.StateElement.DecodeFrom(d) + fce.ID.DecodeFrom(d) fce.V2FileContract.DecodeFrom(d) } diff --git a/types/multiproof_test.go b/types/multiproof_test.go index d1a8e9c..c3b83a8 100644 --- a/types/multiproof_test.go +++ b/types/multiproof_test.go @@ -88,8 +88,7 @@ func multiproofTxns(numTxns int, numElems int) []types.V2Transaction { for i := range txns { for j := range txns[i].SiacoinInputs { if (n+1)%5 == 0 { - txns[i].SiacoinInputs[j].Parent.LeafIndex = types.UnassignedLeafIndex - txns[i].SiacoinInputs[j].Parent.MerkleProof = nil + txns[i].SiacoinInputs[j].Parent.StateElement = types.StateElement{LeafIndex: types.UnassignedLeafIndex} } n++ } diff --git a/types/types.go b/types/types.go index bdee4ed..1de6926 100644 --- a/types/types.go +++ b/types/types.go @@ -603,52 +603,65 @@ type Attestation struct { Signature Signature `json:"signature"` } +// An AttestationID uniquely identifies an attestation. +type AttestationID Hash256 + +// An ElementID identifies a generic element within the state accumulator. In +// practice, it may be a BlockID, SiacoinOutputID, SiafundOutputID, +// FileContractID, or AttestationID. +type ElementID = [32]byte + // A StateElement is a generic element within the state accumulator. type StateElement struct { - ID Hash256 `json:"id"` // SiacoinOutputID, FileContractID, etc. LeafIndex uint64 `json:"leafIndex"` MerkleProof []Hash256 `json:"merkleProof,omitempty"` } // A ChainIndexElement is a record of a ChainIndex within the state accumulator. type ChainIndexElement struct { - StateElement - ChainIndex ChainIndex `json:"chainIndex"` + ID BlockID `json:"id"` + StateElement StateElement + ChainIndex ChainIndex `json:"chainIndex"` } // A SiacoinElement is a record of a SiacoinOutput within the state accumulator. type SiacoinElement struct { - StateElement - SiacoinOutput SiacoinOutput `json:"siacoinOutput"` - MaturityHeight uint64 `json:"maturityHeight"` + ID SiacoinOutputID `json:"id"` + StateElement StateElement `json:"stateElement"` + SiacoinOutput SiacoinOutput `json:"siacoinOutput"` + MaturityHeight uint64 `json:"maturityHeight"` } // A SiafundElement is a record of a SiafundOutput within the state accumulator. type SiafundElement struct { - StateElement - SiafundOutput SiafundOutput `json:"siafundOutput"` - ClaimStart Currency `json:"claimStart"` // value of SiafundPool when element was created + ID SiafundOutputID `json:"id"` + StateElement StateElement `json:"stateElement"` + SiafundOutput SiafundOutput `json:"siafundOutput"` + ClaimStart Currency `json:"claimStart"` // value of SiafundPool when element was created } // A FileContractElement is a record of a FileContract within the state // accumulator. type FileContractElement struct { - StateElement - FileContract FileContract `json:"fileContract"` + ID FileContractID `json:"id"` + StateElement StateElement `json:"stateElement"` + FileContract FileContract `json:"fileContract"` } // A V2FileContractElement is a record of a V2FileContract within the state // accumulator. type V2FileContractElement struct { - StateElement + ID FileContractID `json:"id"` + StateElement StateElement `json:"stateElement"` V2FileContract V2FileContract `json:"v2FileContract"` } // An AttestationElement is a record of an Attestation within the state // accumulator. type AttestationElement struct { - StateElement - Attestation Attestation `json:"attestation"` + ID AttestationID `json:"id"` + StateElement StateElement `json:"stateElement"` + Attestation Attestation `json:"attestation"` } // A V2Transaction effects a change of blockchain state. @@ -697,7 +710,7 @@ func (*V2Transaction) V2FileContractID(txid TransactionID, i int) FileContractID } // AttestationID returns the ID for the attestation at index i. -func (*V2Transaction) AttestationID(txid TransactionID, i int) Hash256 { +func (*V2Transaction) AttestationID(txid TransactionID, i int) AttestationID { return hashAll("id/attestation", txid, i) } @@ -706,9 +719,9 @@ func (*V2Transaction) AttestationID(txid TransactionID, i int) Hash256 { func (txn *V2Transaction) EphemeralSiacoinOutput(i int) SiacoinElement { return SiacoinElement{ StateElement: StateElement{ - ID: Hash256(txn.SiacoinOutputID(txn.ID(), i)), LeafIndex: UnassignedLeafIndex, }, + ID: txn.SiacoinOutputID(txn.ID(), i), SiacoinOutput: txn.SiacoinOutputs[i], } } @@ -718,9 +731,9 @@ func (txn *V2Transaction) EphemeralSiacoinOutput(i int) SiacoinElement { func (txn *V2Transaction) EphemeralSiafundOutput(i int) SiafundElement { return SiafundElement{ StateElement: StateElement{ - ID: Hash256(txn.SiafundOutputID(txn.ID(), i)), LeafIndex: UnassignedLeafIndex, }, + ID: txn.SiafundOutputID(txn.ID(), i), SiafundOutput: txn.SiafundOutputs[i], } } @@ -730,14 +743,14 @@ func (txn *V2Transaction) DeepCopy() V2Transaction { c := *txn c.SiacoinInputs = append([]V2SiacoinInput(nil), c.SiacoinInputs...) for i := range c.SiacoinInputs { - c.SiacoinInputs[i].Parent.MerkleProof = append([]Hash256(nil), c.SiacoinInputs[i].Parent.MerkleProof...) + c.SiacoinInputs[i].Parent.StateElement.MerkleProof = append([]Hash256(nil), c.SiacoinInputs[i].Parent.StateElement.MerkleProof...) c.SiacoinInputs[i].SatisfiedPolicy.Signatures = append([]Signature(nil), c.SiacoinInputs[i].SatisfiedPolicy.Signatures...) c.SiacoinInputs[i].SatisfiedPolicy.Preimages = append([][32]byte(nil), c.SiacoinInputs[i].SatisfiedPolicy.Preimages...) } c.SiacoinOutputs = append([]SiacoinOutput(nil), c.SiacoinOutputs...) c.SiafundInputs = append([]V2SiafundInput(nil), c.SiafundInputs...) for i := range c.SiafundInputs { - c.SiafundInputs[i].Parent.MerkleProof = append([]Hash256(nil), c.SiafundInputs[i].Parent.MerkleProof...) + c.SiafundInputs[i].Parent.StateElement.MerkleProof = append([]Hash256(nil), c.SiafundInputs[i].Parent.StateElement.MerkleProof...) c.SiafundInputs[i].SatisfiedPolicy.Signatures = append([]Signature(nil), c.SiafundInputs[i].SatisfiedPolicy.Signatures...) c.SiafundInputs[i].SatisfiedPolicy.Preimages = append([][32]byte(nil), c.SiafundInputs[i].SatisfiedPolicy.Preimages...) } @@ -745,14 +758,14 @@ func (txn *V2Transaction) DeepCopy() V2Transaction { c.FileContracts = append([]V2FileContract(nil), c.FileContracts...) c.FileContractRevisions = append([]V2FileContractRevision(nil), c.FileContractRevisions...) for i := range c.FileContractRevisions { - c.FileContractRevisions[i].Parent.MerkleProof = append([]Hash256(nil), c.FileContractRevisions[i].Parent.MerkleProof...) + c.FileContractRevisions[i].Parent.StateElement.MerkleProof = append([]Hash256(nil), c.FileContractRevisions[i].Parent.StateElement.MerkleProof...) } c.FileContractResolutions = append([]V2FileContractResolution(nil), c.FileContractResolutions...) for i := range c.FileContractResolutions { - c.FileContractResolutions[i].Parent.MerkleProof = append([]Hash256(nil), c.FileContractResolutions[i].Parent.MerkleProof...) + c.FileContractResolutions[i].Parent.StateElement.MerkleProof = append([]Hash256(nil), c.FileContractResolutions[i].Parent.StateElement.MerkleProof...) if res, ok := c.FileContractResolutions[i].Resolution.(*V2StorageProof); ok { sp := *res - sp.ProofIndex.MerkleProof = append([]Hash256(nil), sp.ProofIndex.MerkleProof...) + sp.ProofIndex.StateElement.MerkleProof = append([]Hash256(nil), sp.ProofIndex.StateElement.MerkleProof...) sp.Proof = append([]Hash256(nil), sp.Proof...) c.FileContractResolutions[i].Resolution = &sp }