总结超级账本合约开发过程中遇到的坑

  1. 不要尝试获取节点本地的时间作为key或value的值 即使每个节点的本地的时间系统是经过校对一致的,但同一个交易在每个节点上执行的时间点不一定是相同的。如果使用time.now().unix()来取...

1. 不要尝试获取节点本地的时间作为key或value的值

即使每个节点的本地的时间系统是经过校对一致的,但同一个交易在每个节点上执行的时间点不一定是相同的。如果使用time.now().unix()来取节点本地的时间作为key或value的值,会导致每个节点执行同一个交易的结果不一致,从而造成交易失败。正确的做法是使用函数GetTxTimestamp()来获取交易的时间。每个节点通过函数GetTxTimestamp()来获取到的交易时间是相同的

例子:

t, err := stub.GetTxTimestamp()

t.Seconds // 交易的时间,unix时间戳,精确到秒

t.Nanos// 交易的时间,unix时间戳,精确到纳秒

time.Unix(t.Seconds, int64(t.Nanos)).UnixNano() / 1e6 // 交易的时间,unix时间戳,精确到毫秒

2. 在同一个区块里多笔交易对同一键进行更新

对同一键进行更新的一个或多笔交易打包到同一块中,只有第1笔交易会成功,后面的交易会失败。相关的原因是:在一个区块里的多笔交易,只有在区块被提交后才会真正生效。而每一个键值都有版本号,假设一个键“key_1”在本个区块提交前的版本号为9,然后在本个区块里有2笔交易分别更新键"key_1"的值为"123"和"456"。那么当第1笔交易更新键“key_1”的值时,键"key_1"的版本号在本个区块里将更新为10,但在本区块未提交前,真正生效的版本号还是9。而当执行第2笔交易时,该交易读取到键"key_1"的版本号还是9,这样第2笔交易还是把键"key_1"的版本号更新为10,这样系统会判断为“双花”行为,从而引起冲突,导致只有第1笔交易会执行成功,第2笔交易执行失败。具体可以参考这篇文章 https://learnblockchain.cn/article/620

因此,试图在fabric合约里创建全局的计数器是一件不可靠的事情。

3. 在同一个区块里多笔交易先后对同一个键进行更新和读取

假设在一个区块里有2笔交易,第1笔交易对键"key_1"值更新为"123",第2笔交易尝试读取键"key_1"的值,结果第2笔交易读键"key_1"的值结果为空。原因上面已经提及到,一个区块里的多笔交易,只有在区块被提交后才会真正生效。

4. 在一个方法内对同一个键先写后读

在一个方法内对同一个键先进行写键值(putState()),然后再进行读键值(getState()),会发现前面的putState操作没有生效,原因也是同前面一样的道理

5. 尝试通过合约使用加密算法对信息进行加密

有些加密算法对同样内容的信息进行加密,有可能每次加密的结果是不一样的。例如:RSA公钥对同一数据加密,每次的结果都不一样。这样会导致不同的节点执行加密的结果不一样,从而导致提交的内容不一致,造成交易失败

6. 没有了解query和invoke的合约函数调用方式

超级账本调用合约函数的方式有2种:query和invoke

通过query方式调用合约函数,只能查询链上数据,不能向链上写数据,并且不产生交易

通过invoke方式调用合约函数,可以向链上写数据,并产生交易

如果一个合约函数里调用了PutState()操作,但是通过query方式来调用该合约函数,虽然不会报错,但PutState()操作实际没有生效,世界状态不会被改变

如果一个合约函数里没有调用PutState()操作,但是通过invoke方式来调用该合约函数,虽然不会报错,但却会产生交易

7. 使用CouchDB,在使用select语句时没有对select的字段建立索引

超级账本可以使用Leveldb或CouchDB。如果要支持富查询,一般会选择使用CouchDB。CouchDB可以支持类似于SQL的select语句来实现富查询。但是CouchDB的效率并不高,在不建立索引的情况下,可能对2-3万条的数据使用select语句就会导致查询非常耗时,从而导致合约函数的执行超时而失败。而对于传统的关系型数据库,2-3万条的数据量,在不建立索引的情况下,对查询的影响不是很明显。

例如下面的select语句:

"{"selector":{\"StudentAge\":{\"$lte\": 10}, \"sort\":[{\"RegisterTime\": \"asc\"}]}}"

最好就需要对字段StudentAge和RegisterTime建立索引。

想知道CouchDB在执行一条查询语句究竟有没有使用索引,用了哪些索引,可以通过向CouchDB发送post请求的方法,通过返回的response来查看使用了哪些索引。具体可参考下面的链接

https://docs.couchdb.org/en/latest/api/database/find.html#db-explain

1.3.8. /db/_explain 这一节的内容 <br>

具体的操作可以通过postman来实现,

post的地址如 http://127.0.0.1:5984/member_goods/_explain

发送的body就是查询select语句的内容,

注意header里需要配置一个KeyValue Content-Type:application/json

8. 使用CouchDB,在使用select语句时尽量避免使用sort进行排序

sort排序会严重消耗查询时间,容易造成查询超时,应尽量避免使用。 <br> <br>


我的知乎:https://www.zhihu.com/people/powervip

我的学习笔记:www.study.win

我的github:https://powervip.github.io/

我的公众号:区块链战斗机

我的网盘:https://pan.baidu.com/s/1frOZuMxdp5IVaY94ZdoN-A

我的csdn:https://blog.csdn.net/powervip

如果你觉得这篇文章写得还可以,请帮忙点个赞,谢谢!著作权归作者所有。如需转载,请注明原文出处并保留原文链接。

你的鼓励,我的动力!

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2022-04-02 17:21
  • 阅读 ( 421 )
  • 学分 ( 2 )
  • 分类:Fabric

0 条评论

请先 登录 后评论
powervip
powervip

7 篇文章, 47 学分