ICAP: 互換客戶端地址協議

alphaqiu發表於2019-02-16

什麼是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 互換客戶端地址協議,一種IBAN相容系統,用於引用和處理客戶帳戶,旨在簡化資金轉移流程,在交易所之間無憂無慮,並最終使KYC和AML成為過去。

這裡有相關於以太坊對ICAP的介紹

在第三方賬戶之間(特別是交易所賬戶)之間轉賬資金給使用者帶來了相當大的負擔,並且由於客戶賬戶中的存款被識別的方式而容易出錯。現有的銀行業通過擁有一個被稱為IBAN的通用程式碼解決了這個問題。該程式碼合併了機構和客戶帳戶以及錯誤檢測機制,實際上消除了微不足道的錯誤併為使用者提供了相當大的便利。不幸的是,這是一個嚴格監管和集中的服務,只有大型的,完善的機構才能使用。目前的議定書ICAP可被視為適用於以太坊系統中任何含有資金的機構的分散版本。

IBAN介紹

國際銀行賬戶號碼~[1]~ (International Bank Account Number,簡稱IBAN)是各國各銀行之間互相定立的標識號碼,可降低國際間金融操作的失誤。它最初是由歐洲銀行標準委員會(ECBS)通過,後來被採納為國際標準 ISO 13616:1997。目前的標準是ISO 13616:2007,表明SWIFT程式碼(ISO 9362)為正式的格式。最初開發是為了促進歐盟範圍內的支付,但現在也已經實施到大多數歐洲國家和其他國家,尤其是在中東加勒比海地區。IBAN最多包含34個字母和數字字元:首兩個字母是ISO 3166-1α-2國家程式碼,然後兩個校驗位,校驗位可檢查完整性。最後一個是特定國家的基本銀行賬戶號碼(BBAN)。BBAN格式的決定是由每個國家的銀行界的約束下,它必須是一個固定長度的不區分大小寫的英數字。它包括國內銀行賬戶號碼,銀行分行的號碼,和潛在的路由資訊。

基本銀行賬戶號碼

基本銀行賬戶號碼(The Basic Bank Account Number,簡稱BBAN)的格式是由國家中央銀行或相應機關所訂定,格式並沒有強制性。一國的基本銀行賬戶號碼須為固定長度且由大小寫不敏感的英數字組成。其包括本國賬戶號碼,子分支辨識碼與路徑資訊。各國皆可擁有不同的編號系統,最多三十字。

IBAN結構

IBAN程式碼由多達34個不區分大小寫的字母數字字元組成,其字元取值範圍為0-9和A-Z。它包含三個資訊:

  • 國家程式碼; 以下內容的頂級識別符號(ISO 3166-1 alpha-2);
  • 錯誤檢測程式碼; 使用mod-97-10校驗和協議(ISO / IEC 7064:2003);
  • 基本銀行賬號(BBAN); 該機構,分支和客戶賬戶的識別符號,其組成取決於上述國家。

舉例來說,從維基百科可以得知,英國的IBAN格式定義為:

國家 格式 說明
英國?? GBkk bbbb ssss sscc cccc cc b = 銀行程式碼 s = 銀行分類程式碼 c = 賬號

GBkk bbbb ssss sscc cccc cc 格式解讀為:

[國家程式碼:GB][錯誤校驗碼:kk][基本銀行賬號:bbbb ssss sscc cccc cc]

對於英國來說,BBAN由以下部分組成:

  • 機構識別符號,4個字元的字母,例如 MIDL 代表匯豐銀行。
  • 分類程式碼(機構內的分行識別符號),一個6位十進位制數字,例如402702匯豐銀行的Lancaster分行。
  • 帳號(分支機構內的客戶識別符號),一個8位十進位制數字。

以太坊ICAP設計

以太坊引入了新的IBAN國家程式碼:XE,X字首為擴充套件的意思,E表示為以太坊(Ethereum),制定為非法定貨幣(例如XBOX, XEOS)。以太坊在IBAN基礎上設計了ICAP,其設計思路與IBAN相容。目的是為了方便各大公鏈之間轉賬?它有三種BBAN程式碼型別:直接型別,基本型別和間接型別。

直接型別

此程式碼的直接BBAN為30個字元,將包含一個欄位:

  • 帳戶識別符號,30個字母數字(<155位)。這將被解釋為表示160位以太坊地址的最低有效位的大端編碼的36進位制整數。因此,這些以太坊地址通常以零位元組開始。

例如XE 73 38O073KYGTWWZN0F2WZ0R8PX5ZPPZS,它的對應的地址00c5496aee77c1ba1f0854206a26dda82a81d6d8

基本型別

與直接編碼相同,但程式碼為31個字元(與IBAN不相容)並組成相同的單個欄位:

  • 帳戶識別符號,31個字元字母數字(<161位)。這將被解釋為一個代表160位以太坊地址的大端編碼的36進位制整數。

間接型別

該程式碼的BBAN在間接時將為16個字元,並將包含三個欄位:

  • 資產識別符號,3個字元的字母數字(<16位);
  • 機構識別符號,4個字元的字母數字(<21位);
  • 機構客戶識別符號,9個字元的字母數字(<47位);

包括四個首字元,這導致最終的客戶帳戶地址長度為20個字元,格式為:

XE81ETHXREGGAVOFYORK

分成:

  • XE 以太坊的國家程式碼;
  • 81 校驗和;
  • ETH 客戶賬戶中的資產識別符號 – 在這種情況下,“ETH”是唯一有效的資產識別符號,因為以太坊的基礎註冊管理機構合同僅支援該資產;
  • XREG 賬戶的機構程式碼 – 在這種情況下,以太坊的基礎註冊管理機構合同;
  • GAVOFYORK 該機構內的客戶識別符號 – 在這種情況下,直接支付且無任何主要地址的額外資料與以太坊基礎註冊合同中的名稱“GAVOFYORK”相關聯;

URI表示形式

iban:XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS

具體實現:以太坊地址轉換成ICAP格式

轉換函式

//ConvertAddressToICAP 將以太坊地址轉換為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)
    }
    // 以太坊國家程式碼XE + 校驗碼 + 賬號
    icap := join("XE", checkDigits(enc), enc)
    return icap, nil
}

36進位制編碼

其規則如同十進位制數轉十六進位制數,其演算法如下:

十六進位制數取值範圍表示s=”0123456789ABCDEF”。其字串陣列下標範圍為0-15,其中對應的下標:A=11,B=12… F=15。

記餘數陣列a,

  1. 把10進位制數除以16,獲取整數商和餘數,記下餘數和整數商,並把餘數放入餘數陣列a;
  2. 整數商不為0時,再次執行第一個步驟,整數商為0時停止,並記錄下此時的餘數;
  3. 倒排餘數陣列a,同時影射每個位置值到s字串陣列下標對應的值。

例如:十進位制數505,500/16 得31餘9,將9放入餘數陣列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進位制編碼也是採用瞭如上演算法,只是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)
}

校驗碼

校驗碼使用了mod-97-10校驗和協議 (ISO / IEC 7064:2003),檢視ISO具體協議需要8,800瑞士法郎。

舉例,對於字串794,其演算法步驟為:

步驟1:追加兩個零佔據校驗字元位置:79400;
步驟2:除以97,得到商818和整數餘數54;
步驟3:將校驗字元值確定為(97 + 1) – 54 = 44並將其附加到原始字串以給出79444。

為了檢查,將字串除以97; 如果提醒是1,則校驗通過。

計算國際銀行帳號(IBAN)的校驗位(xx yyy zzzzzzzz kk)

  • 餘數不等於0的計算。

不包括支票號碼的銀行賬號:06 000 01234567。

a:06 000 01234567 00
b:060000123456700:97 = 618557973780餘數= 40
c:(97 + 1) – 40 = 58結果:06 000 01234567 58

校驗和:06000123456758:97 = 618557973781其餘= 1

  • 無餘數計算或餘數等於0。

不包括支票號碼的銀行賬號:06 000 01234586。

a:06 000 01234586 00
b:060000123458600:97 = 618557973800其餘= 00
c:(97 + 1) – 00 = 98結果:06 000 01234586 98

校驗和:06000123458698:97 = 618557973801餘數= 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碼錶中分別對應10進位制值為65,66...
            //字元A-Z的36進位制字串索引分別是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
}

具體實現:ICAP格式轉換成以太坊地址

校驗

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
}

轉換

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錢包二維碼使用了ICAP格式,掃碼之後出現的格式為:

iban:XE86G29C8IV34UOJMYWHGDSGME33YKEC3QO?account=100&type=ETH

其中XE86G29C8IV34UOJMYWHGDSGME33YKEC3QO是轉賬的地址,account是轉賬的數額,type是轉賬的型別,這裡代表轉ETH。

BOX將相容imToken格式。

完整的程式碼片段可以在這裡找到:https://gist.github.com/alpha…

參考

1.) Wikipedia: International Bank Account Number

2.) ICAP: Inter exchange Client Address Protocol

3.) Open source: BOX a business wallet solution

相關文章