Skip to content

Commit

Permalink
automatically fetch right binary
Browse files Browse the repository at this point in the history
  • Loading branch information
rrrliu committed Jan 23, 2025
1 parent e276983 commit d2d50d0
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 33 deletions.
5 changes: 5 additions & 0 deletions go/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ func main() {
log.Fatal("Missing PRIVATE_KEY environment variable.")
}

if err := pod.Init(); err != nil {
log.Fatal("Failed to initialize pod: ", err)
}

log.Println("Loaded PRIVATE_KEY...")
log.Println("Initialized pod service...")
log.Println("Starting server on port 8080")

http.HandleFunc("/", handleRoot)
Expand Down
6 changes: 6 additions & 0 deletions go/pod/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package pod

func Init() error {
_, err := getOrDownloadPodWorker()
return err
}
174 changes: 173 additions & 1 deletion go/pod/pod.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package pod

import (
"archive/tar"
"compress/gzip"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"
)

type Pod struct {
Expand All @@ -20,6 +27,8 @@ type podCommandRequest struct {
Entries map[string]interface{} `json:"entries"`
}

const STABLE_TAG = "v0.2.4"

func validatePrivateKeyHex(pk string) error {
if len(pk) != 64 {
return fmt.Errorf("private key must be 64 hex characters (32 bytes), got length %d", len(pk))
Expand All @@ -34,13 +43,22 @@ func validatePrivateKeyHex(pk string) error {
return nil
}

var downloadOnce sync.Once
var downloadErr error
var podWorkerPath string // Final path to the extracted pod_worker binary

func dispatchRustCommand(req podCommandRequest) (*Pod, string, error) {
binPath, err := getOrDownloadPodWorker()
if err != nil {
return nil, "", err
}

reqBytes, err := json.Marshal(req)
if err != nil {
return nil, "", fmt.Errorf("failed to marshal request: %w", err)
}

cmd := exec.Command("./pod_worker")
cmd := exec.Command(binPath)
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, "", fmt.Errorf("failed to get stdin: %w", err)
Expand Down Expand Up @@ -75,6 +93,7 @@ func dispatchRustCommand(req podCommandRequest) (*Pod, string, error) {
if err != nil {
return nil, "", fmt.Errorf("failed re-marshal: %w", err)
}

var pod Pod
if err := json.Unmarshal(remarshaled, &pod); err != nil {
return nil, "", fmt.Errorf("failed final unmarshal Pod: %w", err)
Expand All @@ -87,3 +106,156 @@ func dispatchRustCommand(req podCommandRequest) (*Pod, string, error) {

return &pod, string(jsonPodBytes), nil
}

func getOrDownloadPodWorker() (string, error) {
downloadOnce.Do(func() {
// 1. Figure out the correct artifact name based on runtime.GOOS/GOARCH
osName, err := resolveArtifactName(runtime.GOOS, runtime.GOARCH)
if err != nil {
downloadErr = err
return
}

tarURL := fmt.Sprintf(
"https://github.com/0xPARC/parcnet/releases/download/%s/pod_worker-%s.tar.gz",
STABLE_TAG,
osName,
)

tmpDir, err := os.MkdirTemp("", "pod_worker_bin")
if err != nil {
downloadErr = fmt.Errorf("failed to create temp dir: %w", err)
return
}

tarPath := filepath.Join(tmpDir, "pod_worker.tar.gz")

if err := downloadFile(tarURL, tarPath); err != nil {
downloadErr = fmt.Errorf("failed to download artifact: %w", err)
return
}

if err := extractTarGz(tarPath, tmpDir); err != nil {
downloadErr = fmt.Errorf("failed to extract pod_worker tarball: %w", err)
return
}

binName := "pod_worker"
if runtime.GOOS == "windows" {
binName = "pod_worker.exe"
}
finalBin := filepath.Join(tmpDir, binName)

if err := os.Chmod(finalBin, 0755); err != nil {
downloadErr = fmt.Errorf("failed to chmod pod_worker: %w", err)
return
}

podWorkerPath = finalBin
})

if downloadErr != nil {
return "", downloadErr
}
return podWorkerPath, nil
}

// Maps (GOOS, GOARCH) to the "os-name" used in the final artifact filename.
// E.g., if GOOS="linux" and GOARCH="amd64", we get "Linux-x86_64".
func resolveArtifactName(goos, goarch string) (string, error) {
combos := map[[2]string]string{
{"freebsd", "amd64"}: "FreeBSD-x86_64",
{"linux", "amd64"}: "Linux-gnu-x86_64",
{"linux", "arm64"}: "Linux-gnu-aarch64",
{"linux", "arm"}: "Linux-gnu-arm",
{"linux", "386"}: "Linux-gnu-i686",
{"linux", "ppc"}: "Linux-gnu-powerpc",
{"linux", "ppc64"}: "Linux-gnu-powerpc64",
{"linux", "ppc64le"}: "Linux-gnu-powerpc64le",
{"linux", "riscv64"}: "Linux-gnu-riscv64",
{"linux", "s390x"}: "Linux-gnu-s390x",
{"windows", "arm64"}: "Windows-msvc-aarch64",
{"windows", "386"}: "Windows-msvc-i686",
{"windows", "amd64"}: "Windows-msvc-x86_64",
{"darwin", "amd64"}: "macOS-x86_64",
{"darwin", "arm64"}: "macOS-arm64",
}
key := [2]string{goos, goarch}
if val, ok := combos[key]; ok {
return val, nil
}
return "", fmt.Errorf("unsupported GOOS/GOARCH combination: %s/%s", goos, goarch)
}

func downloadFile(url, destination string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status code %d when downloading %s", resp.StatusCode, url)
}

out, err := os.Create(destination)
if err != nil {
return err
}
defer out.Close()

_, err = io.Copy(out, resp.Body)
return err
}

func extractTarGz(src, dest string) error {
f, err := os.Open(src)
if err != nil {
return err
}
defer f.Close()

gz, err := gzip.NewReader(f)
if err != nil {
return err
}
defer gz.Close()

tr := tar.NewReader(gz)

for {
hdr, err := tr.Next()
if err == io.EOF {
// no more files
break
}
if err != nil {
return err
}

path := filepath.Join(dest, hdr.Name)
switch hdr.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
outFile, err := os.Create(path)
if err != nil {
return err
}
if _, err := io.Copy(outFile, tr); err != nil {
outFile.Close()
return err
}
outFile.Close()
default:
// skip special file types
}
}

return nil
}
Binary file removed go/pod/pod_worker
Binary file not shown.
50 changes: 18 additions & 32 deletions go/pod/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io"
"os/exec"
)

// noPadB64 matches Rust's base64::STANDARD_NO_PAD
var noPadB64 = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").WithPadding(base64.NoPadding)

// Verify uses dispatchRustCommand to check the Pod's signature.
func (p *Pod) Verify() (bool, error) {
podCopy := *p

if len(podCopy.SignerPublicKey) == 64 {
rawSPK, err := hex.DecodeString(podCopy.SignerPublicKey)
if err != nil {
return false, fmt.Errorf("failed decode signerPublicKey: %w", err)
return false, fmt.Errorf("failed to decode signerPublicKey: %w", err)
}
podCopy.SignerPublicKey = noPadB64.EncodeToString(rawSPK)
}

if len(podCopy.Signature) == 128 {
rawSig, err := hex.DecodeString(podCopy.Signature)
if err != nil {
return false, fmt.Errorf("failed decode signature hex: %w", err)
return false, fmt.Errorf("failed to decode signature hex: %w", err)
}
podCopy.Signature = noPadB64.EncodeToString(rawSig)
}
Expand All @@ -35,45 +35,31 @@ func (p *Pod) Verify() (bool, error) {
return false, fmt.Errorf("marshal Pod for verify: %w", err)
}

reqMap := map[string]interface{}{
"cmd": "verify",
"pod_json": string(podBytes),
}
reqBytes, err := json.Marshal(reqMap)
if err != nil {
return false, fmt.Errorf("marshal verify request: %w", err)
}

c := exec.Command("./pod_worker")
stdin, _ := c.StdinPipe()
stdout, _ := c.StdoutPipe()

if err := c.Start(); err != nil {
return false, fmt.Errorf("start Rust process: %w", err)
}
if _, err := stdin.Write(reqBytes); err != nil {
return false, fmt.Errorf("writing verify req: %w", err)
req := podCommandRequest{
Cmd: "verify",
Entries: map[string]interface{}{
// The Rust side presumably expects a "pod_json" key containing the Pod in JSON form
"pod_json": string(podBytes),
},
// No PrivateKey needed for verify, so we can leave it empty
}
stdin.Close()

outBytes, err := io.ReadAll(stdout)
if werr := c.Wait(); werr != nil {
return false, nil
}
_, outJSON, err := dispatchRustCommand(req)
if err != nil {
return false, fmt.Errorf("reading verify stdout: %w", err)
return false, fmt.Errorf("failed to dispatch verify command: %w", err)
}

var vr struct {
Verified bool `json:"verified"`
Error string `json:"error,omitempty"`
}
if err := json.Unmarshal(outBytes, &vr); err != nil {
return false, fmt.Errorf("unmarshal verify resp: %w\nOutput: %s", err, string(outBytes))
if err := json.Unmarshal([]byte(outJSON), &vr); err != nil {
return false, fmt.Errorf("unmarshal verify response: %w\nOutput: %s", err, outJSON)
}

if vr.Error != "" {
fmt.Println("[WARN] verify error:", vr.Error)
return false, nil
return false, fmt.Errorf("verify failed: %s", vr.Error)
}

return vr.Verified, nil
}

0 comments on commit d2d50d0

Please sign in to comment.