Walkthroughs of example applications demonstrating nmcd library features and Go best practices.
Run examples from examples/ directory:
git clone https://github.com/opd-ai/nmcd.git
cd nmcd
go run ./examples/simple_resolve d/example
Prerequisites: Go 1.24.11+
Location: examples/simple_resolve/
Demonstrates basic name resolution with auto-detection.
Usage:
go run ./examples/simple_resolve d/example
Key code:
cfg := &client.Config{
Mode: client.ModeAuto,
Network: "regtest",
DataDir: os.TempDir() + "/nmcd-simple",
}
nc, err := client.NewClient(cfg)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer nc.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
record, err := nc.ResolveName(ctx, name)
if err != nil {
if errors.Is(err, client.ErrNameNotFound) {
fmt.Printf("Name not found: %s\n", name)
return
}
log.Fatal(err)
}
fmt.Printf("Name: %s\nValue: %s\nHeight: %d\nExpires: %d\n",
record.Name, record.Value, record.Height, record.ExpiresAt)
Location: examples/embedded_client/
Shows embedded mode without external daemon.
Usage:
go run ./examples/embedded_client
Key features:
cfg := &client.Config{
Mode: client.ModeEmbedded,
Network: "regtest",
DataDir: "/tmp/nmcd-embedded",
WalletEnabled: true,
}
nc, err := client.NewClient(cfg)
defer nc.Close()
// Resolve names
record, err := nc.ResolveName(ctx, "d/example")
// Get node info
info, err := nc.GetInfo(ctx)
fmt.Printf("Block height: %d\n", info.Blocks)
Location: examples/register_name/
Demonstrates two-phase name registration (NAME_NEW → NAME_FIRSTUPDATE).
Usage:
go run ./examples/register_name d/myname '{"ip":"1.2.3.4"}'
Registration flow:
// Phase 1: Pre-register (NAME_NEW)
salt := make([]byte, 20)
rand.Read(salt)
commitTxHash, err := nc.RegisterNameCommitment(ctx, name, salt)
fmt.Printf("Commitment TX: %s\n", commitTxHash)
// Wait 12 blocks minimum
time.Sleep(12 * 10 * time.Minute)
// Phase 2: Reveal and register (NAME_FIRSTUPDATE)
txHash, err := nc.RegisterName(ctx, name, value, salt)
fmt.Printf("Registration TX: %s\n", txHash)
Location: examples/update_name/
Shows updating existing name values.
Usage:
go run ./examples/update_name d/myname '{"ip":"5.6.7.8"}'
Key code:
txHash, err := nc.UpdateName(ctx, name, newValue)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Update TX: %s\n", txHash)
fmt.Printf("Expiration extended +36,000 blocks\n")
Location: examples/list_names/
Lists all registered names with filtering.
Usage:
go run ./examples/list_names
go run ./examples/list_names --namespace d/
Key code:
names, err := nc.ListNames(ctx, &client.NameFilter{
Namespace: "d/",
Limit: 100,
})
for _, name := range names {
fmt.Printf("%s: %s (expires: %d)\n",
name.Name, name.Value, name.ExpiresAt)
}
Location: examples/name_database/
Direct database interaction for advanced use cases.
Usage:
go run ./examples/name_database
Key operations:
db, err := namedb.NewNameDatabase(dataDir, network)
defer db.Close()
// Get name
record, err := db.GetName("d/example")
// List all names
names, err := db.ListNames()
// Get history
history, err := db.GetNameHistory("d/example")
Always use contexts with timeouts:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
record, err := nc.ResolveName(ctx, name)
nc, err := client.NewClient(cfg)
if err != nil {
return err
}
defer nc.Close() // Always close
// Production: Auto-detection (daemon preferred)
Mode: client.ModeAuto
// Development: Embedded (no daemon needed)
Mode: client.ModeEmbedded
// CI/Testing: Daemon with known endpoint
Mode: client.ModeDaemon
RPCAddress: "localhost:18336"
cfg := &client.Config{
WalletEnabled: true,
WalletPath: "/secure/location/wallet.json",
WalletPassword: os.Getenv("WALLET_PASSWORD"),
}
// Mainnet (production)
Network: "mainnet"
// Testnet (testing)
Network: "testnet"
// Regtest (local development)
Network: "regtest"
record, err := nc.ResolveName(ctx, name)
if err != nil {
switch {
case errors.Is(err, client.ErrNameNotFound):
// Name doesn't exist
case errors.Is(err, client.ErrNameExpired):
// Name expired
case errors.Is(err, context.DeadlineExceeded):
// Timeout
case errors.Is(err, client.ErrNotConnected):
// No daemon connection
default:
// Other error
}
}
_, err := nc.RegisterName(ctx, name, value, salt)
if err != nil {
switch {
case errors.Is(err, client.ErrNameAlreadyExists):
// Name taken
case errors.Is(err, client.ErrInvalidName):
// Invalid format
case errors.Is(err, client.ErrInvalidValue):
// Value too large or invalid JSON
case errors.Is(err, client.ErrInsufficientFunds):
// Need more NMC
}
}
fmt.Errorf("resolve failed: %w", err)// Reuse client across requests
var nc *client.Client
func init() {
var err error
nc, err = client.NewClient(&client.Config{Mode: client.ModeAuto})
if err != nil {
log.Fatal(err)
}
}
// Use in handlers
func handler(w http.ResponseWriter, r *http.Request) {
record, err := nc.ResolveName(r.Context(), name)
// ...
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
go func() {
<-sig
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
nc.Close()
os.Exit(0)
}()
import "github.com/prometheus/client_golang/prometheus"
var resolveCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "name_resolves_total"},
[]string{"status"},
)
record, err := nc.ResolveName(ctx, name)
if err != nil {
resolveCounter.WithLabelValues("error").Inc()
} else {
resolveCounter.WithLabelValues("success").Inc()
}
func TestNameResolution(t *testing.T) {
cfg := &client.Config{
Mode: client.ModeEmbedded,
Network: "regtest",
DataDir: t.TempDir(),
}
nc, err := client.NewClient(cfg)
require.NoError(t, err)
defer nc.Close()
// Test resolution
_, err = nc.ResolveName(context.Background(), "d/example")
assert.Error(t, err)
}
Error: failed to connect to daemon
Solution:
# Check daemon is running
ps aux | grep nmcd
# Use embedded mode instead
cfg := &client.Config{Mode: client.ModeEmbedded}
Error: name not found: d/example
Cause: Name doesn’t exist or expired
Solution:
# Check name status
nmcd-cli name_show d/example
# Register if needed
go run ./examples/register_name d/example '{"ip":"1.2.3.4"}'
Error: name already exists
Cause: Name taken or commitment not found
Solution:
Cause: Large embedded blockchain
Solution:
// Use daemon mode for production
cfg := &client.Config{
Mode: client.ModeDaemon,
RPCAddress: "localhost:8336",
}
Cause: Operation timeout
Solution:
// Increase timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
Submit examples via pull request to examples/ directory. Include:
Examples are MIT licensed. See LICENSE file.