package main
import (
"bytes"
"context"
"flag"
"fmt"
"log"
"math/rand"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/fyfey/go-merkle/internal/proto"
"github.com/fyfey/go-merkle/pkg/merkle"
"github.com/gosuri/uilive"
"google.golang.org/grpc"
)
type Status int
const (
Downloading Status = iota
Completed
)
type PartStatus struct {
Idx int
Message string
Status Status
}
const ProgressChar = "█"
const BarWidth = 20.00
var messages []string
var hasher merkle.Hasher = &merkle.SHA256Hasher{}
func main() {
var serverAddr string
var workerCount int
flag.StringVar(&serverAddr, "addr", "127.0.0.1:8080", "Server address")
flag.IntVar(&workerCount, "workers", 10, "Worker count")
flag.Parse()
messages = make([]string, workerCount)
log.Println("Connecting to host...")
conn, err := grpc.Dial(serverAddr, grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := proto.NewMerkleClient(conn)
metadata, err := client.GetMetadata(context.Background(), &proto.Empty{})
if err != nil {
log.Fatal(err)
}
file, err := os.Create(filepath.Join("in", metadata.Filename))
if err != nil {
log.Fatalf("Failed to open file")
}
defer file.Close()
in := make(chan int)
ret := make(chan error)
status := make(chan PartStatus)
writer := uilive.New()
writer.RefreshInterval = time.Millisecond
writer.Start()
go statusWriter(writer, status, metadata.Filename, metadata.Parts, metadata.ChunkSize, workerCount)
wg := sync.WaitGroup{}
wg.Add(int(metadata.Parts))
for i := 0; i < workerCount; i++ {
go worker(i, in, ret, status, client, metadata, file, hasher, &wg)
}
go func() {
for i := 0; i < int(metadata.Parts); i++ {
in <- i
}
}()
go func() {
for err := range ret {
if err != nil {
fmt.Println(err.Error())
break
}
}
}()
wg.Wait()
writer.Stop()
fmt.Println("Done!")
close(in)
close(ret)
}
// prove verifies the merkle proof of a part
func prove(p *proto.Proof, ha []byte, hasher merkle.Hasher) bool {
rootHash := p.Nodes[len(p.Nodes)-1].Hash
for i := 0; i < len(p.Nodes)-1; i++ {
if p.Nodes[i].Side == proto.Proof_ProofNode_LEFT {
ha = hasher.Hash(append(ha, p.Nodes[i].Hash...))
} else {
ha = hasher.Hash(append(p.Nodes[i].Hash, ha...))
}
}
return bytes.Equal(ha, rootHash)
}
// woerkers downloads a part from the server and writes it to the file. sending the status of the download to the status channel
func worker(idx int, in chan int, ret chan error, status chan PartStatus, client proto.MerkleClient, metadata *proto.Metadata, file *os.File, hasher merkle.Hasher, wg *sync.WaitGroup) {
for x := range in {
status <- PartStatus{Idx: idx, Status: Downloading, Message: fmt.Sprintf("%d) #%d...\n", idx, x)}
part, err := client.GetPart(context.Background(), &proto.PartRequest{Idx: int32(x)})
if err != nil {
ret <- err
}
if !prove(part.Proof, hasher.Hash(part.Data), hasher) {
ret <- fmt.Errorf("Part %d failed merkle proof check\n", part.Idx)
}
offset := int64(int(part.Idx) * int(metadata.ChunkSize))
_, err = file.WriteAt(part.Data, offset)
if err != nil {
ret <- fmt.Errorf("Failed writing data to file")
} else {
ret <- nil
}
time.Sleep(time.Duration(rand.Int63n(500)+50) * time.Millisecond)
status <- PartStatus{Idx: idx, Status: Completed, Message: fmt.Sprintf("%d) #%d... OK\n", idx, x)}
time.Sleep(500 * time.Millisecond)
wg.Done()
}
}
// statusWriter updates the messages slice with the latest status of each worker's part download
// and each time a status changes, all statuses are redrawn to the terminal
func statusWriter(writer *uilive.Writer, status chan PartStatus, filename string, totalParts int32, chunkSize int32, workerCount int) {
partsCompleted := 0
for msg := range status {
if msg.Status == Completed {
partsCompleted++
}
messages[msg.Idx] = msg.Message
fmt.Fprintf(writer, "Downloading %s with %d workers...\n", filename, workerCount)
for _, m := range messages {
fmt.Fprintf(writer.Newline(), m)
}
percent := float64(partsCompleted) / float64(totalParts) * 100
bars := percent * BarWidth / 100
fmt.Fprintf(
writer.Newline(),
"Progress: %s%s %d/%d\n",
strings.Repeat(ProgressChar, int(bars)),
strings.Repeat(" ", int(BarWidth-bars)),
int32(partsCompleted)*chunkSize,
int32(totalParts)*chunkSize,
)
}
}
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"path/filepath"
"github.com/fyfey/go-merkle/internal/proto"
"github.com/fyfey/go-merkle/internal/server"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)
var filename string
var chunkSize int
func main() {
c := context.Background()
rootCmd := &cobra.Command{
Use: "merkleserver",
Short: "File server using merkle",
Run: func(cmd *cobra.Command, args []string) {
file, err := os.Stat(filepath.Join("out", filename))
if err != nil {
log.Fatal(err)
}
r, err := os.Open(filepath.Join("out", filename))
if err != nil {
log.Fatal(err)
}
defer r.Close()
s := server.NewServer(c, 8080, r, chunkSize, filename)
s.Start()
log.Printf("file %s size %d\n", filename, file.Size())
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
proto.RegisterMerkleServer(grpcServer, s)
log.Printf("Server listening on 0.0.0.0:%d\n", s.Port)
grpcServer.Serve(lis)
},
}
rootCmd.Flags().StringVarP(&filename, "filename", "f", "", "File to serve")
rootCmd.Flags().IntVarP(&chunkSize, "chunksize", "c", 1024, "Chunk size in bytes to split the file")
rootCmd.MarkFlagRequired("filename")
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
c.Done()
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.19.4
// source: merkle.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Proof_ProofNode_Side int32
const (
Proof_ProofNode_LEFT Proof_ProofNode_Side = 0
Proof_ProofNode_RIGHT Proof_ProofNode_Side = 1
)
// Enum value maps for Proof_ProofNode_Side.
var (
Proof_ProofNode_Side_name = map[int32]string{
0: "LEFT",
1: "RIGHT",
}
Proof_ProofNode_Side_value = map[string]int32{
"LEFT": 0,
"RIGHT": 1,
}
)
func (x Proof_ProofNode_Side) Enum() *Proof_ProofNode_Side {
p := new(Proof_ProofNode_Side)
*p = x
return p
}
func (x Proof_ProofNode_Side) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Proof_ProofNode_Side) Descriptor() protoreflect.EnumDescriptor {
return file_merkle_proto_enumTypes[0].Descriptor()
}
func (Proof_ProofNode_Side) Type() protoreflect.EnumType {
return &file_merkle_proto_enumTypes[0]
}
func (x Proof_ProofNode_Side) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Proof_ProofNode_Side.Descriptor instead.
func (Proof_ProofNode_Side) EnumDescriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{2, 0, 0}
}
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{0}
}
type Metadata struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"`
Parts int32 `protobuf:"varint,2,opt,name=parts,proto3" json:"parts,omitempty"`
ChunkSize int32 `protobuf:"varint,3,opt,name=chunkSize,proto3" json:"chunkSize,omitempty"`
Algo string `protobuf:"bytes,4,opt,name=algo,proto3" json:"algo,omitempty"`
}
func (x *Metadata) Reset() {
*x = Metadata{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{1}
}
func (x *Metadata) GetFilename() string {
if x != nil {
return x.Filename
}
return ""
}
func (x *Metadata) GetParts() int32 {
if x != nil {
return x.Parts
}
return 0
}
func (x *Metadata) GetChunkSize() int32 {
if x != nil {
return x.ChunkSize
}
return 0
}
func (x *Metadata) GetAlgo() string {
if x != nil {
return x.Algo
}
return ""
}
type Proof struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Nodes []*Proof_ProofNode `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
}
func (x *Proof) Reset() {
*x = Proof{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Proof) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Proof) ProtoMessage() {}
func (x *Proof) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Proof.ProtoReflect.Descriptor instead.
func (*Proof) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{2}
}
func (x *Proof) GetNodes() []*Proof_ProofNode {
if x != nil {
return x.Nodes
}
return nil
}
type Part struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Idx int32 `protobuf:"varint,1,opt,name=idx,proto3" json:"idx,omitempty"`
Proof *Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"`
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *Part) Reset() {
*x = Part{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Part) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Part) ProtoMessage() {}
func (x *Part) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Part.ProtoReflect.Descriptor instead.
func (*Part) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{3}
}
func (x *Part) GetIdx() int32 {
if x != nil {
return x.Idx
}
return 0
}
func (x *Part) GetProof() *Proof {
if x != nil {
return x.Proof
}
return nil
}
func (x *Part) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
type PartRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Idx int32 `protobuf:"varint,1,opt,name=idx,proto3" json:"idx,omitempty"`
}
func (x *PartRequest) Reset() {
*x = PartRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PartRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PartRequest) ProtoMessage() {}
func (x *PartRequest) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PartRequest.ProtoReflect.Descriptor instead.
func (*PartRequest) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{4}
}
func (x *PartRequest) GetIdx() int32 {
if x != nil {
return x.Idx
}
return 0
}
type Proof_ProofNode struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Side Proof_ProofNode_Side `protobuf:"varint,1,opt,name=side,proto3,enum=merkle.Proof_ProofNode_Side" json:"side,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
}
func (x *Proof_ProofNode) Reset() {
*x = Proof_ProofNode{}
if protoimpl.UnsafeEnabled {
mi := &file_merkle_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Proof_ProofNode) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Proof_ProofNode) ProtoMessage() {}
func (x *Proof_ProofNode) ProtoReflect() protoreflect.Message {
mi := &file_merkle_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Proof_ProofNode.ProtoReflect.Descriptor instead.
func (*Proof_ProofNode) Descriptor() ([]byte, []int) {
return file_merkle_proto_rawDescGZIP(), []int{2, 0}
}
func (x *Proof_ProofNode) GetSide() Proof_ProofNode_Side {
if x != nil {
return x.Side
}
return Proof_ProofNode_LEFT
}
func (x *Proof_ProofNode) GetHash() []byte {
if x != nil {
return x.Hash
}
return nil
}
var File_merkle_proto protoreflect.FileDescriptor
var file_merkle_proto_rawDesc = []byte{
0x0a, 0x0c, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
0x6e, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x66,
0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66,
0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x1c, 0x0a,
0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61,
0x6c, 0x67, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x67, 0x6f, 0x22,
0xa6, 0x01, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x2d, 0x0a, 0x05, 0x6e, 0x6f, 0x64,
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c,
0x65, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x4e, 0x6f, 0x64,
0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x1a, 0x6e, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6f,
0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f,
0x6f, 0x66, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x69, 0x64,
0x65, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x1b, 0x0a, 0x04, 0x53,
0x69, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x46, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a,
0x05, 0x52, 0x49, 0x47, 0x48, 0x54, 0x10, 0x01, 0x22, 0x51, 0x0a, 0x04, 0x50, 0x61, 0x72, 0x74,
0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69,
0x64, 0x78, 0x12, 0x23, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0d, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66,
0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x1f, 0x0a, 0x0b, 0x50,
0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64,
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69, 0x64, 0x78, 0x32, 0x6a, 0x0a, 0x06,
0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x50, 0x61, 0x72,
0x74, 0x12, 0x13, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e,
0x50, 0x61, 0x72, 0x74, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0d, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x2e, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x79, 0x66, 0x65, 0x79, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_merkle_proto_rawDescOnce sync.Once
file_merkle_proto_rawDescData = file_merkle_proto_rawDesc
)
func file_merkle_proto_rawDescGZIP() []byte {
file_merkle_proto_rawDescOnce.Do(func() {
file_merkle_proto_rawDescData = protoimpl.X.CompressGZIP(file_merkle_proto_rawDescData)
})
return file_merkle_proto_rawDescData
}
var file_merkle_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_merkle_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_merkle_proto_goTypes = []interface{}{
(Proof_ProofNode_Side)(0), // 0: merkle.Proof.ProofNode.Side
(*Empty)(nil), // 1: merkle.Empty
(*Metadata)(nil), // 2: merkle.Metadata
(*Proof)(nil), // 3: merkle.Proof
(*Part)(nil), // 4: merkle.Part
(*PartRequest)(nil), // 5: merkle.PartRequest
(*Proof_ProofNode)(nil), // 6: merkle.Proof.ProofNode
}
var file_merkle_proto_depIdxs = []int32{
6, // 0: merkle.Proof.nodes:type_name -> merkle.Proof.ProofNode
3, // 1: merkle.Part.proof:type_name -> merkle.Proof
0, // 2: merkle.Proof.ProofNode.side:type_name -> merkle.Proof.ProofNode.Side
5, // 3: merkle.Merkle.GetPart:input_type -> merkle.PartRequest
1, // 4: merkle.Merkle.GetMetadata:input_type -> merkle.Empty
4, // 5: merkle.Merkle.GetPart:output_type -> merkle.Part
2, // 6: merkle.Merkle.GetMetadata:output_type -> merkle.Metadata
5, // [5:7] is the sub-list for method output_type
3, // [3:5] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_merkle_proto_init() }
func file_merkle_proto_init() {
if File_merkle_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_merkle_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_merkle_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Metadata); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_merkle_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Proof); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_merkle_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Part); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_merkle_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PartRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_merkle_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Proof_ProofNode); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_merkle_proto_rawDesc,
NumEnums: 1,
NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_merkle_proto_goTypes,
DependencyIndexes: file_merkle_proto_depIdxs,
EnumInfos: file_merkle_proto_enumTypes,
MessageInfos: file_merkle_proto_msgTypes,
}.Build()
File_merkle_proto = out.File
file_merkle_proto_rawDesc = nil
file_merkle_proto_goTypes = nil
file_merkle_proto_depIdxs = nil
}
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// MerkleClient is the client API for Merkle service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MerkleClient interface {
GetPart(ctx context.Context, in *PartRequest, opts ...grpc.CallOption) (*Part, error)
GetMetadata(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Metadata, error)
}
type merkleClient struct {
cc grpc.ClientConnInterface
}
func NewMerkleClient(cc grpc.ClientConnInterface) MerkleClient {
return &merkleClient{cc}
}
func (c *merkleClient) GetPart(ctx context.Context, in *PartRequest, opts ...grpc.CallOption) (*Part, error) {
out := new(Part)
err := c.cc.Invoke(ctx, "/merkle.Merkle/GetPart", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *merkleClient) GetMetadata(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Metadata, error) {
out := new(Metadata)
err := c.cc.Invoke(ctx, "/merkle.Merkle/GetMetadata", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MerkleServer is the server API for Merkle service.
// All implementations must embed UnimplementedMerkleServer
// for forward compatibility
type MerkleServer interface {
GetPart(context.Context, *PartRequest) (*Part, error)
GetMetadata(context.Context, *Empty) (*Metadata, error)
mustEmbedUnimplementedMerkleServer()
}
// UnimplementedMerkleServer must be embedded to have forward compatible implementations.
type UnimplementedMerkleServer struct {
}
func (UnimplementedMerkleServer) GetPart(context.Context, *PartRequest) (*Part, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPart not implemented")
}
func (UnimplementedMerkleServer) GetMetadata(context.Context, *Empty) (*Metadata, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMetadata not implemented")
}
func (UnimplementedMerkleServer) mustEmbedUnimplementedMerkleServer() {}
// UnsafeMerkleServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MerkleServer will
// result in compilation errors.
type UnsafeMerkleServer interface {
mustEmbedUnimplementedMerkleServer()
}
func RegisterMerkleServer(s grpc.ServiceRegistrar, srv MerkleServer) {
s.RegisterService(&Merkle_ServiceDesc, srv)
}
func _Merkle_GetPart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PartRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MerkleServer).GetPart(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/merkle.Merkle/GetPart",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MerkleServer).GetPart(ctx, req.(*PartRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Merkle_GetMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MerkleServer).GetMetadata(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/merkle.Merkle/GetMetadata",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MerkleServer).GetMetadata(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
// Merkle_ServiceDesc is the grpc.ServiceDesc for Merkle service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Merkle_ServiceDesc = grpc.ServiceDesc{
ServiceName: "merkle.Merkle",
HandlerType: (*MerkleServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetPart",
Handler: _Merkle_GetPart_Handler,
},
{
MethodName: "GetMetadata",
Handler: _Merkle_GetMetadata_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "merkle.proto",
}
package server
import (
"context"
"encoding/hex"
"errors"
"fmt"
"io"
"log"
"strconv"
"strings"
"github.com/fyfey/go-merkle/internal/proto"
"github.com/fyfey/go-merkle/pkg/merkle"
)
type Server struct {
context context.Context
Port int
File io.Reader
ChunkSize int
tree *merkle.Tree
Filename string
data [][]byte
proto.UnimplementedMerkleServer
}
func NewServer(c context.Context, port int, file io.Reader, chunkSize int, filename string) *Server {
return &Server{
context: c,
Port: port,
File: file,
ChunkSize: chunkSize,
Filename: filename,
}
}
func (s *Server) Start() {
log.Println("Starting server...")
log.Println("Chunk size:", s.ChunkSize)
log.Println("Creating merkle tree...")
tree, err := merkle.ReadTree(s.File, s.ChunkSize)
if err != nil {
log.Fatal(err)
}
printTree(tree)
log.Println("Merkle tree created with root", hex.EncodeToString(tree.Root()))
s.tree = tree
}
// GetMetedata gets the file's metadata
func (s *Server) GetMetadata(context.Context, *proto.Empty) (*proto.Metadata, error) {
log.Println("GetMetadata")
return &proto.Metadata{
Filename: s.Filename,
Parts: int32(len(s.tree.GetLeaves())),
ChunkSize: int32(s.ChunkSize),
}, nil
}
// GetPart returns a given part
func (s *Server) GetPart(ctx context.Context, in *proto.PartRequest) (*proto.Part, error) {
log.Printf("GetPart %d\n", in.Idx)
data := s.tree.GetLeaf(int(in.Idx)).Data
if len(data) == 0 {
return nil, errors.New("Part does not exist")
}
proof := s.tree.GetLeaf(int(in.Idx)).GetProof()
pbProof := &proto.Proof{
Nodes: make([]*proto.Proof_ProofNode, len(proof)),
}
for i, node := range proof {
side := proto.Proof_ProofNode_RIGHT
if node.Left {
side = proto.Proof_ProofNode_LEFT
}
pbProof.Nodes[i] = &proto.Proof_ProofNode{
Hash: node.Hash,
Side: side,
}
}
part := &proto.Part{
Idx: in.Idx,
Data: data,
Proof: pbProof,
}
return part, nil
}
// printTree prints the merkle tree stats in a visual way
func printTree(tree *merkle.Tree) {
height := strconv.Itoa(tree.Height())
hexRoot := hex.EncodeToString(tree.Root())
leaves := strconv.Itoa(len(tree.GetLeaves()))
fmt.Printf(
"\n"+
" ╔═══════════════════════════╗ \n"+
" ║ root: %s ║ \n"+
" ║ / \\ ↑ ║ \n"+
" ║ / \\ height: %s%s ║ \n"+
" ║ / \\ / \\ ↓ ║ \n"+
" ║ . . . . ║ \n"+
" ║ %s leaves%s ║ \n"+
" ╚═══════════════════════════╝ \n"+
"\n",
hexRoot[:8]+"…"+hexRoot[len(hexRoot)-8:],
height,
strings.Repeat(" ", 7-len(height)),
leaves,
strings.Repeat(" ", 12-len(leaves)),
)
}
package merkle
import "crypto/sha256"
type Hasher interface {
Hash(data []byte) []byte
}
type SHA256Hasher struct{}
func (s SHA256Hasher) Hash(data []byte) []byte {
hash := sha256.Sum256(data)
return hash[:]
}
type DoubleSHA256Hasher struct{}
func (d DoubleSHA256Hasher) Hash(data []byte) []byte {
hash := sha256.Sum256(data)
hash = sha256.Sum256(hash[:])
return hash[:]
}
package merkle
import (
"encoding/hex"
"encoding/json"
"fmt"
)
type Node struct {
hasher Hasher
parent *Node
left, right *Node
hash []byte
Data []byte
}
// NewNode creates a new node and hashes the data
func NewNode(data []byte, hasher Hasher) *Node {
return &Node{
hash: hasher.Hash(data),
hasher: hasher,
Data: data,
}
}
// NewRawNode creates a new node and hashes the data
func NewRawNode(hash []byte, hasher Hasher) *Node {
return &Node{hash: hash, hasher: hasher}
}
func (n *Node) PrintHash() string {
return hex.EncodeToString(n.hash)
}
func (n *Node) String() string {
return fmt.Sprintf("Left: %v, Hash: %s", n.left, hex.EncodeToString(n.hash))
}
// NewParent creates a new node and sets the left and right children
func NewParent(left *Node, right *Node) *Node {
parent := &Node{hasher: left.hasher}
return parent.SetChildren(left, right)
}
// SetChildren sets the left and right children of a node and calculates the hash
func (n *Node) SetChildren(left *Node, right *Node) *Node {
n.left = left
n.right = right
left.parent = n
right.parent = n
n.hash = n.hasher.Hash(append(left.hash, right.hash...))
return n
}
// MarshalJSON marshals the node to JSON
func (n Node) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Left *Node `json:"left"`
Right *Node `json:"right"`
Hash string `json:"hash"`
}{
n.left,
n.right,
hex.EncodeToString(n.hash),
})
}
// Sibling returns the sibling of a node
func (n *Node) Sibling() *Node {
if n.parent == nil {
return nil
}
if n.parent.left == n {
return n.parent.right
}
return n.parent.left
}
// Uncle returns the uncle of a node
func (n *Node) Uncle() *Node {
if n.parent == nil {
return nil
}
return n.parent.Sibling()
}
package merkle
import (
"bytes"
"encoding/hex"
"fmt"
)
type ProofNode struct {
Left bool
Hash []byte
}
func (n ProofNode) String() string {
return fmt.Sprintf("Left: %v, Hash: %s", n.Left, hex.EncodeToString(n.Hash))
}
type MerkleProof []ProofNode
// GetProof returns a merkleProof (list of hashes and whether to place your computed hash on the left)
// The first item in the hash is for the sibling at height 0, then for the sibling of the computed hash
// The last item in the hash is the root hash and should be compared against the computed root hash.
func (n *Node) GetProof() MerkleProof {
proof := MerkleProof{}
nextProof := n.Sibling()
for {
left := bytes.Equal(nextProof.parent.right.hash, nextProof.hash)
proof = append(proof, ProofNode{left, nextProof.hash})
if nextProof.Uncle() == nil {
proof = append(proof, ProofNode{left, nextProof.parent.hash})
break
}
nextProof = nextProof.Uncle()
}
return proof
}
// Prove proves that the a hash is correct for the given proof
func (p MerkleProof) Prove(h []byte, hasher Hasher) bool {
root := p[len(p)-1].Hash
for i := 0; i < len(p)-1; i++ {
if p[i].Left {
h = hasher.Hash(append(h, p[i].Hash...))
} else {
h = hasher.Hash(append(p[i].Hash, h...))
}
}
return bytes.Equal(h, root)
}
package merkle
import (
"errors"
"io"
)
type OddLeafStrategy int
const (
DuplicateOddLeaves OddLeafStrategy = iota + 1
IgnoreOddLeaves
)
type Tree struct {
data [][]*Node
hasher Hasher
oddLeafStrategy OddLeafStrategy
}
type TreeOpt func(*Tree)
func WithSHA256Hasher() TreeOpt {
return func(t *Tree) {
t.hasher = SHA256Hasher{}
}
}
func WithDoubleSHA256Hasher() TreeOpt {
return func(t *Tree) {
t.hasher = DoubleSHA256Hasher{}
}
}
func WithDuplicateOddLeaves() TreeOpt {
return func(t *Tree) {
t.oddLeafStrategy = DuplicateOddLeaves
}
}
func NewTree(opts ...TreeOpt) *Tree {
data := make([][]*Node, 0)
data = append(data, make([]*Node, 0))
t := &Tree{data: data}
for _, opt := range opts {
opt(t)
}
if t.hasher == nil {
t.hasher = SHA256Hasher{}
}
if t.oddLeafStrategy == 0 {
t.oddLeafStrategy = IgnoreOddLeaves
}
return t
}
func ReadTree(r io.Reader, chunkSize int) (*Tree, error) {
if chunkSize <= 0 {
return nil, errors.New("invalid chunk size. Must be greater than 0")
}
buf := make([]byte, chunkSize)
Tree := NewTree()
for {
read, err := r.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
newData := make([]byte, read)
copy(newData, buf[:read])
Tree.Add(newData)
}
Tree.Build()
return Tree, nil
}
func (t *Tree) Add(data []byte) *Tree {
base := t.data
base[0] = append(base[0], NewNode(data, t.hasher))
t.data = base
return t
}
func (t *Tree) AddRaw(hash []byte) *Tree {
base := t.data
base[0] = append(base[0], NewRawNode(hash, t.hasher))
t.data = base
return t
}
// GetLeaf returns the leaf node at the given index
func (t *Tree) GetLeaf(index int) *Node {
return t.data[0][index]
}
// GetHeight returns the nodes at the given height
func (t *Tree) GetHeight(index int) []*Node {
return t.data[index]
}
// GetLeaves returns a slice of leaf nodes
func (t *Tree) GetLeaves() []*Node {
return t.data[0]
}
// Height returns the height of the tree (leaf nodes are at height 0)
func (t *Tree) Height() int {
return len(t.data) - 1
}
// Root returns the root hash of the tree
func (t *Tree) Root() []byte {
return t.data[len(t.data)-1][0].hash
}
// Build builds the tree from the leaf nodes
func (t *Tree) Build() error {
nodes := t.data
if len(nodes) != 1 {
return errors.New("Tree already built")
}
if len(nodes[0]) == 0 {
return errors.New("No nodes to build")
}
height := 0
for {
if len(nodes[height]) == 1 {
break
}
nextHeight := make([]*Node, 0)
for i := 0; i < int(len(nodes[height])/2)*2; i += 2 {
newNode := NewParent(nodes[height][i], nodes[height][i+1])
nextHeight = append(nextHeight, newNode)
}
if len(nodes[height])%2 != 0 {
switch t.oddLeafStrategy {
case DuplicateOddLeaves:
// create a parent with the same node as both children
newNode := NewParent(nodes[height][len(nodes[height])-1], nodes[height][len(nodes[height])-1])
nextHeight = append(nextHeight, newNode)
case IgnoreOddLeaves:
nextHeight = append(nextHeight, nodes[height][len(nodes[height])-1])
default:
return errors.New("Invalid odd leaf strategy")
}
}
nodes = append(nodes, nextHeight)
height++
}
t.data = nodes
return nil
}