// Basic test for reed-solomon coding, verifies that standard input // will produce the correct results. func TestCoding(t *testing.T) { k := 100 m := common.QuorumSize - k bytesPerSegment := 1024 randomBytes, err := crypto.RandomByteSlice(bytesPerSegment * k) if err != nil { t.Fatal(err) } // get hash of original file randomBytesHash, err := crypto.CalculateHash(randomBytes) if err != nil { t.Fatal(err) } // encode original file into a data ring ringSegments, err := EncodeRing(k, bytesPerSegment, randomBytes) if err != nil { t.Fatal(err) } // verify that first k segments are still original data originalDataHash, err := crypto.CalculateHash(randomBytes) if err != nil { t.Fatal(err) } else if originalDataHash != randomBytesHash { t.Fatal("original data was modified after caling EncodeRing!") } // reduce file to a set of k segments and print those segments out remainingSegments := make([]string, k) segmentIndicies := make([]uint8, k) for i := m; i < common.QuorumSize; i++ { remainingSegments[i-m] = ringSegments[i] segmentIndicies[i-m] = uint8(i) } // recover original data recoveredData, err := RebuildSector(k, bytesPerSegment, remainingSegments, segmentIndicies) if err != nil { t.Fatal(err) } // compare to hash of data when first generated recoveredDataHash, err := crypto.CalculateHash(recoveredData) if err != nil { t.Fatal(err) } else if recoveredDataHash != randomBytesHash { t.Fatal("recovered data is different from original data") } // In every test, we check that the hashes equal // every other hash that gets created. This makes // me uneasy. }
// Basic test for reed-solomon coding, verifies that standard input // will produce the correct results. func TestCoding(t *testing.T) { // set encoding parameters k := common.QuorumSize / 2 m := common.QuorumSize - k b := 1024 // create sector data randomBytes, err := crypto.RandomByteSlice(b * k) if err != nil { t.Fatal(err) } // create sector sec, err := common.NewSector(randomBytes) if err != nil { t.Fatal(err) } // calculate encoding parameters params := sec.CalculateParams(k) // encode data into a Ring ring, err := EncodeRing(sec, params) if err != nil { t.Fatal(err) } // create Ring from subset of encoded segments var newRing []common.Segment for i := m; i < common.QuorumSize; i++ { newRing = append(newRing, ring[i]) } // recover original data newSec, err := RebuildSector(newRing, params) if err != nil { t.Fatal(err) } // compare to hash of data when first generated recoveredDataHash, err := crypto.CalculateHash(newSec.Data) if err != nil { t.Fatal(err) } else if recoveredDataHash != sec.Hash { t.Fatal("recovered data is different from original data") } // In every test, we check that the hashes equal // every other hash that gets created. This makes // me uneasy. }
// Using the current State, newHeartbeat() creates a heartbeat that fulfills all // of the requirements of the quorum. func (s *State) newHeartbeat() (hb *heartbeat, err error) { hb = new(heartbeat) // Generate Entropy entropy, err := crypto.RandomByteSlice(common.EntropyVolume) if err != nil { return } copy(hb.entropy[:], entropy) // more code will be added here return }
// Marshal an Address and see if it unmarshalles to the same value // Check that bad input is handled correctly // fuzz over a bunch of random values for Address during long tests func TestAddressMarshalling(t *testing.T) { // marshal, unmarshal, verify at the zero value var a Address ma := a.Marshal() ua, err := UnmarshalAddress(ma) if err != nil { t.Fatal(err) } if ua != a { t.Fatal("Address not equal after marshalling and unmarshalling!") } // test that Address marshal handles bad inputs var bad []byte ua, err = UnmarshalAddress(bad) if err == nil { t.Error("UnmarshalAddress accepted an unitialized []byte") } bad = make([]byte, 4) ua, err = UnmarshalAddress(bad) if err == nil { t.Error("UnmarshalAddress accepted a []byte too short to be an address") } // test marshalling a bunch of random Addresses if testing.Short() { t.Skip() } for i := 0; i < 10000; i++ { randomBytes, err := crypto.RandomByteSlice(crypto.RandomInt(100) + 1) a.Id = Identifier(randomBytes[0]) // random id a.Host = string(randomBytes[1:]) // random host a.Port = crypto.RandomInt(65536) // random port ma = a.Marshal() ua, err = UnmarshalAddress(ma) if err != nil { t.Error(a.Id, " ", a.Host, " ", a.Port) t.Error(err) } if ua != a { t.Error("Fuzzing Test Failed: Marshalled and Unmarshalled address not identical") t.Error(a.Id, " ", a.Host, " ", a.Port) } } }
func generateSector(q common.Quorum) (s *common.Sector, err error) { if q[0].Port == 0 { err = fmt.Errorf("you must connect to a quorum first") return } data, err := crypto.RandomByteSlice(70000) if err != nil { return } s, err = common.NewSector(data) if err != nil { return } SectorDB[s.Hash] = &common.RingHeader{ Hosts: q, Params: s.CalculateParams(common.QuorumSize / 2), } return }
// Using the current State, newHeartbeat() creates a heartbeat that fulfills all // of the requirements of the quorum. func (s *State) newHeartbeat() (hb *heartbeat, err error) { hb = new(heartbeat) // Fetch value used to produce EntropyStage1 in prev. heartbeat hb.entropyStage2 = s.storedEntropyStage2 // Generate EntropyStage2 for next heartbeat entropy, err := crypto.RandomByteSlice(common.EntropyVolume) if err != nil { return } copy(s.storedEntropyStage2[:], entropy) // convert entropy from slice to byte array // Use EntropyStage2 to generate EntropyStage1 for this heartbeat hb.entropyStage1, err = crypto.CalculateTruncatedHash(s.storedEntropyStage2[:]) if err != nil { return } // more code will be added here return }
// TestRPCUploadSector tests the NewRPCServer and uploadFile functions. // NewRPCServer must properly initialize a RPC server. // uploadSector must succesfully distribute a Sector among a quorum. // The uploaded Sector must be successfully reconstructed. func TestRPCuploadSector(t *testing.T) { SectorDB = make(map[crypto.Hash]*common.RingHeader) // create RPCServer var err error router, err = network.NewRPCServer(9985) if err != nil { t.Fatal("Failed to initialize RPCServer:", err) } defer router.Close() // create quorum var q [common.QuorumSize]common.Address var shs [common.QuorumSize]Server for i := 0; i < common.QuorumSize; i++ { q[i] = common.Address{0, "localhost", 9000 + i} qrpc, err := network.NewRPCServer(9000 + i) defer qrpc.Close() if err != nil { t.Fatal("Failed to initialize RPCServer:", err) } q[i].ID = qrpc.RegisterHandler(&shs[i]) } // create sector secData, err := crypto.RandomByteSlice(70000) if err != nil { t.Fatal("Could not generate test data:", err) } sec, err := common.NewSector(secData) if err != nil { t.Fatal("Failed to create sector:", err) } // add sector to database k := common.QuorumSize / 2 SectorDB[sec.Hash] = &common.RingHeader{ Hosts: q, Params: sec.CalculateParams(k), } // upload sector to quorum err = uploadSector(sec) if err != nil { t.Fatal("Failed to upload file:", err) } // rebuild file from first k segments var newRing []common.Segment for i := 0; i < k; i++ { newRing = append(newRing, shs[i].seg) } sec, err = erasure.RebuildSector(newRing, SectorDB[sec.Hash].Params) if err != nil { t.Fatal("Failed to rebuild file:", err) } // check hash rebuiltHash, err := crypto.CalculateHash(sec.Data) if err != nil { t.Fatal("Failed to calculate hash:", err) } if sec.Hash != rebuiltHash { t.Fatal("Failed to recover file: hashes do not match") } }
// TestRPCdownloadSector tests the NewRPCServer and downloadSector functions. // NewRPCServer must properly initialize a RPC server. // downloadSector must successfully retrieve a Sector from a quorum. // The downloaded Sector must match the original Sector. func TestRPCdownloadSector(t *testing.T) { SectorDB = make(map[crypto.Hash]*common.RingHeader) // create sector secData, err := crypto.RandomByteSlice(70000) if err != nil { t.Fatal("Could not generate test data:", err) } sec, err := common.NewSector(secData) if err != nil { t.Fatal("Failed to create sector:", err) } k := common.QuorumSize / 2 params := sec.CalculateParams(k) // encode sector ring, err := erasure.EncodeRing(sec, params) if err != nil { t.Fatal("Failed to encode sector data:", err) } // create RPCServer router, err = network.NewRPCServer(9985) if err != nil { t.Fatal("Failed to initialize RPCServer:", err) } defer router.Close() // create quorum var q [common.QuorumSize]common.Address for i := 0; i < common.QuorumSize; i++ { q[i] = common.Address{0, "localhost", 9000 + i} qrpc, err := network.NewRPCServer(9000 + i) if err != nil { t.Fatal("Failed to initialize RPCServer:", err) } sh := new(Server) sh.seg = ring[i] q[i].ID = qrpc.RegisterHandler(sh) } // add sector to database SectorDB[sec.Hash] = &common.RingHeader{ Hosts: q, Params: params, } // download file from quorum sec, err = downloadSector(sec.Hash) if err != nil { t.Fatal("Failed to download file:", err) } // check hash rebuiltHash, err := crypto.CalculateHash(sec.Data) if err != nil { t.Fatal("Failed to calculate hash:", err) } if sec.Hash != rebuiltHash { t.Fatal("Failed to recover file: hashes do not match") } }
// TestTCPUploadFile tests the NewTCPServer and UploadFile functions. // NewTCPServer must properly initialize a TCP server. // UploadFile must succesfully distribute a file among a quorum. // The uploaded file must be successfully reconstructed. func TestTCPUploadFile(t *testing.T) { // create TCPServer tcp, err := network.NewTCPServer(9988) if err != nil { t.Fatal("Failed to initialize TCPServer:", err) } defer tcp.Close() // create quorum var q [common.QuorumSize]common.Address var uhs [common.QuorumSize]uploadHandler for i := 0; i < common.QuorumSize; i++ { q[i] = common.Address{0, "localhost", 9000 + i} qtcp, err := network.NewTCPServer(9000 + i) defer qtcp.Close() if err != nil { t.Fatal("Failed to initialize TCPServer:", err) } uhs[i].done = make(chan bool, 1) q[i].Id = qtcp.AddMessageHandler(&uhs[i]).Id } // create file file, err := os.Create("InputFile") if err != nil { t.Fatal("Failed to create file \"InputFile\"") } defer file.Close() defer os.Remove("InputFile") fileData, err := crypto.RandomByteSlice(70000) if err != nil { t.Fatal("Could not generate test data:", err) } err = ioutil.WriteFile("InputFile", fileData, 0644) if err != nil { t.Fatal("Failed to write to file InputFile:", err) } // calculate hash origHash, err := crypto.CalculateHash(fileData) if err != nil { t.Fatal("Failed to calculate hash:", err) } // upload file to quorum k := 50 b, err := UploadFile(tcp, file, k, q) if err != nil { t.Fatal("Failed to upload file:", err) } // wait for all participants to complete for i := range uhs { <-uhs[i].done } // rebuild file from first k segments segments := make([]string, k) indices := make([]uint8, k) for i := 0; i < k; i++ { segments[i] = string(uhs[i].data) indices[i] = uint8(uhs[i].index) } rebuiltData, err := erasure.RebuildSector(k, b, segments, indices) if err != nil { t.Fatal("Failed to rebuild file:", err) } // remove padding rebuiltData = rebuiltData[:len(fileData)] // check hash rebuiltHash, err := crypto.CalculateHash(rebuiltData) if err != nil { t.Fatal("Failed to calculate hash:", err) } if origHash != rebuiltHash { t.Fatal("Failed to recover file: hashes do not match") } }
// NewTCPServer must properly initialize a TCP server. // DownloadFile must successfully retrieve a file from a quorum. // The downloaded file must match the original file. func TestTCPDownloadFile(t *testing.T) { t.Skip() // create file fileData, err := crypto.RandomByteSlice(70000) if err != nil { t.Fatal("Could not generate test data:", err) } // calculate hash origHash, err := crypto.CalculateHash(fileData) if err != nil { t.Fatal("Failed to calculate hash:", err) } // encode file k := 50 bytesPerSegment := len(fileData) / k if bytesPerSegment%64 != 0 { bytesPerSegment += 64 - (bytesPerSegment % 64) padding := k*bytesPerSegment - len(fileData) fileData = append(fileData, bytes.Repeat([]byte{0x00}, padding)...) } segments, err := erasure.EncodeRing(k, bytesPerSegment, fileData) if err != nil { t.Fatal("Failed to encode file data:", err) } // create TCPServer tcp, err := network.NewTCPServer(9988) if err != nil { t.Fatal("Failed to initialize TCPServer:", err) } defer tcp.Close() tdh := new(downloadHandler) tdh.segments = make([]string, k) tdh.indices = make([]uint8, k) tdh.k, tdh.b = k, bytesPerSegment tcp.AddMessageHandler(tdh) // create quorum var q [common.QuorumSize]common.Address var tfhs [common.QuorumSize]TestFileHandler for i := 0; i < common.QuorumSize; i++ { q[i] = common.Address{0, "localhost", 9000 + i} qtcp, err := network.NewTCPServer(9000 + i) if err != nil { t.Fatal("Failed to initialize TCPServer:", err) } tfhs[i].tcpServ = qtcp tfhs[i].dest = tcp.Address() tfhs[i].data = segments[i] tfhs[i].done = make(chan bool, 1) q[i].Id = qtcp.AddMessageHandler(&tfhs[i]).Id } // download file from quorum downData, err := DownloadFile(tcp, origHash, len(fileData), k, q) if err != nil { t.Fatal("Failed to download file:", err) } // wait for download to complete <-tdh.done // check hash rebuiltHash, err := crypto.CalculateHash(downData) if err != nil { t.Fatal("Failed to calculate hash:", err) } if origHash != rebuiltHash { t.Fatal("Failed to recover file: hashes do not match") } }