Commit ebc50243 authored by David Vorick's avatar David Vorick

faster, compatible encoding scheme for []crypto.Hash

parent 7bf30e64
package modules
import (
"encoding/json"
"io"
"time"
......@@ -147,6 +148,44 @@ func (s HostDBScans) Len() int { return len(s) }
func (s HostDBScans) Less(i, j int) bool { return s[i].Timestamp.Before(s[j].Timestamp) }
func (s HostDBScans) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// MerkleRootSet is a set of Merkle roots, and gets encoded more efficiently.
type MerkleRootSet []crypto.Hash
// MarshalJSON defines a JSON encoding for a MerkleRootSet.
func (mrs MerkleRootSet) MarshalJSON() ([]byte, error) {
// Copy the whole array into a giant byte slice and then encode that.
fullBytes := make([]byte, crypto.HashSize*len(mrs))
for i := range mrs {
copy(fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize], mrs[i][:])
}
return json.Marshal(fullBytes)
}
// UnmarshalJSON attempts to decode a MerkleRootSet, falling back on the legacy
// decoding of a []crypto.Hash if that fails.
func (mrs *MerkleRootSet) UnmarshalJSON(b []byte) error {
// Decode the giant byte slice, and then split it into separate arrays.
var fullBytes []byte
err := json.Unmarshal(b, &fullBytes)
if err != nil {
// Encoding the byte slice has failed, try decoding it as a []crypto.Hash.
var hashes []crypto.Hash
err := json.Unmarshal(b, &hashes)
if err != nil {
return err
}
*mrs = MerkleRootSet(hashes)
return nil
}
umrs := make(MerkleRootSet, len(fullBytes)/32)
for i := range umrs {
copy(umrs[i][:], fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize])
}
*mrs = umrs
return nil
}
// A RenterContract contains all the metadata necessary to revise or renew a
// file contract.
type RenterContract struct {
......@@ -155,7 +194,7 @@ type RenterContract struct {
ID types.FileContractID `json:"id"`
LastRevision types.FileContractRevision `json:"lastrevision"`
LastRevisionTxn types.Transaction `json:"lastrevisiontxn"`
MerkleRoots []crypto.Hash `json:"merkleroots"`
MerkleRoots MerkleRootSet `json:"merkleroots"`
NetAddress NetAddress `json:"netaddress"`
SecretKey crypto.SecretKey `json:"secretkey"`
StartHeight types.BlockHeight `json:"startheight"`
......
package modules
import (
"crypto/rand"
"os"
"path/filepath"
"testing"
"github.com/NebulousLabs/Sia/build"
"github.com/NebulousLabs/Sia/crypto"
"github.com/NebulousLabs/Sia/persist"
)
// TestMerkleRootSetCompatibility checks that the persist encoding for the
// MerkleRootSet type is compatible with the previous encoding for the data,
// which was a slice of type crypto.Hash.
func TestMerkleRootSetCompatibility(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
// Create some fake headers for the files.
meta := persist.Metadata{
Header: "Test Header",
Version: "0.1.2",
}
// Try multiple sizes of array.
for i := 0; i < 10; i++ {
// Create a []crypto.Hash of length i.
type chStruct struct {
Hashes []crypto.Hash
}
var chs chStruct
for j := 0; j < i; j++ {
var ch crypto.Hash
_, err := rand.Read(ch[:])
if err != nil {
t.Fatal(err)
}
chs.Hashes = append(chs.Hashes, ch)
}
// Save and load, check that they are the same.
dir := build.TempDir("modules", "testMerkleRootSetCompatibility")
err := os.MkdirAll(dir, 0700)
if err != nil {
t.Fatal(err)
}
filename := filepath.Join(dir, "file")
err = persist.SaveFile(meta, chs, filename)
if err != nil {
t.Fatal(err)
}
// Load and verify equivalence.
var loadCHS chStruct
err = persist.LoadFile(meta, &loadCHS, filename)
if err != nil {
t.Fatal(err)
}
if len(chs.Hashes) != len(loadCHS.Hashes) {
t.Fatal("arrays should be the same size")
}
for j := range chs.Hashes {
if chs.Hashes[j] != loadCHS.Hashes[j] {
t.Error("loading failed", i, j)
}
}
// Load into MerkleRootSet and verify equivalence.
type mrStruct struct {
Hashes MerkleRootSet
}
var loadMRS mrStruct
err = persist.LoadFile(meta, &loadMRS, filename)
if err != nil {
t.Fatal(err)
}
if len(chs.Hashes) != len(loadMRS.Hashes) {
t.Fatal("arrays should be the same size")
}
for j := range chs.Hashes {
if chs.Hashes[j] != loadMRS.Hashes[j] {
t.Error("loading failed", i, j)
}
}
// Save as a MerkleRootSet and verify it can be loaded again.
var mrs mrStruct
mrs.Hashes = MerkleRootSet(chs.Hashes)
err = persist.SaveFile(meta, mrs, filename)
if err != nil {
t.Fatal(err)
}
err = persist.LoadFile(meta, &loadMRS, filename)
if err != nil {
t.Fatal(err)
}
if len(mrs.Hashes) != len(loadMRS.Hashes) {
t.Fatal("arrays should be the same size")
}
for j := range mrs.Hashes {
if mrs.Hashes[j] != loadMRS.Hashes[j] {
t.Error("loading failed", i, j)
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment