ãããã¯ãã§ãŒã³å ¥é â JavaScriptã§åŠã¶ãããã¯ãã§ãŒã³ãšBitcoinãŠã©ã¬ããã®ä»çµã¿ãšå®è£
æ¬èšäºã§ã¯ãããã¯ãã§ãŒã³ã®ããã°ã©ãã³ã°ããå®è·µãšãšãã«åŠã³ãŸãããããã¯ãã§ãŒã³ãšã¯ã忣ç°å¢ã®æ°ããããŒã¿æ§é ã§ãã忣åæã®ã¢ã«ãŽãªãºã ã§ãããNode.jsã§ãããã¯ãã§ãŒã³ããã³BitcoinãŠã©ã¬ãããå®è£ ãããã®ä»çµã¿ãçè§£ããŠãããŸãããã
ããªãŒã©ã³ã¹ã§ãšã³ãžãã¢ãšã©ã€ãã£ã³ã°ãªã©ãããããè¡ã£ãŠããerukitiãšç³ããŸãã
å人ã®ãµãŒã¯ã«ãæ±äº¬ã©ãããããŠã¹ããããModern JavaScriptããç°¡åJavaScript ASTå
¥éããJavaScriptã§èŠããæå·é貚å
¥é#1 Bitcoinå®å
šã«çè§£ããããšãã£ãJavaScripté¢é£ã®æè¡å人èªãåèã§çºè¡ããŠããŸãã
ãã®èšäºã§ã¯ããããã¯ãã§ãŒã³ã®ä»çµã¿ã解説ããå®éã«ãããã¯ãã§ãŒã³ãBitcoinãŠã©ã¬ãããäœã£ãŠã¿ãããšããŽãŒã«ãšããŸããå°ãã§ããããã®æè¡ãžã®çè§£ãæ·±ããäžå©ã«ãªãã°å¹žãã§ãã
- ãããã¯ãã§ãŒã³ãšã¯äœãïŒ
- ããŒã¿æ§é ããèŠããããã¯ãã§ãŒã³
- 忣åæã¢ã«ãŽãªãºã ãšããŠã®ãããã¯ãã§ãŒã³
- ãããã¯ãã§ãŒã³ãäœã£ãŠã¿ãã
- BitcoinãŠã©ã¬ãããäœã£ãŠã¿ãã
- æåŸã«
ãããã¯ãã§ãŒã³ãšã¯äœãïŒ
ãããã¯ãã§ãŒã³ãšã¯ãè¬ã®äººç©Satoshi Nakamotoãçã¿åºããæå·é貚Bitcoinã®ã³ã¢ãšãªãæè¡ã§ã忣ç°å¢ã®äžçã«ãŸã£ããæ°ããèãæ¹ãšããŠå°å ¥ããããããŒã¿æ§é ããã³åæ£åæã®ããã®ã¢ã«ãŽãªãºã ã§ãã
The network timestamps transactions by hashing them into an ongoing chain of hash-based proof-of-work, forming a record that cannot be changed without redoing the proof-of-work.
éäžå€®éæš©åãããã¯ãŒã¯ïŒåæ£ç°å¢ãåå ãããã¹ãŠã®ããŒãã察çãªç«å Žã«ããïŒäžã§P2Pé»åãã£ãã·ã¥ã·ã¹ãã ãå®çŸããæ¹æ³ãšããŠãæç³»åã«åŸã£ããã©ã³ã¶ã¯ã·ã§ã³ãããã·ã¥èšç®ãçšããProof of WorkïŒèšç®ã«ãã蚌æïŒãªãã§ã¯å€æŽäžå¯èœãªããŒã¿ãç©ã¿éãããšããææ³ãã2008幎11æ1æ¥ã«æçš¿ãããŸããã
ãããã¯ãã§ãŒã³ã¯ãä¿¡é Œã®ã§ããªãããŒããã€ãªãã£ãŠã忣ç°å¢ã§ã©ããã£ãŠããŒã¿æŽæ°ãããã®ããšããæå³ã§ããšãŠãè峿·±ãæè¡ã§ãã
ãããã¯ãã§ãŒã³ã掻çšãããŠããåé
ãããã¯ãã§ãŒã³ã¯ãæ å ±ã®æ¹ãããããã«ããå ç¢æ§ãé«ãããšãããæå·é貚ã®ååŒã ãã§ãªããããŸããŸãªæ¥çã§ã®å©æŽ»çšãé²ããããŠããŸãã
- é£å
- é£åã®ç£å°ãæµéçµè·¯ãæããã«ããããããããã¯ãã§ãŒã³ã䜿ããã¬ãŒãµããªãã£ã確ä¿ããŠããããžããšèãã¯ã€ã³ãææ©èŸ²äœç©ãªã©ã®ååéã§ ååŒéæåã詊ã¿ãäŒæ¥ããã
- äžåç£ç®¡çã·ã¹ãã
- ç©æ°ŽããŠã¹æ ªåŒäŒç€Ÿãšæ ªåŒäŒç€Ÿbitflyerã®å
±åäºæ¥ã§ã¯ãè³è²žäœå®
ã®æ
å ±ç®¡çã·ã¹ãã ã«ãããã¯ãã§ãŒã³ã掻çšãäžåç£ããŒã¿ãå
¥å±
è
æ
å ±ãã¯ã¬ãŒã æ
å ±ãªã©ãèšé²ãã远跡ã§ãããããªã·ã¹ãã ãæ§ç¯ããŠãã
bitFlyerãèªãè¿æªæ¥ã次äžä»£ãããã¯ãã§ãŒã³ãmiyabiãã®ç¹åŸŽãšæŽ»çšã¯ïŒãBlockchain for Enterprise 2018ã - INTERNET Watch
- é»åååŒ
- 2018幎ãé¢è¥¿é»åã¯äœå°é»åã®P2PååŒã®å®èšŒå®éšãéå§ããããã¯ãã§ãŒã³ã䜿ãã倪éœå
çºé»ã®çç£è
ãšæ¶è²»è
ãé»åãçŽæ¥åãåŒãã§ãããã©ãããã©ãŒã ã®åœ¢æãæ€èšäžã
豪å·ãã¯ãŒã¬ããžã£ãŒç€Ÿãšã®ãããã¯ãã§ãŒã³æè¡ã掻çšããé»åçŽæ¥ååŒãã©ãããã©ãŒã äºæ¥ã«ä¿ãå®èšŒç ç©¶ã®éå§ã«ã€ããŠïœ2018ïœãã¬ã¹ãªãªãŒã¹ïœäŒæ¥æ å ±ïœé¢è¥¿é»å
ãããã®äºäŸã§æ¬åœã«ãããã¯ãã§ãŒã³ãé©ããŠããã®ãïŒ ã¯ããŸã ããããªãææ¢ãã®ç¶æ³ã§ããã ããããããããã¯ãã§ãŒã³ãšã¯äœãïŒ äœãã§ããŠãäœãã§ããªãã®ãïŒ ãèŠæ¥µããããšããéèŠã«ãªã£ãŠããŸãã
ããŒã¿æ§é ããèŠããããã¯ãã§ãŒã³
ãããã¯ãã§ãŒã³ã¯ãæ¥æ¬èªã§ã¯ããã忣åå°åž³ããªã©ãšåŒã°ããŸããå°åž³ïŒãããã¯ãé£ãªã£ãïŒãã§ãŒã³ã«ãªã£ãïŒããŒã¿æ§é ãªã®ã§ããããã¯ãã§ãŒã³ã§ãã
ãããã¯ã«ã¯ãé»å眲åãæœãããã©ã³ã¶ã¯ã·ã§ã³ããåã®ãããã¯ã®ããã·ã¥å€ãå«ãŸããŠããŸãããã©ã³ã¶ã¯ã·ã§ã³ããããã¯ãã€ãã¥ãŒã¿ãã«ïŒäžå€ïŒã§ã远èšãªã³ãªãŒã®ããŒã¿æ§é ã§æãç«ã£ãŠãããšãããŸããæ°ãããããã¯ãçºè¡ããããã«ã¯èšå€§ãªããã·ã¥å€ã®æŒç®ïŒPoWãProof of WorkïŒãå¿ èŠã§ããããã®ãããå€ããããã¯ã§ããã°ããã»ã©æ¹ãããé£ãããªããŸãã
ããã§ã¯ãããŒã¿æ§é ã®åŽé¢ãããããã¯ãã§ãŒã³ã«ã€ããŠèŠãŠãããŸãããã
ãã©ã³ã¶ã¯ã·ã§ã³
ä»®æ³é貚ãšããŠç¥ãããŠããBitcoinãäŸã«æããŠèª¬æããŠãããŸãã
Bitcoinã¯ãLinux財å£ã®bitcoin-mlã§è°è«ãè¡ããããªãã¡ã¬ã³ã¹å®è£ ã§ãããOSSãœãããŠã§ã¢ãBitcoin Coreãã䜿ãããŠããŸããã€ãŸããBitCoinã¯ããŒã¿æ§é ãéä¿¡æ¹æ³ãèŠå®ãããããã³ã«ã§ããããã®ãããã³ã«ã«åŸã£ããœãããŠã§ã¢ã§éçšãããŠãããããã¯ãŒã¯ãã®ãã®ã§ããããšãããŸãã
Bitcoinã®ãã©ã³ã¶ã¯ã·ã§ã³ãšã¯ãééæ å ±ãã·ãªã¢ã©ã€ãºããŠé»å眲åãæœãããã®ã§ãååŒã®åºæ¬åäœãšãªããã®ã§ããé»å眲åãæœãããšã§ãçºè¡äž»ãã€ãŸãBitcoinã®æã¡äž»ãééããŠãããšãã蚌æãè¡ããŸãã
ã·ãªã¢ã©ã€ãºãšã¯ãããããŒã¿ã»ãªããžã§ã¯ããªã©ã笊å·åããç°ãªãããã»ã¹ããã·ã³ãªã©ã®éã§ãããšãããããããã«ãæååããã€ããªããŒã¿ã«ããããšã§ããæååã«ããæ±çšã®ã·ãªã¢ã©ã€ãºãã©ãŒããããšããŠã¯ãJSONããYAMLããããŸãããã€ããªã§ããã°ãProtocol BuffersããMessagePackãªã©ãæåã§ãã
const data = { hoge: 'ã»ã', fuga: 'ãµã' } const serializedData = JSON.stringify(data) console.log(serializedData)
ãããã³ã«ãšããŠã¯ãææ§æ§ãæ®ããšèšç®çµæãç°ãªãããšã«ãªããŸãããã®ãããããã·ã¥å€ãã©ããã£ãŠåãã®ãããã®ããã·ã¥å€ãå ã«é»å眲åãæœãã®ããã·ãªã¢ã©ã€ãºã®ã«ãŒã«ãããã®ããŒã¿ãã©ã®é çªã§ã©ããã£ãŠåŠçããã®ããéèŠã«ãªããŸãã
äŸãã°ãBitcoinã®å€ããã©ã³ã¶ã¯ã·ã§ã³ã§ã¯ãé»å眲åã®é åã«ãã£ãããããŒãåã蟌ãã§ããé»å眲åãæœããŠããŸãããå»å¹Žã®ã¢ããããŒãã§æ¡çšãããSegwit2xã§ã¯ãããã®ãšãªã¢ãåé¢ïŒsegregated witnessïŒããŠããŸãã
å€ããã©ã³ã¶ã¯ã·ã§ã³ã«ãã£ãè匱æ§ãžã®å¯Ÿå¿ãšããåŽé¢ã匷ãã®ã§ãããããã«ãã£ãŠãã©ã³ã¶ã¯ã·ã§ã³ã®äœæãšé»å眲åãåé¢ã§ããããã«ãªããŸããåé¢ã«ãã£ãŠåŠçãã·ã³ãã«ã«ãªããæ°ããä»çµã¿ãå°å ¥ã§ãããšããå©ç¹ããããŸãã
Bitcoinã®ãã©ã³ã¶ã¯ã·ã§ã³ãé«éåããææ°æãæžãããã©ã€ããã³ã°ãããã¯ãŒã¯ïŒãã€ã¯ããã€ã¡ã³ãææ³ã®ã²ãšã€ã§ãå°é¡ååŒã®å®çšåãæåŸ ã§ããæè¡ïŒã«æ¬ ãããªããã®ã§ãã
ãããã¯
ãããã¯ã¯ããã©ã³ã¶ã¯ã·ã§ã³ã®éåäœã§ãããã©ã³ã¶ã¯ã·ã§ã³ãååŒã®åºæ¬åäœã ãšãããšããããã¯ã¯èšé²ã®åºæ¬åäœã§ãããã©ã³ã¶ã¯ã·ã§ã³åäœã§ã¯èšé²ãšããŠã¯èªããããããããã¯ã«åã蟌ãŸããŠåããŠååŒãšããŠæç«ããã®ã§ãã
ãããã¯ãã§ãŒã³ã§ã¯ãåè¿°ã®éãããããã¯ã«ã¯åã®ãããã¯ã®ããã·ã¥å€ããã€ã³ã¿ãšããŠå«ãŸããŠããŸããæåã®ãããã¯ã«ã¯åã®ãããã¯ãšãããã®ãååšããªããããååãšãªãããã·ã¥å€ïŒgenesis hashïŒã䜿ãããŸããããã¯ãœãŒã¹ã³ãŒãã«ããŒãã³ãŒãã£ã³ã°ããããã®ã§ãBitcoinã®æ¬çªãããã¯ãŒã¯ã§ã¯æ¬¡ã®å€ãããã«è©²åœããŸãã
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
ãããã¯ã¯ãæåããææ°ãŸã§å šéšåãããŠççŸããªãããã«äœãããŸãããã©ã³ã¶ã¯ã·ã§ã³ãæ£ããããšããããã¯ã«ççŸããªãããšããããã®æ¡ä»¶ãæºãããªããããã¯ã¯ãããã¯ãšããŠèªãããããä»ã®ããŒãããã¯åŒŸãããŠããŸããŸãã
gitã¯ãããã¯ãã§ãŒã³ãïŒ
gitã¯ãäžçªã«ãŒããšãªãæåã®ã³ãããã®ããã·ã¥å€ãåºçºç¹ãšããŠãã€ãã¥ãŒã¿ãã«ãªããŒã¿ãé£ãªãæ§é ã§ããããŒã¿æ§é ãšããŠèŠãã°ãããã¯ãã§ãŒã³ãšé¡äŒŒããŠããŸãã
ãã ããgitãšãããã¯ãã§ãŒã³ã§ã¯ç®çã®éããããèšèšãããªãç°ãªã£ãŠããŸãã
gitã®ç®çã¯ããã¡ã€ã«ã®å€æŽãå±¥æŽãšããŠæ®ãããšã§ãããã®ããgitã®åãã¡ã€ã«ïŒblobïŒã¯ãããããåç¬ã®ãã¡ã€ã«ãšããŠgitã®ãªããžããªå ã«å¥ã ã«ååšããè€æ°ãã©ã³ããæã£ãŠæåããããããšã蚱容ããŠããŸãã
äžæ¹ãæå·é貚ãšããŠã®ãããã¯ãã§ãŒã³ã§ã¯ãæåããããŠãããšé貚ã®ååŒã«æ¯éãåºãŸããäŸãã°ãBitcoinã«ã¯ãæé·ã®ãã§ãŒã³ãæ£ãããã§ãŒã³ãšèŠãªãããšããã«ãŒã«ããããç¬éçã«è€æ°ã®ãã¡ã€ã«ãçºçããŠã1ã€ã«åæããããã«èšèšãããŠããŸããæé·ã®ãã§ãŒã³ä»¥å€ã¯æçµçã«ç Žæ£ãããã®ã§ãã
ãã®ç¹ã¯ãforkããã©ã³ããåæã«ããŠããgitãšå€§ããç°ãªããšãããã§ãããã
忣åæã¢ã«ãŽãªãºã ãšããŠã®ãããã¯ãã§ãŒã³
ãããã¯ãã§ãŒã³ã¯ããŒã¿æ§é ãç¹åŸŽçã§ã¯ãããŸãããããéèŠãªã®ã¯ã¢ã«ãŽãªãºã é¢ã§ãã
ãããã¯ãã§ãŒã³ã«äœ¿ãããŠãã忣åæã¢ã«ãŽãªãºã ãšã¯ã忣ãããè€æ°ã®ããŒãã§æ£ããããŒã¿ãæŽæ°ããããã«åæãåãããã®ãã®ã§ããããåæãåããã«å¥œãåæã«ããŒã¿ãæŽæ°ã§ããŠããŸãã®ã§ããã°ãããã¯ãã ã®ã§ããããªãããã¯ãŒã¯ã«ãããªãããã§ãã
忣åæã¢ã«ãŽãªãºã ã¯ã忣ãã¡ã€ã«ã·ã¹ãã ã忣ããŒã¿ããŒã¹ãã·ã¹ãã ãªãŒã±ã¹ãã¬ãŒã·ã§ã³ããŒã«ãªã©ããŸããŸãªåæ£ã·ã¹ãã ã§äœ¿ãããŠããŸãã
let counter = 0 console.log(counter) // --> 0 counter++ // ããŒã¿æŽæ° console.log(counter) // --> 1
ãã®ã·ã³ãã«ãªJavaScriptã®ã³ãŒãã§ã¯ãcounterãšãã倿°ã«ã¯åæå€0ãå
¥ã£ãŠããŠãcounter++ãšããã³ãŒãã«ãã£ãŠãããŒã¿ãæžãæãããã1ãšããå€ã«ãªããŸãããã®ã³ãŒãã¯ãã·ã³ã°ã«ã¹ã¬ããã§ãåæã«äžã«æããããåç
§ãããªãããããæç«ããŸãã
忣ç°å¢ã§ã¯ã忣ãããè€æ°å°ã®ãã·ã³ã§1ã€ã®å
±æãããcounterãå®å
šã«æŽæ°ããããã«ã忣åæã¢ã«ãŽãªãºã ãªã©ãå©çšããŸãããå®å
šããšã¯ã以äžã®ãããªããšã瀺ããŸãã
- ããŒã¿ãå€ãªå€ã«ãªããªã
- ããŒã¿ããã¹ãããªã
- æŽæ°ã«å€±æããããã¡ãããšææ¡ã§ãã
ããŒã¿ã®ç©ã¿éãã§æŽæ°ã衚çŸãã
Bitcoinã®ãããªãããã¯ãã§ãŒã³ã§ã¯counterãæŽæ°ããŸãããããŒã¿ãæžãæããã«ããã©ã³ã¶ã¯ã·ã§ã³ã®ç©ã¿éãã§é貚ã®ç§»åã衚çŸããŸããããŒã¿ããŒã¹ããã¡ã€ã«ã·ã¹ãã ã®äžçã§ãã°ããžã£ãŒãã«ãšåŒã°ããæ§é ã«è¿ãã§ãã
const counterLogs = [] const printCounter = () => { console.log(counterLogs.reduce((acc, n) => acc + n, 0)) } printCounter() // --> 0 counterLogs.push(1) // ããŒã¿æŽæ° printCounter() // --> 1
counterLogsã¯ãcounterã®å¢æžããã°ãšããŠä¿åããããã®é
åã§ãã衚瀺ãããšãã«ã¯ãé
åã®ãã¹ãŠã®æ°å€ãåèšããŸãã
printCounterã§äœ¿ãããŠããreduceã¡ãœããã§ã¯ãé
åã®å
šèŠçŽ ãè¶³ãåãããŠããã®åèšå€ãæ±ããŠããŸãã
counterLogsã«äœãå
¥ã£ãŠããªãç¶æ
ã§ã¯ãåæå€ã®0ã«ãªããŸããcounterLogs.push(1)ã§é
åã«1ãšããæ°å€ã远å ãããšãcounterã¯1ã«ãªããŸããäŸãã°ãããã«push(2)ãããšcounterã¯3ã«ãªããŸãã
ããŒã¿æŽæ°ã忣ç°å¢ã§åæãã
å€ãã®åæ£åæã¢ã«ãŽãªãºã ã§ã¯ãããŒã¿æŽæ°ãäžæã«åŒãåãããªãŒããŒãéžåºãããªãŒããŒãã¡ã³ããŒãã¡ã®æŽæ°èŠæ±ãåãåã£ãŠãããŒã¿æŽæ°ãè¡ã£ãŠãããã®å€æŽæ å ±ããããŒããã£ã¹ããããããªä»çµã¿ã«ãªã£ãŠããŸããèå³ã®ããæ¹ã¯ãPaxosãRaftã調ã¹ããšããã§ããããç¹ã«Raftã¯æ±ãããããçè§£ãããããã§ãããã
ããããæå·é貚ã®ããã«ãåãã€ãããšã§åŸãããå¯èœæ§ããããããªããŒããããç°å¢ã§ã¯ãRaftãªã©æ¢åã®ã¢ã«ãŽãªãºã ã§ã¯å¯Ÿå¿ããããŸããããã®ããããããã¯ãã§ãŒã³ã§ã¯ãããŒã¿èšé²ã®åºæ¬åäœã§ãããããã¯ã®çºè¡æé ãã忣åæã¢ã«ãŽãªãºã ãã®ãã®ã«ãªã£ãŠããŸãã
ãã©ã³ã¶ã¯ã·ã§ã³ãé»å眲åã«ãã£ãŠåœäººã®ãã®ã§ããããšã蚌æãããŠãããšããŠããªããããã¯ã§åæãåããªããã°ãªããªãã®ã§ããããïŒ ããã¯åã ã®ãã©ã³ã¶ã¯ã·ã§ã³ãæ£ãããŠããæç³»åã§äžŠã¹ããšãã«ççŸãçããå¯èœæ§ãããããã§ãã
Bitcoinã§ã¯ã次ã«è¿°ã¹ãPoWãçšããŠããããã¯ã®çºè¡ã«ã€ããŠåæãåããŸãã
PoWãšããçºæ
Proof of Workã¯ãBitcoinã§æ¡çšããã忣忿è¡ã§ããããã¯ç¢ºçè«ãšãã²ãŒã çè«çãªã€ã³ã»ã³ãã£ãã®èãæ¹ã«è£æã¡ãããŠããŸãã
Bitcoinã§ã¯ããã©ã³ã¶ã¯ã·ã§ã³ããšããŸãšããŠãããã¯ãçºè¡ãããšãã€ãã³ã°å ±é ¬ïŒäžçªæåã®é ã§50BTCã2018幎çŸåšã¯12.5BTCïŒãåŸãããšãã§ããŸããäŸãã°ã1BTCã60äžåã ãšããã°ã12.5BTCã¯7500äžåã§ãããããã¯ãçºè¡ã§ããã°ãã®ãããªå€§éãæã«å ¥ããããäžçäžã®äººãããã£ãŠãããã¯çºè¡ã§ããæš©å©ã奪ãåã£ãŠããŸãã
ãããããããã¯ãçºè¡ããã®ã¯æ±ºããŠå®¹æã§ã¯ãããŸããã256bitã®ããã·ã¥ãèšç®ããŠãããããã®çµæããæå®ãããæ°å€ïŒé£æåºŠïŒæªæºã§ãªããšãããªããšãããç¹æ®ãªå¶çŽããããŸãã
ãããæºããããã«ããããŒããŒã¿ãä»äžããŠããã·ã¥å€ã調æŽããå¿ èŠããããŸããSHA256ã§ã¯çã£ãããã·ã¥å€ãç®åºãããããªæ¹æ³ã¯ãŸã èŠã€ãã£ãŠããªããããç·åœããã§èšç®ããªããã°ãªããŸããã
é£æåºŠã¯ããããã1é±éïŒæ£ç¢ºã«ã¯2016ãããã¯ïŒããšã«ãBitcoinãããã¯ãŒã¯ã«ãããã¹ãŠã®èšç®åãè²»ãããŠãå¹³åçã«10åã§1ã€ã®ããã·ã¥å€ãèŠã€ãã確çã«èšå®ãããŸãã
- 1ã€åã®ãããã¯ã®ããã·ã¥å€ããããã¯ã®äžã«å ¥ãã
- ãããã¯ã®äžã«ãããã¹ãŠã®ãã©ã³ã¶ã¯ã·ã§ã³ã¯ãéå»ã®ãããã¯ãã¹ãŠã®ãã©ã³ã¶ã¯ã·ã§ã³ãšççŸããªãããã«ãã
- ã»ããæ±ºãããããããã³ã«ã«åŸã£ãŠãã
- ãããŒããŒã¿ãæ¿å ¥ããŠããã·ã¥å€ã調æŽãã
ãããã®æ¡ä»¶ãæºããã°ãããã¯ãçºè¡ã§ããŸãããããã ãã§ã¯10BTCã ãããæã£ãŠããªãAliceããBobãšCarolã®åæ¹ã«10BTCãæ¯æãããšãããšãã®åé¡ïŒäºéæ¯æãåé¡ïŒã解決ã§ããŸããã
æ£åœãªããã·ã¥å€ãæã¡ãè€æ°ã®ãããã¯ããããã€ãŸãæåããç¶æ ãã©ãæ±ãããšããã«ãŒã«ãå¿ èŠã ããã§ãã
ããã§ããããã¯ãã§ãŒã³ã®é·ãïŒBitcoinã§ã¯heightãšåŒã¶ïŒãäžçªé·ããããã¯ãæ£ãããšããããšããã«ãŒã«ããããŸãã
äžçäžã®ããŒããããã£ãŠãã®èšç®ãè¡ã£ãŠããŸããããããã¯ã1ã€çºè¡ã§ãããšããŠããèªåã®ãããã¯ã®ããšã«ãã§ãŒã³ãç¶ããŠãããªããã°ããã®èšç®ã¯ç¡æå³ã«ãªããŸãã
èªåã®çºè¡ãããããã¯ã«å¥ã®ãããã¯ãç¶ãããšããæå·é貚ã®äžçã§ã¯æ¿èªãåŸããšåŒã³ãŸãããããã¯ã«å«ãŸãããã©ã³ã¶ã¯ã·ã§ã³ãæ£ããããšãããã·ã¥å€ãšããŠåç §ãããããã¯ã«äžæ£ããªãããšãç©æ¥µçã«ç¢ºèªãããã€èªåãåç §ãããããã¯ããã®æç¹ã§æé·ã§ãªããšããããèšç®ãç¡é§ã«ãªãããããããã¯ãç¶ãã°ããšã®ãããã¯ãæ¿èªãããŠãããšèŠãªããããã§ãã
ãã®ããã«ãã€ã³ã»ã³ãã£ããåŸãããšããæ¬²æã«ãŸã¿ããå šäžçã®èšç®éã®æå ¥ã«ãããäžæ£ãçãŸãã確çãæ¥µç«¯ã«æžãããŠããã®ã§ãã
AliceãBobãšCarolã®ã©ã¡ãã«ééããããšã«ãªãã®ãã¯ããã®ãã©ã³ã¶ã¯ã·ã§ã³ãå«ãŸãããããã¯ã«å€ããããã¯ãç©ã¿éãªãã°ããããèŠãããããšã¯ãŸããªããªããŸãã
äŸãã°ã1024ãããã¯ç®ã§AliceãBobã«10BTCãééãããã©ã³ã¶ã¯ã·ã§ã³ãå«ãŸããŠãããªãã1025ãããã¯ç®ãããã§ã¯ãŸã èŠãå¯èœæ§ããããŸãããã ãã1026ã1027ãšç¶ããŠãããšèŠãã確çã¯æžã£ãŠãããæŽå²ã¯åæããããšã«ãªããŸãã
Bitcoinã®ååŒã«ã¯æéãæãããšããåé¡ããããŸãããªã«ãã³ã€ã³ãšåŒã°ããBitcoin以å€ã®æå·é貚ã§ã¯é£æåºŠãäžããŠããã£ãšçãã¹ãã³ã§ãããã¯ãçºè¡ãããããã«ããŠããŸãããããã¯äžæ£ãã§ãã確çãäžããããšãæå³ããŠããŠãå®éã«Monacoinã§ã¯ãã®ãããªäžæ£ã«ããååŒæã被害ãåããŠããŸãã
æ¢åã®åæ£åææè¡ã§ã¯ããããªè«å€§ãªèšç®éãå¿ èŠã«ãªããããªã¢ã«ãŽãªãºã ãæ¡çšããŸãããã§ãããæå·é貚ã§ã¯ããã¶ã³ãã³å°è»åé¡ãšåŒã°ãããä»ã®ããŒããã ãŸããŠéããããåãããšããæªæã®ããããŒãã«å¯Ÿå¿ããããããã®ãããªã¢ã«ãŽãªãºã ãå¿ èŠã«ãªãã®ã§ãã
ãããã¯ãã§ãŒã³ãäœã£ãŠã¿ãã
çå±ã®èª¬æã¯ãããŸã§ãšããŠãå®éã«ãããã¯ãã§ãŒã³ãäœã£ãŠã¿ãŸããããããã§æãããµã³ãã«ã¯Node.jsã§ã®å®è¡ãåæãšããŠãããLTSã®ææ°çïŒå·çæç¹ã§10.13.0ïŒã§ç¢ºèªãããŠããŸããããã以åã®ããŒãžã§ã³ã§ãåãã§ããããå·¥çšã¯ä»¥äžã®ãšããã§ãã
- ããã·ã¥å€ããšã
- ãã±ãããäœæãã
- ãã©ã³ã¶ã¯ã·ã§ã³ã®ãã±ãããäœæãã
- ãããã¯ãäœæãã
- ãã·ãªã¢ã©ã€ãºãã
- ãã©ã³ã¶ã¯ã·ã§ã³ãæ€èšŒãã
- ãããã¯ãæ€èšŒãã
- Nodeã¯ã©ã¹ãäœã
1. ããã·ã¥å€ããšã
Node.js APIã®cryptoããã±ãŒãžã«ããcreateHashãšãã颿°ã䜿ããŸãããã®é¢æ°ã®åŒæ°ã«ã䜿ãããããã·ã¥é¢æ°ã®ååãæååã§æå®ããã°ãããã·ã¥ãã¹ããªãŒã ã§åŠçã§ãããªããžã§ã¯ããè¿ã£ãŠããŸãã
const { createHash } = require('crypto') const sha256s = buf => { const hash = createHash('sha256') // hash ã¯ããã·ã¥å€ãæ±ããã¹ããªãŒã ãªããžã§ã¯ã hash.write(buf) return hash .digest() .toString('hex') .substr(-40) }
hash.write()ã®åŒæ°ã«ããã·ã¥å€ã«å«ãããããŒã¿ãæžã蟌ã¿ãŸããhashã¯ã¹ããªãŒã ãªããžã§ã¯ããªã®ã§ãhash.write()ãè€æ°åã«åããŠå®è¡ã§ããããã倧ããªãµã€ãºã®ããŒã¿ãåå²åŠçã§ããŸãããã ããä»åã®ãããªç®çã§ã¯ãã®ãããªäœ¿ãæ¹ãããŸããã
hash.digest()ã«ãã£ãŠãããã·ã¥å€ãçŽãããã€ããªããŒã¿ãBufferåãšããŠåž°ã£ãŠããã®ã§ãæ±ããããããã«toString('hex')ã§16鲿°æååã«å€æããŸãã
æåŸã«substr(-40)ããŠããã®ã¯ãæååã®å
é 40æå以å€ãåãæšãŠãŠããããã§ãã
ä»åã¯ãããã¯ãã§ãŒã³æ§é ãäœããšããå®éšã®ããã256bitïŒ16鲿°æåã§64æåïŒãšããé·ãã¯äžèŠãªã®ã§çç¥ããŠããŸãããããããã¡ããšãããã¯ãã§ãŒã³ãæ§ç¯ããå Žåã¯ãåãæšãŠåŠçãããŸããã
2. ãã±ãããäœæãã
P2Pã®ãããã¯ãã§ãŒã³ãããã¯ãŒã¯ã§ã¯ããã©ã³ã¶ã¯ã·ã§ã³ããããã¯ãããã¯ä»ã®ããŒã¿ããããšãããããã«ã·ãªã¢ã©ã€ãºãããšéœåãããã®ã§ããŸãã¯ã·ãªã¢ã©ã€ãºãã颿°ãäœæããŸãã
const serialize = (type, data) => JSON.stringify({ type, ...data })
JSON.stringifyã¯åŒæ°ã«æå®ããããŒã¿ãJSONæååã«å€æãã颿°ã§ãJavaScriptã®æšæºæ©èœã§ããtypeã¯ãã±ããã®ã¿ã€ããæããŸããæ¬çš¿ã§ã¯ããã©ã³ã¶ã¯ã·ã§ã³ãªãtxã§ããããã¯ãªãblockãšããŸãã
const createPacket = (type, data, rawdata = data) => { const serialized = serialize(type, data) const hash = sha256s(serialized) return { type, serialized, hash, ...rawdata } }
ãŸããtypeãšdataãããšã«ã·ãªã¢ã©ã€ãºããã·ãªã¢ã©ã€ãºãããæååserializedãããšã«ããã·ã¥å€hashãèšç®ããŸããcreatePacketã¯ãã·ãªã¢ã©ã€ãºãããæååãšãã以å€ã®ããŒã¿ã广çã«ç®¡çããããã®é¢æ°ã§ãã
rawdataã¯äœã®ããã«ããã®ã§ããããïŒ ããã¯ããŒã¿ã®ç®¡çäžãã·ãªã¢ã©ã€ãºãããåã®ããŒã¿ãä¿æããŠããæ¹ãæ¥œã ãããšããçç±ã§ãããããã¯ãäœæãããšãã«æå³ãã§ãŠããŸãã
颿°ã®åŒæ°ãšããŠrawdata = dataã¯åæå€ã§ãã3ã€ç®ã®åŒæ°ãæå®ããªããã°ã2ã€ç®ã®åŒæ°ããã®ãŸãŸäœ¿ãããŸãã
...rawdataã¯ããªããžã§ã¯ãã®äžèº«ãããã®å€æ°ãšããŠå±éãããšãããªããžã§ã¯ãã¹ãã¬ããæ§æã§ããJavaScriptã®èšèªä»æ§ã®ææ°çã§ããECMAScript 2018ã§è¿œå ããããšãŠã䟿å©ã§ãã
3. ãã©ã³ã¶ã¯ã·ã§ã³ã®ãã±ãããäœæãã
æ¬çš¿ã§ã¯ããã©ã³ã¶ã¯ã·ã§ã³ã«ã¯ãéä¿¡å ã¢ãã¬ã¹ã»éä¿¡å ã¢ãã¬ã¹ã»éé¡ã ããæžã蟌ãããã«ããŸãã
const createTx = (from, sendTo, amount) => { const data = { from, sendTo, amount } return createPacket('tx', data) }
ããŠããã©ã³ã¶ã¯ã·ã§ã³ã®ç¹æ®ãªåœ¢ãšããŠã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ããããšæžããŸãããã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ã§ã¯ãéä¿¡å ãnullã§ãéé¡ã50åºå®ãšãã圢ã«ããŸãã
const createCoinbaseTx = sendTo => { return createTx(null, sendTo, 50) }
4. ãããã¯ãäœæãã
ãããã¯ã®çæã«ã¯æäœéããã©ã³ã¶ã¯ã·ã§ã³ã®éãŸããšãåã®ãããã¯ã®ããã·ã¥å€ãå¿ èŠã«ãªããŸãã
const createBlock = (txs, prevHash) => { const data = { txs: txs.map(tx => ({ hash: tx.hash, data: tx.serialized })), prevHash } const rawdata = { txs, prevHash } return createPacket('block', data, rawdata) }
ãã®txsã¯ããã§ã«ãã±ããã«ãããã®ã®é
åã§ããåã
ã®èŠçŽ txã«ã¯ãcreatePacket颿°ã§äœæããtypeãhashãserializedãªã©ãçŽããããŠããŸãã
txs.mapã¯é
åã®äžèº«ãå å·¥ããmapã¡ãœããã§ããåŒæ°ã«é¢æ°ãæå®ãããšãèŠçŽ ã®1ã€ãã€ãå å·¥ã§ããŸãã
const data =ã§ãtxsã¡ã³ããŒã«ã¯ãã©ã³ã¶ã¯ã·ã§ã³ã®ã·ãªã¢ã©ã€ãºãããããŒã¿ãšãã©ã³ã¶ã¯ã·ã§ã³ã®ããã·ã¥å€ãprevHashãšããŠåã®ãããã¯ã®ããã·ã¥å€ããŸãšããããŒã¿ãäœæããŠããŸãã
createPacketã§ãrawdataã¯ãã©ã³ã¶ã¯ã·ã§ã³ã®ã·ãªã¢ã©ã€ãºãããåã®ããŒã¿ãä¿æããããã«äœ¿ãããŠããŸããããã«ããããããã¯ãããã€ã¬ã¯ãã«ãã©ã³ã¶ã¯ã·ã§ã³ã®äžèº«ïŒsendToãªã©ïŒã«ã¢ã¯ã»ã¹ã§ããã®ã§ãã
ãããŸã§ã¯ãã±ããã®äœæã«å¿ èŠãªé¢æ°ãäœã£ãŠããŸãããããã±ãããåãåã£ãŠå éšããŒã¿ãšããŠæ±ãããã®åŠçãå¿ èŠã«ãªããŸãã
5. ãã·ãªã¢ã©ã€ãºãã
ãŸãã¯ãã·ãªã¢ã©ã€ãºãããããŒã¿ãJavaScriptã®ããŒã¿ã»ãªããžã§ã¯ãã«å€æïŒãã·ãªã¢ã©ã€ãºïŒã§ãã颿°ãäœæããŸãã
const deserialize = serialized => { const hash = sha256s(serialized) const rawdata = JSON.parse(serialized) return { type: rawdata.type, serialized, hash, ...rawdata } }
JSON.parse颿°ã¯ãJSONæååãšããŠã·ãªã¢ã©ã€ãºãããããŒã¿ããJavaScriptã®ãªããžã§ã¯ãã«å€æãããã®ã§ããããJSONã®ä»æ§ãšããŠæ£ãããªãããŒã¿ã§ããã°äŸå€ãçããã®ã§ãçé¢ç®ã«æžããªãtry/catchæ§æãªã©ã䜿ãå¿
èŠãããã§ãããã
sha256s颿°ã§ã·ãªã¢ã©ã€ãºãããæååã®ããã·ã¥å€ããšããJSON.parseã§typeãçã®ããŒã¿ãåãåºããcreatePacketã§äœæãããã®ãšåãããŒã¿ãäœæããŠããŸãã
6. ãã©ã³ã¶ã¯ã·ã§ã³ãæ€èšŒãã
P2Pãããã¯ãã§ãŒã³ãããã¯ãŒã¯ã§ã¯ãåãåã£ãããŒã¿ãæ¬åœã«æ£ããã®ãïŒ ãšããæ€èšŒãå¿ é ã§ããããã§ãã©ã³ã¶ã¯ã·ã§ã³ãæ€èšŒãã颿°ãäœæããŸãã
æ€èšŒã¯txs.every()ãšããã¡ãœããã§è¡ããŸããé
åã®everyã¡ãœããã¯ãmap颿°ã®ããã«èŠçŽ ããããã«ã€ããŠé¢æ°ãåŒã³åºãããã®é¢æ°ããã¹ãŠtrueãè¿ããŠããå Žåã®ã¿trueããã以å€ã ãšfalseãè¿ãããã«ããŠããŸããæ€èšŒã«æåããã®ã¯trueãè¿ã£ãŠãããšãã ãã§ããfalseãªã倱æãããšããããšã§ãã
const validateTxs = txs => { const wallets = {} return txs.every(({ from, sendTo, amount }) => { if (!from) { if (amount !== 50) { return false } } else { wallets[from] = (wallets[from] || 0) - amount if (wallets[from] < 0) { return false } } wallets[sendTo] = (wallets[sendTo] || 0) + amount return true }) }
ä»åäœæããŠããããã°ã©ã ã®ä»æ§ãšããŠãã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ïŒfromãnullã§amountã50ã®ãã®ïŒã«ãã£ãŠã³ã€ã³ãçããŠããã©ã³ã¶ã¯ã·ã§ã³ã§ã¯ã³ã€ã³ãééããã ãã®ãã®ãšããŠããŸãã
ããã§ãã¢ãã¬ã¹ããšã®ã³ã€ã³ã®ãããšãã远ãããããšããã©ã³ã¶ã¯ã·ã§ã³ãæ£ãããã©ããã®æ€èšŒãã§ããŸãã
walletsã¯å
šå¡ã®ãŠã©ã¬ããïŒè²¡åžãwalletïŒã衚çŸãããªããžã§ã¯ãã§ããwallets['hoge']ã§ãhogeãšããã¢ãã¬ã¹ã®äººã®æ®é«ã«ã¢ã¯ã»ã¹ããŸãã
ããã§ã¯ãŸããif (!from)ã§fromãnullãã確èªããŸããnullã®å Žåãamountã50ã®ãã®ã ããæ£ããã®ã§ããã以å€ã¯æ€èšŒã倱æããŸãã
fromã空ã§ã¯ãªãå Žåã¯éåžžã®ãã©ã³ã¶ã¯ã·ã§ã³ãªã®ã§ãéä¿¡å
ã®æ®é«ãããã©ã³ã¶ã¯ã·ã§ã³ã®éé¡ãåŒããŸãã(wallets[from] || 0)ã¯JavaScriptãªã©ã¹ã¯ãªããèšèªã§ããããã€ãã£ãªã ã§ããè«çæŒç®å||ã§ã¯ã||ã®åãtrueãšããŠå€æã§ããå Žåã¯åã®å€ããã®ãŸãŸäœ¿ãããããã§ãªãå Žåã¯ãåŸè
ããã®ãŸãŸäœ¿ããããšãããã®ã§ãã
ã€ãŸããwallets[from]ã«ãŸã äœãå
¥ã£ãŠããªããäžçªæåã®ç¶æ
ã§ã¯0ãæ¡çšããããšãããã®ã§ããããããŠå£åº§æ®é«ãæžãããŠã¿ãŠãã€ãã¹ã«ãªãã°æ€èšŒå€±æã§ãã
ãããŸã§ã§æ€èšŒã倱æããªããã°ãwallets[sendTo]ã®éé¡ãå¢ãããŸãããã®æé ã§äžéãã®ãã©ã³ã¶ã¯ã·ã§ã³ããã§ãã¯ããŠããšã©ãŒãçããªããã°æ€èšŒã¯æåã§ãã
7. ãããã¯ãæ€èšŒãã
ãã©ã³ã¶ã¯ã·ã§ã³ã®æ€èšŒãéèŠã§ããããã以äžã«ãããã¯ã®æ€èšŒãéèŠã§ãã
ãããã¯æ€èšŒã®é¢æ°ã¯ãåŒæ°ã«ã·ãªã¢ã©ã€ãºããããããã¯ã®ãã±ããã®é
åãåã仿§ã«ããŸãããã®ãããã¯ã®ãã±ãããdeserializeãããšãtxsãšprevHashãšããããããã®ããŒã¿ãåºãŠããŸãã
const createInvalidBlockError = message => new Error(`Invalid Block: ${message}`) const validateBlocks = serializedBlocks => { let allTx = [] let prevHash = '0000000000000000000000000000000000000000' serializedBlocks.forEach(serializedBlock => { const { data, hash } = deserialize(serializedBlock) if (prevHash !== data.prevHash) { throw createInvalidBlockError( `block hash error: ${prevHash} !== ${data.prevHash}` ) } prevHash = hash
ãããŸã§ã®ã³ãŒãã§ãåã®ãããã¯ã§ããprevHashã®æ€èšŒãè¡ã£ãŠããŸãããã®ããã°ã©ã ã§ã¯genesis hashã0000000000000000000000000000000000000000ã«ããŠãããããlet prevHash =ã§åæåããŠããŸããããšã¯ãããã¯ãã«ãŒãã§åŠçããªãããprevHashã®å€å®ãšæŽæ°ãè¡ããŸãã
const txs = data.txs.map(serialized => { const tx = deserialize(serialized).data if (tx.type !== 'tx') { throw createInvalidBlockError(`Tx packet type error: ${tx.type} !== tx`) } return tx })
deserializeããããããã¯ã«å«ãŸããtxsã¯ããã©ã³ã¶ã¯ã·ã§ã³ãã·ãªã¢ã©ã€ãºãããã±ãããªã®ã§ãããã«deserializeããå¿
èŠããããŸãããã®ãšããtxsã«ãã±ããã¿ã€ããšããŠtx以å€ãå«ãŸããŠããã°ãšã©ãŒãšããŠããŸãã
if (txs.length < 1) { throw createInvalidBlockError('Empty Txs') }
txsã®é·ãã1æªæºãã€ãŸã0ã§ããã°äœããã©ã³ã¶ã¯ã·ã§ã³ãå«ãŸããŠãããããšã©ãŒãšããŠããŸãã
if (!isCoinbaseTx(txs[0])) { throw createInvalidBlockError('first Tx must be CoinbaseTx') }
æåã®ãã©ã³ã¶ã¯ã·ã§ã³ã¯ãå¿ ãã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ã§ãã
if (txs.length > 1 && !txs.slice(1).every(tx => !isCoinbaseTx(tx))) { throw createInvalidBlockError('Illegal CoinbaseTx') }
ã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ä»¥å€ã®ãã©ã³ã¶ã¯ã·ã§ã³ãããå Žåããããã¯ãã¹ãŠã³ã€ã³ããŒã¹ãããªãéåžžã®ãã©ã³ã¶ã¯ã·ã§ã³ã§ãã
allTx = allTx.concat(txs) }) evaluateTxs(allTx) }
ãããŸã§äžéããã§ãã¯ããã°ããããã¯ã®æ€èšŒã¯å®äºã§ãã
allTxã¯ããã©ã³ã¶ã¯ã·ã§ã³ããã¹ãŠé£çµãããã®ã§ããã£ãäœã£ãevaluateTxsã§ããã«ãã©ã³ã¶ã¯ã·ã§ã³ã®æ€èšŒãè¡ããŸãã
8. Nodeã¯ã©ã¹ãäœã
ä»åã¯P2Péä¿¡ããã³ãŒããŸã§ã¯äœããŸããããåããŒãïŒãã¢ïŒãå®éšããããã®ã¯ã©ã¹ãäœããŸãã
class Node { constructor(seed = null) { this.address = sha256s(seed || randomBytes(32)) this.pendingTxs = [] this.blocks = [] this.peers = [] this.prevHash = '0000000000000000000000000000000000000000' }
Nodeã¯ã©ã¹ã®ã¡ã³ããŒã«ã¯ãééçšã®ã©ãã«ã§ããaddressãšããããã¯ã«ãŸã å
¥ã£ãŠããªãpendingTxsãšãæ¢ã«çºè¡ãããŠããblocksãšãæ¥ç¶äžèЧã§ããpeersãšãprevHashãæã¡ãŸãã
_getAllTx() { return [].concat( ...this.blocks.map(block => { return deserialize(block).data.txs.map(tx => deserialize(tx).data) }), this.pendingTxs.map(tx => deserialize(tx).data) ) } getBalance(address = this.address) { const txs = this._getAllTx() const wallets = evaluateTxs(txs) return wallets[address] || 0 }
ãŸãã¯ãçŸæç¹ã§ã®åã¢ãã¬ã¹ã®æ®é«ã確èªããgetBalanceã¡ãœããã§ããåŒæ°çç¥æã«ã¯èªåèªèº«ã®æ®é«ãè¿ããŸãã
_getAllTxã¯ãthis.blockã«å«ãŸãããã©ã³ã¶ã¯ã·ã§ã³ãšthis.pendingTxsããã¹ãŠdeserializeããŠ1ã€ã®é
åã«å
¥ãããã®ã§ããJavaScriptã§ã¯æ
£ç¿ãšããŠã_ã§å§ãŸãã¡ã³ããŒã¯ãã©ã€ããŒãæ±ãã«ãããšãããã®ããããŸããTypeScriptã§ããã°ãprivate修食åã䜿ããŸãã
evaluateTxsã¯ãã©ã³ã¶ã¯ã·ã§ã³ã®æ€èšŒã§ãåãŠã©ã¬ããã®è³éç§»åãé ã«è¿œããããŠãããããæçµçµæã¯åã¢ãã¬ã¹ã®æ®é«ãšããããšã«ãªããŸãããŸã éä¿¡ãããŠããªãã¢ãã¬ã¹ã®å Žåã¯ãå
ã»ã©ç޹ä»ãã||ã«ããã€ãã£ãªã ã䜿ã£ãŠ0ãè¿ããŸãã
generate() { const coinbaseTx = createCoinbaseTx(this.address) const txs = [coinbaseTx.serialized, ...this.pendingTxs] const block = createBlock(txs, this.prevHash) this.pendingTxs = [] this.prevHash = block.hash this.blocks.push(block.serialized) this.broadcast(block.serialized) }
generateã¡ãœããã¯ããã€ãã³ã°ãæåãããšããããšã«ããŠãããã¯ãçæããŸãã
ãããã¯ã«å«ãŸãããã©ã³ã¶ã¯ã·ã§ã³ã¯ãèªåå®ã®ã³ã€ã³ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ãšãthis.pendingTxsã§ãã
ãããã¯ãäœæããããthis.pendingTxsãç©ºã«æ»ããŠthis.prevHashãæŽæ°ããthis.blocksã«ãããã¯ã远å ããåŸã»ã©èª¬æããbroadcastã¡ãœããã§ä»ã®ããŒãã«ãããã¯ãæµããŸãã
send(sendTo, amount) { if (amount > this.getBalance()) { throw new Error('Wallet error: Insufficient funds') } const tx = createTx(this.address, sendTo, amount) this.pendingTxs.push(tx.serialized) this.broadcast(tx.serialized) }
sendã¡ãœããã§ééãè¡ããŸããæ®é«ãè¶³ããªãå Žåã¯ãšã©ãŒã«ãªããŸãã
connect(peer) { this.peers.push(peer) peer.peers.push(this) }
connectã¡ãœããã¯ãå¥ã®ããŒãïŒãã¢ïŒããªã¹ãã«è¿œå ããçžæã®ãã¢ã«èªåãç»é²ããŸãã
broadcast(packet) { this.peers.forEach(peer => { peer.recv(packet) }) }
broadcastã¡ãœããã¯ãæ¥ç¶ããŠãããã¢ãã¹ãŠã®ãã±ãããéä¿¡ããŸããå®éã®ã³ãŒããšããŠã¯ãçžæã®recvã¡ãœãããå©ããŠããã ãã§ãã
recv(packet) { const { type } = deserialize(packet).data switch (type) { case 'tx': { this._receiveTx(packet) break } case 'block': { this._receiveBlock(packet) break } } }
recvã¡ãœããã§ã¯ãåãåã£ããã±ããã®typeãèŠãŠãtxãšblockã§åŠçãåããŠããŸãã
_receiveTx(packet) { const { from } = deserialize(packet).data if (from === null) { throw new Error('Invalid Tx: reject CoinbaseTx') } const txs = this._getAllTx() txs.push(deserialize(packet).data) evaluateTxs(txs) this.pendingTxs.push(packet) }
ãã©ã³ã¶ã¯ã·ã§ã³ã®å ŽåããŸãåãåã£ããã©ã³ã¶ã¯ã·ã§ã³ãã³ã€ã³ããŒã¹ãªããšã©ãŒãšããŸããå®éã®P2Pããã°ã©ã ã®å Žåãrecvããšã©ãŒãthrowããã®ã¯è¯ããªãããããã°ã«æ®ãã€ã€ãã±ãããç¡èŠãããšããæåã«ãªãã§ãããã
ãã©ã³ã¶ã¯ã·ã§ã³ããã¹ãŠæ€èšŒããŠãšã©ãŒãåºãªããã°ãthis.pendingTxsã«ãã±ããã远å ããŸãã
_receiveBlock(packet) { const blocks = [...this.blocks, packet] validateBlocks(blocks) this.blocks.push(packet) }
ãããã¯ã®å Žåããæ°ãããããã¯ãå ããŠvalidateBlocksã§æ€èšŒããŠãšã©ãŒãåºãªããã°ãthis.blockã«ãã±ããã远å ããŸãã
BitcoinãŠã©ã¬ãããäœã£ãŠã¿ãã
æåŸã«å®éã®Bitcoinã®ãŠã©ã¬ããããã°ã©ã ãäœã£ãŠã¿ãŸãããã
Bitcoinã®éä¿¡å šéšãå®è£ ããã®ã¯å€§å€ãªãããBitcoinå ¬åŒãŠã©ã¬ããã®Bitcoin CoreãåãããŠãJSON-RPCçµç±ã§å¶åŸ¡ãããšãããã¿ãŒã³ã§ãããŸãã
mkdir bitcoin-wallet cd bitcoin-wallet
ä»åã®ããã°ã©ã ã¯ãbitcoin-walletããšããååã§äœããŸãã倧ãŸããªæé ã¯ä»¥äžã®ãšããã§ãã
- ããã±ãŒãžã®ã€ã³ã¹ããŒã«ãšèµ·å
- 颿°ãäœæ
- Walletã®äœæ
Bitcoin Coreãã€ã³ã¹ããŒã«ãã
macOSã§ããã±ãŒãžç®¡çã·ã¹ãã Homebrewã䜿ã£ãŠããã°ãbitcoindããã±ãŒãžãã€ã³ã¹ããŒã«ããã ãã§ãã
brew install bitcoind
bitcoindãèµ·åãã
ããŒã¿çšã®ãã£ã¬ã¯ããªãäœæããŠãããŠãbitcoindãèµ·åããŸãã
$ mkdir data $ bitcoind -testnet -datadir=data -txindex -server -rpcuser=u -rpcpassword=p
bitcoindãèµ·åãããšãã®æ³šæç¹ãšããŠã-testnetã§ãã¹ãçšã®ãããã¯ãŒã¯ã«æ¥ç¶ããŸããããtestnetã§ã¯ãBitcoin testnet3 faucetã®ãããªãµã€ãã§ãç¡æã§testnetå°çšã®BitcoinãåãåããŸãããã®ã³ã€ã³ã䜿ã£ãŠãå®éã®ãããã¯ãŒã¯äžã§ã®ãã¹ããããã®ã§ãã
-datadir=dataã§ããŒã¿ãã£ã¬ã¯ããªã®äœçœ®ãæå®ããŸããã¡ãªã¿ã«ãçŸæç¹ã§26GBãã®å®¹éãæ¶è²»ããã®ã§ããã¹ããçµããŠäžèŠã«ãªã£ããåé€ããã®ãããããããŸãã
JSON-RPCã§ã¯ããŠãŒã¶ãŒåããã¹ã¯ãŒããã³ãã³ãã©ã€ã³ãããã¯èšå®ãã¡ã€ã«ã§å®çŸ©ããŸãããä»äººã«èŠãããå¯èœæ§ããããã®ãšããŠæ³šæããŠãã ããã
ãŸããæ¬éçšã§äœ¿ãå ŽåããŠã©ã¬ããã®ãã¹ã¯ãŒãããã¯ãªã©ãæ°ãã€ããªããšãããªãããšããããŸãã
å¿ èŠãªããã±ãŒãžãã€ã³ã¹ããŒã«ãã
npm init -y
npm i request-promise
ä»åã¯ãJSON-RPCãå©ãããã«ãrequest-promiseãšããnpmããã±ãŒãžã䜿ããŸãã
æ€çŽ¢ããã°ãBitcoin Coreã®JSON-RPCãå©ãããã®ããã±ãŒãžãããã€ãèŠã€ãããŸãããã©ããå€ããã¡ã³ããã³ã¹ããããŠããŸãããããããèªåã§å©ããŠããããªã«é·ãã³ãŒãã«ãªããªããããä»åã¯èªäœããŠããŸããŸãã
Bitcoin Coreãå©ã
JSON-RPCã§ã¯ãHTTP POSTã§methodãšparamsãšããããããã®ãã©ã¡ãŒã¿ãéãã€ããŸããmethodã¯ãhelpãgetnewaddressãªã©ã®ååãæã¡ãŸãã
ã¡ãªã¿ã«Bitcoinã§ã®éçºã§ã¯ãbitcoin-cliãšããCLIã§Bitcoin Coreãå¶åŸ¡ããã³ãã³ããå©ãã®ãå®çªã§ããããã£ãŠããããšã¯ãŸã£ããåãã§ãã
const rp = require('request-promise') rp(`http://localhost:18332`, { method: 'POST', body: JSON.stringify({ method, params }), auth: { user, pass } })
æè¿ã®JavaScriptã§ã¯ãéåæåŠçã¯Promiseãå®çªã§ããrp颿°ãå©ããšPromiseãè¿ã£ãŠããŸãã
rp(...).then(response => console.log(response))
Promiseã§ã¯thenã¡ãœãããå©ãããšã§éåæåŠçã®ç¶ããæžãããšãã§ããŸããã€ãŸããrequest-promiseã®å ŽåãHTTP POSTãå®è¡ããããšthenã®äžèº«ãå®è¡ãããŸãã
thenã®æ»ãå€ã¯Promiseãªããžã§ã¯ããªãããããã«thenãã€ãªããããŸãã
ããã§ã¯ãããã«äžæ©èžã¿èŸŒãã§éåæåŠçã®async/awaitã䜿ã£ãŠã¿ãŸãã
const dispatch = async (user, pass, method, ...params) => { const { result, error } = JSON.parse(await rp(...)) }
async宣èšããã颿°ã®äžã§ã¯ãawaitãšããããŒã¯ãŒããã€ããããšã§Promiseãåæçã«æ±ããŸããå
·äœçã«ã¯awaitã®ããšã«ç¶ãã³ãŒãã¯ãthenã®äžèº«ãåŒã³åºããããŸã§åŸ
æ©ããŸãã
const { result, error } = JSON.parse(await rp())ãšããã³ãŒãã¯ãrp().then(({ result, error }) => {....})ãšåãæå³ãæã¡ãŸãã
thenãã²ãããé£éãããã®ã¯ãããªãã«é¢åã§ãããawaitã䞊ã¹ãã ããªãæžãããããçè§£ããããã³ãŒãã«ãªããŸãã
Promiseã¯thenã ãã§ã¯ãªããcatchãšããã¡ãœãããæã¡ãŸããããã¯ãšã©ãŒæã®ãªã«ããªãŒãã©ãããããšãããã®ã§ãã
ä»åã®äºäŸã§ã¯ãHTTPã®æ¥ç¶ã«ãšã©ãŒãçããããããã¯JSON-RPCã§methodåãæ£ãããªããªã©ã®ãšã©ãŒãçããå Žåã®åŠçãå¿ èŠã«ãªããŸãã
catch(e => { if (e.statusCode) { return JSON.stringify({ error: JSON.parse(e.error).error }) } else { return JSON.stringify({ error: e.error }) } })
éä¿¡èªäœã¯æåãããã®ã®ãBitcoin JSON-RPCã®ãããã³ã«çãªåé¡ããããšãã«ã¯e.statusCodeã«500ãªã©ãã»ãããããŠããŸãããŸãããã®å Žåãe.errorã«JSONã§ãšã³ã³ãŒãããããšã©ãŒæ
å ±ãå
¥ã£ãŠãããšãã埮åŠã«ãããããããšã«ãªã£ãŠããŸãã
ã¯ã©ã€ã¢ã³ããäœã颿°ãäœã
ããã»ã©ãŸã§ã®ã³ãŒãã§ã¯ãæ¥ç¶å
ãããŒãã³ãŒãã£ã³ã°ãããŠããããšãšãdispatch颿°ã§ãæ¯åuserãšpassãæå®ããŠããŸãã
const createClient = ({ host, rpcport, user, pass }) => { // dispatch颿°ã«ããããã®ãè¿ã }
ãã®ãããhostã»rpcportã»userã»passãšããåŒæ°ã§ãŸãåæåããŠãdispatchã«ããã颿°ãè¿ãããã«ããŸãã
以äžãclient.jsã§å®çŸ©ããŸãã
const rp = require('request-promise') const createClient = ({ host, rpcport, user, pass }) => { return async (method, ...params) => { const { result, error } = JSON.parse( await rp(`http://${host}:${rpcport}`, { method: 'POST', body: JSON.stringify({ method, params }), auth: { user, pass } }).catch(e => { if (e.statusCode) { return JSON.stringify({ error: JSON.parse(e.error).error }) } else { return JSON.stringify({ error: e.error }) } }) ) if (error) { throw error } else { return result } } } module.exports = { createClient }
ãŠã©ã¬ããã®äœæ
ããŠããŠã©ã¬ãããäœããŸããã³ãã³ãã©ã€ã³ã§åãããŒã«ãšããŠäœãã®ã§ãã³ãã³ãã©ã€ã³åŒæ°ãæ±ããŸãã
Node.jsã§ã¯ãprocess.argvã§ã³ãã³ãã©ã€ã³åŒæ°ã«ã¢ã¯ã»ã¹ã§ããŸãã process.argv[0]ã«ã¯Node.jsã®ã³ãã³ããã®ãã®ãå
¥ããprocess.argv[1]ã«ã¯ã¹ã¯ãªããåãå
¥ããŸãããã®ãããåŒæ°ã¯process.argv[2]ããå§ãŸããŸãã
const { createClient } = require('./client') const wallet = async () => { if (process.argv.length < 3) { console.log('usage: wallet <command> [option...]') process.exit(1) } const command = process.argv[2] if (!(command in commands)) { console.log(`unknown command: ${command}`) process.exit(1) } const conf = { host: 'localhost', rpcport: 18332, user: 'u', pass: 'p' } const cl = createClient(conf) await commands[command](cl, ...process.argv.slice(3)) } wallet().catch(err => console.error(err))
process.argv.lengthã3æªæºã®å Žåãusageã衚瀺ããŠçµäºããŸãããŸããåŒæ°ã§æå®ããcommandããåŸã»ã©èª¬æããã³ãã³ãé
åã«ãªãå Žåããunknown commandã衚瀺ããŠçµäºããŸãã
client.jsã§å®çŸ©ããcreateClientã§ã¯ã©ã€ã¢ã³ããäœæããŸãããã®ãšããèšå®ã¯testnetåãã«æ±ºãæã¡ã§æžããŠããŸãã
ãŠã©ã¬ããã®ã³ãã³ãå®çŸ©
ã³ãã³ãã¯ãã®ããã«å®çŸ©ããŠããŸãã
const newAddress = async cl => { const address = await cl('getnewaddress', 'my address') console.log(address) } // äžç¥ const commands = { newAddress, info, send, dump, importPriv }
newAddressã³ãã³ã
$ node src/wallet/cli.js newAddress 2N7wqmAYRhiDUXhnZKtMbWBDSeXG4ddYvSy
èµ·åããçŽåŸã¯ããŠã©ã¬ãããšããŠäœ¿ãããã®ã¢ãã¬ã¹ããããŸããããŸãã¯newAddressã³ãã³ãã§ã¢ãã¬ã¹ãäœæããŸãã
const newAddress = async cl => { const address = await cl('getnewaddress', 'my address') console.log(address) }
getnewaddressãšããJSON-RPCã¡ãœããã¯ãåŒæ°ã«æååãæž¡ããšã©ãã«ãšããŠæ±ããŸããåŠçã®éœåäžãã©ãã«ã¯my address決ãæã¡ãšããŸãã
infoã³ãã³ã
$ node src/wallet/cli.js info addresses: [2N7wqmAYRhiDUXhnZKtMbWBDSeXG4ddYvSy] balance: 0 block height: 273905
infoã³ãã³ãã§ãèªåã®æã£ãŠããã¢ãã¬ã¹ãšãæ®é«ããããã¯é·æ
å ±ã衚瀺ããŸãã
const info = async cl => { const addresses = await cl('getaddressesbylabel', 'my address') const balance = await cl('getbalance') const blockchainInfo = await cl('getblockchaininfo') console.log(`addresses: [${Object.keys(addresses).join(', ')}]`) console.log(`balance: ${balance}`) console.log(`block height: ${blockchainInfo.blocks}`) }
åè¿°ã®my addressãšããã©ãã«ã¯ãgetaddressesbylabelJSON-RPCã¡ãœããã§äœ¿ã£ãŠããŸãã
getblockchaininfoã¯ãBitcoinãããã¯ãŒã¯ã®ç¶æ³ãç¥ãããã®JSON-RPCã¡ãœããã§ãã
sendã³ãã³ã
$ node src/wallet/cli.js send 2N6PaxqoUFbWYiumFq3i6nuQPQ1LG8VJrE1 0.04 bef4f254851ff8cff12c398978f45bb3d73c713c38238330f2869898e0547151
sendã³ãã³ãã¯ãééããã³ãã³ãã§ããééã«æåããã°ééãã©ã³ã¶ã¯ã·ã§ã³ã®IDã衚瀺ãããŸãã
ãã©ã³ã¶ã¯ã·ã§ã³ãèŠãããšãã§ãããµã€ããäŸãã°BlockCypherã®Bitcoin Testnet Block Explorerã§ã確èªã§ããŸãã
const send = async (cl, address, amount) => { const txid = await cl('sendtoaddress', address, amount) console.log(txid) }
sendtoaddressJSON-RPCã¡ãœããã§ééãè¡ããŸãã
dumpã³ãã³ã
$ node src/wallet/cli.js dump 2N6PaxqoUFbWYiumFq3i6nuQPQ1LG8VJrE1 <ãã©ã€ããŒãããŒ>
dumpã¯ãèªåã®æã£ãŠããã¢ãã¬ã¹ã«å¯Ÿå¿ããç§å¯éµãåºåããã³ãã³ãã§ãããã®ç§å¯éµã®æååãä»äººã«èŠããããšãBitcoinãçãŸãæŸé¡ãªã®ã§ã泚æãã ããã
const dump = async (cl, address) => { const priv = await cl('dumpprivkey', address) console.log(priv) }
dumpprivkeyJSON-RPCã¡ãœããã¯ãæå®ããã¢ãã¬ã¹ã®ç§å¯éµãããå Žåã«ããããåŸããã®ã§ãã
importPrivã³ãã³ã
$ node src/wallet/cli.js importPriv <ãã©ã€ããŒãããŒ>
importPrivã³ãã³ãã¯ãç§å¯éµãã€ã³ããŒããããã®ã§ãããããã§ãããããç§å¯éµãä»äººã«ç¥ãããŠã¯ãããªãã®ã§ãã
const importPriv = async (cl, priv) => { await cl('importprivkey', priv, 'my address') }
importprivkeyJSON-RPCã¡ãœããã¯ãæå®ããç§å¯éµãã€ã³ããŒãããŸãããªãã·ã§ã³ã§ç¬¬2åŒæ°ã«ã©ãã«ãæå®ã§ããŸãã
æåŸã«
ãããŸã§ããããã¯ãã§ãŒã³ã®ä»çµã¿ã解説ããå®éã«ãããã¯ãã§ãŒã³ãBitcoinãŠã©ã¬ããã®ãµã³ãã«ããã°ã©ã ãèŠãŠããŸããããµã³ãã«ã®å šãœãŒã¹ã¯ãGitHubã®æ¬¡ã®ã¢ãã¬ã¹ããããŠã³ããŒãã§ããŸãããã²èªåã§è©ŠããŠã¿ãŠãã ããã
ç·šéïŒèäºåæ¥ïŒZINEïŒ




