☁️
Zus Docs
  • About Züs
  • System
    • Providers and Services
      • Miner
      • Sharder
      • Blobber
      • Validator
      • Authorizer
      • Node Locator (0DNS)
    • Storage
      • Architecture and Data Management
      • Protocol
        • Allocations
        • Reference Objects
        • Challenges
        • Write Markers
          • Chain Hashing
          • Two Commit
        • Blobber Repair Protocol
      • ZS3 Server
        • Backup, Recovery and Replication
        • Encryption and Compression
        • S3FS Setup and Usage
        • Backup & Recovery with Restic on Blimp + ZS3 Server
        • Backup & Recovery with Veeam on Blimp + ZS3 Server
      • File Operations
        • Upload
        • Download
        • File Sharing
        • Partial Error Recovery
        • Streaming
        • Rent a Blobber
    • Smart Contracts
      • Storage S.C.
      • Miner S.C.
      • ZCN S.C.
      • ERC-20 S.C.s
      • Bridge Protocol
    • Blockchain & Consensus
      • Entities
    • User Authentication and Wallet Management System
      • OKTA Integration
      • Key Management System (KMS)
  • APIs
    • 0DNS API
    • JS API
    • Mobile API
  • CLIs
    • Storage CLI
      • Quickstart
      • Configuring the tool
    • Wallet CLI
      • Wallet Configuration
      • Quickstart
      • Configuring the tool
  • SDKs
    • Go SDK
      • GO SDK Microservices
    • JS SDK
  • Tokenomics
    • Staking
    • Reward & Penalty
  • ✨Züs Apps
    • 🗝️Vult
      • Getting Started
        • Web
        • Mobile
      • Vult AI
        • Batch Processing
        • Memory Retention
        • Technical Implementation
        • Architecture Overview
      • Login / Register
      • File Management Pages
      • File Sharing
      • Storage Management Dashboard
      • Storage Maintenance and Troubleshooting
      • Züs Subscription
      • Wallet Management
      • Refer a friend
      • Settings
    • 🏗️Blimp
      • Getting Started
      • Login / Register
      • Configure Storage
        • Create Standard Storage Allocation
        • Create Enterprise Allocation
        • Create S3 Server Allocation
        • Create Cloud Migration Allocation
        • Allocation Maintenance and Troubleshooting
      • File Management Pages
      • File Sharing
      • Manage Allocations
      • Upgrade Storage
      • Blimp Vault
      • Refer a friend
      • Settings
      • Launching ZS3 Server
      • Using CLI to backup files into Blimp + ZS3 Server
    • 🏠Chimney
      • Getting Started
      • Login / Register
      • Create New Deployment
      • Manage Your Deployments
      • Homepage
      • Staking Dashboard
      • Rank Dashboard
      • Monitor Dashboard
      • Stats Dashboard
      • Logs Dashboard
      • Wallet Dashboard
      • Operations on your Deployments
      • Restricted Blobbers
      • Settings
        • Manage Profile
        • Wallet Settings
        • Update Blobber Settings
        • Update Blobber Version
        • Refer a friend
        • Help
    • 🌐Atlus
      • Getting Started
      • Home page
      • Service Providers Page
      • Charts Page
        • Market Charts
        • Network Charts
        • Storage Charts
      • Blockchain Page
      • Server Map Page
      • Storage Explainer Page
      • Details Pages
        • Block Details Page
        • Transaction Details Page
        • Wallet Details Page
        • Miner Details Page
        • Sharder Details Page
        • Blobber Details Page
        • Validator Details Page
        • Authorizer Details Page
        • Allocation Details Page
      • Appendix: Common Components
    • ⚡Bolt
      • Getting Started
        • Web
        • Mobile
      • Login / Register
      • Sign In with external wallet
      • Staking Dashboard
      • Staking/Unstaking a provider
      • Claiming Rewards
      • Send/Receive ZCN tokens
      • Buy ZCN
      • Deposit/Withdraw ZCN tokens
      • Activity Dashboard
      • Refer a friend
      • Settings
  • Releases
    • Hardfork
Powered by GitBook
On this page
  • 1. Prerequisites Setup
  • 2. Project File Structure
  • 3. Code Your main.go
  • 4. Build Your Handlers
  • 5. Run Your Server
  • 6. Test With curl
  1. SDKs
  2. Go SDK

GO SDK Microservices

PreviousGo SDKNextTokenomics

Last updated 21 days ago

This guide provides a complete walkthrough for setting up and running a Go-based microservice that interacts with the Züs decentralized storage network.

1. Prerequisites Setup

Before you begin:

Install Go

install . Verify installation:

go version

Get a Züs Wallet

You’ll need a .json wallet file.

It is typically located at:

~/.zcn/wallet.json

Alternatively, you can store the wallet JSON file in any location of your choice. To retrieve your wallet information, you can use the recover wallet command from .

Or you can use any location to create the json file. You can retrieve your wallet info by using from the zwalletcli.

Below is sample wallet structure:

{
  "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "client_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "keys": [
    {
      "public_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "private_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  ],
  "mnemonics": "xxxx xxxx xxxx xxxxx",
  "version": "1.0",
  "date_created": "2023-05-03T12:44:46+05:30",
  "nonce": 0
}

Create config.json

Create a file named config.json in your project root with the following structure:

{
  "wallet_json": "/Users/yourusername/.zcn/wallet.json",
  "block_worker": "https://mainnet.zus.network/dns",
  "chain_id": "0af33bc5402b5f13e349347db8b88b6543157baadb7c9265ef0a8f87429e4a44",
  "signature_scheme": "bls0chain",
  "min_submit": 50,
  "min_confirmation": 50,
  "confirmation_chain_length": 3,
  "sharder_consensous": 3
}

2. Project File Structure

Your project should be structured as follows:

/project-root
├── config.json
├── main.go
└── handlers/
    ├── download_handler.go
    ├── memfile_handler.go
    ├── sdk_handler.go
    └── upload_handler.go

3. Code Your main.go

package main

import (
	"log"
	"net/http"

	"go_zus_blog/handlers" // Replace with your module name if different
	"github.com/gorilla/mux"
)

func main() {
	router := mux.NewRouter()

	router.HandleFunc("/init-sdk", handlers.InitSDKHandler).Methods("POST")
	router.HandleFunc("/upload-file", handlers.UploadFileHandler).Methods("POST")
	router.HandleFunc("/download-file", handlers.DownloadFileHandler).Methods("POST")

	log.Println("Go microservice running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", router))
}

Install Gorilla Mux:

go get -u github.com/gorilla/mux

In case you see an error while importing go_zus_handlers, run this command:

go mod init go_zus_blog

4. Build Your Handlers

sdk_handler.go

package handlers

import (
	"encoding/json"
	"log"
	"net/http"
	"os"

	"github.com/0chain/gosdk/core/client"
)

type Config struct {
	WalletJSON              string `json:"wallet_json"`
	BlockWorker             string `json:"block_worker"`
	ChainID                 string `json:"chain_id"`
	SignatureScheme         string `json:"signature_scheme"`
	MinSubmit               int    `json:"min_submit"`
	MinConfirmation         int    `json:"min_confirmation"`
	ConfirmationChainLength int    `json:"confirmation_chain_length"`
	SharderConsensous       int    `json:"sharder_consensous"`
}

func InitSDKHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	file, err := os.ReadFile("./config.json")
	if err != nil {
		http.Error(w, "Failed to read config.json: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var config Config
	err = json.Unmarshal(file, &config)
	if err != nil {
		http.Error(w, "Invalid config.json: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if config.WalletJSON == "" {
		http.Error(w, "Missing wallet JSON path", http.StatusBadRequest)
		return
	}

	if _, err := os.Stat(config.WalletJSON); os.IsNotExist(err) {
		http.Error(w, "Wallet JSON file not found: "+config.WalletJSON, http.StatusBadRequest)
		return
	}

	walletContent, err := os.ReadFile(config.WalletJSON)
	if err != nil {
		http.Error(w, "Failed to read wallet JSON: "+err.Error(), http.StatusInternalServerError)
		return
	}

	err = client.InitSDK(
		string(walletContent),
		config.BlockWorker,
		config.ChainID,
		config.SignatureScheme,
		0,
		true,
		0,
		config.MinSubmit,
		config.MinConfirmation,
		config.ConfirmationChainLength,
		config.SharderConsensous,
	)
	if err != nil {
		http.Error(w, "SDK initialization failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"message": "SDK initialized successfully"}`))
}

memfile_handler.go

package handlers

import (
	"bytes"
	"errors"
	"io"
	"os"
	"sync"
	"time"
)

type MemFile struct {
	buffer  *bytes.Buffer
	mu      sync.Mutex
	name    string
	modTime time.Time
	mode    os.FileMode
}

func NewMemFile(name string) *MemFile {
	return &MemFile{
		buffer:  new(bytes.Buffer),
		name:    name,
		modTime: time.Now(),
		mode:    0644,
	}
}

func (m *MemFile) Read(p []byte) (int, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.buffer.Read(p)
}

func (m *MemFile) Write(p []byte) (int, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.buffer.Write(p)
}

func (m *MemFile) Seek(offset int64, whence int) (int64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	switch whence {
	case io.SeekStart:
		m.buffer = bytes.NewBuffer(m.buffer.Bytes()[offset:])
		return offset, nil
	case io.SeekEnd:
		m.buffer = bytes.NewBuffer(m.buffer.Bytes()[len(m.buffer.Bytes())+int(offset):])
		return int64(len(m.buffer.Bytes())), nil
	case io.SeekCurrent:
		return 0, errors.New("unsupported seek method")
	}
	return 0, errors.New("invalid seek whence")
}

func (m *MemFile) Close() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.buffer.Reset()
	return nil
}

func (m *MemFile) Stat() (os.FileInfo, error) {
	return &memFileInfo{
		name:    m.name,
		size:    int64(m.buffer.Len()),
		modTime: m.modTime,
		mode:    m.mode,
	}, nil
}

func (m *MemFile) Sync() error {
	return nil
}

func (m *MemFile) ToBytes() []byte {
	return m.buffer.Bytes()
}

type memFileInfo struct {
	name    string
	size    int64
	modTime time.Time
	mode    os.FileMode
}

func (fi *memFileInfo) Name() string       { return fi.name }
func (fi *memFileInfo) Size() int64        { return fi.size }
func (fi *memFileInfo) Mode() os.FileMode  { return fi.mode }
func (fi *memFileInfo) ModTime() time.Time { return fi.modTime }
func (fi *memFileInfo) IsDir() bool        { return false }
func (fi *memFileInfo) Sys() interface{}   { return nil }

upload_handler.go

package handlers

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/0chain/gosdk/constants"
	"github.com/0chain/gosdk/zboxcore/fileref"
	"github.com/0chain/gosdk/zboxcore/sdk"
)

type ShortURLResponse struct {
	AuthTicket string `json:"auth_ticket"`
}

type UploadRequest struct {
	AllocationID     string `json:"allocation_id"`
	LocalPath        string `json:"local_path"`
	Encrypt          bool   `json:"encrypt"`
	WebStreaming     bool   `json:"web_streaming"`
	IsMemoryDownload bool   `json:"download_file"`
}

func UploadFileHandler(w http.ResponseWriter, r *http.Request) {
	var req UploadRequest

	// Decode the incoming JSON payload
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		log.Printf("Invalid request payload: %v", err)
		http.Error(w, "Invalid request payload", http.StatusBadRequest)
		return
	}

	log.Printf("Received upload request: %+v", req)

	// Validate required fields
	if req.AllocationID == "" || req.LocalPath == "" {
		log.Printf("Missing required fields: %+v", req)
		http.Error(w, "AllocationID and LocalPath are required", http.StatusBadRequest)
		return
	}

	// Check if the file exists locally
	if _, err := os.Stat(req.LocalPath); os.IsNotExist(err) {
		log.Printf("File not found at: %s", req.LocalPath)
		http.Error(w, "File not found at "+req.LocalPath, http.StatusBadRequest)
		return
	}

	// Generate the remote path using the file name
	fileName := filepath.Base(req.LocalPath)
	remotePath := "/" + fileName
	log.Printf("Generated remote path: %s", remotePath)

	// Get the allocation object using the provided AllocationID
	allocation, err := sdk.GetAllocation(req.AllocationID)
	if err != nil {
		log.Printf("Failed to get allocation: %v", err)
		http.Error(w, "Failed to get allocation: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Open the local file
	fileReader, err := os.Open(req.LocalPath)
	if err != nil {
		log.Printf("Failed to open file: %v", err)
		http.Error(w, "Failed to open file: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer fileReader.Close()

	// Get file size
	fileInfo, err := fileReader.Stat()
	if err != nil {
		log.Printf("Failed to get file info: %v", err)
		http.Error(w, "Failed to get file info: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Set up the operation for the upload
	operations := []sdk.OperationRequest{
		{
			OperationType: constants.FileOperationInsert,
			FileMeta: sdk.FileMeta{
				Path:       req.LocalPath,
				RemotePath: remotePath,
				RemoteName: fileName,
				ActualSize: fileInfo.Size(),
			},
			FileReader:     fileReader,
			IsWebstreaming: req.WebStreaming,
			IsRepair:       false,
			Opts: func() []sdk.ChunkedUploadOption {
				// Include encryption option only if Encrypt is true
				if req.Encrypt {
					return []sdk.ChunkedUploadOption{
						sdk.WithEncrypt(req.Encrypt),
					}
				}
				return nil
			}(),
		},
	}

	// Perform the multi-operation
	log.Println("Starting DoMultiOperation...")
	err = allocation.DoMultiOperation(operations)
	if err != nil {
		log.Printf("Multi-operation failed: %v", err)
		http.Error(w, "Upload failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	log.Println("File uploaded successfully to Züs network")

	// Generate Auth Ticket
	//authTicket, err := allocation.GetAuthTicketForShare(remotePath, req.FileName, fileref.FILE, "")
	authTicket, err := allocation.GetAuthTicketForShare(remotePath, fileName, fileref.FILE, "")
	if err != nil {
		log.Printf("Failed to generate auth ticket: %v", err)
		http.Error(w, "Failed to generate auth ticket", http.StatusInternalServerError)
		return
	}
	log.Printf("Auth Ticket: %+v", authTicket)

	// Return short URL
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(ShortURLResponse{
		AuthTicket: authTicket,
	})
}

download_handler.go

package handlers

import (
"encoding/json"
"log"
"net/http"

//"path/filepath"
//"strings"

"github.com/0chain/gosdk/zboxcore/fileref"
"github.com/0chain/gosdk/zboxcore/sdk"
)

type DownloadRequest struct {
AllocationID string `json:"allocation_id"`
FileName     string `json:"filename"`
AuthTicket   string `json:"auth_ticket"`
LocalPath    string `json:"local_path"`
}

func DownloadFileHandler(w http.ResponseWriter, r *http.Request) {
var req DownloadRequest

// Decode the incoming JSON payload
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
log.Printf("Invalid request payload: %v", err)
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
log.Printf("Received download request: %+v", req)

allocation, err := sdk.GetAllocation(req.AllocationID)
if err != nil {
http.Error(w, "Failed to get allocation", http.StatusInternalServerError)
return
}

// Generate the remote path using the file name
remotePath := "/" + req.FileName
log.Printf("Generated remote path: %s", remotePath)

remoteLookupHash := fileref.GetReferenceLookup(req.AllocationID, remotePath)
log.Printf("Generated remoteLookupHash: %s", remoteLookupHash)

// Create in-memory file
//memFile := NewMemFile(remotePath)

// Download file
err = allocation.DownloadFromAuthTicket(
req.LocalPath, req.AuthTicket, remoteLookupHash, remotePath, false, nil, true,
)
if err != nil {
http.Error(w, "File download failed", http.StatusInternalServerError)
return
}

5. Run Your Server

go run main.go

Expected output:

Go microservice running on port 8080

6. Test With curl

Initialize SDK

curl -X POST http://localhost:8080/init-sdk

Upload a File

curl -X POST http://localhost:8080/upload-file \
  -H "Content-Type: application/json" \
  -d '{
    "allocation_id": "your_allocation_id",
    "local_path": "/Users/you/Downloads/file.txt",
    "encrypt": false,
    "web_streaming": false,
    "download_file": false
  }'

Download a File

curl -X POST http://localhost:8080/download-file \
  -H "Content-Type: application/json" \
  -d '{
    "allocation_id": "allocation-id",
    "filename": "filename of file you would like to download from allocation",
    "auth_ticket": "auth-ticket-string",
  "local_path": "/path/to/local/download"
  }' 
Go (version 1.20+ recommended)
zwalletcli
recover wallet