我正在编写一个简单的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;
}
}
谢谢!
答案 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当前版本