gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/trustlessstreamer_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  
     7  	"gitlab.com/NebulousLabs/fastrand"
     8  	"gitlab.com/SkynetLabs/skyd/skymodules"
     9  	"go.sia.tech/siad/crypto"
    10  	"go.sia.tech/siad/modules"
    11  )
    12  
    13  // TestBuildSubSectionProof is a unit test for BuildSubSectionProof which builds
    14  // a proof for every possible segment of a section.
    15  func TestBuildSubSectionProof(t *testing.T) {
    16  	ec, err := skymodules.NewRSSubCode(2, 3, crypto.SegmentSize)
    17  	if err != nil {
    18  		t.Fatal(err)
    19  	}
    20  
    21  	// Create the encoded chunk.
    22  	pieceSize := modules.SectorSize //uint64(2 << 14) // 16 kib
    23  	chunkSize := pieceSize * uint64(ec.MinPieces())
    24  	chunk0 := fastrand.Bytes(int(chunkSize))
    25  	chunk1 := fastrand.Bytes(int(chunkSize))
    26  	logicalChunkData0, err := ec.Encode(append([]byte{}, chunk0...)) // deep-copy to not modify 'chunk'
    27  	if err != nil {
    28  		t.Fatal(err)
    29  	}
    30  	logicalChunkData1, err := ec.Encode(append([]byte{}, chunk1...)) // deep-copy to not modify 'chunk'
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	chunks := [][]byte{chunk0, chunk1}
    35  	logicalChunkDatas := [][][]byte{logicalChunkData0, logicalChunkData1}
    36  
    37  	// Get number of sections per chunk.
    38  	requestSize := SkylinkDataSourceRequestSize
    39  	numSections := chunkSize / requestSize
    40  	if chunkSize%requestSize != 0 {
    41  		t.Fatal("chunkSize should be cleanly divided by request size")
    42  	}
    43  
    44  	// Run the test for every potential section of the chunks.
    45  	for chunkIndex, chunk := range chunks {
    46  		logicalChunkData := logicalChunkDatas[chunkIndex]
    47  
    48  		for sectionIndex := uint64(0); sectionIndex < numSections; sectionIndex++ {
    49  			// Compute the offset and length of the section within the
    50  			// chunk.
    51  			offsetInChunk := requestSize * sectionIndex
    52  			lengthInChunk := requestSize
    53  
    54  			// Trim the logical chunk data to only contain the parts of the
    55  			// pieces relevant to the section.
    56  			lcd := make([][]byte, len(logicalChunkData))
    57  			pieceOff, pieceLen := GetPieceOffsetAndLen(ec, offsetInChunk, lengthInChunk)
    58  			for pieceIndex := range lcd {
    59  				lcd[pieceIndex] = logicalChunkData[pieceIndex][pieceOff : pieceOff+pieceLen]
    60  			}
    61  
    62  			// Create the proofs for each piece. That is the proof that the
    63  			// section is part of the sector.
    64  			sectionProofStart := int(pieceOff) / crypto.SegmentSize
    65  			sectionProofEnd := int(pieceOff+pieceLen) / crypto.SegmentSize
    66  			var proofs [][]crypto.Hash
    67  			for _, piece := range logicalChunkData {
    68  				proof := crypto.MerkleRangeProof(piece, sectionProofStart, sectionProofEnd)
    69  				proofs = append(proofs, proof)
    70  			}
    71  
    72  			// The proofs should be valid.
    73  			for pieceIndex, piece := range lcd {
    74  				merkleRoot := crypto.MerkleRoot(logicalChunkData[pieceIndex])
    75  				valid := crypto.VerifyRangeProof(piece, proofs[pieceIndex], int(pieceOff)/crypto.SegmentSize, int(pieceOff+pieceLen)/crypto.SegmentSize, merkleRoot)
    76  				if !valid {
    77  					t.Fatal("invalid merkle proof")
    78  				}
    79  			}
    80  
    81  			// Build the download response.
    82  			dr := newDownloadResponse(offsetInChunk, lengthInChunk, ec, lcd, proofs, nil)
    83  
    84  			// The download response should be able to recover the right
    85  			// data.
    86  			section, err := dr.externDownloadedData.Recover()
    87  			if err != nil {
    88  				t.Fatal(err)
    89  			}
    90  			if !bytes.Equal(section, chunk[offsetInChunk:offsetInChunk+lengthInChunk]) {
    91  				t.Fatal("mismatch", len(section), lengthInChunk)
    92  			}
    93  
    94  			// For every segment of every logical piece within the downloaded data, create a
    95  			// proof that proves this segment is contained within a section
    96  			// by combining the section proof and segment proof using
    97  			// ComputeSectionProof.
    98  			for pieceIndex := range dr.externDownloadedData.Proofs {
    99  				// Compute the merkle root of the logical piece.
   100  				merkleRoot := crypto.MerkleRoot(logicalChunkData[pieceIndex])
   101  
   102  				// Grab the section proof. This proves that the section
   103  				// is part of a sector.
   104  				sectionProof := dr.externDownloadedData.Proofs[pieceIndex]
   105  
   106  				// Build one proof for every segment of the section.
   107  				for startSegment := 0; startSegment < len(lcd[pieceIndex])/crypto.SegmentSize; startSegment++ {
   108  					// Create the subSectionProof. This proves that
   109  					// the downloaded data is part of the section.
   110  					subSectionProof := crypto.MerkleRangeProof(lcd[pieceIndex], startSegment, startSegment+1)
   111  
   112  					// Build the combined proof of section and
   113  					// sub-section proof. This proves that the
   114  					// downloaded data is part of a sector.
   115  					combinedProof := BuildSubSectionProof(sectionIndex, subSectionProof, sectionProof)
   116  
   117  					// Verify the proof.
   118  					segmentProofStart := sectionProofStart + startSegment
   119  					segmentProofEnd := segmentProofStart + 1
   120  					logicalPiece := lcd[pieceIndex]
   121  					valid := crypto.VerifyRangeProof(logicalPiece[startSegment*crypto.SegmentSize:][:crypto.SegmentSize], combinedProof, segmentProofStart, segmentProofEnd, merkleRoot)
   122  					if !valid {
   123  						t.Fatal("invalid merkle proof")
   124  					}
   125  				}
   126  			}
   127  		}
   128  	}
   129  }