Skip to content

Postgres DB implementation #588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ark.unit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ jobs:
args: '-severity high -quiet -exclude=G115 ./...'
- name: Run go work sync
run: go work sync
- name: create pg db
run: make pgtest
- name: unit testing
run: make test
- name: drop pg db
run: make droppgtest

test-sdk:
name: sdk unit tests
Expand Down
1 change: 1 addition & 0 deletions client/datadir/state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"boarding_exit_delay":"","client_type":"","dust":"","explorer_url":"","forfeit_address":"","market_hour_end_time":"","market_hour_period":"","market_hour_round_interval":"","market_hour_start_time":"","network":"","round_interval":"","server_pubkey":"","server_url":"","unilateral_exit_delay":"","utxo_max_amount":"","utxo_min_amount":"","vtxo_max_amount":"","vtxo_min_amount":"","vtxo_tree_expiry":"","wallet_type":"","with_transaction_feed":""}
14 changes: 14 additions & 0 deletions docker-compose.regtest.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
services:
ark-db-pg:
container_name: ark-db-pg
image: postgres
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: secret
POSTGRES_DB: ark-db

arkd-wallet:
build:
context: .
Expand All @@ -20,8 +31,10 @@ services:
context: .
dockerfile: Dockerfile
container_name: arkd
restart: unless-stopped
depends_on:
- arkd-wallet
- ark-db-pg
ports:
- "7070:7070"
environment:
Expand All @@ -35,6 +48,7 @@ services:
- ARK_DATADIR=./data/regtest
- ARK_WALLET_ADDR=arkd-wallet:6060
- ARK_ESPLORA_URL=http://chopsticks:3000
- ARK_DB_URL=postgresql://root:secret@ark-db-pg:5432/ark-db?sslmode=disable
- ARK_VTXO_MIN_AMOUNT=1
volumes:
- type: tmpfs
Expand Down
67 changes: 66 additions & 1 deletion server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,69 @@ migrate:
## sqlc: gen sql
sqlc:
@echo "gen sql..."
@docker run --rm -v ./internal/infrastructure/db/sqlite:/src -w /src sqlc/sqlc generate
@docker run --rm -v ./internal/infrastructure/db/sqlite:/src -w /src sqlc/sqlc generate

#### Postgres database ####
# pg: starts postgres db inside docker container
pg:
@docker run --name ark-pg -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=ark-db -d postgres

# pgtest: starts postgres db inside docker container
pgtest:
@docker run --name ark-pg-test -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=ark-db-test -d postgres

# droppg: stop and remove postgres container
droppg:
@docker stop ark-pg
@docker rm ark-pg

# droppgtest: stop and remove postgres container
droppgtest:
@docker stop ark-pg-test
@docker rm ark-pg-test

# createdb: create db inside docker container
createdb:
@docker exec ark-pg createdb --username=root --owner=root ark-db

# dropdb: drops db inside docker container
dropdb:
@docker exec ark-pg dropdb ark-db

# createtestdb: create test db inside docker container
createtestdb:
@docker exec ark-pg-test createdb --username=root --owner=root ark-db-test

# droptestdb: drops test db inside docker container
droptestdb:
@docker exec ark-pg-test dropdb ark-db-test

# recreatetestdb: drops and creates test db
recreatetestdb: droptestdb createtestdb

# pgcreatetestdb: starts docker container and creates test db, used in CI
pgcreatetestdb:
@chmod u+x ./scripts/create_testdb
@./scripts/create_testdb

# psql: connects to postgres terminal running inside docker container
psql:
@docker exec -it ark-pg psql -U root -d ark-db -c "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema');" \
&& docker exec -it ark-pg psql -U root -d ark-db

# pgmigrate: creates pg migration file (e.g. make FILE=init pgmigrate)
pgmigrate:
@docker run --rm -v /internal/infrastructure/db/postgres/migration:/migration migrate/migrate create -ext sql -dir /migration $(FILE)

# pgmigrate-up: applies all Postgres migrations to the running ark-pg container
pgmigrate-up: dropdb createdb
@docker run --rm \
-v $(PWD)/internal/infrastructure/db/postgres/migration:/migrations \
--network host \
migrate/migrate \
-path=/migrations \
-database "postgres://root:secret@localhost:5432/ark-db?sslmode=disable" up

# pgsqlc: generate sql code for postgres
pgsqlc:
@docker run --rm -v $(PWD)/internal/infrastructure/db/postgres:/src -w /src sqlc/sqlc generate
12 changes: 8 additions & 4 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ This is a Go implementation of the Ark server. An Ark server it's an always-on s
- [Go](https://go.dev/doc/install)
- [Bitcoin Core](https://bitcoincore.org) with `compact block filters` enabled

### Run the arkd and arkd-wallet servers
### Run the postgres, arkd-wallet and arkd servers
1. Start postgres container and create ark-db database
```bash
make pg
```

1. Run arkd-wallet
2. Run arkd-wallet
```bash
cd ../pkg/ark-wallet && make run-neutrino
```

2. Run arkd
3. Run arkd
```bash
make run-neutrino
make run
```

Refer to [config.go](./internal/config/config.go) for the available configuration options via ENV VARs.
3 changes: 2 additions & 1 deletion server/envs/mutinynet.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ ARK_BOARDING_EXIT_DELAY=604672
ARK_DATADIR=./data/mutinynet
ARK_ESPLORA_URL=https://mutinynet.com/api
ARK_WALLET_ADDR=localhost:6060
ARK_ALLOW_ZERO_FEES=true
ARK_ALLOW_ZERO_FEES=true
ARK_DB_URL=postgresql://root:[email protected]:5432/ark-db?sslmode=disable
3 changes: 2 additions & 1 deletion server/envs/regtest.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ ARK_BOARDING_EXIT_DELAY=604672
ARK_DATADIR=./data/regtest
ARK_ESPLORA_URL=http://localhost:3000
ARK_WALLET_ADDR=localhost:6060
ARK_ALLOW_ZERO_FEES=true
ARK_ALLOW_ZERO_FEES=true
ARK_DB_URL=postgresql://root:[email protected]:5432/ark-db?sslmode=disable
7 changes: 7 additions & 0 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/google/uuid v1.6.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
github.com/lib/pq v1.10.9
github.com/lightningnetwork/lnd v0.18.2-beta
github.com/nbd-wtf/go-nostr v0.40.1
github.com/sirupsen/logrus v1.9.3
Expand All @@ -36,6 +37,7 @@ require github.com/stretchr/objx v0.5.2 // indirect

require (
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/aead/siphash v1.0.1 // indirect
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 // indirect
Expand All @@ -50,7 +52,10 @@ require (
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/decred/dcrd/lru v1.1.3 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.1.1+incompatible // indirect
github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/fergusstrange/embedded-postgres v1.28.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
Expand Down Expand Up @@ -95,7 +100,9 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.2.4 // indirect
github.com/ory/dockertest/v3 v3.11.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
Expand Down
5 changes: 5 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88Nh
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
Expand All @@ -104,6 +106,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/fergusstrange/embedded-postgres v1.28.0 h1:Atixd24HCuBHBavnG4eiZAjRizOViwUahKGSjJdz1SU=
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.1.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
Expand Down Expand Up @@ -299,6 +302,7 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/nbd-wtf/go-nostr v0.40.1 h1:+ogxn+CeRwjQSMSU161fOxKWtVWTEz/p++X4O8VKhMw=
github.com/nbd-wtf/go-nostr v0.40.1/go.mod h1:FBa4FBJO7NuANvkeKSlrf0BIyxGufmrUbuelr6Q4Ick=
Expand Down Expand Up @@ -441,6 +445,7 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0 h1:opwv08VbCZ8iecIWs+McMdHRcAXzjAeda3uG2kI/hcA=
Expand Down
20 changes: 17 additions & 3 deletions server/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ var (
"badger": {},
}
supportedDbs = supportedType{
"badger": {},
"sqlite": {},
"badger": {},
"sqlite": {},
"postgres": {},
}
supportedSchedulers = supportedType{
"gocron": {},
Expand Down Expand Up @@ -59,6 +60,7 @@ type Config struct {
DbType string
EventDbType string
DbDir string
DbUrl string
EventDbDir string
RoundInterval int64
SchedulerType string
Expand Down Expand Up @@ -106,6 +108,7 @@ var (
Port = "PORT"
EventDbType = "EVENT_DB_TYPE"
DbType = "DB_TYPE"
DbUrl = "DB_URL"
SchedulerType = "SCHEDULER_TYPE"
TxBuilderType = "TX_BUILDER_TYPE"
LogLevel = "LOG_LEVEL"
Expand Down Expand Up @@ -136,7 +139,7 @@ var (
defaultDatadir = common.AppDataDir("arkd", false)
defaultRoundInterval = 30
DefaultPort = 7070
defaultDbType = "sqlite"
defaultDbType = "postgres"
defaultEventDbType = "badger"
defaultSchedulerType = "gocron"
defaultTxBuilderType = "covenantless"
Expand Down Expand Up @@ -195,6 +198,14 @@ func LoadConfig() (*Config, error) {

dbPath := filepath.Join(viper.GetString(Datadir), "db")

var dbUrl string
if viper.GetString(DbType) == "postgres" {
dbUrl = viper.GetString(DbUrl)
if dbUrl == "" {
return nil, fmt.Errorf("DB_URL not provided")
}
}

return &Config{
Datadir: viper.GetString(Datadir),
WalletAddr: viper.GetString(WalletAddr),
Expand All @@ -206,6 +217,7 @@ func LoadConfig() (*Config, error) {
TxBuilderType: viper.GetString(TxBuilderType),
NoTLS: viper.GetBool(NoTLS),
DbDir: dbPath,
DbUrl: dbUrl,
EventDbDir: dbPath,
LogLevel: viper.GetInt(LogLevel),
VtxoTreeExpiry: determineLocktimeType(viper.GetInt64(VtxoTreeExpiry)),
Expand Down Expand Up @@ -400,6 +412,8 @@ func (c *Config) repoManager() error {
dataStoreConfig = []interface{}{c.DbDir, logger}
case "sqlite":
dataStoreConfig = []interface{}{c.DbDir}
case "postgres":
dataStoreConfig = []interface{}{c.DbUrl}
default:
return fmt.Errorf("unknown db type")
}
Expand Down
18 changes: 9 additions & 9 deletions server/internal/core/application/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,15 @@ func (a *adminService) CreateNotes(ctx context.Context, value uint32, quantity i
Txid: bip322Input.OutPoint.Hash.String(),
VOut: bip322Input.OutPoint.Index,
},
Amount: uint64(note.Value),
PubKey: hex.EncodeToString(bip322Input.WitnessUtxo.PkScript[2:]),
RoundTxid: "",
SpentBy: "",
Spent: false,
Redeemed: false,
Swept: false,
CreatedAt: now,
RedeemTx: "",
Amount: uint64(note.Value),
PubKey: hex.EncodeToString(bip322Input.WitnessUtxo.PkScript[2:]),
CommitmentTxid: "",
SpentBy: "",
Spent: false,
Redeemed: false,
Swept: false,
CreatedAt: now,
RedeemTx: "",
}

notes = append(notes, note.String())
Expand Down
12 changes: 6 additions & 6 deletions server/internal/core/application/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (i *indexerService) GetVtxoChain(ctx context.Context, vtxoKey Outpoint, pag
return &VtxoChainResp{
Chain: chain,
Page: pageResp,
RootCommitmentTxid: vtxo.RoundTxid,
RootCommitmentTxid: vtxo.CommitmentTxid,
Depth: getMaxDepth(chainMap),
}, nil
}
Expand Down Expand Up @@ -332,7 +332,7 @@ func (i *indexerService) buildChain(
if !vtxo.IsPending() {
txs := chain[key].Txs
txs = append(txs, ChainTx{
Txid: vtxo.RoundTxid,
Txid: vtxo.CommitmentTxid,
Type: "commitment",
})
chain[key] = ChainWithExpiry{
Expand Down Expand Up @@ -480,7 +480,7 @@ func (i *indexerService) vtxosToTxs(
continue // settlement or change, ignore
}

commitmentTxid := vtxo.RoundTxid
commitmentTxid := vtxo.CommitmentTxid
virtualTxid := ""
settled := !vtxo.IsPending()
settledBy := ""
Expand Down Expand Up @@ -539,7 +539,7 @@ func (i *indexerService) vtxosToTxs(
vtxo = vtxos[0]
}

commitmentTxid := vtxo.RoundTxid
commitmentTxid := vtxo.CommitmentTxid
virtualTxid := ""
if vtxo.IsPending() {
virtualTxid = vtxo.Txid
Expand Down Expand Up @@ -568,7 +568,7 @@ func findVtxosSpentInSettlement(vtxos []domain.Vtxo, vtxo domain.Vtxo) []domain.
if vtxo.IsPending() {
return nil
}
return findVtxosSpent(vtxos, vtxo.RoundTxid)
return findVtxosSpent(vtxos, vtxo.CommitmentTxid)
}

func findVtxosSpent(vtxos []domain.Vtxo, id string) []domain.Vtxo {
Expand Down Expand Up @@ -601,7 +601,7 @@ func findVtxosSpentInPayment(vtxos []domain.Vtxo, vtxo domain.Vtxo) []domain.Vtx
func findVtxosResultedFromSpentBy(vtxos []domain.Vtxo, spentByTxid string) []domain.Vtxo {
var result []domain.Vtxo
for _, v := range vtxos {
if !v.IsPending() && v.RoundTxid == spentByTxid {
if !v.IsPending() && v.CommitmentTxid == spentByTxid {
result = append(result, v)
break
}
Expand Down
Loading
Loading