Zhuang's Diary

言之有物,持之以恒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
pragma solidity >=0.6.0 <0.7.0;

contract UserCrud {

// 业务主数据结构
struct UserStruct {
bytes32 userEmail;
uint userAge;
uint index;
}

// 将业务主数据存储在 mapping 中,用户地址是用户数据的索引
// address(key) ---> UserStruct
// 0x1 ---> userEmail:alice@co.com, userAge:22
// 0x2 ---> userEmail:bob@home.com, userAge:44
// 以上为 userStructs 中的存储内容
mapping(address => UserStruct) private userStructs;
// 用户地址被收集进入一个数组 userIndex
address[] private userIndex;
// 反馈新用户
event LogNewUser (address indexed userAddress, uint index, bytes32 userEmail, uint userAge);
// 反馈用户更新
event LogUpdateUser(address indexed userAddress, uint index, bytes32 userEmail, uint userAge);
// 反馈用户删除
event LogDeleteUser(address indexed userAddress, uint index);
// 判断入参的用户是否有与记录中的用户重合,即用户是否已经存在
function isUser(address userAddress)
public
view
returns(bool isIndeed)
{
if(userIndex.length == 0) return false;
return (userIndex[userStructs[userAddress].index] == userAddress);
}
// 插入新的用户主数据
function insertUser(
address userAddress,
bytes32 userEmail,
uint userAge)
public
returns(uint index)
{
if(isUser(userAddress)) {revert("this user already exist.");}
userStructs[userAddress].userEmail = userEmail;
userStructs[userAddress].userAge = userAge;
userIndex.push(userAddress);
userStructs[userAddress].index = userIndex.length-1;
emit LogNewUser(
userAddress,
userStructs[userAddress].index,
userEmail,
userAge);
return userIndex.length-1;
}
// 删除旧的用户主数据
function deleteUser(address userAddress)
public
returns(uint index)
{
if(!isUser(userAddress)) {revert("only other user can deleteUser.");}
uint rowToDelete = userStructs[userAddress].index;
address keyToMove = userIndex[userIndex.length-1];
userIndex[rowToDelete] = keyToMove;
userStructs[keyToMove].index = rowToDelete;
userIndex.pop();
emit LogDeleteUser(
userAddress,
rowToDelete);
emit LogUpdateUser(
keyToMove,
rowToDelete,
userStructs[keyToMove].userEmail,
userStructs[keyToMove].userAge);
return rowToDelete;
}
// 获取用户主数据
function getUser(address userAddress)
public
view
returns(bytes32 userEmail, uint userAge, uint index)
{
if(!isUser(userAddress)) {revert("only exist user can be run by getUser.");}
return(
userStructs[userAddress].userEmail,
userStructs[userAddress].userAge,
userStructs[userAddress].index);
}

function updateUserEmail(address userAddress, bytes32 userEmail)
public
returns(bool success)
{
if(!isUser(userAddress)) {revert("only exist user can updateUserEmail.");}
userStructs[userAddress].userEmail = userEmail;
emit LogUpdateUser(
userAddress,
userStructs[userAddress].index,
userEmail,
userStructs[userAddress].userAge);
return true;
}

function updateUserAge(address userAddress, uint userAge)
public
returns(bool success)
{
if(!isUser(userAddress)) {revert("only exist user can updateUserAge.");}
userStructs[userAddress].userAge = userAge;
emit LogUpdateUser(
userAddress,
userStructs[userAddress].index,
userStructs[userAddress].userEmail,
userAge);
return true;
}

function getUserCount()
public
view
returns(uint count)
{
return userIndex.length;
}

function getUserAtIndex(uint index)
public
view
returns(address userAddress)
{
return userIndex[index];
}

}

remix 调试如下图:

将上述模型进一步提炼为库文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
pragma solidity 0.5.1; 

/*
UnorderedKeySet v0.93
Library for managing CRUD operations in dynamic key sets.
https://github.com/rob-Hitchens/UnorderedKeySet
*/

library UnorderedKeySetLib {

struct Set {
mapping(bytes32 => uint) keyPointers;
bytes32[] keyList;
}

function insert(Set storage self, bytes32 key) internal {
require(key != 0x0, "UnorderedKeySet(100) - Key cannot be 0x0");
require(!exists(self, key), "UnorderedKeySet(101) - Key already exists in the set.");
self.keyPointers[key] = self.keyList.push(key)-1;
}

function remove(Set storage self, bytes32 key) internal {
require(exists(self, key), "UnorderedKeySet(102) - Key does not exist in the set.");
bytes32 keyToMove = self.keyList[count(self)-1];
uint rowToReplace = self.keyPointers[key];
self.keyPointers[keyToMove] = rowToReplace;
self.keyList[rowToReplace] = keyToMove;
delete self.keyPointers[key];
self.keyList.length--;
}

function count(Set storage self) internal view returns(uint) {
return(self.keyList.length);
}

function exists(Set storage self, bytes32 key) internal view returns(bool) {
if(self.keyList.length == 0) return false;
return self.keyList[self.keyPointers[key]] == key;
}

function keyAtIndex(Set storage self, uint index) internal view returns(bytes32) {
return self.keyList[index];
}

function nukeSet(Set storage self) public {
delete self.keyList;
}
}
// 合约只需要维护keyList即可
contract UnorderedKeySet {

using UnorderedKeySetLib for UnorderedKeySetLib.Set;
UnorderedKeySetLib.Set set;

event LogUpdate(address sender, string action, bytes32 key);

function exists(bytes32 key) public view returns(bool) {
return set.exists(key);
}

function insert(bytes32 key) public {
set.insert(key);
emit LogUpdate(msg.sender, "insert", key);
}

function remove(bytes32 key) public {
set.remove(key);
emit LogUpdate(msg.sender, "remove", key);
}

function count() public view returns(uint) {
return set.count();
}

function keyAtIndex(uint index) public view returns(bytes32) {
return set.keyAtIndex(index);
}

function nukeSet() public {
set.nukeSet();
}
}

另外 rob-Hitchens 使用 solidity 写了一个红黑树,地址如下:

https://github.com/rob-Hitchens/OrderStatisticsTree

有向图:

https://github.com/rob-Hitchens/GraphLib

关联文档:

  1. 智能合约-CURD的详细分析
  2. 智能合约-自毁模式
  3. 智能合约-工厂合约模式
  4. 智能合约-名字登录模式
  5. 智能合约-退款方式

跟别人一起申请专利好吗?

合作申请的情况并不鲜见,很多企业或个人都存在合作申请的情况。从统计信息来看,以下三种情况最常见:

情况一 共同研发

共同研发即共同申请人之间共同为一件专利创新作出了有益贡献,因此,其专利申请人为双方或多方共同申请,这类情况完全符合合作申请的要求,也是最常见的一种情况。

情况二 委托研发

最常见的委托研发,是甲方提出研发需求,乙方为响应研发需求,给出解决方案。这种情况理论上专利申请权可以是乙方,即问题的实际解决方案的提供方。

情况三 母子公司

如国家电网公司和中国石油化工股份有限公司,由于管理制度的设立,使众多专利申请均以共同申请的方式提交到国家知识产权局。如果以子公司享用了母公司的特殊技术资源为理由,也是可以说得过去的。

但是,不管是共同研发、委托开发,还是母子公司,在面对共同的专利申请时,都可以采用法律约定的形式,在双方的合作中约定专利申请的权利及归属。并且,专利权的最终归属以双方约定为准。

不管怎样,既然选择了合作申请,自然有选择合作申请的理由。对申请人来说,合作申请有哪些利弊呢?

优点一:可以共同分担费用

众所周知,专利申请过程中及专利授权后,都有费用,随着专利数量增多,其费用也越来越多,而年费则随着专利年限增加趋势更快。

因此,一些专利申请较多的企业慢慢发现专利年费成为一种负担。

如果选择共同申请,则可以双方约定共同承担专利成本,减轻企业压力。

优点二:有收益后可以共同分享利益

《专利法》规定:合作申请的专利,许可、转化后的收益需要双方共同分配。也就是合作申请后,专利如果有第三方使用,则共同申请人中的任一方都有权分享专利的收益。

优点三:促进合作

很多能申请专利的技术,是技术创新者多方合作的共同成果。在技术分工越来越细化的今天,大家共同合作取得创新成果,是很常见的合作模式。

将创新成果共同申请专利,共同分担费用、共享未来收益,是很多人愿意选择的合作方式。因此,共同申请专利某些程度上可以促进合作。

优点四可双方均享有专利实施权,在专利数量统计中均有效

《专利法》规定:合作申请的专利,其自主实施无需对方同意,也不需要分配利益。因此,双方都可以自由实施专利权记载的创新内容。但是在专利持有量统计时,双方均拥有此专利权。

如国家电网公司和中国石油化工股份有限公司,就是这样的情况,他们的专利申请量第一,与他们合作申请专利的共同申请人虽然分散,但同时也分别拥有多项专利申请。

任何事情有利有弊。

既然合作申请有这么多好处,是否会有问题呢?

弊端一:一些法律手续需要双方共同签署,麻烦

《专利法》规定:合作申请专利的,在专利申请、转让、排他许可、独占许可、复审、无效、放弃、撤销等过程中,均需要各合作方共同决定,共同签字盖章,相对于独立申请,其手续更繁琐。

弊端二:专利权不完整,融资时受限

基于上述弊端可以看出,合作申请的专利,其专利权不完整,不管是在专利申请时,还是在后期专利权实施过程中,都要经过共同申请人同意。投资在投创新技术项目时,面对专利权不完整的情况,往往会选择迟疑的态度。

因为很多专利技术往往会让投资人认为该公司在此技术上设有技术壁垒,给竞争对手增加难度,提高项目的竞争实力。如果专利是共同申请的,则需要做专利权转让或说明。

但是,在很多实际合作项目中,当投资人关注并在意专利权归属时,共同申请人放弃专利权或作出转让的条件会比较苛刻。

有的甚至会因为双方在利益面前难以谈拢而导致融资失败。使专利共同申请变成融资的一个障碍,这种情况对于一些科技创新型的初创公司影响会更大一些,尤其需要在合作前期规划清晰。

因此,基于企业自己的发展规划,如何与外包方合作,专利申请权如何处置,企业创始人在申请时仍需谨慎行事。

参考链接 ==> https://github.com/primasio/daap/blob/master/README-cn.md

  • 安永(EY)之前发布的Nightfall项目,实现了对以太坊上ERC-721类资产私密转账的支持。但是ERC-721的使用使得资产的注册是必须公开的,在资产注册以后转入一个私密合约,之后的转移操作才是对外不可见的。另外,对于文章、图片等类型的数字资产,最重要的流通方式不是所有权的转移,而是使用权的购买。比如文章的转载权购买、图片的购买使用等。对于这类数字资产,使用NFT模型抽象是不够的,也就更加无法实现使用权的私密购买。
  • ZoKrates工具包中,具体的零知识证明算法使用了Groth 16。实现协议时也可以使用Bellman工具包,或者将算法替换为Bulletproofs或者是Sonic算法,以实现更好的性能,以及去除对可信初始化的依赖。
  • AZTEC协议 实现了基于ERC20资产的交易值隐私。易于同态加密和零知识证明算法。目前已经在MakerDAO的 DAI token 中上线使用。同时,在EIP1108中予以完成。

Whisper是Ethereum P2P协议的一部分,可以让消息在同一个区块链内的用户之间传递。

Whisper是独立于区块链之外的协议,智能合约不能够使用。

开启Whisper协议, geth -shh

节点默认不会回复消息。所以必须直连到接收人,否者消息将无法发送。

关于ethereum 中,nonce问题总结:

  • Tx 中 nonce 过低(too low)时,Tx 立即被拒绝
  • Tx 中 nonce 过高(too high)时,Tx 被放入交易池队列 transaction pool queue
  • 如果 Tx 的 nonce 正好填补了最新的有效的 nonce 和过高的nonce之间的空隙,是的nonce的顺序完整连接时,交易池队列中的交易将被执行
  • 当 Geth 被关掉或者重新启动时,交易池中的 Txs 将消失

nonce问题,详细介绍:

  • 当 Tx 中 nonce 过低时,
1
2
3
4
5
6
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:0})
Nonce too low
at InvalidResponse (<anonymous>:-81662:-106)
at send (<anonymous>:-156322:-106)
at sendTransaction (<anonymous>:-133322:-106)
at <anonymous>:1:1
  • 当 Tx 中 nonce 过高时,
1
2
3
4
5
> txpool.status
{
pending: 0,
queued: 1
}
  • Geth 重启时,
1
2
3
4
5
> txpool.status
{
pending: 0,
queued: 0
}

解决方案

  • 在应用本地保存nonce在数据库中,并且,数据库中的nonce值更新时需要访问锁保护。不建议将nonce保存在内存中,那样的话,在应用死机或者重启时,nonce将丢失。目前思考 Redis DB 是比较好的选择。

  • 如果本地nonce出了问题,可以使用 web3.eth.getTransactionCount(ethAddress) 来恢复。但是 web3.eth.getTransactionCount(ethAddress) 是内存中的值,不是实时准确的,不建议实时使用。

另外:

  • 即使交易池(queue)中有交易存在,nonce正确的交易仍然可以继续进行。

  • 当共识停止工作了,nonce正确的交易进入到pending队列中,nonce较高的交易进入到queue队列中。当共识重新正常工作时,pending队列中的交易会被自动处理而清空。

  • 同时节点具有每隔一个小时,重新处理一次本地交易的能力。(handle local transaction journal rotation)

参考链接 ==> https://ethereum.stackexchange.com/questions/2808/what-happens-when-a-transaction-nonce-is-too-high

参考代码 ==>

1
2
3
4
5
ethereum/go-ethereum/core/tx_pool.go 

func (pool *TxPool) loop() {
......
}

本问题调查过程中,使用到的命令==>

1
2
3
4
5
6
7
8
9
10
11
12
13
eth.getTransactionCount(eth.accounts[0])

personal.unlockAccount(eth.accounts[0],"1234",99999)

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0],value:100,nonce:200})

txpool.status

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0]})

txpool.status

./odyssey update_Validator localhost:26657 UpdateValidator:OA+kUHbBDxh6q2kqF1jNQFoPqjTYsTS2m17un4Z9vI0=*/*10000