eip | title | author | type | category | status | created |
---|---|---|---|---|---|---|
55
|
Mixed-case checksum address encoding
|
Vitalik Buterin
|
Standards Track
|
ERC
|
Final
|
2016-01-14
|
Specification(python)
from ethereum import utils def checksum_encode(addr): # Takes a 20-byte binary address as input o = '' v = utils.big_endian_to_int(utils.sha3(addr.hex())) for i, c in enumerate(addr.hex()): if c in '0123456789'://就是如果address在i位置上的值是数字的话,就不做任何改变 o += c else: //但是如果是字符的话,就要另进行判断,(2**(255 - 4*i))这个的二进制的结果就是从后向前数的255 - 4*i个位置上的值为1,即是为了实现从前往后数为4*i位置的数字 o += c.upper() if (v & (2**(255 - 4*i))) else c.lower()//255的原因是hash的v有32bytes,即32*8=256,从0开始
//所以意思是如果小写十六进制地址的散列v的第4*i位也是1,则以大写形式打印,否则以小写形式打印。 return '0x'+o def test(addrstr): assert(addrstr == checksum_encode(bytes.fromhex(addrstr[2:]))) test('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed') test('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359') test('0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB') test('0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb')
注意:v = utils.big_endian_to_int(utils.sha3(addr.hex()))
所谓的大端模式(Big-endian),是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;比如1234,如果是数字的话应该从低位开始读4321(即小端模式);但是这里为大端模式,所以读法就是1234。这是为了顺序处理addr的hash值
In English, convert the address to hex, but if the i
th digit is a letter (ie. it's one of abcdef
) print it in uppercase if the 4*i
th bit of the hash of the lowercase hexadecimal address is 1 otherwise print it in lowercase.
Rationale
Benefits:
- Backwards compatible with many hex parsers that accept mixed case, allowing it to be easily introduced over time
- Keeps the length at 40 characters。 address长度为40个字符
- On average there will be 15 check bits per address, and the net probability that a randomly generated address if mistyped will accidentally pass a check is 0.0247%. This is a ~50x improvement over ICAP, but not as good as a 4-byte check code. 上面bit的转换效率没有字节的转化效率快,所以下面实现的是半字节(16进制一字符4bits)形式的转换方法:
例子:
const createKeccakHash = require('keccak'); function toChecksumAddress (address) { address = address.toLowerCase().replace('0x', ''); console.log(address); var hash = createKeccakHash('keccak256').update(address).digest('hex');//update就是输入要加密的值,digest就是将加密好的hash值以16进制的形式输出 console.log(hash);//5cfac663f45837b409c4d3dc1cef5f4759734f4989dd53a31b1265734c0b28f4,64个 var ret = '0x'; for (var i = 0; i < address.length; i++) {//所以只用得到hash的前40个字符 if (parseInt(hash[i], 16) >= 8) {//即将16进制的hash[i]转化成10进制的数值后与8进行比较,如果在i位置的hash的值大于或等于8,相应位置的address的值就换成大写,否则就还是小写 ret += address[i].toUpperCase();//其实就是根据得到的hash值来相应将address转换成大小写皆有的形式 console.log('if'); console.log(ret); } else { ret += address[i]; console.log('else'); console.log(ret); } } return ret; } var addr = '0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359';//去掉0x是40个 console.log(toChecksumAddress(addr));
这里的例子address是16进制的,当然,如果the hex address encoded as ASCII,那么就写成:
var hash = createKeccakHash('keccak256').update(Buffer.from(address.toLowerCase(), 'ascii')).digest()
Adoption
Wallet | displays checksummed addresses | rejects invalid mixed-case | rejects too short | rejects too long |
---|---|---|---|---|
Etherwall 2.0.1 | Yes | Yes | Yes | Yes |
Jaxx 1.2.17 | No | Yes | Yes | Yes |
MetaMask 3.7.8 | Yes | Yes | Yes | Yes |
Mist 0.8.10 | Yes | Yes | Yes | Yes |
MyEtherWallet v3.9.4 | Yes | Yes | Yes | Yes |
Parity 1.6.6-beta (UI) | Yes | Yes | Yes | Yes |
Exchange support for mixed-case address checksums, as of 2017-05-27:
Exchange | displays checksummed deposit addresses | rejects invalid mixed-case | rejects too short | rejects too long |
---|---|---|---|---|
Bitfinex | No | Yes | Yes | Yes |
Coinbase | Yes | No | Yes | Yes |
GDAX | Yes | Yes | Yes | Yes |
Kraken | No | No | Yes | Yes |
Poloniex | No | No | Yes | Yes |
Shapeshift | No | No | Yes | Yes |
References
- EIP 55 issue and discussion https://github.com/ethereum/eips/issues/55
- Python implementation in
ethereum-utils
- Ethereumjs-util implementation https://github.com/ethereumjs/ethereumjs-util/blob/75f529458bc7dc84f85fd0446d0fac92d991c262/index.js#L452-L466
/** * Returns a checksummed address * @param {String} address * @return {String} */ exports.toChecksumAddress = function (address) { address = exports.stripHexPrefix(address).toLowerCase() var hash = exports.sha3(address).toString('hex') var ret = '0x' for (var i = 0; i < address.length; i++) { if (parseInt(hash[i], 16) >= 8) { ret += address[i].toUpperCase() } else { ret += address[i] } } return ret }
4.Swift implementation in EthereumKit