From 37cdd17ba82f23606aa4d149382cccd4ad15a2c9 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 08:54:40 +0200 Subject: [PATCH 01/26] Update CGO variables settings --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c4036ba..4c7de4a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { "go.toolsEnvVars": { + "CGO_ENABLED": "1", "CGO_CFLAGS": "-I${workspaceFolder}/vendor/nim-codex/library", - "CGO_LDFLAGS": "-L${workspaceFolder}/vendor/nim-codex/build -Wl,-rpath,${workspaceFolder}/vendor/nim-codex/build", + "CGO_LDFLAGS": "-L${workspaceFolder}/vendor/nim-codex/build -lcodex -Wl,-rpath,${workspaceFolder}/vendor/nim-codex/build", "LD_LIBRARY_PATH": "${workspaceFolder}/vendor/nim-codex/build:${env:LD_LIBRARY_PATH}" } } \ No newline at end of file From 7218764de54bd05bfc10cd855e55d1432f7542c1 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 08:55:01 +0200 Subject: [PATCH 02/26] Add upload cancellation --- codex/testutil.go | 3 +- codex/upload.go | 42 ++++++++++++++++++++++----- codex/upload_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 11 deletions(-) diff --git a/codex/testutil.go b/codex/testutil.go index d98ddc5..79909b2 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -2,6 +2,7 @@ package codex import ( "bytes" + "context" "testing" ) @@ -65,7 +66,7 @@ func uploadHelper(t *testing.T, codex *CodexNode) (string, int) { buf := bytes.NewBuffer([]byte("Hello World!")) len := buf.Len() - cid, err := codex.UploadReader(UploadOptions{Filepath: "hello.txt"}, buf) + cid, err := codex.UploadReader(context.Background(), UploadOptions{Filepath: "hello.txt"}, buf) if err != nil { t.Fatalf("Error happened during upload: %v\n", err) } diff --git a/codex/upload.go b/codex/upload.go index 3e0bfc6..cd71f8d 100644 --- a/codex/upload.go +++ b/codex/upload.go @@ -27,6 +27,8 @@ package codex import "C" import ( "bytes" + "context" + "errors" "fmt" "io" "os" @@ -164,7 +166,7 @@ func (node CodexNode) UploadCancel(sessionId string) error { // - UploadChunk to upload a chunk to codex. // - UploadFinalize to finalize the upload session. // - UploadCancel if an error occurs. -func (node CodexNode) UploadReader(options UploadOptions, r io.Reader) (string, error) { +func (node CodexNode) UploadReader(ctx context.Context, options UploadOptions, r io.Reader) (string, error) { sessionId, err := node.UploadInit(&options) if err != nil { return "", err @@ -179,6 +181,16 @@ func (node CodexNode) UploadReader(options UploadOptions, r io.Reader) (string, } for { + select { + case <-ctx.Done(): + if cancelErr := node.UploadCancel(sessionId); cancelErr != nil { + return "", fmt.Errorf("upload canceled: %v, but failed to cancel upload session: %v", ctx.Err(), cancelErr) + } + return "", errors.New("upload canceled") + default: + // continue + } + n, err := r.Read(buf) if err == io.EOF { break @@ -222,9 +234,9 @@ func (node CodexNode) UploadReader(options UploadOptions, r io.Reader) (string, } // UploadReaderAsync is the asynchronous version of UploadReader using a goroutine. -func (node CodexNode) UploadReaderAsync(options UploadOptions, r io.Reader, onDone func(cid string, err error)) { +func (node CodexNode) UploadReaderAsync(ctx context.Context, options UploadOptions, r io.Reader, onDone func(cid string, err error)) { go func() { - cid, err := node.UploadReader(options, r) + cid, err := node.UploadReader(ctx, options, r) onDone(cid, err) }() } @@ -249,7 +261,7 @@ func (node CodexNode) UploadReaderAsync(options UploadOptions, r io.Reader, onDo // is sent to the stream. // // Internally, it calls UploadInit to create the upload session. -func (node CodexNode) UploadFile(options UploadOptions) (string, error) { +func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (string, error) { bridge := newBridgeCtx() defer bridge.free() @@ -293,13 +305,29 @@ func (node CodexNode) UploadFile(options UploadOptions) (string, error) { return "", bridge.callError("cGoCodexUploadFile") } - return bridge.wait() + var cancelErr error + select { + case <-ctx.Done(): + cancelErr = node.UploadCancel(sessionId) + default: + // continue + } + + _, err = bridge.wait() + if err != nil { + if cancelErr != nil { + return "", fmt.Errorf("upload canceled: %v, but failed to cancel upload session: %v", ctx.Err(), cancelErr) + } + return "", err + } + + return bridge.result, nil } // UploadFileAsync is the asynchronous version of UploadFile using a goroutine. -func (node CodexNode) UploadFileAsync(options UploadOptions, onDone func(cid string, err error)) { +func (node CodexNode) UploadFileAsync(ctx context.Context, options UploadOptions, onDone func(cid string, err error)) { go func() { - cid, err := node.UploadFile(options) + cid, err := node.UploadFile(ctx, options) onDone(cid, err) }() } diff --git a/codex/upload_test.go b/codex/upload_test.go index c58397f..687b214 100644 --- a/codex/upload_test.go +++ b/codex/upload_test.go @@ -2,6 +2,7 @@ package codex import ( "bytes" + "context" "log" "os" "testing" @@ -16,7 +17,7 @@ func TestUploadReader(t *testing.T) { buf := bytes.NewBuffer([]byte("Hello World!")) len := buf.Len() - cid, err := codex.UploadReader(UploadOptions{Filepath: "hello.txt", OnProgress: func(read, total int, percent float64, err error) { + cid, err := codex.UploadReader(context.Background(), UploadOptions{Filepath: "hello.txt", OnProgress: func(read, total int, percent float64, err error) { if err != nil { log.Fatalf("Error happened during upload: %v\n", err) } @@ -42,6 +43,31 @@ func TestUploadReader(t *testing.T) { } } +func TestUploadReaderCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + codex := newCodexNode(t) + buf := bytes.NewBuffer(make([]byte, 1024*1024*10)) + + channelErr := make(chan error, 1) + go func() { + _, e := codex.UploadReader(ctx, UploadOptions{Filepath: "hello.txt"}, buf) + channelErr <- e + }() + + cancel() + err := <-channelErr + + if err == nil { + t.Fatal("UploadReader should have been canceled") + } + + if err.Error() != "upload canceled" { + t.Fatalf("UploadReader returned unexpected error: %v", err) + } +} + func TestUploadFile(t *testing.T) { codex := newCodexNode(t) totalBytes := 0 @@ -61,7 +87,7 @@ func TestUploadFile(t *testing.T) { finalPercent = percent }} - cid, err := codex.UploadFile(options) + cid, err := codex.UploadFile(context.Background(), options) if err != nil { t.Fatalf("UploadReader failed: %v", err) } @@ -79,12 +105,49 @@ func TestUploadFile(t *testing.T) { } } +func TestUploadFileCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // create a tmp file with large content + tmpFile, err := os.Create("./testdata/large_file.txt") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tmpFile.Name()) + + largeContent := make([]byte, 1024*1024*50) + if _, err := tmpFile.Write(largeContent); err != nil { + t.Fatalf("Failed to write to temp file: %v", err) + } + tmpFile.Close() + + codex := newCodexNode(t) + + channelError := make(chan error, 1) + go func() { + _, e := codex.UploadFile(ctx, UploadOptions{Filepath: tmpFile.Name()}) + channelError <- e + }() + + cancel() + err = <-channelError + + if err == nil { + t.Fatal("UploadFile should have been canceled") + } + + if err.Error() != "Failed to upload the file: Failed to stream the file: Stream Closed!" { + t.Fatalf("UploadFile returned unexpected error: %v", err) + } +} + func TestUploadFileNoProgress(t *testing.T) { codex := newCodexNode(t) options := UploadOptions{Filepath: "./testdata/doesnt_exist.txt"} - cid, err := codex.UploadFile(options) + cid, err := codex.UploadFile(context.Background(), options) if err == nil { t.Fatalf("UploadReader should have failed") } From e30c492cf89aefbbe5c49b47733722f3dc2fa514 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 13:16:40 +0200 Subject: [PATCH 03/26] Update README --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 2cdd457..e7bf1a0 100644 --- a/README.md +++ b/README.md @@ -174,8 +174,6 @@ onProgress := func(read, total int, percent float64, err error) { cid, err := codex.UploadReader(UploadOptions{filepath: "hello.txt", onProgress: onProgress}, buf) ``` -Caveat: once started, the upload cannot be cancelled. - #### file The `file` strategy allows you to upload a file on Codex using the path. @@ -192,8 +190,6 @@ onProgress := func(read, total int, percent float64, err error) { cid, err := codex.UploadFile(UploadOptions{filepath: "./testdata/hello.txt", onProgress: onProgress}) ``` -Caveat: once started, the upload cannot be cancelled. - #### chunks The `chunks` strategy allows you to manage the upload by yourself. It requires more code @@ -249,8 +245,6 @@ opt := DownloadStreamOptions{ err := codex.DownloadStream(cid, opt) ``` -Caveat: once started, the download cannot be cancelled. - #### chunks The `chunks` strategy allows to manage the download by yourself. It requires more code From ca4993e4b0288413a50b67af2b6ee4f188eb3bab Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 13:17:28 +0200 Subject: [PATCH 04/26] Bump nim codex --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index 1105b81..eaf20e1 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit 1105b81cc1b202006ca5a16485b3cfc5331468d5 +Subproject commit eaf20e17bc577c789f3df078b9ed1238556656d3 From 43e6e5e81f722ddd4cfced9ea5128b33ad3a8a48 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 13:18:36 +0200 Subject: [PATCH 05/26] Add support for context and cancellation --- codex/download.go | 32 +++++++++++++++++++++++++++++--- codex/download_test.go | 31 ++++++++++++++++++++++++++++--- codex/testutil.go | 20 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/codex/download.go b/codex/download.go index 2feee01..9e1f958 100644 --- a/codex/download.go +++ b/codex/download.go @@ -26,7 +26,9 @@ package codex */ import "C" import ( + "context" "encoding/json" + "fmt" "io" "unsafe" ) @@ -145,7 +147,7 @@ func (node CodexNode) DownloadManifest(cid string) (Manifest, error) { // If options.writer is set, the data will be written into that writer. // The options filepath and writer are not mutually exclusive, i.e you can write // in different places in a same call. -func (node CodexNode) DownloadStream(cid string, options DownloadStreamOptions) error { +func (node CodexNode) DownloadStream(ctx context.Context, cid string, options DownloadStreamOptions) error { bridge := newBridgeCtx() defer bridge.free() @@ -189,6 +191,14 @@ func (node CodexNode) DownloadStream(cid string, options DownloadStreamOptions) var cCid = C.CString(cid) defer C.free(unsafe.Pointer(cCid)) + err := node.DownloadInit(cid, DownloadInitOptions{ + ChunkSize: options.ChunkSize, + Local: options.Local, + }) + if err != nil { + return err + } + var cFilepath = C.CString(options.Filepath) defer C.free(unsafe.Pointer(cFilepath)) @@ -198,8 +208,24 @@ func (node CodexNode) DownloadStream(cid string, options DownloadStreamOptions) return bridge.callError("cGoCodexDownloadLocal") } - _, err := bridge.wait() - return err + var cancelErr error + select { + case <-ctx.Done(): + cancelErr = node.DownloadCancel(cid) + default: + // continue + } + + _, err = bridge.wait() + + if err != nil { + if cancelErr != nil { + return fmt.Errorf("upload canceled: %v, but failed to cancel upload session: %v", ctx.Err(), cancelErr) + } + return err + } + + return nil } // DownloadInit initializes the download process for a specific CID. diff --git a/codex/download_test.go b/codex/download_test.go index 764d388..c680498 100644 --- a/codex/download_test.go +++ b/codex/download_test.go @@ -1,6 +1,7 @@ package codex import ( + "context" "os" "strings" "testing" @@ -32,7 +33,7 @@ func TestDownloadStream(t *testing.T) { }, } - if err := codex.DownloadStream(cid, opt); err != nil { + if err := codex.DownloadStream(context.Background(), cid, opt); err != nil { t.Fatal("Error happened:", err.Error()) } @@ -72,7 +73,7 @@ func TestDownloadStreamWithAutosize(t *testing.T) { }, } - if err := codex.DownloadStream(cid, opt); err != nil { + if err := codex.DownloadStream(context.Background(), cid, opt); err != nil { t.Fatal("Error happened:", err.Error()) } @@ -89,11 +90,35 @@ func TestDownloadStreamWithNotExisting(t *testing.T) { codex := newCodexNode(t, withBlockRetries(1)) opt := DownloadStreamOptions{} - if err := codex.DownloadStream("bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku", opt); err == nil { + if err := codex.DownloadStream(context.Background(), "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku", opt); err == nil { t.Fatal("Error expected when downloading non-existing cid") } } +func TestDownloadStreamCancelled(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + + codex := newCodexNode(t) + cid, _ := uploadBigFileHelper(t, codex) + + channelError := make(chan error, 1) + go func() { + err := codex.DownloadStream(ctx, cid, DownloadStreamOptions{Local: true}) + channelError <- err + }() + + cancel() + err := <-channelError + + if err == nil { + t.Fatal("UploadFile should have been canceled") + } + + if err.Error() != "Failed to stream file: Stream EOF!" { + t.Fatalf("UploadFile returned unexpected error: %v", err) + } +} + func TestDownloadManual(t *testing.T) { codex := newCodexNode(t) cid, _ := uploadHelper(t, codex) diff --git a/codex/testutil.go b/codex/testutil.go index 79909b2..54e2ea5 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -34,6 +34,7 @@ func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { LogFormat: LogFormatNoColors, MetricsEnabled: false, BlockRetries: o.blockRetries, + LogLevel: INFO, }) if err != nil { t.Fatalf("Failed to create Codex node: %v", err) @@ -44,6 +45,11 @@ func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { if err != nil { t.Fatalf("Failed to start Codex node: %v", err) } + + err := node.UpdateLogLevel("INFO") + if err != nil { + t.Fatalf("Failed to set log level: %v", err) + } } t.Cleanup(func() { @@ -73,3 +79,17 @@ func uploadHelper(t *testing.T, codex *CodexNode) (string, int) { return cid, len } + +func uploadBigFileHelper(t *testing.T, codex *CodexNode) (string, int) { + t.Helper() + + len := 1024 * 1024 * 50 + buf := bytes.NewBuffer(make([]byte, len)) + + cid, err := codex.UploadReader(context.Background(), UploadOptions{Filepath: "hello.txt"}, buf) + if err != nil { + t.Fatalf("Error happened during upload: %v\n", err) + } + + return cid, len +} From fca7f76ac119ef9848d77107bb756785af854666 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 13:18:53 +0200 Subject: [PATCH 06/26] Add cancellation test --- codex/upload_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/codex/upload_test.go b/codex/upload_test.go index 687b214..857064e 100644 --- a/codex/upload_test.go +++ b/codex/upload_test.go @@ -45,7 +45,6 @@ func TestUploadReader(t *testing.T) { func TestUploadReaderCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - defer cancel() codex := newCodexNode(t) buf := bytes.NewBuffer(make([]byte, 1024*1024*10)) @@ -107,10 +106,8 @@ func TestUploadFile(t *testing.T) { func TestUploadFileCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - // create a tmp file with large content - tmpFile, err := os.Create("./testdata/large_file.txt") + tmpFile, err := os.Create(os.TempDir() + "/large_file.txt") if err != nil { t.Fatalf("Failed to create temp file: %v", err) } @@ -126,8 +123,8 @@ func TestUploadFileCancel(t *testing.T) { channelError := make(chan error, 1) go func() { - _, e := codex.UploadFile(ctx, UploadOptions{Filepath: tmpFile.Name()}) - channelError <- e + _, err := codex.UploadFile(ctx, UploadOptions{Filepath: tmpFile.Name()}) + channelError <- err }() cancel() From 40aadaf1048e43ff2a76e529297b507cfc1034c9 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 14:27:18 +0200 Subject: [PATCH 07/26] Bump nim codex --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index eaf20e1..dab820f 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit eaf20e17bc577c789f3df078b9ed1238556656d3 +Subproject commit dab820fa44ebde368effd8a5be15fb104d266440 From 27b68f6d8feb7a85e4ea1739aab4b9254d4826ad Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 14:27:31 +0200 Subject: [PATCH 08/26] Update README --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e7bf1a0..c8a16c3 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,8 @@ buf := bytes.NewBuffer([]byte("Hello World!")) onProgress := func(read, total int, percent float64, err error) { // Do something with the data } -cid, err := codex.UploadReader(UploadOptions{filepath: "hello.txt", onProgress: onProgress}, buf) +ctx := context.Background() +cid, err := codex.UploadReader(ctx, UploadOptions{filepath: "hello.txt", onProgress: onProgress}, buf) ``` #### file @@ -187,7 +188,8 @@ The `UploadFile` returns the cid of the content uploaded. onProgress := func(read, total int, percent float64, err error) { // Do something with the data } -cid, err := codex.UploadFile(UploadOptions{filepath: "./testdata/hello.txt", onProgress: onProgress}) +ctx := context.Background() +cid, err := codex.UploadFile(ctx, UploadOptions{filepath: "./testdata/hello.txt", onProgress: onProgress}) ``` #### chunks @@ -242,7 +244,8 @@ opt := DownloadStreamOptions{ // Handle progress }, } -err := codex.DownloadStream(cid, opt) +ctx := context.Background() +err := codex.DownloadStream(ctx, cid, opt) ``` #### chunks From a68d92ee8c936a36e5c8191598f3d65335a54b66 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 14:34:34 +0200 Subject: [PATCH 09/26] Update readme --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8a16c3..29e3c20 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Now the module is ready for use in your project. The release process is defined [here](./RELEASE.md). -## Usage +## API ### Init @@ -307,4 +307,10 @@ peerId := "..." record, err := node.CodexPeerDebug(peerId) ``` -`CodexPeerDebug` is only available if you built with `-d:codex_enable_api_debug_peers=true` flag. \ No newline at end of file +`CodexPeerDebug` is only available if you built with `-d:codex_enable_api_debug_peers=true` flag. + +### Context and cancellation + +Go contexts are exposed only on the long-running operations as `UploadReader`, `UploadFile`, and `DownloadFile`. If the +context is cancelled, those methods cancel the active upload or download. Short lived API calls don’t take a context +because they usually finish before a cancellation signal could matter. \ No newline at end of file From 4a20b994448535dcdd6e9a61a270a01b76617856 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 14:55:54 +0200 Subject: [PATCH 10/26] Remove log level --- codex/testutil.go | 1 - 1 file changed, 1 deletion(-) diff --git a/codex/testutil.go b/codex/testutil.go index 54e2ea5..efa863c 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -34,7 +34,6 @@ func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { LogFormat: LogFormatNoColors, MetricsEnabled: false, BlockRetries: o.blockRetries, - LogLevel: INFO, }) if err != nil { t.Fatalf("Failed to create Codex node: %v", err) From a2983631d6535edbf9e85bf8e2976ae9edfca10a Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 17 Oct 2025 14:56:33 +0200 Subject: [PATCH 11/26] Remove useless call to UpdateLogLevel --- codex/testutil.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/codex/testutil.go b/codex/testutil.go index efa863c..aed20e4 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -44,11 +44,6 @@ func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { if err != nil { t.Fatalf("Failed to start Codex node: %v", err) } - - err := node.UpdateLogLevel("INFO") - if err != nil { - t.Fatalf("Failed to set log level: %v", err) - } } t.Cleanup(func() { From 3d66b10fa94048cd2678a0d7cca81b21de67b1e9 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 20 Oct 2025 13:06:27 +0200 Subject: [PATCH 12/26] Fix cancel error monitoring --- codex/download.go | 30 +++++++++++++++++++++--------- codex/upload.go | 26 +++++++++++++++++++------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/codex/download.go b/codex/download.go index 9e1f958..2bc476d 100644 --- a/codex/download.go +++ b/codex/download.go @@ -208,20 +208,32 @@ func (node CodexNode) DownloadStream(ctx context.Context, cid string, options Do return bridge.callError("cGoCodexDownloadLocal") } - var cancelErr error - select { - case <-ctx.Done(): - cancelErr = node.DownloadCancel(cid) - default: - // continue - } + done := make(chan struct{}) + channelError := make(chan error, 1) + go func() { + select { + case <-ctx.Done(): + channelError <- node.DownloadCancel(cid) + case <-done: + // Nothing to do, download finished + } + }() _, err = bridge.wait() + close(done) + + // Extract the potential cancellation error + var cancelError error + select { + case cancelError = <-channelError: + default: + } if err != nil { - if cancelErr != nil { - return fmt.Errorf("upload canceled: %v, but failed to cancel upload session: %v", ctx.Err(), cancelErr) + if cancelError != nil { + return fmt.Errorf("download canceled: %v, but failed to cancel download session: %v", ctx.Err(), cancelError) } + return err } diff --git a/codex/upload.go b/codex/upload.go index cd71f8d..0643d87 100644 --- a/codex/upload.go +++ b/codex/upload.go @@ -305,15 +305,27 @@ func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (st return "", bridge.callError("cGoCodexUploadFile") } - var cancelErr error - select { - case <-ctx.Done(): - cancelErr = node.UploadCancel(sessionId) - default: - // continue - } + done := make(chan struct{}) + channelError := make(chan error, 1) + go func() { + select { + case <-ctx.Done(): + channelError <- node.UploadCancel(sessionId) + case <-done: + // Nothing to do, upload finished + } + }() _, err = bridge.wait() + close(done) + + // Extract the potential cancellation error + var cancelErr error + select { + case cancelErr = <-channelError: + default: + } + if err != nil { if cancelErr != nil { return "", fmt.Errorf("upload canceled: %v, but failed to cancel upload session: %v", ctx.Err(), cancelErr) From bcdff2f8bac85960ce8c44a7eb221d3ae46b13ef Mon Sep 17 00:00:00 2001 From: Arnaud Date: Tue, 21 Oct 2025 07:40:47 +0200 Subject: [PATCH 13/26] Defer download cancel after the download session is created --- codex/download.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codex/download.go b/codex/download.go index 2bc476d..8b67b55 100644 --- a/codex/download.go +++ b/codex/download.go @@ -199,6 +199,8 @@ func (node CodexNode) DownloadStream(ctx context.Context, cid string, options Do return err } + defer node.DownloadCancel(cid) + var cFilepath = C.CString(options.Filepath) defer C.free(unsafe.Pointer(cFilepath)) From bc8bbf417267923d9c581f1f7adebbb0e9aeadd9 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 23 Oct 2025 06:11:31 +0200 Subject: [PATCH 14/26] Return cancellation errors --- codex/download.go | 2 +- codex/upload.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codex/download.go b/codex/download.go index 8b67b55..debe0d3 100644 --- a/codex/download.go +++ b/codex/download.go @@ -239,7 +239,7 @@ func (node CodexNode) DownloadStream(ctx context.Context, cid string, options Do return err } - return nil + return cancelError } // DownloadInit initializes the download process for a specific CID. diff --git a/codex/upload.go b/codex/upload.go index 0643d87..56455b5 100644 --- a/codex/upload.go +++ b/codex/upload.go @@ -333,7 +333,7 @@ func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (st return "", err } - return bridge.result, nil + return bridge.result, cancelErr } // UploadFileAsync is the asynchronous version of UploadFile using a goroutine. From a9fd02863ce700216a2fc5e39c6c9b00d8458676 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 23 Oct 2025 09:47:39 +0200 Subject: [PATCH 15/26] Defer channel close --- codex/download.go | 5 ++++- codex/upload.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/codex/download.go b/codex/download.go index debe0d3..5794849 100644 --- a/codex/download.go +++ b/codex/download.go @@ -210,7 +210,11 @@ func (node CodexNode) DownloadStream(ctx context.Context, cid string, options Do return bridge.callError("cGoCodexDownloadLocal") } + // Create a done channel to signal the goroutine to stop + // when the download is complete and avoid goroutine leaks. done := make(chan struct{}) + defer close(done) + channelError := make(chan error, 1) go func() { select { @@ -222,7 +226,6 @@ func (node CodexNode) DownloadStream(ctx context.Context, cid string, options Do }() _, err = bridge.wait() - close(done) // Extract the potential cancellation error var cancelError error diff --git a/codex/upload.go b/codex/upload.go index 56455b5..4696721 100644 --- a/codex/upload.go +++ b/codex/upload.go @@ -305,7 +305,11 @@ func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (st return "", bridge.callError("cGoCodexUploadFile") } + // Create a done channel to signal the goroutine to stop + // when the download is complete and avoid goroutine leaks. done := make(chan struct{}) + defer close(done) + channelError := make(chan error, 1) go func() { select { @@ -317,7 +321,6 @@ func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (st }() _, err = bridge.wait() - close(done) // Extract the potential cancellation error var cancelErr error From d2d8802245824d2a90af3166f24fc1d0e210b959 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 23 Oct 2025 11:02:57 +0200 Subject: [PATCH 16/26] Bump nim-codex --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index dab820f..06795c5 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit dab820fa44ebde368effd8a5be15fb104d266440 +Subproject commit 06795c5de562eedc8e179cc0296027295e9b16b9 From 24789fb8558e57868c7510b04d728685edbb8c97 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 23 Oct 2025 11:03:27 +0200 Subject: [PATCH 17/26] Defer UploadCancel after UploadInit to be sure that the upload is cancelled after the function is done --- codex/upload.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codex/upload.go b/codex/upload.go index 4696721..532879d 100644 --- a/codex/upload.go +++ b/codex/upload.go @@ -171,6 +171,7 @@ func (node CodexNode) UploadReader(ctx context.Context, options UploadOptions, r if err != nil { return "", err } + defer node.UploadCancel(sessionId) buf := make([]byte, options.ChunkSize.valOrDefault()) total := 0 @@ -297,6 +298,7 @@ func (node CodexNode) UploadFile(ctx context.Context, options UploadOptions) (st if err != nil { return "", err } + defer node.UploadCancel(sessionId) var cSessionId = C.CString(sessionId) defer C.free(unsafe.Pointer(cSessionId)) From cf52b7097eb714559dad712627bcb06ef3b5293d Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Oct 2025 05:28:23 +0200 Subject: [PATCH 18/26] Bump nim codex --- codex/testutil.go | 1 + vendor/nim-codex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/codex/testutil.go b/codex/testutil.go index aed20e4..c02e54f 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -34,6 +34,7 @@ func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { LogFormat: LogFormatNoColors, MetricsEnabled: false, BlockRetries: o.blockRetries, + LogLevel: "ERROR", }) if err != nil { t.Fatalf("Failed to create Codex node: %v", err) diff --git a/vendor/nim-codex b/vendor/nim-codex index 06795c5..aa1bdf2 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit 06795c5de562eedc8e179cc0296027295e9b16b9 +Subproject commit aa1bdf2300934335eecbd0d6a7091675121d4439 From 597b98dacc7ad1466a0299509d55277afde444d6 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Oct 2025 15:11:22 +0200 Subject: [PATCH 19/26] Bump nim codex --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index aa1bdf2..d50206f 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit aa1bdf2300934335eecbd0d6a7091675121d4439 +Subproject commit d50206fd452fcb7d4a29012954504d74241819d2 From 35c06ba71994f7a306986c5c369807948f0aeca2 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Oct 2025 15:12:42 +0200 Subject: [PATCH 20/26] Update the configuration pattern to create a new node for the tests --- codex/codex_test.go | 12 ++++++-- codex/debug_test.go | 42 +++++++++------------------ codex/download_test.go | 6 ++-- codex/storage_test.go | 2 +- codex/testutil.go | 66 ++++++++++++++++++++---------------------- 5 files changed, 60 insertions(+), 68 deletions(-) diff --git a/codex/codex_test.go b/codex/codex_test.go index d0cb91f..deb7cb7 100644 --- a/codex/codex_test.go +++ b/codex/codex_test.go @@ -3,7 +3,11 @@ package codex import "testing" func TestCodexVersion(t *testing.T) { - node := newCodexNode(t, withNoStart()) + config := defaultConfigHelper(t) + node, err := New(config) + if err != nil { + t.Fatalf("Failed to create Codex node: %v", err) + } version, err := node.Version() if err != nil { @@ -17,7 +21,11 @@ func TestCodexVersion(t *testing.T) { } func TestCodexRevision(t *testing.T) { - node := newCodexNode(t, withNoStart()) + config := defaultConfigHelper(t) + node, err := New(config) + if err != nil { + t.Fatalf("Failed to create Codex node: %v", err) + } revision, err := node.Revision() if err != nil { diff --git a/codex/debug_test.go b/codex/debug_test.go index a3be06c..ba4f7b4 100644 --- a/codex/debug_test.go +++ b/codex/debug_test.go @@ -32,38 +32,19 @@ func TestUpdateLogLevel(t *testing.T) { } defer os.Remove(tmpFile.Name()) - node, err := New(Config{ - LogFile: tmpFile.Name(), - MetricsEnabled: false, + node := newCodexNode(t, Config{ + LogLevel: "INFO", + LogFile: tmpFile.Name(), + LogFormat: LogFormatNoColors, }) - if err != nil { - t.Fatalf("Failed to create Codex node: %v", err) - } - - t.Cleanup(func() { - if err := node.Stop(); err != nil { - t.Logf("cleanup codex: %v", err) - } - - if err := node.Destroy(); err != nil { - t.Logf("cleanup codex: %v", err) - } - }) - - if err := node.Start(); err != nil { - t.Fatalf("Failed to start Codex node: %v", err) - } content, err := os.ReadFile(tmpFile.Name()) + if err != nil { t.Fatalf("Failed to read log file: %v", err) } - if !strings.Contains(string(content), "Started codex node") { - t.Errorf("Log file does not contain 'Started codex node' %s", string(content)) - } - - if err := node.Stop(); err != nil { - t.Fatalf("Failed to stop Codex node: %v", err) + if !strings.Contains(string(content), "INF") { + t.Errorf("Log file does not contain INFO statement %s", string(content)) } err = node.UpdateLogLevel("ERROR") @@ -71,6 +52,11 @@ func TestUpdateLogLevel(t *testing.T) { t.Fatalf("UpdateLogLevel call failed: %v", err) } + if err := node.Stop(); err != nil { + t.Fatalf("Failed to stop Codex node: %v", err) + } + + // Clear the file if err := os.WriteFile(tmpFile.Name(), []byte{}, 0644); err != nil { t.Fatalf("Failed to clear log file: %v", err) } @@ -85,8 +71,8 @@ func TestUpdateLogLevel(t *testing.T) { t.Fatalf("Failed to read log file: %v", err) } - if strings.Contains(string(content), "Starting discovery node") { - t.Errorf("Log file contains 'Starting discovery node'") + if strings.Contains(string(content), "INF") { + t.Errorf("Log file contains INFO statement after log level update: %s", string(content)) } } diff --git a/codex/download_test.go b/codex/download_test.go index c680498..edfc2a5 100644 --- a/codex/download_test.go +++ b/codex/download_test.go @@ -87,7 +87,7 @@ func TestDownloadStreamWithAutosize(t *testing.T) { } func TestDownloadStreamWithNotExisting(t *testing.T) { - codex := newCodexNode(t, withBlockRetries(1)) + codex := newCodexNode(t, Config{BlockRetries: 1}) opt := DownloadStreamOptions{} if err := codex.DownloadStream(context.Background(), "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku", opt); err == nil { @@ -159,7 +159,7 @@ func TestDownloadManifest(t *testing.T) { } func TestDownloadManifestWithNotExistingCid(t *testing.T) { - codex := newCodexNode(t, withBlockRetries(1)) + codex := newCodexNode(t, Config{BlockRetries: 1}) manifest, err := codex.DownloadManifest("bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku") if err == nil { @@ -172,7 +172,7 @@ func TestDownloadManifestWithNotExistingCid(t *testing.T) { } func TestDownloadInitWithNotExistingCid(t *testing.T) { - codex := newCodexNode(t, withBlockRetries(1)) + codex := newCodexNode(t, Config{BlockRetries: 1}) if err := codex.DownloadInit("bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku", DownloadInitOptions{}); err == nil { t.Fatal("expected error when initializing download for non-existent cid") diff --git a/codex/storage_test.go b/codex/storage_test.go index f002bb2..bca6381 100644 --- a/codex/storage_test.go +++ b/codex/storage_test.go @@ -84,7 +84,7 @@ func TestFetch(t *testing.T) { } func TestFetchCidDoesNotExist(t *testing.T) { - codex := newCodexNode(t, withBlockRetries(1)) + codex := newCodexNode(t, Config{BlockRetries: 1}) _, err := codex.Fetch("bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku") if err == nil { diff --git a/codex/testutil.go b/codex/testutil.go index c02e54f..7521be4 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -6,52 +6,50 @@ import ( "testing" ) -type codexNodeTestOption func(*codexNodeTestOptions) +func defaultConfigHelper(t *testing.T) Config { + t.Helper() -type codexNodeTestOptions struct { - noStart bool - blockRetries int -} - -func withNoStart() codexNodeTestOption { - return func(o *codexNodeTestOptions) { o.noStart = true } -} - -func withBlockRetries(n int) codexNodeTestOption { - return func(o *codexNodeTestOptions) { o.blockRetries = n } -} - -func newCodexNode(t *testing.T, opts ...codexNodeTestOption) *CodexNode { - o := codexNodeTestOptions{ - blockRetries: 3000, - } - for _, opt := range opts { - opt(&o) - } - - node, err := New(Config{ + return Config{ DataDir: t.TempDir(), LogFormat: LogFormatNoColors, MetricsEnabled: false, - BlockRetries: o.blockRetries, + BlockRetries: 3000, LogLevel: "ERROR", - }) + } +} + +func newCodexNode(t *testing.T, opts ...Config) *CodexNode { + config := defaultConfigHelper(t) + + if len(opts) > 0 { + c := opts[0] + + if c.BlockRetries > 0 { + config.BlockRetries = c.BlockRetries + } + + if c.LogLevel != "" { + config.LogLevel = c.LogLevel + } + + if c.LogFile != "" { + config.LogFile = c.LogFile + } + } + + node, err := New(config) if err != nil { t.Fatalf("Failed to create Codex node: %v", err) } - if !o.noStart { - err = node.Start() - if err != nil { - t.Fatalf("Failed to start Codex node: %v", err) - } + err = node.Start() + if err != nil { + t.Fatalf("Failed to start Codex node: %v", err) } t.Cleanup(func() { - if !o.noStart { - if err := node.Stop(); err != nil { - t.Logf("cleanup codex: %v", err) - } + if err := node.Stop(); err != nil { + t.Logf("cleanup codex: %v", err) } if err := node.Destroy(); err != nil { From 0c19d39d8c0390c865f7f736283c984f19986895 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Oct 2025 15:13:08 +0200 Subject: [PATCH 21/26] Set LogLevel as string and do not wait for bridge when destroying because it is a sync call --- codex/codex.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/codex/codex.go b/codex/codex.go index 78e8a2b..24495b4 100644 --- a/codex/codex.go +++ b/codex/codex.go @@ -92,7 +92,7 @@ const ( type Config struct { // Default: INFO - LogLevel LogLevel `json:"log-level,omitempty"` + LogLevel string `json:"log-level,omitempty"` // Specifies what kind of logs should be written to stdout // Default: auto @@ -280,8 +280,12 @@ func (node CodexNode) Destroy() error { return bridge.callError("cGoCodexDestroy") } - _, err = bridge.wait() - return err + // We don't wait for the bridge here. + // The destroy function does not call the worker thread, + // it destroys the context directly and return the return + // value synchronously. + + return nil } // Version returns the version of the Codex node. From 25931bdbf06dfc0d4294da2d037fe35489347c33 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 27 Oct 2025 10:54:34 +0100 Subject: [PATCH 22/26] Increase test timeout --- .vscode/settings.json | 3 ++- Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c7de4a..7cbc647 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "CGO_CFLAGS": "-I${workspaceFolder}/vendor/nim-codex/library", "CGO_LDFLAGS": "-L${workspaceFolder}/vendor/nim-codex/build -lcodex -Wl,-rpath,${workspaceFolder}/vendor/nim-codex/build", "LD_LIBRARY_PATH": "${workspaceFolder}/vendor/nim-codex/build:${env:LD_LIBRARY_PATH}" - } + }, + "go.testTimeout": "2m" } \ No newline at end of file diff --git a/Makefile b/Makefile index 2f1f378..d7ddf34 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ build: test: @echo "Running tests..." - CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test ./... + CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOTESTFLAGS="-timeout=2m" go test ./... clean: @echo "Cleaning up..." From f184f0db8d43d019bd69c1e5e1e870a0484266a8 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 27 Oct 2025 10:54:44 +0100 Subject: [PATCH 23/26] Bump nim codex --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index d50206f..2a0aafa 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit d50206fd452fcb7d4a29012954504d74241819d2 +Subproject commit 2a0aafa29cb0147316e57723c1de17d9da9a05ba From 92d0245bc6f244c2f526afd87bb39b644b04761b Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 27 Oct 2025 10:56:32 +0100 Subject: [PATCH 24/26] Small test refactor --- codex/debug_test.go | 73 ++++++--------------------------------------- codex/p2p_test.go | 68 +++-------------------------------------- codex/testutil.go | 8 +++++ 3 files changed, 21 insertions(+), 128 deletions(-) diff --git a/codex/debug_test.go b/codex/debug_test.go index ba4f7b4..b0cc52d 100644 --- a/codex/debug_test.go +++ b/codex/debug_test.go @@ -80,50 +80,10 @@ func TestCodexPeerDebug(t *testing.T) { var bootstrap, node1, node2 *CodexNode var err error - t.Cleanup(func() { - if bootstrap != nil { - if err := bootstrap.Stop(); err != nil { - t.Logf("cleanup bootstrap: %v", err) - } - - if err := bootstrap.Destroy(); err != nil { - t.Logf("cleanup bootstrap: %v", err) - } - } - if node1 != nil { - if err := node1.Stop(); err != nil { - t.Logf("cleanup node1: %v", err) - } - - if err := node1.Destroy(); err != nil { - t.Logf("cleanup node1: %v", err) - } - } - if node2 != nil { - if err := node2.Stop(); err != nil { - t.Logf("cleanup node2: %v", err) - } - - if err := node2.Destroy(); err != nil { - t.Logf("cleanup node2: %v", err) - } - } + bootstrap = newCodexNode(t, Config{ + DiscoveryPort: 8092, }) - bootstrap, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, - DiscoveryPort: 8092, - }) - if err != nil { - t.Fatalf("Failed to create bootstrap: %v", err) - } - - if err := bootstrap.Start(); err != nil { - t.Fatalf("Failed to start bootstrap: %v", err) - } - spr, err := bootstrap.Spr() if err != nil { t.Fatalf("Failed to get bootstrap spr: %v", err) @@ -131,35 +91,15 @@ func TestCodexPeerDebug(t *testing.T) { bootstrapNodes := []string{spr} - node1, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, + node1 = newCodexNode(t, Config{ DiscoveryPort: 8090, BootstrapNodes: bootstrapNodes, }) - if err != nil { - t.Fatalf("Failed to create codex: %v", err) - } - if err := node1.Start(); err != nil { - t.Fatalf("Failed to start codex: %v", err) - } - - node2, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, + node2 = newCodexNode(t, Config{ DiscoveryPort: 8091, BootstrapNodes: bootstrapNodes, }) - if err != nil { - t.Fatalf("Failed to create codex2: %v", err) - } - - if err := node2.Start(); err != nil { - t.Fatalf("Failed to start codex2: %v", err) - } peerId, err := node2.PeerId() if err != nil { @@ -172,9 +112,14 @@ func TestCodexPeerDebug(t *testing.T) { if err == nil { break } + time.Sleep(1 * time.Second) } + if err != nil { + t.Fatalf("CodexPeerDebug call failed: %v", err) + } + if record.PeerId == "" { t.Fatalf("CodexPeerDebug call failed: %v", err) } diff --git a/codex/p2p_test.go b/codex/p2p_test.go index f7d0e64..44106a9 100644 --- a/codex/p2p_test.go +++ b/codex/p2p_test.go @@ -73,50 +73,10 @@ func TestCodexWithPeerId(t *testing.T) { var bootstrap, node1, node2 *CodexNode var err error - t.Cleanup(func() { - if bootstrap != nil { - if err := bootstrap.Stop(); err != nil { - t.Logf("cleanup bootstrap: %v", err) - } - - if err := bootstrap.Destroy(); err != nil { - t.Logf("cleanup bootstrap: %v", err) - } - } - if node1 != nil { - if err := node1.Stop(); err != nil { - t.Logf("cleanup node1: %v", err) - } - - if err := node1.Destroy(); err != nil { - t.Logf("cleanup node1: %v", err) - } - } - if node2 != nil { - if err := node2.Stop(); err != nil { - t.Logf("cleanup node2: %v", err) - } - - if err := node2.Destroy(); err != nil { - t.Logf("cleanup node2: %v", err) - } - } + bootstrap = newCodexNode(t, Config{ + DiscoveryPort: 8092, }) - bootstrap, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, - DiscoveryPort: 8092, - }) - if err != nil { - t.Fatalf("Failed to create bootstrap: %v", err) - } - - if err := bootstrap.Start(); err != nil { - t.Fatalf("Failed to start bootstrap: %v", err) - } - spr, err := bootstrap.Spr() if err != nil { t.Fatalf("Failed to get bootstrap spr: %v", err) @@ -124,35 +84,15 @@ func TestCodexWithPeerId(t *testing.T) { bootstrapNodes := []string{spr} - node1, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, + node1 = newCodexNode(t, Config{ DiscoveryPort: 8090, BootstrapNodes: bootstrapNodes, }) - if err != nil { - t.Fatalf("Failed to create codex: %v", err) - } - if err := node1.Start(); err != nil { - t.Fatalf("Failed to start codex: %v", err) - } - - node2, err = New(Config{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, - MetricsEnabled: false, + node2 = newCodexNode(t, Config{ DiscoveryPort: 8091, BootstrapNodes: bootstrapNodes, }) - if err != nil { - t.Fatalf("Failed to create codex2: %v", err) - } - - if err := node2.Start(); err != nil { - t.Fatalf("Failed to start codex2: %v", err) - } peerId, err := node2.PeerId() if err != nil { diff --git a/codex/testutil.go b/codex/testutil.go index 7521be4..274495b 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -35,6 +35,14 @@ func newCodexNode(t *testing.T, opts ...Config) *CodexNode { if c.LogFile != "" { config.LogFile = c.LogFile } + + if len(c.BootstrapNodes) != 0 { + config.BootstrapNodes = c.BootstrapNodes + } + + if c.DiscoveryPort != 0 { + config.DiscoveryPort = c.DiscoveryPort + } } node, err := New(config) From 621103bedba83360bcfa4361fc920eff8baf160a Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 27 Oct 2025 10:57:05 +0100 Subject: [PATCH 25/26] Ignore bin --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dbe5894..d05311a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,7 @@ nimcache # Test files codex/testdata/hello.downloaded.txt -codex/testdata/hello.downloaded.writer.txt \ No newline at end of file +codex/testdata/hello.downloaded.writer.txt + +# Bin +codex-go \ No newline at end of file From e02490588bc07cfea1166f4654f1c3c606d9ba20 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 30 Oct 2025 06:09:32 +0100 Subject: [PATCH 26/26] Bump nim-codex version --- vendor/nim-codex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nim-codex b/vendor/nim-codex index 2a0aafa..a86d858 160000 --- a/vendor/nim-codex +++ b/vendor/nim-codex @@ -1 +1 @@ -Subproject commit 2a0aafa29cb0147316e57723c1de17d9da9a05ba +Subproject commit a86d8586456d5eb6805b228e80e264ee736a6a90