steemutil

package module
v0.0.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 24, 2025 License: MIT Imports: 5 Imported by: 0

README

steemutil

This is underconstruction. The methods will be change in the future !!!!

Package steemutil provides Steem blockchain-specific convenience functions and types for Go applications.

Overview

steemutil is a comprehensive Go library that provides low-level utilities for interacting with the Steem blockchain. It includes cryptographic functions, transaction handling, protocol definitions, and RPC authentication capabilities.

Features

  • 🔐 RPC Authentication: SignedCall support for authenticated API requests
  • 🔑 Cryptographic Operations: Key generation, signing, and verification
  • 📦 Transaction Handling: Transaction creation, signing, and serialization
  • 🌐 Protocol Support: Complete Steem protocol operation definitions
  • 🛡️ Security: Built-in replay protection and signature validation
  • ⚡ Performance: Optimized for high-performance applications

Installation

go get github.com/steemit/steemutil

Quick Start

Basic Key Operations
package main

import (
    "fmt"
    "github.com/steemit/steemutil/auth"
)

func main() {
    // Generate private key from account and password
    privateKey, err := auth.ToWif("username", "password", "active")
    if err != nil {
        panic(err)
    }
    
    // Convert to public key
    publicKey, err := auth.WifToPublic(privateKey)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Private Key: %s\n", privateKey)
    fmt.Printf("Public Key: %s\n", publicKey)
}
SignedCall RPC Authentication
package main

import (
    "fmt"
    "github.com/steemit/steemutil/rpc"
)

func main() {
    // Create RPC request
    request := &rpc.RpcRequest{
        Method: "condenser_api.get_accounts",
        Params: []interface{}{[]string{"username"}},
        ID:     1,
    }
    
    // Sign the request
    signedRequest, err := rpc.Sign(request, "username", []string{"private-key-wif"})
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Signed Request: %+v\n", signedRequest)
}
Transaction Operations
package main

import (
    "github.com/steemit/steemutil/protocol"
    "github.com/steemit/steemutil/transaction"
)

func main() {
    // Create a vote operation
    voteOp := &protocol.VoteOperation{
        Voter:    "voter",
        Author:   "author", 
        Permlink: "permlink",
        Weight:   10000,
    }
    
    // Create transaction
    tx := &transaction.Transaction{}
    tx.PushOperation(voteOp)
    
    // Sign transaction
    signedTx := transaction.NewSignedTransaction(tx)
    // ... add signing logic
}

Package Structure

Core Packages
  • auth/ - Authentication and key management utilities
  • rpc/ - RPC authentication and signed call support
  • protocol/ - Steem protocol definitions and operations
  • transaction/ - Transaction creation and signing
  • wif/ - Wallet Import Format key handling
  • encoder/ - Binary serialization utilities
Protocol Support
  • protocol/api/ - API method definitions
  • protocol/broadcast/ - Broadcast operation definitions
  • consts/ - Protocol constants and chain parameters
  • jsonrpc2/ - JSON-RPC client implementation

RPC Authentication (SignedCall)

The rpc package provides full support for Steem's signed RPC calls, compatible with steem-js:

Features
  • 🔒 Cryptographic Signing: ECDSA signature generation and verification
  • 🛡️ Security: Nonce generation, timestamp expiration (60s), replay protection
  • 🔄 Compatibility: 100% compatible with steem-js signedCall format
  • 🔑 Multi-Key Support: Sign with multiple private keys simultaneously
  • ⏰ Time Validation: Automatic signature expiration handling
Security Features
  • Unique Nonces: 8-byte random nonce for each request
  • Timestamp Expiration: Signatures expire after 60 seconds
  • Cross-Protocol Protection: Protocol-specific signing constant K
  • No Key Transmission: Private keys never leave your application
Example Usage
// Sign a request
request := &rpc.RpcRequest{
    Method: "condenser_api.get_account_history",
    Params: []interface{}{"username", -1, 100},
    ID:     1,
}

signedRequest, err := rpc.Sign(request, "username", []string{"active-key-wif"})
if err != nil {
    return err
}

// Validate a signed request
params, err := rpc.Validate(signedRequest, rpc.DefaultVerifyFunc)
if err != nil {
    return err
}

Cryptographic Operations

Key Generation
// Generate keys for multiple roles
roles := []string{"active", "posting", "owner", "memo"}
keys, err := auth.GetPrivateKeys("username", "password", roles)

// Generate single key
activeKey, err := auth.ToWif("username", "password", "active")
Message Signing
// Sign arbitrary messages
privateKey := &wif.PrivateKey{}
privateKey.FromWif("private-key-wif")

message := []byte("Hello, Steem!")
signature, err := privateKey.SignSha256(message)

// Verify signatures
publicKey := &wif.PublicKey{}
publicKey.FromWif("private-key-wif") // Derives public key
isValid := publicKey.VerifySha256(message, signature)

Transaction Handling

Creating Transactions
// Create operations
voteOp := &protocol.VoteOperation{
    Voter:    "voter",
    Author:   "author",
    Permlink: "post-permlink", 
    Weight:   10000,
}

transferOp := &protocol.TransferOperation{
    From:   "sender",
    To:     "receiver",
    Amount: "1.000 STEEM",
    Memo:   "Transfer memo",
}

// Build transaction
tx := &transaction.Transaction{}
tx.PushOperation(voteOp)
tx.PushOperation(transferOp)

// Sign transaction
signedTx := transaction.NewSignedTransaction(tx)
err := signedTx.Sign(privateKeys, transaction.SteemChain)
Supported Operations

The library supports all Steem protocol operations:

  • Content: comment, vote, delete_comment
  • Financial: transfer, transfer_to_savings, claim_reward_balance
  • Account: account_create, account_update, recover_account
  • Witness: witness_update, account_witness_vote
  • Market: limit_order_create, limit_order_cancel
  • Custom: custom_json, custom_binary
  • And many more...
Working with Operation Data

The Operation interface provides a Data() any method that returns the operation data. Understanding how to use this method is important for type-safe operation handling.

Return Types

The Data() method returns different types depending on the operation:

  • Known Operations: Returns the operation struct itself (e.g., *VoteOperation, *TransferOperation)
  • Unknown Operations: Returns *json.RawMessage for operations not recognized by the package
Best Practices

1. Type Assertion Based on Operation Type

Always check the operation type before performing type assertions:

import (
    "encoding/json"
    "github.com/steemit/steemutil/protocol"
)

func processOperation(op protocol.Operation) {
    switch op.Type() {
    case protocol.TypeVote:
        // Type assertion is safe after checking Type()
        voteOp := op.Data().(*protocol.VoteOperation)
        fmt.Printf("Voter: %s, Author: %s\n", voteOp.Voter, voteOp.Author)
        
    case protocol.TypeTransfer:
        transferOp := op.Data().(*protocol.TransferOperation)
        fmt.Printf("From: %s, To: %s, Amount: %s\n", 
            transferOp.From, transferOp.To, transferOp.Amount)
        
    case protocol.TypeComment:
        commentOp := op.Data().(*protocol.CommentOperation)
        fmt.Printf("Author: %s, Title: %s\n", commentOp.Author, commentOp.Title)
        
    default:
        // Handle unknown operations
        if rawJSON, ok := op.Data().(*json.RawMessage); ok {
            fmt.Printf("Unknown operation type: %s\n", op.Type())
            fmt.Printf("Raw JSON: %s\n", string(*rawJSON))
        }
    }
}

2. Safe Type Assertion with Error Handling

Use type assertions with the two-value form for safer code:

func safeProcessVote(op protocol.Operation) error {
    if op.Type() != protocol.TypeVote {
        return fmt.Errorf("expected vote operation, got %s", op.Type())
    }
    
    voteOp, ok := op.Data().(*protocol.VoteOperation)
    if !ok {
        return fmt.Errorf("failed to assert vote operation data")
    }
    
    // Use voteOp safely
    fmt.Printf("Processing vote: %s -> %s/%s\n", 
        voteOp.Voter, voteOp.Author, voteOp.Permlink)
    return nil
}

3. Handling Unknown Operations

For operations not recognized by the package, Data() returns *json.RawMessage:

func handleUnknownOperation(op protocol.Operation) {
    if rawJSON, ok := op.Data().(*json.RawMessage); ok {
        // This is an unknown operation type
        var data map[string]any
        if err := json.Unmarshal(*rawJSON, &data); err == nil {
            fmt.Printf("Unknown operation data: %+v\n", data)
        }
    } else {
        // This is a known operation type
        fmt.Printf("Known operation: %s\n", op.Type())
    }
}

4. Direct Operation Access

For known operations, you can directly use the operation struct without calling Data():

// Instead of:
data := op.Data().(*protocol.VoteOperation)

// You can directly cast the operation:
if voteOp, ok := op.(*protocol.VoteOperation); ok {
    // Use voteOp directly
    fmt.Printf("Voter: %s\n", voteOp.Voter)
}

5. Iterating Over Operations

When processing multiple operations:

func processOperations(ops protocol.Operations) {
    for _, op := range ops {
        switch op.Type() {
        case protocol.TypeVote:
            voteOp := op.Data().(*protocol.VoteOperation)
            processVote(voteOp)
            
        case protocol.TypeTransfer:
            transferOp := op.Data().(*protocol.TransferOperation)
            processTransfer(transferOp)
            
        default:
            // Log or handle unknown operations
            fmt.Printf("Unhandled operation type: %s\n", op.Type())
        }
    }
}
Important Notes
  • Type Safety: Always check op.Type() before performing type assertions
  • Unknown Operations: Use *json.RawMessage type assertion to handle unrecognized operations
  • Performance: Direct operation casting (e.g., op.(*VoteOperation)) is more efficient than using Data() for known types
  • Compatibility: The any return type allows the library to handle both known and unknown operation types flexibly

Testing

Run the test suite:

# Run all tests
go test ./...

# Run specific package tests
go test ./rpc -v
go test ./auth -v
go test ./protocol -v

# Run with coverage
go test ./... -cover

Examples

See the examples directory for complete working examples:

  • SignedCall Authentication: examples/signed_call/
  • Key Generation: examples/generate_keys/
  • Transaction Broadcasting: examples/transfer/
  • Vote Operations: examples/vote_post/

API Reference

Authentication (auth/)
  • ToWif(name, password, role string) (string, error) - Generate WIF from credentials
  • GetPrivateKeys(name, password string, roles []string) (map[string]string, error) - Generate multiple keys
  • WifToPublic(wif string) (string, error) - Convert WIF to public key
  • IsWif(wif string) bool - Validate WIF format
  • Verify(name, password string, auths map[string]interface{}) (bool, error) - Verify credentials
RPC Authentication (rpc/)
  • Sign(request *RpcRequest, account string, keys []string) (*SignedRequest, error) - Sign RPC request
  • Validate(request *SignedRequest, verifyFunc func(...) error) ([]interface{}, error) - Validate signed request
  • SignRequest(method string, params []interface{}, id int, account, key string) (*SignedRequest, error) - Convenience function
Transaction (transaction/)
  • NewSignedTransaction(tx *Transaction) *SignedTransaction - Create signed transaction
  • (tx *SignedTransaction) Sign(keys []*wif.PrivateKey, chain *Chain) error - Sign transaction
  • (tx *SignedTransaction) Digest(chain *Chain) ([]byte, error) - Calculate transaction digest
  • (tx *SignedTransaction) Serialize() ([]byte, error) - Serialize transaction
WIF Operations (wif/)
  • (pk *PrivateKey) FromWif(wif string) error - Import from WIF
  • (pk *PrivateKey) ToWif() string - Export to WIF
  • (pk *PrivateKey) SignSha256(message []byte) ([]byte, error) - Sign message
  • (pk *PublicKey) VerifySha256(message, signature []byte) bool - Verify signature

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

  • steemgosdk - High-level Steem Go SDK built on steemutil
  • steem-js - JavaScript Steem library (compatible with our SignedCall)
  • steem - Official Steem blockchain implementation

Support


Note: This library provides low-level utilities. For high-level application development, consider using steemgosdk which provides a more convenient API built on top of steemutil.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	Api     string
	Timeout uint
	Client  *http.Client
}

func (*Client) Send

func (c *Client) Send(data RequestData) (res ResponseData, err error)

type IClient

type IClient interface {
	Send(RequestData) (ResponseData, error)
}

func GetClient

func GetClient(api string, timeout uint) (client IClient)

type RequestData

type RequestData struct {
	Id      uint   `json:"id"`
	JsonRPC string `json:"jsonrpc"`
	Method  string `json:"method"`
	Params  []any  `json:"params"`
}

type ResponseData

type ResponseData struct {
	Id      uint   `json:"id"`
	JsonRPC string `json:"jsonrpc"`
	Result  any    `json:"result"`
}

Directories

Path Synopsis
api

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL