diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java index ec5fb24..39a75b8 100644 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java +++ b/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java @@ -72,8 +72,6 @@ public class GrantSharesGov { static Event unpaused; @DisplayName("ProposalMigrated") static Event1Arg migrated; - @DisplayName("Error") - static Event2Args error; //endregion EVENTS /** @@ -328,16 +326,16 @@ public static int createProposal(Hash160 proposer, Intent[] intents, String offc * @return The id of the proposal. */ public static int createProposal(Hash160 proposer, Intent[] intents, String offchainUri, int linkedProposal, - int acceptanceRate, int quorum) { + int acceptanceRate, int quorum) { - if (!checkWitness(proposer)) fireErrorAndAbort("Not authorised", "createProposal"); + if (!checkWitness(proposer)) Helper.abort("createProposal" + ": " + "Not authorised"); if (acceptanceRate < parameters.getInt(MIN_ACCEPTANCE_RATE_KEY) || acceptanceRate > 100) - fireErrorAndAbort("Invalid acceptance rate", "createProposal"); + Helper.abort("createProposal" + ": " + "Invalid acceptance rate"); if (quorum < parameters.getInt(MIN_QUORUM_KEY) || quorum > 100) - fireErrorAndAbort("Invalid quorum", "createProposal"); + Helper.abort("createProposal" + ": " + "Invalid quorum"); if (linkedProposal >= 0 && proposals.get(linkedProposal) == null) - fireErrorAndAbort("Linked proposal doesn't exist", "createProposal"); - if (!areIntentsValid(intents)) fireErrorAndAbort("Invalid intents", "createProposal"); + Helper.abort("createProposal" + ": " + "Linked proposal doesn't exist"); + if (!areIntentsValid(intents)) Helper.abort("createProposal" + ": " + "Invalid intents"); int id = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); int expiration = parameters.getInt(EXPIRATION_LENGTH_KEY) + getTime(); @@ -377,12 +375,12 @@ private static boolean areIntentsValid(Intent[] intents) { public static void endorseProposal(int id, Hash160 endorser) { abortIfPaused(); if (members.get(endorser.toByteString()) == null || !checkWitness(endorser)) - fireErrorAndAbort("Not authorised", "endorseProposal"); + Helper.abort("endorseProposal" + ": " + "Not authorised"); ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "endorseProposal"); + if (proposalBytes == null) Helper.abort("endorseProposal" + ": " + "Proposal doesn't exist"); Proposal proposal = (Proposal) new StdLib().deserialize(proposalBytes); - if (proposal.expiration <= getTime()) fireErrorAndAbort("Proposal expired", "endorseProposal"); - if (proposal.endorser != null) fireErrorAndAbort("Proposal already endorsed", "endorseProposal"); + if (proposal.expiration <= getTime()) Helper.abort("endorseProposal" + ": " + "Proposal expired"); + if (proposal.endorser != null) Helper.abort("endorseProposal" + ": " + "Proposal already endorsed"); proposal.endorser = endorser; proposal.reviewEnd = getTime() + parameters.getInt(REVIEW_LENGTH_KEY); @@ -404,17 +402,17 @@ public static void endorseProposal(int id, Hash160 endorser) { */ public static void vote(int id, int vote, Hash160 voter) { abortIfPaused(); - if (vote < -1 || vote > 1) fireErrorAndAbort("Invalid vote", "vote"); + if (vote < -1 || vote > 1) Helper.abort("vote" + ": " + "Invalid vote"); if (members.get(voter.toByteString()) == null || !checkWitness(voter)) - fireErrorAndAbort("Not authorised", "vote"); + Helper.abort("vote" + ": " + "Not authorised"); ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "vote"); + if (proposalBytes == null) Helper.abort("vote" + ": " + "Proposal doesn't exist"); Proposal proposal = (Proposal) new StdLib().deserialize(proposalBytes); int time = getTime(); if (proposal.endorser == null || time < proposal.reviewEnd || time >= proposal.votingEnd) - fireErrorAndAbort("Proposal not active", "vote"); + Helper.abort("vote" + ": " + "Proposal not active"); ProposalVotes pv = (ProposalVotes) new StdLib().deserialize(proposalVotes.get(id)); - if (pv.voters.containsKey(voter)) fireErrorAndAbort("Already voted on this proposal", "vote"); + if (pv.voters.containsKey(voter)) Helper.abort("vote" + ": " + "Already voted on this proposal"); pv.voters.put(voter, vote); if (vote < 0) { @@ -440,20 +438,20 @@ public static void vote(int id, int vote, Hash160 voter) { public static Object[] execute(int id) { abortIfPaused(); ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "execute"); + if (proposalBytes == null) Helper.abort("execute" + ": " + "Proposal doesn't exist"); Proposal proposal = (Proposal) new StdLib().deserialize(proposalBytes); if (proposal.endorser == null || getTime() < proposal.timeLockEnd) - fireErrorAndAbort("Proposal not in execution phase", "execute"); - if (proposal.executed) fireErrorAndAbort("Proposal already executed", "execute"); - if (proposal.expiration <= getTime()) fireErrorAndAbort("Proposal expired", "execute"); + Helper.abort("execute" + ": " + "Proposal not in execution phase"); + if (proposal.executed) Helper.abort("execute" + ": " + "Proposal already executed"); + if (proposal.expiration <= getTime()) Helper.abort("execute" + ": " + "Proposal expired"); ProposalData data = (ProposalData) new StdLib().deserialize(proposalData.get(id)); ProposalVotes votes = (ProposalVotes) new StdLib().deserialize(proposalVotes.get(id)); int voteCount = votes.approve + votes.abstain + votes.reject; if (voteCount * 100 / Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) < data.quorum) - fireErrorAndAbort("Quorum not reached", "execute"); + Helper.abort("execute" + ": " + "Quorum not reached"); int yesNoCount = votes.approve + votes.reject; if (yesNoCount == 0 || (votes.approve * 100 / yesNoCount <= data.acceptanceRate)) - fireErrorAndAbort("Proposal rejected", "execute"); + Helper.abort("execute" + ": " + "Proposal rejected"); proposal.executed = true; Object[] returnVals = new Object[data.intents.length]; @@ -491,17 +489,17 @@ private static void abortOnInvalidValue(String paramKey, int value) { case VOTING_LENGTH_KEY: case TIMELOCK_LENGTH_KEY: case EXPIRATION_LENGTH_KEY: - if (value < 0) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value < 0) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; case MIN_ACCEPTANCE_RATE_KEY: case MIN_QUORUM_KEY: - if (value < 0 || value > 100) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value < 0 || value > 100) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; case MULTI_SIG_THRESHOLD_KEY: - if (value <= 0 || value > 100) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value <= 0 || value > 100) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; default: - fireErrorAndAbort("Unknown parameter", "changeParam"); + Helper.abort("changeParam" + ": " + "Unknown parameter"); } } @@ -516,7 +514,7 @@ public static void addMember(ECPoint memberPubKey) { abortIfPaused(); abortIfCallerIsNotSelf(); Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) != null) fireErrorAndAbort("Already a member", "addMember"); + if (members.get(memberHash.toByteString()) != null) Helper.abort("addMember" + ": " + "Already a member"); members.put(memberHash.toByteString(), memberPubKey.toByteString()); Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) + 1); memberAdded.fire(memberHash); @@ -533,7 +531,7 @@ public static void removeMember(ECPoint memberPubKey) { abortIfPaused(); abortIfCallerIsNotSelf(); Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) == null) fireErrorAndAbort("Not a member", "removeMember"); + if (members.get(memberHash.toByteString()) == null) Helper.abort("removeMember" + ": " + "Not a member"); members.delete(memberHash.toByteString()); Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) - 1); memberRemoved.fire(memberHash); @@ -562,9 +560,9 @@ public static void pause() { try { membersMultiSigHash = calcMembersMultiSigAccount(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "pause"); + Helper.abort("pause" + ": " + e.getMessage()); } - if (!checkWitness(membersMultiSigHash)) fireErrorAndAbort("Not authorized", "pause"); + if (!checkWitness(membersMultiSigHash)) Helper.abort("pause" + ": " + "Not authorized"); Storage.put(ctx, PAUSED_KEY, 1); paused.fire(); } @@ -574,27 +572,26 @@ public static void unpause() { try { membersMultiSigHash = calcMembersMultiSigAccount(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "pause"); + Helper.abort("pause" + ": " + e.getMessage()); } - if (!checkWitness(membersMultiSigHash)) fireErrorAndAbort("Not authorized", "unpause"); + if (!checkWitness(membersMultiSigHash)) Helper.abort("unpause" + ": " + "Not authorized"); Storage.put(ctx, PAUSED_KEY, 0); unpaused.fire(); } private static void abortIfCallerIsNotSelf() { if (Runtime.getCallingScriptHash() != Runtime.getExecutingScriptHash()) { - fireErrorAndAbort("Method only callable by the contract itself", "abortIfCallerIsNotSelf"); + Helper.abort("abortIfCallerIsNotSelf" + ": " + "Method only callable by the contract itself"); } } public static void abortIfPaused() { if (Storage.getBoolean(getReadOnlyContext(), PAUSED_KEY)) { - fireErrorAndAbort("Contract is paused", "abortIfPaused"); + Helper.abort("abortIfPaused" + ": " + "Contract is paused"); } } - private static void fireErrorAndAbort(String msg, String method) { - error.fire(msg, method); - Helper.abort(); + private static void abortWithMessage(String method, String msg) { + Helper.abort(method + ": " + msg); } } diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java index 4f54d66..d5bb325 100644 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java +++ b/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java @@ -61,6 +61,7 @@ public class GrantSharesTreasury { static final StorageMap funders = new StorageMap(ctx, FUNDERS_PREFIX); // [hash, List] static final StorageMap whitelistedTokens = new StorageMap(ctx, WHITELISTED_TOKENS_PREFIX); // [hash, max_amount] + //region EVENTS @DisplayName("FunderAdded") static Event1Arg funderAdded; @DisplayName("FunderRemoved") @@ -87,8 +88,7 @@ public class GrantSharesTreasury { static Event3Args tokensReceived; @DisplayName("WhitelistedTokenMigrated") static Event2Args whitelistedTokenMigrated; - @DisplayName("Error") - static Event2Args error; + //endregion EVENTS /** * Initialises this contract on deployment. @@ -300,7 +300,7 @@ public static void setFundersMultiSigThresholdRatio(Integer value) { abortIfPaused(); abortIfCallerIsNotOwner(); if (value <= 0 || value > 100) - fireErrorAndAbort("Invalid threshold ratio", "setFundersMultiSigThresholdRatio"); + Helper.abort("setFundersMultiSigThresholdRatio" + ": " + "Invalid threshold ratio"); Storage.put(ctx, MULTI_SIG_THRESHOLD_KEY, value); thresholdChanged.fire(value); } @@ -319,11 +319,11 @@ public static void setFundersMultiSigThresholdRatio(Integer value) { public static void addFunder(Hash160 accountHash, ECPoint[] publicKeys) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) != null) fireErrorAndAbort("Already a funder", "addFunder"); - if (!isValid(accountHash) || accountHash == zero()) fireErrorAndAbort("Invalid funder hash", "addFunder"); - if (publicKeys.length == 0) fireErrorAndAbort("List of public keys is empty", "addFunder"); + if (funders.get(accountHash.toByteString()) != null) Helper.abort("addFunder" + ": " + "Already a funder"); + if (!isValid(accountHash) || accountHash == zero()) Helper.abort("addFunder" + ": " + "Invalid funder hash"); + if (publicKeys.length == 0) Helper.abort("addFunder" + ": " + "List of public keys is empty"); for (ECPoint key : publicKeys) { - if (!ECPoint.isValid(key)) fireErrorAndAbort("Invalid public key", "addFunder"); + if (!ECPoint.isValid(key)) Helper.abort("addFunder" + ": " + "Invalid public key"); } funders.put(accountHash.toByteString(), new StdLib().serialize(publicKeys)); funderAdded.fire(accountHash); @@ -339,7 +339,7 @@ public static void addFunder(Hash160 accountHash, ECPoint[] publicKeys) { public static void removeFunder(Hash160 accountHash) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) == null) fireErrorAndAbort("Not a funder", "removeFunder"); + if (funders.get(accountHash.toByteString()) == null) Helper.abort("removeFunder" + ": " + "Not a funder"); funders.delete(accountHash.toByteString()); funderRemoved.fire(accountHash); } @@ -356,8 +356,8 @@ public static void removeFunder(Hash160 accountHash) { public static void addWhitelistedToken(Hash160 token, int maxFundingAmount) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (!isValid(token) || token == zero()) fireErrorAndAbort("Invalid token hash", "addWhitelistedToken"); - if (maxFundingAmount <= 0) fireErrorAndAbort("Invalid max funding amount", "addWhitelistedToken"); + if (!isValid(token) || token == zero()) Helper.abort("addWhitelistedToken" + ": " + "Invalid token hash"); + if (maxFundingAmount <= 0) Helper.abort("addWhitelistedToken" + ": " + "Invalid max funding amount"); whitelistedTokens.put(token.toByteString(), maxFundingAmount); whitelistedTokenAdded.fire(token, maxFundingAmount); } @@ -373,7 +373,7 @@ public static void removeWhitelistedToken(Hash160 token) { abortIfPaused(); abortIfCallerIsNotOwner(); if (whitelistedTokens.get(token.toByteString()) == null) - fireErrorAndAbort("Not a whitelisted token", "removeWhitelistedToken"); + Helper.abort("removeWhitelistedToken" + ": " + "Not a whitelisted token"); whitelistedTokens.delete(token.toByteString()); whitelistedTokenRemoved.fire(token); } @@ -392,8 +392,8 @@ public static void releaseTokens(Hash160 tokenContract, Hash160 to, int amount) abortIfPaused(); abortIfCallerIsNotOwner(); int maxFundingAmount = whitelistedTokens.getIntOrZero(tokenContract.toByteString()); - if (maxFundingAmount == 0) fireErrorAndAbort("Token not whitelisted", "releaseTokens"); - if (amount > maxFundingAmount) fireErrorAndAbort("Above token's max funding amount", "releaseTokens"); + if (maxFundingAmount == 0) Helper.abort("releaseTokens" + ": " + "Token not whitelisted"); + if (amount > maxFundingAmount) Helper.abort("releaseTokens" + ": " + "Above token's max funding amount"); Object[] params = new Object[]{Runtime.getExecutingScriptHash(), to, amount, new Object[]{}}; boolean success = (boolean) Contract.call(tokenContract, "transfer", CallFlags.All, params); if (success) { @@ -410,14 +410,14 @@ public static void releaseTokens(Hash160 tokenContract, Hash160 to, int amount) * paused. */ public static void drain() { - if (!isPaused()) fireErrorAndAbort("Contract is not paused", "drain"); + if (!isPaused()) Helper.abort("drain" + ": " + "Contract is not paused"); Hash160 fundersMultiAddress = null; try { fundersMultiAddress = calcFundersMultiSigAddress(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "drain"); + Helper.abort("drain" + ": " + e.getMessage()); } - if (!checkWitness(fundersMultiAddress)) fireErrorAndAbort("Not authorized", "drain"); + if (!checkWitness(fundersMultiAddress)) Helper.abort("drain" + ": " + "Not authorized"); Hash160 selfHash = Runtime.getExecutingScriptHash(); Iterator it = whitelistedTokens.find((byte) (RemovePrefix | KeysOnly)); while (it.next()) { @@ -442,7 +442,7 @@ public static void voteCommitteeMemberWithLeastVotes() { abortIfPaused(); ECPoint c = getCommitteeMemberWithLeastVotes(); if (!new NeoToken().vote(Runtime.getExecutingScriptHash(), c)) - fireErrorAndAbort("Failed voting on candidate", "voteCommitteeMemberWithLeastVotes"); + Helper.abort("voteCommitteeMemberWithLeastVotes" + ": " + "Failed voting on candidate"); voted.fire(c); } @@ -481,15 +481,14 @@ public static void updateContract(ByteString nef, String manifest, Object data) private static void abortIfCallerIsNotOwner() { if (Runtime.getCallingScriptHash().toByteString() != Storage.get(getReadOnlyContext(), OWNER_KEY)) - fireErrorAndAbort("Not authorised", "abortIfCallerIsNotOwner"); + Helper.abort("abortIfCallerIsNotOwner" + ": " + "Not authorised"); } private static void abortIfPaused() { - if (isPaused()) fireErrorAndAbort("Contract is paused", "abortIfCallerIsNotOwner"); + if (isPaused()) Helper.abort("abortIfCallerIsNotOwner" + ": " + "Contract is paused"); } - private static void fireErrorAndAbort(String msg, String method) { - error.fire(msg, method); - Helper.abort(); + private static void abortWithMessage(String method, String msg) { + Helper.abort(method + ": " + msg); } } diff --git a/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java b/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java index f50c3fc..59d0291 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java @@ -10,6 +10,7 @@ import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; @@ -26,32 +27,14 @@ import java.util.List; import java.util.stream.Collectors; -import static com.axlabs.neo.grantshares.util.TestHelper.ADD_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CALC_MEMBER_MULTI_SIG_ACC; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.DENISE; -import static com.axlabs.neo.grantshares.util.TestHelper.GET_MEMBERS; -import static com.axlabs.neo.grantshares.util.TestHelper.MEMBER_ADDED; -import static com.axlabs.neo.grantshares.util.TestHelper.MEMBER_REMOVED; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_EXECUTED; -import static com.axlabs.neo.grantshares.util.TestHelper.REMOVE_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; -import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; -import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; -import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; -import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; +import static com.axlabs.neo.grantshares.util.TestHelper.*; import static io.neow3j.types.ContractParameter.array; -import static io.neow3j.types.ContractParameter.hash160; -import static io.neow3j.types.ContractParameter.publicKey; +import static io.neow3j.types.ContractParameter.*; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -79,7 +62,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -90,9 +72,8 @@ public static void setUp() throws Throwable { //region ADD MEMBER @Test public void fail_calling_add_member_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(ADD_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(ADD_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Order(1) // Is executed before the execute_remove_member test which removes bob from members. @@ -118,8 +99,7 @@ public void execute_add_member() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); + Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() @@ -160,9 +140,8 @@ public void fail_execute_add_member_with_already_member() throws Throwable { voteForProposal(gov, neow3j, id, charlie); // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Already a member", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Already a member")); } @Test @@ -193,9 +172,8 @@ public void fail_execute_add_member_with_invalid_public_key() throws Throwable { //region REMOVE MEMBER @Test public void fail_calling_remove_member_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(REMOVE_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(REMOVE_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Order(2) // Is executed right after the execute_add_member test to remove bob from members @@ -220,8 +198,7 @@ public void execute_remove_member() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); + Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() @@ -260,9 +237,8 @@ public void fail_execute_remove_member_with_non_member() throws Throwable { voteForProposal(gov, neow3j, id, charlie); // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not a member", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Not a member")); } //endregion REMOVE MEMBER diff --git a/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java b/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java index a253aa4..9d9288f 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java @@ -10,6 +10,7 @@ import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; import io.neow3j.utils.Await; @@ -40,7 +41,6 @@ import static com.axlabs.neo.grantshares.util.TestHelper.REVIEW_LENGTH_KEY; import static com.axlabs.neo.grantshares.util.TestHelper.TIMELOCK_LENGTH_KEY; import static com.axlabs.neo.grantshares.util.TestHelper.VOTING_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; @@ -50,6 +50,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -75,7 +77,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -138,9 +139,8 @@ public void execute_change_parameter() throws Throwable { @Test public void fail_calling_change_parameter_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(CHANGE_PARAM, string(REVIEW_LENGTH_KEY), integer(100)) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(CHANGE_PARAM, string(REVIEW_LENGTH_KEY), integer(100)).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Test @@ -158,9 +158,8 @@ public void fail_changing_unknown_parameter() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Unknown parameter", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Unknown parameter")); } @Test @@ -176,9 +175,8 @@ public void fail_changing_voting_length_to_negative_value() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(VOTING_LENGTH_KEY).getInteger().intValue(), is(PHASE_LENGTH * 1000)); } @@ -195,9 +193,8 @@ public void fail_changing_min_acceptance_rate_to_negative_value() throws Throwab // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MIN_ACCEPTANCE_RATE_KEY).getInteger().intValue(), is(MIN_ACCEPTANCE_RATE)); } @@ -214,9 +211,8 @@ public void fail_changing_min_acceptance_rate_to_more_than_hundred() throws Thro // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MIN_ACCEPTANCE_RATE_KEY).getInteger().intValue(), is(MIN_ACCEPTANCE_RATE)); } @@ -233,9 +229,8 @@ public void fail_changing_multisig_threshold_to_zero() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MULTI_SIG_THRESHOLD_KEY).getInteger().intValue(), is(MULTI_SIG_THRESHOLD_RATIO)); } @@ -252,9 +247,8 @@ public void fail_changing_multisig_threshold_to_more_than_hundred() throws Throw // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MULTI_SIG_THRESHOLD_KEY).getInteger().intValue(), is(MULTI_SIG_THRESHOLD_RATIO)); } diff --git a/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java index 9970179..fd5793f 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java @@ -18,6 +18,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; @@ -36,6 +37,7 @@ import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; import static com.axlabs.neo.grantshares.util.TestHelper.ADD_MEMBER; @@ -61,7 +63,6 @@ import static com.axlabs.neo.grantshares.util.TestHelper.UPDATE_CONTRACT; import static com.axlabs.neo.grantshares.util.TestHelper.VOTE; import static com.axlabs.neo.grantshares.util.TestHelper.VOTED; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.createSimpleProposal; @@ -82,8 +83,7 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -91,8 +91,10 @@ public class GrantSharesGovTest { @RegisterExtension - private static ContractTestExtension ext = new ContractTestExtension(); - + private static final ContractTestExtension ext = new ContractTestExtension(); + private final static Path TESTCONTRACT_NEF_FILE = Paths.get("TestContract.nef"); + private final static Path TESTCONTRACT_MANIFEST_FILE = + Paths.get("TestGrantSharesGov.manifest.json"); private static Neow3j neow3j; private static GrantSharesGovContract gov; private static Account alice; // Set to be a DAO member. @@ -100,10 +102,6 @@ public class GrantSharesGovTest { private static Account charlie; // Set to be a DAO member. private static int defaultProposalId; - private final static Path TESTCONTRACT_NEF_FILE = Paths.get("TestContract.nef"); - private final static Path TESTCONTRACT_MANIFEST_FILE = - Paths.get("TestGrantSharesGov.manifest.json"); - @DeployConfig(GrantSharesGov.class) public static DeployConfiguration deployConfig() throws Exception { DeployConfiguration config = new DeployConfiguration(); @@ -115,7 +113,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -158,7 +155,7 @@ public void succeed_creating_and_retrieving_proposal() throws Throwable { .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); // 2. Test correct setup of the created proposal. - NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, asList(integer(id))); + NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, Collections.singletonList(integer(id))); ProposalStruct p = new ProposalStruct(r.getInvocationResult().getStack().get(0).getList()); assertThat(p.id, is(id)); assertThat(p.proposer, is(alice.getScriptHash())); @@ -196,9 +193,8 @@ public void fail_creating_with_missing_linked_proposal() throws Throwable { array(gov.getScriptHash(), alice.getScriptHash(), 1)); String offchainUri = "fail_creating_with_missing_linked_proposal"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, 1000, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Linked proposal doesn't exist", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), offchainUri, 1000, intent).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Linked proposal doesn't exist")); } @Test @@ -208,13 +204,11 @@ public void fail_creating_with_bad_quorum() throws Throwable { array(gov.getScriptHash(), alice.getScriptHash(), 1)); String offchainUri = "fail_creating_with_bad_quorum"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, MIN_QUORUM - 1, - intent).signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid quorum", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, MIN_QUORUM - 1, intent).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid quorum")); - tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, 101, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid quorum", neow3j); + e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, 101, intent).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid quorum")); } @Test @@ -224,13 +218,11 @@ public void fail_creating_with_bad_acceptance_rate() throws Throwable { array(gov.getScriptHash(), alice.getScriptHash(), 1)); String offchainUri = "fail_creating_with_bad_acceptance_rate"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE - 1, MIN_QUORUM, - intent).signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid acceptance rate", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE - 1, MIN_QUORUM, intent).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid acceptance rate")); - tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, 101, MIN_QUORUM, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid acceptance rate", neow3j); + e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, 101, MIN_QUORUM, intent).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid acceptance rate")); } @Test @@ -286,17 +278,15 @@ public void succeed_endorsing_with_member() throws Throwable { @Test @Order(0) public void fail_endorsing_with_non_member() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, bob.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(defaultProposalId, bob.getScriptHash()).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_endorsing_with_member_but_wrong_signer() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, alice.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(defaultProposalId, alice.getScriptHash()).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -314,17 +304,15 @@ public void fail_endorsing_already_endorsed_proposal() throws Throwable { Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Endorse again - Hash256 tx = gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal already endorsed", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Proposal already endorsed")); } @Test @Order(0) public void fail_endorsing_non_existent_proposal() throws Throwable { - Hash256 tx = gov.endorseProposal(1000, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(1000, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test @@ -336,9 +324,8 @@ public void fail_endorsing_expired_proposal() throws Throwable { .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); ext.fastForwardOneBlock(PHASE_LENGTH); - Hash256 tx = gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal expired", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Proposal expired")); } @Test @@ -401,17 +388,15 @@ public void fail_voting_in_review_and_queued_phase() throws Throwable { Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Vote in review phase - Hash256 tx = gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Proposal not active")); // 5. Fast-forward till after the voting phase. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 4. Vote in queued or later phase - tx = gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Proposal not active")); } @Test @@ -440,9 +425,8 @@ public void fail_voting_multiple_times() throws Throwable { Await.waitUntilTransactionIsExecuted(voteTx, neow3j); // 5. Vote the second time - Hash256 tx = gov.vote(id, 1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Already voted on this proposal", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(id, 1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Already voted on this proposal")); // 6. Check votes ProposalStruct proposal = gov.getProposal(id); @@ -457,35 +441,31 @@ public void fail_voting_multiple_times() throws Throwable { @Order(0) public void fail_voting_with_non_member() throws Throwable { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, -1, bob.getScriptHash()).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(defaultProposalId, -1, bob.getScriptHash()).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_voting_on_non_existent_proposal() throws Throwable { - Hash256 tx = gov.vote(1000, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() - .send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(1000, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test @Order(0) public void fail_voting_on_not_endorsed_proposal() throws Throwable { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, -1, charlie.getScriptHash()) - .signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(defaultProposalId, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Proposal not active")); } @Test @Order(0) public void fail_voting_with_invalid_vote() throws Throwable { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, 2, charlie.getScriptHash()) - .signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid vote", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(defaultProposalId, 2, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign()); + assertTrue(e.getMessage().endsWith("Invalid vote")); } @Test @@ -558,7 +538,7 @@ public void create_proposal_with_large_intents_and_offchainUri() throws Throwabl int id = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0) .getStack().get(0).getInteger().intValue(); - NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, asList(integer(id))); + NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, Collections.singletonList(integer(id))); ProposalStruct p = new ProposalStruct(r.getInvocationResult().getStack().get(0).getList()); assertThat(p.id, is(id)); assertThat(p.proposer, is(bob.getScriptHash())); @@ -621,17 +601,15 @@ public void get_number_of_proposals() throws IOException { @Test @Order(0) public void fail_pausing_contract_without_members_account() throws Throwable { - Hash256 tx = gov.invokeFunction(PAUSE).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(PAUSE).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorized")); } @Test @Order(0) public void fail_unpausing_contract_without_members_account() throws Throwable { - Hash256 tx = gov.invokeFunction(UNPAUSE).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(UNPAUSE).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorized")); } // Is executed as the first test of series of test that require the contract to be paused. @@ -667,9 +645,8 @@ public void fail_execute_update_contract_directly() throws Throwable { ContractManifest manifest = getObjectMapper() .readValue(manifestFile, ContractManifest.class); String manifestString = getObjectMapper().writeValueAsString(manifest); - Hash256 tx = gov.updateContract(nef.toArray(), manifestString, null).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.updateContract(nef.toArray(), manifestString, null).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Test @@ -678,66 +655,57 @@ public void fail_create_proposal_calling_contract_management() throws Throwable IntentParam intent1 = IntentParam.addMemberProposal(gov.getScriptHash(), alice.getECKeyPair().getPublicKey()); IntentParam intent2 = new IntentParam(ContractManagement.SCRIPT_HASH, "destroy"); - Hash256 tx = gov.createProposal(alice.getScriptHash(), "fail_create_proposal_calling_contract_management", -1, - intent1, intent2).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid intents", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.createProposal(alice.getScriptHash(), "fail_create_proposal_calling_contract_management", -1, intent1, intent2).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid intents")); } @Order(11) @Test public void fail_change_param_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(CHANGE_PARAM, string(MIN_ACCEPTANCE_RATE_KEY), integer(50)) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(CHANGE_PARAM, string(MIN_ACCEPTANCE_RATE_KEY), integer(50)).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(12) @Test public void fail_add_member_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(ADD_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(ADD_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(13) @Test public void fail_remove_member_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(REMOVE_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.invokeFunction(REMOVE_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(14) @Test public void fail_execute_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.execute(defaultProposalId).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(defaultProposalId).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(15) @Test public void fail_vote_on_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.vote(defaultProposalId, 1, alice.getScriptHash()).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.vote(defaultProposalId, 1, alice.getScriptHash()).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(16) @Test public void fail_endorse_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, alice.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.endorseProposal(defaultProposalId, alice.getScriptHash()).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(17) @Test public void fail_update_contract_on_paused_contract() throws Throwable { - Hash256 tx = gov.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } // Must be executed after all tests orderd after the test that pauses the contract. diff --git a/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java b/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java index d9cf9ae..ebdbda8 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java @@ -21,6 +21,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; @@ -40,6 +41,7 @@ import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -55,7 +57,6 @@ import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.UNPAUSE; import static com.axlabs.neo.grantshares.util.TestHelper.UPDATE_CONTRACT; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; @@ -73,8 +74,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @ContractTest(contracts = {GrantSharesGov.class, GrantSharesTreasury.class}, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -134,7 +134,6 @@ public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); // contracts gov = new GrantSharesGovContract( @@ -206,33 +205,29 @@ public void succeed_voting_on_committee_member() throws Throwable { @Test @Order(0) public void fail_calling_add_whitelisted_token_directly() throws Throwable { - Hash256 tx = treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_calling_remove_whitelisted_token_directly() throws Throwable { - Hash256 tx = treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_calling_add_funder_directly() throws Throwable { - Hash256 tx = treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_calling_remove_funder_directly() throws Throwable { - Hash256 tx = treasury.removeFunder(charlie.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.removeFunder(charlie.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -259,9 +254,8 @@ public void fail_execute_update_contract_directly() throws Throwable { ContractParameter data = string("update contract"); - Hash256 tx = treasury.invokeFunction(UPDATE_CONTRACT, byteArray(nef.toArray()), byteArray(manifestBytes), data) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.invokeFunction(UPDATE_CONTRACT, byteArray(nef.toArray()), byteArray(manifestBytes), data).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -281,9 +275,8 @@ public void fail_release_tokens_with_non_whitelisted_token() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Token not whitelisted", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Token not whitelisted")); } @Test @@ -303,24 +296,22 @@ public void fail_release_tokens_with_to_high_amount() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Above token's max funding amount", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Above token's max funding amount")); } @Test @Order(0) public void fail_calling_release_tokens_directly() throws Throwable { - Hash256 tx = treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_adding_invalid_funder() throws Throwable { ContractParameter intent = new IntentParam(treasury.getScriptHash(), "addFunder", - byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), array(asList(alice.getECKeyPair().getPublicKey()))); + byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), array(Collections.singletonList(alice.getECKeyPair().getPublicKey()))); String offchainUri = "fail_adding_invalid_funder"; // 1. Create and endorse proposal @@ -332,9 +323,8 @@ public void fail_adding_invalid_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid funder hash", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid funder hash")); } @Test @@ -352,9 +342,8 @@ public void fail_adding_funder_with_no_public_keys() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "List of public keys is empty", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("List of public keys is empty")); } @Test @@ -374,9 +363,8 @@ public void fail_adding_funder_with_invalid_public_key() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid public key", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid public key")); } @Test @@ -398,12 +386,10 @@ public void fail_setting_funders_multisig_threshold_ratio_with_invalid_value() t // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id1).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid threshold ratio", neow3j); - tx = gov.execute(id2).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid threshold ratio", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id1).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid threshold ratio")); + e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id2).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid threshold ratio")); } @Test @@ -422,9 +408,8 @@ public void fail_adding_whitelisted_token_with_invalid_token() throws Throwable // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid token hash", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid token hash")); } @Test @@ -443,9 +428,8 @@ public void fail_adding_whitelisted_token_with_invalid_max_funding_amount() thro // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid max funding amount", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Invalid max funding amount")); } @Test @@ -530,9 +514,8 @@ public void fail_adding_already_added_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Already a funder", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Already a funder")); } @Test @@ -579,9 +562,8 @@ public void fail_removing_nonexistant_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not a funder", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Not a funder")); } @Test @@ -765,57 +747,50 @@ public void succeed_pausing_contract() throws Throwable { @Order(21) @Test public void fail_add_funder_on_paused_contract() throws Throwable { - Hash256 tx = treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(22) @Test public void fail_remove_funder_on_paused_contract() throws Throwable { - Hash256 tx = treasury.removeFunder(alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.removeFunder(alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(23) @Test public void fail_add_whitelisted_token_on_paused_contract() throws Throwable { - Hash256 tx = treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(24) @Test public void fail_remove_whitelisted_token_on_paused_contract() throws Throwable { - Hash256 tx = treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(25) @Test public void fail_release_tokens_on_paused_contract() throws Throwable { - Hash256 tx = treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(26) @Test public void fail_vote_on_committee_member_on_paused_contract() throws Throwable { - Hash256 tx = treasury.voteCommitteeMemberWithLeastVotes().signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.voteCommitteeMemberWithLeastVotes().signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(27) @Test public void fail_update_contract_on_paused_contract() throws Throwable { - Hash256 tx = treasury.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(28) diff --git a/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java b/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java index 57cf3f6..5e10a25 100644 --- a/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java @@ -10,6 +10,7 @@ import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; @@ -35,7 +36,6 @@ import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_EXECUTED; import static com.axlabs.neo.grantshares.util.TestHelper.REVIEW_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; @@ -46,6 +46,7 @@ import static io.neow3j.types.ContractParameter.string; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", @@ -76,7 +77,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -88,9 +88,8 @@ public static void setUp() throws Throwable { @Test public void fail_executing_non_existent_proposal() throws Throwable { - Hash256 tx = gov.execute(1000).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(1000).signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test @@ -110,9 +109,8 @@ public void fail_executing_proposal_that_wasnt_endorsed() throws Throwable { .getStack().get(0).getInteger().intValue(); // 2. Call execute - tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not in execution phase", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal not in execution phase")); } @Test @@ -127,9 +125,8 @@ public void fail_executing_proposal_without_votes() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH + PHASE_LENGTH); // 2. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test @@ -157,9 +154,8 @@ public void fail_executing_accepted_proposal_multiple_times() throws Throwable { .getNotifications().get(1).getEventName(), is(PROPOSAL_EXECUTED)); // 4. Call execute the second time and fail. - tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal already executed", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal already executed")); } @Test @@ -229,9 +225,8 @@ public void fail_executing_proposal_quorum_reached_but_rejected() throws Throwab ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test @@ -253,9 +248,8 @@ public void fail_executing_proposal_quorum_reached_but_all_abstained() throws Th ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test @@ -274,9 +268,8 @@ public void fail_executing_proposal_quorum_not_reached() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test @@ -296,9 +289,8 @@ public void fail_executing_proposal_with_different_quorum_not_reached() throws T ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test @@ -320,9 +312,8 @@ public void fail_executing_proposal_with_different_quorum_reached_different_rate ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test @@ -365,8 +356,7 @@ public void fail_executing_expired_proposal() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH + PHASE_LENGTH); // skip voting, time lock, expiration phase // 2. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal expired", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Proposal expired")); } } diff --git a/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java b/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java index fef4a95..34d79f6 100644 --- a/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java @@ -18,6 +18,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; import io.neow3j.types.Hash256; @@ -43,7 +44,6 @@ import static com.axlabs.neo.grantshares.util.TestHelper.IS_PAUSED; import static com.axlabs.neo.grantshares.util.TestHelper.PAUSE; import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; @@ -53,8 +53,7 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @ContractTest(contracts = {GrantSharesGov.class, GrantSharesTreasury.class}, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -117,7 +116,6 @@ public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); // contracts gov = new GrantSharesGovContract( ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); @@ -183,9 +181,8 @@ public void calc_funders_multisig_address() throws Throwable { @Test @Order(0) public void fail_execute_drain_on_unpaused_contract() throws Throwable { - Hash256 tx = treasury.drain().signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is not paused", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.drain().signers(AccountSigner.calledByEntry(alice)).sign()); + assertTrue(e.getMessage().endsWith("Contract is not paused")); } @Order(10) @@ -211,9 +208,8 @@ public void succeed_pausing_contract() throws Throwable { @Order(11) @Test public void fail_execute_drain_with_non_funder() throws Throwable { - Hash256 tx = treasury.drain().signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + Exception e = assertThrows(TransactionConfigurationException.class, () -> treasury.drain().signers(AccountSigner.calledByEntry(bob)).sign()); + assertTrue(e.getMessage().endsWith("Not authorized")); } @Order(12) diff --git a/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java b/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java index f333620..d59c07d 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java @@ -188,12 +188,4 @@ public static Account createMultiSigAccount(int threshold, Account... accounts) .collect(Collectors.toList()); return Account.createMultiSigAccount(pubKeys, threshold); } - - public static void assertAborted(Hash256 tx, String expectedError, Neow3j neow3j) throws IOException { - Await.waitUntilTransactionIsExecuted(tx, neow3j); - NeoApplicationLog.Execution e = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0); - assertThat(e.getState(), is(NeoVMStateType.FAULT)); - String exception = e.getNotifications().get(0).getState().getList().get(0).getString(); - assertThat(exception, containsString(expectedError)); - } }