This is underconstruction. The methods will be change in the future !!!!
Package steemutil provides Steem blockchain-specific convenience functions and types for Go applications.
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.
- 🔐 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
go get github.com/steemit/steemutilpackage 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)
}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)
}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
}auth/- Authentication and key management utilitiesrpc/- RPC authentication and signed call supportprotocol/- Steem protocol definitions and operationstransaction/- Transaction creation and signingwif/- Wallet Import Format key handlingencoder/- Binary serialization utilities
protocol/api/- API method definitionsprotocol/broadcast/- Broadcast operation definitionsconsts/- Protocol constants and chain parametersjsonrpc2/- JSON-RPC client implementation
The rpc package provides full support for Steem's signed RPC calls, compatible with steem-js:
- 🔒 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
- 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
// 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
}// 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")// 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)// 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)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...
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.
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.RawMessagefor operations not recognized by the package
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())
}
}
}- Type Safety: Always check
op.Type()before performing type assertions - Unknown Operations: Use
*json.RawMessagetype assertion to handle unrecognized operations - Performance: Direct operation casting (e.g.,
op.(*VoteOperation)) is more efficient than usingData()for known types - Compatibility: The
anyreturn type allows the library to handle both known and unknown operation types flexibly
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 ./... -coverSee 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/
ToWif(name, password, role string) (string, error)- Generate WIF from credentialsGetPrivateKeys(name, password string, roles []string) (map[string]string, error)- Generate multiple keysWifToPublic(wif string) (string, error)- Convert WIF to public keyIsWif(wif string) bool- Validate WIF formatVerify(name, password string, auths map[string]interface{}) (bool, error)- Verify credentials
Sign(request *RpcRequest, account string, keys []string) (*SignedRequest, error)- Sign RPC requestValidate(request *SignedRequest, verifyFunc func(...) error) ([]interface{}, error)- Validate signed requestSignRequest(method string, params []interface{}, id int, account, key string) (*SignedRequest, error)- Convenience function
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
(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
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
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
- Issues: GitHub Issues
- Documentation: API Documentation
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.