bsv-wallet 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b8425ff5a48801f390149006396bac80983a36231e101ff9e96255b4c702f68
4
- data.tar.gz: 5df7b47ab142634281a76a68de73cd30562a31c4864cddb1ebf5cfe332e60ac0
3
+ metadata.gz: 42266cef3c2585982df666f123097ea4fb4edd9f6619aacf37ed50628148ae1f
4
+ data.tar.gz: cec92d7893bba6a8b25439f5fd9c4d203ae7fce0cc2f63370adfc1a01d6bed61
5
5
  SHA512:
6
- metadata.gz: 7a8faf79916348e238b86b1006a78cf16ce4bcc0317170c7bd0ba80a2627615a129b6b2a694af884a2492379a29f354994581cd74e54adc62cd6138103906094
7
- data.tar.gz: 9f3f53a96072ed5dd27d46265a54558b3bd8073811d7532458276d4d287f59c5b77714017d024f970b95686f7fca8c1f5fc0891ba32e493cb671287ac84ff74c
6
+ metadata.gz: b45b5c376c8c2b20ec7afffd1f57a15e89d19509ad6afecce8da8f3cdd69b9f7d8af20b5157ca380b8d0a65d9db5dc9a6aed2b8ac1518542775207534e576825
7
+ data.tar.gz: aae7eaec036fd2bd620551645e5a111bccfa39ac948ccb6541f24dc5e187acd1c9458a2576b4bca3adc5e31c0ec73d83af270cee8861d940af9952da429fe36c
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'fileutils'
5
+ require 'logger'
6
+
7
+ module BSV
8
+ module Wallet
9
+ # JSON file-backed storage adapter.
10
+ #
11
+ # Persists actions, outputs, and certificates as JSON files in a
12
+ # configurable directory (default: +~/.bsv-wallet/+). Data survives
13
+ # process restarts.
14
+ #
15
+ # Inherits all filtering and pagination logic from {MemoryStore} and
16
+ # adds load-on-init / save-on-mutation.
17
+ #
18
+ # @example Default location
19
+ # store = BSV::Wallet::FileStore.new
20
+ # # Data written to ~/.bsv-wallet/
21
+ #
22
+ # @example Custom directory
23
+ # store = BSV::Wallet::FileStore.new(dir: '/var/lib/my-app/wallet')
24
+ class FileStore < MemoryStore
25
+ DEFAULT_DIR = File.expand_path('~/.bsv-wallet')
26
+
27
+ # @param dir [String] directory for JSON files
28
+ # (default: +~/.bsv-wallet/+ or +BSV_WALLET_DIR+ env var)
29
+ # @param dir [String] directory for JSON files
30
+ # @param logger [Logger, nil] logger for permission warnings (default: Logger to STDERR)
31
+ def initialize(dir: nil, logger: nil)
32
+ super()
33
+ @dir = dir || ENV.fetch('BSV_WALLET_DIR', DEFAULT_DIR)
34
+ @logger = logger || Logger.new($stderr, progname: 'bsv-wallet')
35
+ FileUtils.mkdir_p(@dir, mode: 0o700)
36
+ check_permissions
37
+ load_from_disk
38
+ end
39
+
40
+ # @return [String] the storage directory path
41
+ attr_reader :dir
42
+
43
+ # --- Mutations: delegate to super, then persist ---
44
+
45
+ def store_action(action_data)
46
+ result = super
47
+ save_actions
48
+ result
49
+ end
50
+
51
+ def store_output(output_data)
52
+ result = super
53
+ save_outputs
54
+ result
55
+ end
56
+
57
+ def delete_output(outpoint)
58
+ result = super
59
+ save_outputs if result
60
+ result
61
+ end
62
+
63
+ def store_certificate(cert_data)
64
+ result = super
65
+ save_certificates
66
+ result
67
+ end
68
+
69
+ def delete_certificate(type:, serial_number:, certifier:)
70
+ result = super
71
+ save_certificates if result
72
+ result
73
+ end
74
+
75
+ private
76
+
77
+ def check_permissions
78
+ dir_mode = File.stat(@dir).mode & 0o777
79
+ if dir_mode != 0o700
80
+ @logger.warn("Wallet directory #{@dir} has permissions #{format('%04o', dir_mode)} (expected 0700). " \
81
+ 'Other users may be able to access wallet data.')
82
+ end
83
+
84
+ [actions_path, outputs_path, certificates_path].each do |path|
85
+ next unless File.exist?(path)
86
+
87
+ file_mode = File.stat(path).mode & 0o777
88
+ next if file_mode == 0o600
89
+
90
+ @logger.warn("Wallet file #{path} has permissions #{format('%04o', file_mode)} (expected 0600). " \
91
+ 'Other users may be able to read wallet data.')
92
+ end
93
+ end
94
+
95
+ def actions_path
96
+ File.join(@dir, 'actions.json')
97
+ end
98
+
99
+ def outputs_path
100
+ File.join(@dir, 'outputs.json')
101
+ end
102
+
103
+ def certificates_path
104
+ File.join(@dir, 'certificates.json')
105
+ end
106
+
107
+ def load_from_disk
108
+ @actions = load_file(actions_path)
109
+ @outputs = load_file(outputs_path)
110
+ @certificates = load_file(certificates_path)
111
+ end
112
+
113
+ def load_file(path)
114
+ return [] unless File.exist?(path)
115
+
116
+ data = JSON.parse(File.read(path))
117
+ return [] unless data.is_a?(Array)
118
+
119
+ # Symbolise top-level keys for consistency with MemoryStore
120
+ data.map { |entry| symbolise_keys(entry) }
121
+ rescue JSON::ParserError
122
+ []
123
+ end
124
+
125
+ def save_actions
126
+ write_file(actions_path, @actions)
127
+ end
128
+
129
+ def save_outputs
130
+ write_file(outputs_path, @outputs)
131
+ end
132
+
133
+ def save_certificates
134
+ write_file(certificates_path, @certificates)
135
+ end
136
+
137
+ def write_file(path, data)
138
+ json = JSON.pretty_generate(stringify_keys_deep(data))
139
+ tmp = "#{path}.tmp"
140
+ File.open(tmp, File::WRONLY | File::CREAT | File::TRUNC, 0o600) { |f| f.write(json) }
141
+ File.rename(tmp, path)
142
+ end
143
+
144
+ def symbolise_keys(hash)
145
+ return hash unless hash.is_a?(Hash)
146
+
147
+ hash.each_with_object({}) do |(k, v), result|
148
+ result[k.to_sym] = case v
149
+ when Hash then symbolise_keys(v)
150
+ when Array then v.map { |e| e.is_a?(Hash) ? symbolise_keys(e) : e }
151
+ else v
152
+ end
153
+ end
154
+ end
155
+
156
+ def stringify_keys_deep(obj)
157
+ case obj
158
+ when Hash
159
+ obj.each_with_object({}) do |(k, v), result|
160
+ result[k.to_s] = stringify_keys_deep(v)
161
+ end
162
+ when Array
163
+ obj.map { |e| stringify_keys_deep(e) }
164
+ else
165
+ obj
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BSV
4
4
  module WalletInterface
5
- VERSION = '0.1.2'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -36,11 +36,12 @@ module BSV
36
36
  attr_reader :network
37
37
 
38
38
  # @param key [BSV::Primitives::PrivateKey, String, KeyDeriver] signing key
39
- # @param storage [StorageAdapter] persistence adapter (default: MemoryStore)
39
+ # @param storage [StorageAdapter] persistence adapter (default: FileStore).
40
+ # Use +storage: MemoryStore.new+ for tests.
40
41
  # @param network [String] 'mainnet' (default) or 'testnet'
41
42
  # @param chain_provider [ChainProvider] blockchain data provider (default: NullChainProvider)
42
43
  # @param http_client [#request, nil] injectable HTTP client for certificate issuance
43
- def initialize(key, storage: MemoryStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, http_client: nil)
44
+ def initialize(key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, http_client: nil)
44
45
  super(key)
45
46
  @storage = storage
46
47
  @network = network
@@ -13,6 +13,7 @@ module BSV
13
13
  autoload :Validators, 'bsv/wallet_interface/validators'
14
14
  autoload :StorageAdapter, 'bsv/wallet_interface/storage_adapter'
15
15
  autoload :MemoryStore, 'bsv/wallet_interface/memory_store'
16
+ autoload :FileStore, 'bsv/wallet_interface/file_store'
16
17
  autoload :ChainProvider, 'bsv/wallet_interface/chain_provider'
17
18
  autoload :NullChainProvider, 'bsv/wallet_interface/null_chain_provider'
18
19
  autoload :WalletClient, 'bsv/wallet_interface/wallet_client'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bsv-wallet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Bettison
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-30 00:00:00.000000000 Z
10
+ date: 2026-04-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.3'
32
+ version: '0.4'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.3'
39
+ version: '0.4'
40
40
  description: Implements the BRC-100 standard wallet-to-application interface for the
41
41
  BSV Blockchain.
42
42
  executables: []
@@ -52,6 +52,7 @@ files:
52
52
  - lib/bsv/wallet_interface/errors/invalid_signature_error.rb
53
53
  - lib/bsv/wallet_interface/errors/unsupported_action_error.rb
54
54
  - lib/bsv/wallet_interface/errors/wallet_error.rb
55
+ - lib/bsv/wallet_interface/file_store.rb
55
56
  - lib/bsv/wallet_interface/interface.rb
56
57
  - lib/bsv/wallet_interface/key_deriver.rb
57
58
  - lib/bsv/wallet_interface/memory_store.rb