diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/AbstractCashFlow.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/AbstractCashFlow.kt index 59a59b2bdd..ec68083bc1 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/AbstractCashFlow.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/AbstractCashFlow.kt @@ -12,6 +12,7 @@ package com.r3.corda.enterprise.perftestcordapp.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount +import net.corda.core.crypto.SecureHash import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowException import net.corda.core.flows.FlowLogic @@ -55,7 +56,7 @@ abstract class AbstractCashFlow(override val progressTracker: ProgressTra * transaction, otherwise this is the well known identity. */ @CordaSerializable - data class Result(val stx: SignedTransaction, val recipient: AbstractParty?) + data class Result(val id: SecureHash, val recipient: AbstractParty?) abstract class AbstractRequest(val amount: Amount) } diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlow.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlow.kt index 9786cf3192..bae6312103 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlow.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlow.kt @@ -11,6 +11,9 @@ package com.r3.corda.enterprise.perftestcordapp.flows import co.paralleluniverse.fibers.Suspendable +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.cash.selection.AbstractCashSelection +import com.r3.corda.enterprise.perftestcordapp.issuedBy import net.corda.core.contracts.Amount import net.corda.core.contracts.InsufficientBalanceException import net.corda.core.flows.StartableByRPC @@ -23,9 +26,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.ProgressTracker -import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash -import com.r3.corda.enterprise.perftestcordapp.contracts.asset.cash.selection.AbstractCashSelection -import com.r3.corda.enterprise.perftestcordapp.issuedBy import java.util.* /** @@ -84,7 +84,7 @@ class CashExitFlow(private val amount: Amount, // Commit the transaction progressTracker.currentStep = FINALISING_TX val notarised = finaliseTx(tx, participants, "Unable to notarise exit") - return Result(notarised, null) + return Result(notarised.id, null) } @CordaSerializable diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDoublePayment.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDoublePayment.kt index 503bb00c22..973ee10cd0 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDoublePayment.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDoublePayment.kt @@ -15,9 +15,7 @@ import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount import net.corda.confidential.SwapIdentitiesFlow -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.* import net.corda.core.flows.FlowException import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException @@ -56,7 +54,7 @@ class CashIssueAndDoublePayment(val amount: Amount, = txState.copy(data = txState.data.copy(amount = amt, owner = owner)) val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary)) - val cashStateAndRef = issueResult.stx.tx.outRef(0) + val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef progressTracker.currentStep = GENERATING_ID val txIdentities = if (anonymous) { @@ -93,7 +91,7 @@ class CashIssueAndDoublePayment(val amount: Amount, val cause = expected.cause if (cause is NotaryException) { if (cause.error is NotaryError.Conflict) { - return Result(notarised1, recipient) + return Result(notarised1.id, recipient) } throw expected // Wasn't actually expected! } diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDuplicatePayment.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDuplicatePayment.kt index ae6db61624..a40b547a13 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDuplicatePayment.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndDuplicatePayment.kt @@ -15,9 +15,7 @@ import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount import net.corda.confidential.SwapIdentitiesFlow -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.* import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty @@ -53,7 +51,7 @@ class CashIssueAndDuplicatePayment(val amount: Amount, = txState.copy(data = txState.data.copy(amount = amt, owner = owner)) val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary)) - val cashStateAndRef = issueResult.stx.tx.outRef(0) + val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef progressTracker.currentStep = GENERATING_ID val txIdentities = if (anonymous) { @@ -79,6 +77,6 @@ class CashIssueAndDuplicatePayment(val amount: Amount, val notarised1 = finaliseTx(tx, setOf(recipient), "Unable to notarise spend first time") val notarised2 = finaliseTx(tx, setOf(recipient), "Unable to notarise spend second time") - return Result(notarised2, recipient) + return Result(notarised2.id, recipient) } } \ No newline at end of file diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelection.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelection.kt index df384beeb5..0a59903e1b 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelection.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelection.kt @@ -15,9 +15,7 @@ import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount import net.corda.confidential.SwapIdentitiesFlow -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.* import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty @@ -53,7 +51,7 @@ class CashIssueAndPaymentNoSelection(val amount: Amount, progressTracker.currentStep = GENERATING_TX val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary)) - val cashStateAndRef = issueResult.stx.tx.outRef(0) + val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef progressTracker.currentStep = GENERATING_ID val txIdentities = if (anonymous) { @@ -77,6 +75,6 @@ class CashIssueAndPaymentNoSelection(val amount: Amount, progressTracker.currentStep = FINALISING_TX val notarised = finaliseTx(tx, setOf(recipient), "Unable to notarise spend") - return Result(notarised, recipient) + return Result(notarised.id, recipient) } } diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlow.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlow.kt index 96445ad1d3..7c84ef1057 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlow.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlow.kt @@ -11,6 +11,8 @@ package com.r3.corda.enterprise.perftestcordapp.flows import co.paralleluniverse.fibers.Suspendable +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash +import com.r3.corda.enterprise.perftestcordapp.issuedBy import net.corda.core.contracts.Amount import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party @@ -18,8 +20,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.ProgressTracker -import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash -import com.r3.corda.enterprise.perftestcordapp.issuedBy import java.util.* /** @@ -54,7 +54,7 @@ class CashIssueFlow(private val amount: Amount, progressTracker.currentStep = FINALISING_TX // There is no one to send the tx to as we're the only participants val notarised = finaliseTx(tx, emptySet(), "Unable to notarise issue") - return Result(notarised, ourIdentity) + return Result(notarised.id, ourIdentity) } @CordaSerializable diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlow.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlow.kt index efffbca068..6ced99f6d0 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlow.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlow.kt @@ -11,6 +11,7 @@ package com.r3.corda.enterprise.perftestcordapp.flows import co.paralleluniverse.fibers.Suspendable +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.contracts.Amount import net.corda.core.contracts.InsufficientBalanceException @@ -20,7 +21,6 @@ import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker -import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import java.util.* /** @@ -73,7 +73,7 @@ open class CashPaymentFlow( progressTracker.currentStep = FINALISING_TX val notarised = finaliseTx(tx, setOf(recipient), "Unable to notarise spend") - return Result(notarised, anonymousRecipient) + return Result(notarised.id, anonymousRecipient) } @CordaSerializable diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt index 0c4d108d99..1daf87390d 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt @@ -56,7 +56,7 @@ class CashExitFlowTests { val exitAmount = 500.DOLLARS val future = bankOfCordaNode.startFlow(CashExitFlow(exitAmount, ref)) mockNet.runNetwork() - val exitTx = future.getOrThrow().stx.tx + val exitTx = bankOfCordaNode.services.validatedTransactions.getTransaction(future.getOrThrow().id)!!.tx val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref)) assertEquals(1, exitTx.inputs.size) assertEquals(1, exitTx.outputs.size) diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt index 16d7a3af5d..434b63341b 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt @@ -53,7 +53,7 @@ class CashIssueFlowTests { val ref = OpaqueBytes.of(0x01) val future = bankOfCordaNode.startFlow(CashIssueFlow(expected, ref, notary)) mockNet.runNetwork() - val issueTx = future.getOrThrow().stx + val issueTx = bankOfCordaNode.services.validatedTransactions.getTransaction(future.getOrThrow().id)!! val output = issueTx.tx.outputsOfType().single() assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), output.amount) } diff --git a/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx b/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx new file mode 100644 index 0000000000..05ba921203 --- /dev/null +++ b/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx @@ -0,0 +1,1093 @@ + + + + + + false + true + + + + + + + + + + host + performance-node-1 + = + + + port + 10003 + = + + + username + corda + = + + + password + corda_is_awesome + = + + + notaryName + O=Perf-10.155.0.11,OU=Corda,L=London,C=GB + = + + + otherPartyName + O=Perf-10.155.0.6, OU=Corda, L=London, C=GB + = + + + duration + 300 + = + + + pause + 30 + = + + + remoteAgentCount + 5 + = + + + + + + continue + + false + -1 + + 1 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 5 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 10 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 20 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 40 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 60 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 80 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 100 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + + com.r3.corda.jmeter.CashIssueSampler + + + + + continue + + false + -1 + + 1 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 2 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 5 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 10 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 20 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 30 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + continue + + false + -1 + + 40 + + 1509455820000 + 1509455820000 + true + ${duration} + ${pause} + + + + + + + label + ${__samplerName}-${__groovy(ctx.getThreadGroup().getNumThreads() * vars.get("remoteAgentCount").toInteger())} + = + + + host + ${host} + = + + + port + ${port} + = + + + username + ${username} + = + + + password + ${password} + = + + + notaryName + ${notaryName} + = + + + otherPartyName + ${otherPartyName} + = + + + useCoinSelection + false + = + + + anonymousIdentities + false + = + + + + com.r3.corda.jmeter.CashIssueAndPaySampler + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + + true + + + +