在CorDapp的内部,我编写了一个自定义的generateSpend函数(附加在代码段中)。它结合使用tokenSelection.attemptSpend
和generateExit
和addTokensToRedeem
来花费来自各个发行者的代币来支付一笔金额。
示例:[成本为3木材],由[2奥斯丁发行的尼克持有的木材],[1尼克发行的尼克持有的木材]支付
我的实现中存在明显错误吗?这会导致一个非常奇怪的内存泄漏错误: Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
。
在单独运行时测试通过,但在一次运行时失败。请注意,这里还有100万种可能出错的地方-我最近使用的是Corda 4.4。
我尝试过的事情:
代码和错误:
/**
* When a player spends resources in-game, those resources are consumed as inputs to a transaction. The generateInGameSpend
* method leverages the token-SDK to facilitate the building of transaction proposing the consumption of tokens when they are
* spent (burned) and not transferred to a counter-party.
*
* This method uses the generateExit functionality from the tokenSelection and mutates an input transaction builder in place.
*/
@Suspendable
fun generateInGameSpend(
serviceHub: ServiceHub,
tb: TransactionBuilder,
costs: Map<TokenType, Long>,
holder: Party,
changeOwner: Party,
additionalQueryCriteria: QueryCriteria? = null,
messageToLog: String = "",
logger: Logger? = null
): TransactionBuilder {
// Create a tokenSelector
val tokenSelection = TokenSelection(serviceHub)
// Generate exits for tokens of the appropriate type
costs.filter { it.value > 0 }.forEach { (type, amount) ->
val baseCriteria = heldTokenAmountCriteria(type, holder)
val queryCriteria = additionalQueryCriteria?.let { baseCriteria.and(it) } ?: baseCriteria
// Get a list of tokens satisfying the costs
val tokensToSpend = tokenSelection
.attemptSpend(amount of type, tb.lockId, queryCriteria)
// Run checks on the tokens to ensure the proposed transaction is valid
val notaryToCheck = tokensToSpend.first().state.notary
check(tokensToSpend.all { it.state.notary == notaryToCheck }) { "You are trying to spend assets with different notaries." }
check(tokensToSpend.isNotEmpty()) { "Received empty list of states to spend." }
// Begin to spend tokens to satisfy costs
var spentAmount = Amount(0, type)
tokensToSpend
.groupBy { it.state.data.issuer }
.forEach {
val amountOfTokens = it.value.sumTokenStateAndRefs().withoutIssuer()
spentAmount = spentAmount.plus(amountOfTokens)
val (exitStates, change) = tokenSelection.generateExit(
it.value,
if (spentAmount.quantity > costs[type]!!) Amount(amount, type) else amountOfTokens,
changeOwner)
addTokensToRedeem(tb, exitStates, change)
}
}
// Return the mutated transaction builder
return tb
}
?[m io.github.classgraph.ClassGraphException: Uncaught exception during scan
at io.github.classgraph.ClassGraphException.newClassGraphException(ClassGraphException.java:89) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1183) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1201) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1214) ~[classgraph-4.8.41.jar:4.8.41]
at net.corda.core.internal.ClassGraphUtilsKt.pooledScan(ClassGraphUtils.kt:18) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.internal.ClassLoadingUtilsKt.createInstancesOfClassesImplementing(ClassLoadingUtils.kt:22) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:325) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:297) ~[corda-core-4.4-SNAPSHOT.jar:?]
at java.util.HashMap.computeIfAbsent(HashMap.java:1127) ~[?:1.8.0_211]
at java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2672) ~[?:1.8.0_211]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(AttachmentsClassLoader.kt:322) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext$default(AttachmentsClassLoader.kt:318) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.LedgerTransaction.internalPrepareVerify$core(LedgerTransaction.kt:217) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.LedgerTransaction.verify(LedgerTransaction.kt:207) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.TransactionBuilder.verify(TransactionBuilder.kt:559) ~[corda-core-4.4-SNAPSHOT.jar:?]
at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:131) ~[workflows-0.1.jar:?]
at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:33) ~[workflows-0.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.4-SNAPSHOT.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.4-SNAPSHOT.jar:?]
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_211]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_211]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_211]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:1.8.0_211]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_211]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_211]
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.4-SNAPSHOT.jar:?]
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
?[37m[INFO] 16:44:20,066 [Mock network] statemachine.StaffedFlowHospital. - Flow error allowed to propagate {actor_id=Only For Testing, actor_owning_identity=O=Mock Company 1, L=London, C=GB, actor_store_id=TEST, fiber-id=10000775, flow-id=e2b0e443-f602-46ff-ad4d-93f666cca877, invocation_id=602cdfcf-3c0b-4537-a8a8-c8101c1dfe22, invocation_timestamp=2019-11-13T21:43:08.318Z, origin=Only For Testing, session_id=602cdfcf-3c0b-4537-a8a8-c8101c1dfe22, session_timestamp=2019-11-13T21:43:08.318Z, thread-id=7658}
?[m io.github.classgraph.ClassGraphException: Uncaught exception during scan
at io.github.classgraph.ClassGraphException.newClassGraphException(ClassGraphException.java:89) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1183) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1201) ~[classgraph-4.8.41.jar:4.8.41]
at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1214) ~[classgraph-4.8.41.jar:4.8.41]
at net.corda.core.internal.ClassGraphUtilsKt.pooledScan(ClassGraphUtils.kt:18) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.internal.ClassLoadingUtilsKt.createInstancesOfClassesImplementing(ClassLoadingUtils.kt:22) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:325) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:297) ~[corda-core-4.4-SNAPSHOT.jar:?]
at java.util.HashMap.computeIfAbsent(HashMap.java:1127) ~[?:1.8.0_211]
at java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2672) ~[?:1.8.0_211]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(AttachmentsClassLoader.kt:322) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext$default(AttachmentsClassLoader.kt:318) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.LedgerTransaction.internalPrepareVerify$core(LedgerTransaction.kt:217) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.LedgerTransaction.verify(LedgerTransaction.kt:207) ~[corda-core-4.4-SNAPSHOT.jar:?]
at net.corda.core.transactions.TransactionBuilder.verify(TransactionBuilder.kt:559) ~[corda-core-4.4-SNAPSHOT.jar:?]
at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:131) ~[workflows-0.1.jar:?]
at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:33) ~[workflows-0.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.4-SNAPSHOT.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.4-SNAPSHOT.jar:?]
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_211]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_211]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_211]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:1.8.0_211]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_211]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_211]
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.4-SNAPSHOT.jar:?]
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
测试套件1:
class RobberFlowTests {
private val network = MockNetwork(MockNetworkParameters(
notarySpecs = listOf(MockNetworkNotarySpec(CordaX500Name("Notary", "London", "GB"))),
networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
cordappsForAllNodes = listOf(
TestCordapp.findCordapp("com.flows"),
TestCordapp.findCordapp("com.oracleClientFlows"),
TestCordapp.findCordapp("com.contractsAndStates"),
TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
TestCordapp.findCordapp("com.r3.corda.lib.tokens.money")
)
)
)
private val a = network.createNode(MockNodeParameters())
private val b = network.createNode(MockNodeParameters())
private val c = network.createNode(MockNodeParameters())
private val d = network.createNode(MockNodeParameters())
private val oracleName = CordaX500Name("Oracle", "New York", "US")
private val oracle = network.createNode(
MockNodeParameters(legalName = oracleName).withAdditionalCordapps(
listOf(
TestCordapp.findCordapp("com.oracleService")
)
)
)
@Before
fun setup() {
network.runNetwork()
}
@After
fun tearDown() = network.stopNodes()
@Test
fun player1IsUnableToMoveTheRobberWhenA7IsNotRolled() {
// Get an identity for each of the players of the game.
val p1 = a.info.chooseIdentity()
val p2 = b.info.chooseIdentity()
val p3 = c.info.chooseIdentity()
val p4 = d.info.chooseIdentity()
// Issue a game state onto the ledger
val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
val futureWithGameState = a.startFlow(gameStateIssueFlow)
network.runNetwork()
val stxGameState = futureWithGameState.getOrThrow()
// Get a reference to the issued game state
val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 2, gameBoardState, oracle)
val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
network.runNetwork()
futureWithDiceRoll.getOrThrow()
val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
network.runNetwork()
assertFailsWith<TransactionVerificationException.ContractRejection>("The associated dice roll must have a value of 7.") { futureWithMovedRobber.getOrThrow() }
}
@Test
fun player1IsAbleToMoveTheRobberWhenA7IsRolled() {
// Get an identity for each of the players of the game.
val p1 = a.info.chooseIdentity()
val p2 = b.info.chooseIdentity()
val p3 = c.info.chooseIdentity()
val p4 = d.info.chooseIdentity()
// Issue a game state onto the ledger
val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
val futureWithGameState = a.startFlow(gameStateIssueFlow)
network.runNetwork()
val stxGameState = futureWithGameState.getOrThrow()
// Get a reference to the issued game state
val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 4, gameBoardState, oracle)
val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
network.runNetwork()
futureWithDiceRoll.getOrThrow()
val futureWithClaimedResources = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
network.runNetwork()
val futureWithMovedRobber = futureWithClaimedResources.getOrThrow()
requireThat {
val outputRobber = futureWithMovedRobber.coreTransaction.outputsOfType<RobberState>().first()
"Assert that the robber has been moved to the appropriate position" using (outputRobber.hexTileIndex == HexTileIndex(5))
}
}
@Test
fun player1IsAbleToApplyTheRobberAfterMovingIt() {
// Get an identity for each of the players of the game.
val p1 = a.info.chooseIdentity()
val p2 = b.info.chooseIdentity()
val p3 = c.info.chooseIdentity()
val p4 = d.info.chooseIdentity()
// Issue a game state onto the ledger
val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
val futureWithGameState = a.startFlow(gameStateIssueFlow)
network.runNetwork()
val stxGameState = futureWithGameState.getOrThrow()
// Get a reference to the issued game state
val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 4, gameBoardState, oracle)
val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
network.runNetwork()
futureWithDiceRoll.getOrThrow()
val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
network.runNetwork()
futureWithMovedRobber.getOrThrow()
val futureWithRobberApplied = arrayOfAllPlayerNodesInOrder[0].startFlow(ApplyRobberFlow(gameBoardState.linearId))
network.runNetwork()
val txWithAppliedRobber = futureWithRobberApplied.getOrThrow().coreTransaction
val inputRobber = txWithAppliedRobber.outputsOfType<RobberState>().single()
val outputRobber = txWithAppliedRobber.outputsOfType<RobberState>().single()
requireThat {
"The robber that was deactivated is the robber that was moved" using (outputRobber.linearId == inputRobber.linearId)
"The robber has no changed position" using (outputRobber.hexTileIndex == inputRobber.hexTileIndex)
"The output Robber has been deactivated" using (!outputRobber.active)
}
}
@Test
fun aPlayerIsAbleToRemoveAPlayBlockerState() {
// Get an identity for each of the players of the game.
val p1 = a.info.chooseIdentity()
val p2 = b.info.chooseIdentity()
val p3 = c.info.chooseIdentity()
val p4 = d.info.chooseIdentity()
// Issue a game state onto the ledger
val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
val futureWithGameState = a.startFlow(gameStateIssueFlow)
network.runNetwork()
val stxGameState = futureWithGameState.getOrThrow()
// Get a reference to the issued game state
val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
val nodeWithMoreThan7 = gatherResourcesUntilAPlayerHasMoreThan7(gameBoardState, arrayOfAllPlayerNodesInOrder, oracle, network)
val diceRollTriggeringRobber = getDiceRollWithSpecifiedRollValue(3,4, gameBoardState, oracle)
val futureWithRobberTriggered = arrayOfAllPlayerNodes[0].startFlow(RollDiceFlow(gameBoardState.linearId, diceRollTriggeringRobber))
network.runNetwork()
futureWithRobberTriggered.getOrThrow()
val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
network.runNetwork()
futureWithMovedRobber.getOrThrow()
val futureWithRobberApplied = arrayOfAllPlayerNodesInOrder[0].startFlow(ApplyRobberFlow(gameBoardState.linearId))
network.runNetwork()
val txWithAppliedRobber = futureWithRobberApplied.getOrThrow().coreTransaction
val playBlockerState = txWithAppliedRobber.outputsOfType<PlayBlockerState>()
.filter { it.playerBlocked == nodeWithMoreThan7.info.legalIdentities.first() }
.first()
var resourceTotal = 0
val resourcesToSpend = mutableMapOf<TokenType, Long>()
val playerResources = countAllResourcesForASpecificNode(nodeWithMoreThan7).mutableMap
playerResources.forEach {
if (resourceTotal < playBlockerState.price) {
if (resourceTotal + it.value > playBlockerState.price) {
val amount = it.value + resourceTotal.toLong() - playBlockerState.price
resourcesToSpend[it.key] = amount
resourceTotal += amount.toInt()
}
else {
resourcesToSpend[it.key] = it.value
resourceTotal += it.value.toInt()
}
}
}
val futureWithRemovedPlayBlockerState = nodeWithMoreThan7.startFlow(RemovePlayBlockerFlow(playBlockerState.linearId, resourcesToSpend))
network.runNetwork()
futureWithRemovedPlayBlockerState.getOrThrow()
requireThat {
"All nodes now recognize that the nodeWithMoreThan7 has removed its playBlocker" using (
arrayOfAllPlayerNodes.all { it.services.vaultService.queryBy<PlayBlockerState>().states.filter { it.state.data.playerBlocked == nodeWithMoreThan7.info.legalIdentities.first() }.isEmpty() })
}
}
}
最佳答案
将generateInGameSpend
函数移至Corda服务中,因此永远不会检查代码。
由于流的堆栈不断增长,在运行检查点流时,具有大/长运行循环可能会导致问题。我认为在持久保存检查点/稍后再加载它时可能会引起问题。
Corda服务内部的代码没有检查点,因此可以避免此问题。
关于kotlin - CorDapp流测试中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58859739/