什么是ICAP?從以太坊Homestead指南的詞匯表中可以看出:
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 互換客戶端地址協(xié)議,一種IBAN兼容系統(tǒng),用于引用和處理客戶帳戶,旨在簡(jiǎn)化資金轉(zhuǎn)移流程,在交易所之間無(wú)憂無(wú)慮,并最終使KYC和AML成為過(guò)去。
這里有相關(guān)于以太坊對(duì)ICAP的介紹:
在第三方賬戶之間(特別是交易所賬戶)之間轉(zhuǎn)賬資金給用戶帶來(lái)了相當(dāng)大的負(fù)擔(dān),并且由于客戶賬戶中的存款被識(shí)別的方式而容易出錯(cuò)?,F(xiàn)有的銀行業(yè)通過(guò)擁有一個(gè)被稱(chēng)為IBAN的通用代碼解決了這個(gè)問(wèn)題。該代碼合并了機(jī)構(gòu)和客戶帳戶以及錯(cuò)誤檢測(cè)機(jī)制,實(shí)際上消除了微不足道的錯(cuò)誤并為用戶提供了相當(dāng)大的便利。不幸的是,這是一個(gè)嚴(yán)格監(jiān)管和集中的服務(wù),只有大型的,完善的機(jī)構(gòu)才能使用。目前的議定書(shū)ICAP可被視為適用于以太坊系統(tǒng)中任何含有資金的機(jī)構(gòu)的分散版本。
IBAN介紹
國(guó)際銀行賬戶號(hào)碼[1] (International Bank Account Number,簡(jiǎn)稱(chēng)IBAN)是各國(guó)各銀行之間互相定立的標(biāo)識(shí)號(hào)碼,可降低國(guó)際間金融操作的失誤。它最初是由歐洲銀行標(biāo)準(zhǔn)委員會(huì)(ECBS)通過(guò),后來(lái)被采納為國(guó)際標(biāo)準(zhǔn) ISO 13616:1997。目前的標(biāo)準(zhǔn)是ISO 13616:2007,表明SWIFT代碼(ISO 9362)為正式的格式。最初開(kāi)發(fā)是為了促進(jìn)歐盟范圍內(nèi)的支付,但現(xiàn)在也已經(jīng)實(shí)施到大多數(shù)歐洲國(guó)家和其他國(guó)家,尤其是在中東和加勒比海地區(qū)。IBAN最多包含34個(gè)字母和數(shù)字字符:首兩個(gè)字母是ISO 3166-1α-2國(guó)家代碼,然后兩個(gè)校驗(yàn)位,校驗(yàn)位可檢查完整性。最后一個(gè)是特定國(guó)家的基本銀行賬戶號(hào)碼(BBAN)。BBAN格式的決定是由每個(gè)國(guó)家的銀行界的約束下,它必須是一個(gè)固定長(zhǎng)度的不區(qū)分大小寫(xiě)的英數(shù)字。它包括國(guó)內(nèi)銀行賬戶號(hào)碼,銀行分行的號(hào)碼,和潛在的路由信息。
基本銀行賬戶號(hào)碼
基本銀行賬戶號(hào)碼(The Basic Bank Account Number,簡(jiǎn)稱(chēng)BBAN)的格式是由國(guó)家中央銀行或相應(yīng)機(jī)關(guān)所訂定,格式并沒(méi)有強(qiáng)制性。一國(guó)的基本銀行賬戶號(hào)碼須為固定長(zhǎng)度且由大小寫(xiě)不敏感的英數(shù)字組成。其包括本國(guó)賬戶號(hào)碼,子分支辨識(shí)碼與路徑資訊。各國(guó)皆可擁有不同的編號(hào)系統(tǒng),最多三十字。
IBAN結(jié)構(gòu)
IBAN代碼由多達(dá)34個(gè)不區(qū)分大小寫(xiě)的字母數(shù)字字符組成,其字符取值范圍為0-9和A-Z。它包含三個(gè)信息:
- 國(guó)家代碼; 以下內(nèi)容的頂級(jí)標(biāo)識(shí)符(ISO 3166-1 alpha-2);
- 錯(cuò)誤檢測(cè)代碼; 使用mod-97-10校驗(yàn)和協(xié)議(ISO / IEC 7064:2003);
- 基本銀行賬號(hào)(BBAN); 該機(jī)構(gòu),分支和客戶賬戶的標(biāo)識(shí)符,其組成取決于上述國(guó)家。
舉例來(lái)說(shuō),從維基百科可以得知,英國(guó)的IBAN格式定義為:
| 國(guó)家 | 格式 | 說(shuō)明 |
|---|---|---|
| 英國(guó)???? | GBkk bbbb ssss sscc cccc cc |
b = 銀行代碼 s = 銀行分類(lèi)代碼 c = 賬號(hào) |
該 GBkk bbbb ssss sscc cccc cc 格式解讀為:
[國(guó)家代碼:GB][錯(cuò)誤校驗(yàn)碼:kk][基本銀行賬號(hào):bbbb ssss sscc cccc cc]
對(duì)于英國(guó)來(lái)說(shuō),BBAN由以下部分組成:
- 機(jī)構(gòu)標(biāo)識(shí)符,4個(gè)字符的字母,例如
MIDL代表匯豐銀行。 - 分類(lèi)代碼(機(jī)構(gòu)內(nèi)的分行標(biāo)識(shí)符),一個(gè)6位十進(jìn)制數(shù)字,例如
402702匯豐銀行的Lancaster分行。 - 帳號(hào)(分支機(jī)構(gòu)內(nèi)的客戶標(biāo)識(shí)符),一個(gè)8位十進(jìn)制數(shù)字。
以太坊ICAP設(shè)計(jì)
以太坊引入了新的IBAN國(guó)家代碼:XE,X前綴為擴(kuò)展的意思,E表示為以太坊(Ethereum),制定為非法定貨幣(例如XBOX, XEOS)。以太坊在IBAN基礎(chǔ)上設(shè)計(jì)了ICAP,其設(shè)計(jì)思路與IBAN兼容。目的是為了方便各大公鏈之間轉(zhuǎn)賬?它有三種BBAN代碼類(lèi)型:直接類(lèi)型,基本類(lèi)型和間接類(lèi)型。
直接類(lèi)型
此代碼的直接BBAN為30個(gè)字符,將包含一個(gè)字段:
- 帳戶標(biāo)識(shí)符,30個(gè)字母數(shù)字(<155位)。這將被解釋為表示160位以太坊地址的最低有效位的大端編碼的36進(jìn)制整數(shù)。因此,這些以太坊地址通常以零字節(jié)開(kāi)始。
例如XE 73 38O073KYGTWWZN0F2WZ0R8PX5ZPPZS,它的對(duì)應(yīng)的地址00c5496aee77c1ba1f0854206a26dda82a81d6d8。
基本類(lèi)型
與直接編碼相同,但代碼為31個(gè)字符(與IBAN不兼容)并組成相同的單個(gè)字段:
- 帳戶標(biāo)識(shí)符,31個(gè)字符字母數(shù)字(<161位)。這將被解釋為一個(gè)代表160位以太坊地址的大端編碼的36進(jìn)制整數(shù)。
間接類(lèi)型
該代碼的BBAN在間接時(shí)將為16個(gè)字符,并將包含三個(gè)字段:
- 資產(chǎn)標(biāo)識(shí)符,3個(gè)字符的字母數(shù)字(<16位);
- 機(jī)構(gòu)標(biāo)識(shí)符,4個(gè)字符的字母數(shù)字(<21位);
- 機(jī)構(gòu)客戶標(biāo)識(shí)符,9個(gè)字符的字母數(shù)字(<47位);
包括四個(gè)首字符,這導(dǎo)致最終的客戶帳戶地址長(zhǎng)度為20個(gè)字符,格式為:
XE81ETHXREGGAVOFYORK
分成:
-
XE以太坊的國(guó)家代碼; -
81校驗(yàn)和; -
ETH客戶賬戶中的資產(chǎn)標(biāo)識(shí)符 - 在這種情況下,“ETH”是唯一有效的資產(chǎn)標(biāo)識(shí)符,因?yàn)橐蕴坏幕A(chǔ)注冊(cè)管理機(jī)構(gòu)合同僅支持該資產(chǎn); -
XREG賬戶的機(jī)構(gòu)代碼 - 在這種情況下,以太坊的基礎(chǔ)注冊(cè)管理機(jī)構(gòu)合同; -
GAVOFYORK該機(jī)構(gòu)內(nèi)的客戶標(biāo)識(shí)符 - 在這種情況下,直接支付且無(wú)任何主要地址的額外數(shù)據(jù)與以太坊基礎(chǔ)注冊(cè)合同中的名稱(chēng)“GAVOFYORK”相關(guān)聯(lián);
URI表示形式
iban:XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS
具體實(shí)現(xiàn):以太坊地址轉(zhuǎn)換成ICAP格式
轉(zhuǎn)換函數(shù)
//ConvertAddressToICAP 將以太坊地址轉(zhuǎn)換為ICAP格式的地址
func ConvertAddressToICAP(a common.Address) (string, error) {
enc := base36Encode(a.Big())
if len(enc) < 30 {
enc = join(strings.Repeat("0", 30-len(enc)), enc)
}
// 以太坊國(guó)家代碼XE + 校驗(yàn)碼 + 賬號(hào)
icap := join("XE", checkDigits(enc), enc)
return icap, nil
}
36進(jìn)制編碼
其規(guī)則如同十進(jìn)制數(shù)轉(zhuǎn)十六進(jìn)制數(shù),其算法如下:
十六進(jìn)制數(shù)取值范圍表示s="0123456789ABCDEF"。其字符串?dāng)?shù)組下標(biāo)范圍為0-15,其中對(duì)應(yīng)的下標(biāo):A=11,B=12… F=15。
記余數(shù)數(shù)組a,
- 把10進(jìn)制數(shù)除以16,獲取整數(shù)商和余數(shù),記下余數(shù)和整數(shù)商,并把余數(shù)放入余數(shù)數(shù)組a;
- 整數(shù)商不為0時(shí),再次執(zhí)行第一個(gè)步驟,整數(shù)商為0時(shí)停止,并記錄下此時(shí)的余數(shù);
- 倒排余數(shù)數(shù)組a,同時(shí)影射每個(gè)位置值到s字符串?dāng)?shù)組下標(biāo)對(duì)應(yīng)的值。
例如:十進(jìn)制數(shù)505,500/16 得31余9,將9放入余數(shù)數(shù)組a,由于31不為0,再次用31/16 得1余15,將15放入a,1不為0,再次用1/16 得0余1,將1放入a,倒排a,得[s[1],s[15],s[9]] = ['1', 'F', '9'] = "0x1F9" = 505。
36進(jìn)制編碼也是采用了如上算法,只是s="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"。
var (
Base36Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Big36 = big.NewInt(36)
)
func base36Encode(i *big.Int) string {
var chars []rune
x := new(big.Int)
for {
x.Mod(i, Big36)
chars = append(chars, rune(Base36Chars[x.Uint64()]))
i.Div(i, Big36)
if i.Cmp(Big0) == 0 {
break
}
}
// reverse slice
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
chars[i], chars[j] = chars[j], chars[i]
}
return string(chars)
}
校驗(yàn)碼
校驗(yàn)碼使用了mod-97-10校驗(yàn)和協(xié)議 (ISO / IEC 7064:2003),查看ISO具體協(xié)議需要8,800瑞士法郎。
舉例,對(duì)于字符串794,其算法步驟為:
步驟1:追加兩個(gè)零占據(jù)校驗(yàn)字符位置:79400;
步驟2:除以97,得到商818和整數(shù)余數(shù)54;
步驟3:將校驗(yàn)字符值確定為(97 + 1) - 54 = 44并將其附加到原始字符串以給出79444。
為了檢查,將字符串除以97; 如果提醒是1,則校驗(yàn)通過(guò)。
計(jì)算國(guó)際銀行帳號(hào)(IBAN)的校驗(yàn)位(xx yyy zzzzzzzz kk)
- 余數(shù)不等于0的計(jì)算。
不包括支票號(hào)碼的銀行賬號(hào):06 000 01234567。
a:06 000 01234567 00
b:060000123456700:97 = 618557973780余數(shù)= 40
c:(97 + 1) - 40 = 58結(jié)果:06 000 01234567 58校驗(yàn)和:06000123456758:97 = 618557973781其余= 1
- 無(wú)余數(shù)計(jì)算或余數(shù)等于0。
不包括支票號(hào)碼的銀行賬號(hào):06 000 01234586。
a:06 000 01234586 00
b:060000123458600:97 = 618557973800其余= 00
c:(97 + 1) - 00 = 98結(jié)果:06 000 01234586 98。校驗(yàn)和:06000123458698:97 = 618557973801余數(shù)= 1。
func checkDigits(s string) string {
expanded, _ := iso13616Expand(strings.Join([]string{s, "XE00"}, ""))
num, _ := new(big.Int).SetString(expanded, 10)
//mod-97-10
num.Sub(Big98, num.Mod(num, Big97))
checkDigits := num.String()
// zero padd checksum
if len(checkDigits) == 1 {
checkDigits = join("0", checkDigits)
}
return checkDigits
}
// not base-36, but expansion to decimal literal: A = 10, B = 11, ... Z = 35
func iso13616Expand(s string) (string, error) {
var parts []string
if !validBase36(s) {
return "", errICAPEncoding
}
for _, c := range s {
i := uint64(c)
if i >= 65 {
//字符A-Z在ASCII碼表中分別對(duì)應(yīng)10進(jìn)制值為65,66...
//字符A-Z的36進(jìn)制字符串索引分別是A=10,B=11...
//字符的碼表值-字符的索引值=55
//字符碼表值-55=字符的索引值
parts = append(parts, strconv.FormatUint(uint64(c)-55, 10))
} else {
parts = append(parts, string(c))
}
}
return join(parts...), nil
}
具體實(shí)現(xiàn):ICAP格式轉(zhuǎn)換成以太坊地址
校驗(yàn)
func validCheckSum(s string) error {
s = join(s[4:], s[:4])
expanded, err := iso13616Expand(s)
if err != nil {
return err
}
checkSumNum, _ := new(big.Int).SetString(expanded, 10)
if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 {
return errICAPChecksum
}
return nil
}
轉(zhuǎn)換
func parseICAP(s string) (common.Address, error) {
if !strings.HasPrefix(s, "XE") {
return common.Address{}, errICAPCountryCode
}
if err := validCheckSum(s); err != nil {
return common.Address{}, err
}
// checksum is ISO13616, Ethereum address is base-36
bigAddr, _ := new(big.Int).SetString(s[4:], 36)
return common.BigToAddress(bigAddr), nil
}
用途
imToken錢(qián)包二維碼使用了ICAP格式,掃碼之后出現(xiàn)的格式為:
iban:XE86G29C8IV34UOJMYWHGDSGME33YKEC3QO?account=100&type=ETH
其中XE86G29C8IV34UOJMYWHGDSGME33YKEC3QO是轉(zhuǎn)賬的地址,account是轉(zhuǎn)賬的數(shù)額,type是轉(zhuǎn)賬的類(lèi)型,這里代表轉(zhuǎn)ETH。
BOX將兼容imToken格式。
完整的代碼片段可以在這里找到:https://gist.github.com/alphaqiu/646d45fc5c2b1f42d8529d55b52f58d2
參考
1.) Wikipedia: International Bank Account Number