Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

Commit

Permalink
Add ability to read a file through ProofReader.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brendan McMillion committed Mar 26, 2019
1 parent 0b7b886 commit 362e69a
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 7 deletions.
22 changes: 21 additions & 1 deletion hamt/hamt.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
ipld "github.com/ipfs/go-ipld-format"
dag "github.com/ipfs/go-merkledag"
format "github.com/ipfs/go-unixfs"
coreiface "github.com/ipfs/interface-go-ipfs-core"
)

const (
Expand Down Expand Up @@ -61,6 +62,8 @@ type Shard struct {
// leaf node
key string
val *ipld.Link

rawData []byte
}

// NewShard creates a new, empty HAMT shard with the given size.
Expand Down Expand Up @@ -126,6 +129,7 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) {
ds.cid = pbnd.Cid()
ds.hashFunc = fsn.HashType()
ds.builder = pbnd.CidBuilder()
ds.rawData = pbnd.RawData()

return ds, nil
}
Expand Down Expand Up @@ -280,17 +284,33 @@ func (ds *Shard) Link() (*ipld.Link, error) {
return ipld.MakeLink(nd)
}

func hamtChunk(name string, rawData []byte) []byte {
buff := make([]byte, 4+len(name)+len(rawData))
buff[0] = 1
buff[1] = byte(len(name) >> 16)
buff[2] = byte(len(name) >> 8)
buff[3] = byte(len(name))
copy(buff[4:], []byte(name))
copy(buff[4+len(name):], rawData)

return buff
}

func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error {
childIndex, err := hv.Next(ds.tableSizeLg2)
if err != nil {
return err
}
sliceIndex := ds.childer.sliceIndex(childIndex)

if ds.childer.has(childIndex) {
child, err := ds.childer.get(ctx, ds.childer.sliceIndex(childIndex))
child, err := ds.childer.get(ctx, sliceIndex)
if err != nil {
return err
}
if pw, ok := ctx.Value("proxy-preamble").(coreiface.ProofWriter); ok {
pw.WriteChunk(hamtChunk(ds.childer.link(sliceIndex).Name, ds.rawData))
}

if child.isValueNode() {
if child.key == key {
Expand Down
99 changes: 99 additions & 0 deletions io/proofreader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io

import (
"context"
"fmt"
"io"

ipld "github.com/ipfs/go-ipld-format"
dag "github.com/ipfs/go-merkledag"
mdag "github.com/ipfs/go-merkledag"
unixfs "github.com/ipfs/go-unixfs"
pb "github.com/ipfs/go-unixfs/pb"
coreiface "github.com/ipfs/interface-go-ipfs-core"
)

// NewDagReaderWithProof creates a new proof reader object that reads the data
// represented by the given node, using the passed in DAGService for data
// retrieval.
func NewDagReaderWithProof(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (coreiface.ProofReader, error) {
switch n := n.(type) {
case *mdag.RawNode:
case *mdag.ProtoNode:
fsNode, err := unixfs.FSNodeFromBytes(n.Data())
if err != nil {
return nil, err
}

switch fsNode.Type() {
case unixfs.TFile, unixfs.TRaw:
case unixfs.TDirectory, unixfs.THAMTShard:
return nil, ErrIsDir
case unixfs.TSymlink:
return nil, ErrCantReadSymlinks
default:
return nil, unixfs.ErrUnrecognizedType
}
default:
return nil, ErrUnkownNodeType
}

ctxWithCancel, cancel := context.WithCancel(ctx)

return &proofReader{
cancel: cancel,
dagWalker: ipld.NewWalker(ctxWithCancel, ipld.NewNavigableIPLDNode(n, serv)),
}, nil
}

// proofReader provides a way to easily read the data contained in a dag, in a
// way that it can be presented to an untrusting client.
type proofReader struct {
dagWalker *ipld.Walker
cancel func()
}

func (pr *proofReader) ReadChunk() ([]byte, error) {
var out []byte

err := pr.dagWalker.Iterate(func(visitedNode ipld.NavigableNode) error {
node := ipld.ExtractIPLDNode(visitedNode)

switch node := node.(type) {
case *dag.RawNode:
out = append([]byte{0}, node.RawData()...)

case *dag.ProtoNode:
fsNode, err := unixfs.FSNodeFromBytes(node.Data())
if err != nil {
return fmt.Errorf("incorrectly formatted protobuf: %s", err)
}

switch fsNode.Type() {
case pb.Data_File, pb.Data_Raw:
out = append([]byte{1}, node.RawData()...)
default:
return fmt.Errorf("found %s node in unexpected place",
fsNode.Type().String())
}

default:
return unixfs.ErrUnrecognizedType
}

pr.dagWalker.Pause()
return nil
})

if err == ipld.EndOfDag {
return nil, io.EOF
} else if err != nil {
return nil, err
}
return out, nil
}

func (pr *proofReader) Close() error {
pr.cancel()
return nil
}
11 changes: 5 additions & 6 deletions io/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
hamt "github.com/ipfs/go-unixfs/hamt"

ipld "github.com/ipfs/go-ipld-format"
coreiface "github.com/ipfs/interface-go-ipfs-core"
)

// ResolveUnixfsOnce resolves a single hop of a path through a graph in a
Expand All @@ -16,12 +17,7 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na
pn, ok := nd.(*dag.ProtoNode)
if ok {
fsn, err := ft.FSNodeFromBytes(pn.Data())
if err != nil {
// Not a unixfs node, use standard object traversal code
return nd.ResolveLink(names)
}

if fsn.Type() == ft.THAMTShard {
if err == nil && fsn.Type() == ft.THAMTShard {
rods := dag.NewReadOnlyDagService(ds)
s, err := hamt.NewHamtFromDag(rods, nd)
if err != nil {
Expand All @@ -37,5 +33,8 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na
}
}

if pw, ok := ctx.Value("proxy-preamble").(coreiface.ProofWriter); ok {
pw.WriteChunk(append([]byte{0}, nd.RawData()...))
}
return nd.ResolveLink(names)
}

0 comments on commit 362e69a

Please sign in to comment.