bsv-wallet-postgres 0.1.0 → 0.3.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-wallet-postgres.md → CHANGELOG.md} +34 -0
- data/lib/bsv/wallet_postgres/migrations/002_add_output_state.rb +33 -0
- data/lib/bsv/wallet_postgres/migrations/003_add_wallet_settings.rb +20 -0
- data/lib/bsv/wallet_postgres/migrations/004_add_pending_metadata.rb +69 -0
- data/lib/bsv/wallet_postgres/postgres_store.rb +189 -2
- data/lib/bsv/wallet_postgres/version.rb +1 -1
- metadata +10 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1159b17c5c9a6d0f20be0c47648eab1fbb13f8131e4d9b48d5a13ff5edc8126
|
|
4
|
+
data.tar.gz: 4abd93ce4a43614e4c28b0601d031ce8ca51504dfd2270d6cf58856148dca1d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 846999a97dd79648e234d37863bbceeda51478d1926500d4e2176cbae5eb9646fbc240a67bb4c80a6e5554f49cc4ff51ac779c3b1d73c4d2e475fd335449ac1f
|
|
7
|
+
data.tar.gz: 5643c7caead597cf5327ada2b857a0faf375769f46010bc6545cb995130611c3e05b5c27985333f103e1b240573f4dbea4ce6be7259c23cc43d45448da1bcfb0
|
|
@@ -5,6 +5,40 @@ All notable changes to the `bsv-wallet-postgres` gem are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 0.3.0 — 2026-04-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `update_action_status` and `delete_action` implementations for PostgresStore,
|
|
12
|
+
matching the new StorageAdapter contract introduced in bsv-wallet 0.6.0 (#370)
|
|
13
|
+
|
|
14
|
+
## 0.2.0 — 2026-04-12
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Migration 004** — adds `satoshis`, `pending_since`, `pending_reference`,
|
|
19
|
+
`no_send` columns and a partial index on `(state, basket)` for spendable
|
|
20
|
+
rows (#353)
|
|
21
|
+
- **`find_spendable_outputs(basket:, min_satoshis:, sort_order:)`** — query
|
|
22
|
+
spendable outputs with backward-compatible COALESCE for legacy rows (#354)
|
|
23
|
+
- **`update_output_state(outpoint, new_state, ...)`** — transition output
|
|
24
|
+
state with JSONB data synchronisation (#354)
|
|
25
|
+
- **`lock_utxos(outpoints, reference:, no_send:)`** — atomic
|
|
26
|
+
`UPDATE ... WHERE state = 'spendable' RETURNING` pattern for concurrent
|
|
27
|
+
safety (#355)
|
|
28
|
+
- **`release_stale_pending!(timeout:)`** — recover stuck pending outputs,
|
|
29
|
+
exempting `no_send` locks (#355)
|
|
30
|
+
- **PostgresStore settings methods** — `store_setting` / `find_setting`
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Spendable boolean sync** — `update_output_state`, `lock_utxos`, and
|
|
35
|
+
`release_stale_pending!` now keep the legacy `spendable` column in sync
|
|
36
|
+
with the `state` column; `filter_outputs` uses dual-column WHERE clause
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- Directory restructure — source moved to `gem/bsv-wallet-postgres/`
|
|
41
|
+
|
|
8
42
|
## 0.1.0 — 2026-04-09
|
|
9
43
|
|
|
10
44
|
Initial release of `bsv-wallet-postgres`, a PostgreSQL-backed
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add output lifecycle state column to wallet_outputs.
|
|
4
|
+
#
|
|
5
|
+
# Introduces a nullable +state+ text column alongside the existing boolean
|
|
6
|
+
# +spendable+ column. The two fields are kept in sync by the application
|
|
7
|
+
# layer (+PostgresStore#update_output_state+).
|
|
8
|
+
#
|
|
9
|
+
# === State values
|
|
10
|
+
#
|
|
11
|
+
# * +spendable+ — the output is available for coin selection
|
|
12
|
+
# * +pending+ — the output has been selected but the spending transaction
|
|
13
|
+
# has not yet been broadcast or confirmed
|
|
14
|
+
# * +spent+ — the output has been consumed by a confirmed transaction
|
|
15
|
+
#
|
|
16
|
+
# === Backward compatibility
|
|
17
|
+
#
|
|
18
|
+
# Rows written before this migration have +state = NULL+. The application
|
|
19
|
+
# treats +state IS NULL AND spendable = TRUE+ as equivalent to
|
|
20
|
+
# +state = 'spendable'+ so that existing data continues to behave
|
|
21
|
+
# correctly without a backfill.
|
|
22
|
+
#
|
|
23
|
+
# The migration is intentionally non-destructive: +state+ is nullable
|
|
24
|
+
# so the migration itself applies instantly on large tables (no rewrite,
|
|
25
|
+
# no default scan). A backfill can be run offline if required.
|
|
26
|
+
Sequel.migration do
|
|
27
|
+
change do
|
|
28
|
+
alter_table(:wallet_outputs) do
|
|
29
|
+
add_column :state, String, null: true
|
|
30
|
+
add_index :state
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add wallet settings table to BSV::Wallet::PostgresStore.
|
|
4
|
+
#
|
|
5
|
+
# Provides key-value persistence for wallet configuration (e.g. change
|
|
6
|
+
# output parameters). Keys are short strings (e.g. 'change_params');
|
|
7
|
+
# values are JSON-serialised strings so the schema never needs to change
|
|
8
|
+
# when setting shapes evolve.
|
|
9
|
+
#
|
|
10
|
+
# The table uses +key+ as its primary key so +store_setting+ is a simple
|
|
11
|
+
# upsert (insert-or-replace on conflict).
|
|
12
|
+
Sequel.migration do
|
|
13
|
+
change do
|
|
14
|
+
create_table(:wallet_settings) do
|
|
15
|
+
String :key, primary_key: true
|
|
16
|
+
String :value, text: true, null: false
|
|
17
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add pending lock metadata columns and satoshis to wallet_outputs.
|
|
4
|
+
#
|
|
5
|
+
# Introduces dedicated columns to support the auto-fund UTXO management
|
|
6
|
+
# methods (+find_spendable_outputs+, +lock_utxos+, +update_output_state+,
|
|
7
|
+
# +release_stale_pending!+) without requiring expensive JSONB extraction
|
|
8
|
+
# on every query.
|
|
9
|
+
#
|
|
10
|
+
# === Columns added
|
|
11
|
+
#
|
|
12
|
+
# * +satoshis+ — bigint, nullable. Mirrors the value stored inside
|
|
13
|
+
# the JSONB +data+ blob so that +find_spendable_outputs+
|
|
14
|
+
# can +ORDER BY satoshis+ without casting. New writes
|
|
15
|
+
# populate this column; existing rows leave it NULL
|
|
16
|
+
# and queries fall back to +COALESCE(satoshis, (data->>'satoshis')::bigint, 0)+.
|
|
17
|
+
#
|
|
18
|
+
# * +pending_since+ — timestamp (UTC), nullable. Set to +NOW()+ when a
|
|
19
|
+
# UTXO is locked via +lock_utxos+. Used by
|
|
20
|
+
# +release_stale_pending!+ to identify stale locks.
|
|
21
|
+
#
|
|
22
|
+
# * +pending_reference+ — text, nullable. Caller-supplied label passed to
|
|
23
|
+
# +lock_utxos+. Carried through for observability.
|
|
24
|
+
#
|
|
25
|
+
# * +no_send+ — boolean, default +false+. When +true+ the lock is
|
|
26
|
+
# exempt from automatic stale recovery by
|
|
27
|
+
# +release_stale_pending!+.
|
|
28
|
+
#
|
|
29
|
+
# === Index added
|
|
30
|
+
#
|
|
31
|
+
# A partial index on +(state, basket)+ filtered to rows where
|
|
32
|
+
# +state IS NULL OR state = 'spendable'+ accelerates the hot path of
|
|
33
|
+
# +find_spendable_outputs+: the vast majority of rows in a live wallet
|
|
34
|
+
# are spendable, not pending or spent.
|
|
35
|
+
#
|
|
36
|
+
# === Backward compatibility
|
|
37
|
+
#
|
|
38
|
+
# All new columns are nullable (or carry a safe default). No existing rows
|
|
39
|
+
# are modified. The application layer handles NULL values via +COALESCE+ or
|
|
40
|
+
# explicit IS NULL checks.
|
|
41
|
+
Sequel.migration do
|
|
42
|
+
up do
|
|
43
|
+
alter_table(:wallet_outputs) do
|
|
44
|
+
add_column :satoshis, :bigint, null: true
|
|
45
|
+
add_column :pending_since, :timestamptz, null: true
|
|
46
|
+
add_column :pending_reference, String, null: true
|
|
47
|
+
add_column :no_send, :boolean, null: false, default: false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Partial index on spendable rows only — keeps the index small and
|
|
51
|
+
# fast for the typical coin-selection scan.
|
|
52
|
+
run <<~SQL
|
|
53
|
+
CREATE INDEX wallet_outputs_spendable_basket_idx
|
|
54
|
+
ON wallet_outputs (state, basket)
|
|
55
|
+
WHERE (state IS NULL OR state = 'spendable');
|
|
56
|
+
SQL
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
down do
|
|
60
|
+
run 'DROP INDEX IF EXISTS wallet_outputs_spendable_basket_idx;'
|
|
61
|
+
|
|
62
|
+
alter_table(:wallet_outputs) do
|
|
63
|
+
drop_column :no_send
|
|
64
|
+
drop_column :pending_reference
|
|
65
|
+
drop_column :pending_since
|
|
66
|
+
drop_column :satoshis
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -102,6 +102,23 @@ module BSV
|
|
|
102
102
|
filter_actions(@db[:wallet_actions], query).count
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
def update_action_status(txid, new_status)
|
|
106
|
+
ds = @db[:wallet_actions].where(txid: txid)
|
|
107
|
+
raise WalletError, "Action not found: #{txid}" if ds.empty?
|
|
108
|
+
|
|
109
|
+
ds.update(
|
|
110
|
+
data: Sequel.lit(
|
|
111
|
+
"data || jsonb_build_object('status', ?)",
|
|
112
|
+
new_status
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
symbolise_keys(ds.first[:data])
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def delete_action(txid)
|
|
119
|
+
@db[:wallet_actions].where(txid: txid).delete.positive?
|
|
120
|
+
end
|
|
121
|
+
|
|
105
122
|
# --- Outputs ---
|
|
106
123
|
|
|
107
124
|
def store_output(output_data)
|
|
@@ -109,7 +126,14 @@ module BSV
|
|
|
109
126
|
@db[:wallet_outputs]
|
|
110
127
|
.insert_conflict(
|
|
111
128
|
target: :outpoint,
|
|
112
|
-
update: {
|
|
129
|
+
update: {
|
|
130
|
+
basket: row[:basket],
|
|
131
|
+
tags: row[:tags],
|
|
132
|
+
spendable: row[:spendable],
|
|
133
|
+
state: row[:state],
|
|
134
|
+
satoshis: row[:satoshis],
|
|
135
|
+
data: row[:data]
|
|
136
|
+
}
|
|
113
137
|
)
|
|
114
138
|
.insert(row)
|
|
115
139
|
output_data
|
|
@@ -128,6 +152,154 @@ module BSV
|
|
|
128
152
|
@db[:wallet_outputs].where(outpoint: outpoint).delete.positive?
|
|
129
153
|
end
|
|
130
154
|
|
|
155
|
+
# Returns outputs whose effective state is +:spendable+.
|
|
156
|
+
#
|
|
157
|
+
# Legacy rows with +state = NULL+ are treated as spendable when the
|
|
158
|
+
# +spendable+ boolean is true (or absent), matching MemoryStore's
|
|
159
|
+
# effective_state logic.
|
|
160
|
+
#
|
|
161
|
+
# @param basket [String, nil] restrict to this basket when provided
|
|
162
|
+
# @param min_satoshis [Integer, nil] exclude outputs below this value
|
|
163
|
+
# @param sort_order [Symbol] +:asc+ or +:desc+ (default +:desc+, largest first)
|
|
164
|
+
# @return [Array<Hash>]
|
|
165
|
+
def find_spendable_outputs(basket: nil, min_satoshis: nil, sort_order: :desc)
|
|
166
|
+
ds = @db[:wallet_outputs]
|
|
167
|
+
.where(Sequel.lit('(state = ? OR (state IS NULL AND spendable = TRUE))', 'spendable'))
|
|
168
|
+
ds = ds.where(basket: basket) if basket
|
|
169
|
+
if min_satoshis
|
|
170
|
+
ds = ds.where(
|
|
171
|
+
Sequel.lit('COALESCE(satoshis, (data->>?)::bigint, 0) >= ?', 'satoshis', min_satoshis)
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
satoshis_expr = Sequel.lit('COALESCE(satoshis, (data->>?)::bigint, 0)', 'satoshis')
|
|
175
|
+
ds = ds.order(sort_order == :asc ? Sequel.asc(satoshis_expr) : Sequel.desc(satoshis_expr))
|
|
176
|
+
ds.all.map { |r| symbolise_keys(r[:data]) }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Transitions the state of an existing output.
|
|
180
|
+
#
|
|
181
|
+
# When +new_state+ is +:pending+, sets +pending_since+, +pending_reference+,
|
|
182
|
+
# and +no_send+, and merges those values into the JSONB +data+ blob.
|
|
183
|
+
#
|
|
184
|
+
# When transitioning away from +:pending+, clears the pending metadata
|
|
185
|
+
# columns and removes the corresponding keys from the JSONB blob.
|
|
186
|
+
#
|
|
187
|
+
# @param outpoint [String] the outpoint identifier
|
|
188
|
+
# @param new_state [Symbol] +:spendable+, +:pending+, or +:spent+
|
|
189
|
+
# @param pending_reference [String, nil] caller-supplied label for a pending lock
|
|
190
|
+
# @param no_send [Boolean, nil] true if the lock belongs to a no_send transaction
|
|
191
|
+
# @raise [BSV::Wallet::WalletError] if the outpoint is not found
|
|
192
|
+
# @return [Hash] the updated output hash
|
|
193
|
+
def update_output_state(outpoint, new_state, pending_reference: nil, no_send: nil)
|
|
194
|
+
state_str = new_state.to_s
|
|
195
|
+
|
|
196
|
+
# Keep legacy spendable boolean in sync so filter_outputs and other
|
|
197
|
+
# queries that haven't migrated to the state column still work.
|
|
198
|
+
spendable_bool = new_state == :spendable
|
|
199
|
+
|
|
200
|
+
if new_state == :pending
|
|
201
|
+
updates = {
|
|
202
|
+
state: state_str,
|
|
203
|
+
spendable: spendable_bool,
|
|
204
|
+
pending_since: Sequel.lit('NOW()'),
|
|
205
|
+
pending_reference: pending_reference,
|
|
206
|
+
no_send: no_send ? true : false,
|
|
207
|
+
data: Sequel.lit(
|
|
208
|
+
"data || jsonb_build_object('state', ?, 'pending_since', NOW()::text, 'pending_reference', ?, 'no_send', ?)",
|
|
209
|
+
state_str, pending_reference, no_send ? true : false
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
else
|
|
213
|
+
updates = {
|
|
214
|
+
state: state_str,
|
|
215
|
+
spendable: spendable_bool,
|
|
216
|
+
pending_since: nil,
|
|
217
|
+
pending_reference: nil,
|
|
218
|
+
no_send: false
|
|
219
|
+
}
|
|
220
|
+
# Remove pending keys from JSONB blob, update state
|
|
221
|
+
updates[:data] = Sequel.lit(
|
|
222
|
+
"(data - 'pending_since' - 'pending_reference' - 'no_send') || jsonb_build_object('state', ?)",
|
|
223
|
+
state_str
|
|
224
|
+
)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
ds = @db[:wallet_outputs].where(outpoint: outpoint)
|
|
228
|
+
rows_updated = ds.update(updates)
|
|
229
|
+
raise WalletError, "Output not found: #{outpoint}" if rows_updated.zero?
|
|
230
|
+
|
|
231
|
+
row = ds.first
|
|
232
|
+
symbolise_keys(row[:data])
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Atomically marks a set of outpoints as +:pending+.
|
|
236
|
+
#
|
|
237
|
+
# Uses +UPDATE ... WHERE state = 'spendable' ... RETURNING outpoint+ so that
|
|
238
|
+
# the check-and-set is atomic at the database level. A concurrent caller that
|
|
239
|
+
# wins the race will have already changed the state to 'pending', so the
|
|
240
|
+
# second caller's WHERE clause will not match and will return nothing. No
|
|
241
|
+
# explicit row-level locking is needed — the UPDATE itself takes the lock.
|
|
242
|
+
#
|
|
243
|
+
# Legacy rows with +state = NULL AND spendable = TRUE+ are also eligible.
|
|
244
|
+
#
|
|
245
|
+
# @param outpoints [Array<String>] outpoint identifiers to lock
|
|
246
|
+
# @param reference [String] caller-supplied pending reference
|
|
247
|
+
# @param no_send [Boolean] true if this is a no_send lock
|
|
248
|
+
# @return [Array<String>] outpoints that were actually locked
|
|
249
|
+
def lock_utxos(outpoints, reference:, no_send: false)
|
|
250
|
+
return [] if outpoints.empty?
|
|
251
|
+
|
|
252
|
+
rows = @db[:wallet_outputs]
|
|
253
|
+
.where(outpoint: outpoints)
|
|
254
|
+
.where(Sequel.lit('(state = ? OR (state IS NULL AND spendable = TRUE))', 'spendable'))
|
|
255
|
+
.returning(:outpoint)
|
|
256
|
+
.update(
|
|
257
|
+
state: 'pending',
|
|
258
|
+
spendable: false,
|
|
259
|
+
pending_since: Sequel.lit('NOW()'),
|
|
260
|
+
pending_reference: reference,
|
|
261
|
+
no_send: no_send ? true : false,
|
|
262
|
+
data: Sequel.lit(
|
|
263
|
+
"data || jsonb_build_object('state', 'pending', 'pending_since', NOW()::text, " \
|
|
264
|
+
"'pending_reference', ?, 'no_send', ?)",
|
|
265
|
+
reference, no_send ? true : false
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
rows.map { |r| r[:outpoint] }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Releases stale pending locks back to +:spendable+.
|
|
273
|
+
#
|
|
274
|
+
# Any output in +:pending+ state whose +pending_since+ is older than
|
|
275
|
+
# +timeout+ seconds is reset to +spendable+ and its pending metadata is
|
|
276
|
+
# cleared. Outputs with +no_send = true+ are exempt and remain pending.
|
|
277
|
+
# Outputs with +pending_since = NULL+ are also skipped — they are treated
|
|
278
|
+
# as freshly locked (NULL means "just acquired but no timestamp yet").
|
|
279
|
+
#
|
|
280
|
+
# @param timeout [Integer] age in seconds before a lock is considered stale (default 300)
|
|
281
|
+
# @return [Integer] number of outputs released
|
|
282
|
+
def release_stale_pending!(timeout: 300)
|
|
283
|
+
rows = @db[:wallet_outputs]
|
|
284
|
+
.where(state: 'pending')
|
|
285
|
+
.where(Sequel.lit('no_send IS NOT TRUE'))
|
|
286
|
+
.where(Sequel.lit('pending_since IS NOT NULL'))
|
|
287
|
+
.where(Sequel.lit('pending_since < (NOW() - INTERVAL ?)', "#{timeout} seconds"))
|
|
288
|
+
.returning(:outpoint)
|
|
289
|
+
.update(
|
|
290
|
+
state: 'spendable',
|
|
291
|
+
spendable: true,
|
|
292
|
+
pending_since: nil,
|
|
293
|
+
pending_reference: nil,
|
|
294
|
+
no_send: false,
|
|
295
|
+
data: Sequel.lit(
|
|
296
|
+
"(data - 'pending_since' - 'pending_reference' - 'no_send') || jsonb_build_object('state', 'spendable')"
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
rows.length
|
|
301
|
+
end
|
|
302
|
+
|
|
131
303
|
# --- Certificates ---
|
|
132
304
|
|
|
133
305
|
def store_certificate(cert_data)
|
|
@@ -181,6 +353,18 @@ module BSV
|
|
|
181
353
|
@db[:wallet_transactions].where(txid: txid).get(:tx_hex)
|
|
182
354
|
end
|
|
183
355
|
|
|
356
|
+
# --- Settings ---
|
|
357
|
+
|
|
358
|
+
def store_setting(key, value)
|
|
359
|
+
@db[:wallet_settings]
|
|
360
|
+
.insert_conflict(target: :key, update: { value: value })
|
|
361
|
+
.insert(key: key, value: value)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def find_setting(key)
|
|
365
|
+
@db[:wallet_settings].where(key: key).get(:value)
|
|
366
|
+
end
|
|
367
|
+
|
|
184
368
|
private
|
|
185
369
|
|
|
186
370
|
# --- Row builders ---
|
|
@@ -195,11 +379,14 @@ module BSV
|
|
|
195
379
|
|
|
196
380
|
def output_row(data)
|
|
197
381
|
spendable = data[:spendable] != false # nil treated as spendable, like MemoryStore
|
|
382
|
+
state = data[:state]&.to_s
|
|
198
383
|
{
|
|
199
384
|
outpoint: data[:outpoint],
|
|
200
385
|
basket: data[:basket],
|
|
201
386
|
tags: Sequel.pg_array(Array(data[:tags]), :text),
|
|
202
387
|
spendable: spendable,
|
|
388
|
+
state: state,
|
|
389
|
+
satoshis: data[:satoshis],
|
|
203
390
|
data: Sequel.pg_jsonb(data.to_h)
|
|
204
391
|
}
|
|
205
392
|
end
|
|
@@ -224,7 +411,7 @@ module BSV
|
|
|
224
411
|
ds = ds.where(outpoint: query[:outpoint]) if query[:outpoint]
|
|
225
412
|
ds = ds.where(basket: query[:basket]) if query[:basket]
|
|
226
413
|
ds = apply_array_filter(ds, :tags, query[:tags], query[:tag_query_mode])
|
|
227
|
-
ds = ds.where(spendable
|
|
414
|
+
ds = ds.where(Sequel.lit('(state = ? OR (state IS NULL AND spendable = TRUE))', 'spendable')) unless query[:include_spent]
|
|
228
415
|
ds
|
|
229
416
|
end
|
|
230
417
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-wallet-postgres
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bettison
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: bsv-wallet
|
|
@@ -15,7 +15,7 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.6.0
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: '1.0'
|
|
@@ -25,7 +25,7 @@ dependencies:
|
|
|
25
25
|
requirements:
|
|
26
26
|
- - ">="
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: 0.
|
|
28
|
+
version: 0.6.0
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: '1.0'
|
|
@@ -63,11 +63,14 @@ executables: []
|
|
|
63
63
|
extensions: []
|
|
64
64
|
extra_rdoc_files: []
|
|
65
65
|
files:
|
|
66
|
-
- CHANGELOG
|
|
66
|
+
- CHANGELOG.md
|
|
67
67
|
- LICENSE
|
|
68
68
|
- lib/bsv-wallet-postgres.rb
|
|
69
69
|
- lib/bsv/wallet_postgres.rb
|
|
70
70
|
- lib/bsv/wallet_postgres/migrations/001_create_wallet_tables.rb
|
|
71
|
+
- lib/bsv/wallet_postgres/migrations/002_add_output_state.rb
|
|
72
|
+
- lib/bsv/wallet_postgres/migrations/003_add_wallet_settings.rb
|
|
73
|
+
- lib/bsv/wallet_postgres/migrations/004_add_pending_metadata.rb
|
|
71
74
|
- lib/bsv/wallet_postgres/postgres_store.rb
|
|
72
75
|
- lib/bsv/wallet_postgres/version.rb
|
|
73
76
|
homepage: https://github.com/sgbett/bsv-ruby-sdk
|
|
@@ -76,7 +79,7 @@ licenses:
|
|
|
76
79
|
metadata:
|
|
77
80
|
homepage_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
78
81
|
source_code_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
79
|
-
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/
|
|
82
|
+
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/gem/bsv-wallet-postgres/CHANGELOG.md
|
|
80
83
|
rubygems_mfa_required: 'true'
|
|
81
84
|
rdoc_options: []
|
|
82
85
|
require_paths:
|
|
@@ -92,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
92
95
|
- !ruby/object:Gem::Version
|
|
93
96
|
version: '0'
|
|
94
97
|
requirements: []
|
|
95
|
-
rubygems_version:
|
|
98
|
+
rubygems_version: 4.0.10
|
|
96
99
|
specification_version: 4
|
|
97
100
|
summary: PostgreSQL storage adapter for bsv-wallet
|
|
98
101
|
test_files: []
|