You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:20:29