我学习智能合约的一个主要途径就是在 DappRadar 看各个热门应用的源代码,前些天我在看 dice2win 的时候发现一个有趣的现象:虽然它自从上线以来已经多次部署过智能合约,不过让人好奇的是这些地址有一个特点,都有一个和名字很像的 「D1CE」前缀(因为的地址是十六进制的,所以字母 I 被改为了数字 1)。
如何实现呢,其实 ethereum 源代码里已经给出答案:
func CreateAddress(b common.Address, nonce uint64) common.Address { data, _ := rlp.EncodeToBytes([]interface{}{b, nonce}) return common.BytesToAddress(Keccak256(data)[12:]) }
也就是说,合约地址完全是由 address 和 nonce 决定的,如果我们希望用一个全新的账户来部署合约,那么当它第一次部署的时候,nonce 必然是 0,于是乎可以推断合约地址完全是由 address 决定的,顺着这个思路,我们只要不断生成新的 address,然后判断它们第一次部署的合约地址是否满足定制的 pattern 即可,附上 golang 源代码:
package main import ( "encoding/hex" "flag" "fmt" "regexp" "sync" "sync/atomic" "github.com/ethereum/go-ethereum/crypto" ) var ( concurrency = flag.Int("c", 10, "concurrency") number = flag.Int64("n", 1, "number") pattern = flag.String("p", "", "pattern") ) func init() { flag.Parse() } func main() { var wg sync.WaitGroup wg.Add(*concurrency) reg := regexp.MustCompile("^0x" + *pattern) nonce := uint64(0) for i := 0; i < *concurrency; i++ { go func() { defer wg.Done() run(reg, nonce) }() } wg.Wait() } func run(reg *regexp.Regexp, nonce uint64) { for *number > 0 { key, _ := crypto.GenerateKey() address := crypto.PubkeyToAddress(key.PublicKey) contract := crypto.CreateAddress(address, nonce).Hex() if !reg.MatchString(contract) { continue } if atomic.AddInt64(number, -1) < 0 { break } privateKey := hex.EncodeToString(key.D.Bytes()) fmt.Printf("Contract:\t%s\nAddress:\t%s\nPrivateKey:\t%s\n\n", contract, address.Hex(), privateKey, ) } }
编译后,当执行命令行的时候,指定你想要的 pattern 即可,一旦匹配成功,我们就可以通过生成的账户来部署合约了,不过在部署前你需要确保账户里有足够的以太来支付部署费用。我在 ropsten 测试网络部署了一个地址前缀定制为 ABCD 开头的合约:
需要说明的是,源代码匹配的时候使用了正则,这可能有点慢,如果追求更高的效率,可以牺牲一下匹配的灵活度,通过「strings.HasPrefix」来按字符串匹配。
评论前必须登录!
注册