推导利滚利公式

自从开始学习智能合约之后,接触到了很多「资金盘」游戏,比如前段时间大火的 Fomo3D,不过自从 333ETH 崩盘之后,此类游戏暂时已经没有市场了,因为本质上来说,它们都属于庞氏骗局,在研究相关代码后,让我对利滚利公式产生了一点兴趣,其实很简单,但我年纪大了,实实在在推导了好几遍才搞定,索性把推导过程记录下来。

继续阅读

以太坊地址区分大小写么

以太坊地址区分大小写么?要搞清楚这个问题,我们不妨先在私链上做个实验:

geth> eth.sendTransaction({
    from: eth.accounts[0],
    to: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    value: web3.toWei(1, 'ether')
})

geth> eth.sendTransaction({
    from: eth.accounts[0],
    to: "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    value: web3.toWei(1, 'ether')
})

geth> eth.getBalance("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
geth> eth.getBalance("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

从实验结果上来看,纯小写地址和纯大写地址实际上是同一个地址,那么是不是由此可以得出以太坊地址不区分大小写呢?我们再看下面这个 remix 上的实验:

pragma solidity ^0.5.0;

contract Foo {
    address bar = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
}

代码运行结果如下图所示:

remix

remix

结果提示错误,并给了一个大小写混合的正确地址:

This looks like an address but has an invalid checksum. Correct checksummed address: “0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa”. If this is not used as an address, please prepend ’00’. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals

怎么回事?实际上这是因为 EIP-55 规范引入了 checksum 机制。它有什么用处?设想一下,如果你在输入长长的以太坊地址的时候,不小心输错了一个字符,那么很难发现这样的错误,有了 checksum 机制,我们就能在业务程序里校验地址的准确性,从而降低用户输入错误地址的风险,给出一个 golang 代码的例子:

package main

import (
    "flag"
    "fmt"

    "github.com/ethereum/go-ethereum/common"
)

var (
    address = flag.String("address", "", "address")
)

func init() {
    flag.Parse()
}

func main() {
    addressWithChecksum := common.HexToAddress(*address).Hex()

    if addressWithChecksum != *address {
        fmt.Println("  valid: " + addressWithChecksum)
        fmt.Println("invalid: " + *address)
    } else {
        fmt.Println("This address is valid.")
    }
}

代码运行结果如下图所示:

checksum

checksum

如果你没有 Golang 的运行环境,实际上还有更简单的验证方法:通过 Etherscan 来获取带 checksum 的地址,留意地址栏中的地址和下面 Address 旁边的地址:

etherscan

etherscan

总结:以太坊地址本身不区分大小写,但是出于安全性的考虑,我们应该尽可能使用符合 EIP-55 规范的地址,当然了,业务程序要有相应的 checksum 校验逻辑才行。

关于以太坊随机数

在以太坊应用中,游戏一直都是热点中的热点,而在游戏中,随机数往往是一个不可或缺的功能,比如骰子游戏中,我们需要通过随机数来控制点数,如果一个游戏有一个好的随机数算法的话,那么既可以保证游戏庄家不被黑,也可以保证玩家不被宰。

继续阅读

如何解密keystore文件

如果你用 geth 创建过账号「geth –datadir /path/to/data account new」,那么多半知道 keystore 文件,它通过一个 password 加密保存着账号的私钥:

keystore

keystore

如果我想拿到加密前的私钥怎么办?最容易想到的办法是在 MetaMask 中导入账号的时候选择通过 JSON 文件导入的方式,然后再导出私钥。不过这个方法不方便,也无法实现自动化,下面看看如何通过 golang 解密 keystore 文件:

package main

import (
    "encoding/hex"
    "flag"
    "fmt"
    "io/ioutil"
    "os"

    "github.com/ethereum/go-ethereum/accounts/keystore"
    "github.com/ethereum/go-ethereum/crypto"
)

var (
    file     = flag.String("file", "", "file")
    password = flag.String("password", "", "password")
)

func init() {
    flag.Parse()
}

func main() {
    if _, err := os.Stat(*file); os.IsNotExist(err) {
        flag.Usage()
        os.Exit(1)
    }

    keyjson, err := ioutil.ReadFile(*file)
    if err != nil {
        panic(err)
    }

    key, err := keystore.DecryptKey(keyjson, *password)
    if err != nil {
        panic(err)
    }

    address := key.Address.Hex()
    privateKey := hex.EncodeToString(crypto.FromECDSA(key.PrivateKey))

    fmt.Printf("Address:\t%s\nPrivateKey:\t%s\n",
        address,
        privateKey,
    )
}

更新:本文仅为演示,不推荐通过 flag 传递密码,否则可以在 history 中看到。

如何实现一个定制的智能合约地址

我学习智能合约的一个主要途径就是在 DappRadar 看各个热门应用的源代码,前些天我在看  dice2win 的时候发现一个有趣的现象:虽然它自从上线以来已经多次部署过智能合约,不过让人好奇的是这些地址有一个特点,都有一个和名字很像的 「D1CE」前缀(因为的地址是十六进制的,所以字母 I 被改为了数字 1)。

继续阅读

搭建以太坊智能合约测试环境

早就想学习区块链相关技术了,可惜因为懒惰一直没有付诸实践,最近随着诸如 God.GameFomo3D 等区块链游戏接连暴出安全漏洞,让我对智能合约的兴趣愈发强烈起来,于是利用周末搭建以太坊智能合约测试环境,原想应该很简单,实际却花了不少精力,记录一下以飨读者。

继续阅读