Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix v2 storage proofs #159

Merged
merged 3 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion consensus/merkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func storageProofRoot(leafHash types.Hash256, leafIndex uint64, filesize uint64,
}
root := proofRoot(leafHash, leafIndex, proof[:subtreeHeight])
for _, h := range proof[subtreeHeight:] {
root = blake2b.SumPair(root, h)
root = blake2b.SumPair(h, root)
}
return root
}
Expand Down
68 changes: 0 additions & 68 deletions consensus/merkle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,74 +81,6 @@ func TestElementAccumulatorRoundTrip(t *testing.T) {
}
}

func TestStorageProofRoot(t *testing.T) {
leafHash := types.Hash256{0x01, 0x02, 0x03}
validProof := []types.Hash256{
{0x0A, 0x0B, 0x0C},
{0x0D, 0x0E, 0x0F},
{0x10, 0x11, 0x12},
{0x13, 0x14, 0x15},
}
longProof := append(validProof, types.Hash256{0x16, 0x17, 0x18})
shortProof := validProof[:2]

var validWantRoot types.Hash256
validWantRoot.UnmarshalText([]byte(`h:a89dbbb545aa4b46696230d104076f06b57a6ab08b2341c3d012bdc13e23eb35`))

tests := []struct {
name string
leafHash types.Hash256
leafIndex uint64
filesize uint64
proof []types.Hash256
wantRoot types.Hash256
valid bool
}{
{
name: "ValidProof",
leafHash: leafHash,
leafIndex: 10,
filesize: 829,
proof: validProof,
wantRoot: validWantRoot,
valid: true,
},
{
name: "TooLongProof",
leafHash: leafHash,
leafIndex: 10,
filesize: 829,
proof: longProof,
wantRoot: validWantRoot,
valid: false,
},
{
name: "TooShortProof",
leafHash: leafHash,
leafIndex: 10,
filesize: 829,
proof: shortProof,
wantRoot: validWantRoot,
valid: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotRoot := storageProofRoot(tt.leafHash, tt.leafIndex, tt.filesize, tt.proof)
if tt.valid {
if gotRoot != tt.wantRoot {
t.Errorf("%s failed: got %v, want %v", tt.name, gotRoot, tt.wantRoot)
}
} else {
if gotRoot == tt.wantRoot {
t.Errorf("%s failed: got a valid root for an invalid proof", tt.name)
}
}
})
}
}

func TestUpdateElementProof(t *testing.T) {
tests := []struct {
name string
Expand Down
11 changes: 6 additions & 5 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,12 @@ func (s State) StorageProofLeafIndex(filesize uint64, windowID types.BlockID, fc
// StorageProofLeafHash computes the leaf hash of file contract data. If
// len(leaf) < 64, it will be extended with zeros.
func (s State) StorageProofLeafHash(leaf []byte) types.Hash256 {
const leafSize = len(types.StorageProof{}.Leaf)
buf := make([]byte, 1+leafSize)
buf[0] = leafHashPrefix
copy(buf[1:], leaf)
return types.HashBytes(buf)
if len(leaf) == 64 {
return blake2b.SumLeaf((*[64]byte)(leaf))
}
var buf [64]byte
copy(buf[:], leaf)
return blake2b.SumLeaf(&buf)
}

// replayPrefix returns the replay protection prefix at the current height.
Expand Down
2 changes: 1 addition & 1 deletion consensus/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ func validateFileContracts(ms *MidState, txn types.Transaction, ts V1Transaction
buf[0] = 0 // leaf hash prefix
copy(buf[1:], leaf)
root := types.HashBytes(buf)
subtreeHeight := bits.Len64(leafIndex ^ (lastLeafIndex(filesize)))
subtreeHeight := bits.Len64(leafIndex ^ lastLeafIndex(filesize))
for i, h := range proof {
if leafIndex&(1<<i) != 0 || i >= subtreeHeight {
root = blake2b.SumPair(h, root)
Expand Down
22 changes: 21 additions & 1 deletion consensus/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"
"time"

"go.sia.tech/core/internal/blake2b"
"go.sia.tech/core/types"
)

Expand Down Expand Up @@ -747,8 +748,11 @@ func TestValidateV2Block(t *testing.T) {
giftAmountSC := types.Siacoins(100)
giftAmountSF := uint64(100)
v1GiftFC := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), 100, 100, types.VoidAddress)
v1GiftFC.Filesize = 65
v1GiftFC.FileMerkleRoot = blake2b.SumPair((State{}).StorageProofLeafHash([]byte{1}), (State{}).StorageProofLeafHash([]byte{2}))
v2GiftFC := types.V2FileContract{
Filesize: v1GiftFC.Filesize,
FileMerkleRoot: v1GiftFC.FileMerkleRoot,
ProofHeight: 20,
ExpirationHeight: 30,
RenterOutput: v1GiftFC.ValidProofOutputs[0],
Expand Down Expand Up @@ -1216,14 +1220,30 @@ func TestValidateV2Block(t *testing.T) {
V2: &types.V2BlockData{
Height: cs.Index.Height + 1,
Transactions: []types.V2Transaction{
{},
{
FileContractResolutions: []types.V2FileContractResolution{{
Parent: testFces[0],
Resolution: &types.V2StorageProof{
ProofIndex: cies[len(cies)-2],
Leaf: [64]byte{1},
Proof: []types.Hash256{cs.StorageProofLeafHash([]byte{2})},
},
}},
},
},
},
MinerPayouts: []types.SiacoinOutput{{
Address: types.VoidAddress,
Value: cs.BlockReward(),
}},
}
if cs.StorageProofLeafIndex(testFces[0].V2FileContract.Filesize, cies[len(cies)-2].ChainIndex.ID, types.FileContractID(testFces[0].ID)) == 1 {
b.V2.Transactions[0].FileContractResolutions[0].Resolution = &types.V2StorageProof{
ProofIndex: cies[len(cies)-2],
Leaf: [64]byte{2},
Proof: []types.Hash256{cs.StorageProofLeafHash([]byte{1})},
}
}
signTxn(cs, &b.V2.Transactions[0])
b.V2.Commitment = cs.Commitment(cs.TransactionsCommitment(b.Transactions, b.V2Transactions()), b.MinerPayouts[0].Address)
findBlockNonce(cs, &validBlock)
Expand Down
Loading