r3 Corda中帐户之间的令牌传输



我一直在尝试在r3 Corda中模拟银行系统。我的项目可以在这里找到。我的系统的组成部分是:

  • 中央银行[为其他银行发行代币]
  • BankA
  • 银行B
  • 公证人

我可以在系统中部署和运行节点。然后我就可以在这些银行里做早午餐了。以下命令已分别在BankA和BankB终端中运行:

flow start CreateAndShareAccountFlow accountName: brunchA1, partyToShareAccountInfoToList: [CentralBank, BankA, BankB]
flow start CreateAndShareAccountFlow accountName: brunchB1, partyToShareAccountInfoToList: [CentralBank, BankB, BankA]

我可以从中央银行的终端发行早午餐代币

start IssueCashFlow accountName : brunchA1 , currency : USD , amount : 80

现在,我尝试使用以下命令将标记从brunchA1移动到brunchB1。

start MoveTokensBetweenAccounts senderAccountName : brunchA1, rcvAccountName : brunchB1 , currency : USD , amount : 10

但是在BankA和BankB中运行vaultQuery后,它根本不会被转移!

run vaultQuery contractStateType : com.r3.corda.lib.tokens.contracts.states.FungibleToken

以下是我的MoveTokensBetweenAccounts:的代码片段

import co.paralleluniverse.fibers.Suspendable;
import com.r3.corda.lib.accounts.contracts.states.AccountInfo;
import com.r3.corda.lib.accounts.workflows.UtilitiesKt;
import com.r3.corda.lib.accounts.workflows.flows.RequestKeyForAccount;
import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
import com.r3.corda.lib.tokens.contracts.types.TokenType;
import com.r3.corda.lib.tokens.selection.TokenQueryBy;
import com.r3.corda.lib.tokens.selection.database.config.DatabaseSelectionConfigKt;
import com.r3.corda.lib.tokens.selection.database.selector.DatabaseTokenSelection;
import com.r3.corda.lib.tokens.workflows.flows.move.MoveTokensUtilities;
import com.r3.corda.lib.tokens.workflows.utilities.QueryUtilities;
import kotlin.Pair;
import net.corda.core.contracts.Amount;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.CommandWithParties;
import net.corda.core.contracts.StateAndRef;
import net.corda.core.flows.*;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party;
import net.corda.core.node.services.vault.QueryCriteria;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import java.security.PublicKey;
import java.util.*;

@StartableByRPC
@InitiatingFlow
public class MoveTokensBetweenAccounts extends FlowLogic<String> {
private final String senderAccountName;
private final String rcvAccountName;
private final String currency;
private final Long amount;
public MoveTokensBetweenAccounts(String senderAccountName, String rcvAccountName, String currency, Long amount) {
this.senderAccountName = senderAccountName;
this.rcvAccountName = rcvAccountName;
this.currency = currency;
this.amount = amount;
}
@Override
@Suspendable
public String call() throws FlowException {
AccountInfo senderAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(senderAccountName).get(0).getState().getData();
AccountInfo rcvAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(rcvAccountName).get(0).getState().getData();
AnonymousParty senderAccount = subFlow(new RequestKeyForAccount(senderAccountInfo));        AnonymousParty rcvAccount = subFlow(new RequestKeyForAccount(rcvAccountInfo));
Amount<TokenType> amount = new Amount(this.amount, getInstance(currency));
QueryCriteria queryCriteria = QueryUtilities.heldTokenAmountCriteria(this.getInstance(currency), senderAccount).and(QueryUtilities.sumTokenCriteria());
List<Object> sum = getServiceHub().getVaultService().queryBy(FungibleToken.class, queryCriteria).component5();
if(sum.size() == 0)
throw new FlowException(senderAccountName + " has 0 token balance. Please ask the Central Bank to issue some cash.");
else {
Long tokenBalance = (Long) sum.get(0);
if(tokenBalance < this.amount)
throw new FlowException("Available token balance of " + senderAccountName + " is less than the cost of the ticket. Please ask the Central Bank to issue some cash ");
}
Pair<AbstractParty, Amount<TokenType>> partyAndAmount = new Pair(rcvAccount, amount);
DatabaseTokenSelection tokenSelection = new DatabaseTokenSelection(
getServiceHub(),
DatabaseSelectionConfigKt.MAX_RETRIES_DEFAULT,
DatabaseSelectionConfigKt.RETRY_SLEEP_DEFAULT,
DatabaseSelectionConfigKt.RETRY_CAP_DEFAULT,
DatabaseSelectionConfigKt.PAGE_SIZE_DEFAULT
);
Pair<List<StateAndRef<FungibleToken>>, List<FungibleToken>> inputsAndOutputs =
tokenSelection.generateMove(Arrays.asList(partyAndAmount), senderAccount, new TokenQueryBy(), getRunId().getUuid());
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
TransactionBuilder transactionBuilder = new TransactionBuilder(notary);
MoveTokensUtilities.addMoveTokens(transactionBuilder, inputsAndOutputs.getFirst(), inputsAndOutputs.getSecond());
Set<PublicKey> mySigners = new HashSet<>();
List<CommandWithParties<CommandData>> commandWithPartiesList  = transactionBuilder.toLedgerTransaction(getServiceHub()).getCommands();
for(CommandWithParties<CommandData> commandDataCommandWithParties : commandWithPartiesList) {
if(((ArrayList<PublicKey>)(getServiceHub().getKeyManagementService().filterMyKeys(commandDataCommandWithParties.getSigners()))).size() > 0) {
mySigners.add(((ArrayList<PublicKey>)getServiceHub().getKeyManagementService().filterMyKeys(commandDataCommandWithParties.getSigners())).get(0));
}
}
FlowSession rcvSession = initiateFlow(rcvAccountInfo.getHost());
SignedTransaction selfSignedTransaction = getServiceHub().signInitialTransaction(transactionBuilder, mySigners);
subFlow(new FinalityFlow(selfSignedTransaction, Arrays.asList(rcvSession)));
return null;
}
public TokenType getInstance(String currencyCode) {
Currency currency = Currency.getInstance(currencyCode);
return new TokenType(currency.getCurrencyCode(), 0);
}
}
@InitiatedBy(MoveTokensBetweenAccounts.class)
class MoveTokensBetweenAccountsResponder extends FlowLogic<Void> {
private final FlowSession otherSide;
public MoveTokensBetweenAccountsResponder(FlowSession otherSide) {
this.otherSide = otherSide;
}
@Override
@Suspendable
public Void call() throws FlowException {
subFlow(new ReceiveFinalityFlow(otherSide));
return null;
}
}

在编写MoveTokensBetweenAccounts合同时,我是否遗漏了一些基本内容?我关注了官方Github Samples

任何实施代币运动的具体建议都将大有裨益!

谢谢!

  1. QueryUtilities.heldTokenAmountCriteria()不适用于帐户;它只适用于参与方,而必须使用以下内容:
// Query vault for balance.
QueryCriteria heldByAccount = new QueryCriteria.VaultQueryCriteria().withExternalIds(Collections.singletonList(accountInfo.getIdentifier().getId()));
QueryCriteria queryCriteria = QueryUtilitiesKt
// Specify token type and issuer.
.tokenAmountWithIssuerCriteria(tokenTypePointer, issuer)
// Specify account.
.and(heldByAccount)
// Group by token type and aggregate.
.and(QueryUtilitiesKt.sumTokenCriteria());
Vault.Page<FungibleToken> results = proxy.vaultQueryByCriteria(queryCriteria, FungibleToken.class);
Amount<TokenType> totalBalance = QueryUtilitiesKt.rowsToAmount(tokenTypePointer, results);
  1. 取决于代币的类型(可替代或不可替代(;我将使用addMoveFungibleTokens()addMoveNonFungibleTokens()而不是addMoveTokens()
  2. 老实说,我不明白你为什么使用效用函数(即DatabaseTokenSelection.generateMove()addMoveTokens()(;如果您的交易有多种类型的状态作为输入/输出(例如,汽车代币和美元代币(,并且您希望代币的交换是原子的(要么一切都成功,要么一切都失败(,则可以使用这些状态。在您的情况下,您的交易只有一种状态类型,即您的代币。你不需要那么复杂;只需使用开箱即用的代币SDK MoveFungibleTokensFlow
  3. 此外,在你的问题中,你没有分享你是如何发现代币没有移动的;你创建了一个流量测试吗?该测试是如何在搬家前后查询账户余额的
  4. 下面是一个关于在两个帐户之间移动代币的简单示例;在该示例中,唯一需要更改的是将这里的null替换为仅消耗发送者的令牌的查询条件(见下文(;否则,该移动将消耗源节点上持有的任何令牌(您最终可能会移动属于不同帐户的令牌;这就是为什么您必须指定该查询条件(:
// Query vault for balance.
QueryCriteria heldBySender = new QueryCriteria.VaultQueryCriteria().withExternalIds(Collections.singletonList(accountInfo.getIdentifier().getId()));
  1. 我强烈建议您阅读我关于代币SDK的文章,更重要的是;通过R3的官方免费Corda课程;他们有一个关于库(令牌和帐户(的大部分,请参阅此处
  2. 你也有一个打字错误,它是branch;而不是CCD_ 9

最新更新