0 需求及原理
如果某盟主基于geth构建了一条联盟链,有许多节点加入进来一起玩。由于智能合约的强大特性,如果每个节点都许仙志的发布智能合约,可能会造成江湖群魔乱舞。现在盟主想限制只有加入了白名单的节点才能发布智能合约,应该怎么做呢?
以太坊的核心是交易(Transaction)。转账、发布智能合约、调用合约等等都是交易。在用户通过consol控制台或客户端向geth提交一笔交易时,geth会先运行交易验证函数对交易进行验证,如果不通过就会直接在控制台打印错误信息,从而阻止交易提交给区块链。在验证函数里面增加一个创建智能合约许可的验证,可以解决开篇提到的问题。
tx_pool.go中的validateTx函数时最重要的交易验证函数。可以在该函数中向中心服务器请求可以发布智能合约的账户的白名单列表,如果发起智能合约发布交易的账户不在这个白名单中,则返回错误。
1 go语言Http请求函数
//lzj add
func RequestContractAddrs() ([]string){
log.Info("requestContractAddrs now")
postString := ""
//创建请求
postReq, err := http.NewRequest("POST",
"http://192.168.1.20/api/whitelist/get", //post链接
strings.NewReader(postString)) //post内容
if err != nil {
log.Info("POST请求:创建请求失败", err)
return nil
}
//增加header
postReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
//postReq.Header.Add("x-requested-with", "XMLHttpRequest")
//执行请求
client := &http.Client{}
resp, err := client.Do(postReq)
if err != nil {
log.Info("POST请求:创建请求失败", err)
return nil
}
//读取响应
body, err := ioutil.ReadAll(resp.Body) //此处可增加输入过滤
if err != nil {
log.Info("POST请求:读取body失败", err)
return nil
}
json := string(body)
log.Info("json:","json",json)
code := gjson.Get(json,"code")
codeString:=code.String()
codeInt,err1:= strconv.Atoi(codeString)
if(err1 != nil){
log.Info("str conver to interger fail")
return nil;
}
if(codeInt == 0){
var arr = []string{}
addrs := gjson.Get(json,"data.#.addr")
for _, addr := range addrs.Array() {
arr = append(arr, addr.String())
}
return arr
}
defer resp.Body.Close()
return nil
}
这个函数首先通过Http向服务器发送Post请求来得到数据,服务器会返回一个地址列表的json,形式如:
{"code":0,"msg":"成功","data":[{"addr":"0x0000c7d4809663be639be69cd76b427f57a52bcc2044"},{"addr":"0x000092cd138437787e6f11b3e6b3b9e7e25f5b424262"}]}
解析这个json应用了一个json库(见https://github.com/tidwall/gjson)。然后返回解析得到的地址列表。
2 validateTx函数修改
validateTx函数的末尾加上验证代码:
if(tx.To() == nil){
containFlag := false
addrs := RequestContractAddrs()
if(addrs != nil){
for i:=0;i<len(addrs);i++{
if(strings.ToLower(addrs[i]) == strings.ToLower(from.String())){
containFlag = true
break;
}
}
}
if(!containFlag){
return ErrForbiddenCreateContract
}
}
这段代码判断是否是发布智能合约的交易(if(tx.To() == nil),如果是,先向中心服务器请求获得交易白名单,然后判断当前发布者账号是否在白名单中,如果不在,就抛出错误。
3 实验验证
现在白名单设定是addr0、addr1俩个,在truffle中先让addr0来发布智能合约。
geth端接收到发布交易了:
现在换成用add2来发布智能合约,truffle端打印了"Error: Forbidden create contract":
geth端没有接收到交易: