bsv-wallet-postgres 0.6.0 → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -123
- data/LICENSE +23 -80
- data/db/migrations/001_create_schema.rb +261 -0
- data/db/migrations/002_action_id_cascade.rb +66 -0
- data/db/migrations/003_schema_constraints.rb +297 -0
- data/db/migrations/004_drop_tx_reqs.rb +32 -0
- data/lib/bsv/wallet/postgres/action.rb +80 -0
- data/lib/bsv/wallet/postgres/action_label.rb +14 -0
- data/lib/bsv/wallet/postgres/arc_adapter.rb +32 -0
- data/lib/bsv/wallet/postgres/basket.rb +13 -0
- data/lib/bsv/wallet/postgres/block.rb +13 -0
- data/lib/bsv/wallet/postgres/broadcast.rb +87 -0
- data/lib/bsv/wallet/postgres/broadcast_callback.rb +54 -0
- data/lib/bsv/wallet/postgres/broadcast_queue.rb +98 -0
- data/lib/bsv/wallet/postgres/certificate.rb +13 -0
- data/lib/bsv/wallet/postgres/certificate_field.rb +13 -0
- data/lib/bsv/wallet/postgres/display_txid.rb +25 -0
- data/lib/bsv/wallet/postgres/input.rb +14 -0
- data/lib/bsv/wallet/postgres/label.rb +15 -0
- data/lib/bsv/wallet/postgres/output.rb +64 -0
- data/lib/bsv/wallet/postgres/output_basket.rb +15 -0
- data/lib/bsv/wallet/postgres/output_detail.rb +12 -0
- data/lib/bsv/wallet/postgres/output_tag.rb +14 -0
- data/lib/bsv/wallet/postgres/proof_store.rb +109 -0
- data/lib/bsv/wallet/postgres/setting.rb +32 -0
- data/lib/bsv/wallet/postgres/spendable.rb +12 -0
- data/lib/bsv/wallet/postgres/store.rb +580 -0
- data/lib/bsv/wallet/postgres/tag.rb +15 -0
- data/lib/bsv/wallet/postgres/tx_proof.rb +16 -0
- data/lib/bsv/wallet/postgres/utxo_pool.rb +58 -0
- data/lib/bsv/wallet/postgres/version.rb +9 -0
- data/lib/bsv/wallet/postgres.rb +77 -0
- data/lib/bsv-wallet-postgres.rb +1 -1
- metadata +49 -35
- data/lib/bsv/wallet_postgres/migrations/001_create_wallet_tables.rb +0 -58
- data/lib/bsv/wallet_postgres/migrations/002_add_output_state.rb +0 -33
- data/lib/bsv/wallet_postgres/migrations/003_add_wallet_settings.rb +0 -20
- data/lib/bsv/wallet_postgres/migrations/004_add_pending_metadata.rb +0 -69
- data/lib/bsv/wallet_postgres/migrations/005_add_txid_unique_index.rb +0 -27
- data/lib/bsv/wallet_postgres/migrations/006_create_broadcast_jobs.rb +0 -68
- data/lib/bsv/wallet_postgres/postgres_store.rb +0 -502
- data/lib/bsv/wallet_postgres/solid_queue_adapter.rb +0 -328
- data/lib/bsv/wallet_postgres/version.rb +0 -7
- data/lib/bsv/wallet_postgres.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 828e610f2e6640f09db4de4dddad8e86fa25b6416610b57c026852c73abdb784
|
|
4
|
+
data.tar.gz: 3d89fc8d066a3c4363a295942e1644efaa1e8535971ab6a4cbe81f5a01a282ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98e82a0962d6005cb7c76eee3bee04a0a8a52e834462056a34ce417edb5b1c5bc46eef792c8ab32e33e50e02d454b56e5dfd72017bd42a86aa248146cf767732
|
|
7
|
+
data.tar.gz: 6598af72c9d416a533e4939a2a1746034d006b77f86395bdebd4cd4863bd34ae68e2548c88ace670adecddce2fd5f6ad7fef4065b14c1512a24d29b95ae9d2c7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,133 +1,27 @@
|
|
|
1
|
-
# Changelog
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## [0.100.0] - 2026-05-13
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## 0.6.0 — 2026-04-21
|
|
9
|
-
|
|
10
|
-
### Added
|
|
11
|
-
- `update_output_basket` implementation on `PostgresStore`, matching the new Store interface method
|
|
12
|
-
|
|
13
|
-
### Changed
|
|
14
|
-
- Updated for bsv-wallet 0.10.0 namespace changes (`Interface`, `Client`, `Store`)
|
|
15
|
-
|
|
16
|
-
## 0.5.0 — 2026-04-16
|
|
17
|
-
|
|
18
|
-
### Changed — **Breaking**
|
|
19
|
-
|
|
20
|
-
- `SolidQueueAdapter` now sets the wallet action status to `'unproven'` instead of `'completed'` on successful broadcast. Aligns with the status taxonomy change in bsv-wallet 0.9.0 (HLR #455). The `wallet_broadcast_jobs` row status is unchanged (job queue lifecycle is a separate domain).
|
|
21
|
-
|
|
22
|
-
### Added
|
|
23
|
-
|
|
24
|
-
- `BroadcastQueue#broadcast_enabled?` interface method; `SolidQueueAdapter` returns `true` (broadcaster required at construction time). Enables `WalletClient#broadcast_enabled?` to detect a queue-embedded broadcaster.
|
|
25
|
-
|
|
26
|
-
### Changed
|
|
27
|
-
|
|
28
|
-
- Minimum `bsv-wallet` version raised from `>= 0.6.0` to `>= 0.9.0`. Required for the new status taxonomy and `broadcast_enabled?` interface.
|
|
29
|
-
|
|
30
|
-
## 0.4.0 — 2026-04-12
|
|
31
|
-
|
|
32
|
-
### Added
|
|
33
|
-
- `BSV::Wallet::SolidQueueAdapter` — PostgreSQL-backed async broadcast queue implementing the `BroadcastQueue` interface
|
|
34
|
-
- Migration 006: `wallet_broadcast_jobs` table with `FOR UPDATE SKIP LOCKED` polling support
|
|
35
|
-
- Background worker thread broadcasts transactions and promotes/rolls back wallet state
|
|
36
|
-
- Recovery on restart via stale `locked_at` detection
|
|
37
|
-
- Idempotent enqueue on duplicate txid (crash recovery)
|
|
38
|
-
- `MAX_ATTEMPTS` enforcement (5) prevents infinite retry loops
|
|
39
|
-
- Guard refuses MemoryStore attachment
|
|
40
|
-
|
|
41
|
-
### Fixed
|
|
42
|
-
- Migration timestamps use `timestamptz` (matching migration 004 pattern)
|
|
43
|
-
- `start()` check-and-set is atomic under mutex (prevents TOCTOU double-spawn)
|
|
44
|
-
- Deserialization failures mark job as failed immediately (no tight retry loop)
|
|
45
|
-
|
|
46
|
-
## 0.3.1 — 2026-04-12
|
|
47
|
-
|
|
48
|
-
### Fixed
|
|
49
|
-
- `update_action_status` now scopes to a single row by primary key, preventing unintended multi-row updates when duplicate txids exist
|
|
50
|
-
- Added migration 005: unique index on `wallet_actions.txid` enforcing one action per transaction
|
|
51
|
-
|
|
52
|
-
## 0.3.0 — 2026-04-12
|
|
5
|
+
First release of the PostgreSQL adapter for the Ruby BRC-100 wallet.
|
|
53
6
|
|
|
54
7
|
### Added
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- **
|
|
63
|
-
`no_send` columns and a partial index on `(state, basket)` for spendable
|
|
64
|
-
rows (#353)
|
|
65
|
-
- **`find_spendable_outputs(basket:, min_satoshis:, sort_order:)`** — query
|
|
66
|
-
spendable outputs with backward-compatible COALESCE for legacy rows (#354)
|
|
67
|
-
- **`update_output_state(outpoint, new_state, ...)`** — transition output
|
|
68
|
-
state with JSONB data synchronisation (#354)
|
|
69
|
-
- **`lock_utxos(outpoints, reference:, no_send:)`** — atomic
|
|
70
|
-
`UPDATE ... WHERE state = 'spendable' RETURNING` pattern for concurrent
|
|
71
|
-
safety (#355)
|
|
72
|
-
- **`release_stale_pending!(timeout:)`** — recover stuck pending outputs,
|
|
73
|
-
exempting `no_send` locks (#355)
|
|
74
|
-
- **PostgresStore settings methods** — `store_setting` / `find_setting`
|
|
75
|
-
|
|
76
|
-
### Fixed
|
|
77
|
-
|
|
78
|
-
- **Spendable boolean sync** — `update_output_state`, `lock_utxos`, and
|
|
79
|
-
`release_stale_pending!` now keep the legacy `spendable` column in sync
|
|
80
|
-
with the `state` column; `filter_outputs` uses dual-column WHERE clause
|
|
8
|
+
- **Schema** — actions, outputs, inputs, spendable, baskets, labels, tags, certificates, tx_proofs, blocks, broadcasts
|
|
9
|
+
- **Store** — full BRC-100 action lifecycle: create, sign, promote, abort, reap, list, query
|
|
10
|
+
- **ProofStore** — merkle proof persistence with block normalization
|
|
11
|
+
- **UTXOPool** — UTXO selection with sizing strategy and limp mode
|
|
12
|
+
- **BroadcastQueue** — broadcast lifecycle management
|
|
13
|
+
- **Pushable/Fetchable** — Broadcast and Action adopt entity-driven network interaction
|
|
14
|
+
- **Migrations** — sequential schema migrations with constraint enforcement
|
|
15
|
+
- **Database trigger** — prevents outbound outputs from entering the spendable set
|
|
81
16
|
|
|
82
17
|
### Changed
|
|
18
|
+
- **blocks table** — normalized from tx_proofs; stores block headers independently
|
|
19
|
+
- **tx_reqs removed** — replaced by structural queries via Fetchable pattern
|
|
83
20
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## 0.1.0 — 2026-04-09
|
|
87
|
-
|
|
88
|
-
Initial release of `bsv-wallet-postgres`, a PostgreSQL-backed
|
|
89
|
-
`BSV::Wallet::StorageAdapter` implementation. Unblocks production
|
|
90
|
-
deployments of `bsv-wallet` where state has to survive container
|
|
91
|
-
restarts, and makes multi-instance wallet services possible for the
|
|
92
|
-
first time.
|
|
21
|
+
## [0.1.0] - 2026-05-01
|
|
93
22
|
|
|
94
23
|
### Added
|
|
95
24
|
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(53 examples), plus 10 postgres-specific specs covering upsert
|
|
100
|
-
semantics, GIN tag queries, JSONB attribute containment, concurrent
|
|
101
|
-
inserts, and migration idempotency.
|
|
102
|
-
|
|
103
|
-
- **Shipped Sequel migration** at
|
|
104
|
-
`lib/bsv/wallet_postgres/migrations/001_create_wallet_tables.rb`.
|
|
105
|
-
Five tables (wallet_outputs, wallet_actions, wallet_certificates,
|
|
106
|
-
wallet_proofs, wallet_transactions) with JSONB data columns,
|
|
107
|
-
dedicated indexed columns for filter paths, and GIN indexes on the
|
|
108
|
-
`tags` / `labels` arrays.
|
|
109
|
-
|
|
110
|
-
- **`PostgresStore.migrate!(db)`** convenience
|
|
111
|
-
wrapper over `Sequel::Migrator.run` so consumers can apply the
|
|
112
|
-
shipped schema with a single call. Operators who prefer their own
|
|
113
|
-
migration framework can copy the migration file instead.
|
|
114
|
-
|
|
115
|
-
- **Docs** at `docs/guides/wallet-postgres.md` with
|
|
116
|
-
a 30-second quickstart, schema overview, and production
|
|
117
|
-
considerations (pool sizing, multi-instance, backups,
|
|
118
|
-
thread-safety).
|
|
119
|
-
|
|
120
|
-
### Infrastructure
|
|
121
|
-
|
|
122
|
-
- **CI postgres service**. The GitHub Actions test job now runs a
|
|
123
|
-
Postgres 16 container and exposes `DATABASE_URL` to rspec, so the
|
|
124
|
-
`:postgres`-tagged specs run against a live database on every
|
|
125
|
-
Ruby matrix row (2.7 → 3.4). Local developers without Postgres
|
|
126
|
-
still get a green suite — those specs skip gracefully.
|
|
127
|
-
|
|
128
|
-
### Dependencies
|
|
129
|
-
|
|
130
|
-
- `bsv-wallet-postgres` runtime: `bsv-wallet >= 0.3.4, < 1.0`,
|
|
131
|
-
`sequel ~> 5`, `pg ~> 1`. The wallet floor matches the pinning style
|
|
132
|
-
`bsv-wallet` uses for its `bsv-sdk` dependency so security releases
|
|
133
|
-
propagate.
|
|
25
|
+
- Initial 17-table PostgreSQL schema migration
|
|
26
|
+
- Sequel models for all wallet tables
|
|
27
|
+
- Connection management module
|
data/LICENSE
CHANGED
|
@@ -1,86 +1,29 @@
|
|
|
1
|
-
Open BSV License
|
|
2
|
-
Zug, Switzerland (CHE-427.008.338) ("Licensor"), to you as a user (henceforth
|
|
3
|
-
"You", "User" or "Licensee").
|
|
1
|
+
Open BSV License
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
meanings:
|
|
3
|
+
Copyright (c) 2026 Simon Bettison
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
blocks which has been produced in a manner which is consistent with the
|
|
23
|
-
rules set forth in the Network Access Rules.
|
|
24
|
-
|
|
25
|
-
"Network Access Rules" or "Rules" means the set of rules regulating the
|
|
26
|
-
relationship between BSV Association and the nodes on BSV based on the Bitcoin
|
|
27
|
-
Protocol rules and those set out in the Bitcoin White Paper, and available here
|
|
28
|
-
https://bsvblockchain.org/network-access-rules.
|
|
29
|
-
|
|
30
|
-
"Software" means the software the subject of this licence, including any/all
|
|
31
|
-
intellectual property rights therein and associated documentation files.
|
|
32
|
-
|
|
33
|
-
BSV Association grants permission, free of charge and on a non-exclusive and
|
|
34
|
-
revocable basis, to any person obtaining a copy of the Software to deal in the
|
|
35
|
-
Software without restriction, including without limitation the rights to use,
|
|
36
|
-
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
37
|
-
Software, and to permit persons to whom the Software is furnished to do so,
|
|
38
|
-
subject to and conditioned upon the following conditions:
|
|
39
|
-
|
|
40
|
-
1 - The text "© BSV Association," and this license shall be included in all
|
|
41
|
-
copies or substantial portions of the Software.
|
|
42
|
-
2 - The Software, and any software that is derived from the Software or parts
|
|
43
|
-
thereof, must only be used on the BSV Blockchains.
|
|
44
|
-
|
|
45
|
-
For the avoidance of doubt, this license is granted subject to and conditioned
|
|
46
|
-
upon your compliance with these terms only. In the event of non-compliance, the
|
|
47
|
-
license shall extinguish and you can be enjoined from violating BSV's
|
|
48
|
-
intellectual property rights (incl. damages and similar related claims).
|
|
49
|
-
|
|
50
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
51
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES REGARDING ENTITLEMENT,
|
|
52
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
53
|
-
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS THEREOF BE LIABLE FOR ANY CLAIM,
|
|
54
|
-
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
55
|
-
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
56
|
-
DEALINGS IN THE SOFTWARE.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Version 0.1.1 of the Bitcoin SV software, and prior versions of software upon
|
|
60
|
-
which it was based, were licensed under the MIT License, which is included below.
|
|
61
|
-
|
|
62
|
-
The MIT License (MIT)
|
|
63
|
-
|
|
64
|
-
Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
65
|
-
Copyright (c) 2009-2015 Bitcoin Developers
|
|
66
|
-
Copyright (c) 2009-2017 The Bitcoin Core developers
|
|
67
|
-
Copyright (c) 2017 The Bitcoin ABC developers
|
|
68
|
-
Copyright (c) 2018 Bitcoin Association for BSV
|
|
69
|
-
Copyright (c) 2023 BSV Association
|
|
70
|
-
|
|
71
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
72
|
-
this software and associated documentation files (the "Software"), to deal in
|
|
73
|
-
the Software without restriction, including without limitation the rights to
|
|
74
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
75
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
76
|
-
subject to the following conditions:
|
|
77
|
-
|
|
78
|
-
The above copyright notice and this permission notice shall be included in all
|
|
79
|
-
copies or substantial portions of the Software.
|
|
15
|
+
2. The Software, and any software that is derived from the Software or parts
|
|
16
|
+
thereof, can only be used on the Bitcoin SV blockchains. The Bitcoin SV
|
|
17
|
+
blockchains are defined, for purposes of this license, as the Bitcoin
|
|
18
|
+
blockchain containing block combinator
|
|
19
|
+
000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b and the
|
|
20
|
+
test combinator
|
|
21
|
+
00000000a6e140e27e4eb65e3cb4e4e07b1a0e3964b396c97a4b36e4ac06eb54.
|
|
80
22
|
|
|
81
23
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
82
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
83
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
84
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
85
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
86
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
29
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
extension :pg_enum
|
|
6
|
+
|
|
7
|
+
# Enum: broadcast intent for actions
|
|
8
|
+
create_enum(:broadcast_intent, %w[delayed inline none])
|
|
9
|
+
|
|
10
|
+
# 1. blocks — known block headers (chain tracker's local view)
|
|
11
|
+
create_table(:blocks) do
|
|
12
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
13
|
+
column :height, :integer, null: false, unique: true
|
|
14
|
+
column :merkle_root, :bytea, null: false
|
|
15
|
+
column :block_hash, :bytea
|
|
16
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
17
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# 2. tx_proofs — merkle inclusion proofs (settlement evidence)
|
|
21
|
+
create_table(:tx_proofs) do
|
|
22
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
23
|
+
column :wtxid, :bytea, null: false, unique: true
|
|
24
|
+
foreign_key :block_id, :blocks, type: :bigint
|
|
25
|
+
column :block_index, :integer
|
|
26
|
+
column :merkle_path, :bytea
|
|
27
|
+
column :raw_tx, :bytea
|
|
28
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
29
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 2. actions — transaction lifecycle
|
|
33
|
+
create_table(:actions) do
|
|
34
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
35
|
+
foreign_key :tx_proof_id, :tx_proofs, type: :bigint
|
|
36
|
+
column :wtxid, :bytea
|
|
37
|
+
column :reference, :text, unique: true, default: Sequel.function(:gen_random_uuid)
|
|
38
|
+
column :outgoing, :boolean, null: false, default: true
|
|
39
|
+
column :satoshis, :bigint
|
|
40
|
+
column :description, :text
|
|
41
|
+
column :version, :integer
|
|
42
|
+
column :nlocktime, :bigint
|
|
43
|
+
column :broadcast, :broadcast_intent, null: false, default: 'delayed'
|
|
44
|
+
column :raw_tx, :bytea
|
|
45
|
+
column :input_beef, :bytea
|
|
46
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
47
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
48
|
+
|
|
49
|
+
index :wtxid, unique: true, where: Sequel.lit('wtxid IS NOT NULL')
|
|
50
|
+
index :broadcast
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# 3. broadcasts — ARC lifecycle
|
|
54
|
+
create_table(:broadcasts) do
|
|
55
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
56
|
+
foreign_key :action_id, :actions, type: :bigint, null: false, unique: true
|
|
57
|
+
column :broadcast_at, :timestamptz
|
|
58
|
+
column :tx_status, :text
|
|
59
|
+
column :arc_status, :integer
|
|
60
|
+
column :block_hash, :bytea
|
|
61
|
+
column :block_height, :integer
|
|
62
|
+
column :merkle_path, :bytea
|
|
63
|
+
column :extra_info, :text
|
|
64
|
+
column :competing_txs, 'text[]'
|
|
65
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
66
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# 4. baskets — output grouping with replenishment policy
|
|
70
|
+
create_table(:baskets) do
|
|
71
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
72
|
+
column :name, :text, null: false
|
|
73
|
+
column :target_count, :integer
|
|
74
|
+
column :target_value, :integer
|
|
75
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
76
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
77
|
+
column :deleted_at, :timestamptz
|
|
78
|
+
|
|
79
|
+
index :name, unique: true, where: Sequel.lit('deleted_at IS NULL')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# 5. outputs — immutable append-only log
|
|
83
|
+
create_table(:outputs) do
|
|
84
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
85
|
+
foreign_key :action_id, :actions, type: :bigint, null: false
|
|
86
|
+
column :satoshis, :bigint, null: false
|
|
87
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
88
|
+
column :locking_script, :bytea
|
|
89
|
+
column :vout, :integer, null: false
|
|
90
|
+
column :sender_identity_key, :text
|
|
91
|
+
column :derivation_prefix, :text
|
|
92
|
+
column :derivation_suffix, :text
|
|
93
|
+
|
|
94
|
+
unique %i[action_id vout]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# 6. spendable — the UTXO set (~28 bytes/row)
|
|
98
|
+
create_table(:spendable) do
|
|
99
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
100
|
+
foreign_key :output_id, :outputs, type: :bigint, null: false, unique: true
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# 7. output_details — display and application metadata
|
|
104
|
+
create_table(:output_details) do
|
|
105
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
106
|
+
foreign_key :output_id, :outputs, type: :bigint, null: false, unique: true
|
|
107
|
+
column :change, :boolean, null: false, default: false
|
|
108
|
+
column :type, :text
|
|
109
|
+
column :purpose, :text
|
|
110
|
+
column :provided_by, :text
|
|
111
|
+
column :description, :text
|
|
112
|
+
column :custom_instructions, :text
|
|
113
|
+
column :script_length, :integer
|
|
114
|
+
column :script_offset, :integer
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# 8. output_baskets — basket membership
|
|
118
|
+
create_table(:output_baskets) do
|
|
119
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
120
|
+
foreign_key :output_id, :outputs, type: :bigint, null: false, unique: true
|
|
121
|
+
foreign_key :basket_id, :baskets, type: :bigint, null: false
|
|
122
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
123
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
124
|
+
|
|
125
|
+
index :basket_id
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# 9. inputs — structural lock mechanism
|
|
129
|
+
create_table(:inputs) do
|
|
130
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
131
|
+
foreign_key :action_id, :actions, type: :bigint, null: false, on_delete: :cascade
|
|
132
|
+
foreign_key :output_id, :outputs, type: :bigint, null: false
|
|
133
|
+
column :vin, :integer, null: false
|
|
134
|
+
column :nsequence, :bigint, null: false, default: 4_294_967_295
|
|
135
|
+
column :description, :text
|
|
136
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
137
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
138
|
+
|
|
139
|
+
unique :output_id
|
|
140
|
+
unique %i[action_id vin]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# 10. labels — label definitions
|
|
144
|
+
create_table(:labels) do
|
|
145
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
146
|
+
column :label, :text, null: false
|
|
147
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
148
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
149
|
+
column :deleted_at, :timestamptz
|
|
150
|
+
|
|
151
|
+
index :label, unique: true, where: Sequel.lit('deleted_at IS NULL')
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# 11. action_labels — join table
|
|
155
|
+
create_table(:action_labels) do
|
|
156
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
157
|
+
foreign_key :action_id, :actions, type: :bigint, null: false
|
|
158
|
+
foreign_key :label_id, :labels, type: :bigint, null: false
|
|
159
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
160
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
161
|
+
column :deleted_at, :timestamptz
|
|
162
|
+
|
|
163
|
+
unique %i[action_id label_id]
|
|
164
|
+
index :label_id
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# 12. tags — tag definitions
|
|
168
|
+
create_table(:tags) do
|
|
169
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
170
|
+
column :tag, :text, null: false
|
|
171
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
172
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
173
|
+
column :deleted_at, :timestamptz
|
|
174
|
+
|
|
175
|
+
index :tag, unique: true, where: Sequel.lit('deleted_at IS NULL')
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# 13. output_tags — join table
|
|
179
|
+
create_table(:output_tags) do
|
|
180
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
181
|
+
foreign_key :output_id, :outputs, type: :bigint, null: false
|
|
182
|
+
foreign_key :tag_id, :tags, type: :bigint, null: false
|
|
183
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
184
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
185
|
+
column :deleted_at, :timestamptz
|
|
186
|
+
|
|
187
|
+
unique %i[output_id tag_id]
|
|
188
|
+
index :tag_id
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# 14. certificates — identity certificates (BRC-52)
|
|
192
|
+
create_table(:certificates) do
|
|
193
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
194
|
+
column :type, :text, null: false
|
|
195
|
+
column :subject, :text
|
|
196
|
+
column :serial_number, :text, null: false
|
|
197
|
+
column :certifier, :text, null: false
|
|
198
|
+
column :verifier, :text
|
|
199
|
+
column :revocation_outpoint, :text
|
|
200
|
+
column :signature, :text
|
|
201
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
202
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
203
|
+
column :deleted_at, :timestamptz
|
|
204
|
+
|
|
205
|
+
unique %i[type serial_number certifier]
|
|
206
|
+
index :certifier
|
|
207
|
+
index :subject
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# 15. certificate_fields — per-field encryption for selective revelation
|
|
211
|
+
create_table(:certificate_fields) do
|
|
212
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
213
|
+
foreign_key :certificate_id, :certificates, type: :bigint, null: false, on_delete: :cascade
|
|
214
|
+
column :name, :text, null: false
|
|
215
|
+
column :value, :text
|
|
216
|
+
column :master_key, :text
|
|
217
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
218
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
219
|
+
|
|
220
|
+
unique %i[certificate_id name]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# 16. tx_reqs — proof-harvesting work queue
|
|
224
|
+
create_table(:tx_reqs) do
|
|
225
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
226
|
+
foreign_key :tx_proof_id, :tx_proofs, type: :bigint
|
|
227
|
+
column :wtxid, :bytea, null: false, unique: true
|
|
228
|
+
column :status, :text, null: false, default: 'unmined'
|
|
229
|
+
column :attempts, :integer, null: false, default: 0
|
|
230
|
+
column :notified, :boolean, null: false, default: false
|
|
231
|
+
column :history, :text
|
|
232
|
+
column :notify, :text
|
|
233
|
+
column :batch, :text
|
|
234
|
+
column :raw_tx, :bytea
|
|
235
|
+
column :input_beef, :bytea
|
|
236
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
237
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
238
|
+
|
|
239
|
+
index :status
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# 17. settings — key-value wallet configuration
|
|
243
|
+
create_table(:settings) do
|
|
244
|
+
column :id, :bigint, primary_key: true, identity: :always
|
|
245
|
+
column :key, :text, null: false, unique: true
|
|
246
|
+
column :value, :text
|
|
247
|
+
column :created_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
248
|
+
column :updated_at, :timestamptz, null: false, default: Sequel.function(:now)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
down do
|
|
253
|
+
drop_table :settings, :tx_reqs, :certificate_fields, :certificates,
|
|
254
|
+
:output_tags, :tags, :action_labels, :labels, :inputs,
|
|
255
|
+
:output_baskets, :output_details, :spendable, :outputs,
|
|
256
|
+
:baskets, :broadcasts, :actions, :tx_proofs, :blocks
|
|
257
|
+
|
|
258
|
+
extension :pg_enum
|
|
259
|
+
drop_enum(:broadcast_intent)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
# Add action_id with ON DELETE CASCADE to relationship tables.
|
|
6
|
+
# Denormalized (derivable via output_id -> outputs.action_id) but
|
|
7
|
+
# justified: set once at creation, never changes, enables cascade
|
|
8
|
+
# cleanup — deleting an action automatically removes its spendable
|
|
9
|
+
# entries, basket memberships, and output details.
|
|
10
|
+
add_column :spendable, :action_id, :bigint
|
|
11
|
+
run <<~SQL
|
|
12
|
+
ALTER TABLE spendable
|
|
13
|
+
ADD CONSTRAINT spendable_action_id_fkey
|
|
14
|
+
FOREIGN KEY (action_id) REFERENCES actions (id) ON DELETE CASCADE
|
|
15
|
+
SQL
|
|
16
|
+
|
|
17
|
+
add_column :output_baskets, :action_id, :bigint
|
|
18
|
+
run <<~SQL
|
|
19
|
+
ALTER TABLE output_baskets
|
|
20
|
+
ADD CONSTRAINT output_baskets_action_id_fkey
|
|
21
|
+
FOREIGN KEY (action_id) REFERENCES actions (id) ON DELETE CASCADE
|
|
22
|
+
SQL
|
|
23
|
+
|
|
24
|
+
add_column :output_details, :action_id, :bigint
|
|
25
|
+
run <<~SQL
|
|
26
|
+
ALTER TABLE output_details
|
|
27
|
+
ADD CONSTRAINT output_details_action_id_fkey
|
|
28
|
+
FOREIGN KEY (action_id) REFERENCES actions (id) ON DELETE CASCADE
|
|
29
|
+
SQL
|
|
30
|
+
|
|
31
|
+
# Change outputs.action_id FK to ON DELETE SET NULL and make nullable.
|
|
32
|
+
# Outputs are immutable log entries — when an action is deleted (abort,
|
|
33
|
+
# reaper), the output rows survive as orphans with NULL action_id.
|
|
34
|
+
# They have no spendable entry (cascade-deleted), no basket, and are
|
|
35
|
+
# invisible to the wallet.
|
|
36
|
+
run <<~SQL
|
|
37
|
+
ALTER TABLE outputs
|
|
38
|
+
DROP CONSTRAINT IF EXISTS outputs_action_id_fkey,
|
|
39
|
+
ALTER COLUMN action_id DROP NOT NULL,
|
|
40
|
+
ADD CONSTRAINT outputs_action_id_fkey
|
|
41
|
+
FOREIGN KEY (action_id) REFERENCES actions (id) ON DELETE SET NULL
|
|
42
|
+
SQL
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
down do
|
|
46
|
+
run <<~SQL
|
|
47
|
+
ALTER TABLE outputs
|
|
48
|
+
DROP CONSTRAINT IF EXISTS outputs_action_id_fkey,
|
|
49
|
+
ALTER COLUMN action_id SET NOT NULL,
|
|
50
|
+
ADD CONSTRAINT outputs_action_id_fkey
|
|
51
|
+
FOREIGN KEY (action_id) REFERENCES actions (id)
|
|
52
|
+
SQL
|
|
53
|
+
alter_table(:spendable) do
|
|
54
|
+
drop_constraint :spendable_action_id_fkey
|
|
55
|
+
drop_column :action_id
|
|
56
|
+
end
|
|
57
|
+
alter_table(:output_baskets) do
|
|
58
|
+
drop_constraint :output_baskets_action_id_fkey
|
|
59
|
+
drop_column :action_id
|
|
60
|
+
end
|
|
61
|
+
alter_table(:output_details) do
|
|
62
|
+
drop_constraint :output_details_action_id_fkey
|
|
63
|
+
drop_column :action_id
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|