1.
우연히 JP Morgan의 국경간 지급결제보고서를 읽었습니다. 단순 결제보고서라면 관심을 가지지 않았을텐데 소개글에 생소한 단어가 있었기때문입니다.
A full-scale multi-central bank digital currency (mCBDC)
CBDC앞에 수식어가 달린 mCBDC. 어떤 내용인지 궁금했습니다.
J.P. Morgan releases Unlocking $120 billion in Cross-Border Payments report
보고서에서 말하는 Multi가 무슨 의미인지는 국제결제흐름을 소개한 도표를 보시면 이해할 수 있습니다.
CBDC를 이용한 국가간 지급결제시스템에 대한 논의는 BIS를 중심으로 이루어지고 있는데 BIS가 mCBDC라는 개념을 검토하기 시작한 때는 21년 1월부터입니다. BIS innovation Hub가 m-CBDC Bridge를 검토할 때입니다.
mBridge 플랫폼의 개념적 모델은 아래와 같습니다.
이의 결과로 내놓은 보고서가 Multi-CBDC arrangements and the future of cross-border payments 입니다.
mCBDCs – Unlocking 120 billion value in cross-border payments
BIS가 추진한 mBridge 프로젝트인 Project Inthanon-LionRock의 결과는 어떨까요? 아래 보고서에 따라서 속도와 비용면에서 긍정적이라고 합니다. 보고서를 보면 Ethereum을 기반으로 Hyperledger Besu를 사용하고 있습니다. Hyperledger Besu와 HyperLedger가 무엇이 다른지 찾아보니까 Hyperledger Besu는 Ethereum Client이네요. Announcing Hyperledger Besu의 소개입니다.
What is Hyperledger Besu?
Hyperledger Besu is an open source Ethereum client developed under the Apache 2.0 license and written in Java. It can be run on the Ethereum public network or on private permissioned networks, as well as test networks such as Rinkeby, Ropsten, and Görli. Hyperledger Besu includes several consensus algorithms including PoW, PoA, and IBFT, and has comprehensive permissioning schemes designed specifically for uses in a consortium environment.
또다른 자료를 찾아보니까 HyperLedger Fabric도 Ethereum의 Smart Contract를 지원하네요.
In the spirit of expanding choices, Hyperledger Fabric now supports Ethereum Virtual Machine (EVM) bytecode smart contracts. Contracts can now be written in languages such as Solidity or Vyper. Along with introducing a new smart contract runtime, Fabric also has a corresponding web3 provider which can be used to develop decentralized applications (DApps) using web3.js. This new feature comes as part of the 1.3 release and is motivated with the goal to enable developers to be able to migrate or create DApps for a permissioned platform.
Hyperledger Fabric Now Supports Ethereum중에서
2.
BIS의 프로젝트외에 Visa도 비슷한 개념의 프로젝트를 추진중입니다.Making digital currency interoperable는 Visa가 추진하고 있는 UPC를 소개하고 있습니다.
UPC와 관련한 Visa의 보고서들입니다. 각각 Cross-border payments for Central Bank Digital Currencies 와 Universal Payment Channels: An Interoperability Platform for Digital Currencies이 출처입니다.
아래는 Visa가 공개한 Smart Contract 코드입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
/** *Submitted for verification at Etherscan.io on 2021-09-24 */ // SPDX-License-Identifier: CC-BY-NC-ND-4.0 // License: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode // Note this is a sample work of the contract only. // In this version we use a dedicated contract for each partnership. In future versions we use a single contract for all participants. // We do not use the Proxy model in this version. In future versions we use a Proxy model to allow us to make upgrades to the contract. pragma solidity ^0.7.6; // Note SafeMath is nolonger needed once we migrate to solidity v0.8.0 contract SafeMath { function safeAdd(int a, uint b) internal pure returns (int) { int c = int(b); require(c>=0); c = a + c; require(c>=a); return c; } /// @return a-b function safeSub(int a, uint b) internal pure returns (int) { int c = int(b); require(c>=0); c = a - c; require(c <= a); return c; } function safeAdd(uint a, uint b) internal pure returns (uint) { uint c = a + b; require(c>=a); return c; } /// @return a-b function safeSub(uint a, uint b) internal pure returns (uint) { require(a>=b); uint c = a - b; return c; } function safeAdd(uint a, int b) internal pure returns (uint) { if (b>=0) return safeAdd(a, uint(b)); return safeSub(a, uint(-b)); } /// @return a-b function safeSub(uint a, int b) internal pure returns (uint) { if (b>=0) return safeSub(a, uint(b)); return safeAdd(a, uint(-b)); } } interface ERC20{ function transferFrom(address _from, address _to, uint _value) external returns (bool success); function transfer(address _to, uint _value) external returns (bool success); } contract UniversalPaymentChannel is SafeMath { enum ChannelStatus {ACTIVE, UNILATERALCLOSING, COOPERATIVECLOSING, CLOSED} struct secret { bytes32 val; bool exists; } // Contracts variables address payable private vk_s; address private vk_i; // address of client i and server s uint256 private channelExpiry; // linux time when channel expires. zero means the channel has no expiry. uint256 private disputeTime; // time (in seconds) for disputes. eg: to allow 5 days for disputes use 432000 uint256 private clientDeposit; uint256 serverDeposit; // total deposit balance of client i and server s int256 private finalCredit; // aggregate amount of money client i is owed by server s. this can be negative if i owes s. uint256 private finalIdx; // latest state id seen by contract uint256 private channelId; // unique channel id ChannelStatus private status; // channel status address private closeRequester; // the first party that request a close channel (i.e., sets the expiry time) mapping (bytes32 => secret) private paymentSecrets; // mapping between hash values and their secrets ERC20 tokenImplementation; // e.g., address of the USDC contract //uint256 chainId = block.chainid; // compiler version >0.8.x uint256 private chainId; // ============================ Helper Functions============================= modifier onlyChannelParticipants { require((msg.sender == vk_i) || (msg.sender == vk_s)); _; } function hashFunction(bytes memory message) private pure returns (bytes32) { return keccak256(message); } function secretHashing(bytes memory secret2) private pure returns (bytes32) { return sha256(secret2); } function sigVerify(address ver, bytes32 message, bytes32 r, bytes32 s, uint8 v) private pure returns (bool){ return ver == ecrecover(addPrefixHash(message), v, r, s); } function addPrefixHash(bytes32 message) private pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message)); } // =========================================================================== constructor (uint256 cid, address payable _vk_s, address _vk_i, uint256 _disputeTime, ERC20 _tokenImplementation) { require(_vk_s != address(0)); require(_vk_i != address(0)); require(_vk_i != _vk_s); require(address(_tokenImplementation) != address(0)); channelId = cid; vk_s = _vk_s; vk_i = _vk_i; disputeTime = _disputeTime; tokenImplementation = _tokenImplementation; status = ChannelStatus.ACTIVE; finalIdx = 0; finalCredit = 0; clientDeposit = 0; serverDeposit = 0; channelExpiry = 0; chainId = _chainID(); } function _chainID() private pure returns (uint256) { uint256 chainID; assembly { chainID := chainid() } return chainID; } function getParams() public view returns(uint256, address, address, uint256, ERC20) { return (channelId, vk_s, vk_i, disputeTime, tokenImplementation); } function getState() public view returns(ChannelStatus, uint256, int256, uint256, uint256, uint256, address) { return (status, finalIdx, finalCredit, serverDeposit, clientDeposit, channelExpiry/10, closeRequester); } function getPaymentSecret(bytes32 hash) public view returns(bytes32) { return paymentSecrets[hash].val; } /// @notice deposit tokens from other than Ether tokens function depositToken(uint256 amount) public onlyChannelParticipants { require(status == ChannelStatus.ACTIVE); // dev: channel status is not active require(tokenImplementation.transferFrom(msg.sender, address(this), amount)); // dev: token transfer has failed if (msg.sender == vk_i) // client clientDeposit = safeAdd(clientDeposit, amount); else // msg.sender == vk_s serverDeposit = safeAdd(serverDeposit, amount); } /// @notice begin to close this channel. function initClose() public onlyChannelParticipants{ require(status == ChannelStatus.ACTIVE || status == ChannelStatus.UNILATERALCLOSING); if (status == ChannelStatus.ACTIVE) { channelExpiry = safeAdd(block.timestamp, disputeTime); // set channel expiry time closeRequester = msg.sender; // set the first requester of channel closing status = ChannelStatus.UNILATERALCLOSING; }else if (msg.sender != closeRequester) status = ChannelStatus.COOPERATIVECLOSING; } /// @notice claiming a pending payment or settled state using a promise or receipt. /// @param idx the id associated to the latest state /// @param clientCredit the aggregate total amount that s owes i. This may be negative, if i owes s. /// @param amount pending payment amount that the counter party is paying the caller. /// @param hash hash of the secretToken (payment condition) /// @param expiry the time the payment is valid for /// @param isPromise this variable indicates whether is a claim for a promise or a receipt // @param r,s,v signature of the counter party on the claiming state /// @param secretToken secret value that fulfills the payment condition function claim(uint256 idx, int256 clientCredit, uint256 txAmount, bytes32 hash, uint256 expiry, uint8 isPromise, bytes32 r, bytes32 s, uint8 v, bytes32 secretToken) public onlyChannelParticipants { require(status == ChannelStatus.ACTIVE || status == ChannelStatus.UNILATERALCLOSING); if (status == ChannelStatus.UNILATERALCLOSING) require(block.timestamp <= channelExpiry); require(idx > finalIdx); // dev: claim index is outdated if (isPromise == 1){ require((secretHashing(abi.encodePacked(secretToken)) == hash) && (block.timestamp <= expiry)); // dev: promise has expired or secret doesn't match paymentSecrets[hash].exists = true; paymentSecrets[hash].val = secretToken; }else{ require(txAmount == 0); // dev: receipt's txAmount must equal zero } bytes32 c_hash = hashFunction(abi.encodePacked(chainId, channelId, idx, clientCredit, txAmount, hash, expiry, isPromise)); if (msg.sender == vk_s) { require(sigVerify(vk_i, c_hash, r, s, v)); // dev: signature does not match require(txAmount <= safeAdd(clientDeposit, clientCredit)); // dev: tx amount exceeds the deposit and credit values finalCredit = safeSub(clientCredit, txAmount); } else{ // msg.sender == vk_i require(sigVerify(vk_s, c_hash, r, s, v)); // dev: signature does not match require(txAmount <= safeSub(serverDeposit, clientCredit)); // dev: tx amount exceeds the deposit and credit values finalCredit = safeAdd(clientCredit, txAmount); } finalIdx = idx; initClose(); } /// @notice upon expiry of the channel or cooperative closing parties can call to withdraw their remaining balances and channel will close function withdrawToken() public onlyChannelParticipants { require(status == ChannelStatus.UNILATERALCLOSING || status == ChannelStatus.COOPERATIVECLOSING); // dev: channel status is not in a closing stage if(status == ChannelStatus.UNILATERALCLOSING) require(block.timestamp >= channelExpiry); // dev: channel has not expired yet in unilateralclosing clientDeposit = safeAdd(clientDeposit, finalCredit); serverDeposit = safeSub(serverDeposit, finalCredit); finalCredit = 0; [gview file="https://www.smallake.kr/wp-content/uploads/2021/12/글로벌-지급결제시장-트렌드-변화.pdf"] uint256 withdrawAmount = 0; if (msg.sender == vk_i) { withdrawAmount = clientDeposit; clientDeposit = 0; } else { // msg.sender == vk_s withdrawAmount = serverDeposit; serverDeposit = 0; } if (withdrawAmount > 0){ require(tokenImplementation.transfer(msg.sender, withdrawAmount)); // if ether needs to be transfer use msg.sender.transfer(withdrawAmount); } if (clientDeposit == 0 && serverDeposit == 0){ status = ChannelStatus.CLOSED; // no need if we selfdestruct selfdestruct(vk_s); } } } |
3.
이상을 통해 정리할 수 있는 국제지급결제의 트렌트는 실시간과 상호연동입니다. CBDC의 도입을 시대적 흐름으로 보고 이를 기반으로 하여 국경간 지급결제를 실시간으로 이루어지도록 하기 위한 노력을 추진중입니다. 하나금융연구소의 글로벌 지급결제시장 트렌드 변화를 보면 ①지급결제 통합 가속화 ②은행권참여 확대 ③디지털 화폐의 빠른 확산 ④국가별 규제 정비 ⑤산업 통합 및 M&A 확대 등을 핵심 트렌드로 지목합니다. 첫번째의 통합이라는 흐름이 상호연동성과 연결될 듯 합니다.
참고로 여신금융연구소가 발간한 ‘글로벌 카드 브랜드사의 가상화폐 도입 추진계획’입니다.