# 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配置
![](https://img.kancloud.cn/4f/6e/4f6ee46317cd193ec9bc3c8ad06b212d_653x625.png)
如果程序是通过命令行参数运行的,配置文件参考如下:
~~~
{
"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,克隆存储库
![](https://img.kancloud.cn/6a/6d/6a6d0914b006932cce14f482c2d501cb_901x547.png)
刚刚建立的本地顶级文件夹,输入项目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可以多窗口运行。
![](https://img.kancloud.cn/59/27/59279a49d1aeae04d667c05b016fab17_2091x916.png)
每一个窗口都可以独立设置环境变量
如第一个窗口设置: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管理工具