手机端使用以太坊的方式
对以太坊区块链数据的操作,你必须是一个全节点,并实时的进行数据的交互。但是如果手机端当做一个全节点去处理,是一个不现实的问题。所以解决方式就是一个全节点开启JSON-RPC服务,供手机端进行调用,然后这个节点处理手机端的数据请求。这个时候就需要了解JSON-RPC API,以便调用。
-
普通的交易和余额查询及其他
- 具体内容参考JSON-RPC官方文档
- 交易的发出使用eth_sendrawtransaction
- 余额的查询使用eth_getbalance
- 具体使用方式参考官方文档
- 注意由于个人私钥非常重要,所以交易的个人签名相关必须在本地处理
-
智能合约的调用
- 智能合约的调用
- 一些查询操作是用eth_call方法
- 一些需要改变合约内部数值的操作,需要当做一个交易处理使用eth_sendrawtransaction
- 智能合约调用与普通的交易的异同
- 首先toAddress是合约地址
- 其次交易可选项data字段需要放入一些特殊的数据
- 最后普通的查询不消耗gas
- 智能合约data字段的处理
具体理论参考[Ethereum-Contract-ABI]文档 具体例子:如下 // 先获得方法ID(Method ID) 获得的方式是 1. 例如:function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; } 2. 如果想调用这个方法则先获得方法ID,即对baz(uint32,bool)做KECCAK256hash计算,然后取计算结果的前四个字节,也就是hash值的前八位,然后拼接0x,就是这个方法的ID. keccak256(@"baz(uint32,bool)") = cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2 前八位即cdcd77c0,拼接0x = 0xcdcd77c0,所以这个方法的ID就是0xcdcd77c0。 // 对传递的参数进行处理 1. 第一个参数以uint32的参数,如果传入69,则首先将69转化为十六进制的0x45 然后与32字节的总长度为64的(0x0000000000000000000000000000000000000000000000000000000000000000)进行相加 = 0x0000000000000000000000000000000000000000000000000000000000000045 2. 第二个参数bool的参数,如果传入true,则为0x1。(如果传入false则为0x0) 然后与32字节的总长度为64的(0x0000000000000000000000000000000000000000000000000000000000000000)进行相加 = 0x0000000000000000000000000000000000000000000000000000000000000001 3. 与方法ID结果拼接0x是16进制的标志,在拼接时不参与拼接,最后在结果前拼接0x 即最后的结果是cdcd77c0 + 0000000000000000000000000000000000000000000000000000000000000045 + 0000000000000000000000000000000000000000000000000000000000000001 = 0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001 4. 这个时候eth_call的data字段就处理完毕了,你就可以去调用这个合约的这个方法了 如果没有特殊情况,这就是调用的方式。最后把第3步得到的结果值赋予data字段即可。 [注意] 1. 方法ID的获取的特殊处理 1)如果函数中有uint,int字段,则在进行做KECCAK256hash计算时用uint256替代,int256不用这么做。 2)如果函数中有fixed, ufixed字段,则进行计算时用fixed128x19替代,如果本身就是fixed128x19字段,那么就用这个字段。 3)例如:如果function baz(uint x, bool y) ,则在应该处理baz(uint256,bool)这个字段,其他情况同样操作。 2. 参数中有动态类型(dynamic type)时的处理 1)string,bytes为动态,因为长度不固定,如果bytes10则为静态 2)uint256[]为动态 3)处理当有动态和静态参数时,先拼接静态最后拼接动态,然后加上偏移量 4)怎么确定偏移量的具体值有点不清楚。动态类型的处理还可以 3. 一个简单的例子 // vote(uint vot, uint contrastIndex, string name) // uint 使用uint256替代 NSData *voteData = [@"vote(uint256,uint256,string)" dataUsingEncoding:NSUTF8StringEncoding]; NSData *voteKeccak256Data = [SecureData KECCAK256:voteData]; Hash *voteHash = [Hash hashWithData:voteKeccak256Data]; NSString *voteMethodID = [voteHash.hexString substringToIndex:10]; // 选择1 NSString *value1 = @"0000000000000000000000000000000000000000000000000000000000000001"; // 和第0个对比 NSString *value2 = @"0000000000000000000000000000000000000000000000000000000000000000"; // 第三个参数名字 string类型,这个在特殊一些 * 1. 首先string是动态类型,首先添加 0000000000000000000000000000000000000000000000000000000000000060 2. 其次是字符的长度‘xiaolongxia’ 为11 - 0xb 000000000000000000000000000000000000000000000000000000000000000b 3. 字符的编码不够的话,右边拼接(不是左边拼接) ‘xiaolongxia’ 的编码是7869616f6c6f6e67786961 7869616f6c6f6e67786961000000000000000000000000000000000000000000 4. 组装起来就是string类型的处理 * // 首先是添加偏移量的值,因为是动态类型编码 NSString *addString = @"0000000000000000000000000000000000000000000000000000000000000060"; // 其次是字符长度的编码 NSString *lengString = @"000000000000000000000000000000000000000000000000000000000000000b"; // 最后是字符的编码 NSData *nameData = [@"xiaolongxia" dataUsingEncoding:NSUTF8StringEncoding]; NSString *namHexString = [SecureData dataToHexString:nameData]; namHexString = [namHexString substringFromIndex:2]; namHexString = [self fillZeroTo32ByteWithValue:namHexString]; totalString = [NSString stringWithFormat:@"%@%@%@%@%@%@",voteMethodID, value1, value2, addString, lengString,nameTotal];
- data字段的处理,动态类型的偏移量,还有点不明确,暂时先到这。有待补充
**补充偏移量的确定** 1. 首先解释动态类型,即长度不固定,可长可短的变量。 定义首先bytes,string, 数组(type[])是动态类型。例如:uint256[],是动态的,uint256[10]就是静态的,因为uint256是静态的,然后数组的个数也确定了,所以uint256[10]是静态的。 bytes是动态的,但是bytes10就是静态的,因为bytes10代表着长度确定为10个。 所以动态的可以定位为bytes,string,T[](T代表着任意类型),bytes[K] 或者string[K](K>0)。其他所以类型都是静态的。 2. 偏移量的确定。 函数:f(uint,uint32[],bytes10,bytes) 传入的值:(0x123, [0x456, 0x789], "1234567890", "Hello, world!") 具体编码如下: 1. 首先获得methodID,对f(uint256,uint32[],bytes10,bytes)的编码取前4个字节,8个长度为8be65246 即0x8be65246 2. 参数的处理 1)第一个参数uint类型,值为0x123所以结果是 <参数部分的第1个字段> 0x0000000000000000000000000000000000000000000000000000000000000123 2)第二个参数uint32[]类型,是动态类型,这里先放一个偏移量字段值为0,代表数值不定 <参数部分的第2个字段> 0x0000000000000000000000000000000000000000000000000000000000000000 3)第三个字段是bytes10,值为"1234567890",对这个字符串进行编码得到0x3132333435363738393 <参数部分的第3个字段> 0x3132333435363738393000000000000000000000000000000000000000000000 4)第四个字段是bytes是动态类型,这里先放一个偏移量字段值为0,代表数值不定 <参数部分的第4个字段> 0x0000000000000000000000000000000000000000000000000000000000000000 5)处理第二个参数,作为一个动态类型 (1)是一个数组,有两个元素,所以第一值为 <参数部分的第5个字段> 0x0000000000000000000000000000000000000000000000000000000000000002 (2)第二个值为数组第一个元素 0x456,所以值为 <参数部分的第6个字段> 0x0000000000000000000000000000000000000000000000000000000000000456 (3)第三个值为数组第二个元素 0x789,所以值为 <参数部分的第7个字段> 0x0000000000000000000000000000000000000000000000000000000000000789 (4)第二个参数的值处理完毕,这个时候考虑当时预留的偏移量,在第 5)步开始前,有4个64位的,4个32字节的数据,如果4*32转化为16进制的话是8*16即0x80,所以这个字段偏移量为0x80,所以值为 0x0000000000000000000000000000000000000000000000000000000000000080 6)处理第四个参数,做一个动态类型 (1)是一个字符,字符为"Hello, world!",长度为13,所以值为 <参数部分的第8个字段> 0x000000000000000000000000000000000000000000000000000000000000000d (2)字符的编码为0x48656c6c6f2c20776f726c6421,所以值为 <参数部分的第9个字段> 0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000 (3)第四个参数处理完毕,考虑此时第四个参数的偏移量,在第 6)步开始前,有7个64位的,7个32字节的数据,如果7*32转化为16进制的话是14*16即0xe0,所以这个字段偏移量为0xe0,所以值为 0x00000000000000000000000000000000000000000000000000000000000000e0 3. 所以拼接的最后结果是 方法ID编码: 0x8be65246 第一参数的值: 0000000000000000000000000000000000000000000000000000000000000123 第二参数的偏移量: 0000000000000000000000000000000000000000000000000000000000000080 第三参数的值: 3132333435363738393000000000000000000000000000000000000000000000 第四参数的偏移量: 00000000000000000000000000000000000000000000000000000000000000e0 第二参数的值: 0000000000000000000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000000000000000456 0000000000000000000000000000000000000000000000000000000000000789 第四参数的值: 000000000000000000000000000000000000000000000000000000000000000d 48656c6c6f2c20776f726c642100000000000000000000000000000000000000 即最终确定的data值为 0x8be6524600000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000
-
对事件调用的补充
参考以太坊事件log分析 -
返回的结果如果有类似发送数据这种情况,会同样处理,所以对结果值的解析,可以参考发送值的拼接处理
-
网络请求
网络请求就是普通的数据接口调用,基本都是数据字段的拼接。具体调用方式参考Etherscan供开发api和INFURA api。
参考资料
JSON-RPC官方文档
Etherscan供开发api
INFURA api
Solidity官方调用规范文档
Ethereum-Contract-ABI
网友评论