# 1、struct的序列化
struct中的變量命名必須以大寫字母開頭,否則在使用以下方法序列化時候,其值不會被序列化到[]byte之中,也就是說,反序列化后,其值會丟失。
~~~
func?(b? *Block)?Serialize()?[]byte?{
varresult?bytes.Buffer?//定義一個buffer存儲序列化后的數據
//初始化一個encoder,gob是標準庫的一部分
//encoder根據參數的類型來創建,這里將編碼為字節數組
encoder?:=?gob.NewEncoder(&result)
err?:=?encoder.Encode(b)?//編碼
if?err?!=?nil?{
????????log.Panic(err)?//如果出錯,將記錄log后,Panic調用,立即終止當前函數的執行
????}
return?result.Bytes()
}
~~~
# 2、VS-CODE的Debug配置

如果程序是通過命令行參數運行的,配置文件參考如下:
~~~
{
"version":?"0.2.0",
"configurations":?[
????????{
"name":?"Launch",
"type":?"go",
"request":?"launch",
"mode":?"auto",
"remotePath":?"",
"port":?5546,
"program":?"${fileDirname}",
"env":?{},
"args":?["createblockchain","-address","Ivan"]//main的執行命令:main createblockchain -address Ivan
//"args":?["printchain"]//main的執行命令:main printchain
????????}
????]
}
~~~
# 3、flag包是解析命令行參數輸入的很好的工具
(1)定義CLI處理器
~~~
typeCLIstruct{}
~~~
(2)檢測命令的合法性(普通檢查)
~~~
//validateArgs?校驗命令,如果無效,打印使用說明
//參數是否符合命令要求,不在這里進行檢查,而在執行命令前檢查
func?(cli?\*CLI)?validateArgs()?{
iflen(os.Args)?<?2?{?//所有命令至少有兩個參數,第一個是程序名稱,第二個是命名名稱
????????cli.printUsage()
????????os.Exit(1)
????}
}
~~~
(3)命令行使用說明
~~~
//printUsage?打印命令行幫助信息
func?(cli?\*CLI)?printUsage()?{
????fmt.Println("Usage:")
????fmt.Println("???getbalance?-address?ADDRESS??-?獲得某個地址的余額")
????fmt.Println("???createblockchain?-address?ADDRESS?-?創建一個新的區塊鏈并發送創始區塊獎勵給到address")
????fmt.Println("???printchain?-?打印區塊鏈中的所有區塊")
????fmt.Println("???send?-from?FROM?-to?To?-amount?-?發送amount數量的幣,從地址FROM到TO")
}
~~~
(4)Run?讀取命令行參數,執行相應的命令
~~~
//使用標準庫里面的?flag?包來解析命令行參數:
func?(cli?*CLI)?Run()?{
????cli.validateArgs()
//定義名稱為"getbalance"的空的flagset集合
getBalanceCmd?:=?flag.NewFlagSet("getbalance",?flag.ExitOnError)
//定義名稱為"createBlockchainCmd"的空的flagset集合
createBlockchainCmd?:=?flag.NewFlagSet("createblockchain",?flag.ExitOnError)
//定義名稱為"sendCmd"的空的flagset集合
sendCmd?:=?flag.NewFlagSet("send",?flag.ExitOnError)
//定義名稱為"printchain"的空的flagset集合
printChainCmd?:=?flag.NewFlagSet("printchain",?flag.ExitOnError)
//String用指定的名稱給getBalanceAddress?新增一個字符串flag
//以指針的形式返回getBalanceAddress
getBalanceAddress?:=?getBalanceCmd.String("address",?"",?"獲得金錢的地址")
createBlockchainAddress?:=?createBlockchainCmd.String("address",?"",?"接受挖出創始區塊獎勵的的地址")
sendFrom?:=?sendCmd.String("from",?"",?"錢包源地址")
sendTo?:=?sendCmd.String("to",?"",?"錢包目的地址")
sendAmount?:=?sendCmd.Int("amount",?0,?"轉移資金的數量")
//os.Args包含以程序名稱開始的命令行參數
switch?os.Args[1]?{?os.Args[0]為程序名稱,真正傳遞的參數index從1開始,一般而言Args\[1\]為命令名稱
case"getbalance":
//Parse調用之前,必須保證getBalanceCmd所有的flag都已經定義在其中
err?:=?getBalanceCmd.Parse(os.Args\[2:\])?//僅解析參數,不含命令
if?err?!=?nil?{
????????????log.Panic(err)
????????}
case"createblockchain":
err?:=?createBlockchainCmd.Parse(os.Args\[2:\])
if?err?!=?nil?{
????????????log.Panic(err)
????????}
case"printchain":
//Parse調用之前,必須保證addBlockCmd所有的flag都已經定義在其中
//根據命令設計,這里將返回nil,所以在前面沒有定義接收解析后數據的flag
//但printChainCmd的parsed=true
err?:=?printChainCmd.Parse(os.Args\[2:\])?//僅僅解析參數,不含命令
if?err?!=?nil?{
????????????log.Panic(err)
????????}
case"send":
err?:=?sendCmd.Parse(os.Args\[2:\])
if?err?!=?nil?{
????????????log.Panic(err)
????????}
default:
????????cli.printUsage()
????????os.Exit(1)
????}
if?getBalanceCmd.Parsed()?{
if?*getBalanceAddress?==?""?{
????????????getBalanceCmd.Usage()
????????????os.Exit(1)
????????}
????????cli.getBalance(\*getBalanceAddress)
????}
if?createBlockchainCmd.Parsed()?{
if?*createBlockchainAddress?==?""?{
????????????createBlockchainCmd.Usage()
????????????os.Exit(1)
????????}
????????cli.createBlockchain(\*createBlockchainAddress)
????}
if?printChainCmd.Parsed()?{
????????cli.printChain()
????}
if?sendCmd.Parsed()?{
if?*sendFrom?==?""?||?*sendTo?==?""?||?*sendAmount?<=?0?{
????????????sendCmd.Usage()
????????????os.Exit(1)
????????}
????????cli.send(\*sendFrom,?\*sendTo,?\*sendAmount)
????}
}
~~~
## 4、github與vs-code集成,同時打開多個github控制的項目
有時候,我們需要在一個vs-code界面上,同時打開多個受github控制的項目(每個項目為獨立的github倉庫),其做法如下:
(1)在本地磁盤建立一個頂級文件夾;
(2)打開VS-CODE,克隆存儲庫

剛剛建立的本地頂級文件夾,輸入項目github地址。
(3)VS-CODE關閉文件夾,然后打開剛建立的頂級文件夾
這時候,所有的子文件夾會自動導入進來了。
頂級文件夾下的各子文件夾對應于子項目,獨立進行github更新。
平時工作,除非建立分支,一般操作三板斧:Add(+)、Commit、Push,將本地修改同步到云端。
## 5、map類型的使用
map是go里面非常強大的類型,對于值為struct類型、數組或其他非常復雜的結構的key-value列表,非常適合使用它來進行存儲處理。
## 6、append的兩種用法
~~~
x := []int {1,2,3}
y := []int {4,5,6}
//注意下面這兩個區別
fmt.Println(append(x,4,5,6))
fmt.Println(append(x,y...));
~~~
第一種用法中,第一個參數為slice,后面可以添加多個參數,這種情況一般是將一個或多個元素加入到現有的數組之中:
x=append(x,4,5,6)
第二種用法,是將兩個slice拼接在一起,在第二個slice的名稱后面加三個點,構建一個新的數組,這時候append只支持兩個參數,不支持任意個數的參數。
z:=append(x,y...)
## 7、數組的切片引用方式:復用
我們一般在構建了數組實例后,通過函數或方法返回給調用者使用,一般不會直接返回數組指針,而是以切片形式,返回給調用者。這種方式是Go里面的普遍方式。函數或方法內部也是這么使用。
~~~
func checksum(payload []byte) []byte {
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])//引用firstSHA 數組的全部切片
return secondSHA[:addressChecksumLen]//返回用secondSHA的部分切片
}
~~~
## 8、判斷文件是否存在
~~~
if_,?err?:=?os.Stat(walletFile);?os.IsNotExist(err)?{
return?err
??}
~~~
## 9、[]byte轉為字符串:
hex.EncodeToString(prevTX.ID)//prevTX.ID為[]byte類型
## 10、break語句
(1)break//直接退出當前層次的循環
(2)break WORK//退出WORK標簽對應的代碼塊,標簽要求必須定義在對應的 for、switch 和 select 的代碼塊上
## 11、vs-code多命令窗口運行
vs-code可以多窗口運行。

每一個窗口都可以獨立設置環境變量
如第一個窗口設置:set NODE_ID=3000,第二個窗口設置環境變量:set NODE_ID=3001,第三個窗口設置為set NODE_ID=3002。
在命令行窗口運行服務后,如希望暫停,通過鍵盤上ctrl+c組合鍵執行。
## 12、go env
可以使用命令行查看 Go 開發包的環境變量配置信息,這些配置信息里可以查看到當前的 GOPATH 路徑設置情況。在命令行中運行`go env`。
## 13、gobEncode
go內置的gobEncode,可以將一個stuct轉換成一個[]byte數組,一個典型的用途是傳輸遠程過程調用(RPC)的參數和結果:
~~~
data?:=?block{nodeAddress,?b.Serialize()}
payload?:=?gobEncode(data)
~~~
~~~
func gobEncode(data?interface{})?[]byte?{
varbuff?bytes.Buffer
enc?:=?gob.NewEncoder(&buff)
err?:=?enc.Encode(data)
if?err?!=?nil?{
????????log.Panic(err)
????}
return?buff.Bytes()
}
~~~
與此對應的是## GobDecoder。
## 14、通道chan
chan 可以理解為隊列,遵循先進先出的規則,用于在不同goroutine之間通信。
```
// 聲明不帶緩沖的通道
ch1 := make(chan string)
// 聲明帶10個緩沖的通道
ch2 := make(chan string, 10)
// 聲明只讀通道
ch3 := make(<-chan string)
// 聲明只寫通道
ch4 := make(chan<- string)
```
注意:
不帶緩沖的通道,進和出都會阻塞。
帶緩沖的通道,進一次長度 +1,出一次長度 -1,如果長度等于緩沖長度時,再進就會阻塞。
### 寫入 chan
```
ch1 := make(chan string, 10)
ch1 <- "a"
```
### 讀取 chan
```
val, ok := <- ch1 // 或
val := <- ch1
```
### 關閉 chan
```
close(chan)
```
注意:
* close 以后不能再寫入,寫入會出現 panic
* 重復 close 會出現 panic
* 只讀的 chan 不能 close
* close 以后還可以讀取數據
- 重要更新說明
- linechain發布
- linechain新版設計
- 引言一
- 引言二
- 引言三
- vs-code設置及開發環境設置
- BoltDB數據庫應用
- 關于Go語言、VS-code的一些Tips
- 區塊鏈的架構
- 網絡通信與區塊鏈
- 單元測試
- 比特幣腳本語言
- 關于區塊鏈的一些概念
- 區塊鏈組件
- 區塊鏈第一版:基本原型
- 區塊鏈第二版:增加工作量證明
- 區塊鏈第三版:持久化
- 區塊鏈第四版:交易
- 區塊鏈第五版:實現錢包
- 區塊鏈第六版:實現UTXO集
- 區塊鏈第七版:網絡
- 階段小結
- 區塊鏈第八版:P2P
- P2P網絡架構
- 區塊鏈網絡層
- P2P區塊鏈最簡體驗
- libp2p建立P2P網絡的關鍵概念
- 區塊鏈結構層設計與實現
- 用戶交互層設計與實現
- 網絡層設計與實現
- 建立節點發現機制
- 向區塊鏈網絡請求區塊信息
- 向區塊鏈網絡發布消息
- 運行區塊鏈
- LineChain
- 系統運行流程
- Multihash
- 區塊鏈網絡的節點發現機制深入探討
- DHT
- Bootstrap
- 連接到所有引導節點
- Advertise
- 搜索其它peers
- 連接到搜到的其它peers
- 區塊鏈網絡的消息訂發布-訂閱機制深入探討
- LineChain:適用于智能合約編程的腳本語言支持
- LineChain:解決分叉問題
- LineChain:多重簽名
- libp2p升級到v0.22版本
- 以太坊基礎
- 重溫以太坊的樹結構
- 世界狀態樹
- (智能合約)賬戶存儲樹
- 交易樹
- 交易收據樹
- 小結
- 以太坊的存儲結構
- 以太坊狀態數據庫
- MPT
- 以太坊POW共識算法
- 智能合約存儲
- Polygon Edge
- block結構
- transaction數據結構
- 數據結構小結
- 關于本區塊鏈的一些說明
- UML工具-PlantUML
- libp2p介紹
- JSON-RPC
- docker制作:啟動多個應用系統
- Dockerfile
- docker-entrypoint.sh
- supervisord.conf
- docker run
- nginx.conf
- docker基礎操作整理
- jupyter計算交互環境
- git技巧一
- git技巧二
- 使用github項目的最佳實踐
- windows下package管理工具