From be55b2499bd7f3ddd266c01e64ce7b66ae0c78a0 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 26 Mar 2026 14:31:25 +0000 Subject: [PATCH 1/2] chore(node): upgrade to core v0.8.0-alpha.1 Co-Authored-By: Virgil --- CLAUDE.md | 6 ++-- go.mod | 5 ++-- go.sum | 66 +++++++++++++++++++++++++++++++++++++++---- logging/logger.go | 4 +-- node/bundle.go | 57 ++++++++++++++++++------------------- node/controller.go | 30 ++++++++++---------- node/core_fs.go | 53 ++++++++++++++++++++++++++++++++++ node/dispatcher.go | 8 +++--- node/errors.go | 6 ++-- node/identity.go | 63 ++++++++++++++++++++--------------------- node/levin/header.go | 6 ++-- node/levin/storage.go | 26 ++++++++--------- node/levin/varint.go | 24 ++++++++-------- node/peer.go | 45 +++++++++++++++-------------- node/protocol.go | 8 +++--- node/transport.go | 36 +++++++++++------------ node/worker.go | 31 ++++++++++---------- ueps/packet.go | 13 ++++----- ueps/reader.go | 7 ++--- 19 files changed, 297 insertions(+), 197 deletions(-) create mode 100644 node/core_fs.go diff --git a/CLAUDE.md b/CLAUDE.md index 5f35b3c..27d11bb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project -`go-p2p` is the P2P networking layer for the Lethean network. Module path: `forge.lthn.ai/core/go-p2p` +`go-p2p` is the P2P networking layer for the Lethean network. Module path: `dappco.re/go/core/p2p` ## Prerequisites @@ -80,8 +80,8 @@ type ProfileManager interface { - Licence: EUPL-1.2 — new files need `// SPDX-License-Identifier: EUPL-1.2` - Security-first: do not weaken HMAC, challenge-response, Zip Slip defence, or rate limiting - Use `logging` package only — no `fmt.Println` or `log.Printf` in library code -- Error handling: use `coreerr.E()` from `go-log` — never `fmt.Errorf` or `errors.New` in library code -- File I/O: use `coreio.Local` from `go-io` — never `os.ReadFile`/`os.WriteFile` in library code (exception: `os.OpenFile` for streaming writes where `coreio` lacks support) +- Error handling: use `core.E()` from `dappco.re/go/core` — never `fmt.Errorf` or `errors.New` in library code +- File I/O: use `dappco.re/go/core` filesystem helpers (package-level adapters in `node/` backed by `core.Fs`) — never `os.ReadFile`/`os.WriteFile` in library code (exception: `os.OpenFile` for streaming writes where filesystem helpers cannot preserve tar header mode bits) - Hot-path debug logging uses sampling pattern: `if counter.Add(1)%interval == 0` ### Transport test helper diff --git a/go.mod b/go.mod index 003f271..e395272 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,7 @@ module dappco.re/go/core/p2p go 1.26.0 require ( - dappco.re/go/core/io v0.2.0 - dappco.re/go/core/log v0.1.0 + dappco.re/go/core v0.8.0-alpha.1 forge.lthn.ai/Snider/Borg v0.3.1 forge.lthn.ai/Snider/Poindexter v0.0.3 github.com/adrg/xdg v0.5.3 @@ -15,11 +14,11 @@ require ( require ( forge.lthn.ai/Snider/Enchantrix v0.0.4 // indirect - forge.lthn.ai/core/go-log v0.0.4 // indirect github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/klauspost/compress v1.18.4 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect golang.org/x/crypto v0.49.0 // indirect golang.org/x/sys v0.42.0 // indirect diff --git a/go.sum b/go.sum index d7916fc..e06c31a 100644 --- a/go.sum +++ b/go.sum @@ -1,45 +1,99 @@ -dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4= -dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E= -dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc= -dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs= +dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk= +dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= forge.lthn.ai/Snider/Borg v0.3.1 h1:gfC1ZTpLoZai07oOWJiVeQ8+qJYK8A795tgVGJHbVL8= forge.lthn.ai/Snider/Borg v0.3.1/go.mod h1:Z7DJD0yHXsxSyM7Mjl6/g4gH1NBsIz44Bf5AFlV76Wg= forge.lthn.ai/Snider/Enchantrix v0.0.4 h1:biwpix/bdedfyc0iVeK15awhhJKH6TEMYOTXzHXx5TI= forge.lthn.ai/Snider/Enchantrix v0.0.4/go.mod h1:OGCwuVeZPq3OPe2h6TX/ZbgEjHU6B7owpIBeXQGbSe0= forge.lthn.ai/Snider/Poindexter v0.0.3 h1:cx5wRhuLRKBM8riIZyNVAT2a8rwRhn1dodFBktocsVE= forge.lthn.ai/Snider/Poindexter v0.0.3/go.mod h1:ddzGia98k3HKkR0gl58IDzqz+MmgW2cQJOCNLfuWPpo= -forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0= -forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/clipperhouse/uax29/v2 v2.4.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= +github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= +github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw= +github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/wailsapp/go-webview2 v1.0.23/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= +github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= +github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logging/logger.go b/logging/logger.go index 4ab0666..d368b82 100644 --- a/logging/logger.go +++ b/logging/logger.go @@ -10,7 +10,7 @@ import ( "sync" "time" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // Level represents the severity of a log message. @@ -280,6 +280,6 @@ func ParseLevel(s string) (Level, error) { case "ERROR": return LevelError, nil default: - return LevelInfo, coreerr.E("logging.ParseLevel", "unknown log level: "+s, nil) + return LevelInfo, core.E("logging.ParseLevel", "unknown log level: "+s, nil) } } diff --git a/node/bundle.go b/node/bundle.go index 8c48f57..e60a659 100644 --- a/node/bundle.go +++ b/node/bundle.go @@ -11,8 +11,7 @@ import ( "path/filepath" "strings" - coreio "dappco.re/go/core/io" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "forge.lthn.ai/Snider/Borg/pkg/datanode" "forge.lthn.ai/Snider/Borg/pkg/tim" @@ -50,14 +49,14 @@ func CreateProfileBundle(profileJSON []byte, name string, password string) (*Bun // Create a TIM with just the profile config t, err := tim.New() if err != nil { - return nil, coreerr.E("CreateProfileBundle", "failed to create TIM", err) + return nil, core.E("CreateProfileBundle", "failed to create TIM", err) } t.Config = profileJSON // Encrypt to STIM format stimData, err := t.ToSigil(password) if err != nil { - return nil, coreerr.E("CreateProfileBundle", "failed to encrypt bundle", err) + return nil, core.E("CreateProfileBundle", "failed to encrypt bundle", err) } // Calculate checksum @@ -86,9 +85,9 @@ func CreateProfileBundleUnencrypted(profileJSON []byte, name string) (*Bundle, e // CreateMinerBundle creates an encrypted bundle containing a miner binary and optional profile. func CreateMinerBundle(minerPath string, profileJSON []byte, name string, password string) (*Bundle, error) { // Read miner binary - minerContent, err := coreio.Local.Read(minerPath) + minerContent, err := fsRead(minerPath) if err != nil { - return nil, coreerr.E("CreateMinerBundle", "failed to read miner binary", err) + return nil, core.E("CreateMinerBundle", "failed to read miner binary", err) } minerData := []byte(minerContent) @@ -97,19 +96,19 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo filepath.Base(minerPath): minerData, }) if err != nil { - return nil, coreerr.E("CreateMinerBundle", "failed to create tarball", err) + return nil, core.E("CreateMinerBundle", "failed to create tarball", err) } // Create DataNode from tarball dn, err := datanode.FromTar(tarData) if err != nil { - return nil, coreerr.E("CreateMinerBundle", "failed to create datanode", err) + return nil, core.E("CreateMinerBundle", "failed to create datanode", err) } // Create TIM from DataNode t, err := tim.FromDataNode(dn) if err != nil { - return nil, coreerr.E("CreateMinerBundle", "failed to create TIM", err) + return nil, core.E("CreateMinerBundle", "failed to create TIM", err) } // Set profile as config if provided @@ -120,7 +119,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo // Encrypt to STIM format stimData, err := t.ToSigil(password) if err != nil { - return nil, coreerr.E("CreateMinerBundle", "failed to encrypt bundle", err) + return nil, core.E("CreateMinerBundle", "failed to encrypt bundle", err) } checksum := calculateChecksum(stimData) @@ -137,7 +136,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { // Verify checksum first if calculateChecksum(bundle.Data) != bundle.Checksum { - return nil, coreerr.E("ExtractProfileBundle", "checksum mismatch - bundle may be corrupted", nil) + return nil, core.E("ExtractProfileBundle", "checksum mismatch - bundle may be corrupted", nil) } // If it's unencrypted JSON, just return it @@ -148,7 +147,7 @@ func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { // Decrypt STIM format t, err := tim.FromSigil(bundle.Data, password) if err != nil { - return nil, coreerr.E("ExtractProfileBundle", "failed to decrypt bundle", err) + return nil, core.E("ExtractProfileBundle", "failed to decrypt bundle", err) } return t.Config, nil @@ -158,25 +157,25 @@ func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { func ExtractMinerBundle(bundle *Bundle, password string, destDir string) (string, []byte, error) { // Verify checksum if calculateChecksum(bundle.Data) != bundle.Checksum { - return "", nil, coreerr.E("ExtractMinerBundle", "checksum mismatch - bundle may be corrupted", nil) + return "", nil, core.E("ExtractMinerBundle", "checksum mismatch - bundle may be corrupted", nil) } // Decrypt STIM format t, err := tim.FromSigil(bundle.Data, password) if err != nil { - return "", nil, coreerr.E("ExtractMinerBundle", "failed to decrypt bundle", err) + return "", nil, core.E("ExtractMinerBundle", "failed to decrypt bundle", err) } // Convert rootfs to tarball and extract tarData, err := t.RootFS.ToTar() if err != nil { - return "", nil, coreerr.E("ExtractMinerBundle", "failed to convert rootfs to tar", err) + return "", nil, core.E("ExtractMinerBundle", "failed to convert rootfs to tar", err) } // Extract tarball to destination minerPath, err := extractTarball(tarData, destDir) if err != nil { - return "", nil, coreerr.E("ExtractMinerBundle", "failed to extract tarball", err) + return "", nil, core.E("ExtractMinerBundle", "failed to extract tarball", err) } return minerPath, t.Config, nil @@ -256,11 +255,11 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Ensure destDir is an absolute, clean path for security checks absDestDir, err := filepath.Abs(destDir) if err != nil { - return "", coreerr.E("extractTarball", "failed to resolve destination directory", err) + return "", core.E("extractTarball", "failed to resolve destination directory", err) } absDestDir = filepath.Clean(absDestDir) - if err := coreio.Local.EnsureDir(absDestDir); err != nil { + if err := fsEnsureDir(absDestDir); err != nil { return "", err } @@ -281,12 +280,12 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Reject absolute paths if filepath.IsAbs(cleanName) { - return "", coreerr.E("extractTarball", "invalid tar entry: absolute path not allowed: "+hdr.Name, nil) + return "", core.E("extractTarball", "invalid tar entry: absolute path not allowed: "+hdr.Name, nil) } // Reject paths that escape the destination directory if strings.HasPrefix(cleanName, ".."+string(os.PathSeparator)) || cleanName == ".." { - return "", coreerr.E("extractTarball", "invalid tar entry: path traversal attempt: "+hdr.Name, nil) + return "", core.E("extractTarball", "invalid tar entry: path traversal attempt: "+hdr.Name, nil) } // Build the full path and verify it's within destDir @@ -295,26 +294,26 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Final security check: ensure the path is still within destDir if !strings.HasPrefix(fullPath, absDestDir+string(os.PathSeparator)) && fullPath != absDestDir { - return "", coreerr.E("extractTarball", "invalid tar entry: path escape attempt: "+hdr.Name, nil) + return "", core.E("extractTarball", "invalid tar entry: path escape attempt: "+hdr.Name, nil) } switch hdr.Typeflag { case tar.TypeDir: - if err := coreio.Local.EnsureDir(fullPath); err != nil { + if err := fsEnsureDir(fullPath); err != nil { return "", err } case tar.TypeReg: // Ensure parent directory exists - if err := coreio.Local.EnsureDir(filepath.Dir(fullPath)); err != nil { + if err := fsEnsureDir(filepath.Dir(fullPath)); err != nil { return "", err } - // os.OpenFile is used deliberately here instead of coreio.Local.Create/Write - // because coreio hardcodes file permissions (0644) and we need to preserve + // os.OpenFile is used deliberately here instead of core.Fs.Create/Write + // because the helper writes with fixed default permissions and we need to preserve // the tar header's mode bits — executable binaries require 0755. f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(hdr.Mode)) if err != nil { - return "", coreerr.E("extractTarball", "failed to create file "+hdr.Name, err) + return "", core.E("extractTarball", "failed to create file "+hdr.Name, err) } // Limit file size to prevent decompression bombs (100MB max per file) @@ -323,11 +322,11 @@ func extractTarball(tarData []byte, destDir string) (string, error) { written, err := io.Copy(f, limitedReader) f.Close() if err != nil { - return "", coreerr.E("extractTarball", "failed to write file "+hdr.Name, err) + return "", core.E("extractTarball", "failed to write file "+hdr.Name, err) } if written > maxFileSize { - coreio.Local.Delete(fullPath) - return "", coreerr.E("extractTarball", "file "+hdr.Name+" exceeds maximum size", nil) + fsDelete(fullPath) + return "", core.E("extractTarball", "file "+hdr.Name+" exceeds maximum size", nil) } // Track first executable diff --git a/node/controller.go b/node/controller.go index 224c4d6..a79bbfc 100644 --- a/node/controller.go +++ b/node/controller.go @@ -6,7 +6,7 @@ import ( "sync" "time" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "dappco.re/go/core/p2p/logging" ) @@ -67,11 +67,11 @@ func (c *Controller) sendRequest(peerID string, msg *Message, timeout time.Durat if c.transport.GetConnection(peerID) == nil { peer := c.peers.GetPeer(peerID) if peer == nil { - return nil, coreerr.E("Controller.sendRequest", "peer not found: "+peerID, nil) + return nil, core.E("Controller.sendRequest", "peer not found: "+peerID, nil) } conn, err := c.transport.Connect(peer) if err != nil { - return nil, coreerr.E("Controller.sendRequest", "failed to connect to peer", err) + return nil, core.E("Controller.sendRequest", "failed to connect to peer", err) } // Use the real peer ID after handshake (it may have changed) actualPeerID = conn.Peer.ID @@ -96,7 +96,7 @@ func (c *Controller) sendRequest(peerID string, msg *Message, timeout time.Durat // Send the message if err := c.transport.Send(actualPeerID, msg); err != nil { - return nil, coreerr.E("Controller.sendRequest", "failed to send message", err) + return nil, core.E("Controller.sendRequest", "failed to send message", err) } // Wait for response @@ -107,7 +107,7 @@ func (c *Controller) sendRequest(peerID string, msg *Message, timeout time.Durat case resp := <-respCh: return resp, nil case <-ctx.Done(): - return nil, coreerr.E("Controller.sendRequest", "request timeout", nil) + return nil, core.E("Controller.sendRequest", "request timeout", nil) } } @@ -120,7 +120,7 @@ func (c *Controller) GetRemoteStats(peerID string) (*StatsPayload, error) { msg, err := NewMessage(MsgGetStats, identity.ID, peerID, nil) if err != nil { - return nil, coreerr.E("Controller.GetRemoteStats", "failed to create message", err) + return nil, core.E("Controller.GetRemoteStats", "failed to create message", err) } resp, err := c.sendRequest(peerID, msg, 10*time.Second) @@ -144,7 +144,7 @@ func (c *Controller) StartRemoteMiner(peerID, minerType, profileID string, confi } if minerType == "" { - return coreerr.E("Controller.StartRemoteMiner", "miner type is required", nil) + return core.E("Controller.StartRemoteMiner", "miner type is required", nil) } payload := StartMinerPayload{ @@ -155,7 +155,7 @@ func (c *Controller) StartRemoteMiner(peerID, minerType, profileID string, confi msg, err := NewMessage(MsgStartMiner, identity.ID, peerID, payload) if err != nil { - return coreerr.E("Controller.StartRemoteMiner", "failed to create message", err) + return core.E("Controller.StartRemoteMiner", "failed to create message", err) } resp, err := c.sendRequest(peerID, msg, 30*time.Second) @@ -169,7 +169,7 @@ func (c *Controller) StartRemoteMiner(peerID, minerType, profileID string, confi } if !ack.Success { - return coreerr.E("Controller.StartRemoteMiner", "miner start failed: "+ack.Error, nil) + return core.E("Controller.StartRemoteMiner", "miner start failed: "+ack.Error, nil) } return nil @@ -188,7 +188,7 @@ func (c *Controller) StopRemoteMiner(peerID, minerName string) error { msg, err := NewMessage(MsgStopMiner, identity.ID, peerID, payload) if err != nil { - return coreerr.E("Controller.StopRemoteMiner", "failed to create message", err) + return core.E("Controller.StopRemoteMiner", "failed to create message", err) } resp, err := c.sendRequest(peerID, msg, 30*time.Second) @@ -202,7 +202,7 @@ func (c *Controller) StopRemoteMiner(peerID, minerName string) error { } if !ack.Success { - return coreerr.E("Controller.StopRemoteMiner", "miner stop failed: "+ack.Error, nil) + return core.E("Controller.StopRemoteMiner", "miner stop failed: "+ack.Error, nil) } return nil @@ -222,7 +222,7 @@ func (c *Controller) GetRemoteLogs(peerID, minerName string, lines int) ([]strin msg, err := NewMessage(MsgGetLogs, identity.ID, peerID, payload) if err != nil { - return nil, coreerr.E("Controller.GetRemoteLogs", "failed to create message", err) + return nil, core.E("Controller.GetRemoteLogs", "failed to create message", err) } resp, err := c.sendRequest(peerID, msg, 10*time.Second) @@ -281,7 +281,7 @@ func (c *Controller) PingPeer(peerID string) (float64, error) { msg, err := NewMessage(MsgPing, identity.ID, peerID, payload) if err != nil { - return 0, coreerr.E("Controller.PingPeer", "failed to create message", err) + return 0, core.E("Controller.PingPeer", "failed to create message", err) } resp, err := c.sendRequest(peerID, msg, 5*time.Second) @@ -309,7 +309,7 @@ func (c *Controller) PingPeer(peerID string) (float64, error) { func (c *Controller) ConnectToPeer(peerID string) error { peer := c.peers.GetPeer(peerID) if peer == nil { - return coreerr.E("Controller.ConnectToPeer", "peer not found: "+peerID, nil) + return core.E("Controller.ConnectToPeer", "peer not found: "+peerID, nil) } _, err := c.transport.Connect(peer) @@ -320,7 +320,7 @@ func (c *Controller) ConnectToPeer(peerID string) error { func (c *Controller) DisconnectFromPeer(peerID string) error { conn := c.transport.GetConnection(peerID) if conn == nil { - return coreerr.E("Controller.DisconnectFromPeer", "peer not connected: "+peerID, nil) + return core.E("Controller.DisconnectFromPeer", "peer not connected: "+peerID, nil) } return conn.Close() diff --git a/node/core_fs.go b/node/core_fs.go new file mode 100644 index 0000000..f7056c8 --- /dev/null +++ b/node/core_fs.go @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package node + +import core "dappco.re/go/core" + +var localFS = (&core.Fs{}).New("/") + +func fsEnsureDir(path string) error { + return fsResultErr(localFS.EnsureDir(path)) +} + +func fsWrite(path, content string) error { + return fsResultErr(localFS.Write(path, content)) +} + +func fsRead(path string) (string, error) { + result := localFS.Read(path) + if !result.OK { + return "", fsResultErr(result) + } + + content, ok := result.Value.(string) + if !ok { + return "", core.E("node.fsRead", "filesystem read returned non-string content", nil) + } + + return content, nil +} + +func fsDelete(path string) error { + return fsResultErr(localFS.Delete(path)) +} + +func fsRename(oldPath, newPath string) error { + return fsResultErr(localFS.Rename(oldPath, newPath)) +} + +func fsExists(path string) bool { + return localFS.Exists(path) +} + +func fsResultErr(result core.Result) error { + if result.OK { + return nil + } + + if err, ok := result.Value.(error); ok && err != nil { + return err + } + + return core.E("node.fs", "filesystem operation failed", nil) +} diff --git a/node/dispatcher.go b/node/dispatcher.go index c240e18..a91a0ac 100644 --- a/node/dispatcher.go +++ b/node/dispatcher.go @@ -5,7 +5,7 @@ import ( "iter" "sync" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "dappco.re/go/core/p2p/logging" "dappco.re/go/core/p2p/ueps" @@ -134,12 +134,12 @@ func (d *Dispatcher) Dispatch(pkt *ueps.ParsedPacket) error { var ( // ErrThreatScoreExceeded is returned when a packet's ThreatScore exceeds // the safety threshold. - ErrThreatScoreExceeded = coreerr.E("Dispatcher.Dispatch", fmt.Sprintf("packet rejected: threat score exceeds safety threshold (%d)", ThreatScoreThreshold), nil) + ErrThreatScoreExceeded = core.E("Dispatcher.Dispatch", fmt.Sprintf("packet rejected: threat score exceeds safety threshold (%d)", ThreatScoreThreshold), nil) // ErrUnknownIntent is returned when no handler is registered for the // packet's IntentID. - ErrUnknownIntent = coreerr.E("Dispatcher.Dispatch", "packet dropped: unknown intent", nil) + ErrUnknownIntent = core.E("Dispatcher.Dispatch", "packet dropped: unknown intent", nil) // ErrNilPacket is returned when a nil packet is passed to Dispatch. - ErrNilPacket = coreerr.E("Dispatcher.Dispatch", "nil packet", nil) + ErrNilPacket = core.E("Dispatcher.Dispatch", "nil packet", nil) ) diff --git a/node/errors.go b/node/errors.go index 218610b..96cf152 100644 --- a/node/errors.go +++ b/node/errors.go @@ -1,14 +1,14 @@ package node -import coreerr "dappco.re/go/core/log" +import core "dappco.re/go/core" // Sentinel errors shared across the node package. var ( // ErrIdentityNotInitialized is returned when a node operation requires // a node identity but none has been generated or loaded. - ErrIdentityNotInitialized = coreerr.E("node", "node identity not initialized", nil) + ErrIdentityNotInitialized = core.E("node", "node identity not initialized", nil) // ErrMinerManagerNotConfigured is returned when a miner operation is // attempted but no MinerManager has been set on the Worker. - ErrMinerManagerNotConfigured = coreerr.E("node", "miner manager not configured", nil) + ErrMinerManagerNotConfigured = core.E("node", "miner manager not configured", nil) ) diff --git a/node/identity.go b/node/identity.go index 5b650a5..d745c5c 100644 --- a/node/identity.go +++ b/node/identity.go @@ -12,8 +12,7 @@ import ( "sync" "time" - coreio "dappco.re/go/core/io" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "forge.lthn.ai/Snider/Borg/pkg/stmf" "github.com/adrg/xdg" @@ -26,7 +25,7 @@ const ChallengeSize = 32 func GenerateChallenge() ([]byte, error) { challenge := make([]byte, ChallengeSize) if _, err := rand.Read(challenge); err != nil { - return nil, coreerr.E("GenerateChallenge", "failed to generate challenge", err) + return nil, core.E("GenerateChallenge", "failed to generate challenge", err) } return challenge, nil } @@ -80,12 +79,12 @@ type NodeManager struct { func NewNodeManager() (*NodeManager, error) { keyPath, err := xdg.DataFile("lethean-desktop/node/private.key") if err != nil { - return nil, coreerr.E("NodeManager.New", "failed to get key path", err) + return nil, core.E("NodeManager.New", "failed to get key path", err) } configPath, err := xdg.ConfigFile("lethean-desktop/node.json") if err != nil { - return nil, coreerr.E("NodeManager.New", "failed to get config path", err) + return nil, core.E("NodeManager.New", "failed to get config path", err) } return NewNodeManagerWithPaths(keyPath, configPath) @@ -135,7 +134,7 @@ func (n *NodeManager) GenerateIdentity(name string, role NodeRole) error { // Generate X25519 keypair using STMF keyPair, err := stmf.GenerateKeyPair() if err != nil { - return coreerr.E("NodeManager.GenerateIdentity", "failed to generate keypair", err) + return core.E("NodeManager.GenerateIdentity", "failed to generate keypair", err) } // Derive node ID from public key (first 16 bytes as hex = 32 char ID) @@ -156,12 +155,12 @@ func (n *NodeManager) GenerateIdentity(name string, role NodeRole) error { // Save private key if err := n.savePrivateKey(); err != nil { - return coreerr.E("NodeManager.GenerateIdentity", "failed to save private key", err) + return core.E("NodeManager.GenerateIdentity", "failed to save private key", err) } // Save identity config if err := n.saveIdentity(); err != nil { - return coreerr.E("NodeManager.GenerateIdentity", "failed to save identity", err) + return core.E("NodeManager.GenerateIdentity", "failed to save identity", err) } return nil @@ -180,19 +179,19 @@ func (n *NodeManager) DeriveSharedSecret(peerPubKeyBase64 string) ([]byte, error // Load peer's public key peerPubKey, err := stmf.LoadPublicKeyBase64(peerPubKeyBase64) if err != nil { - return nil, coreerr.E("NodeManager.DeriveSharedSecret", "failed to load peer public key", err) + return nil, core.E("NodeManager.DeriveSharedSecret", "failed to load peer public key", err) } // Load our private key privateKey, err := ecdh.X25519().NewPrivateKey(n.privateKey) if err != nil { - return nil, coreerr.E("NodeManager.DeriveSharedSecret", "failed to load private key", err) + return nil, core.E("NodeManager.DeriveSharedSecret", "failed to load private key", err) } // Derive shared secret using ECDH sharedSecret, err := privateKey.ECDH(peerPubKey) if err != nil { - return nil, coreerr.E("NodeManager.DeriveSharedSecret", "failed to derive shared secret", err) + return nil, core.E("NodeManager.DeriveSharedSecret", "failed to derive shared secret", err) } // Hash the shared secret using SHA-256 (same pattern as Borg/trix) @@ -204,13 +203,13 @@ func (n *NodeManager) DeriveSharedSecret(peerPubKeyBase64 string) ([]byte, error func (n *NodeManager) savePrivateKey() error { // Ensure directory exists dir := filepath.Dir(n.keyPath) - if err := coreio.Local.EnsureDir(dir); err != nil { - return coreerr.E("NodeManager.savePrivateKey", "failed to create key directory", err) + if err := fsEnsureDir(dir); err != nil { + return core.E("NodeManager.savePrivateKey", "failed to create key directory", err) } // Write private key - if err := coreio.Local.Write(n.keyPath, string(n.privateKey)); err != nil { - return coreerr.E("NodeManager.savePrivateKey", "failed to write private key", err) + if err := fsWrite(n.keyPath, string(n.privateKey)); err != nil { + return core.E("NodeManager.savePrivateKey", "failed to write private key", err) } return nil @@ -220,17 +219,17 @@ func (n *NodeManager) savePrivateKey() error { func (n *NodeManager) saveIdentity() error { // Ensure directory exists dir := filepath.Dir(n.configPath) - if err := coreio.Local.EnsureDir(dir); err != nil { - return coreerr.E("NodeManager.saveIdentity", "failed to create config directory", err) + if err := fsEnsureDir(dir); err != nil { + return core.E("NodeManager.saveIdentity", "failed to create config directory", err) } data, err := json.MarshalIndent(n.identity, "", " ") if err != nil { - return coreerr.E("NodeManager.saveIdentity", "failed to marshal identity", err) + return core.E("NodeManager.saveIdentity", "failed to marshal identity", err) } - if err := coreio.Local.Write(n.configPath, string(data)); err != nil { - return coreerr.E("NodeManager.saveIdentity", "failed to write identity", err) + if err := fsWrite(n.configPath, string(data)); err != nil { + return core.E("NodeManager.saveIdentity", "failed to write identity", err) } return nil @@ -239,27 +238,27 @@ func (n *NodeManager) saveIdentity() error { // loadIdentity loads the node identity from disk. func (n *NodeManager) loadIdentity() error { // Load identity config - content, err := coreio.Local.Read(n.configPath) + content, err := fsRead(n.configPath) if err != nil { - return coreerr.E("NodeManager.loadIdentity", "failed to read identity", err) + return core.E("NodeManager.loadIdentity", "failed to read identity", err) } var identity NodeIdentity if err := json.Unmarshal([]byte(content), &identity); err != nil { - return coreerr.E("NodeManager.loadIdentity", "failed to unmarshal identity", err) + return core.E("NodeManager.loadIdentity", "failed to unmarshal identity", err) } // Load private key - keyContent, err := coreio.Local.Read(n.keyPath) + keyContent, err := fsRead(n.keyPath) if err != nil { - return coreerr.E("NodeManager.loadIdentity", "failed to read private key", err) + return core.E("NodeManager.loadIdentity", "failed to read private key", err) } privateKey := []byte(keyContent) // Reconstruct keypair from private key keyPair, err := stmf.LoadKeyPair(privateKey) if err != nil { - return coreerr.E("NodeManager.loadIdentity", "failed to load keypair", err) + return core.E("NodeManager.loadIdentity", "failed to load keypair", err) } n.identity = &identity @@ -275,16 +274,16 @@ func (n *NodeManager) Delete() error { defer n.mu.Unlock() // Remove private key (ignore if already absent) - if coreio.Local.Exists(n.keyPath) { - if err := coreio.Local.Delete(n.keyPath); err != nil { - return coreerr.E("NodeManager.Delete", "failed to remove private key", err) + if fsExists(n.keyPath) { + if err := fsDelete(n.keyPath); err != nil { + return core.E("NodeManager.Delete", "failed to remove private key", err) } } // Remove identity config (ignore if already absent) - if coreio.Local.Exists(n.configPath) { - if err := coreio.Local.Delete(n.configPath); err != nil { - return coreerr.E("NodeManager.Delete", "failed to remove identity", err) + if fsExists(n.configPath) { + if err := fsDelete(n.configPath); err != nil { + return core.E("NodeManager.Delete", "failed to remove identity", err) } } diff --git a/node/levin/header.go b/node/levin/header.go index e93531f..189782a 100644 --- a/node/levin/header.go +++ b/node/levin/header.go @@ -8,7 +8,7 @@ package levin import ( "encoding/binary" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // HeaderSize is the exact byte length of a serialised Levin header. @@ -43,8 +43,8 @@ const ( // Sentinel errors returned by DecodeHeader. var ( - ErrBadSignature = coreerr.E("levin", "bad signature", nil) - ErrPayloadTooBig = coreerr.E("levin", "payload exceeds maximum size", nil) + ErrBadSignature = core.E("levin", "bad signature", nil) + ErrPayloadTooBig = core.E("levin", "payload exceeds maximum size", nil) ) // Header is the 33-byte packed header that prefixes every Levin message. diff --git a/node/levin/storage.go b/node/levin/storage.go index 3d39718..d3d2499 100644 --- a/node/levin/storage.go +++ b/node/levin/storage.go @@ -10,7 +10,7 @@ import ( "math" "slices" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // Portable storage signatures and version (9-byte header). @@ -41,12 +41,12 @@ const ( // Sentinel errors for storage encoding and decoding. var ( - ErrStorageBadSignature = coreerr.E("levin.storage", "bad storage signature", nil) - ErrStorageTruncated = coreerr.E("levin.storage", "truncated storage data", nil) - ErrStorageBadVersion = coreerr.E("levin.storage", "unsupported storage version", nil) - ErrStorageNameTooLong = coreerr.E("levin.storage", "entry name exceeds 255 bytes", nil) - ErrStorageTypeMismatch = coreerr.E("levin.storage", "value type mismatch", nil) - ErrStorageUnknownType = coreerr.E("levin.storage", "unknown type tag", nil) + ErrStorageBadSignature = core.E("levin.storage", "bad storage signature", nil) + ErrStorageTruncated = core.E("levin.storage", "truncated storage data", nil) + ErrStorageBadVersion = core.E("levin.storage", "unsupported storage version", nil) + ErrStorageNameTooLong = core.E("levin.storage", "entry name exceeds 255 bytes", nil) + ErrStorageTypeMismatch = core.E("levin.storage", "value type mismatch", nil) + ErrStorageUnknownType = core.E("levin.storage", "unknown type tag", nil) ) // Section is an ordered map of named values forming a portable storage section. @@ -394,7 +394,7 @@ func encodeValue(buf []byte, v Value) ([]byte, error) { return encodeSection(buf, v.objectVal) default: - return nil, coreerr.E("levin.encodeValue", fmt.Sprintf("unknown type tag: 0x%02x", v.Type), ErrStorageUnknownType) + return nil, core.E("levin.encodeValue", fmt.Sprintf("unknown type tag: 0x%02x", v.Type), ErrStorageUnknownType) } } @@ -441,7 +441,7 @@ func encodeArray(buf []byte, v Value) ([]byte, error) { return buf, nil default: - return nil, coreerr.E("levin.encodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) + return nil, core.E("levin.encodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) } } @@ -476,7 +476,7 @@ func DecodeStorage(data []byte) (Section, error) { func decodeSection(buf []byte) (Section, int, error) { count, n, err := UnpackVarint(buf) if err != nil { - return nil, 0, coreerr.E("levin.decodeSection", "section entry count", err) + return nil, 0, core.E("levin.decodeSection", "section entry count", err) } off := n @@ -507,7 +507,7 @@ func decodeSection(buf []byte) (Section, int, error) { // Value. val, consumed, err := decodeValue(buf[off:], tag) if err != nil { - return nil, 0, coreerr.E("levin.decodeSection", "field "+name, err) + return nil, 0, core.E("levin.decodeSection", "field "+name, err) } off += consumed @@ -613,7 +613,7 @@ func decodeValue(buf []byte, tag uint8) (Value, int, error) { return Value{Type: TypeObject, objectVal: sec}, consumed, nil default: - return Value{}, 0, coreerr.E("levin.decodeValue", fmt.Sprintf("unknown type tag: 0x%02x", tag), ErrStorageUnknownType) + return Value{}, 0, core.E("levin.decodeValue", fmt.Sprintf("unknown type tag: 0x%02x", tag), ErrStorageUnknownType) } } @@ -681,6 +681,6 @@ func decodeArray(buf []byte, tag uint8) (Value, int, error) { return Value{Type: tag, objectArray: arr}, off, nil default: - return Value{}, 0, coreerr.E("levin.decodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) + return Value{}, 0, core.E("levin.decodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) } } diff --git a/node/levin/varint.go b/node/levin/varint.go index 2830e71..edbe7e7 100644 --- a/node/levin/varint.go +++ b/node/levin/varint.go @@ -6,27 +6,27 @@ package levin import ( "encoding/binary" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // Size-mark bits occupying the two lowest bits of the first byte. const ( - varintMask = 0x03 - varintMark1 = 0x00 // 1 byte, max 63 - varintMark2 = 0x01 // 2 bytes, max 16,383 - varintMark4 = 0x02 // 4 bytes, max 1,073,741,823 - varintMark8 = 0x03 // 8 bytes, max 4,611,686,018,427,387,903 - varintMax1 = 63 - varintMax2 = 16_383 - varintMax4 = 1_073_741_823 - varintMax8 = 4_611_686_018_427_387_903 + varintMask = 0x03 + varintMark1 = 0x00 // 1 byte, max 63 + varintMark2 = 0x01 // 2 bytes, max 16,383 + varintMark4 = 0x02 // 4 bytes, max 1,073,741,823 + varintMark8 = 0x03 // 8 bytes, max 4,611,686,018,427,387,903 + varintMax1 = 63 + varintMax2 = 16_383 + varintMax4 = 1_073_741_823 + varintMax8 = 4_611_686_018_427_387_903 ) // ErrVarintTruncated is returned when the buffer is too short. -var ErrVarintTruncated = coreerr.E("levin", "truncated varint", nil) +var ErrVarintTruncated = core.E("levin", "truncated varint", nil) // ErrVarintOverflow is returned when the value is too large to encode. -var ErrVarintOverflow = coreerr.E("levin", "varint overflow", nil) +var ErrVarintOverflow = core.E("levin", "varint overflow", nil) // PackVarint encodes v using the epee portable-storage varint scheme. // The low two bits of the first byte indicate the total encoded width; diff --git a/node/peer.go b/node/peer.go index d4ff02c..e876cd6 100644 --- a/node/peer.go +++ b/node/peer.go @@ -10,8 +10,7 @@ import ( "sync" "time" - coreio "dappco.re/go/core/io" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "dappco.re/go/core/p2p/logging" poindexter "forge.lthn.ai/Snider/Poindexter" @@ -79,13 +78,13 @@ func validatePeerName(name string) error { return nil // Empty names are allowed (optional field) } if len(name) < PeerNameMinLength { - return coreerr.E("validatePeerName", "peer name too short", nil) + return core.E("validatePeerName", "peer name too short", nil) } if len(name) > PeerNameMaxLength { - return coreerr.E("validatePeerName", "peer name too long", nil) + return core.E("validatePeerName", "peer name too long", nil) } if !peerNameRegex.MatchString(name) { - return coreerr.E("validatePeerName", "peer name contains invalid characters (use alphanumeric, hyphens, underscores, spaces)", nil) + return core.E("validatePeerName", "peer name contains invalid characters (use alphanumeric, hyphens, underscores, spaces)", nil) } return nil } @@ -123,7 +122,7 @@ var ( func NewPeerRegistry() (*PeerRegistry, error) { peersPath, err := xdg.ConfigFile("lethean-desktop/peers.json") if err != nil { - return nil, coreerr.E("PeerRegistry.New", "failed to get peers path", err) + return nil, core.E("PeerRegistry.New", "failed to get peers path", err) } return NewPeerRegistryWithPath(peersPath) @@ -244,7 +243,7 @@ func (r *PeerRegistry) AddPeer(peer *Peer) error { if peer.ID == "" { r.mu.Unlock() - return coreerr.E("PeerRegistry.AddPeer", "peer ID is required", nil) + return core.E("PeerRegistry.AddPeer", "peer ID is required", nil) } // Validate peer name (P2P-LOW-3) @@ -255,7 +254,7 @@ func (r *PeerRegistry) AddPeer(peer *Peer) error { if _, exists := r.peers[peer.ID]; exists { r.mu.Unlock() - return coreerr.E("PeerRegistry.AddPeer", "peer "+peer.ID+" already exists", nil) + return core.E("PeerRegistry.AddPeer", "peer "+peer.ID+" already exists", nil) } // Set defaults @@ -280,7 +279,7 @@ func (r *PeerRegistry) UpdatePeer(peer *Peer) error { if _, exists := r.peers[peer.ID]; !exists { r.mu.Unlock() - return coreerr.E("PeerRegistry.UpdatePeer", "peer "+peer.ID+" not found", nil) + return core.E("PeerRegistry.UpdatePeer", "peer "+peer.ID+" not found", nil) } r.peers[peer.ID] = peer @@ -297,7 +296,7 @@ func (r *PeerRegistry) RemovePeer(id string) error { if _, exists := r.peers[id]; !exists { r.mu.Unlock() - return coreerr.E("PeerRegistry.RemovePeer", "peer "+id+" not found", nil) + return core.E("PeerRegistry.RemovePeer", "peer "+id+" not found", nil) } delete(r.peers, id) @@ -351,7 +350,7 @@ func (r *PeerRegistry) UpdateMetrics(id string, pingMS, geoKM float64, hops int) peer, exists := r.peers[id] if !exists { r.mu.Unlock() - return coreerr.E("PeerRegistry.UpdateMetrics", "peer "+id+" not found", nil) + return core.E("PeerRegistry.UpdateMetrics", "peer "+id+" not found", nil) } peer.PingMS = pingMS @@ -373,7 +372,7 @@ func (r *PeerRegistry) UpdateScore(id string, score float64) error { peer, exists := r.peers[id] if !exists { r.mu.Unlock() - return coreerr.E("PeerRegistry.UpdateScore", "peer "+id+" not found", nil) + return core.E("PeerRegistry.UpdateScore", "peer "+id+" not found", nil) } // Clamp score to 0-100 @@ -656,8 +655,8 @@ func (r *PeerRegistry) scheduleSave() { func (r *PeerRegistry) saveNow() error { // Ensure directory exists dir := filepath.Dir(r.path) - if err := coreio.Local.EnsureDir(dir); err != nil { - return coreerr.E("PeerRegistry.saveNow", "failed to create peers directory", err) + if err := fsEnsureDir(dir); err != nil { + return core.E("PeerRegistry.saveNow", "failed to create peers directory", err) } // Convert to slice for JSON @@ -665,18 +664,18 @@ func (r *PeerRegistry) saveNow() error { data, err := json.MarshalIndent(peers, "", " ") if err != nil { - return coreerr.E("PeerRegistry.saveNow", "failed to marshal peers", err) + return core.E("PeerRegistry.saveNow", "failed to marshal peers", err) } // Use atomic write pattern: write to temp file, then rename tmpPath := r.path + ".tmp" - if err := coreio.Local.Write(tmpPath, string(data)); err != nil { - return coreerr.E("PeerRegistry.saveNow", "failed to write peers temp file", err) + if err := fsWrite(tmpPath, string(data)); err != nil { + return core.E("PeerRegistry.saveNow", "failed to write peers temp file", err) } - if err := coreio.Local.Rename(tmpPath, r.path); err != nil { - coreio.Local.Delete(tmpPath) // Clean up temp file - return coreerr.E("PeerRegistry.saveNow", "failed to rename peers file", err) + if err := fsRename(tmpPath, r.path); err != nil { + fsDelete(tmpPath) // Clean up temp file + return core.E("PeerRegistry.saveNow", "failed to rename peers file", err) } return nil @@ -718,14 +717,14 @@ func (r *PeerRegistry) save() error { // load reads peers from disk. func (r *PeerRegistry) load() error { - content, err := coreio.Local.Read(r.path) + content, err := fsRead(r.path) if err != nil { - return coreerr.E("PeerRegistry.load", "failed to read peers", err) + return core.E("PeerRegistry.load", "failed to read peers", err) } var peers []*Peer if err := json.Unmarshal([]byte(content), &peers); err != nil { - return coreerr.E("PeerRegistry.load", "failed to unmarshal peers", err) + return core.E("PeerRegistry.load", "failed to unmarshal peers", err) } r.peers = make(map[string]*Peer) diff --git a/node/protocol.go b/node/protocol.go index 80ca346..f93b2f2 100644 --- a/node/protocol.go +++ b/node/protocol.go @@ -3,7 +3,7 @@ package node import ( "fmt" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // ProtocolError represents an error from the remote peer. @@ -26,7 +26,7 @@ type ResponseHandler struct{} // 3. If response type matches expected (returns error if not) func (h *ResponseHandler) ValidateResponse(resp *Message, expectedType MessageType) error { if resp == nil { - return coreerr.E("ResponseHandler.ValidateResponse", "nil response", nil) + return core.E("ResponseHandler.ValidateResponse", "nil response", nil) } // Check for error response @@ -40,7 +40,7 @@ func (h *ResponseHandler) ValidateResponse(resp *Message, expectedType MessageTy // Check expected type if resp.Type != expectedType { - return coreerr.E("ResponseHandler.ValidateResponse", "unexpected response type: expected "+string(expectedType)+", got "+string(resp.Type), nil) + return core.E("ResponseHandler.ValidateResponse", "unexpected response type: expected "+string(expectedType)+", got "+string(resp.Type), nil) } return nil @@ -55,7 +55,7 @@ func (h *ResponseHandler) ParseResponse(resp *Message, expectedType MessageType, if target != nil { if err := resp.ParsePayload(target); err != nil { - return coreerr.E("ResponseHandler.ParseResponse", "failed to parse "+string(expectedType)+" payload", err) + return core.E("ResponseHandler.ParseResponse", "failed to parse "+string(expectedType)+" payload", err) } } diff --git a/node/transport.go b/node/transport.go index e30c0e5..ceb1cb1 100644 --- a/node/transport.go +++ b/node/transport.go @@ -15,7 +15,7 @@ import ( "sync/atomic" "time" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "dappco.re/go/core/p2p/logging" "forge.lthn.ai/Snider/Borg/pkg/smsg" @@ -290,7 +290,7 @@ func (t *Transport) Stop() error { defer cancel() if err := t.server.Shutdown(ctx); err != nil { - return coreerr.E("Transport.Stop", "server shutdown error", err) + return core.E("Transport.Stop", "server shutdown error", err) } } @@ -321,7 +321,7 @@ func (t *Transport) Connect(peer *Peer) (*PeerConnection, error) { } conn, _, err := dialer.Dial(u.String(), nil) if err != nil { - return nil, coreerr.E("Transport.Connect", "failed to connect to peer", err) + return nil, core.E("Transport.Connect", "failed to connect to peer", err) } pc := &PeerConnection{ @@ -336,7 +336,7 @@ func (t *Transport) Connect(peer *Peer) (*PeerConnection, error) { // This also derives and stores the shared secret in pc.SharedSecret if err := t.performHandshake(pc); err != nil { conn.Close() - return nil, coreerr.E("Transport.Connect", "handshake failed", err) + return nil, core.E("Transport.Connect", "handshake failed", err) } // Store connection using the real peer ID from handshake @@ -369,7 +369,7 @@ func (t *Transport) Send(peerID string, msg *Message) error { t.mu.RUnlock() if !exists { - return coreerr.E("Transport.Send", "peer "+peerID+" not connected", nil) + return core.E("Transport.Send", "peer "+peerID+" not connected", nil) } return pc.Send(msg) @@ -629,7 +629,7 @@ func (t *Transport) performHandshake(pc *PeerConnection) error { // Generate challenge for the server to prove it has the matching private key challenge, err := GenerateChallenge() if err != nil { - return coreerr.E("Transport.performHandshake", "generate challenge", err) + return core.E("Transport.performHandshake", "generate challenge", err) } payload := HandshakePayload{ @@ -640,41 +640,41 @@ func (t *Transport) performHandshake(pc *PeerConnection) error { msg, err := NewMessage(MsgHandshake, identity.ID, pc.Peer.ID, payload) if err != nil { - return coreerr.E("Transport.performHandshake", "create handshake message", err) + return core.E("Transport.performHandshake", "create handshake message", err) } // First message is unencrypted (peer needs our public key) data, err := MarshalJSON(msg) if err != nil { - return coreerr.E("Transport.performHandshake", "marshal handshake message", err) + return core.E("Transport.performHandshake", "marshal handshake message", err) } if err := pc.Conn.WriteMessage(websocket.TextMessage, data); err != nil { - return coreerr.E("Transport.performHandshake", "send handshake", err) + return core.E("Transport.performHandshake", "send handshake", err) } // Wait for ack _, ackData, err := pc.Conn.ReadMessage() if err != nil { - return coreerr.E("Transport.performHandshake", "read handshake ack", err) + return core.E("Transport.performHandshake", "read handshake ack", err) } var ackMsg Message if err := json.Unmarshal(ackData, &ackMsg); err != nil { - return coreerr.E("Transport.performHandshake", "unmarshal handshake ack", err) + return core.E("Transport.performHandshake", "unmarshal handshake ack", err) } if ackMsg.Type != MsgHandshakeAck { - return coreerr.E("Transport.performHandshake", "expected handshake_ack, got "+string(ackMsg.Type), nil) + return core.E("Transport.performHandshake", "expected handshake_ack, got "+string(ackMsg.Type), nil) } var ackPayload HandshakeAckPayload if err := ackMsg.ParsePayload(&ackPayload); err != nil { - return coreerr.E("Transport.performHandshake", "parse handshake ack payload", err) + return core.E("Transport.performHandshake", "parse handshake ack payload", err) } if !ackPayload.Accepted { - return coreerr.E("Transport.performHandshake", "handshake rejected: "+ackPayload.Reason, nil) + return core.E("Transport.performHandshake", "handshake rejected: "+ackPayload.Reason, nil) } // Update peer with the received identity info @@ -686,15 +686,15 @@ func (t *Transport) performHandshake(pc *PeerConnection) error { // Verify challenge response - derive shared secret first using the peer's public key sharedSecret, err := t.node.DeriveSharedSecret(pc.Peer.PublicKey) if err != nil { - return coreerr.E("Transport.performHandshake", "derive shared secret for challenge verification", err) + return core.E("Transport.performHandshake", "derive shared secret for challenge verification", err) } // Verify the server's response to our challenge if len(ackPayload.ChallengeResponse) == 0 { - return coreerr.E("Transport.performHandshake", "server did not provide challenge response", nil) + return core.E("Transport.performHandshake", "server did not provide challenge response", nil) } if !VerifyChallenge(challenge, ackPayload.ChallengeResponse, sharedSecret) { - return coreerr.E("Transport.performHandshake", "challenge response verification failed: server may not have matching private key", nil) + return core.E("Transport.performHandshake", "challenge response verification failed: server may not have matching private key", nil) } // Store the shared secret for later use @@ -841,7 +841,7 @@ func (pc *PeerConnection) Send(msg *Message) error { // Set write deadline to prevent blocking forever if err := pc.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil { - return coreerr.E("PeerConnection.Send", "failed to set write deadline", err) + return core.E("PeerConnection.Send", "failed to set write deadline", err) } defer pc.Conn.SetWriteDeadline(time.Time{}) // Reset deadline after send diff --git a/node/worker.go b/node/worker.go index af917d4..1ea18ea 100644 --- a/node/worker.go +++ b/node/worker.go @@ -6,7 +6,7 @@ import ( "path/filepath" "time" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" "dappco.re/go/core/p2p/logging" "github.com/adrg/xdg" @@ -55,7 +55,6 @@ func NewWorker(node *NodeManager, transport *Transport) *Worker { } } - // SetMinerManager sets the miner manager for handling miner operations. func (w *Worker) SetMinerManager(manager MinerManager) { w.minerManager = manager @@ -119,7 +118,7 @@ func (w *Worker) HandleMessage(conn *PeerConnection, msg *Message) { func (w *Worker) handlePing(msg *Message) (*Message, error) { var ping PingPayload if err := msg.ParsePayload(&ping); err != nil { - return nil, coreerr.E("Worker.handlePing", "invalid ping payload", err) + return nil, core.E("Worker.handlePing", "invalid ping payload", err) } pong := PongPayload{ @@ -202,12 +201,12 @@ func (w *Worker) handleStartMiner(msg *Message) (*Message, error) { var payload StartMinerPayload if err := msg.ParsePayload(&payload); err != nil { - return nil, coreerr.E("Worker.handleStartMiner", "invalid start miner payload", err) + return nil, core.E("Worker.handleStartMiner", "invalid start miner payload", err) } // Validate miner type is provided if payload.MinerType == "" { - return nil, coreerr.E("Worker.handleStartMiner", "miner type is required", nil) + return nil, core.E("Worker.handleStartMiner", "miner type is required", nil) } // Get the config from the profile or use the override @@ -217,11 +216,11 @@ func (w *Worker) handleStartMiner(msg *Message) (*Message, error) { } else if w.profileManager != nil { profile, err := w.profileManager.GetProfile(payload.ProfileID) if err != nil { - return nil, coreerr.E("Worker.handleStartMiner", "profile not found: "+payload.ProfileID, nil) + return nil, core.E("Worker.handleStartMiner", "profile not found: "+payload.ProfileID, nil) } config = profile } else { - return nil, coreerr.E("Worker.handleStartMiner", "no config provided and no profile manager configured", nil) + return nil, core.E("Worker.handleStartMiner", "no config provided and no profile manager configured", nil) } // Start the miner @@ -249,7 +248,7 @@ func (w *Worker) handleStopMiner(msg *Message) (*Message, error) { var payload StopMinerPayload if err := msg.ParsePayload(&payload); err != nil { - return nil, coreerr.E("Worker.handleStopMiner", "invalid stop miner payload", err) + return nil, core.E("Worker.handleStopMiner", "invalid stop miner payload", err) } err := w.minerManager.StopMiner(payload.MinerName) @@ -272,7 +271,7 @@ func (w *Worker) handleGetLogs(msg *Message) (*Message, error) { var payload GetLogsPayload if err := msg.ParsePayload(&payload); err != nil { - return nil, coreerr.E("Worker.handleGetLogs", "invalid get logs payload", err) + return nil, core.E("Worker.handleGetLogs", "invalid get logs payload", err) } // Validate and limit the Lines parameter to prevent resource exhaustion @@ -283,7 +282,7 @@ func (w *Worker) handleGetLogs(msg *Message) (*Message, error) { miner, err := w.minerManager.GetMiner(payload.MinerName) if err != nil { - return nil, coreerr.E("Worker.handleGetLogs", "miner not found: "+payload.MinerName, nil) + return nil, core.E("Worker.handleGetLogs", "miner not found: "+payload.MinerName, nil) } lines := miner.GetConsoleHistory(payload.Lines) @@ -301,7 +300,7 @@ func (w *Worker) handleGetLogs(msg *Message) (*Message, error) { func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, error) { var payload DeployPayload if err := msg.ParsePayload(&payload); err != nil { - return nil, coreerr.E("Worker.handleDeploy", "invalid deploy payload", err) + return nil, core.E("Worker.handleDeploy", "invalid deploy payload", err) } // Reconstruct Bundle object from payload @@ -321,19 +320,19 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err switch bundle.Type { case BundleProfile: if w.profileManager == nil { - return nil, coreerr.E("Worker.handleDeploy", "profile manager not configured", nil) + return nil, core.E("Worker.handleDeploy", "profile manager not configured", nil) } // Decrypt and extract profile data profileData, err := ExtractProfileBundle(bundle, password) if err != nil { - return nil, coreerr.E("Worker.handleDeploy", "failed to extract profile bundle", err) + return nil, core.E("Worker.handleDeploy", "failed to extract profile bundle", err) } // Unmarshal into interface{} to pass to ProfileManager var profile any if err := json.Unmarshal(profileData, &profile); err != nil { - return nil, coreerr.E("Worker.handleDeploy", "invalid profile data JSON", err) + return nil, core.E("Worker.handleDeploy", "invalid profile data JSON", err) } if err := w.profileManager.SaveProfile(profile); err != nil { @@ -366,7 +365,7 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err // Extract miner bundle minerPath, profileData, err := ExtractMinerBundle(bundle, password, installDir) if err != nil { - return nil, coreerr.E("Worker.handleDeploy", "failed to extract miner bundle", err) + return nil, core.E("Worker.handleDeploy", "failed to extract miner bundle", err) } // If the bundle contained a profile config, save it @@ -396,7 +395,7 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err return msg.Reply(MsgDeployAck, ack) default: - return nil, coreerr.E("Worker.handleDeploy", "unknown bundle type: "+payload.BundleType, nil) + return nil, core.E("Worker.handleDeploy", "unknown bundle type: "+payload.BundleType, nil) } } diff --git a/ueps/packet.go b/ueps/packet.go index 0fb590c..7241b2a 100644 --- a/ueps/packet.go +++ b/ueps/packet.go @@ -7,7 +7,7 @@ import ( "encoding/binary" "io" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // TLV Types @@ -23,7 +23,7 @@ const ( // UEPSHeader represents the conscious routing metadata type UEPSHeader struct { - Version uint8 // Default 0x09 + Version uint8 // Default 0x09 CurrentLayer uint8 TargetLayer uint8 IntentID uint8 // Semantic Token @@ -68,7 +68,7 @@ func (p *PacketBuilder) MarshalAndSign(sharedSecret []byte) ([]byte, error) { if err := writeTLV(buf, TagIntent, []byte{p.Header.IntentID}); err != nil { return nil, err } - + // Threat Score is uint16, needs binary packing tsBuf := make([]byte, 2) binary.BigEndian.PutUint16(tsBuf, p.Header.ThreatScore) @@ -105,22 +105,21 @@ func (p *PacketBuilder) MarshalAndSign(sharedSecret []byte) ([]byte, error) { func writeTLV(w io.Writer, tag uint8, value []byte) error { // Check length constraint (2 byte length = max 65535 bytes) if len(value) > 65535 { - return coreerr.E("ueps.writeTLV", "TLV value too large for 2-byte length header", nil) + return core.E("ueps.writeTLV", "TLV value too large for 2-byte length header", nil) } if _, err := w.Write([]byte{tag}); err != nil { return err } - + lenBuf := make([]byte, 2) binary.BigEndian.PutUint16(lenBuf, uint16(len(value))) if _, err := w.Write(lenBuf); err != nil { return err } - + if _, err := w.Write(value); err != nil { return err } return nil } - diff --git a/ueps/reader.go b/ueps/reader.go index dcd1fe7..8546804 100644 --- a/ueps/reader.go +++ b/ueps/reader.go @@ -8,7 +8,7 @@ import ( "encoding/binary" "io" - coreerr "dappco.re/go/core/log" + core "dappco.re/go/core" ) // ParsedPacket holds the verified data @@ -93,7 +93,7 @@ func ReadAndVerify(r *bufio.Reader, sharedSecret []byte) (*ParsedPacket, error) verify: if len(signature) == 0 { - return nil, coreerr.E("ueps.ReadAndVerify", "UEPS packet missing HMAC signature", nil) + return nil, core.E("ueps.ReadAndVerify", "UEPS packet missing HMAC signature", nil) } // 5. Verify HMAC @@ -104,7 +104,7 @@ verify: expectedMAC := mac.Sum(nil) if !hmac.Equal(signature, expectedMAC) { - return nil, coreerr.E("ueps.ReadAndVerify", "integrity violation: HMAC mismatch (ThreatScore +100)", nil) + return nil, core.E("ueps.ReadAndVerify", "integrity violation: HMAC mismatch (ThreatScore +100)", nil) } return &ParsedPacket{ @@ -112,4 +112,3 @@ verify: Payload: payload, }, nil } - -- 2.45.3 From 04ae11da43672a2a6d60e4eff9684a26b0697050 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 26 Mar 2026 14:37:02 +0000 Subject: [PATCH 2/2] refactor(node): replace stdlib helpers with core primitives Co-Authored-By: Virgil --- logging/logger.go | 18 ++++++-------- node/bufpool.go | 34 +++++++++++-------------- node/bundle.go | 58 +++++++++++++++++++++++++++---------------- node/controller.go | 3 +-- node/dispatcher.go | 9 +++---- node/identity.go | 18 +++++++------- node/levin/storage.go | 9 +++---- node/message.go | 32 +++++++++++++++--------- node/peer.go | 16 ++++++------ node/protocol.go | 4 +-- node/transport.go | 14 +++++------ node/worker.go | 14 +++++------ 12 files changed, 118 insertions(+), 111 deletions(-) diff --git a/logging/logger.go b/logging/logger.go index d368b82..ddb0459 100644 --- a/logging/logger.go +++ b/logging/logger.go @@ -2,11 +2,9 @@ package logging import ( - "fmt" "io" "maps" "os" - "strings" "sync" "time" @@ -115,7 +113,7 @@ func (l *Logger) log(level Level, msg string, fields Fields) { } // Build the log line - var sb strings.Builder + sb := core.NewBuilder() timestamp := time.Now().Format("2006/01/02 15:04:05") sb.WriteString(timestamp) sb.WriteString(" [") @@ -138,12 +136,12 @@ func (l *Logger) log(level Level, msg string, fields Fields) { sb.WriteString(" ") sb.WriteString(k) sb.WriteString("=") - sb.WriteString(fmt.Sprintf("%v", v)) + sb.WriteString(core.Sprint(v)) } } sb.WriteString("\n") - fmt.Fprint(l.output, sb.String()) + _, _ = l.output.Write([]byte(sb.String())) } // Debug logs a debug message. @@ -168,22 +166,22 @@ func (l *Logger) Error(msg string, fields ...Fields) { // Debugf logs a formatted debug message. func (l *Logger) Debugf(format string, args ...any) { - l.log(LevelDebug, fmt.Sprintf(format, args...), nil) + l.log(LevelDebug, core.Sprintf(format, args...), nil) } // Infof logs a formatted informational message. func (l *Logger) Infof(format string, args ...any) { - l.log(LevelInfo, fmt.Sprintf(format, args...), nil) + l.log(LevelInfo, core.Sprintf(format, args...), nil) } // Warnf logs a formatted warning message. func (l *Logger) Warnf(format string, args ...any) { - l.log(LevelWarn, fmt.Sprintf(format, args...), nil) + l.log(LevelWarn, core.Sprintf(format, args...), nil) } // Errorf logs a formatted error message. func (l *Logger) Errorf(format string, args ...any) { - l.log(LevelError, fmt.Sprintf(format, args...), nil) + l.log(LevelError, core.Sprintf(format, args...), nil) } // mergeFields combines multiple Fields maps into one. @@ -270,7 +268,7 @@ func Errorf(format string, args ...any) { // ParseLevel parses a string into a log level. func ParseLevel(s string) (Level, error) { - switch strings.ToUpper(s) { + switch core.Upper(s) { case "DEBUG": return LevelDebug, nil case "INFO": diff --git a/node/bufpool.go b/node/bufpool.go index 7848214..e7244c2 100644 --- a/node/bufpool.go +++ b/node/bufpool.go @@ -2,8 +2,9 @@ package node import ( "bytes" - "encoding/json" "sync" + + core "dappco.re/go/core" ) // bufferPool provides reusable byte buffers for JSON encoding. @@ -29,27 +30,22 @@ func putBuffer(buf *bytes.Buffer) { } } -// MarshalJSON encodes a value to JSON using a pooled buffer. +// MarshalJSON encodes a value to JSON using Core's JSON primitive and then +// restores the historical no-EscapeHTML behaviour expected by the node package. // Returns a copy of the encoded bytes (safe to use after the function returns). func MarshalJSON(v any) ([]byte, error) { - buf := getBuffer() - defer putBuffer(buf) - - enc := json.NewEncoder(buf) - // Don't escape HTML characters (matches json.Marshal behavior for these use cases) - enc.SetEscapeHTML(false) - if err := enc.Encode(v); err != nil { - return nil, err + encoded := core.JSONMarshal(v) + if !encoded.OK { + return nil, encoded.Value.(error) } + data := encoded.Value.([]byte) - // json.Encoder.Encode adds a newline; remove it to match json.Marshal - data := buf.Bytes() - if len(data) > 0 && data[len(data)-1] == '\n' { - data = data[:len(data)-1] - } + data = bytes.ReplaceAll(data, []byte(`\u003c`), []byte("<")) + data = bytes.ReplaceAll(data, []byte(`\u003e`), []byte(">")) + data = bytes.ReplaceAll(data, []byte(`\u0026`), []byte("&")) - // Return a copy since the buffer will be reused - result := make([]byte, len(data)) - copy(result, data) - return result, nil + // Return a copy since callers may retain the slice after subsequent calls. + out := make([]byte, len(data)) + copy(out, data) + return out, nil } diff --git a/node/bundle.go b/node/bundle.go index e60a659..f3866c8 100644 --- a/node/bundle.go +++ b/node/bundle.go @@ -5,11 +5,8 @@ import ( "bytes" "crypto/sha256" "encoding/hex" - "encoding/json" "io" "os" - "path/filepath" - "strings" core "dappco.re/go/core" @@ -93,7 +90,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo // Create a tarball with the miner binary tarData, err := createTarball(map[string][]byte{ - filepath.Base(minerPath): minerData, + core.PathBase(minerPath): minerData, }) if err != nil { return nil, core.E("CreateMinerBundle", "failed to create tarball", err) @@ -211,7 +208,7 @@ func createTarball(files map[string][]byte) ([]byte, error) { for name, content := range files { // Create parent directories if needed - dir := filepath.Dir(name) + dir := core.PathDir(name) if dir != "." && !dirs[dir] { hdr := &tar.Header{ Name: dir + "/", @@ -226,7 +223,7 @@ func createTarball(files map[string][]byte) ([]byte, error) { // Determine file mode (executable for binaries in miners/) mode := int64(0644) - if filepath.Dir(name) == "miners" || !isJSON(content) { + if core.PathDir(name) == "miners" || !isJSON(content) { mode = 0755 } @@ -253,11 +250,16 @@ func createTarball(files map[string][]byte) ([]byte, error) { // extractTarball extracts a tar archive to a directory, returns first executable found. func extractTarball(tarData []byte, destDir string) (string, error) { // Ensure destDir is an absolute, clean path for security checks - absDestDir, err := filepath.Abs(destDir) - if err != nil { - return "", core.E("extractTarball", "failed to resolve destination directory", err) + absDestDir := destDir + if !core.PathIsAbs(absDestDir) { + cwd, err := os.Getwd() + if err != nil { + return "", core.E("extractTarball", "failed to resolve destination directory", err) + } + absDestDir = core.CleanPath(core.Concat(cwd, string(os.PathSeparator), absDestDir), string(os.PathSeparator)) + } else { + absDestDir = core.CleanPath(absDestDir, string(os.PathSeparator)) } - absDestDir = filepath.Clean(absDestDir) if err := fsEnsureDir(absDestDir); err != nil { return "", err @@ -276,24 +278,27 @@ func extractTarball(tarData []byte, destDir string) (string, error) { } // Security: Sanitize the tar entry name to prevent path traversal (Zip Slip) - cleanName := filepath.Clean(hdr.Name) + cleanName := core.CleanPath(hdr.Name, "/") // Reject absolute paths - if filepath.IsAbs(cleanName) { + if core.PathIsAbs(cleanName) { return "", core.E("extractTarball", "invalid tar entry: absolute path not allowed: "+hdr.Name, nil) } // Reject paths that escape the destination directory - if strings.HasPrefix(cleanName, ".."+string(os.PathSeparator)) || cleanName == ".." { + if core.HasPrefix(cleanName, "../") || cleanName == ".." { return "", core.E("extractTarball", "invalid tar entry: path traversal attempt: "+hdr.Name, nil) } // Build the full path and verify it's within destDir - fullPath := filepath.Join(absDestDir, cleanName) - fullPath = filepath.Clean(fullPath) + fullPath := core.CleanPath(core.Concat(absDestDir, string(os.PathSeparator), cleanName), string(os.PathSeparator)) // Final security check: ensure the path is still within destDir - if !strings.HasPrefix(fullPath, absDestDir+string(os.PathSeparator)) && fullPath != absDestDir { + allowedPrefix := core.Concat(absDestDir, string(os.PathSeparator)) + if absDestDir == string(os.PathSeparator) { + allowedPrefix = absDestDir + } + if !core.HasPrefix(fullPath, allowedPrefix) && fullPath != absDestDir { return "", core.E("extractTarball", "invalid tar entry: path escape attempt: "+hdr.Name, nil) } @@ -304,7 +309,7 @@ func extractTarball(tarData []byte, destDir string) (string, error) { } case tar.TypeReg: // Ensure parent directory exists - if err := fsEnsureDir(filepath.Dir(fullPath)); err != nil { + if err := fsEnsureDir(core.PathDir(fullPath)); err != nil { return "", err } @@ -345,16 +350,25 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // StreamBundle writes a bundle to a writer (for large transfers). func StreamBundle(bundle *Bundle, w io.Writer) error { - encoder := json.NewEncoder(w) - return encoder.Encode(bundle) + result := core.JSONMarshal(bundle) + if !result.OK { + return result.Value.(error) + } + _, err := w.Write(result.Value.([]byte)) + return err } // ReadBundle reads a bundle from a reader. func ReadBundle(r io.Reader) (*Bundle, error) { - var bundle Bundle - decoder := json.NewDecoder(r) - if err := decoder.Decode(&bundle); err != nil { + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { return nil, err } + + var bundle Bundle + result := core.JSONUnmarshal(buf.Bytes(), &bundle) + if !result.OK { + return nil, result.Value.(error) + } return &bundle, nil } diff --git a/node/controller.go b/node/controller.go index a79bbfc..cf7f21b 100644 --- a/node/controller.go +++ b/node/controller.go @@ -2,7 +2,6 @@ package node import ( "context" - "encoding/json" "sync" "time" @@ -137,7 +136,7 @@ func (c *Controller) GetRemoteStats(peerID string) (*StatsPayload, error) { } // StartRemoteMiner requests a remote peer to start a miner with a given profile. -func (c *Controller) StartRemoteMiner(peerID, minerType, profileID string, configOverride json.RawMessage) error { +func (c *Controller) StartRemoteMiner(peerID, minerType, profileID string, configOverride RawMessage) error { identity := c.node.GetIdentity() if identity == nil { return ErrIdentityNotInitialized diff --git a/node/dispatcher.go b/node/dispatcher.go index a91a0ac..32899c9 100644 --- a/node/dispatcher.go +++ b/node/dispatcher.go @@ -1,7 +1,6 @@ package node import ( - "fmt" "iter" "sync" @@ -69,7 +68,7 @@ func (d *Dispatcher) RegisterHandler(intentID byte, handler IntentHandler) { defer d.mu.Unlock() d.handlers[intentID] = handler d.log.Debug("handler registered", logging.Fields{ - "intent_id": fmt.Sprintf("0x%02X", intentID), + "intent_id": core.Sprintf("0x%02X", intentID), }) } @@ -108,7 +107,7 @@ func (d *Dispatcher) Dispatch(pkt *ueps.ParsedPacket) error { d.log.Warn("packet dropped: threat score exceeds safety threshold", logging.Fields{ "threat_score": pkt.Header.ThreatScore, "threshold": ThreatScoreThreshold, - "intent_id": fmt.Sprintf("0x%02X", pkt.Header.IntentID), + "intent_id": core.Sprintf("0x%02X", pkt.Header.IntentID), "version": pkt.Header.Version, }) return ErrThreatScoreExceeded @@ -121,7 +120,7 @@ func (d *Dispatcher) Dispatch(pkt *ueps.ParsedPacket) error { if !exists { d.log.Warn("packet dropped: unknown intent", logging.Fields{ - "intent_id": fmt.Sprintf("0x%02X", pkt.Header.IntentID), + "intent_id": core.Sprintf("0x%02X", pkt.Header.IntentID), "version": pkt.Header.Version, }) return ErrUnknownIntent @@ -134,7 +133,7 @@ func (d *Dispatcher) Dispatch(pkt *ueps.ParsedPacket) error { var ( // ErrThreatScoreExceeded is returned when a packet's ThreatScore exceeds // the safety threshold. - ErrThreatScoreExceeded = core.E("Dispatcher.Dispatch", fmt.Sprintf("packet rejected: threat score exceeds safety threshold (%d)", ThreatScoreThreshold), nil) + ErrThreatScoreExceeded = core.E("Dispatcher.Dispatch", core.Sprintf("packet rejected: threat score exceeds safety threshold (%d)", ThreatScoreThreshold), nil) // ErrUnknownIntent is returned when no handler is registered for the // packet's IntentID. diff --git a/node/identity.go b/node/identity.go index d745c5c..4e4f255 100644 --- a/node/identity.go +++ b/node/identity.go @@ -7,8 +7,6 @@ import ( "crypto/rand" "crypto/sha256" "encoding/hex" - "encoding/json" - "path/filepath" "sync" "time" @@ -202,7 +200,7 @@ func (n *NodeManager) DeriveSharedSecret(peerPubKeyBase64 string) ([]byte, error // savePrivateKey saves the private key to disk with restricted permissions. func (n *NodeManager) savePrivateKey() error { // Ensure directory exists - dir := filepath.Dir(n.keyPath) + dir := core.PathDir(n.keyPath) if err := fsEnsureDir(dir); err != nil { return core.E("NodeManager.savePrivateKey", "failed to create key directory", err) } @@ -218,15 +216,16 @@ func (n *NodeManager) savePrivateKey() error { // saveIdentity saves the public identity to the config file. func (n *NodeManager) saveIdentity() error { // Ensure directory exists - dir := filepath.Dir(n.configPath) + dir := core.PathDir(n.configPath) if err := fsEnsureDir(dir); err != nil { return core.E("NodeManager.saveIdentity", "failed to create config directory", err) } - data, err := json.MarshalIndent(n.identity, "", " ") - if err != nil { - return core.E("NodeManager.saveIdentity", "failed to marshal identity", err) + result := core.JSONMarshal(n.identity) + if !result.OK { + return core.E("NodeManager.saveIdentity", "failed to marshal identity", result.Value.(error)) } + data := result.Value.([]byte) if err := fsWrite(n.configPath, string(data)); err != nil { return core.E("NodeManager.saveIdentity", "failed to write identity", err) @@ -244,8 +243,9 @@ func (n *NodeManager) loadIdentity() error { } var identity NodeIdentity - if err := json.Unmarshal([]byte(content), &identity); err != nil { - return core.E("NodeManager.loadIdentity", "failed to unmarshal identity", err) + result := core.JSONUnmarshalString(content, &identity) + if !result.OK { + return core.E("NodeManager.loadIdentity", "failed to unmarshal identity", result.Value.(error)) } // Load private key diff --git a/node/levin/storage.go b/node/levin/storage.go index d3d2499..f8896f4 100644 --- a/node/levin/storage.go +++ b/node/levin/storage.go @@ -5,7 +5,6 @@ package levin import ( "encoding/binary" - "fmt" "maps" "math" "slices" @@ -394,7 +393,7 @@ func encodeValue(buf []byte, v Value) ([]byte, error) { return encodeSection(buf, v.objectVal) default: - return nil, core.E("levin.encodeValue", fmt.Sprintf("unknown type tag: 0x%02x", v.Type), ErrStorageUnknownType) + return nil, core.E("levin.encodeValue", core.Sprintf("unknown type tag: 0x%02x", v.Type), ErrStorageUnknownType) } } @@ -441,7 +440,7 @@ func encodeArray(buf []byte, v Value) ([]byte, error) { return buf, nil default: - return nil, core.E("levin.encodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) + return nil, core.E("levin.encodeArray", core.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) } } @@ -613,7 +612,7 @@ func decodeValue(buf []byte, tag uint8) (Value, int, error) { return Value{Type: TypeObject, objectVal: sec}, consumed, nil default: - return Value{}, 0, core.E("levin.decodeValue", fmt.Sprintf("unknown type tag: 0x%02x", tag), ErrStorageUnknownType) + return Value{}, 0, core.E("levin.decodeValue", core.Sprintf("unknown type tag: 0x%02x", tag), ErrStorageUnknownType) } } @@ -681,6 +680,6 @@ func decodeArray(buf []byte, tag uint8) (Value, int, error) { return Value{Type: tag, objectArray: arr}, off, nil default: - return Value{}, 0, core.E("levin.decodeArray", fmt.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) + return Value{}, 0, core.E("levin.decodeArray", core.Sprintf("unknown type tag: array of 0x%02x", elemType), ErrStorageUnknownType) } } diff --git a/node/message.go b/node/message.go index d4b2b6e..b90ef0b 100644 --- a/node/message.go +++ b/node/message.go @@ -5,6 +5,7 @@ import ( "slices" "time" + core "dappco.re/go/core" "github.com/google/uuid" ) @@ -20,6 +21,9 @@ const ( // Used for version negotiation during handshake. var SupportedProtocolVersions = []string{"1.0"} +// RawMessage is the message payload byte slice used for deferred JSON decoding. +type RawMessage = json.RawMessage + // IsProtocolVersionSupported checks if a given version is supported. func IsProtocolVersionSupported(version string) bool { return slices.Contains(SupportedProtocolVersions, version) @@ -57,18 +61,18 @@ const ( // Message represents a P2P message between nodes. type Message struct { - ID string `json:"id"` // UUID - Type MessageType `json:"type"` - From string `json:"from"` // Sender node ID - To string `json:"to"` // Recipient node ID (empty for broadcast) - Timestamp time.Time `json:"ts"` - Payload json.RawMessage `json:"payload"` - ReplyTo string `json:"replyTo,omitempty"` // ID of message being replied to + ID string `json:"id"` // UUID + Type MessageType `json:"type"` + From string `json:"from"` // Sender node ID + To string `json:"to"` // Recipient node ID (empty for broadcast) + Timestamp time.Time `json:"ts"` + Payload RawMessage `json:"payload"` + ReplyTo string `json:"replyTo,omitempty"` // ID of message being replied to } // NewMessage creates a new message with a generated ID and timestamp. func NewMessage(msgType MessageType, from, to string, payload any) (*Message, error) { - var payloadBytes json.RawMessage + var payloadBytes RawMessage if payload != nil { data, err := MarshalJSON(payload) if err != nil { @@ -102,7 +106,11 @@ func (m *Message) ParsePayload(v any) error { if m.Payload == nil { return nil } - return json.Unmarshal(m.Payload, v) + result := core.JSONUnmarshal(m.Payload, v) + if !result.OK { + return result.Value.(error) + } + return nil } // --- Payload Types --- @@ -135,9 +143,9 @@ type PongPayload struct { // StartMinerPayload requests starting a miner. type StartMinerPayload struct { - MinerType string `json:"minerType"` // Required: miner type (e.g., "xmrig", "tt-miner") - ProfileID string `json:"profileId,omitempty"` - Config json.RawMessage `json:"config,omitempty"` // Override profile config + MinerType string `json:"minerType"` // Required: miner type (e.g., "xmrig", "tt-miner") + ProfileID string `json:"profileId,omitempty"` + Config RawMessage `json:"config,omitempty"` // Override profile config } // StopMinerPayload requests stopping a miner. diff --git a/node/peer.go b/node/peer.go index e876cd6..cedc741 100644 --- a/node/peer.go +++ b/node/peer.go @@ -1,10 +1,8 @@ package node import ( - "encoding/json" "iter" "maps" - "path/filepath" "regexp" "slices" "sync" @@ -654,7 +652,7 @@ func (r *PeerRegistry) scheduleSave() { // Must be called with r.mu held (at least RLock). func (r *PeerRegistry) saveNow() error { // Ensure directory exists - dir := filepath.Dir(r.path) + dir := core.PathDir(r.path) if err := fsEnsureDir(dir); err != nil { return core.E("PeerRegistry.saveNow", "failed to create peers directory", err) } @@ -662,10 +660,11 @@ func (r *PeerRegistry) saveNow() error { // Convert to slice for JSON peers := slices.Collect(maps.Values(r.peers)) - data, err := json.MarshalIndent(peers, "", " ") - if err != nil { - return core.E("PeerRegistry.saveNow", "failed to marshal peers", err) + result := core.JSONMarshal(peers) + if !result.OK { + return core.E("PeerRegistry.saveNow", "failed to marshal peers", result.Value.(error)) } + data := result.Value.([]byte) // Use atomic write pattern: write to temp file, then rename tmpPath := r.path + ".tmp" @@ -723,8 +722,9 @@ func (r *PeerRegistry) load() error { } var peers []*Peer - if err := json.Unmarshal([]byte(content), &peers); err != nil { - return core.E("PeerRegistry.load", "failed to unmarshal peers", err) + result := core.JSONUnmarshalString(content, &peers) + if !result.OK { + return core.E("PeerRegistry.load", "failed to unmarshal peers", result.Value.(error)) } r.peers = make(map[string]*Peer) diff --git a/node/protocol.go b/node/protocol.go index f93b2f2..19b55dd 100644 --- a/node/protocol.go +++ b/node/protocol.go @@ -1,8 +1,6 @@ package node import ( - "fmt" - core "dappco.re/go/core" ) @@ -13,7 +11,7 @@ type ProtocolError struct { } func (e *ProtocolError) Error() string { - return fmt.Sprintf("remote error (%d): %s", e.Code, e.Message) + return core.Sprintf("remote error (%d): %s", e.Code, e.Message) } // ResponseHandler provides helpers for handling protocol responses. diff --git a/node/transport.go b/node/transport.go index ceb1cb1..44bcc36 100644 --- a/node/transport.go +++ b/node/transport.go @@ -4,8 +4,6 @@ import ( "context" "crypto/tls" "encoding/base64" - "encoding/json" - "fmt" "iter" "maps" "net/http" @@ -457,7 +455,7 @@ func (t *Transport) handleWSUpgrade(w http.ResponseWriter, r *http.Request) { // Decode handshake message (not encrypted yet, contains public key) var msg Message - if err := json.Unmarshal(data, &msg); err != nil { + if result := core.JSONUnmarshal(data, &msg); !result.OK { conn.Close() return } @@ -485,7 +483,7 @@ func (t *Transport) handleWSUpgrade(w http.ResponseWriter, r *http.Request) { rejectPayload := HandshakeAckPayload{ Identity: *identity, Accepted: false, - Reason: fmt.Sprintf("incompatible protocol version %s, supported: %v", payload.Version, SupportedProtocolVersions), + Reason: core.Sprintf("incompatible protocol version %s, supported: %v", payload.Version, SupportedProtocolVersions), } rejectMsg, _ := NewMessage(MsgHandshakeAck, identity.ID, payload.Identity.ID, rejectPayload) if rejectData, err := MarshalJSON(rejectMsg); err == nil { @@ -660,8 +658,8 @@ func (t *Transport) performHandshake(pc *PeerConnection) error { } var ackMsg Message - if err := json.Unmarshal(ackData, &ackMsg); err != nil { - return core.E("Transport.performHandshake", "unmarshal handshake ack", err) + if result := core.JSONUnmarshal(ackData, &ackMsg); !result.OK { + return core.E("Transport.performHandshake", "unmarshal handshake ack", result.Value.(error)) } if ackMsg.Type != MsgHandshakeAck { @@ -932,8 +930,8 @@ func (t *Transport) decryptMessage(data []byte, sharedSecret []byte) (*Message, // Parse message from JSON var msg Message - if err := json.Unmarshal([]byte(smsgMsg.Body), &msg); err != nil { - return nil, err + if result := core.JSONUnmarshalString(smsgMsg.Body, &msg); !result.OK { + return nil, result.Value.(error) } return &msg, nil diff --git a/node/worker.go b/node/worker.go index 1ea18ea..c5b504c 100644 --- a/node/worker.go +++ b/node/worker.go @@ -2,8 +2,6 @@ package node import ( "encoding/base64" - "encoding/json" - "path/filepath" "time" core "dappco.re/go/core" @@ -331,8 +329,8 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err // Unmarshal into interface{} to pass to ProfileManager var profile any - if err := json.Unmarshal(profileData, &profile); err != nil { - return nil, core.E("Worker.handleDeploy", "invalid profile data JSON", err) + if result := core.JSONUnmarshal(profileData, &profile); !result.OK { + return nil, core.E("Worker.handleDeploy", "invalid profile data JSON", result.Value.(error)) } if err := w.profileManager.SaveProfile(profile); err != nil { @@ -353,8 +351,8 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err case BundleMiner, BundleFull: // Determine installation directory // We use w.DataDir/lethean-desktop/miners/ - minersDir := filepath.Join(w.DataDir, "lethean-desktop", "miners") - installDir := filepath.Join(minersDir, payload.Name) + minersDir := core.JoinPath(w.DataDir, "lethean-desktop", "miners") + installDir := core.JoinPath(minersDir, payload.Name) logging.Info("deploying miner bundle", logging.Fields{ "name": payload.Name, @@ -371,8 +369,8 @@ func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, err // If the bundle contained a profile config, save it if len(profileData) > 0 && w.profileManager != nil { var profile any - if err := json.Unmarshal(profileData, &profile); err != nil { - logging.Warn("failed to parse profile from miner bundle", logging.Fields{"error": err}) + if result := core.JSONUnmarshal(profileData, &profile); !result.OK { + logging.Warn("failed to parse profile from miner bundle", logging.Fields{"error": result.Value.(error)}) } else { if err := w.profileManager.SaveProfile(profile); err != nil { logging.Warn("failed to save profile from miner bundle", logging.Fields{"error": err}) -- 2.45.3