具有相同linearId的Corda Vault查询返回三个未使用状态

时间:2018-06-20 14:31:07

标签: corda

我正在编写一个简单的cordapp来更新用户银行帐户的余额。以下是我更新某人帐户余额的步骤: 1.使用创建流程创建线性状态UserBalance。 2.通过检索我使用linearId产生的未消耗状态来更新状态的余额字段,并创建一个具有相同linearId但余额不同的新状态。 3.在库中查询UserBalance状态

问题: 创建状态后第一次查询保管库时,这就是我得到的:

{
  "states" : [ {
    "state" : {
      "data" : {
        "balance" : 0,
        "accountOwner" : "O=PartyB, L=New York, C=US",
        "linearId" : {
          "externalId" : null,
          "id" : "12800465-51bb-4700-9017-576bcecc6cec"
        },
        "participants" : [ "O=PartyB, L=New York, C=US", "O=PartyA, L=London, C=GB" ]
      },
      "contract" : "com.template.UserBalanceContract",
      "notary" : "O=Notary, L=London, C=GB",
      "encumbrance" : null,
      "constraint" : { }
    },
    "ref" : {
      "txhash" : "91B7429BE4E9B7E1579A12E764F601ED6912D432B6770E097F4EF027E145B17A",
      "index" : 0
    }
  } ],
  "statesMetadata" : [ {
    "ref" : {
      "txhash" : "91B7429BE4E9B7E1579A12E764F601ED6912D432B6770E097F4EF027E145B17A",
      "index" : 0
    },
    "contractStateClassName" : "com.template.UserBalance",
    "recordedTime" : 1529495492.423000000,
    "consumedTime" : null,
    "status" : "UNCONSUMED",
    "notary" : "O=Notary, L=London, C=GB",
    "lockId" : null,
    "lockUpdateTime" : null
  } ],
  "totalStatesAvailable" : -1,
  "stateTypes" : "UNCONSUMED",
  "otherResults" : [ ]
}

但是在更新流程(由于余额已在新状态下更新而成功运行)之后,我得到了以下奇怪的查询结果:

{
  "states" : [ {
    "state" : {
      "data" : {
        "balance" : 0,
        "accountOwner" : "O=PartyB, L=New York, C=US",
        "linearId" : {
          "externalId" : null,
          "id" : "12800465-51bb-4700-9017-576bcecc6cec"
        },
        "participants" : [ "O=PartyB, L=New York, C=US", "O=PartyA, L=London, C=GB" ]
      },
      "contract" : "com.template.UserBalanceContract",
      "notary" : "O=Notary, L=London, C=GB",
      "encumbrance" : null,
      "constraint" : { }
    },
    "ref" : {
      "txhash" : "91B7429BE4E9B7E1579A12E764F601ED6912D432B6770E097F4EF027E145B17A",
      "index" : 0
    }
  }, {
    "state" : {
      "data" : {
        "balance" : 0,
        "accountOwner" : "O=PartyB, L=New York, C=US",
        "linearId" : {
          "externalId" : null,
          "id" : "12800465-51bb-4700-9017-576bcecc6cec"
        },
        "participants" : [ "O=PartyB, L=New York, C=US", "O=PartyA, L=London, C=GB" ]
      },
      "contract" : "com.template.UserBalanceContract",
      "notary" : "O=Notary, L=London, C=GB",
      "encumbrance" : null,
      "constraint" : { }
    },
    "ref" : {
      "txhash" : "988F38AE9F0EAC26F8B5095A3E1540304A8DF3047B10D3228ADE40B4BF928978",
      "index" : 0
    }
  }, {
    "state" : {
      "data" : {
        "balance" : 5,
        "accountOwner" : "O=PartyB, L=New York, C=US",
        "linearId" : {
          "externalId" : null,
          "id" : "12800465-51bb-4700-9017-576bcecc6cec"
        },
        "participants" : [ "O=PartyB, L=New York, C=US", "O=PartyA, L=London, C=GB" ]
      },
      "contract" : "com.template.UserBalanceContract",
      "notary" : "O=Notary, L=London, C=GB",
      "encumbrance" : null,
      "constraint" : { }
    },
    "ref" : {
      "txhash" : "988F38AE9F0EAC26F8B5095A3E1540304A8DF3047B10D3228ADE40B4BF928978",
      "index" : 1
    }
  } ],
  "statesMetadata" : [ {
    "ref" : {
      "txhash" : "91B7429BE4E9B7E1579A12E764F601ED6912D432B6770E097F4EF027E145B17A",
      "index" : 0
    },
    "contractStateClassName" : "com.template.UserBalance",
    "recordedTime" : 1529495492.423000000,
    "consumedTime" : null,
    "status" : "UNCONSUMED",
    "notary" : "O=Notary, L=London, C=GB",
    "lockId" : null,
    "lockUpdateTime" : null
  }, {
    "ref" : {
      "txhash" : "988F38AE9F0EAC26F8B5095A3E1540304A8DF3047B10D3228ADE40B4BF928978",
      "index" : 0
    },
    "contractStateClassName" : "com.template.UserBalance",
    "recordedTime" : 1529495581.759000000,
    "consumedTime" : null,
    "status" : "UNCONSUMED",
    "notary" : "O=Notary, L=London, C=GB",
    "lockId" : null,
    "lockUpdateTime" : null
  }, {
    "ref" : {
      "txhash" : "988F38AE9F0EAC26F8B5095A3E1540304A8DF3047B10D3228ADE40B4BF928978",
      "index" : 1
    },
    "contractStateClassName" : "com.template.UserBalance",
    "recordedTime" : 1529495581.759000000,
    "consumedTime" : null,
    "status" : "UNCONSUMED",
    "notary" : "O=Notary, L=London, C=GB",
    "lockId" : null,
    "lockUpdateTime" : null
  } ],
  "totalStatesAvailable" : -1,
  "stateTypes" : "UNCONSUMED",
  "otherResults" : [ ]
}

结果是,当我尝试使用相同的linearId更新新状态时,我发现了3个具有相同linearId的未消耗状态。操作失败...

有人知道为什么会这样吗?以下是我的状态和流程代码:

package com.template;

import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.LinearState;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;

import java.util.List;

public class UserBalance implements LinearState {


    private int balance;

    private Party accountOwner;
    private Party bank;

    private final UniqueIdentifier linearId;

    public UserBalance(int balance, Party accountOwner, Party bank, UniqueIdentifier linearId){
        this.balance = balance;
        this.accountOwner = accountOwner;
        this.bank = bank;
        this.linearId = linearId;
    }

    public int getBalance(){
        return balance;
    }

    public Party getAccountOwner() {return accountOwner;}

    @Override
    public UniqueIdentifier getLinearId(){
        return linearId;
    }

    @Override
    public List<AbstractParty> getParticipants() {
        return ImmutableList.of(accountOwner, bank);
    }

}

package com.template;

import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndContract;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;

import java.security.PublicKey;
import java.util.List;


@InitiatingFlow
@StartableByRPC
public class CreateUserBalanceFlow extends FlowLogic<Void> {
    private final int balance;
    private final Party accountOwner;

    private final UniqueIdentifier linearId;

    private final ProgressTracker progressTracker = new ProgressTracker();

    // constructor
    public CreateUserBalanceFlow(int initialBalance, Party accountOwner, String linearId){
        this.balance = initialBalance;
        this.accountOwner = accountOwner;
        this.linearId = UniqueIdentifier.Companion.fromString(linearId);
    }

    @Override
    public ProgressTracker getProgressTracker(){
        return progressTracker;
    }


    /**
     * The flow logic is encapsulated within the call() method.
     */
    @Suspendable
    @Override
    public Void call() throws FlowException {
        final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);

        final TransactionBuilder txBuilder = new TransactionBuilder();
        txBuilder.setNotary(notary);

        //create transactionComponent

        //create output state
        UserBalance outputState = new UserBalance(balance, accountOwner, getOurIdentity(), linearId);
        StateAndContract outputStateAndContract = new StateAndContract(outputState, UserBalanceContract.UserBalance_Contract_ID);
        List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey());

        //create the create command
        Command cmd = new Command<>(new UserBalanceContract.Create(),requiredSigners);

        txBuilder.withItems(outputStateAndContract, cmd);

        txBuilder.verify(getServiceHub());

        final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

        subFlow(new FinalityFlow(signedTx));

        return null;

    }


}

package com.template;

import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndContract;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.vault.QueryCriteria;
import net.corda.core.contracts.StateAndRef;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;

import java.security.PublicKey;
import java.util.List;


@InitiatingFlow
@StartableByRPC
public class UpdateUserBalanceFlow extends FlowLogic<Void> {

    private final UniqueIdentifier linearId;
    private final int changeInBalance;

    private final ProgressTracker progressTracker = new ProgressTracker();

    //Constructor

    public UpdateUserBalanceFlow(String linearId, int changeInBalance){
        this.linearId = UniqueIdentifier.Companion.fromString(linearId);
        this.changeInBalance = changeInBalance;
    }

    @Override
    public ProgressTracker getProgressTracker(){
        return progressTracker;
    }

    @Override
    @Suspendable
    public Void call() throws FlowException {
        final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);

        final TransactionBuilder txBuilder = new TransactionBuilder();
        txBuilder.setNotary(notary);

        //retrieve the input state
        QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria(
                null,
                ImmutableList.of(linearId),
                Vault.StateStatus.UNCONSUMED,
                null);

        List<StateAndRef<UserBalance>> UserBalances = getServiceHub().getVaultService().queryBy(UserBalance.class, queryCriteria).getStates();
        if (UserBalances.size() != 1) {
            throw new FlowException(String.format(UserBalances.size() + UserBalances.toString()));
        }

        UserBalance inputUserBalance = UserBalances.get(0).getState().getData();

        //get the existing balance
        int existingBalance = inputUserBalance.getBalance();
        Party accountOwner = inputUserBalance.getAccountOwner();

        int newBalance = existingBalance + changeInBalance;


        //create output state
        UserBalance outputUserBalance = new UserBalance(newBalance, accountOwner, getOurIdentity(), linearId);

        //Build the transaction
        List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey());
        StateAndContract outputStateAndContract = new StateAndContract(outputUserBalance, UserBalanceContract.UserBalance_Contract_ID);
        StateAndContract inputStateAndContract = new StateAndContract(inputUserBalance, UserBalanceContract.UserBalance_Contract_ID);
        Command cmd = new Command<>(new UserBalanceContract.Update(),requiredSigners);

        txBuilder.withItems(inputStateAndContract, outputStateAndContract, cmd);

        txBuilder.verify(getServiceHub());

        final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

        subFlow(new FinalityFlow(signedTx));

        return null;

    }



}

谢谢!

1 个答案:

答案 0 :(得分:0)

    StateAndContract inputStateAndContract = new StateAndContract(inputUserBalance, UserBalanceContract.UserBalance_Contract_ID);

inputStateAndContract->实际上是Tx中的输出状态。

如果您查看TransactionBuilder中的withItems函数。

 StateAndContract -> addOutputState(t.state, t.contract) so all objects of type StateAndContract are acutally outputstate of a Tx not input

要向输入中添加状态,您需要传递StateAndRef

 is StateAndRef<*> -> addInputState(t)

所以要消耗状态传递状态并引用。在您的情况下,应为UserBalances.get(0),其类型为StateAndRef,指向您的UserBalance当前版本