forked from Qortal/qortal
Blockchain config (ncluding genesis block) moved out into config file
This commit is contained in:
parent
68f99cfc11
commit
f50b53a407
161
blockchain.json
Normal file
161
blockchain.json
Normal file
@ -0,0 +1,161 @@
|
||||
{
|
||||
"coinSupply": "10000000000",
|
||||
"blockDifficultyInterval": 10,
|
||||
"minBlockTime": 60,
|
||||
"maxBlockTime": 300,
|
||||
"blockTimestampMargin": 500,
|
||||
"maxBytesPerUnitFee": 1024,
|
||||
"unitFee": "1.0",
|
||||
"genesis": {
|
||||
"assets": [
|
||||
{ "name": "QORA", "description": "QORA coin" }
|
||||
]
|
||||
"timestamp": "1400247274336",
|
||||
"generatingBalance": "10000000",
|
||||
"transactions": [
|
||||
{ "recipient": "QUD9y7NZqTtNwvSAUfewd7zKUGoVivVnTW", "amount": "7032468.191" },
|
||||
{ "recipient": "QVafvKkE5bZTkq8PcXvdaxwuLNN2DGCwYk", "amount": "1716146.084" },
|
||||
{ "recipient": "QV42QQP7frYWqsVq536g7zSk97fUpf2ZSN", "amount": "5241707.06" },
|
||||
{ "recipient": "QgkLTm5GkepJpgr53nAgUyYRsvmyHpb2zT", "amount": "854964.0816" },
|
||||
{ "recipient": "Qc8kN338XQULMBuUa6mTqL5tipvELDhzeZ", "amount": "769467.6734" },
|
||||
{ "recipient": "QQ81BA75jZcpjQBLZE1qcHrXV8ARC1DEec", "amount": "85496408.16" },
|
||||
{ "recipient": "QeoSe4DscWX4AFkNBCdm4WS1V7QkUFSQLP", "amount": "854968.3564" },
|
||||
{ "recipient": "Qdfu3Eh21ZVHNDY1xyNaFqTTEYscSmfSsm", "amount": "85496408.16" },
|
||||
{ "recipient": "QeDSr4abXKRg9j5hTN3TK9UGuH3umThZ42", "amount": "4445813.224" },
|
||||
{ "recipient": "QQKDuo1txYB9E2xim79YVR6SQ1ZbJtJtFX", "amount": "47023024.49" },
|
||||
{ "recipient": "QLeaeGr4CDA95FmeMtFh8okJRMLoq8Cge5", "amount": "170992816.3" },
|
||||
{ "recipient": "QSwN5oa8ZHWJmc6FeAJ8Xr1SHaEuSahw1J", "amount": "3419856.326" },
|
||||
{ "recipient": "QWnoGd4a7iXqQmNEpUtCb1x7nWgcya8QbE", "amount": "17056533.43" },
|
||||
{ "recipient": "QbJqhsJjcy3vkzsJ1kHvgn26pQF3sZEypc", "amount": "42705455.87" },
|
||||
{ "recipient": "QiBhBcseKzaDnHKyqEJs8z1Xx2rSb9XhBr", "amount": "141069073.5" },
|
||||
{ "recipient": "QTwYwxBhzivFEWY5yfzyz1pqhJ8XCroKwv", "amount": "85496408.16" },
|
||||
{ "recipient": "QfikxUU15Dy1oxbcDNEcLeU5cHvbrceq3A", "amount": "17099281.63" },
|
||||
{ "recipient": "QhdqBmKZeQ3Hg1XUuR5nKtAkw47tuoRi2q", "amount": "12824461.22" },
|
||||
{ "recipient": "QaVNyTqsTHA6JWMcqntcJf1u9c3qid76xH", "amount": "128244612.2" },
|
||||
{ "recipient": "QYaDa7bmgo5L9qkcfJKjhPPrQkvGjEoc7y", "amount": "85496408.16" },
|
||||
{ "recipient": "QQPddvWaYf4pbCyVHEVoyfC72EiaAv4JhT", "amount": "25648922.45" },
|
||||
{ "recipient": "QSQpTNtTZMwaDuNq56Jz73KHWXaey9JrT1", "amount": "26341443.35" },
|
||||
{ "recipient": "QVjcFWE6TnGePGJEtbNc1thwD2sgHBLvUV", "amount": "42940528.25" },
|
||||
{ "recipient": "Qga93mWNqTuJYx6o33vjUpFH7Cn4sxLyoG", "amount": "2564892.245" },
|
||||
{ "recipient": "QXyHKyQPJnb4ejyTkvS26x9sjWnhTTJ1Uc", "amount": "10259568.98" },
|
||||
{ "recipient": "QLurSSgJvW7WXHDFSobgfakgqXxjoZzwUH", "amount": "85496408.16" },
|
||||
{ "recipient": "QadxfiARcsmwzE93wqq82yXFi3ykM2qdtS", "amount": "79118376.11" },
|
||||
{ "recipient": "QRHhhtz3Cv9RPKB1QBBfkRmRfpXx8vkRa5", "amount": "22435418.54" },
|
||||
{ "recipient": "Qh8UnEs55n8jcnBaBwVtrTGkFFFBDyrMqH", "amount": "128757590.7" },
|
||||
{ "recipient": "QhF7Fu3f54CTYA7zBQ223NQEssi2yAbAcx", "amount": "258481290.8" },
|
||||
{ "recipient": "QPk9VB6tigoifrUYQrw4arBNk7i8HEgsDD", "amount": "128244612.2" },
|
||||
{ "recipient": "QXWJnEPsdtaLQAEutJFR4ySiMUJCWDzZJX", "amount": "85496408.16" },
|
||||
{ "recipient": "QVFs42gM4Cixf4Y5vDFvKKxRAamUPMCAVq", "amount": "85496408.16" },
|
||||
{ "recipient": "Qec5ueWc4rcBrty47GZfFSqvLymxvcycFm", "amount": "129091026.7" },
|
||||
{ "recipient": "QfYiztbDz1Nb9EMhgHidLycvuPN8HEcHEj", "amount": "128244612.2" },
|
||||
{ "recipient": "QPdWsZtaZcAKqk2HWVhEVbws4qG5KUTXmg", "amount": "179285967.9" },
|
||||
{ "recipient": "QVkNs5NcwQpsrCXpWzuMXkMehJr5mkvLVy", "amount": "8558190.456" },
|
||||
{ "recipient": "Qg19DzyEfyZANx6JLy4GrSGF5LuZ2MLqyZ", "amount": "42748204.08" },
|
||||
{ "recipient": "Qf3A8L5WJNHt1xZxmayrTp2d5owzdkcxM6", "amount": "50519827.58" },
|
||||
{ "recipient": "QeKR4W6qkFJGF7Hmu7rSUzTSQiqJzZLXdt", "amount": "10216820.77" },
|
||||
{ "recipient": "QWg7T5i3uBY3xeBLFTLYYruR15Ln11vwo4", "amount": "170992816.3" },
|
||||
{ "recipient": "QUYdM5fHECPZxKQQAmoxoQa2aWg8TZYfPw", "amount": "85496408.16" },
|
||||
{ "recipient": "QjhfEZCgrjUbnLRnWqWxzyYqKQpjjxkuA8", "amount": "86665653.61" },
|
||||
{ "recipient": "QMA53u3wrzDoxC57CWUJePNdR8FoqinqUS", "amount": "85496408.16" },
|
||||
{ "recipient": "QSuCp6mB5zNNeJKD62aq2hR9h84ks1WhHf", "amount": "161588211.4" },
|
||||
{ "recipient": "QS2tCUk7GQefg4zGewwrumxSPmN6fgA7Xc", "amount": "170992816.3" },
|
||||
{ "recipient": "Qcn6FZRxAgp3japtvjgUkBY6KPfbPZMZtM", "amount": "170992816.3" },
|
||||
{ "recipient": "QZrmXZkRmjV2GwMt72Rr1ZqHJjv8raDk5J", "amount": "17099281.63" },
|
||||
{ "recipient": "QeZzwGDfAHa132jb6r4rQHbgJstLuT8QJ3", "amount": "255875360.3" },
|
||||
{ "recipient": "Qj3L139sMMuFvvjKQDwRnoSgKUnoMhDQs5", "amount": "76946767.34" },
|
||||
{ "recipient": "QWJvpvbFRZHu7LRbY5MjzvrMBgzJNFYjCX", "amount": "178251461.4" },
|
||||
{ "recipient": "QRyECqW54ywKVt4kZTEXyRY17aaFUaxzc4", "amount": "8772355.539" },
|
||||
{ "recipient": "QgpH3K3ArkQTg15xjKqGq3BRgE3aNH9Q2P", "amount": "46766535.26" },
|
||||
{ "recipient": "QVZ6pxi8e3K3S44zLbnrLSLwSoYT8CWbwV", "amount": "233172022.2" },
|
||||
{ "recipient": "QNbA69dbnmwqJHLQeS9v63hSLZXXGkmtC6", "amount": "46626632.05" },
|
||||
{ "recipient": "QgzudSKbcLUeQUhFngotVswDSkbU42dSMr", "amount": "83786479.99" },
|
||||
{ "recipient": "QfkQ2UzKMBGPwj8Sm31SArjtXoka1ubU3i", "amount": "116345066.7" },
|
||||
{ "recipient": "QgxHHNwawZeTmQ3i5d9enchi4T9VmzNZ5k", "amount": "155448014.8" },
|
||||
{ "recipient": "QMNugJWNsLuV4Qmbzdf8r8RMEdXk5PNM69", "amount": "155448014.8" },
|
||||
{ "recipient": "QVhWuJkCjStNMV4U8PtNM9Qz4PvLAEtVSj", "amount": "101041209.6" },
|
||||
{ "recipient": "QXjNcckFG9gTr9YbiA3RrRhn3mPJ9zyR4G", "amount": "3108960.297" },
|
||||
{ "recipient": "QThnuBadmExtxk81vhFKimSzbPaPcuPAdm", "amount": "155448014.8" },
|
||||
{ "recipient": "QRc6sQthLHjfkmm2BUhu74g33XtkDoB7JP", "amount": "77773983.95" },
|
||||
{ "recipient": "QcDLhirHkSbR4TLYeShLzHw61B8UGTFusk", "amount": "23317202.22" },
|
||||
{ "recipient": "QXRnsXE6srHEf2abGh4eogs2mRsmNiuw6V", "amount": "5440680.519" },
|
||||
{ "recipient": "QRJmEswbDw4x1kwsLyxtMS9533fv5cDvQV", "amount": "3886200.371" },
|
||||
{ "recipient": "Qg43mCzWmFVwhVfx34g6shXnSU7U7amJNx", "amount": "6217920.593" },
|
||||
{ "recipient": "QQ9PveFTW64yUcXEE6AxhokWCwhmn8F2TD", "amount": "8549640.816" },
|
||||
{ "recipient": "QQaxJuTkW5XXn4DhhRekXpdXaWcsxEfCNG", "amount": "3886200.371" },
|
||||
{ "recipient": "QifWFqW8XWL5mcNxtdr5z1LVC7XUu9tNSK", "amount": "3116732.697" },
|
||||
{ "recipient": "QavhBKRN4vuyzHNNqcWxjcohRAJNTdTmh4", "amount": "154670774.8" },
|
||||
{ "recipient": "QMQyR3Hybof8WpQsXPxh19AZFCj4Z4mmke", "amount": "77724007.42" },
|
||||
{ "recipient": "QbT3GGjp1esTXtowVk2XCtBsKoRB8mkP61", "amount": "77724007.42" },
|
||||
{ "recipient": "QT13tVMZEtbrgJEsBBcTtnyqGveC7mtqAb", "amount": "23317202.22" },
|
||||
{ "recipient": "QegT2Ws5YjLQzEZ9YMzWsAZMBE8cAygHZN", "amount": "12606834" },
|
||||
{ "recipient": "QXoKRBJiJGKwvdA3jkmoUhkM7y6vuMp2pn", "amount": "65117173.41" },
|
||||
{ "recipient": "QY6SpdBzUev9ziqkmyaxESZSbdKwqGdedn", "amount": "89382608.53" },
|
||||
{ "recipient": "QeMxyt1nEE7tbFbioc87xhiKb4szx5DsjY", "amount": "15544801.48" },
|
||||
{ "recipient": "QcTp3THGZvJ42f2mWsQrawGvgBoSHgHZyk", "amount": "39639243.78" },
|
||||
{ "recipient": "QjSH91mTDN6TeV1naAcfwPhmRogufV4n1u", "amount": "23317202.22" },
|
||||
{ "recipient": "QiFLELeLm2TFWsnknzje51wMdt3Srkjz8g", "amount": "1554480.148" },
|
||||
{ "recipient": "QhxtJ3vvhsvVU9x2j5n2R3TXzutfLMUvBR", "amount": "23317202.22" },
|
||||
{ "recipient": "QUtUSNQfqexZZkaZ2s9LcpqjnTezPTnuAx", "amount": "15544801.48" },
|
||||
{ "recipient": "Qg6sPLxNMYxjEDGLLaFkkWx6ip3py5fLEt", "amount": "777240.0742" },
|
||||
{ "recipient": "QeLixskYbdkiAHmhBVMa2Pdi85YPFqw3Ed", "amount": "38862003.71" },
|
||||
{ "recipient": "Qary17o9qvZ2fifiVC8tF5zoBJm79n18zA", "amount": "3893972.772" },
|
||||
{ "recipient": "QLvCWDGwzwpR29XgiThMGDX2vxyFW5rFHB", "amount": "8790585.239" },
|
||||
{ "recipient": "Qgc77fSoAoUSVJfq62GxxTin6dBtU7Y6Hb", "amount": "194310018.5" },
|
||||
{ "recipient": "QPmPKjwPLCuRei6abuhMtMocxAEeSuLVcv", "amount": "23317202.22" },
|
||||
{ "recipient": "QcGfZePUN7JHs9WEEkJhXGzALy4JybiS3N", "amount": "194224522.1" },
|
||||
{ "recipient": "QSeXGwk7eQjR8j7bndJST19qWtM2qnqL1u", "amount": "38862003.71" },
|
||||
{ "recipient": "QU9i68h71nKTg4gwc5yJHzNRQdQEswP7Kn", "amount": "139592317.3" },
|
||||
{ "recipient": "QdKrZGCkwXSSeXJhVA1idDXsA4VFtrjPHN", "amount": "15544801.48" },
|
||||
{ "recipient": "QiYJ2B797xFpWYFu4XWivhGhyPXLU7S5Mr", "amount": "77724007.42" },
|
||||
{ "recipient": "QWxqtsNXUWSjYns2wdngh4WBSWQzLoQHvx", "amount": "232613963.9" },
|
||||
{ "recipient": "QTAGfu4FpTZ1bnvnd17YPtB3zabxfWKNeM", "amount": "101041209.6" },
|
||||
{ "recipient": "QPtRxchgRdwdnoZRwhiAoa77AvVPNSRcQk", "amount": "114254290.9" },
|
||||
{ "recipient": "QMcfoVc9Jat2pMFLHcuPEPnY6p6uBK6Dk7", "amount": "77724007.42" },
|
||||
{ "recipient": "Qi84KosdwSWHZX3qz4WcMgqYGutBmj14dd", "amount": "15544801.48" },
|
||||
{ "recipient": "QjAtcHsgig2tvdGr5tR4oGmRarhuojrAK1", "amount": "2883560.675" },
|
||||
{ "recipient": "QPJPNLP2NMHu5athB7ydezdTA6zviCV378", "amount": "6373368.608" },
|
||||
{ "recipient": "QfVLpmLbuUnA1JEe9FmeUAzihoBvqYDp8B", "amount": "15544801.48" },
|
||||
{ "recipient": "QVVFdy6VLFqAFCb6XSBJLLZybiKgfgDDZV", "amount": "10725913.02" },
|
||||
{ "recipient": "QVFXyeG1xpAR8Xg3u7oAmW8unueGAfeaKi", "amount": "31221733.78" },
|
||||
{ "recipient": "QdtQtrM1h3TLtwAGCNyoTrW5HyiPRLhrPq", "amount": "138426457.2" },
|
||||
{ "recipient": "QMukUMr84Mi2niz6rdhEJMkKJBve7uuRfe", "amount": "116586011.1" },
|
||||
{ "recipient": "QZR8c7dmfwqGPujebFH1miQToJZ4JQfU1X", "amount": "217938116.8" },
|
||||
{ "recipient": "QVV5Uu8eCxufTrBtquDKA96d7Kk8S4V7yX", "amount": "40091961.25" },
|
||||
{ "recipient": "QY9YdgfTEUFvQ2UJszGS63qkwdENkW1PQ5", "amount": "154670774.8" },
|
||||
{ "recipient": "QNgiswyhVyNJG4UMzvoSf29wDvGZqqs7WG", "amount": "11658601.11" },
|
||||
{ "recipient": "QabjgFiY34oihNkUcy9hpFjQdCaypCShMe", "amount": "54406805.19" },
|
||||
{ "recipient": "QionidPRekdshCTRL3c7idWWRAqGYcKaFN", "amount": "7772400.742" },
|
||||
{ "recipient": "QcJdBJiVgiNBNg6ZwZAiEfYDMi5ZTQaYAa", "amount": "81386689.86" },
|
||||
{ "recipient": "QNc8XMpPwM1HESwB7kqw8HoQ5sK2miZ2un", "amount": "190423818.2" },
|
||||
{ "recipient": "QUP1SeaNw7CvCnmDp5ai3onWYwThS4GEpu", "amount": "3886200.371" },
|
||||
{ "recipient": "QinToqEztNN1TsLdQEuzTHh7vUrEo6JTU2", "amount": "102440241.8" },
|
||||
{ "recipient": "QcLJYLV4RD4GmPcoNnh7dQrWeYgiiPiqFQ", "amount": "32644083.11" },
|
||||
{ "recipient": "QdYdYGYfgmMX4jQNWMZqLr81R3HdnuiKkv", "amount": "76169527.27" },
|
||||
{ "recipient": "Qi62mUW5zfJhgRL8FRmCpjSCCnSKtf76S6", "amount": "76169527.27" },
|
||||
{ "recipient": "QgFkxqQGkLW6CD95N2zTnT1PPqb9nxWp6b", "amount": "76169527.27" },
|
||||
{ "recipient": "QfNUBudYsrrq27YqiHGLUg6BtG52W1W1ci", "amount": "15544801.48" },
|
||||
{ "recipient": "QPSFoexnGoMH7EPdg72dM7SvqA7d4M2cu7", "amount": "37307523.56" },
|
||||
{ "recipient": "QQxt5WMvoJ2TNScAzcoxHXPnLTeQ43nQ7N", "amount": "21995894.1" },
|
||||
{ "recipient": "QicpACxck2oDYpzP8iWRQYD4oirCtvjok9", "amount": "93268808.9" },
|
||||
{ "recipient": "QVTJkdQkTGgqEED9kAsp4BZbYNJqWfhgGw", "amount": "153909079.5" },
|
||||
{ "recipient": "QQL5vCkhpXnP9F4wqNiBQsNaCocmRcDSUY", "amount": "15512934.64" },
|
||||
{ "recipient": "QSvEex3p2LaZCVBaCyL8MpYsEpHLwed17r", "amount": "155448014.8" },
|
||||
{ "recipient": "Qb3Xv96GucQpBG8n96QVFgcs2xXsEWW4CE", "amount": "38862003.71" },
|
||||
{ "recipient": "QdRua9MqXufALpQFDeYiQDYk3EBGdwGXSx", "amount": "230303229.1" },
|
||||
{ "recipient": "Qh16Umei91JqiHEVWV8AC6ED9aBqbDYuph", "amount": "231073474" },
|
||||
{ "recipient": "QMu6HXfZCnwaNmyFjjhWTYAUW7k1x7PoVr", "amount": "231073474" },
|
||||
{ "recipient": "QgcphUTiVHHfHg8e1LVgg5jujVES7ZDUTr", "amount": "115031531" },
|
||||
{ "recipient": "QbQk9s4j4EAxAguBhmqA8mdtTct3qGnsrx", "amount": "138348733.2" },
|
||||
{ "recipient": "QT79PhvBwE6vFzfZ4oh5wdKVsEazZuVJFy", "amount": "6360421.343" }
|
||||
]
|
||||
},
|
||||
"featureTriggers": {
|
||||
"message": { "height": "99000" },
|
||||
"AT": { "height": "99000" },
|
||||
"assets": { "timestamp": "0" },
|
||||
"voting": { "timestamp": "1403715600000" },
|
||||
"arbitrary": { "timestamp": "1405702800000" },
|
||||
"powfix": { "timestamp": "1456426800000" },
|
||||
"v2" : { "timestamp": "1552500000000" }
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package controller;
|
||||
|
||||
import api.ApiService;
|
||||
import qora.block.BlockChain;
|
||||
import repository.DataException;
|
||||
import repository.RepositoryFactory;
|
||||
import repository.RepositoryManager;
|
||||
import repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import settings.Settings;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -22,6 +24,9 @@ public class Controller {
|
||||
public static void main(String args[]) {
|
||||
LOGGER.info("Starting up...");
|
||||
|
||||
// Load/check settings, which potentially sets up blockchain config, etc.
|
||||
Settings.getInstance();
|
||||
|
||||
LOGGER.info("Starting repository");
|
||||
try {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||
@ -31,6 +36,13 @@ public class Controller {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
BlockChain.validate();
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Couldn't validate repository", e);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
LOGGER.info("Starting API");
|
||||
try {
|
||||
ApiService apiService = ApiService.getInstance();
|
||||
|
@ -149,7 +149,7 @@ public class BTC {
|
||||
|
||||
private BTC() {
|
||||
// Start wallet
|
||||
if (Settings.getInstance().isTestNet()) {
|
||||
if (Settings.getInstance().useBitcoinTestNet()) {
|
||||
params = TestNet3Params.get();
|
||||
chainFileName = "bitcoinj-testnet.spvchain";
|
||||
checkpointsFileName = "checkpoints-testnet.txt";
|
||||
|
@ -7,16 +7,22 @@ import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.account.GenesisAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
@Schema(
|
||||
allOf = {
|
||||
TransactionData.class
|
||||
}
|
||||
)
|
||||
public class GenesisTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private String recipient;
|
||||
private BigDecimal amount;
|
||||
private long assetId;
|
||||
|
||||
// Constructors
|
||||
|
||||
@ -24,16 +30,25 @@ public class GenesisTransactionData extends TransactionData {
|
||||
protected GenesisTransactionData() {
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp, byte[] signature) {
|
||||
// Zero fee
|
||||
super(TransactionType.GENESIS, BigDecimal.ZERO, GenesisAccount.PUBLIC_KEY, timestamp, null, signature);
|
||||
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
|
||||
this(recipient, amount, Asset.QORA, timestamp, signature);
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp) {
|
||||
this(recipient, amount, assetId, timestamp, null);
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp) {
|
||||
this(recipient, amount, timestamp, null);
|
||||
this(recipient, amount, Asset.QORA, timestamp, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
@ -46,4 +61,8 @@ public class GenesisTransactionData extends TransactionData {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class Account {
|
||||
BlockRepository blockRepository = this.repository.getBlockRepository();
|
||||
BlockData blockData = blockRepository.getLastBlock();
|
||||
|
||||
for (int i = 1; i < BlockChain.BLOCK_RETARGET_INTERVAL && blockData != null && blockData.getHeight() > 1; ++i) {
|
||||
for (int i = 1; i < BlockChain.getInstance().getBlockDifficultyInterval() && blockData != null && blockData.getHeight() > 1; ++i) {
|
||||
Block block = new Block(this.repository, blockData);
|
||||
|
||||
// CIYAM AT transactions should be fetched from repository so no special handling needed here
|
||||
|
@ -117,6 +117,8 @@ public class Block {
|
||||
|
||||
/** Cached copy of next block's generating balance */
|
||||
protected BigDecimal cachedNextGeneratingBalance;
|
||||
/** Minimum Qora balance for use in calculations. */
|
||||
public static final BigDecimal MIN_BALANCE = BigDecimal.valueOf(1L).setScale(8);
|
||||
|
||||
// Other useful constants
|
||||
|
||||
@ -255,11 +257,11 @@ public class Block {
|
||||
if (this.blockData.getHeight() == null)
|
||||
throw new IllegalStateException("Can't determine next block's version as this block has no height set");
|
||||
|
||||
if (this.blockData.getHeight() < BlockChain.getATReleaseHeight())
|
||||
if (this.blockData.getHeight() < BlockChain.getInstance().getATReleaseHeight())
|
||||
return 1;
|
||||
else if (this.blockData.getTimestamp() < BlockChain.getPowFixReleaseTimestamp())
|
||||
else if (this.blockData.getTimestamp() < BlockChain.getInstance().getPowFixReleaseTimestamp())
|
||||
return 2;
|
||||
else if (this.blockData.getTimestamp() < BlockChain.getQoraV2Timestamp())
|
||||
else if (this.blockData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return 3;
|
||||
else
|
||||
return 4;
|
||||
@ -281,7 +283,7 @@ public class Block {
|
||||
throw new IllegalStateException("Can't calculate next block's generating balance as this block's height is unset");
|
||||
|
||||
// This block not at the start of an interval?
|
||||
if (this.blockData.getHeight() % BlockChain.BLOCK_RETARGET_INTERVAL != 0)
|
||||
if (this.blockData.getHeight() % BlockChain.getInstance().getBlockDifficultyInterval() != 0)
|
||||
return this.blockData.getGeneratingBalance();
|
||||
|
||||
// Return cached calculation if we have one
|
||||
@ -296,7 +298,7 @@ public class Block {
|
||||
BlockData firstBlock = this.blockData;
|
||||
|
||||
try {
|
||||
for (int i = 1; firstBlock != null && i < BlockChain.BLOCK_RETARGET_INTERVAL; ++i)
|
||||
for (int i = 1; firstBlock != null && i < BlockChain.getInstance().getBlockDifficultyInterval(); ++i)
|
||||
firstBlock = blockRepo.fromSignature(firstBlock.getReference());
|
||||
} catch (DataException e) {
|
||||
firstBlock = null;
|
||||
@ -310,20 +312,21 @@ public class Block {
|
||||
long previousGeneratingTime = this.blockData.getTimestamp() - firstBlock.getTimestamp();
|
||||
|
||||
// Calculate expected forging time (in ms) for a whole interval based on this block's generating balance.
|
||||
long expectedGeneratingTime = Block.calcForgingDelay(this.blockData.getGeneratingBalance()) * BlockChain.BLOCK_RETARGET_INTERVAL * 1000;
|
||||
long expectedGeneratingTime = Block.calcForgingDelay(this.blockData.getGeneratingBalance()) * BlockChain.getInstance().getBlockDifficultyInterval()
|
||||
* 1000;
|
||||
|
||||
// Finally, scale generating balance such that faster than expected previous intervals produce larger generating balances.
|
||||
// NOTE: we have to use doubles and longs here to keep compatibility with Qora v1 results
|
||||
double multiplier = (double) expectedGeneratingTime / (double) previousGeneratingTime;
|
||||
long nextGeneratingBalance = (long) (this.blockData.getGeneratingBalance().doubleValue() * multiplier);
|
||||
|
||||
this.cachedNextGeneratingBalance = BlockChain.minMaxBalance(BigDecimal.valueOf(nextGeneratingBalance).setScale(8));
|
||||
this.cachedNextGeneratingBalance = Block.minMaxBalance(BigDecimal.valueOf(nextGeneratingBalance).setScale(8));
|
||||
|
||||
return this.cachedNextGeneratingBalance;
|
||||
}
|
||||
|
||||
public static long calcBaseTarget(BigDecimal generatingBalance) {
|
||||
generatingBalance = BlockChain.minMaxBalance(generatingBalance);
|
||||
generatingBalance = Block.minMaxBalance(generatingBalance);
|
||||
return generatingBalance.longValue() * calcForgingDelay(generatingBalance);
|
||||
}
|
||||
|
||||
@ -331,10 +334,11 @@ public class Block {
|
||||
* Return expected forging delay, in seconds, since previous block based on passed generating balance.
|
||||
*/
|
||||
public static long calcForgingDelay(BigDecimal generatingBalance) {
|
||||
generatingBalance = BlockChain.minMaxBalance(generatingBalance);
|
||||
generatingBalance = Block.minMaxBalance(generatingBalance);
|
||||
|
||||
double percentageOfTotal = generatingBalance.divide(BlockChain.MAX_BALANCE).doubleValue();
|
||||
long actualBlockTime = (long) (BlockChain.MIN_BLOCK_TIME + ((BlockChain.MAX_BLOCK_TIME - BlockChain.MIN_BLOCK_TIME) * (1 - percentageOfTotal)));
|
||||
double percentageOfTotal = generatingBalance.divide(BlockChain.getInstance().getMaxBalance()).doubleValue();
|
||||
long actualBlockTime = (long) (BlockChain.getInstance().getMinBlockTime()
|
||||
+ ((BlockChain.getInstance().getMaxBlockTime() - BlockChain.getInstance().getMinBlockTime()) * (1 - percentageOfTotal)));
|
||||
|
||||
return actualBlockTime;
|
||||
}
|
||||
@ -670,7 +674,7 @@ public class Block {
|
||||
return ValidationResult.TIMESTAMP_OLDER_THAN_PARENT;
|
||||
|
||||
// Check timestamp is not in the future (within configurable ~500ms margin)
|
||||
if (this.blockData.getTimestamp() - BlockChain.BLOCK_TIMESTAMP_MARGIN > NTP.getTime())
|
||||
if (this.blockData.getTimestamp() - BlockChain.getInstance().getBlockTimestampMargin() > NTP.getTime())
|
||||
return ValidationResult.TIMESTAMP_IN_FUTURE;
|
||||
|
||||
// Legacy gen1 test: check timestamp milliseconds is the same as parent timestamp milliseconds?
|
||||
@ -948,4 +952,17 @@ public class Block {
|
||||
this.repository.getBlockRepository().delete(this.blockData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Qora balance adjusted to within min/max limits.
|
||||
*/
|
||||
public static BigDecimal minMaxBalance(BigDecimal balance) {
|
||||
if (balance.compareTo(Block.MIN_BALANCE) < 0)
|
||||
return Block.MIN_BALANCE;
|
||||
|
||||
if (balance.compareTo(BlockChain.getInstance().getMaxBalance()) > 0)
|
||||
return BlockChain.getInstance().getMaxBalance();
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,17 @@
|
||||
package qora.block;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import data.assets.AssetData;
|
||||
import data.block.BlockData;
|
||||
@ -18,28 +28,175 @@ import settings.Settings;
|
||||
*/
|
||||
public class BlockChain {
|
||||
|
||||
/** Minimum Qora balance for use in calculations. */
|
||||
public static final BigDecimal MIN_BALANCE = BigDecimal.valueOf(1L).setScale(8);
|
||||
/** Maximum Qora balance. */
|
||||
public static final BigDecimal MAX_BALANCE = BigDecimal.valueOf(10_000_000_000L).setScale(8);
|
||||
private static final Logger LOGGER = LogManager.getLogger(BlockChain.class);
|
||||
|
||||
public enum FeatureValueType {
|
||||
height,
|
||||
timestamp;
|
||||
}
|
||||
|
||||
private static BlockChain instance = null;
|
||||
|
||||
// Properties
|
||||
private BigDecimal unitFee;
|
||||
private BigDecimal maxBytesPerUnitFee;
|
||||
private BigDecimal minFeePerByte;
|
||||
/** Maximum coin supply. */
|
||||
private BigDecimal maxBalance;;
|
||||
/** Number of blocks between recalculating block's generating balance. */
|
||||
public static final int BLOCK_RETARGET_INTERVAL = 10;
|
||||
private int blockDifficultyInterval;
|
||||
/** Minimum target time between blocks, in seconds. */
|
||||
public static final long MIN_BLOCK_TIME = 60;
|
||||
private long minBlockTime;
|
||||
/** Maximum target time between blocks, in seconds. */
|
||||
public static final long MAX_BLOCK_TIME = 300;
|
||||
private long maxBlockTime;
|
||||
/** Maximum acceptable timestamp disagreement offset in milliseconds. */
|
||||
public static final long BLOCK_TIMESTAMP_MARGIN = 500L;
|
||||
private long blockTimestampMargin;
|
||||
/** Map of which blockchain features are enabled when (height/timestamp) */
|
||||
private Map<String, Map<FeatureValueType, Long>> featureTriggers;
|
||||
|
||||
// Various release timestamps / block heights
|
||||
private static final int MESSAGE_RELEASE_HEIGHT = 99000;
|
||||
private static final int AT_RELEASE_HEIGHT = 99000;
|
||||
private static final long POWFIX_RELEASE_TIMESTAMP = 1456426800000L; // Block Version 3 // 2016-02-25T19:00:00+00:00
|
||||
private static final long ASSETS_RELEASE_TIMESTAMP = 0L; // From Qora epoch
|
||||
private static final long VOTING_RELEASE_TIMESTAMP = 1403715600000L; // 2014-06-25T17:00:00+00:00
|
||||
private static final long ARBITRARY_RELEASE_TIMESTAMP = 1405702800000L; // 2014-07-18T17:00:00+00:00
|
||||
// Constructors, etc.
|
||||
|
||||
private static final long QORA_V2_TIMESTAMP = 1552500000000L; // 2019-03-13T18:00:00+00:00 // Future Qora v2 blocks and transactions
|
||||
private BlockChain() {
|
||||
}
|
||||
|
||||
public static BlockChain getInstance() {
|
||||
if (instance == null)
|
||||
Settings.getInstance();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public BigDecimal getUnitFee() {
|
||||
return this.unitFee;
|
||||
}
|
||||
|
||||
public BigDecimal getMaxBytesPerUnitFee() {
|
||||
return this.maxBytesPerUnitFee;
|
||||
}
|
||||
|
||||
public BigDecimal getMinFeePerByte() {
|
||||
return this.minFeePerByte;
|
||||
}
|
||||
|
||||
public BigDecimal getMaxBalance() {
|
||||
return this.maxBalance;
|
||||
}
|
||||
|
||||
public int getBlockDifficultyInterval() {
|
||||
return this.blockDifficultyInterval;
|
||||
}
|
||||
|
||||
public long getMinBlockTime() {
|
||||
return this.minBlockTime;
|
||||
}
|
||||
|
||||
public long getMaxBlockTime() {
|
||||
return this.maxBlockTime;
|
||||
}
|
||||
|
||||
public long getBlockTimestampMargin() {
|
||||
return this.blockTimestampMargin;
|
||||
}
|
||||
|
||||
private long getFeatureTrigger(String feature, FeatureValueType valueType) {
|
||||
Map<FeatureValueType, Long> featureTrigger = featureTriggers.get(feature);
|
||||
if (featureTrigger == null)
|
||||
return 0;
|
||||
|
||||
Long value = featureTrigger.get(valueType);
|
||||
if (value == null)
|
||||
return 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Convenience methods for specific blockchain feature triggers
|
||||
|
||||
public long getMessageReleaseHeight() {
|
||||
return getFeatureTrigger("message", FeatureValueType.height);
|
||||
}
|
||||
|
||||
public long getATReleaseHeight() {
|
||||
return getFeatureTrigger("AT", FeatureValueType.height);
|
||||
}
|
||||
|
||||
public long getPowFixReleaseTimestamp() {
|
||||
return getFeatureTrigger("powfix", FeatureValueType.timestamp);
|
||||
}
|
||||
|
||||
public long getAssetsReleaseTimestamp() {
|
||||
return getFeatureTrigger("assets", FeatureValueType.timestamp);
|
||||
}
|
||||
|
||||
public long getVotingReleaseTimestamp() {
|
||||
return getFeatureTrigger("voting", FeatureValueType.timestamp);
|
||||
}
|
||||
|
||||
public long getArbitraryReleaseTimestamp() {
|
||||
return getFeatureTrigger("arbitrary", FeatureValueType.timestamp);
|
||||
}
|
||||
|
||||
public long getQoraV2Timestamp() {
|
||||
return getFeatureTrigger("v2", FeatureValueType.timestamp);
|
||||
}
|
||||
|
||||
// Blockchain config from JSON
|
||||
|
||||
public static void fromJSON(JSONObject json) {
|
||||
Object genesisJson = json.get("genesis");
|
||||
if (genesisJson == null) {
|
||||
LOGGER.error("No \"genesis\" entry found in blockchain config");
|
||||
throw new RuntimeException("No \"genesis\" entry found in blockchain config");
|
||||
}
|
||||
GenesisBlock.fromJSON((JSONObject) genesisJson);
|
||||
|
||||
// Simple blockchain properties
|
||||
BigDecimal unitFee = Settings.getJsonBigDecimal(json, "unitFee");
|
||||
long maxBytesPerUnitFee = (Long) Settings.getTypedJson(json, "maxBytesPerUnitFee", Long.class);
|
||||
BigDecimal maxBalance = Settings.getJsonBigDecimal(json, "coinSupply");
|
||||
int blockDifficultyInterval = ((Long) Settings.getTypedJson(json, "blockDifficultyInterval", Long.class)).intValue();
|
||||
long minBlockTime = (Long) Settings.getTypedJson(json, "minBlockTime", Long.class);
|
||||
long maxBlockTime = (Long) Settings.getTypedJson(json, "maxBlockTime", Long.class);
|
||||
long blockTimestampMargin = (Long) Settings.getTypedJson(json, "blockTimestampMargin", Long.class);
|
||||
|
||||
// blockchain feature triggers
|
||||
Map<String, Map<FeatureValueType, Long>> featureTriggers = new HashMap<>();
|
||||
JSONObject featuresJson = (JSONObject) Settings.getTypedJson(json, "featureTriggers", JSONObject.class);
|
||||
for (Object feature : featuresJson.keySet()) {
|
||||
String featureKey = (String) feature;
|
||||
JSONObject trigger = (JSONObject) Settings.getTypedJson(featuresJson, featureKey, JSONObject.class);
|
||||
|
||||
if (!trigger.containsKey("height") && !trigger.containsKey("timestamp")) {
|
||||
LOGGER.error("Feature trigger \"" + featureKey + "\" must contain \"height\" or \"timestamp\" in blockchain config file");
|
||||
throw new RuntimeException("Feature trigger \"" + featureKey + "\" must contain \"height\" or \"timestamp\" in blockchain config file");
|
||||
}
|
||||
|
||||
String triggerKey = (String) trigger.keySet().iterator().next();
|
||||
FeatureValueType featureValueType = FeatureValueType.valueOf(triggerKey);
|
||||
if (featureValueType == null) {
|
||||
LOGGER.error("Unrecognised feature trigger value type \"" + triggerKey + "\" for feature \"" + featureKey + "\" in blockchain config file");
|
||||
throw new RuntimeException(
|
||||
"Unrecognised feature trigger value type \"" + triggerKey + "\" for feature \"" + featureKey + "\" in blockchain config file");
|
||||
}
|
||||
|
||||
Long value = (Long) Settings.getJsonQuotedLong(trigger, triggerKey);
|
||||
|
||||
featureTriggers.put(featureKey, Collections.singletonMap(featureValueType, value));
|
||||
}
|
||||
|
||||
instance = new BlockChain();
|
||||
instance.unitFee = unitFee;
|
||||
instance.maxBytesPerUnitFee = BigDecimal.valueOf(maxBytesPerUnitFee).setScale(8);
|
||||
instance.minFeePerByte = unitFee.divide(instance.maxBytesPerUnitFee, MathContext.DECIMAL32);
|
||||
instance.maxBalance = maxBalance;
|
||||
instance.blockDifficultyInterval = blockDifficultyInterval;
|
||||
instance.minBlockTime = minBlockTime;
|
||||
instance.maxBlockTime = maxBlockTime;
|
||||
instance.blockTimestampMargin = blockTimestampMargin;
|
||||
instance.featureTriggers = featureTriggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some sort start-up/initialization/checking method.
|
||||
@ -74,79 +231,17 @@ public class BlockChain {
|
||||
repository.rebuild();
|
||||
|
||||
// Add Genesis Block
|
||||
GenesisBlock genesisBlock = new GenesisBlock(repository);
|
||||
GenesisBlock genesisBlock = GenesisBlock.getInstance(repository);
|
||||
genesisBlock.process();
|
||||
|
||||
// Add QORA asset.
|
||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
||||
AssetData qoraAssetData = new AssetData(Asset.QORA, genesisBlock.getGenerator().getAddress(), "Qora", "This is the simulated Qora asset.",
|
||||
10_000_000_000L, true, genesisBlock.getBlockData().getGeneratorSignature());
|
||||
BlockChain.getInstance().getMaxBalance().longValue(), true, genesisBlock.getBlockData().getGeneratorSignature());
|
||||
repository.getAssetRepository().save(qoraAssetData);
|
||||
|
||||
repository.saveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Qora balance adjusted to within min/max limits.
|
||||
*/
|
||||
public static BigDecimal minMaxBalance(BigDecimal balance) {
|
||||
if (balance.compareTo(MIN_BALANCE) < 0)
|
||||
return MIN_BALANCE;
|
||||
|
||||
if (balance.compareTo(MAX_BALANCE) > 0)
|
||||
return MAX_BALANCE;
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
public static int getMessageReleaseHeight() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return MESSAGE_RELEASE_HEIGHT;
|
||||
}
|
||||
|
||||
public static int getATReleaseHeight() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return AT_RELEASE_HEIGHT;
|
||||
}
|
||||
|
||||
public static long getPowFixReleaseTimestamp() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return POWFIX_RELEASE_TIMESTAMP;
|
||||
}
|
||||
|
||||
public static long getAssetsReleaseTimestamp() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return ASSETS_RELEASE_TIMESTAMP;
|
||||
}
|
||||
|
||||
public static long getVotingReleaseTimestamp() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return VOTING_RELEASE_TIMESTAMP;
|
||||
}
|
||||
|
||||
public static long getArbitraryReleaseTimestamp() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return ARBITRARY_RELEASE_TIMESTAMP;
|
||||
}
|
||||
|
||||
public static long getQoraV2Timestamp() {
|
||||
if (Settings.getInstance().isTestNet())
|
||||
return 0;
|
||||
|
||||
return QORA_V2_TIMESTAMP;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,13 @@ import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Longs;
|
||||
@ -21,158 +28,92 @@ import settings.Settings;
|
||||
|
||||
public class GenesisBlock extends Block {
|
||||
|
||||
// Properties
|
||||
private static final int GENESIS_BLOCK_VERSION = 1;
|
||||
private static final byte[] GENESIS_REFERENCE = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1 }; // NOTE: Neither 64 nor 128 bytes!
|
||||
private static final BigDecimal GENESIS_GENERATING_BALANCE = BigDecimal.valueOf(10_000_000L).setScale(8);
|
||||
private static final Logger LOGGER = LogManager.getLogger(GenesisBlock.class);
|
||||
|
||||
private static final byte[] GENESIS_REFERENCE = new byte[] {
|
||||
1, 1, 1, 1, 1, 1, 1, 1
|
||||
}; // NOTE: Neither 64 nor 128 bytes!
|
||||
private static final byte[] GENESIS_GENERATOR_PUBLIC_KEY = GenesisAccount.PUBLIC_KEY; // NOTE: 8 bytes not 32 bytes!
|
||||
public static final long GENESIS_TIMESTAMP = 1400247274336L; // QORA RELEASE: Fri May 16 13:34:34.336 2014 UTC
|
||||
private static final byte[] GENESIS_GENERATOR_SIGNATURE = calcSignature();
|
||||
private static final byte[] GENESIS_TRANSACTIONS_SIGNATURE = calcSignature();
|
||||
|
||||
// Properties
|
||||
private static BlockData blockData;
|
||||
private static List<TransactionData> transactionsData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public GenesisBlock(Repository repository) throws DataException {
|
||||
super(repository, new BlockData(GENESIS_BLOCK_VERSION, GENESIS_REFERENCE, 0, BigDecimal.ZERO.setScale(8), GENESIS_TRANSACTIONS_SIGNATURE, 1,
|
||||
Settings.getInstance().getGenesisTimestamp(), GENESIS_GENERATING_BALANCE, GENESIS_GENERATOR_PUBLIC_KEY, GENESIS_GENERATOR_SIGNATURE, 0, BigDecimal.ZERO.setScale(8)));
|
||||
private GenesisBlock(Repository repository, BlockData blockData, List<TransactionData> transactions) throws DataException {
|
||||
super(repository, blockData, transactions, Collections.emptyList());
|
||||
}
|
||||
|
||||
this.transactions = new ArrayList<Transaction>();
|
||||
public static GenesisBlock getInstance(Repository repository) throws DataException {
|
||||
return new GenesisBlock(repository, blockData, transactionsData);
|
||||
}
|
||||
|
||||
// Genesis transactions
|
||||
addGenesisTransaction("QUD9y7NZqTtNwvSAUfewd7zKUGoVivVnTW", "7032468.191");
|
||||
addGenesisTransaction("QVafvKkE5bZTkq8PcXvdaxwuLNN2DGCwYk", "1716146.084");
|
||||
addGenesisTransaction("QV42QQP7frYWqsVq536g7zSk97fUpf2ZSN", "5241707.06");
|
||||
addGenesisTransaction("QgkLTm5GkepJpgr53nAgUyYRsvmyHpb2zT", "854964.0816");
|
||||
addGenesisTransaction("Qc8kN338XQULMBuUa6mTqL5tipvELDhzeZ", "769467.6734");
|
||||
addGenesisTransaction("QQ81BA75jZcpjQBLZE1qcHrXV8ARC1DEec", "85496408.16");
|
||||
addGenesisTransaction("QeoSe4DscWX4AFkNBCdm4WS1V7QkUFSQLP", "854968.3564");
|
||||
addGenesisTransaction("Qdfu3Eh21ZVHNDY1xyNaFqTTEYscSmfSsm", "85496408.16");
|
||||
addGenesisTransaction("QeDSr4abXKRg9j5hTN3TK9UGuH3umThZ42", "4445813.224");
|
||||
addGenesisTransaction("QQKDuo1txYB9E2xim79YVR6SQ1ZbJtJtFX", "47023024.49");
|
||||
addGenesisTransaction("QLeaeGr4CDA95FmeMtFh8okJRMLoq8Cge5", "170992816.3");
|
||||
addGenesisTransaction("QSwN5oa8ZHWJmc6FeAJ8Xr1SHaEuSahw1J", "3419856.326");
|
||||
addGenesisTransaction("QWnoGd4a7iXqQmNEpUtCb1x7nWgcya8QbE", "17056533.43");
|
||||
addGenesisTransaction("QbJqhsJjcy3vkzsJ1kHvgn26pQF3sZEypc", "42705455.87");
|
||||
addGenesisTransaction("QiBhBcseKzaDnHKyqEJs8z1Xx2rSb9XhBr", "141069073.5");
|
||||
addGenesisTransaction("QTwYwxBhzivFEWY5yfzyz1pqhJ8XCroKwv", "85496408.16");
|
||||
addGenesisTransaction("QfikxUU15Dy1oxbcDNEcLeU5cHvbrceq3A", "17099281.63");
|
||||
addGenesisTransaction("QhdqBmKZeQ3Hg1XUuR5nKtAkw47tuoRi2q", "12824461.22");
|
||||
addGenesisTransaction("QaVNyTqsTHA6JWMcqntcJf1u9c3qid76xH", "128244612.2");
|
||||
addGenesisTransaction("QYaDa7bmgo5L9qkcfJKjhPPrQkvGjEoc7y", "85496408.16");
|
||||
addGenesisTransaction("QQPddvWaYf4pbCyVHEVoyfC72EiaAv4JhT", "25648922.45");
|
||||
addGenesisTransaction("QSQpTNtTZMwaDuNq56Jz73KHWXaey9JrT1", "26341443.35");
|
||||
addGenesisTransaction("QVjcFWE6TnGePGJEtbNc1thwD2sgHBLvUV", "42940528.25");
|
||||
addGenesisTransaction("Qga93mWNqTuJYx6o33vjUpFH7Cn4sxLyoG", "2564892.245");
|
||||
addGenesisTransaction("QXyHKyQPJnb4ejyTkvS26x9sjWnhTTJ1Uc", "10259568.98");
|
||||
addGenesisTransaction("QLurSSgJvW7WXHDFSobgfakgqXxjoZzwUH", "85496408.16");
|
||||
addGenesisTransaction("QadxfiARcsmwzE93wqq82yXFi3ykM2qdtS", "79118376.11");
|
||||
addGenesisTransaction("QRHhhtz3Cv9RPKB1QBBfkRmRfpXx8vkRa5", "22435418.54");
|
||||
addGenesisTransaction("Qh8UnEs55n8jcnBaBwVtrTGkFFFBDyrMqH", "128757590.7");
|
||||
addGenesisTransaction("QhF7Fu3f54CTYA7zBQ223NQEssi2yAbAcx", "258481290.8");
|
||||
addGenesisTransaction("QPk9VB6tigoifrUYQrw4arBNk7i8HEgsDD", "128244612.2");
|
||||
addGenesisTransaction("QXWJnEPsdtaLQAEutJFR4ySiMUJCWDzZJX", "85496408.16");
|
||||
addGenesisTransaction("QVFs42gM4Cixf4Y5vDFvKKxRAamUPMCAVq", "85496408.16");
|
||||
addGenesisTransaction("Qec5ueWc4rcBrty47GZfFSqvLymxvcycFm", "129091026.7");
|
||||
addGenesisTransaction("QfYiztbDz1Nb9EMhgHidLycvuPN8HEcHEj", "128244612.2");
|
||||
addGenesisTransaction("QPdWsZtaZcAKqk2HWVhEVbws4qG5KUTXmg", "179285967.9");
|
||||
addGenesisTransaction("QVkNs5NcwQpsrCXpWzuMXkMehJr5mkvLVy", "8558190.456");
|
||||
addGenesisTransaction("Qg19DzyEfyZANx6JLy4GrSGF5LuZ2MLqyZ", "42748204.08");
|
||||
addGenesisTransaction("Qf3A8L5WJNHt1xZxmayrTp2d5owzdkcxM6", "50519827.58");
|
||||
addGenesisTransaction("QeKR4W6qkFJGF7Hmu7rSUzTSQiqJzZLXdt", "10216820.77");
|
||||
addGenesisTransaction("QWg7T5i3uBY3xeBLFTLYYruR15Ln11vwo4", "170992816.3");
|
||||
addGenesisTransaction("QUYdM5fHECPZxKQQAmoxoQa2aWg8TZYfPw", "85496408.16");
|
||||
addGenesisTransaction("QjhfEZCgrjUbnLRnWqWxzyYqKQpjjxkuA8", "86665653.61");
|
||||
addGenesisTransaction("QMA53u3wrzDoxC57CWUJePNdR8FoqinqUS", "85496408.16");
|
||||
addGenesisTransaction("QSuCp6mB5zNNeJKD62aq2hR9h84ks1WhHf", "161588211.4");
|
||||
addGenesisTransaction("QS2tCUk7GQefg4zGewwrumxSPmN6fgA7Xc", "170992816.3");
|
||||
addGenesisTransaction("Qcn6FZRxAgp3japtvjgUkBY6KPfbPZMZtM", "170992816.3");
|
||||
addGenesisTransaction("QZrmXZkRmjV2GwMt72Rr1ZqHJjv8raDk5J", "17099281.63");
|
||||
addGenesisTransaction("QeZzwGDfAHa132jb6r4rQHbgJstLuT8QJ3", "255875360.3");
|
||||
addGenesisTransaction("Qj3L139sMMuFvvjKQDwRnoSgKUnoMhDQs5", "76946767.34");
|
||||
addGenesisTransaction("QWJvpvbFRZHu7LRbY5MjzvrMBgzJNFYjCX", "178251461.4");
|
||||
addGenesisTransaction("QRyECqW54ywKVt4kZTEXyRY17aaFUaxzc4", "8772355.539");
|
||||
addGenesisTransaction("QgpH3K3ArkQTg15xjKqGq3BRgE3aNH9Q2P", "46766535.26");
|
||||
addGenesisTransaction("QVZ6pxi8e3K3S44zLbnrLSLwSoYT8CWbwV", "233172022.2");
|
||||
addGenesisTransaction("QNbA69dbnmwqJHLQeS9v63hSLZXXGkmtC6", "46626632.05");
|
||||
addGenesisTransaction("QgzudSKbcLUeQUhFngotVswDSkbU42dSMr", "83786479.99");
|
||||
addGenesisTransaction("QfkQ2UzKMBGPwj8Sm31SArjtXoka1ubU3i", "116345066.7");
|
||||
addGenesisTransaction("QgxHHNwawZeTmQ3i5d9enchi4T9VmzNZ5k", "155448014.8");
|
||||
addGenesisTransaction("QMNugJWNsLuV4Qmbzdf8r8RMEdXk5PNM69", "155448014.8");
|
||||
addGenesisTransaction("QVhWuJkCjStNMV4U8PtNM9Qz4PvLAEtVSj", "101041209.6");
|
||||
addGenesisTransaction("QXjNcckFG9gTr9YbiA3RrRhn3mPJ9zyR4G", "3108960.297");
|
||||
addGenesisTransaction("QThnuBadmExtxk81vhFKimSzbPaPcuPAdm", "155448014.8");
|
||||
addGenesisTransaction("QRc6sQthLHjfkmm2BUhu74g33XtkDoB7JP", "77773983.95");
|
||||
addGenesisTransaction("QcDLhirHkSbR4TLYeShLzHw61B8UGTFusk", "23317202.22");
|
||||
addGenesisTransaction("QXRnsXE6srHEf2abGh4eogs2mRsmNiuw6V", "5440680.519");
|
||||
addGenesisTransaction("QRJmEswbDw4x1kwsLyxtMS9533fv5cDvQV", "3886200.371");
|
||||
addGenesisTransaction("Qg43mCzWmFVwhVfx34g6shXnSU7U7amJNx", "6217920.593");
|
||||
addGenesisTransaction("QQ9PveFTW64yUcXEE6AxhokWCwhmn8F2TD", "8549640.816");
|
||||
addGenesisTransaction("QQaxJuTkW5XXn4DhhRekXpdXaWcsxEfCNG", "3886200.371");
|
||||
addGenesisTransaction("QifWFqW8XWL5mcNxtdr5z1LVC7XUu9tNSK", "3116732.697");
|
||||
addGenesisTransaction("QavhBKRN4vuyzHNNqcWxjcohRAJNTdTmh4", "154670774.8");
|
||||
addGenesisTransaction("QMQyR3Hybof8WpQsXPxh19AZFCj4Z4mmke", "77724007.42");
|
||||
addGenesisTransaction("QbT3GGjp1esTXtowVk2XCtBsKoRB8mkP61", "77724007.42");
|
||||
addGenesisTransaction("QT13tVMZEtbrgJEsBBcTtnyqGveC7mtqAb", "23317202.22");
|
||||
addGenesisTransaction("QegT2Ws5YjLQzEZ9YMzWsAZMBE8cAygHZN", "12606834");
|
||||
addGenesisTransaction("QXoKRBJiJGKwvdA3jkmoUhkM7y6vuMp2pn", "65117173.41");
|
||||
addGenesisTransaction("QY6SpdBzUev9ziqkmyaxESZSbdKwqGdedn", "89382608.53");
|
||||
addGenesisTransaction("QeMxyt1nEE7tbFbioc87xhiKb4szx5DsjY", "15544801.48");
|
||||
addGenesisTransaction("QcTp3THGZvJ42f2mWsQrawGvgBoSHgHZyk", "39639243.78");
|
||||
addGenesisTransaction("QjSH91mTDN6TeV1naAcfwPhmRogufV4n1u", "23317202.22");
|
||||
addGenesisTransaction("QiFLELeLm2TFWsnknzje51wMdt3Srkjz8g", "1554480.148");
|
||||
addGenesisTransaction("QhxtJ3vvhsvVU9x2j5n2R3TXzutfLMUvBR", "23317202.22");
|
||||
addGenesisTransaction("QUtUSNQfqexZZkaZ2s9LcpqjnTezPTnuAx", "15544801.48");
|
||||
addGenesisTransaction("Qg6sPLxNMYxjEDGLLaFkkWx6ip3py5fLEt", "777240.0742");
|
||||
addGenesisTransaction("QeLixskYbdkiAHmhBVMa2Pdi85YPFqw3Ed", "38862003.71");
|
||||
addGenesisTransaction("Qary17o9qvZ2fifiVC8tF5zoBJm79n18zA", "3893972.772");
|
||||
addGenesisTransaction("QLvCWDGwzwpR29XgiThMGDX2vxyFW5rFHB", "8790585.239");
|
||||
addGenesisTransaction("Qgc77fSoAoUSVJfq62GxxTin6dBtU7Y6Hb", "194310018.5");
|
||||
addGenesisTransaction("QPmPKjwPLCuRei6abuhMtMocxAEeSuLVcv", "23317202.22");
|
||||
addGenesisTransaction("QcGfZePUN7JHs9WEEkJhXGzALy4JybiS3N", "194224522.1");
|
||||
addGenesisTransaction("QSeXGwk7eQjR8j7bndJST19qWtM2qnqL1u", "38862003.71");
|
||||
addGenesisTransaction("QU9i68h71nKTg4gwc5yJHzNRQdQEswP7Kn", "139592317.3");
|
||||
addGenesisTransaction("QdKrZGCkwXSSeXJhVA1idDXsA4VFtrjPHN", "15544801.48");
|
||||
addGenesisTransaction("QiYJ2B797xFpWYFu4XWivhGhyPXLU7S5Mr", "77724007.42");
|
||||
addGenesisTransaction("QWxqtsNXUWSjYns2wdngh4WBSWQzLoQHvx", "232613963.9");
|
||||
addGenesisTransaction("QTAGfu4FpTZ1bnvnd17YPtB3zabxfWKNeM", "101041209.6");
|
||||
addGenesisTransaction("QPtRxchgRdwdnoZRwhiAoa77AvVPNSRcQk", "114254290.9");
|
||||
addGenesisTransaction("QMcfoVc9Jat2pMFLHcuPEPnY6p6uBK6Dk7", "77724007.42");
|
||||
addGenesisTransaction("Qi84KosdwSWHZX3qz4WcMgqYGutBmj14dd", "15544801.48");
|
||||
addGenesisTransaction("QjAtcHsgig2tvdGr5tR4oGmRarhuojrAK1", "2883560.675");
|
||||
addGenesisTransaction("QPJPNLP2NMHu5athB7ydezdTA6zviCV378", "6373368.608");
|
||||
addGenesisTransaction("QfVLpmLbuUnA1JEe9FmeUAzihoBvqYDp8B", "15544801.48");
|
||||
addGenesisTransaction("QVVFdy6VLFqAFCb6XSBJLLZybiKgfgDDZV", "10725913.02");
|
||||
addGenesisTransaction("QVFXyeG1xpAR8Xg3u7oAmW8unueGAfeaKi", "31221733.78");
|
||||
addGenesisTransaction("QdtQtrM1h3TLtwAGCNyoTrW5HyiPRLhrPq", "138426457.2");
|
||||
addGenesisTransaction("QMukUMr84Mi2niz6rdhEJMkKJBve7uuRfe", "116586011.1");
|
||||
addGenesisTransaction("QZR8c7dmfwqGPujebFH1miQToJZ4JQfU1X", "217938116.8");
|
||||
addGenesisTransaction("QVV5Uu8eCxufTrBtquDKA96d7Kk8S4V7yX", "40091961.25");
|
||||
addGenesisTransaction("QY9YdgfTEUFvQ2UJszGS63qkwdENkW1PQ5", "154670774.8");
|
||||
addGenesisTransaction("QNgiswyhVyNJG4UMzvoSf29wDvGZqqs7WG", "11658601.11");
|
||||
addGenesisTransaction("QabjgFiY34oihNkUcy9hpFjQdCaypCShMe", "54406805.19");
|
||||
addGenesisTransaction("QionidPRekdshCTRL3c7idWWRAqGYcKaFN", "7772400.742");
|
||||
addGenesisTransaction("QcJdBJiVgiNBNg6ZwZAiEfYDMi5ZTQaYAa", "81386689.86");
|
||||
addGenesisTransaction("QNc8XMpPwM1HESwB7kqw8HoQ5sK2miZ2un", "190423818.2");
|
||||
addGenesisTransaction("QUP1SeaNw7CvCnmDp5ai3onWYwThS4GEpu", "3886200.371");
|
||||
addGenesisTransaction("QinToqEztNN1TsLdQEuzTHh7vUrEo6JTU2", "102440241.8");
|
||||
addGenesisTransaction("QcLJYLV4RD4GmPcoNnh7dQrWeYgiiPiqFQ", "32644083.11");
|
||||
addGenesisTransaction("QdYdYGYfgmMX4jQNWMZqLr81R3HdnuiKkv", "76169527.27");
|
||||
addGenesisTransaction("Qi62mUW5zfJhgRL8FRmCpjSCCnSKtf76S6", "76169527.27");
|
||||
addGenesisTransaction("QgFkxqQGkLW6CD95N2zTnT1PPqb9nxWp6b", "76169527.27");
|
||||
addGenesisTransaction("QfNUBudYsrrq27YqiHGLUg6BtG52W1W1ci", "15544801.48");
|
||||
addGenesisTransaction("QPSFoexnGoMH7EPdg72dM7SvqA7d4M2cu7", "37307523.56");
|
||||
addGenesisTransaction("QQxt5WMvoJ2TNScAzcoxHXPnLTeQ43nQ7N", "21995894.1");
|
||||
addGenesisTransaction("QicpACxck2oDYpzP8iWRQYD4oirCtvjok9", "93268808.9");
|
||||
addGenesisTransaction("QVTJkdQkTGgqEED9kAsp4BZbYNJqWfhgGw", "153909079.5");
|
||||
addGenesisTransaction("QQL5vCkhpXnP9F4wqNiBQsNaCocmRcDSUY", "15512934.64");
|
||||
addGenesisTransaction("QSvEex3p2LaZCVBaCyL8MpYsEpHLwed17r", "155448014.8");
|
||||
addGenesisTransaction("Qb3Xv96GucQpBG8n96QVFgcs2xXsEWW4CE", "38862003.71");
|
||||
addGenesisTransaction("QdRua9MqXufALpQFDeYiQDYk3EBGdwGXSx", "230303229.1");
|
||||
addGenesisTransaction("Qh16Umei91JqiHEVWV8AC6ED9aBqbDYuph", "231073474");
|
||||
addGenesisTransaction("QMu6HXfZCnwaNmyFjjhWTYAUW7k1x7PoVr", "231073474");
|
||||
addGenesisTransaction("QgcphUTiVHHfHg8e1LVgg5jujVES7ZDUTr", "115031531");
|
||||
addGenesisTransaction("QbQk9s4j4EAxAguBhmqA8mdtTct3qGnsrx", "138348733.2");
|
||||
addGenesisTransaction("QT79PhvBwE6vFzfZ4oh5wdKVsEazZuVJFy", "6360421.343");
|
||||
// Construction from JSON
|
||||
|
||||
public static void fromJSON(JSONObject json) {
|
||||
// Version
|
||||
int version = 1; // but could be bumped later
|
||||
|
||||
// Timestamp
|
||||
String timestampStr = (String) Settings.getTypedJson(json, "timestamp", String.class);
|
||||
long timestamp;
|
||||
|
||||
if (timestampStr.equals("now"))
|
||||
timestamp = System.currentTimeMillis();
|
||||
else
|
||||
try {
|
||||
timestamp = Long.parseUnsignedLong(timestampStr);
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.error("Unable to parse genesis timestamp: " + timestampStr);
|
||||
throw new RuntimeException("Unable to parse genesis timestamp");
|
||||
}
|
||||
|
||||
// Transactions
|
||||
JSONArray transactionsJson = (JSONArray) Settings.getTypedJson(json, "transactions", JSONArray.class);
|
||||
List<TransactionData> transactions = new ArrayList<>();
|
||||
|
||||
for (Object transactionObj : transactionsJson) {
|
||||
if (!(transactionObj instanceof JSONObject)) {
|
||||
LOGGER.error("Genesis transaction malformed in blockchain config file");
|
||||
throw new RuntimeException("Genesis transaction malformed in blockchain config file");
|
||||
}
|
||||
|
||||
JSONObject transactionJson = (JSONObject) transactionObj;
|
||||
|
||||
String recipient = (String) Settings.getTypedJson(transactionJson, "recipient", String.class);
|
||||
BigDecimal amount = Settings.getJsonBigDecimal(transactionJson, "amount");
|
||||
|
||||
// assetId is optional
|
||||
if (transactionJson.containsKey("assetId")) {
|
||||
long assetId = (Long) Settings.getTypedJson(transactionJson, "assetId", Long.class);
|
||||
|
||||
// We're into version 4 genesis block territory now
|
||||
version = 4;
|
||||
|
||||
transactions.add(new GenesisTransactionData(recipient, amount, assetId, timestamp));
|
||||
} else {
|
||||
transactions.add(new GenesisTransactionData(recipient, amount, timestamp));
|
||||
}
|
||||
}
|
||||
|
||||
// Generating balance
|
||||
BigDecimal generatingBalance = Settings.getJsonBigDecimal(json, "generatingBalance");
|
||||
|
||||
byte[] reference = GENESIS_REFERENCE;
|
||||
int transactionCount = transactions.size();
|
||||
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
||||
byte[] generatorPublicKey = GENESIS_GENERATOR_PUBLIC_KEY;
|
||||
byte[] bytesForSignature = getBytesForSignature(version, reference, generatingBalance, generatorPublicKey);
|
||||
byte[] generatorSignature = calcSignature(bytesForSignature);
|
||||
byte[] transactionsSignature = generatorSignature;
|
||||
int height = 1;
|
||||
int atCount = 0;
|
||||
BigDecimal atFees = BigDecimal.ZERO.setScale(8);
|
||||
|
||||
blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
|
||||
generatorPublicKey, generatorSignature, atCount, atFees);
|
||||
transactionsData = transactions;
|
||||
}
|
||||
|
||||
// More information
|
||||
@ -181,12 +122,14 @@ public class GenesisBlock extends Block {
|
||||
if (blockData.getHeight() != 1)
|
||||
return false;
|
||||
|
||||
byte[] signature = calcSignature(blockData);
|
||||
|
||||
// Validate block signature
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, blockData.getGeneratorSignature()))
|
||||
if (!Arrays.equals(signature, blockData.getGeneratorSignature()))
|
||||
return false;
|
||||
|
||||
// Validate transactions signature
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, blockData.getTransactionsSignature()))
|
||||
if (!Arrays.equals(signature, blockData.getTransactionsSignature()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -200,12 +143,6 @@ public class GenesisBlock extends Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addGenesisTransaction(String recipient, String amount) {
|
||||
this.transactions.add(Transaction.fromData(this.repository,
|
||||
new GenesisTransactionData(recipient, new BigDecimal(amount).setScale(8), this.getBlockData().getTimestamp())));
|
||||
this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuse to calculate genesis block's generator signature!
|
||||
* <p>
|
||||
@ -243,12 +180,12 @@ public class GenesisBlock extends Block {
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
private static byte[] calcSignature() {
|
||||
byte[] digest = Crypto.digest(getBytesForSignature());
|
||||
private static byte[] calcSignature(byte[] bytes) {
|
||||
byte[] digest = Crypto.digest(bytes);
|
||||
return Bytes.concat(digest, digest);
|
||||
}
|
||||
|
||||
private static byte[] getBytesForSignature() {
|
||||
private static byte[] getBytesForSignature(int version, byte[] reference, BigDecimal generatingBalance, byte[] generatorPublicKey) {
|
||||
try {
|
||||
// Passing expected size to ByteArrayOutputStream avoids reallocation when adding more bytes than default 32.
|
||||
// See below for explanation of some of the values used to calculated expected size.
|
||||
@ -259,18 +196,18 @@ public class GenesisBlock extends Block {
|
||||
* Bytes.ensureCapacity(versionBytes, 0, 4) did not truncate versionBytes back to 4 bytes either. This means 8 bytes were used even though
|
||||
* VERSION_LENGTH is set to 4. Correcting this historic bug will break genesis block signatures!
|
||||
*/
|
||||
bytes.write(Longs.toByteArray(GENESIS_BLOCK_VERSION));
|
||||
bytes.write(Longs.toByteArray(version));
|
||||
|
||||
/*
|
||||
* NOTE: Historic code had the reference expanded to only 64 bytes whereas standard block references are 128 bytes. Correcting this historic bug
|
||||
* will break genesis block signatures!
|
||||
*/
|
||||
bytes.write(Bytes.ensureCapacity(GENESIS_REFERENCE, 64, 0));
|
||||
bytes.write(Bytes.ensureCapacity(reference, 64, 0));
|
||||
|
||||
bytes.write(Longs.toByteArray(GENESIS_GENERATING_BALANCE.longValue()));
|
||||
bytes.write(Longs.toByteArray(generatingBalance.longValue()));
|
||||
|
||||
// NOTE: Genesis account's public key is only 8 bytes, not the usual 32, so we have to pad.
|
||||
bytes.write(Bytes.ensureCapacity(GENESIS_GENERATOR_PUBLIC_KEY, 32, 0));
|
||||
bytes.write(Bytes.ensureCapacity(generatorPublicKey, 32, 0));
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException e) {
|
||||
@ -278,14 +215,23 @@ public class GenesisBlock extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
/** Convenience method for calculating genesis block signatures from block data */
|
||||
private static byte[] calcSignature(BlockData blockData) {
|
||||
byte[] bytes = getBytesForSignature(blockData.getVersion(), blockData.getReference(), blockData.getGeneratingBalance(),
|
||||
blockData.getGeneratorPublicKey());
|
||||
return calcSignature(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignatureValid() {
|
||||
byte[] signature = calcSignature(this.getBlockData());
|
||||
|
||||
// Validate block signature
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, this.getBlockData().getGeneratorSignature()))
|
||||
if (!Arrays.equals(signature, this.getBlockData().getGeneratorSignature()))
|
||||
return false;
|
||||
|
||||
// Validate transactions signature
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, this.getBlockData().getTransactionsSignature()))
|
||||
if (!Arrays.equals(signature, this.getBlockData().getTransactionsSignature()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -114,7 +114,7 @@ public class ArbitraryTransaction extends Transaction {
|
||||
if (arbitraryTransactionData.getVersion() != ArbitraryTransaction.getVersionByTimestamp(arbitraryTransactionData.getTimestamp()))
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
if (this.arbitraryTransactionData.getTimestamp() < BlockChain.getArbitraryReleaseTimestamp())
|
||||
if (this.arbitraryTransactionData.getTimestamp() < BlockChain.getInstance().getArbitraryReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check data length
|
||||
|
@ -120,7 +120,7 @@ public class CreateOrderTransaction extends Transaction {
|
||||
|
||||
// Check creator has enough funds for fee in QORA
|
||||
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
|
||||
if (createOrderTransactionData.getTimestamp() >= BlockChain.getPowFixReleaseTimestamp()
|
||||
if (createOrderTransactionData.getTimestamp() >= BlockChain.getInstance().getPowFixReleaseTimestamp()
|
||||
&& creator.getConfirmedBalance(Asset.QORA).compareTo(createOrderTransactionData.getFee()) < 0)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class CreatePollTransaction extends Transaction {
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Are CreatePollTransactions even allowed at this point?
|
||||
// In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used
|
||||
if (this.createPollTransactionData.getTimestamp() < BlockChain.getVotingReleaseTimestamp())
|
||||
if (this.createPollTransactionData.getTimestamp() < BlockChain.getInstance().getVotingReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check owner address is valid
|
||||
|
@ -131,7 +131,7 @@ public class DeployATTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getATReleaseHeight())
|
||||
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getInstance().getATReleaseHeight())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check name size bounds
|
||||
|
@ -83,7 +83,7 @@ public class IssueAssetTransaction extends Transaction {
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Are IssueAssetTransactions even allowed at this point?
|
||||
// In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used
|
||||
if (this.issueAssetTransactionData.getTimestamp() < BlockChain.getAssetsReleaseTimestamp())
|
||||
if (this.issueAssetTransactionData.getTimestamp() < BlockChain.getInstance().getAssetsReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check owner address is valid
|
||||
@ -120,7 +120,7 @@ public class IssueAssetTransaction extends Transaction {
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Check the asset name isn't already taken. This check is not present in gen1.
|
||||
if (issueAssetTransactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (issueAssetTransactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName()))
|
||||
return ValidationResult.ASSET_ALREADY_EXISTS;
|
||||
|
||||
|
@ -94,7 +94,7 @@ public class MessageTransaction extends Transaction {
|
||||
if (messageTransactionData.getVersion() != MessageTransaction.getVersionByTimestamp(messageTransactionData.getTimestamp()))
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getMessageReleaseHeight())
|
||||
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getInstance().getMessageReleaseHeight())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check data length
|
||||
|
@ -92,7 +92,7 @@ public class MultiPaymentTransaction extends Transaction {
|
||||
List<PaymentData> payments = multiPaymentTransactionData.getPayments();
|
||||
|
||||
// Are MultiPaymentTransactions even allowed at this point?
|
||||
if (this.multiPaymentTransactionData.getTimestamp() < BlockChain.getAssetsReleaseTimestamp())
|
||||
if (this.multiPaymentTransactionData.getTimestamp() < BlockChain.getInstance().getAssetsReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check number of payments
|
||||
@ -107,7 +107,7 @@ public class MultiPaymentTransaction extends Transaction {
|
||||
|
||||
// Check sender has enough funds for fee
|
||||
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
|
||||
if (multiPaymentTransactionData.getTimestamp() >= BlockChain.getPowFixReleaseTimestamp()
|
||||
if (multiPaymentTransactionData.getTimestamp() >= BlockChain.getInstance().getPowFixReleaseTimestamp()
|
||||
&& sender.getConfirmedBalance(Asset.QORA).compareTo(multiPaymentTransactionData.getFee()) < 0)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
|
@ -98,7 +98,7 @@ public class SellNameTransaction extends Transaction {
|
||||
return ValidationResult.NEGATIVE_AMOUNT;
|
||||
|
||||
// Check amount within bounds
|
||||
if (sellNameTransactionData.getAmount().compareTo(BlockChain.MAX_BALANCE) > 0)
|
||||
if (sellNameTransactionData.getAmount().compareTo(BlockChain.getInstance().getMaxBalance()) > 0)
|
||||
return ValidationResult.INVALID_AMOUNT;
|
||||
|
||||
// Check fee is positive
|
||||
|
@ -18,7 +18,6 @@ import qora.account.PublicKeyAccount;
|
||||
import qora.block.BlockChain;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import settings.Settings;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.TransactionTransformer;
|
||||
|
||||
@ -119,13 +118,6 @@ public abstract class Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
/** Minimum fee for a transaction */
|
||||
public static final BigDecimal MINIMUM_FEE = BigDecimal.ONE;
|
||||
|
||||
// Cached info to make transaction processing faster
|
||||
protected static final BigDecimal maxBytePerFee = BigDecimal.valueOf(Settings.getInstance().getMaxBytePerFee());
|
||||
protected static final BigDecimal minFeePerByte = BigDecimal.ONE.divide(maxBytePerFee, MathContext.DECIMAL32);
|
||||
|
||||
// Properties
|
||||
protected Repository repository;
|
||||
protected TransactionData transactionData;
|
||||
@ -227,7 +219,7 @@ public abstract class Transaction {
|
||||
}
|
||||
|
||||
public boolean hasMinimumFee() {
|
||||
return this.transactionData.getFee().compareTo(MINIMUM_FEE) >= 0;
|
||||
return this.transactionData.getFee().compareTo(BlockChain.getInstance().getUnitFee()) >= 0;
|
||||
}
|
||||
|
||||
public BigDecimal feePerByte() {
|
||||
@ -239,27 +231,31 @@ public abstract class Transaction {
|
||||
}
|
||||
|
||||
public boolean hasMinimumFeePerByte() {
|
||||
return this.feePerByte().compareTo(minFeePerByte) >= 0;
|
||||
return this.feePerByte().compareTo(BlockChain.getInstance().getMinFeePerByte()) >= 0;
|
||||
}
|
||||
|
||||
public BigDecimal calcRecommendedFee() {
|
||||
int dataLength;
|
||||
try {
|
||||
BigDecimal recommendedFee = BigDecimal.valueOf(TransactionTransformer.getDataLength(this.transactionData))
|
||||
.divide(maxBytePerFee, MathContext.DECIMAL32).setScale(8);
|
||||
|
||||
// security margin
|
||||
recommendedFee = recommendedFee.add(new BigDecimal("0.0000001"));
|
||||
|
||||
if (recommendedFee.compareTo(MINIMUM_FEE) <= 0) {
|
||||
recommendedFee = MINIMUM_FEE;
|
||||
} else {
|
||||
recommendedFee = recommendedFee.setScale(0, BigDecimal.ROUND_UP);
|
||||
}
|
||||
|
||||
return recommendedFee.setScale(8);
|
||||
dataLength = TransactionTransformer.getDataLength(this.transactionData);
|
||||
} catch (TransformationException e) {
|
||||
throw new IllegalStateException("Unable to get transaction byte length?");
|
||||
}
|
||||
|
||||
BigDecimal maxBytePerUnitFee = BlockChain.getInstance().getMaxBytesPerUnitFee();
|
||||
|
||||
BigDecimal recommendedFee = BigDecimal.valueOf(dataLength).divide(maxBytePerUnitFee, MathContext.DECIMAL32).setScale(8);
|
||||
|
||||
// security margin
|
||||
recommendedFee = recommendedFee.add(new BigDecimal("0.00000001"));
|
||||
|
||||
if (recommendedFee.compareTo(BlockChain.getInstance().getUnitFee()) <= 0) {
|
||||
recommendedFee = BlockChain.getInstance().getUnitFee();
|
||||
} else {
|
||||
recommendedFee = recommendedFee.setScale(0, BigDecimal.ROUND_UP);
|
||||
}
|
||||
|
||||
return recommendedFee.setScale(8);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -269,9 +265,9 @@ public abstract class Transaction {
|
||||
* @return transaction version number, likely 1 or 3
|
||||
*/
|
||||
public static int getVersionByTimestamp(long timestamp) {
|
||||
if (timestamp < BlockChain.getPowFixReleaseTimestamp()) {
|
||||
if (timestamp < BlockChain.getInstance().getPowFixReleaseTimestamp()) {
|
||||
return 1;
|
||||
} else if (timestamp < BlockChain.getQoraV2Timestamp()) {
|
||||
} else if (timestamp < BlockChain.getInstance().getQoraV2Timestamp()) {
|
||||
return 3;
|
||||
} else {
|
||||
return 4;
|
||||
|
@ -86,7 +86,7 @@ public class TransferAssetTransaction extends Transaction {
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Are TransferAssetTransactions even allowed at this point?
|
||||
// In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used
|
||||
if (this.transferAssetTransactionData.getTimestamp() < BlockChain.getAssetsReleaseTimestamp())
|
||||
if (this.transferAssetTransactionData.getTimestamp() < BlockChain.getInstance().getAssetsReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check reference is correct
|
||||
|
@ -73,7 +73,7 @@ public class VoteOnPollTransaction extends Transaction {
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Are VoteOnPollTransactions even allowed at this point?
|
||||
// In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used
|
||||
if (this.voteOnPollTransactionData.getTimestamp() < BlockChain.getVotingReleaseTimestamp())
|
||||
if (this.voteOnPollTransactionData.getTimestamp() < BlockChain.getInstance().getVotingReleaseTimestamp())
|
||||
return ValidationResult.NOT_YET_RELEASED;
|
||||
|
||||
// Check name size bounds
|
||||
|
@ -163,7 +163,7 @@ public class HSQLDBDatabaseUpdates {
|
||||
case 3:
|
||||
// Genesis Transactions
|
||||
stmt.execute("CREATE TABLE GenesisTransactions (signature Signature, recipient QoraAddress NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
|
@ -17,14 +17,16 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
|
||||
}
|
||||
|
||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature)) {
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM GenesisTransactions WHERE signature = ?",
|
||||
signature)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
String recipient = resultSet.getString(1);
|
||||
BigDecimal amount = resultSet.getBigDecimal(2).setScale(8);
|
||||
long assetId = resultSet.getLong(3);
|
||||
|
||||
return new GenesisTransactionData(recipient, amount, timestamp, signature);
|
||||
return new GenesisTransactionData(recipient, amount, assetId, timestamp, signature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch genesis transaction from repository", e);
|
||||
}
|
||||
@ -35,8 +37,8 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
|
||||
GenesisTransactionData genesisTransactionData = (GenesisTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("GenesisTransactions");
|
||||
saveHelper.bind("signature", genesisTransactionData.getSignature()).bind("recipient", genesisTransactionData.getRecipient()).bind("amount",
|
||||
genesisTransactionData.getAmount());
|
||||
saveHelper.bind("signature", genesisTransactionData.getSignature()).bind("recipient", genesisTransactionData.getRecipient())
|
||||
.bind("amount", genesisTransactionData.getAmount()).bind("asset_id", genesisTransactionData.getAssetId());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
|
@ -2,10 +2,14 @@ package settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
@ -13,29 +17,31 @@ import org.json.simple.JSONValue;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import qora.block.GenesisBlock;
|
||||
import qora.block.BlockChain;
|
||||
|
||||
public class Settings {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(Settings.class);
|
||||
|
||||
// Properties
|
||||
private static Settings instance;
|
||||
private long genesisTimestamp = GenesisBlock.GENESIS_TIMESTAMP;
|
||||
private int maxBytePerFee = 1024;
|
||||
private String userpath = "";
|
||||
private boolean useBitcoinTestNet = false;
|
||||
|
||||
// RPC
|
||||
private int rpcPort = 9085;
|
||||
private List<String> rpcAllowed = new ArrayList<String>(Arrays.asList("127.0.0.1", "::1")); // ipv4, ipv6
|
||||
private boolean rpcEnabled = true;
|
||||
|
||||
|
||||
// Globalization
|
||||
private String translationsPath = "globalization/";
|
||||
private String[] translationsDefaultLocales = {"en"};
|
||||
|
||||
private String[] translationsDefaultLocales = {
|
||||
"en"
|
||||
};
|
||||
|
||||
// Constants
|
||||
private static final String SETTINGS_FILENAME = "settings.json";
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
private Settings() {
|
||||
@ -51,6 +57,7 @@ public class Settings {
|
||||
|
||||
if (!file.exists()) {
|
||||
// log lack of settings file
|
||||
LOGGER.info("Settings file not found: " + path + filename);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -79,19 +86,20 @@ public class Settings {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.userpath = path;
|
||||
process(settingsJSON);
|
||||
|
||||
this.userpath = path;
|
||||
break;
|
||||
} while (true);
|
||||
} catch (IOException | ClassCastException e) {
|
||||
|
||||
LOGGER.error("Unable to parse settings file: " + path + filename);
|
||||
throw new RuntimeException("Unable to parse settings file", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Other methods
|
||||
|
||||
public static Settings getInstance() {
|
||||
public static synchronized Settings getInstance() {
|
||||
if (instance == null)
|
||||
instance = new Settings(SETTINGS_FILENAME);
|
||||
|
||||
@ -108,83 +116,108 @@ public class Settings {
|
||||
}
|
||||
|
||||
private void process(JSONObject json) {
|
||||
if (json.containsKey("testnetstamp")) {
|
||||
if (json.get("testnetstamp").toString().equals("now") || ((Long) json.get("testnetstamp")).longValue() == 0) {
|
||||
this.genesisTimestamp = System.currentTimeMillis();
|
||||
} else {
|
||||
this.genesisTimestamp = ((Long) json.get("testnetstamp")).longValue();
|
||||
// RPC
|
||||
if (json.containsKey("rpcport"))
|
||||
this.rpcPort = ((Long) json.get("rpcport")).intValue();
|
||||
|
||||
if (json.containsKey("rpcallowed")) {
|
||||
JSONArray allowedArray = (JSONArray) json.get("rpcallowed");
|
||||
this.rpcAllowed = new ArrayList<String>(allowedArray);
|
||||
}
|
||||
|
||||
if (json.containsKey("rpcenabled"))
|
||||
this.rpcEnabled = ((Boolean) json.get("rpcenabled")).booleanValue();
|
||||
|
||||
// Globalization
|
||||
if (json.containsKey("translationspath"))
|
||||
this.translationsPath = ((String) json.get("translationspath"));
|
||||
|
||||
if (json.containsKey("translationsdefaultlocales"))
|
||||
this.translationsDefaultLocales = ((String[]) json.get("translationsdefaultlocales"));
|
||||
|
||||
if (json.containsKey("blockchainConfig")) {
|
||||
String filename = (String) json.get("blockchainConfig");
|
||||
File file = new File(this.userpath + filename);
|
||||
|
||||
if (!file.exists()) {
|
||||
LOGGER.info("Blockchain config file not found: " + this.userpath + filename);
|
||||
throw new RuntimeException("Unable to read blockchain config file");
|
||||
}
|
||||
|
||||
try {
|
||||
List<String> lines = Files.readLines(file, Charsets.UTF_8);
|
||||
JSONObject blockchainJSON = (JSONObject) JSONValue.parse(String.join("\n", lines));
|
||||
BlockChain.fromJSON(blockchainJSON);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to parse blockchain config file: " + this.userpath + filename);
|
||||
throw new RuntimeException("Unable to parse blockchain config file", e);
|
||||
}
|
||||
}
|
||||
|
||||
// RPC
|
||||
if(json.containsKey("rpcport"))
|
||||
{
|
||||
this.rpcPort = ((Long) json.get("rpcport")).intValue();
|
||||
}
|
||||
|
||||
if(json.containsKey("rpcallowed"))
|
||||
{
|
||||
JSONArray allowedArray = (JSONArray) json.get("rpcallowed");
|
||||
this.rpcAllowed = new ArrayList<String>(allowedArray);
|
||||
}
|
||||
|
||||
if(json.containsKey("rpcenabled"))
|
||||
{
|
||||
this.rpcEnabled = ((Boolean) json.get("rpcenabled")).booleanValue();
|
||||
}
|
||||
|
||||
// Globalization
|
||||
if(json.containsKey("translationspath"))
|
||||
{
|
||||
this.translationsPath = ((String) json.get("translationspath"));
|
||||
}
|
||||
|
||||
if(json.containsKey("translationsdefaultlocales"))
|
||||
{
|
||||
this.translationsDefaultLocales = ((String[]) json.get("translationsdefaultlocales"));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTestNet() {
|
||||
return this.genesisTimestamp != GenesisBlock.GENESIS_TIMESTAMP;
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public int getMaxBytePerFee() {
|
||||
return this.maxBytePerFee;
|
||||
}
|
||||
|
||||
public long getGenesisTimestamp() {
|
||||
return this.genesisTimestamp;
|
||||
}
|
||||
|
||||
public String getUserpath() {
|
||||
return this.userpath;
|
||||
}
|
||||
|
||||
public int getRpcPort()
|
||||
{
|
||||
public int getRpcPort() {
|
||||
return this.rpcPort;
|
||||
}
|
||||
|
||||
public List<String> getRpcAllowed()
|
||||
{
|
||||
|
||||
public List<String> getRpcAllowed() {
|
||||
return this.rpcAllowed;
|
||||
}
|
||||
|
||||
public boolean isRpcEnabled()
|
||||
{
|
||||
public boolean isRpcEnabled() {
|
||||
return this.rpcEnabled;
|
||||
}
|
||||
|
||||
public String translationsPath()
|
||||
{
|
||||
|
||||
public String translationsPath() {
|
||||
return this.translationsPath;
|
||||
}
|
||||
|
||||
public String[] translationsDefaultLocales()
|
||||
{
|
||||
|
||||
public String[] translationsDefaultLocales() {
|
||||
return this.translationsDefaultLocales;
|
||||
}
|
||||
|
||||
public boolean useBitcoinTestNet() {
|
||||
return this.useBitcoinTestNet;
|
||||
}
|
||||
|
||||
// Config parsing
|
||||
|
||||
public static Object getTypedJson(JSONObject json, String key, Class<?> clazz) {
|
||||
if (!json.containsKey(key)) {
|
||||
LOGGER.error("Missing \"" + key + "\" in blockchain config file");
|
||||
throw new RuntimeException("Missing \"" + key + "\" in blockchain config file");
|
||||
}
|
||||
|
||||
Object value = json.get(key);
|
||||
if (!clazz.isInstance(value)) {
|
||||
LOGGER.error("\"" + key + "\" not " + clazz.getSimpleName() + " in blockchain config file");
|
||||
throw new RuntimeException("\"" + key + "\" not " + clazz.getSimpleName() + " in blockchain config file");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static BigDecimal getJsonBigDecimal(JSONObject json, String key) {
|
||||
try {
|
||||
return new BigDecimal((String) getTypedJson(json, key, String.class));
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.error("Unable to parse \"" + key + "\" in blockchain config file");
|
||||
throw new RuntimeException("Unable to parse \"" + key + "\" in blockchain config file");
|
||||
}
|
||||
}
|
||||
|
||||
public static Long getJsonQuotedLong(JSONObject json, String key) {
|
||||
try {
|
||||
return Long.parseLong((String) getTypedJson(json, key, String.class));
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.error("Unable to parse \"" + key + "\" in blockchain config file");
|
||||
throw new RuntimeException("Unable to parse \"" + key + "\" in blockchain config file");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
|
||||
ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData;
|
||||
byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
if (arbitraryTransactionData.getVersion() == 1 || transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (arbitraryTransactionData.getVersion() == 1 || transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return bytes;
|
||||
|
||||
// Special v1 version
|
||||
|
@ -90,7 +90,7 @@ public class CreateOrderTransactionTransformer extends TransactionTransformer {
|
||||
* @throws TransformationException
|
||||
*/
|
||||
public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
|
||||
if (transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
// Special v1 version
|
||||
|
@ -62,7 +62,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
||||
pollOptions.add(new PollOptionData(optionName));
|
||||
|
||||
// V1 only: voter count also present
|
||||
if (timestamp < BlockChain.getQoraV2Timestamp()) {
|
||||
if (timestamp < BlockChain.getInstance().getQoraV2Timestamp()) {
|
||||
int voterCount = byteBuffer.getInt();
|
||||
if (voterCount != 0)
|
||||
throw new TransformationException("Unexpected voter count in byte data for CreatePollTransaction");
|
||||
@ -88,7 +88,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
||||
// option-string-length, option-string
|
||||
dataLength += INT_LENGTH + Utf8.encodedLength(pollOptionData.getOptionName());
|
||||
|
||||
if (transactionData.getTimestamp() < BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp())
|
||||
// v1 only: voter-count (should always be zero)
|
||||
dataLength += INT_LENGTH;
|
||||
}
|
||||
@ -120,7 +120,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
||||
for (PollOptionData pollOptionData : pollOptions) {
|
||||
Serialization.serializeSizedString(bytes, pollOptionData.getOptionName());
|
||||
|
||||
if (transactionData.getTimestamp() < BlockChain.getQoraV2Timestamp()) {
|
||||
if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp()) {
|
||||
// In v1, CreatePollTransaction uses Poll.toBytes which serializes voters too.
|
||||
// Zero voters as this is a new poll.
|
||||
bytes.write(Ints.toByteArray(0));
|
||||
@ -149,7 +149,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
||||
public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
|
||||
byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
if (transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return bytes;
|
||||
|
||||
// Special v1 version
|
||||
|
@ -146,7 +146,7 @@ public class DeployATTransactionTransformer extends TransactionTransformer {
|
||||
* @throws TransformationException
|
||||
*/
|
||||
public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
|
||||
if (transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
// Special v1 version
|
||||
|
@ -11,6 +11,8 @@ import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import data.transaction.TransactionData;
|
||||
import qora.assets.Asset;
|
||||
import qora.block.BlockChain;
|
||||
import data.transaction.GenesisTransactionData;
|
||||
import transform.TransformationException;
|
||||
import utils.Serialization;
|
||||
@ -20,9 +22,11 @@ public class GenesisTransactionTransformer extends TransactionTransformer {
|
||||
// Property lengths
|
||||
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
||||
private static final int AMOUNT_LENGTH = LONG_LENGTH;
|
||||
private static final int ASSET_ID_LENGTH = LONG_LENGTH;
|
||||
|
||||
// Note that Genesis transactions don't require reference, fee or signature:
|
||||
private static final int TYPELESS_LENGTH = TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH;
|
||||
private static final int TYPELESS_LENGTH_V1 = TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH;
|
||||
private static final int TYPELESS_LENGTH_V4 = TIMESTAMP_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH + ASSET_ID_LENGTH;
|
||||
|
||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||
long timestamp = byteBuffer.getLong();
|
||||
@ -31,11 +35,18 @@ public class GenesisTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
|
||||
return new GenesisTransactionData(recipient, amount, timestamp);
|
||||
long assetId = Asset.QORA;
|
||||
if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
assetId = byteBuffer.getLong();
|
||||
|
||||
return new GenesisTransactionData(recipient, amount, assetId, timestamp);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH;
|
||||
if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH_V1;
|
||||
else
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH_V4;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
@ -50,6 +61,9 @@ public class GenesisTransactionTransformer extends TransactionTransformer {
|
||||
Serialization.serializeAddress(bytes, genesisTransactionData.getRecipient());
|
||||
Serialization.serializeBigDecimal(bytes, genesisTransactionData.getAmount());
|
||||
|
||||
if (genesisTransactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
bytes.write(Longs.toByteArray(genesisTransactionData.getAssetId()));
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException | ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
@ -65,6 +79,7 @@ public class GenesisTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
json.put("recipient", genesisTransactionData.getRecipient());
|
||||
json.put("amount", genesisTransactionData.getAmount().toPlainString());
|
||||
json.put("assetId", genesisTransactionData.getAssetId());
|
||||
} catch (ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
byte[] assetReference = new byte[ASSET_REFERENCE_LENGTH];
|
||||
// In v1, IssueAssetTransaction uses Asset.parse which also deserializes reference.
|
||||
if (timestamp < BlockChain.getQoraV2Timestamp())
|
||||
if (timestamp < BlockChain.getInstance().getQoraV2Timestamp())
|
||||
byteBuffer.get(assetReference);
|
||||
|
||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
@ -73,7 +73,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
||||
+ Utf8.encodedLength(issueAssetTransactionData.getDescription());
|
||||
|
||||
// In v1, IssueAssetTransaction uses Asset.toBytes which also serializes reference.
|
||||
if (transactionData.getTimestamp() < BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp())
|
||||
dataLength += ASSET_REFERENCE_LENGTH;
|
||||
|
||||
return dataLength;
|
||||
@ -100,7 +100,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
||||
bytes.write((byte) (issueAssetTransactionData.getIsDivisible() ? 1 : 0));
|
||||
|
||||
// In v1, IssueAssetTransaction uses Asset.toBytes which also serializes Asset's reference which is the IssueAssetTransaction's signature
|
||||
if (transactionData.getTimestamp() < BlockChain.getQoraV2Timestamp()) {
|
||||
if (transactionData.getTimestamp() < BlockChain.getInstance().getQoraV2Timestamp()) {
|
||||
byte[] assetReference = issueAssetTransactionData.getSignature();
|
||||
if (assetReference != null)
|
||||
bytes.write(assetReference);
|
||||
@ -129,7 +129,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
||||
public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
|
||||
byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
if (transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return bytes;
|
||||
|
||||
// Special v1 version
|
||||
|
@ -99,7 +99,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer {
|
||||
public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
|
||||
byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
if (transactionData.getTimestamp() >= BlockChain.getQoraV2Timestamp())
|
||||
if (transactionData.getTimestamp() >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||
return bytes;
|
||||
|
||||
// Special v1 version
|
||||
|
@ -22,7 +22,7 @@ public class BlockTests extends Common {
|
||||
@Test
|
||||
public void testGenesisBlockTransactions() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
GenesisBlock block = new GenesisBlock(repository);
|
||||
GenesisBlock block = GenesisBlock.getInstance(repository);
|
||||
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
|
@ -41,7 +41,7 @@ public class GenesisTests {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
assertEquals(0, repository.getBlockRepository().getBlockchainHeight(), "Blockchain should be empty for this test");
|
||||
|
||||
GenesisBlock block = new GenesisBlock(repository);
|
||||
GenesisBlock block = GenesisBlock.getInstance(repository);
|
||||
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
|
@ -25,7 +25,7 @@ public class SerializationTests extends Common {
|
||||
@Test
|
||||
public void testGenesisSerialization() throws TransformationException, DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
GenesisBlock block = new GenesisBlock(repository);
|
||||
GenesisBlock block = GenesisBlock.getInstance(repository);
|
||||
|
||||
GenesisTransaction transaction = (GenesisTransaction) block.getTransactions().get(1);
|
||||
assertNotNull(transaction);
|
||||
|
@ -22,7 +22,7 @@ public class SignatureTests extends Common {
|
||||
String expected58 = "6pHMBFif7jXFG654joT8GPaymau1fMtaxacRyqSrnAwQMQDvqRuLpHpfFyqX4gWVvj4pF1mwQhFgqWAvjVvPJUjmBZQvL751dM9cEcQBTaUcxtNLuWZCVUAtbnWN9f7FsLppHhkPbxwpoodL3UJYRGt3EZrG17mhv1RJbmq8j6rr7Mk";
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
GenesisBlock block = new GenesisBlock(repository);
|
||||
GenesisBlock block = GenesisBlock.getInstance(repository);
|
||||
BlockData blockData = block.getBlockData();
|
||||
|
||||
System.out
|
||||
|
@ -481,7 +481,7 @@ public class TransactionTests {
|
||||
@Test
|
||||
public void testCreatePollTransaction() throws DataException {
|
||||
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
|
||||
createTestAccounts(BlockChain.getVotingReleaseTimestamp() + 1_000L);
|
||||
createTestAccounts(BlockChain.getInstance().getVotingReleaseTimestamp() + 1_000L);
|
||||
|
||||
// Make a new create poll transaction
|
||||
String pollName = "test poll";
|
||||
|
Loading…
Reference in New Issue
Block a user