eth-multisig-v4
https://kandi.openweaver.com/javascript/BitGo/eth-multisig-v4
https://github.com/BitGo/eth-multisig-v4
Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。
下面来看下并发情况下读写 map 时会出现的问题,代码如下:
// 创建一个int到int的映射
m := make(map[int]int)
// 开启一段并发代码
go func() {
// 不停地对map进行写入
for {
m[1] = 1
}
}()
// 开启一段并发代码
go func() {
// 不停地对map进行读取
for {
_ = m[1]
}
}()
// 无限循环, 让并发程序在后台执行
for {
}
运行代码会报错,输出如下:
fatal error: concurrent map read and map write
错误信息显示,并发的 map 读和 map 写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对这种并发操作进行检查并提前发现。
需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。
sync.Map 有以下特性:
并发安全的 sync.Map 演示代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
var scene sync.Map
// 将键值对保存到sync.Map
scene.Store("greece", 97)
scene.Store("london", 100)
scene.Store("egypt", 200)
// 从sync.Map中根据键取值
fmt.Println(scene.Load("london"))
// 根据键删除对应的键值对
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{}) bool {
fmt.Println("iterate:", k, v)
return true
})
}
代码输出如下:
100 true
iterate: egypt 200
iterate: greece 97
代码说明如下:
sync.Map 没有提供获取 map 数量的方法,替代方法是在获取 sync.Map 时遍历自行计算数量,sync.Map 为了保证并发安全有一些性能损失,因此在非并发情况下,使用 map 相比使用 sync.Map 会有更好的性能。
在 Web3.js 1.0.0 中,可以通过实用函数获得编码的函数签名。
let encodedFunctionSignature = web3.eth.abi.encodeFunctionSignature('sendMessage(string,address)');
console.log(encodedFunctionSignature);
// => 0xc48d6d5e
https://piyolab.github.io/playground/ethereum/getEncodedFunctionSignature/
http://blog.playground.io/entry/2018/05/08/163727
https://web3js.readthedocs.io/en/1.0/web3-eth-abi.html#encodefunctionsignature
英文原文:https://piyopiyo.medium.com/how-to-get-ethereum-encoded-function-signatures-1449e171c840
如果您一直在以太坊上进行开发,您就会知道无法将结构从合约传递到合约或从 web3 传递到合约的痛苦。在 Atra Blockchain Services,我们为用户自动创建和部署以太坊合约,这一限制直接影响了我们的数据存储服务 dTables。
现在,启用 ABIEncoderV2 后,您可以将结构类型从 web3 或其他合约传递给函数。在启用 ABIEncoderV2 的情况下编译合约时,编译后的 ABI 输出会发生一些变化。ABI JSON 现在将包含一种称为“元组”的新类型,当它遇到结构作为函数中的参数时。元组类型与属性“组件”配对,组件属性是一个包含 {name, type} 对象列表的数组。
下面是使用结构体作为输入参数的合约的 ABI 示例。注意类型和组件属性。
(GitHub code)
{
"constant": false,
"inputs": [{
"components": [{
"name": "text",
"type": "string"
}],
"name": "recordData",
"type": "tuple"
}],
"name": "Insert",
"outputs": [{
"name": "success",
"type": "bool"
}],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
下面是一个使用新编码器的示例存储合约 (GitHub code)
pragma solidity ^0.5.3;
//注意此处
pragma experimental ABIEncoderV2; //0.7.0 之前需添加支持
pragma abicoder v2; // 0.7.0 之后需要添加
contract storageContract {
event Inserted(address _sender, address _recordId);
event Updated(address _sender, address _recordId);
event Deleted(address _sender, address _recordId);
struct Data {
string text;
}
struct Record {
Data data;
uint idListPointer;
}
mapping(address => Record) public Table;
address[] public IdList;
constructor() public {}
// Check if recordId is in IdList, it's common for the record to be deleted and not by in the IdList anymore
function Exists(address recordId) public view returns(bool exists) {
if (IdList.length == 0) return false;
return (IdList[Table[recordId].idListPointer] == recordId);
}
function GetLength() public view returns(uint count) {
return IdList.length;
}
function GetByIndex(uint recordIndex) public view returns(address recordId, Data memory record) {
require(recordIndex < IdList.length);
return (IdList[recordIndex], Table[IdList[recordIndex]].data);
}
function GetById(address recordId) public view returns(uint index, Data memory record) {
require(Exists(recordId));
return (Table[recordId].idListPointer, Table[recordId].data);
}
function Insert(Data memory recordData) public returns(bool success) {
address recordAddress = address(now);
require(!Exists(recordAddress));
Table[recordAddress].data = recordData;
Table[recordAddress].idListPointer = IdList.push(recordAddress) - 1;
emit Inserted(msg.sender, recordAddress);
return true;
}
function Update(address recordId, Data memory recordData) public returns(bool success) {
require(Exists(recordId));
Table[recordId].data = recordData;
emit Updated(msg.sender, recordId);
return true;
}
// once a record has been deleted from the idList it can no longer be modified, but the memory remains
// You can still pull deleted records if you hit the Table object directly, you will not be able to use GetByIndex or GetById
function Delete(address recordId) public returns(bool success) {
require(Exists(recordId));
// get the record id to delete
uint recordToDelete = Table[recordId].idListPointer;
// set the last item in the id list to keep and move
address keyToMove = IdList[IdList.length - 1];
// replace the id of the deleted record with the one we want to keep i.e the last item
IdList[recordToDelete] = keyToMove;
// update the last record in the list to point to it's new position in the key list which is the old deleted records spot
Table[keyToMove].idListPointer = recordToDelete;
// remove the last element from the id list that holds the old pointer for the keep record
IdList.length--;
// emit event
emit Deleted(msg.sender, recordId);
return true;
}
}
在函数参数中使用结构可以显着减少合约的混乱和复杂性,同时使它们交互起来更加愉快。
外部函数 不可以接受多维数组作为参数
如果原文件加入 pragma abicoder v2; 可以启用ABI v2版编码功能,这此功能可用。 (注:在 0.7.0 之前是使用pragma experimental ABIEncoderV2;
)
内部函数 则不需要启用ABI v2 就接受多维数组作为参数。
参考自:https://learnblockchain.cn/docs/solidity/contracts.html