Corda 4.9中Scheduler与Vault事务记录失败问题求助
问题:Corda 4.9中实现SchedulableState时的事务记录失败问题
我是Corda新手,想要实现一个类似待办事项列表的Scheduler,每30秒检查任务分配情况。使用无特殊配置的Java模板,数据库为H2,Corda版本为4.9。我的State类继承了ContractState、LinearState和SchedulableState,执行创建任务的Flow时出现事务记录失败的错误,怀疑是调度活动方法中调用Vault的环节有问题。
ToDoState代码
@BelongsToContract(ToDoContract.class) public class TodoState implements ContractState, LinearState, SchedulableState { private final Instant deadlineReminder; private final Party assignedBy; private final Party assignedTo; private final String taskDescription; private UniqueIdentifier linearId; public Party getAssignedBy() { return assignedBy; } public Party getAssignedTo() { return assignedTo; } public String getTaskDescription() { return taskDescription; } public TodoState(Party assignedBy, Party assignedTo, String taskDescription) { this.assignedBy = assignedBy; this.assignedTo = assignedTo; this.taskDescription = taskDescription; this.linearId = new UniqueIdentifier(); this.deadlineReminder = Instant.now().plusSeconds(30); } @ConstructorForDeserialization public TodoState(Party assignedBy, Party assignedTo, String taskDescription, UniqueIdentifier linearId, Instant deadlineReminder) { this.assignedBy = assignedBy; this.assignedTo = assignedTo; this.taskDescription = taskDescription; this.linearId = linearId; this.deadlineReminder = deadlineReminder; } public TodoState assign(Party assignedTo) { return new TodoState(assignedBy, assignedTo, taskDescription, linearId, deadlineReminder); } @NotNull @Override public List<AbstractParty> getParticipants() { return Arrays.asList(assignedBy, assignedTo); } @NotNull @Override public UniqueIdentifier getLinearId() { return linearId; } @Nullable @Override public ScheduledActivity nextScheduledActivity(@NotNull StateRef thisStateRef, @NotNull FlowLogicRefFactory flowLogicRefFactory) { System.out.println("nextScheduledActivity invoked"); System.out.println("StateRef TX is " + thisStateRef.getTxhash()); final ScheduledActivity scheduledActivity = new ScheduledActivity(flowLogicRefFactory.create( "com.template.flows.AlarmFlow", thisStateRef ), deadlineReminder); System.out.println("Passed"); return scheduledActivity; } }
CreateToDoFlow代码
public class CreateToDoFlow { @InitiatingFlow @StartableByRPC public static class CreateTodoFlowInitiator extends FlowLogic<Void> { private final String taskDescription; private Party me; public CreateTodoFlowInitiator(String task) { this.taskDescription = task; } @Override @Suspendable public Void call() throws FlowException { this.me = getOurIdentity(); final Party notary = getServiceHub().getNetworkMapCache().getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB")); final TodoState output = new TodoState(this.me, this.me, this.taskDescription); final TransactionBuilder builder = new TransactionBuilder(notary); builder.addOutputState(output); builder.addCommand(new Command.CreateToDoCommand(), me.getOwningKey()); builder.verify(getServiceHub()); final SignedTransaction ptx = getServiceHub().signInitialTransaction(builder); subFlow(new FinalityFlow(ptx, Collections.<FlowSession>emptySet())); System.out.println("1"); return null; } } }
AlarmFlow代码
public class AlarmFlow { @InitiatingFlow @SchedulableFlow public static class AlarmFlowInitiator extends FlowLogic<Void> { private StateRef stateRef; //public constructor public AlarmFlowInitiator(StateRef stateRef) { this.stateRef = stateRef; } @Override @Suspendable public Void call() throws FlowException { ServiceHub sb = getServiceHub(); StateAndRef<TodoState> todoStateAndRef = sb.toStateAndRef(stateRef); TodoState todo = todoStateAndRef.getState().getData(); sb.getVaultService().addNoteToTransaction( stateRef.getTxhash(), "Reminder made: " + Instant.now() ); System.out.println("DeadLine is coming up for task: " + todo.getTaskDescription()); return null; } } }
执行错误信息
执行命令 flow start CreateTodoFlow task: "Pay bill" 时出现以下错误:
[ERROR] 23:48:19+0200 [Node thread-1] vault.NodeVaultService. - Failed to record transaction states locally - the node could be now in an inconsistent state with other peers and/or the notary - hospitalising the flow {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000001, flow-id=5eb282b3-4b47-459d-917d-06ea0de16e6f, invocation_id=aadb5b6a-4716-4875-a18e-78a351592365, invocation_timestamp=2022-09-19T21:48:19.052Z, origin=internalShell, session_id=b78e7e1d-b91f-4c40-b42e-468e7b5f4fb0, session_timestamp=2022-09-19T21:48:18.782Z, thread-id=139, tx_id=1BBDFF4EC549457D1C8D60E30041AE97436D44778675BEB2C78E737DBFFFE124}
解决方案
1. 修正Flow的全类名引用
在TodoState的nextScheduledActivity方法中,创建ScheduledActivity时指定的Flow路径错误。实际可执行的Flow是内部类AlarmFlowInitiator,需要使用包含内部类的全路径(用$分隔外部类和内部类):
final ScheduledActivity scheduledActivity = new ScheduledActivity(flowLogicRefFactory.create( "com.template.flows.AlarmFlow$AlarmFlowInitiator", thisStateRef ), deadlineReminder);
2. 确认@SchedulableFlow注解位置
确保@SchedulableFlow注解标注在**内部类AlarmFlowInitiator**上(你的代码已经正确标注,但需确认没有误标在外部类),这是Corda识别可调度Flow的必要条件。
3. 简化nextScheduledActivity方法逻辑
nextScheduledActivity会在事务记录阶段被调用,此时节点状态还未完全稳定,避免在这里打印日志或执行额外操作,保持逻辑简洁:
@Nullable @Override public ScheduledActivity nextScheduledActivity(@NotNull StateRef thisStateRef, @NotNull FlowLogicRefFactory flowLogicRefFactory) { return new ScheduledActivity( flowLogicRefFactory.create("com.template.flows.AlarmFlow$AlarmFlowInitiator", thisStateRef), deadlineReminder ); }
4. 验证Contract的事务约束
确保ToDoContract中正确实现了CreateToDoCommand对应的验证逻辑,比如:
- 事务只有一个输出状态(TodoState)
- 命令签名包含任务创建者的公钥
- 输出状态的参与者正确
如果Contract验证失败,也会导致事务无法被节点记录,进而触发该错误。
内容的提问来源于stack exchange,提问作者Andrea Mucci
相关产品推荐
相关产品推荐

