Skip to content

Commit

Permalink
Merge pull request #205 from SiaFoundation/proof-supplement
Browse files Browse the repository at this point in the history
consensus: Add V1StorageProofSupplement, fixing encoding bug
  • Loading branch information
n8maninger authored Sep 17, 2024
2 parents 37cd8b8 + a176ef9 commit 0f61e58
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 58 deletions.
69 changes: 29 additions & 40 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,31 +626,7 @@ func (ms *MidState) fileContractElement(ts V1TransactionSupplement, id types.Fil
if i, ok := ms.created[types.Hash256(id)]; ok {
return ms.fces[i], true
}
return ts.fileContractElement(id)
}

func (ms *MidState) mustSiacoinElement(ts V1TransactionSupplement, id types.SiacoinOutputID) types.SiacoinElement {
sce, ok := ms.siacoinElement(ts, id)
if !ok {
panic("missing SiacoinElement")
}
return sce
}

func (ms *MidState) mustSiafundElement(ts V1TransactionSupplement, id types.SiafundOutputID) types.SiafundElement {
sfe, ok := ms.siafundElement(ts, id)
if !ok {
panic("missing SiafundElement")
}
return sfe
}

func (ms *MidState) mustFileContractElement(ts V1TransactionSupplement, id types.FileContractID) types.FileContractElement {
fce, ok := ms.fileContractElement(ts, id)
if !ok {
panic("missing FileContractElement")
}
return fce
return ts.revision(id)
}

func (ms *MidState) spent(id types.Hash256) (types.TransactionID, bool) {
Expand Down Expand Up @@ -684,6 +660,25 @@ func NewMidState(s State) *MidState {
}
}

// A V1StorageProofSupplement pairs a file contract with the block ID used to
// derive its storage proof leaf index.
type V1StorageProofSupplement struct {
FileContract types.FileContractElement
WindowID types.BlockID
}

// EncodeTo implements types.EncoderTo.
func (sps V1StorageProofSupplement) EncodeTo(e *types.Encoder) {
sps.FileContract.EncodeTo(e)
sps.WindowID.EncodeTo(e)
}

// DecodeFrom implements types.DecoderFrom.
func (sps *V1StorageProofSupplement) DecodeFrom(d *types.Decoder) {
sps.FileContract.DecodeFrom(d)
sps.WindowID.DecodeFrom(d)
}

// A V1TransactionSupplement contains elements that are associated with a v1
// transaction, but not included in the transaction. For example, v1
// transactions reference the ID of each SiacoinOutput they spend, but do not
Expand All @@ -695,24 +690,23 @@ type V1TransactionSupplement struct {
SiacoinInputs []types.SiacoinElement
SiafundInputs []types.SiafundElement
RevisedFileContracts []types.FileContractElement
ValidFileContracts []types.FileContractElement
StorageProofBlockIDs []types.BlockID // must match ValidFileContracts
StorageProofs []V1StorageProofSupplement
}

// EncodeTo implements types.EncoderTo.
func (ts V1TransactionSupplement) EncodeTo(e *types.Encoder) {
types.EncodeSlice(e, ts.SiacoinInputs)
types.EncodeSlice(e, ts.SiafundInputs)
types.EncodeSlice(e, ts.RevisedFileContracts)
types.EncodeSlice(e, ts.ValidFileContracts)
types.EncodeSlice(e, ts.StorageProofs)
}

// DecodeFrom implements types.DecoderFrom.
func (ts *V1TransactionSupplement) DecodeFrom(d *types.Decoder) {
types.DecodeSlice(d, &ts.SiacoinInputs)
types.DecodeSlice(d, &ts.SiafundInputs)
types.DecodeSlice(d, &ts.RevisedFileContracts)
types.DecodeSlice(d, &ts.ValidFileContracts)
types.DecodeSlice(d, &ts.StorageProofs)
}

func (ts V1TransactionSupplement) siacoinElement(id types.SiacoinOutputID) (sce types.SiacoinElement, ok bool) {
Expand All @@ -733,27 +727,22 @@ func (ts V1TransactionSupplement) siafundElement(id types.SiafundOutputID) (sfe
return
}

func (ts V1TransactionSupplement) fileContractElement(id types.FileContractID) (fce types.FileContractElement, ok bool) {
func (ts V1TransactionSupplement) revision(id types.FileContractID) (fce types.FileContractElement, ok bool) {
for _, fce := range ts.RevisedFileContracts {
if types.FileContractID(fce.ID) == id {
return fce, true
}
}
for _, fce := range ts.ValidFileContracts {
if types.FileContractID(fce.ID) == id {
return fce, true
}
}
return
}

func (ts V1TransactionSupplement) storageProofWindowID(id types.FileContractID) types.BlockID {
for i, fce := range ts.ValidFileContracts {
if types.FileContractID(fce.ID) == id {
return ts.StorageProofBlockIDs[i]
func (ts V1TransactionSupplement) storageProof(id types.FileContractID) (sps V1StorageProofSupplement, ok bool) {
for _, sps := range ts.StorageProofs {
if types.FileContractID(sps.FileContract.ID) == id {
return sps, true
}
}
panic("missing contract for storage proof window ID") // developer error
return
}

// A V1BlockSupplement contains elements that are associated with a v1 block,
Expand Down
26 changes: 20 additions & 6 deletions consensus/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,13 +455,20 @@ func (ms *MidState) addAttestationElement(ae types.AttestationElement) {
func (ms *MidState) ApplyTransaction(txn types.Transaction, ts V1TransactionSupplement) {
txid := txn.ID()
for _, sci := range txn.SiacoinInputs {
ms.spendSiacoinElement(ms.mustSiacoinElement(ts, sci.ParentID), txid)
sce, ok := ms.siacoinElement(ts, sci.ParentID)
if !ok {
panic("missing SiacoinElement")
}
ms.spendSiacoinElement(sce, txid)
}
for i, sco := range txn.SiacoinOutputs {
ms.addSiacoinElement(txn.SiacoinOutputID(i), sco)
}
for _, sfi := range txn.SiafundInputs {
sfe := ms.mustSiafundElement(ts, sfi.ParentID)
sfe, ok := ms.siafundElement(ts, sfi.ParentID)
if !ok {
panic("missing SiafundElement")
}
claimPortion := ms.siafundPool.Sub(sfe.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfe.SiafundOutput.Value)
ms.spendSiafundElement(sfe, txid)
ms.addImmatureSiacoinElement(sfi.ParentID.ClaimOutputID(), types.SiacoinOutput{Value: claimPortion, Address: sfi.ClaimAddress})
Expand All @@ -473,12 +480,19 @@ func (ms *MidState) ApplyTransaction(txn types.Transaction, ts V1TransactionSupp
ms.addFileContractElement(txn.FileContractID(i), fc)
}
for _, fcr := range txn.FileContractRevisions {
ms.reviseFileContractElement(ms.mustFileContractElement(ts, fcr.ParentID), fcr.FileContract)
fce, ok := ms.fileContractElement(ts, fcr.ParentID)
if !ok {
panic("missing FileContractElement")
}
ms.reviseFileContractElement(fce, fcr.FileContract)
}
for _, sp := range txn.StorageProofs {
fce := ms.mustFileContractElement(ts, sp.ParentID)
ms.resolveFileContractElement(fce, true, txid)
for i, sco := range fce.FileContract.ValidProofOutputs {
sps, ok := ts.storageProof(sp.ParentID)
if !ok {
panic("missing V1StorageProofSupplement")
}
ms.resolveFileContractElement(sps.FileContract, true, txid)
for i, sco := range sps.FileContract.FileContract.ValidProofOutputs {
ms.addImmatureSiacoinElement(sp.ParentID.ValidOutputID(i), sco)
}
}
Expand Down
6 changes: 4 additions & 2 deletions consensus/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,8 +828,10 @@ func TestApplyRevertBlockV1(t *testing.T) {

// add block with storage proof
bs = db.supplementTipBlock(b5)
bs.Transactions[0].ValidFileContracts = append(bs.Transactions[0].ValidFileContracts, db.fces[txnB5.StorageProofs[0].ParentID])
bs.Transactions[0].StorageProofBlockIDs = append(bs.Transactions[0].StorageProofBlockIDs, b3.ID())
bs.Transactions[0].StorageProofs = append(bs.Transactions[0].StorageProofs, V1StorageProofSupplement{
FileContract: db.fces[txnB5.StorageProofs[0].ParentID],
WindowID: b3.ID(),
})
prev = cs
if au, err := addBlock(&b5, bs); err != nil {
t.Fatal(err)
Expand Down
16 changes: 6 additions & 10 deletions consensus/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,12 @@ func validateFileContracts(ms *MidState, txn types.Transaction, ts V1Transaction
if txid, ok := ms.spent(types.Hash256(sp.ParentID)); ok {
return fmt.Errorf("storage proof %v conflicts with previous proof (in %v)", i, txid)
}
fce, ok := ms.fileContractElement(ts, sp.ParentID)
sps, ok := ts.storageProof(sp.ParentID)
if !ok {
return fmt.Errorf("storage proof %v references nonexistent file contract", i)
}
fc := fce.FileContract
windowID := ts.storageProofWindowID(sp.ParentID)
leafIndex := ms.base.StorageProofLeafIndex(fc.Filesize, windowID, sp.ParentID)
fc := sps.FileContract.FileContract
leafIndex := ms.base.StorageProofLeafIndex(fc.Filesize, sps.WindowID, sp.ParentID)
leaf := storageProofLeaf(leafIndex, fc.Filesize, sp.Leaf)
if leaf == nil {
continue
Expand Down Expand Up @@ -923,14 +922,11 @@ func validateSupplement(s State, b types.Block, bs V1BlockSupplement) error {
return fmt.Errorf("revised file contract %v is not present in the accumulator", fce.ID)
}
}
for _, fce := range txn.ValidFileContracts {
if !s.Elements.containsUnresolvedFileContractElement(fce) {
return fmt.Errorf("valid file contract %v is not present in the accumulator", fce.ID)
for _, sps := range txn.StorageProofs {
if !s.Elements.containsUnresolvedFileContractElement(sps.FileContract) {
return fmt.Errorf("valid file contract %v is not present in the accumulator", sps.FileContract.ID)
}
}
if len(txn.StorageProofBlockIDs) != len(txn.ValidFileContracts) {
return errors.New("incorrect number of storage proof block IDs")
}
}
for _, fce := range bs.ExpiringFileContracts {
if !s.Elements.containsUnresolvedFileContractElement(fce) {
Expand Down

0 comments on commit 0f61e58

Please sign in to comment.