Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

2.8. Hyperledger fabric 銀行應用探索

一直想寫這篇文章,可是我個人對銀行系統瞭解甚少,網上很多文章有多拿銀行來舉例,鋪天蓋地的文章,卻沒有一篇告訴你究竟如何落地。

其中不少文章中提到銀行SWIFT系統,什麼事 SWIFT 呢?

2.8.1. 電匯年代

這要從電匯說起,年輕時候上學,每個學期都有一筆學費,那時主要交通是鐵路,攜帶現金非常不便,母親將5000元人民幣縫在我的貼身內褲邊上。到了學校拆開綫取出,味道好極了,呵呵。

後來從同學那裡得知,可以使用郵局匯款,首先去郵局,拿一個特殊信封填好地址,然後將錢交給工作人員。一周以後信封皮會記掛號信到收款人手裡,那個這個信封去指定郵局取款。

記得第二學期就出現電匯,銀行提供的電匯服務比郵政的速度快,也比郵局更方便。

		
  用戶A ----- 匯票 -----> 用戶B
   |                     | 
  申請                   提款
   |                     |
   V                     V
  銀行A ----- 劃賬 -----> 銀行B		
		
		

電匯是用戶A銀行提出申請,將錢交給A銀行。銀行馬上通過網絡通知B銀行,用戶B就可以提款。沒有多久郵政的系統也換成這套系統了。

那個年代只有撥號網絡,幀中繼,ATM(是一種網絡,不是ATM取款機) 等窄帶網絡,現在用寬頻上網的90後無法想法那個網速。

2.8.2. 通存通取年代

ISDN,DDN 專線的出現,才有了穩定窄帶通信,銀行網點互聯成為可能。

MasterCard萬事達,Visa維薩卡率先進入中國。很快銀行內部就能實現轉賬了,ATM機互聯成為可能,這時結算全部使用 MasterCard萬事達,Visa維薩卡。銀行需向MasterCard萬事達,Visa維薩機構支付費用。促使一些有實力銀行研發自己的內部系統,但是這些系統僅限內部使用,如果銀行卡上沒有MasterCard/Visa 的只能本銀行使用,無法跨行。沒有實力的銀行則會選擇門檻稍低的 INTERLINK、PLUS 等機構合作。

98年在學校寢室,一位同學拿了一張牡丹萬事達卡,全寢室傳看,那個年代在哈爾濱提款機都不普及,只有較大的銀行網點才有。

2000來深圳,那時有個深銀聯,深圳首先實現了跨行業務,2年後(2002年)銀聯出現,國家強推,所有銀行開始支持銀聯。當時銀行卡上同時有兩個標示,MasterCard/Visa 和 銀聯。

同年招商銀行率先推出網上銀行。招商銀行電腦部一度成為IT紅黑榜上評價最高的公司,可惜沒有跟上互聯網的步伐......

目前新開的銀行卡已經看不到 MasterCard/Visa 標誌了,甚至香港的銀行卡也預設使用銀聯,出國旅遊耍刷 UnionPay 跨境支付非常方便。

2.8.3. 跨境匯款

跨境匯款你別想像銀聯一樣,填寫一個姓名一個賬號,點擊一下轉賬按鈕這樣的操作。每個國家的政策也不同,政策不允許這樣操作。

跨境只能匯款,無法轉賬,又回到了電匯時代,只是不再主要紙質的匯票了

		
  用戶A ----- SWIFT CODE -----> 用戶B
   |                             | 
  申請                           提款
   |                             |
   V                             V
  銀行A -------- SWIFT --------> 銀行B		
		
		

跨境匯款必須依賴 SWIFT 系統,由於我國的政策問題,個人很少涉及跨境業務,所以多數人對 SWIFT 不是很瞭解。 如果你在香港開一張銀行卡例如匯豐銀行,拿到銀行給的信封裡,就會有一個 SWIFT 碼。

你會遇到這個問題,無法輸入對方的名字,例如:

		
Nickname:netkiller
English name: Neo chen 
Nippon name: ちんけいほう (音訳) 
Korean name: 천징봉
Thailand name: ภูมิภาพภูเขา
Vietnam: Trần Cảnh Phong
		
		

所以就需要每個銀行一個代碼,每個賬號一個代碼,由於全世界銀行太多,銀行系統各種各樣,每個國家的語言也不同,難以達成一致,聯合國也沒有能力統一標準。新建一套系統不太可能,所以80年代的標準仍然沿用至今。

使用 SWIFT 面臨的問題

  • 網絡速度慢

  • 手續費高

  • 技術落後

  • 不能實時到賬

  • 脆弱容易被攻擊

SWIFT的誕生甚至早于數字時代,可以說是目前最好的跨境通訊和交易系統,但它的確需要與時俱進。

牽頭做一個世界銀聯不太可能,世界各國銀行無法想政府一樣,一個紅標頭檔,下面招辦,行政手段推動。且業務差異大,系統複雜超乎想象,這個中心資料庫誰來管理呢?

SWIFT早就意識到了這些問題,並宣佈進軍區塊鏈,同時加入超級賬本項目(Hyperledger Project)成為會員。可以肯定下一個版本的SWIFT灰使用區塊鏈技術,一步一步逐漸取代就系統。

2.8.4. 區塊鏈能做什麼

區塊鏈可以解決銀行哪些痛點,先說說 SWIFT 2.0 (區塊鏈SWIFT)我想SWIFT仍然會兼容現有的協議。SWIFT CODE協議仍然會保留。短時間不可能被取代SWIFT CODE因為這個體系已經使用很多年。

		
  用戶A ----- SWIFT CODE -----> 用戶B
   |                             | 
  申請                           提款
   |                             |
   V                             V
  銀行A -------- 劃賬  --------> 銀行B		
    \                            /
     \                          /
      V                        V
  +---------------------------------+
  | Hyperledger Fabric 盟鏈          |
  +---------------------------------+
  | Smart Contract                  |
  +---------------------------------+
		
		

後端將會被區塊鏈取代

另外銀行的跨國業務將會走自己的區塊鏈,不再依賴 SWIFT, 因為費用等問題。

		
  用戶A --------- 轉賬 ---------> 用戶B
   |                             | 
  申請                           提款
   |                             |
   V                             V
英國渣打銀行 ----- 劃賬  -----> 深圳渣打銀行		
    \                            /
     \                          /
      V                        V
  +---------------------------------+
  | Hyperledger Fabric 盟鏈          |
  +---------------------------------+
  | Smart Contract                  |
  +---------------------------------+
		
		

2.8.5. 智能合約怎麼落地

我對銀行業務實在不瞭解,這裡只能設想一下場景。下面是我之前寫的一個Token合約,我將它應用到這個場景中

		
package main 

/* 
--------------------------------------------------
Author: netkiller <netkiller@msn.com>
Home: http://www.netkiller.cn
Data: 2018-03-20 11:00 PM
--------------------------------------------------
CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=token3:1.0 chaincode/token/token3
peer chaincode install -n token3 -v 1.0 -p chaincodedev/chaincode/token
peer chaincode instantiate -C myc -n token3 -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["coinbase"]}'
peer chaincode invoke -C myc -n token3 -v 1.0 -c '{"function":"showAccount","Args":["coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Netkiller Token","NKC","1000000","coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["NEO Token","NEC","1000000","coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"setLock","Args":["true"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"setLock","Args":["false"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"mintToken","Args":["NKC","5000","coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["netkiller"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","NKC","100"]}'		
peer chaincode invoke -C myc -n token3 -c '{"function":"balance","Args":["netkiller","NKC"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"frozenAccount","Args":["netkiller","true"]}'
--------------------------------------------------
*/

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

type Msg struct{
	Status 	bool	`json:"Status"`
	Code 	int		`json:"Code"`
	Message string	`json:"Message"`
}

type Currency struct{
	TokenName 		string	`json:"TokenName"`
	TokenSymbol 	string	`json:"TokenSymbol"`
	TotalSupply 	float64	`json:"TotalSupply"`
}

type Token struct {
	Lock		bool	`json:"Lock"`
	Currency	map[string]Currency	`json:"Currency"`
}

func (token *Token) transfer (_from *Account, _to *Account, _currency string, _value float64) []byte{

	var rev []byte
	if (token.Lock){
		msg := &Msg{Status: false, Code: 0, Message: "鎖倉狀態,停止一切轉賬活動"}
		rev, _ = json.Marshal(msg)
		return rev
	}
	if(_from.Frozen ) {
		msg := &Msg{Status: false, Code: 0, Message: "From 賬號凍結"}
		rev, _ = json.Marshal(msg)
		return rev
	}
	if( _to.Frozen) {
		msg := &Msg{Status: false, Code: 0, Message: "To 賬號凍結"}
		rev, _ = json.Marshal(msg)
		return rev
	}
	if(!token.isCurrency(_currency)){
		msg := &Msg{Status: false, Code: 0, Message: "貨幣符號不存在"}
		rev, _ = json.Marshal(msg)
		return rev
	}
	if(_from.BalanceOf[_currency] >= _value){
		_from.BalanceOf[_currency] -= _value;
		_to.BalanceOf[_currency] += _value;

		msg := &Msg{Status: true, Code: 0, Message: "轉賬成功"}
		rev, _ = json.Marshal(msg)
		return rev
	}else{
		msg := &Msg{Status: false, Code: 0, Message: "餘額不足"}
		rev, _ = json.Marshal(msg)
		return rev
	}
	
}
func (token *Token) initialSupply(_name string, _symbol string, _supply float64, _account *Account) []byte{
	if _,ok := token.Currency[_symbol]; ok {
		msg := &Msg{Status: false, Code: 0, Message: "代幣已經存在"}
		rev, _ := json.Marshal(msg)
		return rev
	}

	if _account.BalanceOf[_symbol] > 0 {
		msg := &Msg{Status: false, Code: 0, Message: "賬號中存在代幣"}
		rev, _ := json.Marshal(msg)
		return rev
	}else{
		token.Currency[_symbol] = Currency{TokenName: _name, TokenSymbol: _symbol, TotalSupply: _supply}
		_account.BalanceOf[_symbol] = _supply

		msg := &Msg{Status: true, Code: 0, Message: "代幣初始化成功"}
		rev, _ := json.Marshal(msg)
		return rev
	}

}

func (token *Token) mint(_currency string, _amount float64, _account *Account) []byte{
	if(!token.isCurrency(_currency)){
		msg := &Msg{Status: false, Code: 0, Message: "貨幣符號不存在"}
		rev, _ := json.Marshal(msg)
		return rev
	}
	cur := token.Currency[_currency]
	cur.TotalSupply += _amount;
	token.Currency[_currency] = cur
	_account.BalanceOf[_currency] += _amount;

	msg := &Msg{Status: true, Code: 0, Message: "代幣增發成功"}
	rev, _ := json.Marshal(msg)
	return rev
	
}
func (token *Token) burn(_currency string, _amount float64, _account *Account) []byte{
	if(!token.isCurrency(_currency)){
		msg := &Msg{Status: false, Code: 0, Message: "貨幣符號不存在"}
		rev, _ := json.Marshal(msg)
		return rev
	}
	if(token.Currency[_currency].TotalSupply >= _amount){
		cur := token.Currency[_currency]
		cur.TotalSupply -= _amount;
		token.Currency[_currency] = cur
		_account.BalanceOf[_currency] -= _amount;

		msg := &Msg{Status: false, Code: 0, Message: "代幣回收成功"}
		rev, _ := json.Marshal(msg)
		return rev
	}else{
		msg := &Msg{Status: false, Code: 0, Message: "代幣回收失敗,回收額度不足"}
		rev, _ := json.Marshal(msg)
		return rev
	}
	
}
func (token *Token) isCurrency(_currency string) bool {
	if _, ok := token.Currency[_currency]; ok {
		return true
	}else{
		return false
	}
}
func (token *Token) setLock(_look bool) bool {
	token.Lock = _look
	return token.Lock
}
type Account struct {
	Name			string	`json:"Name"`
	Frozen			bool	`json:"Frozen"`
	BalanceOf		map[string]float64	`json:"BalanceOf"`
}
func (account *Account) balance (_currency string) map[string]float64{
	bal	:= map[string]float64{_currency:account.BalanceOf[_currency]}
	return bal
}

func (account *Account) balanceAll() map[string]float64{
	return account.BalanceOf
}

// -----------
const TokenKey = "Token"

// Define the Smart Contract structure
type SmartContract struct {
	
}

func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) pb.Response {

	token := &Token{Currency: map[string]Currency{}}

	tokenAsBytes, err := json.Marshal(token)
	err = stub.PutState(TokenKey, tokenAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Init Token %s \n", string(tokenAsBytes))
	}
	return shim.Success(nil)
}

func (s *SmartContract) Query(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	if function == "balance" {
		return s.balance(stub, args)
	} else if function == "balanceAll" {
		return s.balanceAll(stub, args)
	} else if function == "showAccount" {
		return s.showAccount(stub, args)
	}
	return shim.Error("Invalid Smart Contract function name.")
}

func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response {

	// Retrieve the requested Smart Contract function and arguments
	function, args := stub.GetFunctionAndParameters()
	// Route to the appropriate handler function to interact with the ledger appropriately
	if function == "initLedger" {
		return s.initLedger(stub, args)
	} else if function == "createAccount" {
		return s.createAccount(stub, args)
	} else if function == "initCurrency" {
		return s.initCurrency(stub, args)
	} else if function == "setLock" {
		return s.setLock(stub, args)
	} else if function == "transferToken" {
		return s.transferToken(stub, args)
	} else if function == "frozenAccount" {
		return s.frozenAccount(stub, args)
	} else if function == "mintToken" {
		return s.mintToken(stub, args)
	} else if function == "balance" {
		return s.balance(stub, args)
	} else if function == "balanceAll" {
		return s.balanceAll(stub, args)
	} else if function == "showAccount" {
		return s.showAccount(stub, args)
	} else if function == "showToken" {
		return s.showToken(stub, args)
	}

	return shim.Error("Invalid Smart Contract function name.")
}

func (s *SmartContract) createAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	key  := args[0]
	name  := args[0]
	existAsBytes,err := stub.GetState(key)
	fmt.Printf("GetState(%s) %s \n", key, string(existAsBytes))
	if string(existAsBytes) != "" {
		fmt.Println("Failed to create account, Duplicate key.")
		return shim.Error("Failed to create account, Duplicate key.")
	}

	account := Account{
		Name: name,
		Frozen: false,
		BalanceOf: map[string]float64{}}

	accountAsBytes, _ := json.Marshal(account)
	err = stub.PutState(key, accountAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("createAccount %s \n", string(accountAsBytes))

	return shim.Success(accountAsBytes)
}
func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	return shim.Success(nil)
}
func (s *SmartContract) showToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	tokenAsBytes,err := stub.GetState(TokenKey)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("GetState(%s)) %s \n", TokenKey, string(tokenAsBytes))
	}
	return shim.Success(tokenAsBytes)
}

func (s *SmartContract) initCurrency(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	_name  := args[0]
	_symbol:= args[1]
	_supply,_:= strconv.ParseFloat(args[2], 64)
	_account := args[3]

	coinbaseAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes))

	coinbase := &Account{}

	json.Unmarshal(coinbaseAsBytes, &coinbase)

	token := Token{}
	existAsBytes,err := stub.GetState(TokenKey)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("GetState(%s)) %s \n", TokenKey, string(existAsBytes))
	}
	json.Unmarshal(existAsBytes, &token)
	
	result := token.initialSupply(_name,_symbol,_supply, coinbase)

	tokenAsBytes, _ := json.Marshal(token)
	err = stub.PutState(TokenKey, tokenAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Init Token %s \n", string(tokenAsBytes))
	}

	coinbaseAsBytes, _ = json.Marshal(coinbase)
	err = stub.PutState(_account, coinbaseAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes))

	

	return shim.Success(result)
}

func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}
	_from 		:= args[0]
	_to			:= args[1]
	_currency 	:= args[2]
	_amount,_	:= strconv.ParseFloat(args[3], 32)
	
	if(_amount <= 0){
		return shim.Error("Incorrect number of amount")
	}

	fromAsBytes,err := stub.GetState(_from)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("fromAccount %s \n", string(fromAsBytes))
	fromAccount := &Account{}
	json.Unmarshal(fromAsBytes, &fromAccount)

	toAsBytes,err := stub.GetState(_to)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("toAccount %s \n", string(toAsBytes))
	toAccount := &Account{}
	json.Unmarshal(toAsBytes, &toAccount)

	tokenAsBytes,err := stub.GetState(TokenKey)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Token %s \n", string(toAsBytes))
	token := Token{Currency: map[string]Currency{}}
	json.Unmarshal(tokenAsBytes, &token)

	result := token.transfer(fromAccount, toAccount, _currency, _amount)
	fmt.Printf("Result %s \n", string(result))

	fromAsBytes, err = json.Marshal(fromAccount)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(_from, fromAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("fromAccount %s \n", string(fromAsBytes))
	}

	toAsBytes, err = json.Marshal(toAccount)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(_to, toAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("toAccount %s \n", string(toAsBytes))
	}

	return shim.Success(result)
}
func (s *SmartContract) mintToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}
	_currency 	:= args[0]
	_amount,_	:= strconv.ParseFloat(args[1], 32)
	_account	:= args[2]

	coinbaseAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes))
	}

	coinbase := &Account{}
	json.Unmarshal(coinbaseAsBytes, &coinbase)

	tokenAsBytes,err := stub.GetState(TokenKey)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Token before %s \n", string(tokenAsBytes))

	token := Token{}

	json.Unmarshal(tokenAsBytes, &token)
	
	result := token.mint(_currency, _amount, coinbase)

	tokenAsBytes, err = json.Marshal(token)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(TokenKey, tokenAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Token after %s \n", string(tokenAsBytes))

	coinbaseAsBytes, _ = json.Marshal(coinbase)
	err = stub.PutState(_account, coinbaseAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes))
	}

	fmt.Printf("mintToken %s \n", string(tokenAsBytes))

	return shim.Success(result)
}

func (s *SmartContract) setLock(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	_look := args[0]

	tokenAsBytes,err := stub.GetState(TokenKey)
	if err != nil {
		return shim.Error(err.Error())
	}
	// fmt.Printf("setLock - begin %s \n", string(tokenAsBytes))

	token := Token{}

	json.Unmarshal(tokenAsBytes, &token)

	if(_look == "true"){
		token.setLock(true)
	}else{
		token.setLock(false)
	}

	tokenAsBytes, err = json.Marshal(token)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(TokenKey, tokenAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("setLock - end %s \n", string(tokenAsBytes))

	return shim.Success(nil)
}
func (s *SmartContract) frozenAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	
	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	_account	:= args[0]
	_status		:= args[1]

	accountAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}
	// fmt.Printf("setLock - begin %s \n", string(tokenAsBytes))

	account := Account{}

	json.Unmarshal(accountAsBytes, &account)

	var status bool
	if(_status == "true"){
		status = true;
	}else{
		status = false
	}

	account.Frozen = status
	
	accountAsBytes, err = json.Marshal(account)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(_account, accountAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("frozenAccount - end %s \n", string(accountAsBytes))
	}

	return shim.Success(nil)
}

func (s *SmartContract) showAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	_account 	:= args[0]

	accountAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Account balance %s \n", string(accountAsBytes))
	}
	return shim.Success(accountAsBytes)
}

func (s *SmartContract) balance(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	_account 	:= args[0]
	_currency 	:= args[1]

	accountAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Account balance %s \n", string(accountAsBytes))
	}

	account := Account{}
	json.Unmarshal(accountAsBytes, &account)
	result := account.balance(_currency)

	resultAsBytes, _ := json.Marshal(result)
	fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes))	

	return shim.Success(resultAsBytes)
}

func (s *SmartContract) balanceAll(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	_account 	:= args[0]

	accountAsBytes,err := stub.GetState(_account)
	if err != nil {
		return shim.Error(err.Error())
	}else{
		fmt.Printf("Account balance %s \n", string(accountAsBytes))
	}

	account := Account{}
	json.Unmarshal(accountAsBytes, &account)
	result := account.balanceAll()
	resultAsBytes, _ := json.Marshal(result)
	fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes))	

	return shim.Success(resultAsBytes)
}

// The main function is only relevant in unit test mode. Only included here for completeness.
func main() {

	// Create a new Smart Contract
	err := shim.Start(new(SmartContract))
	if err != nil {
		fmt.Printf("Error creating new Smart Contract: %s", err)
	}
}
		
		

部署鏈碼,然後實例化鏈碼

		
peer chaincode install -n token3 -v 1.0 -p chaincodedev/chaincode/token
peer chaincode instantiate -C myc -n token3 -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')"		
		
		

首先初始化賬號,需要將現有賬號同步到區塊鏈上,這是上鏈操作。賬號分為兩種,一個是 coinbase 銀行的總賬號,另外是用戶賬號

		
peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["coinbase"]}'		
		
		

初始化外幣,銀行有多少外幣存量,初始化到 coinbase 賬號

		
peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Chain","RMB","1000000000","coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Japan","JPY","1000000000","coinbase"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["USA","USD","1000000000","coinbase"]}'		
		
		

為用戶創建賬號

		
peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["netkiller"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["neo"]}'	
		
		

同步用戶賬號中的外幣

		
peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","RMB","10000"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","USD","100"]}'


peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","neo","RMB","10000"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","neo","USD","100"]}'	
		
		

現在區塊鏈上的用戶資金已經跟資料庫中的資金同步。

現在netkiller這個用戶需要給neo轉賬 50 美金

		
peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["netkiller","neo","USD","50"]}'	
		
		

現在 netkiller 賬號中又 50美金,neo 賬號中有 150美金。

		
peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["netkiller"]}'
peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["neo"]}'
		
		

如果 neo 賬號被法院申請凍結了,怎麼辦?可以使用下面方法設置凍結賬號

		
peer chaincode invoke -C myc -n token3 -c '{"function":"frozenAccount","Args":["neo","true"]}'
		
		

此時 neo 賬號被凍結,資金無法轉入,轉出。

2.8.6. 總結

以上僅僅是提供一個區塊鏈學歷案例,作者也設想了一個場景。但是真是情況怎樣作者也不清楚。

上面通正合約沒有考慮到匯率和中間價問題,需要進一步完善。

另外還需要一個守護進程訂閲 Event 狀態,每做一筆轉賬交易,event 會受到然後將這個操作同步到中心化資料庫。

		
         用戶A ------------- 轉賬 ------------> 用戶B
           |                                    | 
          申請                                 提款
           |                                    |
           V                                    V
       英國渣打銀行 --------- 劃賬  ---------> 深圳渣打銀行		
       /        \                            /      \
      /          \                          /        \
     V            V                        V          V
+----------+  +---------------------------------+  +----------+ 
|          |  | Hyperledger Fabric 盟鏈          |  |          |
| Database |  +---------------------------------+  | Database |
|          |  | Smart Contract                  |  |          |
+----------+  +---------------------------------+  +----------+ 
     ^                 o               o                 ^
     |                 |               |                 |
     +----- Event -----+               +----- Event -----+