Initial Commit
This commit is contained in:
73
.gitignore
vendored
Normal file
73
.gitignore
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# Credentials and local config
|
||||||
|
credentials.yaml
|
||||||
|
credentials.yml
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
*.p12
|
||||||
|
|
||||||
|
# OS specific
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.log
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
debug
|
||||||
|
__debug_bin
|
||||||
|
|
||||||
|
# Air (live reload for Go)
|
||||||
|
tmp/
|
||||||
|
.air.toml
|
||||||
|
|
||||||
|
# GoLand
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Test cache
|
||||||
|
*.cover
|
||||||
|
coverage.out
|
||||||
|
coverage.html
|
||||||
96
README.md
Normal file
96
README.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Dragonchain Go SDK
|
||||||
|
|
||||||
|
A self-contained Go SDK for interacting with Dragonchain nodes.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Initialize the SDK
|
||||||
|
client := sdk.NewDragonchainSDK(
|
||||||
|
"your-public-id",
|
||||||
|
"your-auth-key-id",
|
||||||
|
"your-auth-key",
|
||||||
|
"https://your-dragonchain-endpoint.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check system health
|
||||||
|
if err := client.System.Health(); err != nil {
|
||||||
|
log.Fatal("Health check failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get system status
|
||||||
|
status, err := client.System.Status()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to get status:", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Chain ID: %s, Level: %d\n", status.ID, status.Level)
|
||||||
|
|
||||||
|
// Create a transaction
|
||||||
|
txn := &models.TransactionCreateRequest{
|
||||||
|
TxnType: "my-transaction-type",
|
||||||
|
Payload: map[string]interface{}{
|
||||||
|
"message": "Hello Dragonchain",
|
||||||
|
},
|
||||||
|
Tag: "example-tag",
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Transaction.Create(txn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create transaction:", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Created transaction: %s\n", resp.TransactionID)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Endpoints
|
||||||
|
|
||||||
|
### System
|
||||||
|
- `Health()` - Check system health
|
||||||
|
- `Status()` - Get system status
|
||||||
|
|
||||||
|
### Transaction
|
||||||
|
- `Create(req)` - Create a new transaction
|
||||||
|
- `CreateBulk(req)` - Create multiple transactions
|
||||||
|
- `Get(transactionID)` - Get transaction by ID
|
||||||
|
|
||||||
|
### Transaction Type
|
||||||
|
- `Create(req)` - Create a new transaction type
|
||||||
|
- `Get(txnType)` - Get transaction type by name
|
||||||
|
- `List()` - List all transaction types
|
||||||
|
- `Delete(txnType)` - Delete a transaction type
|
||||||
|
|
||||||
|
### Smart Contract
|
||||||
|
- `Create(req)` - Create a new smart contract
|
||||||
|
- `Get(contractID)` - Get smart contract by ID
|
||||||
|
- `List()` - List all smart contracts
|
||||||
|
- `Update(contractID, req)` - Update a smart contract
|
||||||
|
- `Upload(contractID, req)` - Upload smart contract code
|
||||||
|
- `Delete(contractID)` - Delete a smart contract
|
||||||
|
|
||||||
|
### Block
|
||||||
|
- `Get(blockID)` - Get block by ID
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
The SDK uses HMAC-SHA256 authentication. You need to provide:
|
||||||
|
- `publicID` - Your Dragonchain public ID
|
||||||
|
- `authKeyID` - Your authentication key ID
|
||||||
|
- `authKey` - Your authentication key
|
||||||
|
- `baseURL` - The base URL of your Dragonchain node (e.g., "https://mychain.dragonchain.com")
|
||||||
26
block/block.go
Normal file
26
block/block.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package block
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockClient struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockClient(c *client.Client) *BlockClient {
|
||||||
|
return &BlockClient{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *BlockClient) Get(blockID string) (*models.Block, error) {
|
||||||
|
var resp models.Block
|
||||||
|
path := fmt.Sprintf("/api/v1/block/%s", blockID)
|
||||||
|
err := bc.client.Get(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
144
client/client.go
Normal file
144
client/client.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
publicID string
|
||||||
|
authKeyID string
|
||||||
|
authKey string
|
||||||
|
baseURL string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(publicID, authKeyID, authKey, baseURL string) *Client {
|
||||||
|
return &Client{
|
||||||
|
publicID: publicID,
|
||||||
|
authKeyID: authKeyID,
|
||||||
|
authKey: authKey,
|
||||||
|
baseURL: strings.TrimSuffix(baseURL, "/"),
|
||||||
|
httpClient: &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) generateAuthHeader(method, path, timestamp, contentType string, body []byte) string {
|
||||||
|
msgStr := c.hmacMessage(method, path, timestamp, contentType, body)
|
||||||
|
hmacMsg := c.createHmac(msgStr)
|
||||||
|
b64hmac := base64.StdEncoding.EncodeToString(hmacMsg)
|
||||||
|
return fmt.Sprintf("DC1-HMAC-SHA256 %s:%s", c.authKeyID, b64hmac)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) hmacMessage(method, path, timestamp, contentType string, body []byte) string {
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(body)
|
||||||
|
hashContent := h.Sum(nil)
|
||||||
|
b64Content := base64.StdEncoding.EncodeToString(hashContent)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
|
||||||
|
strings.ToUpper(method),
|
||||||
|
path,
|
||||||
|
c.publicID,
|
||||||
|
timestamp,
|
||||||
|
contentType,
|
||||||
|
b64Content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) createHmac(msgStr string) []byte {
|
||||||
|
h := hmac.New(sha256.New, []byte(c.authKey))
|
||||||
|
h.Write([]byte(msgStr))
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) doRequest(method, path, contentType string, body any, response any) error {
|
||||||
|
var bodyBytes []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if body != nil {
|
||||||
|
if !checkByteSlice(body) {
|
||||||
|
bodyBytes, err = json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal request body: %w", err)
|
||||||
|
}
|
||||||
|
contentType = "application/json"
|
||||||
|
} else {
|
||||||
|
bodyBytes = body.([]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bodyBytes) == 0 {
|
||||||
|
bodyBytes = []byte("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fullURL := c.baseURL + path
|
||||||
|
req, err := http.NewRequest(method, fullURL, bytes.NewBuffer(bodyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
authHeader := c.generateAuthHeader(method, path, timestamp, contentType, bodyBytes)
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", authHeader)
|
||||||
|
req.Header.Set("Dragonchain", c.publicID)
|
||||||
|
req.Header.Set("Timestamp", timestamp)
|
||||||
|
if contentType != "" {
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, strings.TrimSpace(string(respBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if response != nil && len(respBody) > 0 {
|
||||||
|
if err := json.Unmarshal(respBody, response); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkByteSlice(body interface{}) bool {
|
||||||
|
_, ok := body.([]byte)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get(path string, response any) error {
|
||||||
|
return c.doRequest(http.MethodGet, path, "", nil, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Post(path, contentType string, body any, response any) error {
|
||||||
|
return c.doRequest(http.MethodPost, path, contentType, body, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Put(path, contentType string, body any, response any) error {
|
||||||
|
return c.doRequest(http.MethodPut, path, contentType, body, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Delete(path string, response any) error {
|
||||||
|
return c.doRequest(http.MethodDelete, path, "", nil, response)
|
||||||
|
}
|
||||||
80
contract/contract.go
Normal file
80
contract/contract.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContractClient struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContractClient(c *client.Client) *ContractClient {
|
||||||
|
return &ContractClient{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) Create(req *models.SmartContractCreateRequest) (*models.SmartContract, error) {
|
||||||
|
var resp models.SmartContract
|
||||||
|
err := cc.client.Post("/api/v1/contract", models.ContentTypeJSON, req, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) Get(contractID string) (*models.SmartContract, error) {
|
||||||
|
var resp models.SmartContract
|
||||||
|
path := fmt.Sprintf("/api/v1/contract/%s", contractID)
|
||||||
|
err := cc.client.Get(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) List() (*models.ListResponse, error) {
|
||||||
|
var resp models.ListResponse
|
||||||
|
err := cc.client.Get("/api/v1/contract", &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) Update(contractID string, req *models.SmartContractUpdateRequest) (*models.SuccessResponse, error) {
|
||||||
|
var resp models.SuccessResponse
|
||||||
|
path := fmt.Sprintf("/api/v1/contract/%s", contractID)
|
||||||
|
err := cc.client.Put(path, models.ContentTypeJSON, req, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) Upload(contractID string, filepath string) (*models.SuccessResponse, error) {
|
||||||
|
fileContent, err := os.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp models.SuccessResponse
|
||||||
|
path := fmt.Sprintf("/api/v1/contract/%s/upload", contractID)
|
||||||
|
err = cc.client.Put(path, "application/octet-stream", fileContent, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ContractClient) Delete(contractID string) (*models.SuccessResponse, error) {
|
||||||
|
var resp models.SuccessResponse
|
||||||
|
path := fmt.Sprintf("/api/v1/contract/%s", contractID)
|
||||||
|
err := cc.client.Delete(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
221
credentials/credentials.go
Normal file
221
credentials/credentials.go
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChainConfig represents a single chain configuration
|
||||||
|
type ChainConfig struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
PublicId string `yaml:"publicId"`
|
||||||
|
AuthKeyId string `yaml:"authKeyId"`
|
||||||
|
AuthKey string `yaml:"authKey"`
|
||||||
|
Endpoint string `yaml:"endpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config represents the entire configuration file structure
|
||||||
|
type Config struct {
|
||||||
|
Default string `yaml:"default"`
|
||||||
|
Chains []ChainConfig `yaml:"chains"`
|
||||||
|
node *yaml.Node // Store the original node for preserving formatting
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig reads and parses a YAML configuration file
|
||||||
|
func LoadConfig(filePath string) (*Config, error) {
|
||||||
|
// Expand the file path (handles ~/, ./, and environment variables)
|
||||||
|
expandedPath, err := expandPath(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to expand path %s: %w", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
data, err := ioutil.ReadFile(expandedPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read config file %s: %w", expandedPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse YAML with Node to preserve formatting
|
||||||
|
var node yaml.Node
|
||||||
|
if err := yaml.Unmarshal(data, &node); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse YAML config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
if err := node.Decode(&config); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode YAML config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the node for later use when saving
|
||||||
|
config.node = &node
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfigFromString parses YAML configuration from a string
|
||||||
|
func LoadConfigFromString(yamlContent string) (*Config, error) {
|
||||||
|
var node yaml.Node
|
||||||
|
if err := yaml.Unmarshal([]byte(yamlContent), &node); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse YAML config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
if err := node.Decode(&config); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode YAML config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.node = &node
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultChain returns the chain configuration for the default publicId
|
||||||
|
func (c *Config) GetDefaultChain() *ChainConfig {
|
||||||
|
for i := range c.Chains {
|
||||||
|
if c.Chains[i].PublicId == c.Default {
|
||||||
|
return &c.Chains[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChainByPublicId returns the chain configuration for the specified publicId
|
||||||
|
func (c *Config) GetChainByPublicId(publicId string) *ChainConfig {
|
||||||
|
for i := range c.Chains {
|
||||||
|
if c.Chains[i].PublicId == publicId {
|
||||||
|
return &c.Chains[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChains returns all chain names
|
||||||
|
func (c *Config) ListChains() []string {
|
||||||
|
names := make([]string, len(c.Chains))
|
||||||
|
for i, chain := range c.Chains {
|
||||||
|
names[i] = chain.Name
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddChain adds a chain configuration to the configuration
|
||||||
|
func (c *Config) AddChain(chain *ChainConfig) {
|
||||||
|
c.Chains = append(c.Chains, *chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefault sets the default chain publicId
|
||||||
|
func (c *Config) SetDefault(publicId string) {
|
||||||
|
c.Default = publicId
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChain deletes a chain configuration from the configuration
|
||||||
|
func (c *Config) DeleteChain(publicId string) error {
|
||||||
|
if publicId == c.Default {
|
||||||
|
return fmt.Errorf("cannot delete default chain")
|
||||||
|
}
|
||||||
|
for i, chain := range c.Chains {
|
||||||
|
if chain.PublicId == publicId {
|
||||||
|
c.Chains = append(c.Chains[:i], c.Chains[i+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveConfig writes the configuration to a YAML file
|
||||||
|
func (c *Config) SaveConfig(filePath string) error {
|
||||||
|
expandedPath, err := expandPath(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to expand path %s: %w", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
if c.node != nil {
|
||||||
|
// Update the node with current config values while preserving formatting
|
||||||
|
if err := c.node.Encode(c); err != nil {
|
||||||
|
return fmt.Errorf("failed to encode config to node: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure newlines between chain entries
|
||||||
|
c.ensureChainSeparation()
|
||||||
|
|
||||||
|
// Marshal the node to preserve comments and formatting
|
||||||
|
data, err = yaml.Marshal(c.node)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal config to YAML: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to regular marshaling if no node is available
|
||||||
|
data, err = yaml.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal config to YAML: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(expandedPath, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to write config file %s: %w", expandedPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureChainSeparation adds blank lines between chain entries in the YAML node
|
||||||
|
func (c *Config) ensureChainSeparation() {
|
||||||
|
if c.node == nil || len(c.node.Content) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the document node, then the mapping node
|
||||||
|
docNode := c.node
|
||||||
|
if docNode.Kind == yaml.DocumentNode && len(docNode.Content) > 0 {
|
||||||
|
docNode = docNode.Content[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if docNode.Kind != yaml.MappingNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the "chains" key in the mapping
|
||||||
|
for i := 0; i < len(docNode.Content); i += 2 {
|
||||||
|
if i+1 >= len(docNode.Content) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
keyNode := docNode.Content[i]
|
||||||
|
valueNode := docNode.Content[i+1]
|
||||||
|
|
||||||
|
if keyNode.Value == "chains" && valueNode.Kind == yaml.SequenceNode {
|
||||||
|
// Add HeadComment (newline before) to each chain entry except the first
|
||||||
|
for j := 1; j < len(valueNode.Content); j++ {
|
||||||
|
chainNode := valueNode.Content[j]
|
||||||
|
if chainNode.HeadComment == "" {
|
||||||
|
chainNode.HeadComment = "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandPath is a simple path expansion function (you can replace this with your utils.ExpandPath)
|
||||||
|
func expandPath(path string) (string, error) {
|
||||||
|
// Expand environment variables
|
||||||
|
path = os.ExpandEnv(path)
|
||||||
|
|
||||||
|
// Handle home directory
|
||||||
|
if len(path) > 0 && path[0] == '~' {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(path) == 1 {
|
||||||
|
path = home
|
||||||
|
} else if path[1] == '/' {
|
||||||
|
path = home + path[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
35
dragonchain.go
Normal file
35
dragonchain.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package sdk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/block"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/contract"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/system"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/transaction"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/transactiontype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DragonchainSDK struct {
|
||||||
|
client *client.Client
|
||||||
|
Transaction *transaction.TransactionClient
|
||||||
|
TransactionType *transactiontype.TransactionTypeClient
|
||||||
|
Contract *contract.ContractClient
|
||||||
|
Block *block.BlockClient
|
||||||
|
System *system.SystemClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDragonchainSDK(publicID, authKeyID, authKey, baseURL string) *DragonchainSDK {
|
||||||
|
c := client.NewClient(publicID, authKeyID, authKey, baseURL)
|
||||||
|
return &DragonchainSDK{
|
||||||
|
client: c,
|
||||||
|
Transaction: transaction.NewTransactionClient(c),
|
||||||
|
TransactionType: transactiontype.NewTransactionTypeClient(c),
|
||||||
|
Contract: contract.NewContractClient(c),
|
||||||
|
Block: block.NewBlockClient(c),
|
||||||
|
System: system.NewSystemClient(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sdk *DragonchainSDK) GetClient() *client.Client {
|
||||||
|
return sdk.client
|
||||||
|
}
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
|
|
||||||
|
require gopkg.in/yaml.v3 v3.0.1
|
||||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
159
models/models.go
Normal file
159
models/models.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
const (
|
||||||
|
ContentTypeJSON = "application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionCreateRequest struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
TxnType string `json:"txn_type"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionCreateResponse struct {
|
||||||
|
TransactionID string `json:"transaction_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionBulkRequest struct {
|
||||||
|
Transactions []TransactionCreateRequest `json:"transactions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionBulkResponse struct {
|
||||||
|
TransactionIDs []string `json:"transaction_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionListResponse struct {
|
||||||
|
TransactionTypes []*TransactionType `json:"transactionTypes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transaction struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Header TransactionHeader `json:"header"`
|
||||||
|
Proof TransactionProof `json:"proof"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionHeader struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
DcId string `json:"dc_id"`
|
||||||
|
TxnId string `json:"txn_id"`
|
||||||
|
Invoker string `json:"invoker"`
|
||||||
|
BlockId string `json:"block_id"`
|
||||||
|
TxnType string `json:"txn_type"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionProof struct {
|
||||||
|
Full string `json:"full"`
|
||||||
|
Stripped string `json:"stripped"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListTransactionsResponse struct {
|
||||||
|
Transactions []Transaction `json:"transactions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionTypeCreateRequest struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
TxnType string `json:"txn_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionTypeCreateResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionType struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Modified int64 `json:"modified"`
|
||||||
|
TxnType string `json:"txn_type"`
|
||||||
|
ContractID string `json:"contract_id,omitempty"`
|
||||||
|
CustomIndexes []interface{} `json:"custom_indexes"`
|
||||||
|
ActiveSinceBlock string `json:"active_since_block"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmartContractCreateRequest struct {
|
||||||
|
Environment string `json:"environment"`
|
||||||
|
TransactionType string `json:"transactionType"`
|
||||||
|
ExecutionOrder string `json:"executionOrder"`
|
||||||
|
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
|
||||||
|
Secrets map[string]string `json:"secret,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmartContractUpdateRequest struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
|
||||||
|
Secrets map[string]string `json:"secret,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmartContract struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Created uint64 `json:"created"`
|
||||||
|
Modified uint64 `json:"modified"`
|
||||||
|
|
||||||
|
Version string `json:"version"`
|
||||||
|
Environment string `json:"environment"`
|
||||||
|
TransactionType string `json:"transactionType"`
|
||||||
|
ExecutionOrder string `json:"executionOrder"`
|
||||||
|
|
||||||
|
ExecutionInfo *SmartContractExecutionInfo `json:"executionInfo"`
|
||||||
|
|
||||||
|
EnvVars map[string]string `json:"envVars"`
|
||||||
|
Secrets []string `json:"secrets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmartContractExecutionInfo struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// LocalExecutable type
|
||||||
|
ExecutablePath string `json:"executablePath"`
|
||||||
|
ExecutableWorkingDirectory string `json:"executableWorkingDirectory"`
|
||||||
|
ExecutableHash string `json:"executableHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Block struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
ID string `json:"block_id"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
PrevID string `json:"prev_id"`
|
||||||
|
PrevProof string `json:"prev_proof"`
|
||||||
|
Transactions []string `json:"transactions"`
|
||||||
|
Proof BlockProof `json:"proof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockProof struct {
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
Proof string `json:"proof"`
|
||||||
|
Nonce int64 `json:"nonce,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemStatus struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
HashAlgo string `json:"hashAlgo"`
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
EncryptionAlgo string `json:"encryptionAlgo"`
|
||||||
|
IndexingEnabled bool `json:"indexingEnabled"`
|
||||||
|
|
||||||
|
// Level 5 Node
|
||||||
|
Funded string `json:"funded,omitempty"`
|
||||||
|
BroadcastInterval string `json:"broadcastInterval,omitempty"`
|
||||||
|
Network string `json:"network,omitempty"`
|
||||||
|
InterchainWallet string `json:"interchainWallet,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuccessResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResponse struct {
|
||||||
|
Items []interface{} `json:"items"`
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
|
}
|
||||||
27
system/system.go
Normal file
27
system/system.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SystemClient struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSystemClient(c *client.Client) *SystemClient {
|
||||||
|
return &SystemClient{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SystemClient) Health() error {
|
||||||
|
return sc.client.Get("/api/v1/health", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SystemClient) Status() (*models.SystemStatus, error) {
|
||||||
|
var resp models.SystemStatus
|
||||||
|
err := sc.client.Get("/api/v1/status", &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
54
transaction/transaction.go
Normal file
54
transaction/transaction.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionClient struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransactionClient(c *client.Client) *TransactionClient {
|
||||||
|
return &TransactionClient{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TransactionClient) Create(req *models.TransactionCreateRequest) (*models.TransactionCreateResponse, error) {
|
||||||
|
var resp models.TransactionCreateResponse
|
||||||
|
err := tc.client.Post("/api/v1/transaction", models.ContentTypeJSON, req, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TransactionClient) CreateBulk(req *models.TransactionBulkRequest) (*models.TransactionBulkResponse, error) {
|
||||||
|
var resp models.TransactionBulkResponse
|
||||||
|
err := tc.client.Post("/api/v1/transaction/bulk", models.ContentTypeJSON, req, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TransactionClient) Get(transactionID string) (*models.Transaction, error) {
|
||||||
|
var resp models.Transaction
|
||||||
|
path := fmt.Sprintf("/api/v1/transaction/%s", transactionID)
|
||||||
|
err := tc.client.Get(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TransactionClient) List() (*models.ListTransactionsResponse, error) {
|
||||||
|
var resp models.ListTransactionsResponse
|
||||||
|
path := "/api/v1/transaction/"
|
||||||
|
err := tc.client.Get(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
54
transactiontype/transactiontype.go
Normal file
54
transactiontype/transactiontype.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package transactiontype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
|
||||||
|
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionTypeClient struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransactionTypeClient(c *client.Client) *TransactionTypeClient {
|
||||||
|
return &TransactionTypeClient{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ttc *TransactionTypeClient) Create(req *models.TransactionTypeCreateRequest) (*models.TransactionTypeCreateResponse, error) {
|
||||||
|
var resp models.TransactionTypeCreateResponse
|
||||||
|
err := ttc.client.Post("/api/v1/transaction-type", models.ContentTypeJSON, req, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ttc *TransactionTypeClient) Get(txnType string) (*models.TransactionType, error) {
|
||||||
|
var resp models.TransactionType
|
||||||
|
path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType)
|
||||||
|
err := ttc.client.Get(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ttc *TransactionTypeClient) List() (*models.TransactionListResponse, error) {
|
||||||
|
var resp models.TransactionListResponse
|
||||||
|
err := ttc.client.Get("/api/v1/transaction-types", &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ttc *TransactionTypeClient) Delete(txnType string) (*models.SuccessResponse, error) {
|
||||||
|
var resp models.SuccessResponse
|
||||||
|
path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType)
|
||||||
|
err := ttc.client.Delete(path, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user