2.2 开发跨链智能合约
2.2.3 跨链智能合约方法示例
/* * preCommitSend is the first step of two-phase commit cross-chain transaction process, it will be executed on the client side and lock the related assets/data
* In this example, this function transfer X units from client's local
* account(first arg) to remote account(second arg) and lock the local account * The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account */func (t *TCSExampleChaincode) preCommitSend(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4") }
txID := args[3]
// Get account balance from blockchain account := args[0]
balanceBytes, err := stub.GetState(account) if err != nil {
balance, err := strconv.Atoi(string(balanceBytes)) if err != nil {
return shim.Error(err.Error()) }
// Get transfer amount from args amount, err := strconv.Atoi(args[2])
开发指南 2 跨链链代码开发(Hyperledger Fabric)
if err != nil {
return shim.Error(err.Error()) }
// Execute the business process balance = balance - amount
// Use specific function to put state for cross-chain transaction
err = putStateWithLock(stub, txID,account, []byte(strconv.Itoa(balance))) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
/* * preCommitRecv is the second step of two-phase cross-chain transaction process, it will be executed on the server side and lock the related assets/data
* In this example, this function will make local account(second arg) received * X units transferred by remote account(first arg) and lock the local account * The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account */func (t *TCSExampleChaincode) preCommitRecv(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4") }
txID := args[3]
// Get account balance from blockchain account := args[1]
balanceBytes, err := stub.GetState(account) if err != nil {
balance, err := strconv.Atoi(string(balanceBytes)) if err != nil {
return shim.Error(err.Error()) }
// Get transfer amount from args amount, err := strconv.Atoi(args[2]) if err != nil {
return shim.Error(err.Error()) }
// Execute the business process balance = balance + amount
// Use specific function to put state for cross-chain transaction
err = putStateWithLock(stub, txID, account, []byte(strconv.Itoa(balance))) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
/* * commitSend is the third step of two-phase cross-chain transaction process, it will be executed on the client side and unlock the related assets/data
* In this example, this function will unlock server side's local account(second arg), * so that it can be used by next cross-chain transaction
* The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account
*/func (t *TCSExampleChaincode) commitSend(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4") }
txID := args[3]
// unlock client side's local account account := args[0]
err := unlockAccount(stub, txID, account) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
/* * commitRecv is the forth step of two-phase cross-chain transaction process, it will be executed on the server side and unlock the related assets/data
开发指南 2 跨链链代码开发(Hyperledger Fabric)
* In this example, this function will unlock server side's local account(second arg), * so that it can be used by next cross-chain transaction
* The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account
*/func (t *TCSExampleChaincode) commitRecv(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3") }
txID := args[3]
// unlock server side's local account account := args[1]
err := unlockAccount(stub, txID, account) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
/* * rollbackSend is the rollback function for client side, it will be executed when error happened during the cross-chain tx process
* In this example, this function will recover the balance of client side's local account and unlock it, * so that it can be used by next cross-chain transaction
* The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account
*/func (t *TCSExampleChaincode) rollbackSend(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3") }
return shim.Success(nil) }
2.2.3.6 交易接收方回滚(rollbackRecv)
/* * rollbackRecv is the rollback function for server side, it will be executed when error happened during the cross-chain tx process
* In this example, this function will recover the balance of server side's local account and unlock it, * so that it can be used by next cross-chain tx
* The example args[] is {"a","b","1","txid123"}
* @Param args[0]: The name of the account on blockchain A that will transfer the amount of units to the account on blockchain B
* @Param args[1]: The name of the account on blockchain B that will receive the amount of units from the account on blockchainA
* @Param args[2]: The amount of unit that will be transferred from args[0](account on blockchain A) to args[1](account on blockchain B)
* @Param args[3]: The id of this transaction that transfer units of one account to another account
*/func (t *TCSExampleChaincode) rollbackRecv(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3") }
return shim.Success(nil) }
2.2.3.7 修改跨链资产数值(putStateWithLock)
在跨链资产交换涉及的智能合约方法中,所有对跨链资产的修改都必须与资产上锁同 时进行。可将上述逻辑封装至一个方法中,便于后续在其他智能合约方法(主要是 preCommitSend与preCommitRecv)中调用:
/* * putStateWithLock will update the account balance and lock the account
* @Param account: The name of the account that whose balance is going to be updated with lock * @Param balance: The amount of units that will be put to the account
* @Param txID: The id of this transaction that transfer units of one account to another account
*/func putStateWithLock(stub shim.ChaincodeStubInterface, txID string, account string, balance []byte) error { accountBytes, err := stub.GetState(account)
if err != nil {
accountLockKey := account + lockSuffix
accountLockBytes, err := stub.GetState(accountLockKey) if err != nil {
return fmt.Errorf("failed to get accountLock state: %v", err) }
accountLock := AccountLock{}
开发指南 2 跨链链代码开发(Hyperledger Fabric)
/*
* Detect the account lock's status * if account is not locked:
* 1. put lock to the blockchain ledger with current account balance * 2. update the account balance
* If account is locked:
* 1. is locked by the same txID, then return success
* 2. is not locked by this txID,then not allowed to update account balance */
accountLockBytes, err = json.Marshal(accountLock) if err != nil {
return fmt.Errorf("failed to marshal accountLockBytes: %v", err) }
err = stub.PutState(accountLockKey, accountLockBytes) if err != nil {
err = json.Unmarshal(accountLockBytes, &accountLock) if err != nil {
return fmt.Errorf("account %s locked", account) }
/* * unlockAccount will delete the account's lock from the blockchain * @Param account: The name of the account whose lock will be unlocked
* @Param txID: The id of this transaction that transfer units of one account to another account */func unlockAccount(stub shim.ChaincodeStubInterface, txID string, account string) error { // get account lock state with account lock's key
accountLockKey := account + lockSuffix
accountLockBytes, err := stub.GetState(accountLockKey) if err != nil {
err = json.Unmarshal(accountLockBytes, &accountLock) if err != nil {
return fmt.Errorf("failed to unmarshal accountLockBytes: %v", err) }
if accountLock.CrossTXID != txID {
return fmt.Errorf("not the owner of the lock")
/* * rollback function will recover the account balance to the previous status * @Param account: The name of the account whose balance will be rollback
* @Param txID: The id of this transaction that transfer units of one account to another account */func rollback(stub shim.ChaincodeStubInterface, txID string, account string) error {
// get server side's local account lock(with preValue inside) accountLockKey := account + lockSuffix
accountLockBytes, err := stub.GetState(accountLockKey) if err != nil {
return fmt.Errorf("failed to get accountLock state: %v", err) }
if accountLockBytes == nil { return nil
}
accountLock := AccountLock{}
err = json.Unmarshal(accountLockBytes, &accountLock) if err != nil {
return fmt.Errorf("failed to unmarshal accountLockBytes: %v", err) }
// recover the balance of server side's local account to the previous balance err = stub.PutState(account, []byte(accountLock.PreValue))
if err != nil {
return fmt.Errorf("failed to put state of preValue account: %v", err) }
// unlock server side's local account err = unlockAccount(stub, txID, account) if err != nil {
return fmt.Errorf("failed to unlock in rollbackRev: %v", err) }
return nil }
开发指南 2 跨链链代码开发(Hyperledger Fabric)