package main /* #cgo LDFLAGS: -L../../build/ -lcodex #cgo LDFLAGS: -L../../ -Wl,-rpath,../../ #include "../../library/libcodex.h" #include #include #include void libcodexNimMain(void); static void codex_host_init_once(void){ static int done; if (!__atomic_exchange_n(&done, 1, __ATOMIC_SEQ_CST)) libcodexNimMain(); } extern void globalEventCallback(int ret, char* msg, size_t len, void* userData); typedef struct { int ret; char* msg; size_t len; uintptr_t h; } Resp; static void* allocResp(uintptr_t h) { Resp* r = (Resp*)calloc(1, sizeof(Resp)); r->h = h; return r; } static void freeResp(void* resp) { if (resp != NULL) { free(resp); } } static int getRet(void* resp) { if (resp == NULL) { return 0; } Resp* m = (Resp*) resp; return m->ret; } static char* getMyCharPtr(void* resp) { if (resp == NULL) { return NULL; } Resp* m = (Resp*) resp; return m->msg; } static size_t getMyCharLen(void* resp) { if (resp == NULL) { return 0; } Resp* m = (Resp*) resp; return m->len; } // resp must be set != NULL in case interest on retrieving data from the callback void callback(int ret, char* msg, size_t len, void* resp); static void* cGoCodexNew(const char* configJson, void* resp) { void* ret = codex_new(configJson, (CodexCallback) callback, resp); return ret; } static int cGoCodexVersion(void* codexCtx, void* resp) { return codex_version(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexRevision(void* codexCtx, void* resp) { return codex_revision(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexRepo(void* codexCtx, void* resp) { return codex_repo(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexDebug(void* codexCtx, void* resp) { return codex_debug(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexSpr(void* codexCtx, void* resp) { return codex_spr(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexPeerId(void* codexCtx, void* resp) { return codex_peer_id(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexLogLevel(void* codexCtx, char* logLevel, void* resp) { return codex_log_level(codexCtx, logLevel, (CodexCallback) callback, resp); } static int cGoCodexConnect(void* codexCtx, char* peerId, const char** peerAddresses, uintptr_t peerAddressesSize, void* resp) { return codex_connect(codexCtx, peerId, peerAddresses, peerAddressesSize, (CodexCallback) callback, resp); } static int cGoCodexPeerDebug(void* codexCtx, char* peerId, void* resp) { return codex_peer_debug(codexCtx, peerId, (CodexCallback) callback, resp); } static int cGoCodexUploadInit(void* codexCtx, char* filepath, size_t chunkSize, void* resp) { return codex_upload_init(codexCtx, filepath, chunkSize, (CodexCallback) callback, resp); } static int cGoCodexUploadChunk(void* codexCtx, char* sessionId, const uint8_t* chunk, size_t len, void* resp) { return codex_upload_chunk(codexCtx, sessionId, chunk, len, (CodexCallback) callback, resp); } static int cGoCodexUploadFinalize(void* codexCtx, char* sessionId, void* resp) { return codex_upload_finalize(codexCtx, sessionId, (CodexCallback) callback, resp); } static int cGoCodexUploadCancel(void* codexCtx, char* sessionId, void* resp) { return codex_upload_cancel(codexCtx, sessionId, (CodexCallback) callback, resp); } static int cGoCodexUploadFile(void* codexCtx, char* sessionId, void* resp) { return codex_upload_file(codexCtx, sessionId, (CodexCallback) callback, resp); } static int cGoCodexDownloadInit(void* codexCtx, char* cid, size_t chunkSize, bool local, void* resp) { return codex_download_init(codexCtx, cid, chunkSize, local, (CodexCallback) callback, resp); } static int cGoCodexDownloadChunk(void* codexCtx, char* cid, void* resp) { return codex_download_chunk(codexCtx, cid, (CodexCallback) callback, resp); } static int cGoCodexDownloadLocal(void* codexCtx, char* cid, size_t chunkSize, void* resp) { return codex_download_local(codexCtx, cid, chunkSize, (CodexCallback) callback, resp); } static int cGoCodexDownloadCancel(void* codexCtx, char* cid, void* resp) { return codex_download_cancel(codexCtx, cid, (CodexCallback) callback, resp); } static int cGoCodexStart(void* codexCtx, void* resp) { return codex_start(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexStop(void* codexCtx, void* resp) { return codex_stop(codexCtx, (CodexCallback) callback, resp); } static int cGoCodexDestroy(void* codexCtx, void* resp) { return codex_destroy(codexCtx, (CodexCallback) callback, resp); } static void cGoCodexSetEventCallback(void* codexCtx) { // The 'globalEventCallback' Go function is shared amongst all possible Codex instances. // Given that the 'globalEventCallback' is shared, we pass again the // codexCtx instance but in this case is needed to pick up the correct method // that will handle the event. // In other words, for every call the libcodex makes to globalEventCallback, // the 'userData' parameter will bring the context of the node that registered // that globalEventCallback. // This technique is needed because cgo only allows to export Go functions and not methods. codex_set_event_callback(codexCtx, (CodexCallback) globalEventCallback, codexCtx); } */ import "C" import ( "bytes" "encoding/json" "errors" "fmt" "io" "log" "os" "os/signal" "path" "runtime/cgo" "sync" "syscall" "unsafe" ) type LogLevel string const ( Trace LogLevel = "TRACE" Debug LogLevel = "DEBUG" Info LogLevel = "INFO" Notice LogLevel = "NOTICE" Warn LogLevel = "WARN" Error LogLevel = "ERROR" Fatal LogLevel = "FATAL" ) type LogFormat string const ( LogFormatAuto LogFormat = "auto" LogFormatColors LogFormat = "colors" LogFormatNoColors LogFormat = "nocolors" LogFormatJSON LogFormat = "json" ) type RepoKind string const ( FS RepoKind = "fs" SQLite RepoKind = "sqlite" LevelDb RepoKind = "leveldb" ) type CodexConfig struct { LogFormat LogFormat `json:"log-format,omitempty"` MetricsEnabled bool `json:"metrics,omitempty"` MetricsAddress string `json:"metrics-address,omitempty"` DataDir string `json:"data-dir,omitempty"` ListenAddrs []string `json:"listen-addrs,omitempty"` Nat string `json:"nat,omitempty"` DiscoveryPort int `json:"disc-port,omitempty"` NetPrivKeyFile string `json:"net-privkey,omitempty"` BootstrapNodes []byte `json:"bootstrap-node,omitempty"` MaxPeers int `json:"max-peers,omitempty"` NumThreads int `json:"num-threads,omitempty"` AgentString string `json:"agent-string,omitempty"` RepoKind RepoKind `json:"repo-kind,omitempty"` StorageQuota int `json:"storage-quota,omitempty"` BlockTtl int `json:"block-ttl,omitempty"` BlockMaintenanceInterval int `json:"block-mi,omitempty"` BlockMaintenanceNumberOfBlocks int `json:"block-mn,omitempty"` CacheSize int `json:"cache-size,omitempty"` LogFile string `json:"log-file,omitempty"` } type RestPeerRecord struct { PeerId string `json:"peerId"` SeqNo int `json:"seqNo"` Addresses []string `json:"addresses,omitempty"` } type RestNode struct { NodeId string `json:"nodeId"` PeerId string `json:"peerId"` Record string `json:"record"` Address *string `json:"address"` Seen bool `json:"seen"` } type RestRoutingTable struct { LocalNode RestNode `json:"localNode"` Nodes []RestNode `json:"nodes"` } type CodexDebugInfo struct { ID string `json:"id"` Addrs []string `json:"addrs"` Spr string `json:"spr"` AnnounceAddresses []string `json:"announceAddresses"` Table RestRoutingTable `json:"table"` } type CodexNode struct { ctx unsafe.Pointer } const defaultBlockSize = 1024 * 64 type OnProgressFunc func(read, total int, percent float64, err error) type CodexUploadOptions struct { filepath string chunkSize int onProgress OnProgressFunc } type bridgeCtx struct { wg *sync.WaitGroup h cgo.Handle resp unsafe.Pointer result string err error // Callback used for upload and download onProgress func(read int, chunk []byte) } func newBridgeCtx() *bridgeCtx { bridge := &bridgeCtx{} bridge.wg = &sync.WaitGroup{} bridge.wg.Add(1) bridge.h = cgo.NewHandle(bridge) bridge.resp = C.allocResp(C.uintptr_t(uintptr(bridge.h))) return bridge } func (b *bridgeCtx) free() { if b.h > 0 { b.h.Delete() b.h = 0 } if b.resp != nil { C.freeResp(b.resp) b.resp = nil } } func (b *bridgeCtx) CallError(name string) error { return fmt.Errorf("Failed the call to %s. Returned code: %d.", name, C.getRet(b.resp)) } func (b *bridgeCtx) wait() (string, error) { b.wg.Wait() return b.result, b.err } func getReaderSize(r io.Reader) int64 { switch v := r.(type) { case *os.File: stat, err := v.Stat() if err != nil { return 0 } return stat.Size() case *bytes.Buffer: return int64(v.Len()) default: return 0 } } //export callback func callback(ret C.int, msg *C.char, len C.size_t, resp unsafe.Pointer) { if resp == nil { return } m := (*C.Resp)(resp) m.ret = ret m.msg = msg m.len = len if m.h == 0 { return } h := cgo.Handle(m.h) if h == 0 { return } if v, ok := h.Value().(*bridgeCtx); ok { switch ret { case C.RET_PROGRESS: if v.onProgress == nil { return } if msg != nil { chunk := C.GoBytes(unsafe.Pointer(msg), C.int(len)) v.onProgress(int(C.int(len)), chunk) } else { v.onProgress(int(C.int(len)), nil) } case C.RET_OK: retMsg := C.GoStringN(msg, C.int(len)) v.result = retMsg v.err = nil if v.wg != nil { v.wg.Done() } case C.RET_ERR: retMsg := C.GoStringN(msg, C.int(len)) v.err = errors.New(retMsg) if v.wg != nil { v.wg.Done() } } } } func CodexNew(config CodexConfig) (*CodexNode, error) { bridge := newBridgeCtx() defer bridge.free() jsonConfig, err := json.Marshal(config) if err != nil { return nil, err } cJsonConfig := C.CString(string(jsonConfig)) defer C.free(unsafe.Pointer(cJsonConfig)) ctx := C.cGoCodexNew(cJsonConfig, bridge.resp) if _, err := bridge.wait(); err != nil { return nil, bridge.err } return &CodexNode{ctx: ctx}, bridge.err } func (self CodexNode) CodexVersion() (string, error) { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexVersion(self.ctx, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexVersion") } return bridge.wait() } func (self CodexNode) CodexRevision() (string, error) { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexRevision(self.ctx, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexRevision") } return bridge.wait() } func (self CodexNode) CodexRepo() (string, error) { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexRepo(self.ctx, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexRepo") } return bridge.wait() } func (self CodexNode) CodexDebug() (CodexDebugInfo, error) { var info CodexDebugInfo bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexDebug(self.ctx, bridge.resp) != C.RET_OK { return info, bridge.CallError("cGoCodexDebug") } value, err := bridge.wait() if err != nil { return info, err } err = json.Unmarshal([]byte(value), &info) return info, err } func (self CodexNode) CodexSpr() (string, error) { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexSpr(self.ctx, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexSpr") } return bridge.wait() } func (self CodexNode) CodexPeerId() (string, error) { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexPeerId(self.ctx, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexPeerId") } return bridge.wait() } func (self CodexNode) CodexLogLevel(logLevel LogLevel) error { bridge := newBridgeCtx() defer bridge.free() var cLogLevel = C.CString(fmt.Sprintf("%s", logLevel)) defer C.free(unsafe.Pointer(cLogLevel)) if C.cGoCodexLogLevel(self.ctx, cLogLevel, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexLogLevel") } _, err := bridge.wait() return err } func (self CodexNode) CodexConnect(peerId string, peerAddresses []string) error { bridge := newBridgeCtx() defer bridge.free() var cPeerId = C.CString(peerId) defer C.free(unsafe.Pointer(cPeerId)) if len(peerAddresses) > 0 { var cAddresses = make([]*C.char, len(peerAddresses)) for i, addr := range peerAddresses { cAddresses[i] = C.CString(addr) defer C.free(unsafe.Pointer(cAddresses[i])) } if C.cGoCodexConnect(self.ctx, cPeerId, &cAddresses[0], C.uintptr_t(len(peerAddresses)), bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexConnect") } } else { if C.cGoCodexConnect(self.ctx, cPeerId, nil, 0, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexConnect") } } _, err := bridge.wait() return err } func (self CodexNode) CodexPeerDebug(peerId string) (RestPeerRecord, error) { var record RestPeerRecord bridge := newBridgeCtx() defer bridge.free() var cPeerId = C.CString(peerId) defer C.free(unsafe.Pointer(cPeerId)) if C.cGoCodexPeerDebug(self.ctx, cPeerId, bridge.resp) != C.RET_OK { return record, bridge.CallError("cGoCodexPeerDebug") } value, err := bridge.wait() if err != nil { return record, err } err = json.Unmarshal([]byte(value), &record) return record, err } func (self CodexNode) CodexUploadInit(options *CodexUploadOptions) (string, error) { bridge := newBridgeCtx() defer bridge.free() var cFilename = C.CString(options.filepath) defer C.free(unsafe.Pointer(cFilename)) if options.chunkSize == 0 { options.chunkSize = defaultBlockSize } var cChunkSize = C.size_t(options.chunkSize) if C.cGoCodexUploadInit(self.ctx, cFilename, cChunkSize, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexUploadInit") } return bridge.wait() } func (self CodexNode) CodexUploadChunk(sessionId string, chunk []byte) error { bridge := newBridgeCtx() defer bridge.free() var cSessionId = C.CString(sessionId) defer C.free(unsafe.Pointer(cSessionId)) var cChunkPtr *C.uint8_t if len(chunk) > 0 { cChunkPtr = (*C.uint8_t)(unsafe.Pointer(&chunk[0])) } if C.cGoCodexUploadChunk(self.ctx, cSessionId, cChunkPtr, C.size_t(len(chunk)), bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexUploadChunk") } _, err := bridge.wait() return err } func (self CodexNode) CodexUploadFinalize(sessionId string) (string, error) { bridge := newBridgeCtx() defer bridge.free() var cSessionId = C.CString(sessionId) defer C.free(unsafe.Pointer(cSessionId)) if C.cGoCodexUploadFinalize(self.ctx, cSessionId, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexUploadFinalize") } return bridge.wait() } func (self CodexNode) CodexUploadCancel(sessionId string) error { bridge := newBridgeCtx() defer bridge.free() var cSessionId = C.CString(sessionId) defer C.free(unsafe.Pointer(cSessionId)) if C.cGoCodexUploadCancel(self.ctx, cSessionId, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexUploadCancel") } _, err := bridge.wait() return err } func (self CodexNode) CodexUploadReader(options CodexUploadOptions, r io.Reader) (string, error) { sessionId, err := self.CodexUploadInit(&options) if err != nil { return "", err } if options.chunkSize == 0 { options.chunkSize = defaultBlockSize } buf := make([]byte, options.chunkSize) total := 0 var size int64 if options.onProgress != nil { size = getReaderSize(r) } for { n, err := r.Read(buf) if err == io.EOF { break } if err != nil { if cancelErr := self.CodexUploadCancel(sessionId); cancelErr != nil { return "", fmt.Errorf("failed to upload chunk %v and failed to cancel upload session %v", err, cancelErr) } return "", err } if n == 0 { break } if err := self.CodexUploadChunk(sessionId, buf[:n]); err != nil { if cancelErr := self.CodexUploadCancel(sessionId); cancelErr != nil { return "", fmt.Errorf("failed to upload chunk %v and failed to cancel upload session %v", err, cancelErr) } return "", err } total += n if options.onProgress != nil && size > 0 { percent := float64(total) / float64(size) * 100.0 // The last block could be a bit over the size due to padding // on the chunk size. if percent > 100.0 { percent = 100.0 } options.onProgress(n, total, percent, nil) } } return self.CodexUploadFinalize(sessionId) } func (self CodexNode) CodexUploadReaderAsync(options CodexUploadOptions, r io.Reader, onDone func(cid string, err error)) { go func() { cid, err := self.CodexUploadReader(options, r) onDone(cid, err) }() } func (self CodexNode) CodexUploadFile(options CodexUploadOptions) (string, error) { bridge := newBridgeCtx() defer bridge.free() if options.onProgress != nil { stat, err := os.Stat(options.filepath) if err != nil { return "", err } size := stat.Size() total := 0 if size > 0 { bridge.onProgress = func(read int, _ []byte) { if read == 0 { return } total += read percent := float64(total) / float64(size) * 100.0 // The last block could be a bit over the size due to padding // on the chunk size. if percent > 100.0 { percent = 100.0 } options.onProgress(read, int(size), percent, nil) } } } sessionId, err := self.CodexUploadInit(&options) if err != nil { return "", err } var cSessionId = C.CString(sessionId) defer C.free(unsafe.Pointer(cSessionId)) if C.cGoCodexUploadFile(self.ctx, cSessionId, bridge.resp) != C.RET_OK { return "", bridge.CallError("cGoCodexUploadFile") } return bridge.wait() } func (self CodexNode) CodexUploadFileAsync(options CodexUploadOptions, onDone func(cid string, err error)) { go func() { cid, err := self.CodexUploadFile(options) onDone(cid, err) }() } func (self CodexNode) CodexDownloadLocal(cid string, chunkSize int, w io.Writer) error { bridge := newBridgeCtx() defer bridge.free() bridge.onProgress = func(read int, chunk []byte) { if read == 0 { return } if _, err := w.Write(chunk); err != nil { log.Println(err) } } var cCid = C.CString(cid) defer C.free(unsafe.Pointer(cCid)) if chunkSize == 0 { chunkSize = defaultBlockSize } var cChunkSize = C.size_t(chunkSize) if C.cGoCodexDownloadLocal(self.ctx, cCid, cChunkSize, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexDownloadLocal") } _, err := bridge.wait() return err } func (self CodexNode) CodexDownloadLocalAsync(cid string, chunkSize int, w io.Writer, onDone func(error)) { go func() { err := self.CodexDownloadLocal(cid, chunkSize, w) onDone(err) }() } func (self CodexNode) CodexDownloadInit(cid string, chunkSize int, local bool) error { bridge := newBridgeCtx() defer bridge.free() var cCid = C.CString(cid) defer C.free(unsafe.Pointer(cCid)) if chunkSize == 0 { chunkSize = defaultBlockSize } var cChunkSize = C.size_t(chunkSize) var cLocal = C.bool(local) if C.cGoCodexDownloadInit(self.ctx, cCid, cChunkSize, cLocal, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexDownloadInit") } _, err := bridge.wait() return err } func (self CodexNode) CodexDownloadChunk(cid string) ([]byte, error) { bridge := newBridgeCtx() defer bridge.free() var bytes []byte bridge.onProgress = func(read int, chunk []byte) { bytes = chunk } var cCid = C.CString(cid) defer C.free(unsafe.Pointer(cCid)) if C.cGoCodexDownloadChunk(self.ctx, cCid, bridge.resp) != C.RET_OK { return nil, bridge.CallError("cGoCodexDownloadChunk") } if _, err := bridge.wait(); err != nil { return nil, err } return bytes, nil } func (self CodexNode) CodexDownloadCancel(cid string) error { bridge := newBridgeCtx() defer bridge.free() var cCid = C.CString(cid) defer C.free(unsafe.Pointer(cCid)) if C.cGoCodexDownloadCancel(self.ctx, cCid, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexDownloadCancel") } _, err := bridge.wait() return err } func (self CodexNode) CodexStart() error { bridge := newBridgeCtx() defer bridge.free() if C.cGoCodexStart(self.ctx, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexStart") } _, err := bridge.wait() return err } func (self CodexNode) CodexStartAsync(onDone func(error)) { go func() { err := self.CodexStart() onDone(err) }() } func (self CodexNode) CodexStop() error { bridge := newBridgeCtx() if C.cGoCodexStop(self.ctx, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexStop") } _, err := bridge.wait() return err } func (self CodexNode) CodexDestroy() error { bridge := newBridgeCtx() if C.cGoCodexDestroy(self.ctx, bridge.resp) != C.RET_OK { return bridge.CallError("cGoCodexDestroy") } _, err := bridge.wait() return err } //export globalEventCallback func globalEventCallback(callerRet C.int, msg *C.char, len C.size_t, userData unsafe.Pointer) { // This is shared among all Golang instances self := CodexNode{ctx: userData} self.MyEventCallback(callerRet, msg, len) } func (self CodexNode) MyEventCallback(callerRet C.int, msg *C.char, len C.size_t) { log.Println("Event received:", C.GoStringN(msg, C.int(len))) } func (self CodexNode) CodexSetEventCallback() { // Notice that the events for self node are handled by the 'MyEventCallback' method C.cGoCodexSetEventCallback(self.ctx) } func main() { config := CodexConfig{} node, err := CodexNew(config) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex created.") node.CodexSetEventCallback() version, err := node.CodexVersion() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex version:", version) revision, err := node.CodexRevision() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex revision:", revision) repo, err := node.CodexRepo() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex repo:", repo) log.Println("Starting Codex...") err = node.CodexStart() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex started...") // for i := 0; i < 150; i++ { debug, err := node.CodexDebug() if err != nil { log.Fatal("Error happened:", err.Error()) } pretty, err := json.MarshalIndent(debug, "", " ") if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println(string(pretty)) spr, err := node.CodexSpr() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex SPR:", spr) peerId, err := node.CodexPeerId() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Peer Id:", peerId) err = node.CodexLogLevel(Trace) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Log Level set to TRACE") sessionId, err := node.CodexUploadInit(&CodexUploadOptions{filepath: "hello.txt"}) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Upload Init sessionId:", sessionId) err = node.CodexUploadChunk(sessionId, []byte("Hello ")) if err != nil { log.Fatal("Error happened:", err.Error()) } err = node.CodexUploadChunk(sessionId, []byte("World!")) if err != nil { log.Fatal("Error happened:", err.Error()) } cid, err := node.CodexUploadFinalize(sessionId) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Upload Finalized, cid:", cid) buf := bytes.NewBuffer([]byte("Hello World!")) cid, err = node.CodexUploadReader(CodexUploadOptions{filepath: "hello.txt", onProgress: func(read, total int, percent float64, err error) { if err != nil { log.Fatal("Error happened during upload: %v\n", err) } log.Printf("Uploaded %d bytes, total %d bytes (%.2f%%)\n", read, total, percent) }}, buf) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Upload Finalized from reader, cid:", cid) current, err := os.Getwd() if err != nil { log.Fatal("Error happened:", err.Error()) } // Choose a big file to see the progress logs filepath := path.Join(current, "examples", "golang", "hello.txt") //filepath := path.Join(current, "examples", "golang", "discord-0.0.109.deb") options := CodexUploadOptions{filepath: filepath, onProgress: func(read, total int, percent float64, err error) { if err != nil { log.Fatal("Error happened during upload: %v\n", err) } log.Printf("Uploaded %d bytes, total %d bytes (%.2f%%)\n", read, total, percent) }} cid, err = node.CodexUploadFile(options) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Upload File finalized, cid: .", cid) f, err := os.Create("hello.loaded.txt") if err != nil { log.Fatal(err) } defer f.Close() // log.Println("Codex Download Local starting... attempt", i+1) if err := node.CodexDownloadLocal(cid, 0, f); err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Download Local finished.") // log.Println("Codex Download Init starting... attempt", i+1) if err := node.CodexDownloadInit(cid, 0, true); err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Download Init finished.") // log.Println("Codex Download Chunk starting... attempt", i+1) chunk, err := node.CodexDownloadChunk(cid) if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex Download Chunk finished. Size:", len(chunk)) // } // err = node.CodexConnect(peerId, []string{}) // if err != nil { // log.Fatal("Error happened:", err.Error()) // } // log.Println("Codex connecting to self...") // val, err := node.CodexPeerDebug(peerId) // if err != nil { // log.Fatal("Error happened:", err.Error()) // } // log.Println("Codex debugging self...", val) // Wait for a SIGINT or SIGTERM signal ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) <-ch log.Println("Stopping the node...") err = node.CodexStop() if err != nil { log.Fatal("Error happened:", err.Error()) } log.Println("Codex stopped...") log.Println("Destroying the node...") err = node.CodexDestroy() if err != nil { log.Fatal("Error happened:", err.Error()) } }