• 沒有找到結果。

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)

3 跨链交易

相關文件