第三章 金融 KYC 平台系統架構
3.4 Smart Contract 設計
國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
36
圖 3.1 金融 KYC 平台聯盟鏈架構示意圖 資料來源:本研究整理
3.4 Smart Contract 設計
本研究的聯盟鏈是使用 ethereum homestead 版本,smart contract 是透過 solidity 的語言進行開發。
另外,本研究的 KYC 資料,是以富邦投信之”客戶投資適性分析表及風險預 告書”為範本做一個展示,並假設所有聯盟鏈中各銀行的 KYC 資料格式都是標準 一樣的。將其包含的 KYC 資訊,原始資料存放在銀行,而 Hash/簽章等相關的 資料放置於區塊鏈中以方便 Dapp 進行資料的比對。
底下為此風險預告書之原始資料:
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
37
圖 3.2 富邦投信客戶投資適性分析表及風險預告書(一) 資料來源:富邦金控網站”
https://www.fubon.com/asset-management/download/download_asset-management/2 0160301-03.pdf”
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
38
圖 3.3 富邦投信客戶投資適性分析表及風險預告書(二) 資料來源:富邦金控網站”
https://www.fubon.com/asset-management/download/download_asset-management/2 0160301-03.pdf”
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
39
根據以上的風險預告書原始資料,共有 27 個 KYC 的屬性。本研究在 smart contract(solidity)上針對每一筆 KYC 屬性設計其儲存及異動架構範例如下圖說明。
以姓名(name)屬性為例,姓名屬性會有一個 structure 來儲存其附屬的屬性資料,
分別記錄 1. 姓名的 HASH 值(hash_value) 2. 姓名屬性的註冊銀行(owner_of_bank) 3. 姓名屬性註冊銀行針對該屬性 hash 值,用其私鑰的簽章(digital_sig) 4. 是否允 許其他銀行查詢(queriable) 等四個資訊。而此姓名屬性(name)會透過 solidity 的 mapping 功能,以使用者的身分證/統編資料的 hash 資訊,透過 name_mapping 在不同使用者間指向到正確使用者的 structure 資料。而要異動姓名屬性時,必須 透過姓名異動的獨立函式(set_name)來異動,並在此函式的參數中,帶入使用者 身分證/統編、hash 值、註冊銀行、簽章資訊、是否同意存取等資訊,來對此姓 名屬性裡的相關附屬屬性做資料變更,在函式最後,會執行一個事件(event)宣告,
告知 ethereum 有一個事件發生,並在事件中附加使用者 id hash,以及式執行什 麼異動(set_name)以及是那間註冊銀行(owner_of_bank)異動的資訊,此事件後續 能夠做為是否要讓此平台進行主動的 KYC 同步動作的參考。
圖 3.4 智慧合約 KYC 資料架構圖範例,以姓名(name)為例 資料來源:本研究整理
‧
在實作後發現,ethereum 的 smart contract 對於 contract 的裡頭變數及函數傳遞的 參數數量大小有其限制,而且當變數的屬性若為 string 屬性,會比使用 bytes32 宣告要花費更多的 gas 去建立 contract。若塞入所有的 KYC 欄位,則在建立該 contract 時會發生問題,導致該 contract 無法使用,由於本研究的假設情境是在 聯盟鏈中執行,並不需要考慮 gas 的使用狀況,可以在 Dapp 程式中給予足夠的‧
struct p_income_monthly { #客戶個人月收入
string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct p_home_income_yearly { #客戶家庭月收入 string hash_value;
struct c_business_type { #法人行業別
string hash_value;
string owner_of_bank;
string digital_sig;
‧
mapping (bytes32 => exist) public exist_mapping;
mapping (bytes32 => name) public name_mapping;
mapping (bytes32 => phone) public phone_mapping;
mapping (bytes32 => id) public id_mapping;
mapping (bytes32 => p_education) public p_education_mapping;
mapping (bytes32 => p_birthday) public p_birthday_mapping;
mapping (bytes32 => p_career) public p_career_mapping;
mapping (bytes32 => p_income_monthly) public p_income_monthly_mapping;
mapping (bytes32 => p_home_income_yearly) public p_home_income_yearly_mapping;
mapping (bytes32 => p_insurance) public p_insurance_mapping;
mapping (bytes32 => c_business_type) public c_business_type_mapping;
3. 變更每個 KYC 屬性的 structure 時,透過不同的 function 達成,每個變更最 後 trigger 一個智慧合約的 event,廣播有 KYC 屬性異動事件
function set_total_digest(string customerid, string value, string owner_of_bank) { total_digest_mapping[sha256(customerid)].value = value;
total_digest_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
SETEVENT(sha256(customerid), "set_total_digest", owner_of_bank);
}
function set_exist(string customerid, bool value, string owner_of_bank ) { exist_mapping[sha256(customerid)].value = value;
exist_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
SETEVENT(sha256(customerid), "set_exist", owner_of_bank);
}
function set_name(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
name_mapping[sha256(customerid)].hash_value = hash_value;
name_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
name_mapping[sha256(customerid)].digital_sig = digital_sig;
name_mapping[sha256(customerid)].queriable = queriable;
‧
SETEVENT(sha256(customerid), "set_name", owner_of_bank);
}
function set_phone(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
phone_mapping[sha256(customerid)].hash_value = hash_value;
phone_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
phone_mapping[sha256(customerid)]. digital_sig = digital_sig;
phone_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_phone", owner_of_bank);
}
function set_id(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
id_mapping[sha256(customerid)].hash_value = hash_value;
id_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
id_mapping[sha256(customerid)]. digital_sig = digital_sig;
id_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_id", owner_of_bank);
}
function set_p_education(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_education_mapping[sha256(customerid)].hash_value = hash_value;
p_education_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
p_education_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_education_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_education", owner_of_bank);
}
function set_p_birthday(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_birthday_mapping[sha256(customerid)].hash_value = hash_value;
p_birthday_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
p_birthday_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_birthday_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_birthday", owner_of_bank);
}
function set_p_career(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_career_mapping[sha256(customerid)].hash_value = hash_value;
p_career_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
‧
p_career_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_career_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_career", owner_of_bank);
}
function set_p_income_monthly(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_income_monthly_mapping[sha256(customerid)].hash_value = hash_value;
p_income_monthly_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
p_income_monthly_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_income_monthly_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_income_monthly", owner_of_bank);
}
function set_p_home_income_yearly(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_home_income_yearly_mapping[sha256(customerid)].hash_value = hash_value;
p_home_income_yearly_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
p_home_income_yearly_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_home_income_yearly_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_home_income_yearly", owner_of_bank);
}
function set_p_insurance(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
p_insurance_mapping[sha256(customerid)].hash_value = hash_value;
p_insurance_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
p_insurance_mapping[sha256(customerid)]. digital_sig = digital_sig;
p_insurance_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_p_insurance", owner_of_bank);
}
function set_c_business_type(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_business_type_mapping[sha256(customerid)].hash_value = hash_value;
c_business_type_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_business_type_mapping[sha256(customerid)]. digital_sig = digital_sig;
‧
c_business_type_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_business_type", owner_of_bank);
}
struct c_income_monthly { #法人月營收
string hash_value;
struct investment_info_source { #客戶投資理財來源 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct investment_source { #客戶所得與資金來源
string hash_value;
‧
struct investment_knowledge_1 { #客戶投資知識專業能力一 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct investment_knowledge_2 { #客戶投資知識專業能力二 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct funding_experience { #客戶投資基金經驗 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
mapping (bytes32 => c_org_type) public c_org_type_mapping;
mapping (bytes32 => c_capital) public c_capital_mapping;
mapping (bytes32 => c_income_monthly) public c_income_monthly_mapping;
‧
mapping (bytes32 => c_over_25) public c_over_25_mapping;
mapping (bytes32 => c_professional_org) public c_professional_org_mapping;
mapping (bytes32 => investment_info_source) public investment_info_source_mapping;
mapping (bytes32 => investment_source) public investment_source_mapping;
mapping (bytes32 => investment_amount) public investment_amount_mapping;
mapping (bytes32 => investment_type) public investment_type_mapping;
mapping (bytes32 => investment_knowledge_1) public investment_knowledge_1_mapping;
mapping (bytes32 => investment_knowledge_2) public investment_knowledge_2_mapping;
mapping (bytes32 => funding_experience) public funding_experience_mapping;
function set_c_org_type(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_org_type_mapping[sha256(customerid)].hash_value = hash_value;
c_org_type_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_org_type_mapping[sha256(customerid)]. digital_sig = digital_sig;
c_org_type_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_org_type", owner_of_bank);
}
function set_c_capital(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_capital_mapping[sha256(customerid)].hash_value = hash_value;
c_capital_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_capital_mapping[sha256(customerid)]. digital_sig = digital_sig;
c_capital_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_capital", owner_of_bank);
}
function set_c_income_monthly(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_income_monthly_mapping[sha256(customerid)].hash_value = hash_value;
c_income_monthly_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_income_monthly_mapping[sha256(customerid)]. digital_sig = digital_sig;
c_income_monthly_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_income_monthly", owner_of_bank);
}
‧
function set_c_over_25(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_over_25_mapping[sha256(customerid)].hash_value = hash_value;
c_over_25_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_over_25_mapping[sha256(customerid)]. digital_sig = digital_sig;
c_over_25_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_over_25", owner_of_bank);
}
function set_c_professional_org(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
c_professional_org_mapping[sha256(customerid)].hash_value = hash_value;
c_professional_org_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
c_professional_org_mapping[sha256(customerid)]. digital_sig = digital_sig;
c_professional_org_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_c_professional_org", owner_of_bank);
}
function set_investment_info_source(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_info_source_mapping[sha256(customerid)].hash_value = hash_value;
investment_info_source_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_info_source_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_info_source_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_info_source", owner_of_bank);
}
function set_investment_source(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_source_mapping[sha256(customerid)].hash_value = hash_value;
investment_source_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_source_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_source_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_source", owner_of_bank);
}
function set_investment_amount(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_amount_mapping[sha256(customerid)].hash_value = hash_value;
‧
investment_amount_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_amount_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_amount_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_amount", owner_of_bank);
}
function set_investment_type(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_type_mapping[sha256(customerid)].hash_value = hash_value;
investment_type_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_type_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_type_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_type", owner_of_bank);
}
function set_investment_knowledge_1(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_knowledge_1_mapping[sha256(customerid)].hash_value = hash_value;
investment_knowledge_1_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_knowledge_1_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_knowledge_1_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_knowledge_1", owner_of_bank);
}
function set_investment_knowledge_2(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_knowledge_2_mapping[sha256(customerid)].hash_value = hash_value;
investment_knowledge_2_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_knowledge_2_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_knowledge_2_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_knowledge_2", owner_of_bank);
‧
function set_funding_experience(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
funding_experience_mapping[sha256(customerid)].hash_value = hash_value;
funding_experience_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
funding_experience_mapping[sha256(customerid)]. digital_sig = digital_sig;
funding_experience_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_funding_experience", owner_of_bank);
}
Contract C: (放置 1/3 的 KYC 欄位、不再分段說明) struct investment_experience { #客戶過往投資經驗 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct investment_purpose { #客戶投資目的 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct investment_favorite { #客戶基金偏好 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
struct investment_risk_taken { #客戶投資風險承受度 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
‧
struct investment_of_income { #客戶資金操作 string hash_value;
string owner_of_bank;
string digital_sig;
bool queriable;
}
mapping (bytes32 => investment_experience) public investment_experience_mapping;
mapping (bytes32 => investment_purpose) public investment_purpose_mapping;
mapping (bytes32 => investment_favorite) public investment_favorite_mapping;
mapping (bytes32 => investment_risk_taken) public investment_risk_taken_mapping;
mapping (bytes32 => investment_of_income) public investment_of_income_mapping;
function set_investment_experience(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_experience_mapping[sha256(customerid)].hash_value = hash_value;
investment_experience_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_experience_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_experience_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_experience", owner_of_bank);
}
function set_investment_purpose(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_purpose_mapping[sha256(customerid)].hash_value = hash_value;
investment_purpose_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_purpose_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_purpose_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_purpose", owner_of_bank);
}
function set_investment_favorite(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_favorite_mapping[sha256(customerid)].hash_value = hash_value;
investment_favorite_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_favorite_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_favorite_mapping[sha256(customerid)].queriable = queriable;
‧
SETEVENT(sha256(customerid), "set_investment_favorite", owner_of_bank);
}
function set_investment_risk_taken(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_risk_taken_mapping[sha256(customerid)].hash_value = hash_value;
investment_risk_taken_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_risk_taken_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_risk_taken_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_risk_taken", owner_of_bank);
}
function set_investment_of_income(string customerid, string hash_value, string owner_of_bank, string digital_sig, bool queriable ) {
investment_of_income_mapping[sha256(customerid)].hash_value = hash_value;
investment_of_income_mapping[sha256(customerid)].owner_of_bank = owner_of_bank;
investment_of_income_mapping[sha256(customerid)]. digital_sig = digital_sig;
investment_of_income_mapping[sha256(customerid)].queriable = queriable;
SETEVENT(sha256(customerid), "set_investment_of_income", owner_of_bank);
}
meteor:PRIMARY> db.colKycinfo_A.find().pretty() {
"_id" : "K64c873ia9iEr7tLi", #Mongo DB 的 index ID
"id" : "N1234", #客戶的身分證號/統一編號
"shaid" : #客戶 id 的 Hash 值
"0xd4d4f8f5a75bbee10f9e2237f1527dc811918991c7245cdf69d042acc763e786"
"name" : "Jason1", #客戶名稱