/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.http.twophased; import nxt.BlockchainTest; import nxt.Constants; import nxt.Nxt; import nxt.http.APICall; import nxt.http.twophased.TestCreateTwoPhased.TwoPhasedMoneyTransferBuilder; import nxt.util.Logger; import org.json.simple.JSONObject; import org.junit.Assert; import org.junit.Test; public class TestApproveTransaction extends BlockchainTest { @Test public void validVoteCasting() { int duration = 10; APICall apiCall = new TwoPhasedMoneyTransferBuilder() .finishHeight(Nxt.getBlockchain().getHeight() + duration) .build(); JSONObject transactionJSON = TestCreateTwoPhased.issueCreateTwoPhased(apiCall, false); generateBlock(); apiCall = new APICall.Builder("approveTransaction") .param("secretPhrase", CHUCK.getSecretPhrase()) .param("transactionFullHash", (String) transactionJSON.get("fullHash")) .param("feeNQT", Constants.ONE_NXT) .build(); JSONObject response = apiCall.invoke(); Logger.logMessage("approvePhasedTransactionResponse:" + response.toJSONString()); Assert.assertNotNull(response.get("transaction")); generateBlocks(duration); Assert.assertEquals(-50 * Constants.ONE_NXT - 2 * Constants.ONE_NXT, ALICE.getBalanceDiff()); Assert.assertEquals(50 * Constants.ONE_NXT, BOB.getBalanceDiff()); Assert.assertEquals(-Constants.ONE_NXT, CHUCK.getBalanceDiff()); } @Test public void invalidVoteCasting() { int duration = 10; APICall apiCall = new TwoPhasedMoneyTransferBuilder() .finishHeight(Nxt.getBlockchain().getHeight() + duration) .build(); JSONObject transactionJSON = TestCreateTwoPhased.issueCreateTwoPhased(apiCall, false); generateBlock(); apiCall = new APICall.Builder("approveTransaction") .param("secretPhrase", DAVE.getSecretPhrase()) .param("transactionFullHash", (String) transactionJSON.get("fullHash")) .param("feeNQT", Constants.ONE_NXT) .build(); JSONObject response = apiCall.invoke(); Assert.assertNotNull(response.get("error")); generateBlock(); Assert.assertEquals("ALICE balance: ", -2 * Constants.ONE_NXT, ALICE.getBalanceDiff()); Assert.assertEquals("BOB balance: ", 0, BOB.getBalanceDiff()); Assert.assertEquals("CHUCK balance: ", 0, CHUCK.getBalanceDiff()); Assert.assertEquals("DAVE balance: ", 0, DAVE.getBalanceDiff()); generateBlocks(duration); Assert.assertEquals("ALICE balance: ", -2 * Constants.ONE_NXT, ALICE.getBalanceDiff()); Assert.assertEquals("BOB balance: ", 0, BOB.getBalanceDiff()); Assert.assertEquals("CHUCK balance: ", 0, CHUCK.getBalanceDiff()); Assert.assertEquals("DAVE balance: ", 0, DAVE.getBalanceDiff()); } @Test public void sendMoneyPhasedNoVoting() { long fee = 2*Constants.ONE_NXT; JSONObject response = new APICall.Builder("sendMoney"). param("secretPhrase", ALICE.getSecretPhrase()). param("recipient", BOB.getStrId()). param("amountNQT", 100 * Constants.ONE_NXT). param("feeNQT", fee). param("phased", "true"). param("phasingFinishHeight", baseHeight + 2). param("phasingVotingModel", -1). build().invoke(); Logger.logDebugMessage("sendMoney: " + response); generateBlock(); // Transaction is not applied yet, fee is paid // Forger Assert.assertEquals(fee, FORGY.getBalanceDiff()); Assert.assertEquals(fee, FORGY.getUnconfirmedBalanceDiff()); // Sender Assert.assertEquals(-fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(0, BOB.getBalanceDiff()); Assert.assertEquals(0, BOB.getUnconfirmedBalanceDiff()); generateBlock(); // Transaction is applied // Sender Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getBalanceDiff()); Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getUnconfirmedBalanceDiff()); } @Test public void sendMoneyPhasedByTransactionHash() { JSONObject response = getSignedBytes(); Logger.logDebugMessage("signedSendMessage: " + response); String fullHash = (String)response.get("fullHash"); Assert.assertEquals(64, fullHash.length()); String approvalTransactionBytes = (String)response.get("transactionBytes"); long fee = 3 * Constants.ONE_NXT; response = new APICall.Builder("sendMoney"). param("secretPhrase", ALICE.getSecretPhrase()). param("recipient", BOB.getStrId()). param("amountNQT", 100 * Constants.ONE_NXT). param("feeNQT", fee). param("phased", "true"). param("phasingFinishHeight", baseHeight + 3). param("phasingVotingModel", 4). param("phasingLinkedFullHash", fullHash). param("phasingQuorum", 1). build().invoke(); Logger.logDebugMessage("sendMoney: " + response); generateBlock(); // Transaction is not applied yet // Sender Assert.assertEquals(-fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(0, BOB.getBalanceDiff()); Assert.assertEquals(0, BOB.getUnconfirmedBalanceDiff()); response = new APICall.Builder("broadcastTransaction"). param("transactionBytes", approvalTransactionBytes). build().invoke(); Logger.logDebugMessage("broadcastTransaction: " + response); generateBlock(); // Transaction is applied before finish height // Sender Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getBalanceDiff()); Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getUnconfirmedBalanceDiff()); } @Test public void sendMoneyPhasedByTransactionHash2of3() { JSONObject response = getSignedBytes(); Logger.logDebugMessage("signedSendMessage: " + response); String fullHash1 = (String)response.get("fullHash"); Assert.assertEquals(64, fullHash1.length()); String approvalTransactionBytes1 = (String)response.get("transactionBytes"); response = getSignedBytes(); Logger.logDebugMessage("signedSendMessage: " + response); String fullHash2 = (String)response.get("fullHash"); Assert.assertEquals(64, fullHash1.length()); response = getSignedBytes(); Logger.logDebugMessage("signedSendMessage: " + response); String fullHash3 = (String)response.get("fullHash"); Assert.assertEquals(64, fullHash1.length()); String approvalTransactionBytes3 = (String)response.get("transactionBytes"); long fee = 5 * Constants.ONE_NXT; response = new APICall.Builder("sendMoney"). param("secretPhrase", ALICE.getSecretPhrase()). param("recipient", BOB.getStrId()). param("amountNQT", 100 * Constants.ONE_NXT). param("feeNQT", fee). param("phased", "true"). param("phasingFinishHeight", baseHeight + 2). param("phasingVotingModel", 4). param("phasingLinkedFullHash", new String[] { fullHash1, fullHash2, fullHash3 }). param("phasingQuorum", 2). build().invoke(); Logger.logDebugMessage("sendMoney: " + response); generateBlock(); // Transaction is not applied yet // Sender Assert.assertEquals(-fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(0, BOB.getBalanceDiff()); Assert.assertEquals(0, BOB.getUnconfirmedBalanceDiff()); response = new APICall.Builder("broadcastTransaction"). param("transactionBytes", approvalTransactionBytes1). build().invoke(); Logger.logDebugMessage("broadcastTransaction: " + response); response = new APICall.Builder("broadcastTransaction"). param("transactionBytes", approvalTransactionBytes3). build().invoke(); Logger.logDebugMessage("broadcastTransaction: " + response); generateBlock(); // Transaction is applied since 2 out 3 hashes were provided // Sender Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getBalanceDiff()); Assert.assertEquals(100 * Constants.ONE_NXT, BOB.getUnconfirmedBalanceDiff()); } @Test public void sendMoneyPhasedByTransactionHashNotApplied() { long fee = 3 * Constants.ONE_NXT; JSONObject response = new APICall.Builder("sendMoney"). param("secretPhrase", ALICE.getSecretPhrase()). param("recipient", BOB.getStrId()). param("amountNQT", 100 * Constants.ONE_NXT). param("feeNQT", fee). param("phased", "true"). param("phasingFinishHeight", baseHeight + 2). param("phasingVotingModel", 4). param("phasingLinkedFullHash", "a13bbe67211fea8d59b2621f1e0118bb242dc5000d428a23a8bd47491a05d681"). // this hash does not match any transaction param("phasingQuorum", 1). build().invoke(); Logger.logDebugMessage("sendMoney: " + response); generateBlock(); // Transaction is not applied yet // Sender Assert.assertEquals(-fee, ALICE.getBalanceDiff()); Assert.assertEquals(-100 * Constants.ONE_NXT - fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(0, BOB.getBalanceDiff()); Assert.assertEquals(0, BOB.getUnconfirmedBalanceDiff()); generateBlock(); // Transaction is rejected since full hash does not match // Sender Assert.assertEquals(-fee, ALICE.getBalanceDiff()); Assert.assertEquals(-fee, ALICE.getUnconfirmedBalanceDiff()); // Recipient Assert.assertEquals(0, BOB.getBalanceDiff()); Assert.assertEquals(0, BOB.getUnconfirmedBalanceDiff()); } @Test public void setAliasPhasedByTransactionHashInvalid() { JSONObject response = getSignedBytes(); Logger.logDebugMessage("signedSendMessage: " + response); String fullHash = (String)response.get("fullHash"); Assert.assertEquals(64, fullHash.length()); String approvalTransactionBytes = (String)response.get("transactionBytes"); long fee = 2 * Constants.ONE_NXT; String alias = "alias" + System.currentTimeMillis(); response = new APICall.Builder("setAlias"). param("secretPhrase", ALICE.getSecretPhrase()). param("aliasName", alias). param("feeNQT", fee). param("phased", "true"). param("phasingFinishHeight", baseHeight + 4). param("phasingVotingModel", 4). param("phasingLinkedFullHash", fullHash). param("phasingQuorum", 1). build().invoke(); Logger.logDebugMessage("setAlias: " + response); generateBlock(); response = new APICall.Builder("getAlias"). param("aliasName", alias). build().invoke(); Logger.logDebugMessage("getAlias: " + response); Assert.assertEquals((long)5, response.get("errorCode")); response = new APICall.Builder("broadcastTransaction"). param("transactionBytes", approvalTransactionBytes). build().invoke(); Logger.logDebugMessage("broadcastTransaction: " + response); generateBlock(); // allocate the same alias immediately response = new APICall.Builder("setAlias"). param("secretPhrase", BOB.getSecretPhrase()). param("aliasName", alias). param("feeNQT", fee). build().invoke(); Logger.logDebugMessage("setSameAlias: " + response); generateBlock(); // phased setAlias transaction is applied but invalid response = new APICall.Builder("getAlias"). param("aliasName", alias). build().invoke(); Logger.logDebugMessage("getAlias: " + response); Assert.assertEquals(BOB.getStrId(), response.get("account")); generateBlock(); // phased setAlias transaction is applied but invalid response = new APICall.Builder("getAlias"). param("aliasName", alias). build().invoke(); Logger.logDebugMessage("getAlias: " + response); Assert.assertEquals(BOB.getStrId(), response.get("account")); } private JSONObject getSignedBytes() { JSONObject response = new APICall.Builder("sendMessage"). param("publicKey", CHUCK.getPublicKeyStr()). param("recipient", ALICE.getStrId()). param("message", "approval notice"). param("feeNQT", Constants.ONE_NXT). build().invoke(); Logger.logDebugMessage("sendMessage not broadcasted: " + response); response = new APICall.Builder("signTransaction"). param("secretPhrase", CHUCK.getSecretPhrase()). param("unsignedTransactionBytes", (String)response.get("unsignedTransactionBytes")). build().invoke(); return response; } }