Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
59 changes: 42 additions & 17 deletions .github/workflows/migration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
jobs:
migration-test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:15
Expand All @@ -21,7 +21,7 @@ jobs:
--health-retries 5
ports:
- 5432:5432

redis:
image: redis:7
options: >-
Expand All @@ -31,20 +31,20 @@ jobs:
--health-retries 5
ports:
- 6379:6379

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24.2'

- name: Download go-wrappers
run: |
git clone https://github.com/vultisig/go-wrappers.git ../go-wrappers

- name: Create test config
run: |
cat > dca.json <<EOF
Expand Down Expand Up @@ -82,43 +82,43 @@ jobs:
}
}
EOF

- name: Build dca binary
run: |
export LD_LIBRARY_PATH=../go-wrappers/includes/linux/:$LD_LIBRARY_PATH
go build -o dca cmd/dca/server/*.go

- name: Run dca and check migrations
run: |
export LD_LIBRARY_PATH=../go-wrappers/includes/linux/:$LD_LIBRARY_PATH
export VS_CONFIG_NAME=dca

# Start dca in background
./dca &
DCA_PID=$!

# Give it time to run migrations
sleep 10

# Check if process is still running
if ! kill -0 $DCA_PID 2>/dev/null; then
echo "DCA process failed to start"
exit 1
fi

# Check if migrations ran successfully by verifying tables exist
PGPASSWORD=mypassword psql -h localhost -U myuser -d vultisig-plugin -c "\dt" | grep -E "(plugin_policies|time_triggers|transaction_history) | wc -l" > /dev/null
if [ $? -ne 0 ]; then
echo "Migrations did not run successfully - expected tables not found"
kill $DCA_PID
exit 1
fi

echo "All migrations ran successfully!"

# Stop dca
kill $DCA_PID

- name: Check migration integrity
run: |
# Verify goose migrations table exists and has entries
Expand All @@ -127,5 +127,30 @@ jobs:
echo "Goose migrations table not found or empty"
exit 1
fi

echo "Migration integrity check passed!"

echo "Migration integrity check passed!"

- name: Check schema updates
run: |
# Dump current schema
PGPASSWORD=mypassword pg_dump -h localhost -U myuser -d vultisig-plugin \
--schema-only \
--no-comments \
--no-owner \
--quote-all-identifiers \
-T public.goose_db_version \
-T public.goose_db_version_id_seq | sed \
-e '/^--.*/d' \
-e '/^SET /d' \
-e '/^SELECT pg_catalog./d' \
-e 's/"public"\.//' | awk '/./ { e=0 } /^$$/ { e += 1 } e <= 1' \
> ./current_schema.sql

# Compare with repository schema
if ! diff -u ./storage/postgres/schema/schema.sql ./current_schema.sql; then
echo "Schema has changed but schema.sql was not updated!"
echo "Please run 'make dump-schema CONFIG=config-plugin.yaml' to update the schema file."
exit 1
fi

echo "Schema is up to date!"
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,24 @@ plugin-server:
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/vultisigner/main.go

plugin-worker:
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/worker/main.go
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/worker/main.go

# Dump database schema
# Usage: make dump-schema CONFIG=config-plugin.yaml
dump-schema:
@if [ -z "$(CONFIG)" ]; then \
echo "Error: CONFIG parameter is required. Usage: make dump-schema CONFIG=config-plugin.yaml"; \
exit 1; \
fi
@DSN=$$(yq eval '.database.dsn' $(CONFIG)); \
pg_dump "$$DSN" --schema-only \
--no-comments \
--no-owner \
--quote-all-identifiers \
-T public.goose_db_version \
-T public.goose_db_version_id_seq | sed \
-e '/^--.*/d' \
-e '/^SET /d' \
-e '/^SELECT pg_catalog./d' \
-e 's/"public"\.//' | awk '/./ { e=0 } /^$$/ { e += 1 } e <= 1' \
> ./storage/postgres/schema/schema.sql
141 changes: 141 additions & 0 deletions storage/postgres/schema/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

CREATE TYPE "plugin_id" AS ENUM (
'vultisig-dca-0000',
'vultisig-payroll-0000',
'vultisig-fees-feee'
);
Comment on lines +2 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Typo in enum value ‘vultisig-fees-feee’.
The third entry in the plugin_id enum has an extra ‘e’. Please confirm the intended plugin identifier and correct this value.

🤖 Prompt for AI Agents
In storage/postgres/schema/schema.sql around lines 2 to 6, there is a typo in
the third enum value of the "plugin_id" type: 'vultisig-fees-feee' has an extra
'e'. Confirm the correct plugin identifier and update this enum value to the
intended spelling, removing the extra 'e' if it is a mistake.


CREATE TYPE "transaction_status" AS ENUM (
'PENDING',
'SIGNING_FAILED',
'SIGNED',
'BROADCAST',
'MINED',
'REJECTED'
);

CREATE TYPE "trigger_status" AS ENUM (
'PENDING',
'RUNNING'
);

CREATE TYPE "tx_indexer_status" AS ENUM (
'PROPOSED',
'VERIFIED',
'SIGNED'
);

CREATE TYPE "tx_indexer_status_onchain" AS ENUM (
'PENDING',
'SUCCESS',
'FAIL'
);

CREATE TABLE "plugin_policies" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"public_key" "text" NOT NULL,
"plugin_id" "plugin_id" NOT NULL,
"plugin_version" "text" NOT NULL,
"policy_version" integer NOT NULL,
"signature" "text" NOT NULL,
"recipe" "text" NOT NULL,
"active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL
);

CREATE TABLE "time_triggers" (
"id" integer NOT NULL,
"policy_id" "uuid" NOT NULL,
"cron_expression" "text" NOT NULL,
"start_time" timestamp without time zone NOT NULL,
"end_time" timestamp without time zone,
"frequency" integer NOT NULL,
"interval" integer NOT NULL,
"last_execution" timestamp without time zone,
"status" "trigger_status" NOT NULL,
"created_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP
);

CREATE SEQUENCE "time_triggers_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE "time_triggers_id_seq" OWNED BY "public"."time_triggers"."id";

CREATE TABLE "transaction_history" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"policy_id" "uuid" NOT NULL,
"tx_body" "text" NOT NULL,
"tx_hash" "text" NOT NULL,
"status" "transaction_status" NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"(),
"updated_at" timestamp with time zone DEFAULT "now"(),
"metadata" "jsonb",
"error_message" "text"
);

CREATE TABLE "tx_indexer" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"plugin_id" character varying(255) NOT NULL,
"tx_hash" character varying(255),
"chain_id" integer NOT NULL,
"policy_id" "uuid" NOT NULL,
"from_public_key" character varying(255) NOT NULL,
"proposed_tx_hex" "text" NOT NULL,
"status" "tx_indexer_status" DEFAULT 'PROPOSED'::"public"."tx_indexer_status" NOT NULL,
"status_onchain" "tx_indexer_status_onchain",
"lost" boolean DEFAULT false NOT NULL,
"broadcasted_at" timestamp without time zone,
"created_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
"updated_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);

ALTER TABLE ONLY "time_triggers" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."time_triggers_id_seq"'::"regclass");

ALTER TABLE ONLY "plugin_policies"
ADD CONSTRAINT "plugin_policies_pkey" PRIMARY KEY ("id");

ALTER TABLE ONLY "time_triggers"
ADD CONSTRAINT "time_triggers_pkey" PRIMARY KEY ("id");

ALTER TABLE ONLY "transaction_history"
ADD CONSTRAINT "transaction_history_pkey" PRIMARY KEY ("id");

ALTER TABLE ONLY "tx_indexer"
ADD CONSTRAINT "tx_indexer_pkey" PRIMARY KEY ("id");

ALTER TABLE ONLY "transaction_history"
ADD CONSTRAINT "unique_tx_hash" UNIQUE ("tx_hash");

Comment on lines +112 to +114

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Redundant index on transaction_history.tx_hash.
You already add a unique constraint (unique_tx_hash) which creates an index. The separate idx_transaction_history_tx_hash is unnecessary and should be removed.

Also applies to: 129-129

🤖 Prompt for AI Agents
In storage/postgres/schema/schema.sql around lines 112 to 114 and line 129,
there is a redundant index on transaction_history.tx_hash because the unique
constraint unique_tx_hash already creates an index. Remove the separate index
named idx_transaction_history_tx_hash to avoid duplication and unnecessary
overhead.

CREATE INDEX "idx_plugin_policies_active" ON "plugin_policies" USING "btree" ("active");

CREATE INDEX "idx_plugin_policies_plugin_id" ON "plugin_policies" USING "btree" ("plugin_id");

CREATE INDEX "idx_plugin_policies_public_key" ON "plugin_policies" USING "btree" ("public_key");

CREATE INDEX "idx_time_triggers_policy_id" ON "time_triggers" USING "btree" ("policy_id");

CREATE INDEX "idx_time_triggers_start_time" ON "time_triggers" USING "btree" ("start_time");

CREATE INDEX "idx_transaction_history_policy_id" ON "transaction_history" USING "btree" ("policy_id");

CREATE INDEX "idx_transaction_history_status" ON "transaction_history" USING "btree" ("status");

CREATE INDEX "idx_transaction_history_tx_hash" ON "transaction_history" USING "btree" ("tx_hash");

CREATE INDEX "idx_tx_indexer_status_onchain_lost" ON "tx_indexer" USING "btree" ("status_onchain", "lost");

ALTER TABLE ONLY "transaction_history"
ADD CONSTRAINT "fk_policy" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");

ALTER TABLE ONLY "time_triggers"
ADD CONSTRAINT "time_triggers_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");
Comment on lines +133 to +137

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Duplicate foreign key for transaction_history.policy_id.
The policy_id FK on transaction_history is applied twice (fk_policy and transaction_history_policy_id_fkey). Remove one to avoid conflicts during schema application.

🤖 Prompt for AI Agents
In storage/postgres/schema/schema.sql around lines 133 to 137, there are
duplicate foreign key constraints on transaction_history.policy_id named
fk_policy and transaction_history_policy_id_fkey. Remove one of these foreign
key constraints to avoid conflicts during schema application, ensuring only a
single foreign key constraint exists for transaction_history.policy_id
referencing plugin_policies(id).


ALTER TABLE ONLY "transaction_history"
ADD CONSTRAINT "transaction_history_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");