-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase_setup.sql
More file actions
258 lines (236 loc) · 10.4 KB
/
Copy pathdatabase_setup.sql
File metadata and controls
258 lines (236 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
-- ChainCore Database Setup Script (Fixed)
-- Creates all missing tables and functions for the blockchain system
-- ================================
-- 1. TRANSACTIONS TABLE
-- ================================
CREATE TABLE IF NOT EXISTS transactions (
id SERIAL PRIMARY KEY,
transaction_id VARCHAR(255) UNIQUE NOT NULL,
block_id INTEGER NOT NULL,
block_index INTEGER NOT NULL,
transaction_type VARCHAR(50) NOT NULL CHECK (transaction_type IN ('coinbase', 'transfer')),
inputs_json JSONB,
outputs_json JSONB,
total_amount DECIMAL(20,8) NOT NULL DEFAULT 0,
is_coinbase BOOLEAN NOT NULL DEFAULT FALSE,
timestamp DECIMAL(20,6) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for transactions table
CREATE INDEX IF NOT EXISTS idx_transactions_tx_id ON transactions (transaction_id);
CREATE INDEX IF NOT EXISTS idx_transactions_block_id ON transactions (block_id);
CREATE INDEX IF NOT EXISTS idx_transactions_block_index ON transactions (block_index);
CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions (transaction_type);
CREATE INDEX IF NOT EXISTS idx_transactions_coinbase ON transactions (is_coinbase);
-- ================================
-- 2. UTXOS TABLE
-- ================================
CREATE TABLE IF NOT EXISTS utxos (
id SERIAL PRIMARY KEY,
utxo_key VARCHAR(255) UNIQUE NOT NULL,
transaction_id VARCHAR(255) NOT NULL,
output_index INTEGER NOT NULL,
recipient_address VARCHAR(255) NOT NULL,
amount DECIMAL(20,8) NOT NULL,
block_index INTEGER NOT NULL,
is_spent BOOLEAN NOT NULL DEFAULT FALSE,
spent_in_transaction VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for UTXOs table
CREATE INDEX IF NOT EXISTS idx_utxos_key ON utxos (utxo_key);
CREATE INDEX IF NOT EXISTS idx_utxos_address ON utxos (recipient_address);
CREATE INDEX IF NOT EXISTS idx_utxos_spent ON utxos (is_spent);
CREATE INDEX IF NOT EXISTS idx_utxos_block ON utxos (block_index);
CREATE INDEX IF NOT EXISTS idx_utxos_address_unspent ON utxos (recipient_address, is_spent);
-- ================================
-- 3. MINING_STATS TABLE
-- ================================
CREATE TABLE IF NOT EXISTS mining_stats (
id SERIAL PRIMARY KEY,
node_id VARCHAR(255) NOT NULL,
block_id INTEGER NOT NULL,
mining_duration_seconds DECIMAL(10,3) NOT NULL,
hash_attempts BIGINT NOT NULL,
hash_rate DECIMAL(15,2) NOT NULL,
mining_started_at TIMESTAMP NOT NULL,
mining_completed_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for mining_stats table
CREATE INDEX IF NOT EXISTS idx_mining_stats_node ON mining_stats (node_id);
CREATE INDEX IF NOT EXISTS idx_mining_stats_block ON mining_stats (block_id);
CREATE INDEX IF NOT EXISTS idx_mining_stats_duration ON mining_stats (mining_duration_seconds);
CREATE INDEX IF NOT EXISTS idx_mining_stats_hash_rate ON mining_stats (hash_rate);
-- ================================
-- 4. NODES TABLE
-- Network nodes registration and tracking
-- ================================
CREATE TABLE IF NOT EXISTS nodes (
id SERIAL PRIMARY KEY,
node_id VARCHAR(255) UNIQUE NOT NULL,
node_url VARCHAR(255) NOT NULL,
api_port INTEGER NOT NULL,
p2p_port INTEGER,
status VARCHAR(50) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
blocks_mined INTEGER NOT NULL DEFAULT 0,
total_rewards DECIMAL(20,8) NOT NULL DEFAULT 0,
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for nodes table
CREATE INDEX IF NOT EXISTS idx_nodes_node_id ON nodes (node_id);
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes (status);
CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON nodes (last_seen);
CREATE INDEX IF NOT EXISTS idx_nodes_blocks_mined ON nodes (blocks_mined DESC);
-- ================================
-- 5. ADDRESS_BALANCES TABLE
-- Persistent table to store balances for all seen addresses
-- ================================
CREATE TABLE IF NOT EXISTS address_balances (
address VARCHAR(255) PRIMARY KEY,
balance DECIMAL(20,8) NOT NULL DEFAULT 0,
utxo_count INTEGER NOT NULL DEFAULT 0,
last_activity_block INTEGER,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for fast lookups and rich-list queries
CREATE INDEX IF NOT EXISTS idx_address_balances_balance ON address_balances (balance DESC);
CREATE INDEX IF NOT EXISTS idx_address_balances_updated ON address_balances (updated_at);
-- ================================
-- 6. UPDATE BLOCKS TABLE (add missing columns if needed)
-- ================================
-- Add columns that might be missing from blocks table
DO $$
BEGIN
-- Add columns if they don't exist
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'blocks' AND column_name = 'miner_node') THEN
ALTER TABLE blocks ADD COLUMN miner_node VARCHAR(255) DEFAULT 'unknown';
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'blocks' AND column_name = 'miner_address') THEN
ALTER TABLE blocks ADD COLUMN miner_address VARCHAR(255) DEFAULT 'unknown';
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'blocks' AND column_name = 'transaction_count') THEN
ALTER TABLE blocks ADD COLUMN transaction_count INTEGER DEFAULT 0;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'blocks' AND column_name = 'raw_data') THEN
ALTER TABLE blocks ADD COLUMN raw_data JSONB;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'blocks' AND column_name = 'created_at') THEN
ALTER TABLE blocks ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
END IF;
END $$;
-- Add indexes to blocks table
CREATE INDEX IF NOT EXISTS idx_blocks_index ON blocks (block_index);
CREATE INDEX IF NOT EXISTS idx_blocks_hash ON blocks (hash);
CREATE INDEX IF NOT EXISTS idx_blocks_previous_hash ON blocks (previous_hash);
CREATE INDEX IF NOT EXISTS idx_blocks_miner_node ON blocks (miner_node);
CREATE INDEX IF NOT EXISTS idx_blocks_timestamp ON blocks (timestamp);
-- ================================
-- 7. ADD_BLOCK STORED FUNCTION
-- ================================
CREATE OR REPLACE FUNCTION add_block(
p_block_index INTEGER,
p_hash VARCHAR(255),
p_previous_hash VARCHAR(255),
p_merkle_root VARCHAR(255),
p_timestamp DECIMAL(20,6),
p_nonce BIGINT,
p_difficulty DECIMAL(20,8),
p_miner_node VARCHAR(255),
p_miner_address VARCHAR(255),
p_block_data JSONB
) RETURNS INTEGER AS $$
DECLARE
block_id INTEGER;
BEGIN
-- Insert block and return the ID
INSERT INTO blocks (
block_index, hash, previous_hash, merkle_root,
timestamp, nonce, difficulty, miner_node, miner_address,
transaction_count, raw_data, created_at
) VALUES (
p_block_index, p_hash, p_previous_hash, p_merkle_root,
p_timestamp, p_nonce, p_difficulty, p_miner_node, p_miner_address,
COALESCE((p_block_data->>'transaction_count')::INTEGER, 0),
p_block_data, CURRENT_TIMESTAMP
) RETURNING id INTO block_id;
RETURN block_id;
END;
$$ LANGUAGE plpgsql;
-- ================================
-- 8. REFRESH MATERIALIZED VIEW FUNCTION
-- ================================
CREATE OR REPLACE FUNCTION refresh_address_balances()
RETURNS VOID AS $$
BEGIN
-- Rebuild the address_balances persistent table from utxos
-- This will replace contents with aggregated current unspent UTXOs
TRUNCATE TABLE address_balances;
-- Build a set of all addresses that have ever appeared in transactions or utxos
WITH seen_addresses AS (
-- Addresses from UTXO recipients (both spent and unspent)
SELECT DISTINCT recipient_address AS address FROM utxos
UNION
-- Addresses from transaction outputs (recipients)
SELECT DISTINCT (elem->>'recipient_address') AS address
FROM transactions, jsonb_array_elements(outputs_json) AS elem
WHERE outputs_json IS NOT NULL AND elem->>'recipient_address' IS NOT NULL
UNION
-- Addresses from transaction inputs (senders) - extract from input addresses
SELECT DISTINCT (elem->>'address') AS address
FROM transactions, jsonb_array_elements(inputs_json) AS elem
WHERE inputs_json IS NOT NULL AND elem->>'address' IS NOT NULL
),
balances AS (
SELECT recipient_address,
SUM(amount) AS balance,
COUNT(*) AS utxo_count,
MAX(block_index) AS last_activity_block
FROM utxos
WHERE is_spent = FALSE
GROUP BY recipient_address
),
all_activity AS (
-- Get last activity block for each address from all sources
SELECT address,
MAX(activity_block) AS last_activity_block
FROM (
-- Activity from UTXOs (both spent and unspent)
SELECT recipient_address AS address, block_index AS activity_block FROM utxos
UNION ALL
-- Activity from transactions
SELECT (elem->>'recipient_address') AS address, t.block_index AS activity_block
FROM transactions t, jsonb_array_elements(t.outputs_json) AS elem
WHERE t.outputs_json IS NOT NULL AND elem->>'recipient_address' IS NOT NULL
) activities
GROUP BY address
)
INSERT INTO address_balances (address, balance, utxo_count, last_activity_block, updated_at)
SELECT
s.address,
COALESCE(b.balance, 0) AS balance,
COALESCE(b.utxo_count, 0) AS utxo_count,
COALESCE(a.last_activity_block, b.last_activity_block) AS last_activity_block,
CURRENT_TIMESTAMP
FROM seen_addresses s
LEFT JOIN balances b ON s.address = b.recipient_address
LEFT JOIN all_activity a ON s.address = a.address;
END;
$$ LANGUAGE plpgsql;
-- ================================
-- INITIAL DATA SETUP
-- ================================
-- Refresh the materialized view initially (will be empty)
-- Populate address_balances table initially
SELECT refresh_address_balances();
-- ================================
-- SETUP COMPLETE - VERIFICATION
-- ================================
SELECT 'ChainCore Database Setup Complete!' AS status;
SELECT
table_name,
(SELECT COUNT(*) FROM information_schema.columns WHERE table_name = t.table_name AND table_schema = 'public') AS column_count
FROM information_schema.tables t
WHERE table_schema = 'public'
ORDER BY table_name;