主页 > imtoken苹果版下载官网怎样下载 > 以太坊区块数据结构 utils:帮助分析co的输入数据

以太坊区块数据结构 utils:帮助分析co的输入数据

Solidity-utils

其他语言:英语

阐明

帮助开发者处理以太坊数据的工具类。 目前以太坊区块数据结构,它实现了对以太坊区块链中智能合约交易输入数据的分析。

在以太坊区块链数据中,可以通过查询交易来查看交易的附加数据。 如果一个区块链项目将公共数据上链,用户可以通过Hash查看一笔交易的公共数据到达区块链。 数据本身公开透明的效果。 而tx.Data()得到的原始数据是一个字节数组,按照一定的规则存储。 直接输出如图1所示,用户可读性低,需要用户进行相应的分析才能看到真实的数据。

本工具提供TransformTxData()方法,传入智能合约的abi和交易的数据,处理后输出可读数据,如图2所示。

image-20200404200841901

图 1 初始数据

image-20200404200900219

图 2 处理后的数据

参考以太坊的数据类型一文

(一)Solidity编程五种数据类型

(2) 深入理解Solidity-type

关于智能合约二进制接口

(3) 智能合约应用二进制接口规范

其他工具

(4) 十六进制转换工具

智能合约二进制接口

参考这里:

智能合约应用二进制接口规范,推荐先阅读本文,详细解释了智能合约中的二进制存储规范。

深入理解Solidity-type

数据类型

智能合约有以下基本类型,包括静态类型和动态类型,静态类型分为基本类型和固定数组长度。

静态类型和动态类型的区别在于,在存储虚拟机时,会先存储静态类型的数据。 当遇到动态类型的数据时,会记录偏移量,数据部分会存放在偏移量的位置。 这时,数据部分包括数据长度和数据内容本身。

uint<M>: M位无符号整型, 0 < M <= 256, M % 8 == 0. e.g. uint8, uint16, uint24, uint36, uint256静态类型

int<M>:  M位有符号整型, 0 < M <= 256, M % 8 == 0. e.g. int8, int16, int24, int36, int256静态类型

address:  uint160 类似, 160位数据类型静态类型

uint, int:  分别等同于 uint256, int256静态类型

bool:  uint8 类似但值仅限于0和1静态类型

fixed<M>x<N>:  有符号的定长浮点型, M表示该类型占用的位数 8 <= M <= 256, M % 8 ==0 N表示可用的小数位数 0 < N <= 80. e.g. fixed128x18静态类型

ufixed<M>x<N>: 无符号的定长浮点型M表示该类型占用的位数 8 <= M <= 256, M % 8 ==0 N表示可用的小数位数 0 < N <= 80. e.g. ufixed128x18静态类型

fixed, ufixed: 分别等同于 fixed128x18, ufixed128x18静态类型

bytes<M>: 定长字节数组, 0 < M <= 32静态类型

function: 包含20字节的function地址和4字节的函数标识等同于bytes24静态类型


bytes: 类似于byte[],但元素是连续存在一起的不会补0),而byte[]是按每32字节一单元的方式存放动态类型

string: 以UTF-8编码方式编码的字符串动态类型

以太坊每分钟出多少区块_以太坊开发区块链_以太坊区块数据结构

<type>[]: 存储<type>类型元素的数组动态类型 (T1,T2,...,Tn): 包含了 types T1, , Tn 的元组, n >= 0动态类型

这里只支持以下常见数据类型的转换。

boolint
address
string
bytes1, byte2, ... , byte31, byte32 
int, int8, int16, ... ,int248, int256
uint, uint8, uint16, ... ,uint248, uint256
int[], uint[], bytes[] // 静态数据类型定长数组

数据分析

根据智能合约二进制存储规范1,可以对从交易中获取的Input数据进行如下详细划分分析,以方法createActivity()为例:

function createActivity(genesisId: uint256, name: string, brief: string, detail: string, status: string, associationId: uint256, creator: address)

原始数据:

0x7afb2807000000000000000000000000000000000000000000000000113dd03b6240400000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000113d90faf74050000000000000000000000000008540f71f4200d6ea8854f1b9e7073c2503d3e6f9000000000000000000000000000000000000000000000000000000000000001de796abe68385e998b2e68ea7c2b7e5bf97e684bfe59ca8e8a18ce58aa800000000000000000000000000000000000000000000000000000000000000000000cfe4b8bae58887e5ae9ee7bbb4e68aa4e4babae6b091e7bea4e4bc97e79a84e7949fe591bde5ae89e585a8e5928ce8baabe4bd93e581a5e5bab7efbc8ce585a8e58a9be5819ae5a5bde68891e58ebfe696b0e59e8be586a0e78ab6e79785e6af92e6849fe69f93e79a84e882bae7828ee796abe68385e998b2e68ea7e5b7a5e4bd9cefbc8ce69c80e5a4a7e99990e5baa6e5878fe5b091e4babae7bea4e8819ae99b86e38081e998bbe696ade796abe68385e4bca0e692adefbc8ce9818fe588b6e796abe68385e89493e5bbb6e380820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010931e38081e7bb84e7bb87e5bf97e684bfe88085e588b0e585ace59bade38081e5b9bfe59cbae58a9de5afbce7bea4e4bc97e688b4e5a5bde58fa3e7bda9e38081e4b88de8819ae99b86e380820a32e38081e5b7a1e980bbe8bda6e796abe68385e998b2e68ea7e5aea3e4bca0e380820a33e38081e7bb84e7bb87e5bf97e684bfe88085e588b0e5869ce8b4b8e5b882e59cbae9858de59088e789a9e4b89ae7ab99e4babae59198e7bbb4e68c81e5b882e59cbae7a7a9e5ba8fe380820a34e38081e7bb84e7bb87e5bf97e684bfe88085e58f82e4b88ee5869ce8b4b8e5b882e59cbae38081e585ace585b1e59cbae68980e38081e59f8ee58cbae585ace58e95e6b688e6af92e3808200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e68aa5e5908de4b8ad0000000000000000000000000000000000000000000000

拆分数据

// 以32个字节为单位进行分割

0x7afb2807 //MethodID, Keccak散列的前4个字节
000000000000000000000000000000000000000000000000113dd03b62404000	//	uint256(静态) genesisId
00000000000000000000000000000000000000000000000000000000000000e0	//	string偏移量224个字节(动态) name
0000000000000000000000000000000000000000000000000000000000000120	// 	string偏移量288个字节(动态) brief
0000000000000000000000000000000000000000000000000000000000000220	//	string偏移量544个字节(动态) detail
0000000000000000000000000000000000000000000000000000000000000360	//	string 偏移量864个字节(动态) status
000000000000000000000000000000000000000000000000113d90faf7405000	//	uint256(静态) associationId
0000000000000000000000008540f71f4200d6ea8854f1b9e7073c2503d3e6f9	//	address(静态) creator

000000000000000000000000000000000000000000000000000000000000001d	//	长度29个字节 name
e796abe68385e998b2e68ea7c2b7e5bf97e684bfe59ca8e8a18ce58aa8000000	//	数据内容

00000000000000000000000000000000000000000000000000000000000000cf	//	长度207个字节 breif
e4b8bae58887e5ae9ee7bbb4e68aa4e4babae6b091e7bea4e4bc97e79a84e794	// 	数据内容
9fe591bde5ae89e585a8e5928ce8baabe4bd93e581a5e5bab7efbc8ce585a8e5
8a9be5819ae5a5bde68891e58ebfe696b0e59e8be586a0e78ab6e79785e6af92
e6849fe69f93e79a84e882bae7828ee796abe68385e998b2e68ea7e5b7a5e4bd
9cefbc8ce69c80e5a4a7e99990e5baa6e5878fe5b091e4babae7bea4e8819ae9
9b86e38081e998bbe696ade796abe68385e4bca0e692adefbc8ce9818fe588b6
e796abe68385e89493e5bbb6e380820000000000000000000000000000000000

0000000000000000000000000000000000000000000000000000000000000109	//	长度265个字节 detail
31e38081e7bb84e7bb87e5bf97e684bfe88085e588b0e585ace59bade38081e5	// 	数据内容
b9bfe59cbae58a9de5afbce7bea4e4bc97e688b4e5a5bde58fa3e7bda9e38081
e4b88de8819ae99b86e380820a32e38081e5b7a1e980bbe8bda6e796abe68385
e998b2e68ea7e5aea3e4bca0e380820a33e38081e7bb84e7bb87e5bf97e684bf
e88085e588b0e5869ce8b4b8e5b882e59cbae9858de59088e789a9e4b89ae7ab
99e4babae59198e7bbb4e68c81e5b882e59cbae7a7a9e5ba8fe380820a34e380

以太坊每分钟出多少区块_以太坊区块数据结构_以太坊开发区块链

81e7bb84e7bb87e5bf97e684bfe88085e58f82e4b88ee5869ce8b4b8e5b882e5 9cbae38081e585ace585b1e59cbae68980e38081e59f8ee58cbae585ace58e95 e6b688e6af92e380820000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000009 // 长度9个字节 status e68aa5e5908de4b8ad0000000000000000000000000000000000000000000000 // 数据内容

具体实现数据结构SolidityDataType

用于描述 Solidity 数据类型的结构

type SolidityDataType struct {
	Name     string `json:"name"`     // 类型名,如:int32
	Type     Kind   `json:"type"`     // 值类型或引用类型
	IsNumber bool   `json:"isNumber"` // 是否数字
	IsArray  bool   `json:"isArray"`  // 是否数组
	BitSize  int    `json:"bitSize"`  // 以bit为单位的长度,用于以后补0,仅对值类型生效
}

输入数据

Transaction中用于封装Data字节数组的结构体

type InputData struct {
	Param     SolidityDataType `json:"-"`         //参数的数据类型
	ParamName string           `json:"paramName"` //参数名
	Offset    int64            `json:"-"`         //偏移量,非值类型时为0
	Data      string           `json:"data"`      //数据内容
}

固体法

用于描述 Solidity 函数的结构

type SolidityMethod struct {
	Name       string     `json:"name"`
	InputDatas InputDatas `json:"params"`
}

实现函数 GetSolidityTypeByName

通过数据类型名称构造SolidityType,如下输入可以输出如下结果:

name = "uint"	=>	returns { uint, value, 256 }		// uint类型,值类型,256位
name = "string"	=>	returns { string, variable, 0 }		// string类型,非值类型,无意义
name = "byte3[10]"	=>	returns { bytes3[10], value, 0}	// bytes3数组类型,值类型,3*8*10=240位

func GetSolidityTypeByName(name string) *SolidityDataType {
	var bitSize = 0
	var typeName = VALUE
	var isNumber = false
	var isArray = false
	if name == BOOL { // bool
		bitSize = 8
	} else if name == ADDRESS {
		bitSize = 20 * 8
	} else if strings.Contains(name, UINT) {
		isNumber = true
		if len(name) == len(UINT) { // uint
			bitSize = 256
		} else if strings.Contains(name, "[") { //uint[N]

以太坊开发区块链_以太坊每分钟出多少区块_以太坊区块数据结构

isArray = true base := 256 left := strings.Index(name, "[") right := strings.Index(name, "]") length := cast.ToInt(name[left+1 : right]) // N if left > len(UINT) { //uint[N] base = cast.ToInt(name[len(UINT):left]) // M } bitSize = base * length //M == 0 , uint[N] } else { bitSize = cast.ToInt(strings.Trim(name, UINT)) } } else if strings.Contains(name, INT) { isNumber = true if len(name) == len(INT) { //int bitSize = 256 } else if strings.Contains(name, "[") { //int[] isArray = true base := 256 left := strings.Index(name, "[") right := strings.Index(name, "]") length := cast.ToInt(name[left+1 : right]) // N if left > len(INT) { //int base = cast.ToInt(name[len(INT):left]) // M } bitSize = base * length //M == 0 , int[N] } else { bitSize = cast.ToInt(strings.Trim(name, INT)) } } else if strings.Contains(name, BYTES) { if len(name) == len(BYTES) { //bytes非值类型 typeName = VARIABLE } else if strings.Contains(name, "[") { //bytes[] isArray = true base := 1 left := strings.Index(name, "[") right := strings.Index(name, "]") length := cast.ToInt(name[left+1 : right]) // N if left > len(BYTES) { //bytes base = cast.ToInt(name[len(BYTES):left]) // M } bitSize = 8 * base * length //M == 0 , bytes[N] } else { bitSize = 8 * cast.ToInt(strings.Trim(name, BYTES)) } } else if name == STRING { typeName = VARIABLE } else {

以太坊区块数据结构_以太坊每分钟出多少区块_以太坊开发区块链

bitSize = 0 typeName = VALUE name = "" } return &SolidityDataType{name, typeName, isNumber, isArray, bitSize} }

转换数据

传入参数:智能合约的abi、交易的数据、输出已处理的SolidityMethod。

func TransformTxData(abiStr string, input []byte) (*SolidityMethod, error) {
	abi, err := abi.JSON(strings.NewReader(abiStr))
	if err != nil {
		return nil, err
	}
	method, err := abi.MethodById(input)
	if err != nil {
		return nil, err
	}
	inputDatas, err := unmarshalInput(method.Inputs, input)
	if err != nil {
		return nil, err
	}
	return &SolidityMethod{
		Name:       method.String(),
		InputDatas: inputDatas,
	}, nil
}

// 把inputs转换为InputDatas
func unmarshalInput(args abi.Arguments, input []byte) (InputDatas, error) {
	const head int64 = 4
	const bitSize int64 = 32
	var next = head

	var resultList InputDatas
	var inputData InputData
	for _, arg := range args {
		dataType := GetSolidityTypeByName(arg.Type.String())
		inputData.Param = *dataType
		inputData.ParamName = arg.Name
		inputData.Offset = 0
		if dataType.Type == VALUE { //值类型
			inputData.Data = hexutil.Encode(input[next : next+bitSize])
			if dataType.IsNumber {
				dataInt, err := DecodeBigFromHex(inputData.Data)
				if err != nil {
					return nil, err
				}
				inputData.Data = config.ToString(dataInt)

以太坊区块数据结构_以太坊每分钟出多少区块_以太坊开发区块链

} } else { //动态类型 offsetHex := hexutil.Encode(input[next : next+bitSize]) offsetInt, err := DecodeBigFromHex(offsetHex) if err != nil { return nil, err } inputData.Offset = offsetInt.Int64() dataHead := head + offsetInt.Int64() dataNext := dataHead + bitSize dataLenHex := hexutil.Encode(input[dataHead:dataNext]) dataLen, err := DecodeBigFromHex(dataLenHex) if err != nil { return nil, err } dataHead = dataNext dataNext = dataHead + dataLen.Int64() inputData.Data = string(input[dataHead:dataNext]) } next += bitSize resultList = append(resultList, inputData) } return resultList, nil }

从十六进制解码大

封装hexutil.DecodeBig()函数。 由于该函数只允许输入不带“0”前缀的十六进制字符串,否则会报错:“hex number with leading zero digits”以太坊区块数据结构,所以写了TrimLeadingZero(hexStr string)函数,前缀为“0”通过循环不断取出,得到不带前缀0的16进制字符串。

// 十六进制转换为十进制
func DecodeBigFromHex(hexStr string) (*big.Int, error) {
   hexStr = TrimLeadingZero(hexStr)
   return hexutil.DecodeBig(hexStr)
}

// 删除十六进制字符串前缀多余的0
func TrimLeadingZero(hexStr string) string {
   if strings.Contains(hexStr, "0x") {
      hexStr = strings.TrimPrefix(hexStr, "0x")
   }
   for {
      if !strings.HasPrefix(hexStr, "0") { //不再拥有“0”前缀时跳出
         break
      }
      hexStr = strings.TrimPrefix(hexStr, "0")
   }
   if hexStr == "" {
      return "0x" + "0"
   }
   return "0x" + hexStr
}