Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Jun 11, 2024
0 parents commit 50308a4
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Publish

# Controls when the action will run.
on:
# Triggers the workflow on new SemVer tags
push:
branches:
- master
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-**'

concurrency:
group: ${{ github.workflow }}

jobs:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
name: generate tags
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=sha,prefix=
type=semver,pattern={{version}}
- uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM docker.io/library/golang:1.21 AS builder

WORKDIR /cpuminerd

# Install dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy source
COPY . .

# Enable CGO for sqlite3 support
ENV CGO_ENABLED=1

RUN go generate ./...
RUN go build -o bin/ -tags='netgo timetzdata' -trimpath -a -ldflags '-s -w' ./cmd/cpuminerd

FROM scratch
LABEL maintainer="The Sia Foundation <[email protected]>" \
org.opencontainers.image.description.vendor="The Sia Foundation" \
org.opencontainers.image.description="A basic cpu miner for mining using a walletd node" \
org.opencontainers.image.source="https://github.com/SiaFoundation/cpuminer" \
org.opencontainers.image.licenses=MIT

ENV PUID=0
ENV PGID=0

# copy binary and prepare data dir.
COPY --from=builder /cpuminerd/bin/* /usr/bin/

# API port
EXPOSE 9980/tcp
# RPC port
EXPOSE 9981/tcp

USER ${PUID}:${PGID}

ENTRYPOINT [ "cpuminerd" ]
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# CPU Miner

A very simple (and naive) single-threaded CPU miner for Siacoin testnet mining

## Usage

```bash
./cpuminerd --addr="addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" --http="http://localhost:9980/api" --password="sia is cool"
```

## Docker Compose
```yml
services:
walletd:
image: ghcr.io/siafoundation/walletd:master
ports:
- localhost:9980:9980
- 9981: 9981
volumes:
- ./wallet:/data
restart: unless-stopped
cpu-miner:
image: ghcr.io/siafoundation/cpuminer:master
command: --addr="addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" --http="http://walletd:9980/api" --password="sia is cool"
restart: unless-stopped


147 changes: 147 additions & 0 deletions cmd/cpuminerd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package main

import (
"flag"
"fmt"
"math/big"
"os"
"time"

"go.sia.tech/core/types"
"go.sia.tech/coreutils"
"go.sia.tech/walletd/api"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"lukechampine.com/frand"
)

func runCPUMiner(c *api.Client, minerAddr types.Address, log *zap.Logger) {
log.Info("starting cpu miner", zap.String("minerAddr", minerAddr.String()))
start := time.Now()

check := func(msg string, err error) bool {
if err != nil {
log.Error(msg, zap.Error(err))
time.Sleep(15 * time.Second)
return false
}
return true
}

for {
elapsed := time.Since(start)
cs, err := c.ConsensusTipState()
if !check("failed to get consensus tip state", err) {
continue
}

d, _ := new(big.Int).SetString(cs.Difficulty.String(), 10)
d.Mul(d, big.NewInt(int64(1+elapsed)))
log := log.With(zap.Uint64("height", cs.Index.Height+1), zap.Stringer("parentID", cs.Index.ID), zap.Stringer("difficulty", d))

log.Debug("mining block")
txns, v2txns, err := c.TxpoolTransactions()
if !check("failed to get txpool transactions", err) {
continue
}

b := types.Block{
ParentID: cs.Index.ID,
Nonce: cs.NonceFactor() * frand.Uint64n(100),
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{Address: minerAddr, Value: cs.BlockReward()}},
Transactions: txns,
}
for _, txn := range txns {
b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.TotalFees())
}
for _, txn := range v2txns {
b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.MinerFee)
}
if len(v2txns) > 0 || cs.Index.Height+1 >= cs.Network.HardforkV2.RequireHeight {
b.V2 = &types.V2BlockData{
Height: cs.Index.Height + 1,
Transactions: v2txns,
}
b.V2.Commitment = cs.Commitment(cs.TransactionsCommitment(b.Transactions, b.V2Transactions()), b.MinerPayouts[0].Address)
}

if !coreutils.FindBlockNonce(cs, &b, time.Minute) {
log.Debug("failed to find nonce")
continue
}
log.Debug("found nonce", zap.Uint64("nonce", b.Nonce))
index := types.ChainIndex{Height: cs.Index.Height + 1, ID: b.ID()}
tip, err := c.ConsensusTip()
if !check("failed to get consensus tip:", err) {
continue
}

if tip != cs.Index {
log.Info("mined stale block", zap.Stringer("current", tip), zap.Stringer("original", cs.Index))
} else if err := c.SyncerBroadcastBlock(b); err != nil {
log.Error("mined invalid block", zap.Error(err))
}
log.Info("mined block", zap.Stringer("blockID", index.ID), zap.Stringer("fees", b.MinerPayouts[0].Value), zap.Int("transactions", len(b.Transactions)), zap.Int("v2transactions", len(b.V2Transactions())))
}
}

func parseLogLevel(level string) zap.AtomicLevel {
switch level {
case "debug":
return zap.NewAtomicLevelAt(zap.DebugLevel)
case "info":
return zap.NewAtomicLevelAt(zap.InfoLevel)
case "warn":
return zap.NewAtomicLevelAt(zap.WarnLevel)
case "error":
return zap.NewAtomicLevelAt(zap.ErrorLevel)
default:
fmt.Printf("invalid log level %q", level)
os.Exit(1)
}
panic("unreachable")
}

func main() {
var (
minerAddrStr string

apiAddress string
apiPassword string

logLevel string
)

flag.StringVar(&minerAddrStr, "address", "", "address to send mining rewards to")
flag.StringVar(&apiAddress, "api", "localhost:9980", "address of the walletd API")
flag.StringVar(&apiPassword, "password", "", "password for the walletd API")
flag.StringVar(&logLevel, "log.level", "info", "log level")
flag.Parse()

var address types.Address
if err := address.UnmarshalText([]byte(minerAddrStr)); err != nil {
panic(err)
}

c := api.NewClient(apiAddress, apiPassword)
if _, err := c.ConsensusTip(); err != nil {
panic(err)
}

cfg := zap.NewProductionEncoderConfig()
cfg.EncodeTime = zapcore.RFC3339TimeEncoder
cfg.EncodeDuration = zapcore.StringDurationEncoder
cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder

cfg.StacktraceKey = ""
cfg.CallerKey = ""
encoder := zapcore.NewConsoleEncoder(cfg)

log := zap.New(zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), parseLogLevel(logLevel)))
defer log.Sync()

zap.RedirectStdLog(log)

runCPUMiner(c, address, log)
}
26 changes: 26 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module go.sia.tech/cpuminerd

go 1.21.8

toolchain go1.21.11

require (
go.sia.tech/core v0.2.7
go.sia.tech/coreutils v0.0.5
go.sia.tech/walletd v0.1.1-alpha.0.20240610172105-eb95d4161b91
go.uber.org/zap v1.27.0
lukechampine.com/frand v1.4.2
)

require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
go.sia.tech/jape v0.11.2-0.20240306154058-9832414a5385 // indirect
go.sia.tech/mux v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/tools v0.22.0 // indirect
)
45 changes: 45 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.sia.tech/core v0.2.7 h1:9Q/3BHL6ziAMPeiko863hhTD/Zs2s7OqEUiPKouDny8=
go.sia.tech/core v0.2.7/go.mod h1:BMgT/reXtgv6XbDgUYTCPY7wSMbspDRDs7KMi1vL6Iw=
go.sia.tech/coreutils v0.0.5 h1:Jj03VrqAayYHgA9fwV13+X88WB+Wr1p8wuLw2B8d2FI=
go.sia.tech/coreutils v0.0.5/go.mod h1:SkSpHeq3tBh2ff4HXuBk2WtlhkYQQtdcvU4Yv1Rd2bU=
go.sia.tech/jape v0.11.2-0.20240306154058-9832414a5385 h1:Gho1g6pkv56o6Ut9cez/Yu5o4xlA8WNkDbPn6RWXL7g=
go.sia.tech/jape v0.11.2-0.20240306154058-9832414a5385/go.mod h1:wU+h6Wh5olDjkPXjF0tbZ1GDgoZ6VTi4naFw91yyWC4=
go.sia.tech/mux v1.2.0 h1:ofa1Us9mdymBbGMY2XH/lSpY8itFsKIo/Aq8zwe+GHU=
go.sia.tech/mux v1.2.0/go.mod h1:Yyo6wZelOYTyvrHmJZ6aQfRoer3o4xyKQ4NmQLJrBSo=
go.sia.tech/walletd v0.1.1-alpha.0.20240610172105-eb95d4161b91 h1:9saK9WcYSK3DDwj6aWyM3MUYlyZaJFiYzdm7KHg4I+Y=
go.sia.tech/walletd v0.1.1-alpha.0.20240610172105-eb95d4161b91/go.mod h1:1CaApRlon10cIDnocnErbolh416CiUeP5jgEbVtmWx8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=

0 comments on commit 50308a4

Please sign in to comment.