以太坊的iban格式地址生成

2018/07/11 note 共 6227 字,约 18 分钟

简介

imToken钱包转账的二维码扫描后信息为iban开头的一段字符串:
iban:XE86G29C8IV34UOJMYWHGDSGME33YKEC3QO?account=100&type=ETH
这种格式是ICAP: 互换客户端地址协议定义的,有关ICAP协议的简介如下:

Interexchange Client Address Protocol, an IBAN-compatible system for referencing and transacting to client accounts aimed to streamline the process of transferring funds, worry-free between exchanges and, ultimately, making KYC and AML concerns a thing of the past.
ICAP 互换客户端地址协议,一种IBAN兼容系统,用于引用和处理客户帐户,旨在简化资金转移流程,在交易所之间无忧无虑,并最终使KYC和AML成为过去。

以太坊有关ICAP的wiki:ICAP: Inter exchange Client Address Protocol

生成过程

比如在以太坊的wiki中,提到了一个例子:地址0x00c5496aee77c1ba1f0854206a26dda82a81d6d8对应的ICAP格式地址为XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS
(ps:wiki中的例子本来是错误的,我给修改了,但是也没看到审核的过程,直接修改成功了)

步骤1:将地址转为36进制编码

将地址0x00c5496aee77c1ba1f0854206a26dda82a81d6d8看做是16进制编码的一个大数,把这个大数按照36进制编码输出并前补0到30个字符。
示例代码:

BigInteger value = new BigInteger(address, 16);  
StringBuilder bban = new StringBuilder(value.toString(36).toUpperCase());  
while (bban.length() < 15 * 2) {  
    bban.insert(0, '0');  
}  

输出的字符串为:38O073KYGTWWZN0F2WZ0R8PX5ZPPZS

其实这也可以理解为base36编码的过程,编码对应的字符串范围为0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

步骤2:计算校验位

1、 将36进制编码的字符串38O073KYGTWWZN0F2WZ0R8PX5ZPPZS前面补上XE00(数字0不是字母O),其中00的位置就是需要填写校验位的位置。
2、 将补上的XE00放到编码字符串的末尾(为啥不直接补在末尾?),此时字符串为38O073KYGTWWZN0F2WZ0R8PX5ZPPZSXE00
3、 对上面的字符串,取出每一位,按照0=0,1=1, ... 9=9,A=10,B=11 ... Z=35的规则,将单个字符转换以后的结果拼接成一个新的字符串:

3 8 O  0 7 3 K  Y  G  T  W  W  Z  N  0 F  2 W  Z  0 R  8 P  X  5 Z  P  P  Z  S  X  E  0 0  
3 8 24 0 7 3 20 34 16 29 32 32 35 23 0 15 2 32 35 0 27 8 25 33 5 35 25 25 35 28 33 14 0 0  

结果字符串为:38240732034162932323523015232350278253353525253528331400
4、 将上面的字符串看做是一个10进制的大数,除以97取余:
38240732034162932323523015232350278253353525253528331400 % 97=25
取余结果为25
5、 校验位=(97 + 1) - 25=73
6、 将73转为2位的字符串替换开头的校验位位置,ICAP结果就是:XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS

java代码实现

代码实现参见:IBAN.java

代码优化

代码中取余的操作是不断截取字符串换成int值取余做的,换成java的BigInteger类进行取余操作效率会更高
这个类代码也不多,把修改后的代码复制到这里保存:

import java.math.BigInteger;  
  
public class IBAN {  
  
    public static class Iban {  
        private String address;  
        private String token;  
        private String amount;  
  
        public Iban(String address, String token, String amount) {  
            this.address = address;  
            this.token = token;  
            this.amount = amount;  
        }  
  
        public String getAmount() {  
            return amount;  
        }  
  
        public String getAddress() {  
            return address;  
        }  
  
        public String getToken() {  
            return token;  
        }  
  
        public void setAmount(String amount) {  
            this.amount = amount;  
        }  
  
        public void setAddress(String address) {  
            this.address = address;  
        }  
  
        public void setToken(String token) {  
            this.token = token;  
        }  
  
        @Override  
        public String toString() {  
            return "Iban [address=" + address + ", token=" + token + ", amount=" + amount + "]";  
        }  
    }  
  
    private static boolean validateIBAN(String iban) {  
        int len = iban.length();  
        if (len < 4 || !iban.matches("[0-9A-Z]+"))  
            return false;  
  
        iban = iban.substring(4) + iban.substring(0, 4);  
  
        StringBuilder sb = new StringBuilder();  
        for (int i = 0; i < len; i++)  
            sb.append(Character.digit(iban.charAt(i), 36));  
  
        BigInteger bigInt = new BigInteger(sb.toString());  
  
        return bigInt.mod(BigInteger.valueOf(97)).intValue() == 1;  
    }  
  
    public IBAN() {  
        // TODO Auto-generated constructor stub  
    }  
  
    public Iban decode(String result) {  
        int ibanEndpoint = result.indexOf("?");  
        String iban = result.substring(5, ibanEndpoint < 0 ? result.length() : ibanEndpoint);  
        String address = this.toAddress(iban);  
        String query = result.substring(ibanEndpoint + 1, result.length());  
        String[] params = query.split("&");  
        String token = null;  
        String amount = null;  
        for (String param : params) {  
            if (param.startsWith("token=")) {  
                token = param.substring(6);  
                continue;  
            }  
            if (param.startsWith("amount=")) {  
                amount = param.substring(7);  
            }  
        }  
        return new Iban(address, token, amount);  
    }  
  
    public String encode(String address, String token, String amount) throws Exception {  
        return String.format("iban:%s?token=%s&amount=%s", this.toIban(address), token, amount);  
    }  
  
    private String toAddress(String iban) {  
        String base36 = iban.substring(4);  
        StringBuilder base16 = new StringBuilder(new BigInteger(base36, 36).toString(16));  
        while (base16.length() < 20) {  
            base16.insert(0, "0");  
        }  
        return "0x" + base16.toString().toLowerCase();  
    }  
  
    public String toIban(String address) throws Exception {  
        if(address.length() != 42) {  
            throw new Exception("The length of address is 42.");  
        }  
        address = address.toLowerCase().substring(2);  
        BigInteger value = new BigInteger(address, 16);  
        StringBuilder bban = new StringBuilder(value.toString(36).toUpperCase());  
        while (bban.length() < 15 * 2) {  
            bban.insert(0, '0');  
        }  
        String iban = "XE00" + bban;  
  
        iban = iban.substring(4) + iban.substring(0, 4);  
        StringBuilder code = new StringBuilder();  
        for (int i = 0; i < iban.length(); i++) {  
            char chr = iban.charAt(i);  
            if (chr >= 'A' && chr <= 'Z') {  
                int temp = chr - 'A' + 10;  
                code.append(String.valueOf(temp));  
            } else {  
                code.append(String.valueOf((chr - '0')));  
            }  
        }  
  
//        String remainder = code.toString();  
//        String block;  
//        while (remainder.length() > 2) {  
//            int endPoint = remainder.length() >= 9 ? 9 : remainder.length();  
//            block = remainder.substring(0, endPoint);  
//            remainder = parseInt(block, 10) % 97 + remainder.substring(block.length());  
//        }  
//  
//        int checkNum = parseInt(remainder, 10) % 97;  
//        String checkDigit = ("0" + (98 - checkNum));  
//        checkDigit = checkDigit.substring(checkDigit.length() - 2);  
  
        // 把上面代码换成大数的取余操作  
        BigInteger biTem = new BigInteger(code.toString(), 10);  
        BigInteger biRemainder = biTem.remainder(BigInteger.valueOf(97));  
        String checkDigit = BigInteger.valueOf(97 + 1).subtract(biRemainder).toString(10);  
        checkDigit = "0" + checkDigit;  
        checkDigit = checkDigit.substring(checkDigit.length() - 2);  
  
        String ibanAddress = "XE" + checkDigit + bban;  
        if (validateIBAN(ibanAddress)) {  
            return ibanAddress;  
        }  
        return null;  
    }  
  
    public static void main(String[] args) throws Exception {  
        IBAN iban = new IBAN();  
        // TODO Auto-generated method stub  
        String address = iban.toAddress("XE039RBH0XKV9FZMTH2701Q37FLX10NTWXU");  
        System.out.println("IBAN to Address: " + address);  
  
        String ibanAddress = iban.toIban("0x00c5496aee77c1ba1f0854206a26dda82a81d6d8");  
        System.out.println("Address to IBAN: " + ibanAddress);  
        String ibanAddress2 = iban.toIban("0xc94770007dda54cF92009BFF0dE90c06F603a09f");  
        System.out.println("Address to IBAN: " + ibanAddress2);  
  
        Iban ibanObj = iban.decode("iban:XE039RBH0XKV9FZMTH2701Q37FLX10NTWXU?token=ETH&amount=5");  
        System.out.println("IBAN decode: " + ibanObj.toString());  
  
        String ibanString = iban.encode("0x538b392D57d867A57eE8Eed05737cB08B4691302", "NBRC", "5");  
        System.out.println("IBAN encode: " + ibanString);  
        System.out.println("0x538b392d57d867a57ee8eed05737cb08b4691302".equals("0x538b392d57d867a57ee8eed05737cb08b4691302"));  
    }  
}  

代码执行的log:

IBAN to Address: 0x538b392d57d867a57ee8eed05737cb08b4691302  
Address to IBAN: XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS  
Address to IBAN: XE15NIF30EOWCXHSNK5SW4QJRGJQ7NS3AN3  
IBAN decode: Iban [address=0x538b392d57d867a57ee8eed05737cb08b4691302, token=ETH, amount=5]  
IBAN encode: iban:XE039RBH0XKV9FZMTH2701Q37FLX10NTWXU?token=NBRC&amount=5  
true  

文档信息

Search

    Table of Contents