From f50b53a4072b6003631e3a96b09fd8145d1d9e0e Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 17 Dec 2018 13:28:37 +0000 Subject: [PATCH] Blockchain config (ncluding genesis block) moved out into config file --- blockchain.json | 161 +++++++++++ src/controller/Controller.java | 12 + src/crosschain/BTC.java | 2 +- .../transaction/GenesisTransactionData.java | 25 +- src/qora/account/Account.java | 2 +- src/qora/block/Block.java | 41 ++- src/qora/block/BlockChain.java | 255 ++++++++++------ src/qora/block/GenesisBlock.java | 272 +++++++----------- .../transaction/ArbitraryTransaction.java | 2 +- .../transaction/CreateOrderTransaction.java | 2 +- .../transaction/CreatePollTransaction.java | 2 +- src/qora/transaction/DeployATTransaction.java | 2 +- .../transaction/IssueAssetTransaction.java | 4 +- src/qora/transaction/MessageTransaction.java | 2 +- .../transaction/MultiPaymentTransaction.java | 4 +- src/qora/transaction/SellNameTransaction.java | 2 +- src/qora/transaction/Transaction.java | 46 ++- .../transaction/TransferAssetTransaction.java | 2 +- .../transaction/VoteOnPollTransaction.java | 2 +- .../hsqldb/HSQLDBDatabaseUpdates.java | 2 +- .../HSQLDBGenesisTransactionRepository.java | 10 +- src/settings/Settings.java | 169 ++++++----- .../ArbitraryTransactionTransformer.java | 2 +- .../CreateOrderTransactionTransformer.java | 2 +- .../CreatePollTransactionTransformer.java | 8 +- .../DeployATTransactionTransformer.java | 2 +- .../GenesisTransactionTransformer.java | 21 +- .../IssueAssetTransactionTransformer.java | 8 +- .../MultiPaymentTransactionTransformer.java | 2 +- tests/test/BlockTests.java | 2 +- tests/test/GenesisTests.java | 2 +- tests/test/SerializationTests.java | 2 +- tests/test/SignatureTests.java | 2 +- tests/test/TransactionTests.java | 2 +- 34 files changed, 686 insertions(+), 390 deletions(-) create mode 100644 blockchain.json diff --git a/blockchain.json b/blockchain.json new file mode 100644 index 00000000..b04c3b5e --- /dev/null +++ b/blockchain.json @@ -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" } + } +} diff --git a/src/controller/Controller.java b/src/controller/Controller.java index 7fe15d21..cdcbc45a 100644 --- a/src/controller/Controller.java +++ b/src/controller/Controller.java @@ -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(); diff --git a/src/crosschain/BTC.java b/src/crosschain/BTC.java index f3cf0286..ce7a9e17 100644 --- a/src/crosschain/BTC.java +++ b/src/crosschain/BTC.java @@ -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"; diff --git a/src/data/transaction/GenesisTransactionData.java b/src/data/transaction/GenesisTransactionData.java index 36b53e1a..27163837 100644 --- a/src/data/transaction/GenesisTransactionData.java +++ b/src/data/transaction/GenesisTransactionData.java @@ -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; + } + } diff --git a/src/qora/account/Account.java b/src/qora/account/Account.java index 6bbd3347..32bf44f6 100644 --- a/src/qora/account/Account.java +++ b/src/qora/account/Account.java @@ -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 diff --git a/src/qora/block/Block.java b/src/qora/block/Block.java index 8cdb4ede..2c14e22d 100644 --- a/src/qora/block/Block.java +++ b/src/qora/block/Block.java @@ -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; + } + } diff --git a/src/qora/block/BlockChain.java b/src/qora/block/BlockChain.java index 0029ad92..d8fe490e 100644 --- a/src/qora/block/BlockChain.java +++ b/src/qora/block/BlockChain.java @@ -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> 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 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> 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; - } - } diff --git a/src/qora/block/GenesisBlock.java b/src/qora/block/GenesisBlock.java index 0d309dd4..fee72215 100644 --- a/src/qora/block/GenesisBlock.java +++ b/src/qora/block/GenesisBlock.java @@ -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 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 transactions) throws DataException { + super(repository, blockData, transactions, Collections.emptyList()); + } - this.transactions = new ArrayList(); + 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 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! *

@@ -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; diff --git a/src/qora/transaction/ArbitraryTransaction.java b/src/qora/transaction/ArbitraryTransaction.java index 62e8a633..ba9b06fc 100644 --- a/src/qora/transaction/ArbitraryTransaction.java +++ b/src/qora/transaction/ArbitraryTransaction.java @@ -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 diff --git a/src/qora/transaction/CreateOrderTransaction.java b/src/qora/transaction/CreateOrderTransaction.java index 4a02d9d8..8803e06f 100644 --- a/src/qora/transaction/CreateOrderTransaction.java +++ b/src/qora/transaction/CreateOrderTransaction.java @@ -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; } diff --git a/src/qora/transaction/CreatePollTransaction.java b/src/qora/transaction/CreatePollTransaction.java index 48ae3de1..6cf424f4 100644 --- a/src/qora/transaction/CreatePollTransaction.java +++ b/src/qora/transaction/CreatePollTransaction.java @@ -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 diff --git a/src/qora/transaction/DeployATTransaction.java b/src/qora/transaction/DeployATTransaction.java index 0446d90c..d9f11cc8 100644 --- a/src/qora/transaction/DeployATTransaction.java +++ b/src/qora/transaction/DeployATTransaction.java @@ -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 diff --git a/src/qora/transaction/IssueAssetTransaction.java b/src/qora/transaction/IssueAssetTransaction.java index c55ae86b..b18cdfc9 100644 --- a/src/qora/transaction/IssueAssetTransaction.java +++ b/src/qora/transaction/IssueAssetTransaction.java @@ -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; diff --git a/src/qora/transaction/MessageTransaction.java b/src/qora/transaction/MessageTransaction.java index abc93d0a..d23f2cbf 100644 --- a/src/qora/transaction/MessageTransaction.java +++ b/src/qora/transaction/MessageTransaction.java @@ -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 diff --git a/src/qora/transaction/MultiPaymentTransaction.java b/src/qora/transaction/MultiPaymentTransaction.java index 98053190..5370e7e1 100644 --- a/src/qora/transaction/MultiPaymentTransaction.java +++ b/src/qora/transaction/MultiPaymentTransaction.java @@ -92,7 +92,7 @@ public class MultiPaymentTransaction extends Transaction { List 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; diff --git a/src/qora/transaction/SellNameTransaction.java b/src/qora/transaction/SellNameTransaction.java index 16d35504..6322dde7 100644 --- a/src/qora/transaction/SellNameTransaction.java +++ b/src/qora/transaction/SellNameTransaction.java @@ -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 diff --git a/src/qora/transaction/Transaction.java b/src/qora/transaction/Transaction.java index 808739b9..c586d2b6 100644 --- a/src/qora/transaction/Transaction.java +++ b/src/qora/transaction/Transaction.java @@ -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; diff --git a/src/qora/transaction/TransferAssetTransaction.java b/src/qora/transaction/TransferAssetTransaction.java index 4bf0703f..47948151 100644 --- a/src/qora/transaction/TransferAssetTransaction.java +++ b/src/qora/transaction/TransferAssetTransaction.java @@ -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 diff --git a/src/qora/transaction/VoteOnPollTransaction.java b/src/qora/transaction/VoteOnPollTransaction.java index 6d72ed35..0dca6a42 100644 --- a/src/qora/transaction/VoteOnPollTransaction.java +++ b/src/qora/transaction/VoteOnPollTransaction.java @@ -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 diff --git a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java index adb879b5..4602b7fe 100644 --- a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -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; diff --git a/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java index f285f02a..f56ff5e3 100644 --- a/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java +++ b/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java @@ -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); diff --git a/src/settings/Settings.java b/src/settings/Settings.java index 4c94358f..63cb4f14 100644 --- a/src/settings/Settings.java +++ b/src/settings/Settings.java @@ -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 rpcAllowed = new ArrayList(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(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 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(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 getRpcAllowed() - { + + public List 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"); + } + } + } diff --git a/src/transform/transaction/ArbitraryTransactionTransformer.java b/src/transform/transaction/ArbitraryTransactionTransformer.java index d77888be..3cece624 100644 --- a/src/transform/transaction/ArbitraryTransactionTransformer.java +++ b/src/transform/transaction/ArbitraryTransactionTransformer.java @@ -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 diff --git a/src/transform/transaction/CreateOrderTransactionTransformer.java b/src/transform/transaction/CreateOrderTransactionTransformer.java index ef235e31..a336b19c 100644 --- a/src/transform/transaction/CreateOrderTransactionTransformer.java +++ b/src/transform/transaction/CreateOrderTransactionTransformer.java @@ -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 diff --git a/src/transform/transaction/CreatePollTransactionTransformer.java b/src/transform/transaction/CreatePollTransactionTransformer.java index 0c637df8..4087e91b 100644 --- a/src/transform/transaction/CreatePollTransactionTransformer.java +++ b/src/transform/transaction/CreatePollTransactionTransformer.java @@ -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 diff --git a/src/transform/transaction/DeployATTransactionTransformer.java b/src/transform/transaction/DeployATTransactionTransformer.java index ca0077d4..f5ab60ed 100644 --- a/src/transform/transaction/DeployATTransactionTransformer.java +++ b/src/transform/transaction/DeployATTransactionTransformer.java @@ -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 diff --git a/src/transform/transaction/GenesisTransactionTransformer.java b/src/transform/transaction/GenesisTransactionTransformer.java index 208c39ea..f0ab8e99 100644 --- a/src/transform/transaction/GenesisTransactionTransformer.java +++ b/src/transform/transaction/GenesisTransactionTransformer.java @@ -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); } diff --git a/src/transform/transaction/IssueAssetTransactionTransformer.java b/src/transform/transaction/IssueAssetTransactionTransformer.java index 65b23e90..58fd378c 100644 --- a/src/transform/transaction/IssueAssetTransactionTransformer.java +++ b/src/transform/transaction/IssueAssetTransactionTransformer.java @@ -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 diff --git a/src/transform/transaction/MultiPaymentTransactionTransformer.java b/src/transform/transaction/MultiPaymentTransactionTransformer.java index b1a54c49..6a8d1335 100644 --- a/src/transform/transaction/MultiPaymentTransactionTransformer.java +++ b/src/transform/transaction/MultiPaymentTransactionTransformer.java @@ -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 diff --git a/tests/test/BlockTests.java b/tests/test/BlockTests.java index f70370f8..5928b709 100644 --- a/tests/test/BlockTests.java +++ b/tests/test/BlockTests.java @@ -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()); diff --git a/tests/test/GenesisTests.java b/tests/test/GenesisTests.java index 84b56f51..15f9e016 100644 --- a/tests/test/GenesisTests.java +++ b/tests/test/GenesisTests.java @@ -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()); diff --git a/tests/test/SerializationTests.java b/tests/test/SerializationTests.java index 61a1b0e8..f546fc45 100644 --- a/tests/test/SerializationTests.java +++ b/tests/test/SerializationTests.java @@ -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); diff --git a/tests/test/SignatureTests.java b/tests/test/SignatureTests.java index 83a05fc6..304503db 100644 --- a/tests/test/SignatureTests.java +++ b/tests/test/SignatureTests.java @@ -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 diff --git a/tests/test/TransactionTests.java b/tests/test/TransactionTests.java index d230f16e..f57def65 100644 --- a/tests/test/TransactionTests.java +++ b/tests/test/TransactionTests.java @@ -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";