Skip to content

Commit

Permalink
Merge pull request #67 from JackalLabs/marston/ipfs-folders-again
Browse files Browse the repository at this point in the history
Implementing a new Proof Type for Raw IPFS Nodes
  • Loading branch information
TheMarstonConnell authored Nov 28, 2024
2 parents 8f8fb52 + 5406a81 commit 2add5ce
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 51 deletions.
33 changes: 24 additions & 9 deletions api/file_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io"
"net/http"
"strconv"
"strings"
"time"

cid "github.com/ipfs/go-cid"
Expand Down Expand Up @@ -60,6 +59,16 @@ func PostFileHandler(fio *file_system.FileSystem, prover *proofs.Prover, wl *wal
return
}

proofTypeString := req.Form.Get("type")
if len(proofTypeString) == 0 {
proofTypeString = "0"
}
proofType, err := strconv.ParseInt(proofTypeString, 10, 64)
if err != nil {
handleErr(fmt.Errorf("cannot parse proof type: %w", err), w, http.StatusBadRequest)
return
}

file, fh, err := req.FormFile("file") // Retrieve the file from form data
if err != nil {
handleErr(fmt.Errorf("cannot get file from form: %w", err), w, http.StatusBadRequest)
Expand Down Expand Up @@ -103,7 +112,7 @@ func PostFileHandler(fio *file_system.FileSystem, prover *proofs.Prover, wl *wal
}
}

size, c, err := fio.WriteFile(file, merkle, sender, startBlock, wl.AccAddress(), chunkSize)
size, c, err := fio.WriteFile(file, merkle, sender, startBlock, wl.AccAddress(), chunkSize, proofType)
if err != nil {
handleErr(fmt.Errorf("failed to write file to disk: %w", err), w, http.StatusInternalServerError)
return
Expand Down Expand Up @@ -139,19 +148,24 @@ func PostIPFSFolder(f *file_system.FileSystem) func(http.ResponseWriter, *http.R
}
defer req.Body.Close()

cidList := strings.Split(string(body), ",")

childCIDs := make([]cid.Cid, len(cidList))
var cidList map[string]string

fmt.Println(cidList)
err = json.Unmarshal(body, &cidList)
if err != nil {
if err != nil {
http.Error(w, "Error parsing request body", http.StatusInternalServerError)
return
}
}

for i, s := range cidList {
childCIDs := make(map[string]cid.Cid)
for key, s := range cidList {
c, err := cid.Parse(s)
if err != nil {
http.Error(w, fmt.Sprintf("Could not parse %s", s), http.StatusInternalServerError)
return
}
childCIDs[i] = c
childCIDs[key] = c
}

root, err := f.CreateIPFSFolder(childCIDs)
Expand All @@ -161,7 +175,8 @@ func PostIPFSFolder(f *file_system.FileSystem) func(http.ResponseWriter, *http.R
}

f := types.CidFolderResponse{
Cid: root,
Cid: root.Cid().String(),
Data: root.RawData(),
}

err = json.NewEncoder(w).Encode(f)
Expand Down
3 changes: 2 additions & 1 deletion api/types/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ type CidResponse struct {
}

type CidFolderResponse struct {
Cid string `json:"cid"`
Cid string `json:"cid"`
Data []byte `json:"data"`
}

type CidMapResponse struct {
Expand Down
80 changes: 59 additions & 21 deletions file_system/file_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"fmt"
"io"

"github.com/ipfs/boxo/ipld/merkledag"

"github.com/ipfs/boxo/ipld/unixfs"

"github.com/dgraph-io/badger/v4"
Expand Down Expand Up @@ -73,7 +75,7 @@ func BuildTree(buf io.Reader, chunkSize int64) ([]byte, []byte, [][]byte, int, e
return r, exportedTree, chunks, size, nil
}

func (f *FileSystem) WriteFile(reader io.Reader, merkle []byte, owner string, start int64, address string, chunkSize int64) (size int, cid string, err error) {
func (f *FileSystem) WriteFile(reader io.Reader, merkle []byte, owner string, start int64, address string, chunkSize int64, proofType int64) (size int, cid string, err error) {
log.Info().Msg(fmt.Sprintf("Writing %x to disk", merkle))
root, exportedTree, chunks, s, err := BuildTree(reader, chunkSize)
if err != nil {
Expand All @@ -91,9 +93,24 @@ func (f *FileSystem) WriteFile(reader io.Reader, merkle []byte, owner string, st
}
buf := bytes.NewBuffer(b)

n, err := f.ipfs.AddFile(context.Background(), buf, nil)
if err != nil {
return 0, "", err
var n ipldFormat.Node
if proofType == 1 {
folderNode := unixfs.EmptyDirNode()
err := folderNode.UnmarshalJSON(buf.Bytes())
if err != nil {
return 0, "", err
}

err = f.ipfs.Add(context.Background(), folderNode)
if err != nil {
return 0, "", err
}
n = folderNode
} else {
n, err = f.ipfs.AddFile(context.Background(), buf, nil)
if err != nil {
return 0, "", err
}
}

err = f.db.Update(func(txn *badger.Txn) error {
Expand All @@ -116,31 +133,39 @@ func (f *FileSystem) WriteFile(reader io.Reader, merkle []byte, owner string, st
return size, n.Cid().String(), nil
}

func (f *FileSystem) CreateIPFSFolder(childCIDs []cid.Cid) (cidRes string, err error) {
func (f *FileSystem) CreateIPFSFolder(childCIDs map[string]cid.Cid) (node ipldFormat.Node, err error) {
n, err := f.GenIPFSFolderData(childCIDs)
if err != nil {
return nil, err
}

// Add the folder node to the DAG service
err = f.ipfs.Add(context.Background(), n)
if err != nil {
return nil, err
}

return n, nil
}

func (f *FileSystem) GenIPFSFolderData(childCIDs map[string]cid.Cid) (node ipldFormat.Node, err error) {
folderNode := unixfs.EmptyDirNode()

for i, childCID := range childCIDs {
for key, childCID := range childCIDs {
// Create a link
linkName := fmt.Sprintf("%d", i)
link := &ipldFormat.Link{
Name: linkName,
Name: key,
Cid: childCID,
}

// Add the link to the folder node
err := folderNode.AddRawLink(linkName, link)
err := folderNode.AddRawLink(key, link)
if err != nil {
return "", err
return nil, err
}
}

// Add the folder node to the DAG service
err = f.ipfs.Add(context.Background(), folderNode)
if err != nil {
return "", err
}

return folderNode.Cid().String(), nil
return folderNode, nil
}

func (f *FileSystem) DeleteFile(merkle []byte, owner string, start int64) error {
Expand Down Expand Up @@ -281,7 +306,7 @@ func (f *FileSystem) Dump() (map[string]string, error) {
return files, err
}

func (f *FileSystem) GetFileTreeByChunk(merkle []byte, owner string, start int64, chunkToLoad int, chunkSize int) (*merkletree.MerkleTree, []byte, error) {
func (f *FileSystem) GetFileTreeByChunk(merkle []byte, owner string, start int64, chunkToLoad int, chunkSize int, proofType int64) (*merkletree.MerkleTree, []byte, error) {
tree := treeKey(merkle, owner, start)

var newTree merkletree.MerkleTree
Expand Down Expand Up @@ -330,9 +355,22 @@ func (f *FileSystem) GetFileTreeByChunk(merkle []byte, owner string, start int64
return nil, nil, fmt.Errorf("failed to decode cid: %s | %w", fcid, err)
}

chunkOut, err := f.ipfs.GetFileChunk(context.Background(), c, chunkToLoad, chunkSize)
if err != nil {
return nil, nil, fmt.Errorf("failed to get chunk from unixfs %w", err)
var chunkOut []byte
if proofType == 1 {
n, err := f.ipfs.Get(context.Background(), c)
if err != nil {
return nil, nil, fmt.Errorf("failed to get node chunk for cid: %s | %w", fcid, err)
}
data, err := n.(*merkledag.ProtoNode).MarshalJSON()
if err != nil {
return nil, nil, fmt.Errorf("failed to cast proto node: %s | %w", fcid, err)
}
chunkOut = data
} else {
chunkOut, err = f.ipfs.GetFileChunk(context.Background(), c, chunkToLoad, chunkSize)
if err != nil {
return nil, nil, fmt.Errorf("failed to get chunk from unixfs %w", err)
}
}

if chunkOut == nil {
Expand Down
10 changes: 5 additions & 5 deletions file_system/file_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func BenchmarkFileWrites(b *testing.B) {
root, _, _, _, err := BuildTree(buf2, 10240)
require.NoError(b, err)

_, _, err = f.WriteFile(buf, root, "file_owner", 0, "myself", 10240)
_, _, err = f.WriteFile(buf, root, "file_owner", 0, "myself", 10240, 0)
require.NoError(b, err)

}
Expand Down Expand Up @@ -115,7 +115,7 @@ func TestWriteFile(t *testing.T) {
m, err := hex.DecodeString(merkle)
require.NoError(t, err)

size, _, err := f.WriteFile(buf, m, "file_owner", 0, "myself", 1024)
size, _, err := f.WriteFile(buf, m, "file_owner", 0, "myself", 1024, 0)

require.NoError(t, err)

Expand Down Expand Up @@ -165,7 +165,7 @@ func TestWriteFileWithDomain(t *testing.T) {
m, err := hex.DecodeString(merkle)
require.NoError(t, err)

size, _, err := f.WriteFile(buf, m, "file_owner", 0, "myself", 1024)
size, _, err := f.WriteFile(buf, m, "file_owner", 0, "myself", 1024, 0)

require.NoError(t, err)

Expand Down Expand Up @@ -228,7 +228,7 @@ func TestWriteAndProveFiles(t *testing.T) {
owner := "file_owner"
var start int64 = 0

_, _, err = f.WriteFile(b2, root, owner, start, "myself", chunkSize)
_, _, err = f.WriteFile(b2, root, owner, start, "myself", chunkSize, 0)
require.NoError(t, err)

ms, _, _, err := f.ListFiles()
Expand All @@ -239,7 +239,7 @@ func TestWriteAndProveFiles(t *testing.T) {
totalBlocks := size / int(chunkSize)
for i := 0; i < totalBlocks; i++ {

p, c, err := proofs.GenProof(f, root, owner, start, i, int(chunkSize))
p, c, err := proofs.GenProof(f, root, owner, start, i, int(chunkSize), 0)
require.NoError(t, err)

h := sha256.New()
Expand Down
20 changes: 16 additions & 4 deletions ipfs/ipfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import (
"fmt"
"strings"

"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/host"
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"

ipfslite "github.com/hsanjuan/ipfs-lite"
"github.com/ipfs/boxo/blockstore"
datastore "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore"
"github.com/libp2p/go-libp2p/core/crypto"
multiaddr "github.com/multiformats/go-multiaddr"
"github.com/multiformats/go-multiaddr"
)

func MakeIPFS(ctx context.Context, ds datastore.Batching, bs blockstore.Blockstore, port int, customDomain string) (*ipfslite.Peer, host.Host, error) {
Expand All @@ -20,16 +22,22 @@ func MakeIPFS(ctx context.Context, ds datastore.Batching, bs blockstore.Blocksto
return nil, nil, err
}

defaultPort, err := multiaddr.NewMultiaddr("/ip4/0.0.0.0/tcp/4001")
if err != nil {
return nil, nil, fmt.Errorf("failed to make ipv4 ipfs address | %w", err)
}

listen, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port))
if err != nil {
return nil, nil, fmt.Errorf("failed to make ipv4 ipfs address | %w", err)
}

listen6, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip6/::/tcp/%d", port))
if err != nil {
return nil, nil, fmt.Errorf("failed to make ipv6 ipfs address | %w", err)
}

m := []multiaddr.Multiaddr{listen, listen6}
m := []multiaddr.Multiaddr{listen, listen6, defaultPort}

if !strings.Contains(customDomain, "example.com") && len(customDomain) > 2 {
if !strings.HasPrefix(customDomain, "/") {
Expand All @@ -42,13 +50,17 @@ func MakeIPFS(ctx context.Context, ds datastore.Batching, bs blockstore.Blocksto
m = append(m, domainListener)
}

opts := libp2p.ChainOptions(
libp2p.Security(libp2ptls.ID, libp2ptls.New),
)

h, dht, err := ipfslite.SetupLibp2p(
ctx,
priv,
nil,
m,
ds,
ipfslite.Libp2pOptionsExtra...,
append(ipfslite.Libp2pOptionsExtra, opts)...,
)
if err != nil {
return nil, h, err
Expand Down
8 changes: 4 additions & 4 deletions network/downloads.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/rs/zerolog/log"
)

func DownloadFile(f *file_system.FileSystem, merkle []byte, owner string, start int64, wallet *wallet.Wallet, fileSize int64, myUrl string, chunkSize int64) error {
func DownloadFile(f *file_system.FileSystem, merkle []byte, owner string, start int64, wallet *wallet.Wallet, fileSize int64, myUrl string, chunkSize int64, proofType int64) error {
queryParams := &types.QueryFindFile{
Merkle: merkle,
}
Expand All @@ -37,7 +37,7 @@ func DownloadFile(f *file_system.FileSystem, merkle []byte, owner string, start
continue
}

size, err := DownloadFileFromURL(f, url, merkle, owner, start, wallet.AccAddress(), chunkSize)
size, err := DownloadFileFromURL(f, url, merkle, owner, start, wallet.AccAddress(), chunkSize, proofType)
if err != nil {
log.Info().Msg(fmt.Sprintf("Couldn't get %x from %s, trying again...", merkle, url))
continue
Expand All @@ -58,7 +58,7 @@ func DownloadFile(f *file_system.FileSystem, merkle []byte, owner string, start
return nil
}

func DownloadFileFromURL(f *file_system.FileSystem, url string, merkle []byte, owner string, start int64, address string, chunkSize int64) (int, error) {
func DownloadFileFromURL(f *file_system.FileSystem, url string, merkle []byte, owner string, start int64, address string, chunkSize int64, proofType int64) (int, error) {
log.Info().Msg(fmt.Sprintf("Downloading %x from %s...", merkle, url))
cli := http.Client{}
req, err := http.NewRequest("GET", fmt.Sprintf("%s/download/%x", url, merkle), nil)
Expand Down Expand Up @@ -93,7 +93,7 @@ func DownloadFileFromURL(f *file_system.FileSystem, url string, merkle []byte, o

reader := bytes.NewReader(buff.Bytes())

size, _, err := f.WriteFile(reader, merkle, owner, start, address, chunkSize)
size, _, err := f.WriteFile(reader, merkle, owner, start, address, chunkSize, proofType)
if err != nil {
return 0, err
}
Expand Down
6 changes: 3 additions & 3 deletions proofs/proofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func GenerateMerkleProof(tree *merkletree.MerkleTree, index int, item []byte) (b
// GenProof generates a proof from an arbitrary file on the file system
//
// returns proof, item and error
func GenProof(io FileSystem, merkle []byte, owner string, start int64, block int, chunkSize int) ([]byte, []byte, error) {
tree, chunk, err := io.GetFileTreeByChunk(merkle, owner, start, block, chunkSize)
func GenProof(io FileSystem, merkle []byte, owner string, start int64, block int, chunkSize int, proofType int64) ([]byte, []byte, error) {
tree, chunk, err := io.GetFileTreeByChunk(merkle, owner, start, block, chunkSize, proofType)
if err != nil {
e := fmt.Errorf("cannot get chunk for %x at %d | %w", merkle, block, err)
log.Error().Err(e)
Expand Down Expand Up @@ -143,7 +143,7 @@ func (p *Prover) GenerateProof(merkle []byte, owner string, start int64, blockHe

log.Debug().Msg(fmt.Sprintf("Getting file tree by chunk for %x", merkle))

proof, item, err := GenProof(p.io, merkle, owner, start, block, p.chunkSize)
proof, item, err := GenProof(p.io, merkle, owner, start, block, p.chunkSize, file.ProofType)

return proof, item, newProof.ChunkToProve, err
}
Expand Down
2 changes: 1 addition & 1 deletion proofs/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Prover struct {
type FileSystem interface {
DeleteFile([]byte, string, int64) error
ProcessFiles(func([]byte, string, int64)) error
GetFileTreeByChunk([]byte, string, int64, int, int) (*merkletree.MerkleTree, []byte, error)
GetFileTreeByChunk([]byte, string, int64, int, int, int64) (*merkletree.MerkleTree, []byte, error)
}

func (p *Prover) Inc() {
Expand Down
2 changes: 1 addition & 1 deletion recycle/recycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (r *RecycleDepot) activateFile(openFile types.UnifiedFile) (size int, cid s
openFile.Owner,
openFile.Start,
"",
r.chunkSize)
r.chunkSize, 0)
if err != nil {
return 0, "", fmt.Errorf("could not write file | %w", err)
}
Expand Down
Loading

0 comments on commit 2add5ce

Please sign in to comment.