/
DbVersion.java
268 lines (260 loc) · 13 KB
/
DbVersion.java
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
259
260
261
262
263
264
265
266
267
268
package nxt;
import nxt.util.Logger;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
final class DbVersion {
static void init() {
try (Connection con = Db.getConnection(); Statement stmt = con.createStatement()) {
int nextUpdate = 1;
try {
ResultSet rs = stmt.executeQuery("SELECT next_update FROM version");
if (! rs.next()) {
throw new RuntimeException("Invalid version table");
}
nextUpdate = rs.getInt("next_update");
if (! rs.isLast()) {
throw new RuntimeException("Invalid version table");
}
rs.close();
Logger.logMessage("Database update may take a while if needed, current db version " + (nextUpdate - 1) + "...");
} catch (SQLException e) {
Logger.logMessage("Initializing an empty database");
stmt.executeUpdate("CREATE TABLE version (next_update INT NOT NULL)");
stmt.executeUpdate("INSERT INTO version VALUES (1)");
con.commit();
}
update(nextUpdate);
} catch (SQLException e) {
throw new RuntimeException(e.toString(), e);
}
}
private static void apply(String sql) {
try (Connection con = Db.getConnection(); Statement stmt = con.createStatement()) {
try {
if (sql != null) {
Logger.logDebugMessage("Will apply sql:\n" + sql);
stmt.executeUpdate(sql);
}
stmt.executeUpdate("UPDATE version SET next_update = next_update + 1");
con.commit();
} catch (Exception e) {
con.rollback();
throw e;
}
} catch (SQLException e) {
throw new RuntimeException("Database error executing " + sql, e);
}
}
private static void update(int nextUpdate) {
switch (nextUpdate) {
case 1:
apply("CREATE TABLE IF NOT EXISTS block (db_id INT IDENTITY, id BIGINT NOT NULL, version INT NOT NULL, "
+ "timestamp INT NOT NULL, previous_block_id BIGINT, "
+ "FOREIGN KEY (previous_block_id) REFERENCES block (id) ON DELETE CASCADE, total_amount INT NOT NULL, "
+ "total_fee INT NOT NULL, payload_length INT NOT NULL, generator_public_key BINARY(32) NOT NULL, "
+ "previous_block_hash BINARY(32), cumulative_difficulty VARBINARY NOT NULL, base_target BIGINT NOT NULL, "
+ "next_block_id BIGINT, FOREIGN KEY (next_block_id) REFERENCES block (id) ON DELETE SET NULL, "
+ "index INT NOT NULL, height INT NOT NULL, generation_signature BINARY(64) NOT NULL, "
+ "block_signature BINARY(64) NOT NULL, payload_hash BINARY(32) NOT NULL, generator_account_id BIGINT NOT NULL)");
case 2:
apply("CREATE UNIQUE INDEX IF NOT EXISTS block_id_idx ON block (id)");
case 3:
apply("CREATE TABLE IF NOT EXISTS transaction (db_id INT IDENTITY, id BIGINT NOT NULL, "
+ "deadline SMALLINT NOT NULL, sender_public_key BINARY(32) NOT NULL, recipient_id BIGINT NOT NULL, "
+ "amount INT NOT NULL, fee INT NOT NULL, referenced_transaction_id BIGINT, index INT NOT NULL, "
+ "height INT NOT NULL, block_id BIGINT NOT NULL, FOREIGN KEY (block_id) REFERENCES block (id) ON DELETE CASCADE, "
+ "signature BINARY(64) NOT NULL, timestamp INT NOT NULL, type TINYINT NOT NULL, subtype TINYINT NOT NULL, "
+ "sender_account_id BIGINT NOT NULL, attachment OTHER)");
case 4:
apply("CREATE UNIQUE INDEX IF NOT EXISTS transaction_id_idx ON transaction (id)");
case 5:
apply("CREATE UNIQUE INDEX IF NOT EXISTS block_height_idx ON block (height)");
case 6:
apply("CREATE INDEX IF NOT EXISTS transaction_timestamp_idx ON transaction (timestamp)");
case 7:
apply("CREATE INDEX IF NOT EXISTS block_generator_account_id_idx ON block (generator_account_id)");
case 8:
apply("CREATE INDEX IF NOT EXISTS transaction_sender_account_id_idx ON transaction (sender_account_id)");
case 9:
apply("CREATE INDEX IF NOT EXISTS transaction_recipient_id_idx ON transaction (recipient_id)");
case 10:
apply("ALTER TABLE block ALTER COLUMN generator_account_id RENAME TO generator_id");
case 11:
apply("ALTER TABLE transaction ALTER COLUMN sender_account_id RENAME TO sender_id");
case 12:
apply("ALTER INDEX block_generator_account_id_idx RENAME TO block_generator_id_idx");
case 13:
apply("ALTER INDEX transaction_sender_account_id_idx RENAME TO transaction_sender_id_idx");
case 14:
apply("ALTER TABLE block DROP COLUMN IF EXISTS index");
case 15:
apply("ALTER TABLE transaction DROP COLUMN IF EXISTS index");
case 16:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS block_timestamp INT");
case 17:
apply(null);
case 18:
apply("ALTER TABLE transaction ALTER COLUMN block_timestamp SET NOT NULL");
case 19:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS hash BINARY(32)");
case 20:
apply(null);
case 21:
apply(null);
case 22:
apply("CREATE INDEX IF NOT EXISTS transaction_hash_idx ON transaction (hash)");
case 23:
apply(null);
case 24:
apply("ALTER TABLE block ALTER COLUMN total_amount BIGINT");
case 25:
apply("ALTER TABLE block ALTER COLUMN total_fee BIGINT");
case 26:
apply("ALTER TABLE transaction ALTER COLUMN amount BIGINT");
case 27:
apply("ALTER TABLE transaction ALTER COLUMN fee BIGINT");
case 28:
apply(null);
case 29:
apply(null);
case 30:
apply(null);
case 31:
apply(null);
case 32:
apply(null);
case 33:
apply(null);
case 34:
apply(null);
case 35:
apply(null);
case 36:
apply("CREATE TABLE IF NOT EXISTS peer (address VARCHAR PRIMARY KEY)");
case 37:
apply(null);
case 38:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS full_hash BINARY(32)");
case 39:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS referenced_transaction_full_hash BINARY(32)");
case 40:
apply(null);
case 41:
apply("ALTER TABLE transaction ALTER COLUMN full_hash SET NOT NULL");
case 42:
apply("CREATE UNIQUE INDEX IF NOT EXISTS transaction_full_hash_idx ON transaction (full_hash)");
case 43:
apply(null);
case 44:
apply(null);
case 45:
apply(null);
case 46:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS attachment_bytes VARBINARY");
case 47:
BlockDb.deleteAll();
apply(null);
case 48:
apply("ALTER TABLE transaction DROP COLUMN attachment");
case 49:
apply("UPDATE transaction a SET a.referenced_transaction_full_hash = "
+ "(SELECT full_hash FROM transaction b WHERE b.id = a.referenced_transaction_id) "
+ "WHERE a.referenced_transaction_full_hash IS NULL");
case 50:
apply("ALTER TABLE transaction DROP COLUMN referenced_transaction_id");
case 51:
apply("ALTER TABLE transaction DROP COLUMN hash");
case 52:
if (Constants.isTestnet) {
BlockchainProcessorImpl.getInstance().validateAtNextScan();
}
apply(null);
case 53:
apply("DROP INDEX transaction_recipient_id_idx");
case 54:
apply("ALTER TABLE transaction ALTER COLUMN recipient_id SET NULL");
case 55:
try (Connection con = Db.getConnection();
Statement stmt = con.createStatement();
PreparedStatement pstmt = con.prepareStatement("UPDATE transaction SET recipient_id = null WHERE type = ? AND subtype = ?")) {
try {
for (byte type = 0; type <= 4; type++) {
for (byte subtype = 0; subtype <= 8; subtype++) {
TransactionType transactionType = TransactionType.findTransactionType(type, subtype);
if (transactionType == null) {
continue;
}
if (!transactionType.hasRecipient()) {
pstmt.setByte(1, type);
pstmt.setByte(2, subtype);
pstmt.executeUpdate();
}
}
}
stmt.executeUpdate("UPDATE version SET next_update = next_update + 1");
con.commit();
} catch (SQLException e) {
con.rollback();
throw e;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
case 56:
apply("CREATE INDEX IF NOT EXISTS transaction_recipient_id_idx ON transaction (recipient_id)");
case 57:
apply("DROP INDEX transaction_timestamp_idx");
case 58:
apply("CREATE INDEX IF NOT EXISTS transaction_timestamp_idx ON transaction (timestamp DESC)");
case 59:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS version TINYINT");
case 60:
apply("UPDATE transaction SET version = 0");
case 61:
apply("ALTER TABLE transaction ALTER COLUMN version SET NOT NULL");
case 62:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS has_message BOOLEAN NOT NULL DEFAULT FALSE");
case 63:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS has_encrypted_message BOOLEAN NOT NULL DEFAULT FALSE");
case 64:
apply("UPDATE transaction SET has_message = TRUE WHERE type = 1 AND subtype = 0");
case 65:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS has_public_key_announcement BOOLEAN NOT NULL DEFAULT FALSE");
case 66:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS ec_block_height INT DEFAULT NULL");
case 67:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS ec_block_id BIGINT DEFAULT NULL");
case 68:
apply("ALTER TABLE transaction ADD COLUMN IF NOT EXISTS has_encrypttoself_message BOOLEAN NOT NULL DEFAULT FALSE");
case 69:
/* Remove all peers since we start blacklisting peers from before 0.3.3 */
apply("DELETE FROM peer");
case 70:
if (!Constants.isTestnet) {
apply("INSERT INTO peer (address) VALUES " +
"('5.101.102.194'), ('5.101.102.195'), ('5.101.102.196'), ('5.101.102.197'), " +
"('5.101.102.199'), ('5.101.102.200'), ('5.101.102.201'), ('5.101.102.202'), " +
"('5.101.102.203'), ('5.101.102.204'), ('5.101.102.205'), ('107.170.73.9'), " +
"('107.170.123.54'), ('107.170.138.55')");
} else {
apply("INSERT INTO peer (address) VALUES " + "('178.62.176.45'), ('178.62.176.46')");
}
case 71:
apply(null);
case 72:
/* Validate the chain to be compatible with the 0.3.3 fork */
if ( ! Constants.isTestnet) {
BlockchainProcessorImpl.getInstance().validateAtNextScan();
}
apply(null);
case 73:
return;
default:
throw new RuntimeException("Database inconsistent with code, probably trying to run older code on newer database");
}
}
private DbVersion() {} //never
}