区块链与DApp开发(学习总结)

0 区块链认知

开发区块链与Dapp所需知识

GO 语言: 联盟链、私有链的搭建,设立接口、监听
Solidity语言: 智能合约开发,区块链上的逻辑实现
Node.js : 监听、通信、RPC
JavaScript : Dapp应用程序开发

<学习资料>

  1. Linux: 《linux从入门到精通》
  2. JavaScript: 《JavaScript编程精解》
  3. 以太坊: 《深入理解以太坊》、《第一行代码以太坊》、以太坊源码
  4. Go: 《GO语言实战》《GO并发编程实战》
  5. Solidity与智能合约: 《Solidity编程》《深入以太坊智能合约开发》
  6. 区块链: 《GO语言公链开发实战》
  7. 网络与前端:《node.js区块链开发》(利用node深度使用区块链功能)《零基础学微信小程序开发》
  8. Dapp实战: 《区块链以太坊Dapp开发实战》(GO+solidity)、《区块链Dapp》(JS+Solidity)、《区块链开发实战实用案例分析》(案例应用多但需要基础 Solidity+JS)、《区块链智能合约与Dapp应用实战》(solidity+Java)

现在市面上的区块链书很多、很杂,学习难度也相差很大。在走了一些弯路后,本人翻阅了这些书的主要内容、记录了每本书的主要侧重点与讲述较好的地方,并给出如下的阅读建议。并非要全部通读,尤其在有了一定基础之后可直接阅读书中的核心部分。
有了此相对平滑的学习曲线,相信你我在自学的路上一定能越走越远。祝你成功!

建议学习顺序(我目前还在5):

 1. 《JavaScript编程精解》(掌握JS的基本编程)
 2. 《Solidity编程》(掌握智能合约的基本编程)
 3. 《区块链以太坊Dapp开发实战》 《GO语言实战》(参考) (学习GO的RPC接口链接以太坊)
 4. 《深入理解以太坊》(此时学习以太坊内部的细节更易于理解)
 5. 《第一行代码以太坊》(学习通过web3接口链接以太坊)
 6. 《区块链Dapp》(Dapp完整实战)
 7. 《区块链开发实战实用案例分析》(更复杂更多元的项目)《深入以太坊智能合约开发》(参考避免重复造轮子)
 8. 《node.js区块链开发》(开放了更多功能,不再局限于只使用接口)
 9. 《GO语言公链开发实战》《GO并发编程实战》(完全解开手脚,不再局限于别人的链,订制自己的联盟链、企业链)

实际应用项目

电子身份、证据认证系统
供应链上下游智能合约
网上拍卖软件
众筹平台
融资、股份认证等


<区块链知识>

部署私有链

Ganache
使用Ganache图形界面端或ganache-cli快速搭建私有链,使用图形界面可快速监控链上的实时交易。
给出一个win下exe格式的下载链接:ganache图形界面下载地址

账户管理

MetaMask
Mist钱包已经停用,使用轻度的MetaMask钱包管理主网、测试网、私有链中的账户

生成以太坊钱包

使用以太坊源码调用创建账户的函数,地址将保存在keystore下
再使用解码函数传入密码从中解出私钥(交易签名、导入其他钱包时使用)

//存储钱包文件的位置
	keydir := "./keystores"
	//调用源码
	ks := keystore.NewKeyStore(keydir,keystore.StandardScryptN,keystore.StandardScryptP)
	wallet,err := ks.NewAccount(password)

发布智能合约

remix:

  1. JavaScript VM虚拟机,测试合约函数功能使用
  2. 注入web3 向主网、测试网发布合约。此时在remix网页下打开metamask插件选择对应网络可自动切换
  3. web3自定义网络, 可连接到私有链

Truffle:

  1. 在truffle中新建工程init并导入到ganache的项目中
  2. 在配置文件中配置网络连接位置,一般为私有链测试
  3. sol文件复制进合约文件夹,同时编译文件夹中生成编译文件(仿写案例,注意文件名序号递增)
  4. build编译当前工程内的所有sol文件
  5. migrate部署智能合约进当前网络

给出Truffle官方文档https://www.trufflesuite.com/docs/truffle/overview


<智能合约知识>

编写智能合约

remix
使用网页版remix编写智能合约
中文版remix网站 http://remix.hubwiz.com/
英文版remix网站 http://remix.ethereum.org
建议使用英文版,因为中文版可能存在以太坊测试网络连不上的问题
配合atom保存到本地

事件与监听

智能合约无法自动触发,只能通过外部调用。所以智能合约只能发布事件,但不能监听。
发布的事件数据储存在该交易(函数调用)的日志中,如下图所示为满足ERC20协议的Transfer交易触发的交易事件。
区块链与DApp开发(学习总结)_第1张图片
LOG由address、Topic、Data三部分组成

  1. Address: 代表发布事件的合约地址
  2. Topic:第一个数据为事件触发的函数头的HASH值,如上图中数据内容为:sha3('Transfer(address,uint256)')
  3. Data:触发事件传出的参数,此事件触发后返回三个值为from,to,value(ERC20协议)

同时,在solidity中定义事件时给出indexed关键字则参数将保存在topic中,而不存在data中,更有利于筛选事件:

event Transfer(address indexed from,address indexed to, uint256 value)

则Topic将出现3行数据,分别为函数头hash,传出参数1 from,参数2 to

函数ID生成方法

用Keecak256算法给函数声明字符串进行hash,取结果的前四个字节:

bytes4(keccak256(Transfer(address,uint256)))

ERC20协议

加密货币协议,可用于股权证明、众筹证明等一切权益共识

ERC271协议

加密猫所用的协议,唯一个体币协议


<与区块链交互>

<<利用GO

查询区块链消息

开发环境 :GO

  1. 导入以太坊源码包、所有依赖包并配置gcc编译器
  2. rpcClient,err := rpc.DialHTTP(erc.NodeUrl)配置以太坊RPC接口

接口协议
采用json结构传输数据,如获取交易信息 methodName := "eth_getTransactionByHash"的输出结构:

type Transaction struct {
     
	Hash 		string 		`json: "hash"`
	Nonce 		string		`json: "nonce"`
	BlockHash 	string 		`json: "blockHash"`
	BlockNumber  string 		`json: "blockNumber"`
	TransactionIndex  string 		`json: "transactionIndex"`
	From 		string 		`json: "from"`
	To 			string 		`json: "to"`
	Value 		string 		`json: "value"`
	GasPrice 	string 		`json: "gasPrice"`
	Gas 		string 		`json: "gas"`
	Input 		string 		`json: "input"`

}

更多接口方法查询以太坊JSON RPC手册 http://cw.hubwiz.com/card/c/ethereum-json-rpc-api/1/3/19/

以太坊RPC接口nodeURL:
免费的节点调用网站:https://infura.io/
生成独立专属节点,比etherscan好用

eth_call访问合约函数

eth_call函数只用于访问不修改内存变量(view、pure)类的函数,因为eth_call的内部修改不会被广播,也不消耗Gas

访问接口

Call(&result,methodName,arg,"latest")

methodName 为“eth_call”

call()传入参数arg

type CallArg struct {
     
	From 	common.Address  `json: "from"`
 	To 		common.Address  `json: "to"`
	Gas 	string 			`json: "gas"`
	GasPrice string			`json: "gasPrice"`
	Value 	string			`json: "value"`
	Data 	string			`json: "data"`
	Nonce 	string			`json: "nonce"`

}

to为智能合约的地址
若出现GAS用尽的报错,将Gas值改为“0x1000000“,但并不会真的消耗gas

Data设置

data存储调用函数的信息和传入参数,分为以下2个部分:

  1. 调用该智能合约的函数ID

ID生成方法: 根据函数的ABI编码(从remix中复制)生成ID,如ERC20协议的balanceOf函数的ABI编码

contractABI := `[{
			"constant": true,
			"inputs": [
				{
					"name": "_owner",
					"type": "address"
				}
			],
			"name": "balanceOf",
			"outputs": [
				{
					"name": "_value",
					"type": "uint256"
				}
			],
			"payable": false,
			"stateMutability": "view",
			"type": "function"
		},
		{
			"constant": true,
			"inputs": [],
			"name": "symbol",
			"outputs": [
				{
					"name": "",
					"type": "string"
				}
			],
			"payable": false,
			"stateMutability": "view",
			"type": "function"
		}]`
	methodName := "balanceOf"

生成ID:

func MakeMethodID(methodName string,abistr string)(string,error){
     
	abi := &abi2.ABI{
     }
	//生成该函数实例
	err := abi.UnmarshalJSON([]byte(abistr))
	if err != nil{
     
		return "", err
	}

	method := abi.Methods[methodName]
	methodIdBytes := method.ID
	methodId := "0x"+common.Bytes2Hex(methodIdBytes)
	return methodId,nil
}

当然也可以通过remix编译合约后生成的JSON来得到函数ID,如某合约的各个ID:

"methodIdentifiers": {
     
			"allowed(address,address)": "5c658165",
			"approve(address,uint256)": "095ea7b3",
			"balance(address)": "e3d670d7",
			"balanceAll()": "42729c14",
			"balanceOf(address)": "70a08231",
			"decimals()": "313ce567",
			"gotCoin(address)": "3d492e56",
			"name()": "06fdde03",
			"symbol()": "95d89b41",
			"totalSupply()": "18160ddd",
			"transfer(address,address,uint256)": "beabacc8",
			"transfer(address,uint256)": "a9059cbb"
		}
  1. 传入参数

字符串类型:

arg1 := common.HexToHash("aa").String()[2:]

地址类型:

common.HexToHash(userAddress).String()[2:]

整型:

	//先传入确定真实值
	valueStr := "100"
	//真实值
	realValue := myTools.GetRealDecimalValue(valueStr,0)
	//转换为bigint类型
	realValueBig,_ := new(big.Int).SetString(realValue,10)
	//转换为以太坊所用的16进制hash类型
	arg := common.BytesToHash(realValueBig.Bytes()).String()[2:]
  1. 字符串组合
		Data: methodId + arg1 + arg2,

实现以太坊交易

发送数据

参数组装后需要通过私钥签名,签名过程

types.SignTx(tx, types.HomesteadSigner{
     }, unlockedKey.PrivateKey)

签名后进行RLP序列化以向其他节点传入数据:
https://segmentfault.com/a/1190000011763339

解读数据

  1. RLP反序列化 在decode.go中
  2. 参数校验+签名校验 在validate.go中
  3. 订单池判断,能否加入订单池 在tx_pool.go中

使用eth_sendRawTransaction发起交易(以太坊版本 1.6)

eth_sendRawTransaction可以实现如下三种功能:

  1. ETH转账
  2. 部署智能合约
  3. 调用智能合约(修改状态变量后能广播)

调用方法如下:

err = r.client.Getrpc().Call(&txHash,methodName,common.ToHex(txRlpData))

发送一个以太坊交易需要以下流程:

  1. 按标准构建交易结构体
  2. 拥有一个keystore存储的JSON格式钱包
  3. 解锁钱包
  4. 为交易签名
  5. RLP序列化
  6. 获取发送交易地址的nonce
  7. 发送交易

构建交易:

transaction := types.NewTransaction(nonce.Uint64(),to,amount,gasLimit_,gasPrice_, databytes)

以太坊源码:

func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
     
	return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
}
func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
     
	if len(data) > 0 {
     
		data = common.CopyBytes(data)
	}
	d := txdata{
     
		AccountNonce: nonce,
		Recipient:    to,
		Payload:      data,
		Amount:       new(big.Int),
		GasLimit:     new(big.Int),
		Price:        new(big.Int),
		V:            new(big.Int),
		R:            new(big.Int),
		S:            new(big.Int),
	}
	if amount != nil {
     
		d.Amount.Set(amount)
	}
	if gasLimit != nil {
     
		d.GasLimit.Set(gasLimit)
	}
	if gasPrice != nil {
     
		d.Price.Set(gasPrice)
	}

	return &Transaction{
     data: d}
}

交易结构体,确认数据类型:

type txdata struct {
     
	AccountNonce uint64          `json:"nonce"    gencodec:"required"`
	Price        *big.Int        `json:"gasPrice" gencodec:"required"`
	GasLimit     *big.Int        `json:"gas"      gencodec:"required"`
	Recipient    *common.Address `json:"to"       rlp:"nil"` // nil means contract creation
	Amount       *big.Int        `json:"value"    gencodec:"required"`
	Payload      []byte          `json:"input"    gencodec:"required"`

	// Signature values
	V *big.Int `json:"v" gencodec:"required"`
	R *big.Int `json:"r" gencodec:"required"`
	S *big.Int `json:"s" gencodec:"required"`

	// This is only used when marshaling to JSON.
	Hash *common.Hash `json:"hash" rlp:"-"`
}

注意:交易ETH时Payload为nil,调用函数时amount为0,部署合约时to为空
获取nonce

func (r *ETHERRPCRequester) GetNonce(address string)(*big.Int,error){
     
	methodName := "eth_getTransactionCount"
	nonce := ""
	err := r.client.Getrpc().Call(&nonce,methodName,address,"pending")
	if err != nil{
     
		return nil, err
	}
	//返回的是16进制 0x开头
	n,_ := new(big.Int).SetString(nonce[2:],16)
	return n,nil
}

序列化:

//rlp序列化
txRlpData,err := rlp.EncodeToBytes(signTx)

发送ETH或Token时需要进行换算:

//decimal为几,等于硬币最小单位到小数点后几位
func GetRealDecimalValue(value string,decimal int) string{
     
	if strings.Contains(value,"."){
     
		//存在小数
		arr := strings.Split(value,".")
		if len(arr) != 2{
     
			return ""
		}
		num := len(arr[1])
		left := decimal - num
		return arr[0] + arr[1] + strings.Repeat("0",left)
	}else {
     
		return value + strings.Repeat("0",decimal)
	}
}

实现监听

<<利用web.js

(持续更新)

你可能感兴趣的:(区块链,区块链,以太坊,智能合约)