【小徑分岔的花園:通往未來的多種可能性】
這是推薦的短篇小說,會有一點燒腦,出自阿根廷作家博爾赫斯。
他在這部〈小徑分岔的花園〉的短篇中,用了類似於量子力學的概念,探討關於「時間」的奧秘。
而對很多作家來說,故事裡出現太多「巧合」會是一大敗筆,但在這部短篇中,這些巧合反倒成為一種特別的隱喻。
一起來看看這部頗有深意的文學作品吧。
-
小徑分岔的花園 / 博爾赫斯
獻給維多利亞·奧坎波
利德爾·哈特寫的《歐洲戰爭史》第二百四十二頁有段記載,說是十三個英國師(有一千四百門大炮支援)對塞爾-蒙托邦防線的進攻原定於1916年7月24日發動,後來推遲到29日上午。利德爾·哈特上尉解釋說延期的原因是滂沱大雨,當然並無出奇之處。青島大學前英語教師余准博士的證言,經過記錄、複述、由本人簽名核實,卻對這一事件提供了始料不及的說明。證言記錄缺了前兩頁。
……我掛上電話聽筒。我隨即辨出那個用德語接電話的聲音。是理查·馬登的聲音。馬登在維克托·魯納伯格的住處,這意味著我們的全部辛勞付諸東流,我們的生命也到了盡頭——但是這一點是次要的,至少在我看來如此。這就是說,魯納伯格已經被捕,或者被殺。在那天日落之前,我也會遭到同樣的命運。馬登毫不留情。說得更確切一些,他非心狠手辣不可。作為一個聽命於英國的愛爾蘭人,他有辦事不熱心甚至叛賣的嫌疑,如今有機會挖出日爾曼帝國的兩名間諜,拘捕或者打死他們,他怎麼會不抓住這個天賜良機,感激不盡呢?我上樓進了自己的房間,可笑地鎖上門,仰面躺在小鐵床上。窗外還是慣常的房頂和下午六點鐘被雲遮掩的太陽。這一天既無預感又無徵兆,成了我大劫難逃的死日,簡直難以置信。雖然我父親已經去世,雖然我小時候在海豐一個對稱的花園裡待過,難道我現在也得死去?隨後我想,所有的事情不早不晚偏偏在目前都落到我頭上了。多少年來平平靜靜,現在卻出了事;天空、陸地和海洋人數千千萬萬,真出事的時候出在我頭上……馬登那張叫人難以容忍的馬臉在我眼前浮現,驅散了我的胡思亂想。我又恨又怕(我已經騙過了理查·馬登,只等上絞刑架,承認自己害怕也無所謂了),心想那個把事情搞得一團糟、自鳴得意的武夫肯定知道我掌握秘密。準備轟擊昂克萊的英國炮隊所在地的名字。一隻鳥掠過窗外灰色的天空,我在想像中把它化為一架飛機,再把這架飛機化成許多架,在法國的天空精確地投下炸彈,摧毀了炮隊。我的嘴巴在被一顆槍彈打爛之前能喊出那個地名,讓德國那邊聽到就好了……我血肉之軀所能發的聲音太微弱了。怎麼才能讓它傳到頭頭的耳朵?那個病懨懨的討厭的人,只知道魯納伯格和我在斯塔福德郡,在柏林閉塞的辦公室裡望眼欲穿等我們的消息,沒完沒了地翻閱報紙……我得逃跑,我大聲說。我毫無必要地悄悄起來,仿佛馬登已經在窺探我。我不由自主地檢查一下口袋裡的物品,也許僅僅是為了證實自己毫無辦法。我找到的都是意料之中的東西。那只美國掛表,鎳制錶鏈和那枚四角形的硬幣,拴著魯納伯格住所鑰匙的鏈子,現在已經沒有用處但是能構成證據,一個筆記本,一封我看後決定立即銷毀但是沒有銷毀的信,假護照,一枚五先令的硬幣,兩個先令和幾個便士,一枝紅藍鉛筆,一塊手帕和裝有一顆子彈的左輪手槍。我可笑地拿起槍,在手裡掂掂,替自己壯膽。我模糊地想,槍聲可以傳得很遠。不出十分鐘,我的計畫已考慮成熟。電話號碼簿給了我一個人的名字,唯有他才能替我把情報傳出去:他住在芬頓郊區,不到半小時的火車路程。
我是個怯懦的人。我現在不妨說出來,因為我已經實現了一個誰都不會說是冒險的計畫。我知道實施過程很可怕。不,我不是為德國幹的。我才不關心一個使我墮落成為間諜的野蠻的國家呢。此外,我認識一個英國人——一個謙遜的人——對我來說並不低於歌德。我同他談話的時間不到一小時,但是在那一小時中間他就像是歌德……我之所以這麼做,是因為我覺得頭頭瞧不起我這個種族的人——瞧不起在我身上彙集的無數先輩。我要向他證明一個黃種人能夠拯救他的軍隊。此外,我要逃出上尉的掌心。他隨時都可能敲我的門,叫我的名字。我悄悄地穿好衣服,對著鏡子裡的我說了再見,下了樓,打量一下靜寂的街道,出去了。火車站離此不遠,但我認為還是坐馬車妥當。理由是減少被人認出的危險;事實是在闃無一人的街上,我覺得特別顯眼,特別不安全。我記得我吩咐馬車夫不到車站入口處就停下來。我磨磨蹭蹭下了車,我要去的地點是阿什格羅夫村,但買了一張再過一站下的車票。這趟車馬上就開:八點五十分。我得趕緊,下一趟九點半開車。月臺上幾乎沒有人。我在幾個車廂看看:有幾個農民,一個服喪的婦女,一個專心致志在看塔西倫的《編年史》的青年,一個顯得很高興的士兵。列車終於開動。我認識的一個男人匆匆跑來,一直追到月臺盡頭,可是晚了一步。是理查·馬登上尉。我垂頭喪氣、忐忑不安,躲開可怕的視窗,縮在座位角落裡。我從垂頭喪氣變成自我解嘲的得意。心想我的決鬥已經開始,即使全憑僥倖搶先了四十分鐘,躲過了對手的攻擊,我也贏得了第一個回合。我想這一小小的勝利預先展示了徹底成功。我想勝利不能算小,如果沒有火車時刻表給我寶貴的搶先一著,我早就給關進監獄或者給打死了。我不無詭辯地想,我怯懦的順利證明我能完成冒險事業。我從怯懦中汲取了在關鍵時刻沒有拋棄我的力量。我預料人們越來越屈從於窮凶極惡的事情;要不了多久世界上全是清一色的武夫和強盜了;我要奉勸他們的是:做窮凶極惡的事情的人應當假想那件事情已經完成,應當把將來當成過去那樣無法挽回。我就是那樣做的,我把自己當成已經死去的人,冷眼觀看那一天,也許是最後一天的逝去和夜晚的降臨。列車在兩旁的梣樹中徐徐行駛。在荒涼得像是曠野的地方停下。沒有人報站名。是阿什格羅夫嗎?我問月臺上幾個小孩。阿什格羅夫,他們回答說。我便下了車。
月臺上有一盞燈光照明,但是小孩們的臉在陰影中。有一個小孩問我:您是不是要去斯蒂芬·亞伯特博士家?另一個小孩也不等我回答,說道:他家離這兒很遠,不過您走左邊那條路,每逢交叉路口就往左拐,不會找不到的。我給了他們一枚錢幣(我身上最後的一枚),下了幾級石階,走上那條僻靜的路。路緩緩下坡。是一條泥土路,兩旁都是樹,枝丫在上空相接,低而圓的月亮仿佛在陪伴我走。
有一陣子我想理查·馬登用某種辦法已經瞭解到我鋌而走險的計畫。但我立即又明白那是不可能的。小孩叫我老是往左拐,使我想起那就是找到某些迷宮的中心院子的慣常做法。我對迷宮有所瞭解:我不愧是彭㝡的曾孫,彭㝡是雲南總督,他辭去了高官厚祿,一心想寫一部比《紅樓夢》人物更多的小說,建造一個誰都走不出來的迷宮。他在這些龐雜的工作上花了十三年工夫,但是一個外來的人刺殺了他,他的小說像部天書,他的迷宮也無人發現。我在英國的樹下思索著那個失落的迷宮:我想像它在一個秘密的山峰上原封未動,被稻田埋沒或者淹在水下,我想像它廣闊無比,不僅是一些八角涼亭和通幽曲徑,而是由河川、省份和王國組成……我想像出一個由迷宮組成的迷宮,一個錯綜複雜、生生不息的迷宮,包羅過去和將來,在某種意義上甚至牽涉到別的星球。我沉浸在這種虛幻的想像中,忘掉了自己被追捕的處境。在一段不明確的時間裡,我覺得自己抽象地領悟了這個世界。模糊而生機勃勃的田野、月亮、傍晚的時光,以及輕鬆的下坡路,這一切使我百感叢生。傍晚顯得親切、無限。道路繼續下傾,在模糊的草地裡岔開兩支。一陣清悅的樂聲抑揚頓挫,隨風飄蕩,或近或遠,穿透葉叢和距離。我心想,一個人可以成為別人的仇敵,成為別人一個時期的仇敵,但不能成為一個地區、螢火蟲、字句、花園、水流和風的仇敵。我這麼想著,來到一扇生銹的大鐵門前。從欄杆裡,可以望見一條林陰道和一座涼亭似的建築。我突然明白了兩件事,第一件微不足道,第二件難以置信;樂聲來自涼亭,是中國音樂。正因為如此,我並不用心傾聽就全盤接受了。我不記得門上是不是有鈴,還是我擊掌叫門。像火花迸濺似的樂聲沒有停止。
然而,一盞燈籠從深處房屋出來,逐漸走近:一盞月白色的鼓形燈籠,有時被樹幹擋住。提燈籠的是個高個子。由於光線耀眼,我看不清他的臉。他打開鐵門,慢條斯理地用中文對我說:「看來彭熙情意眷眷,不讓我寂寞。您准也是想參觀花園吧?」
我聽出他說的是我們一個領事的姓名,我莫名其妙地接著說:「花園?」
「小徑分岔的花園。」
我心潮起伏,難以理解地肯定說:「那是我曾祖彭㝡的花園。」
「您的曾祖?您德高望重的曾祖?請進,請進。」
潮濕的小徑彎彎曲曲,同我兒時的記憶一樣。我們來到一間藏著東方和西方書籍的書房。我認出幾卷用黃絹裝訂的手抄本,那是從未付印的明朝第三個皇帝下詔編纂的《永樂大典》的逸卷。留聲機上的唱片還在旋轉,旁邊有一隻青銅鳳凰。我記得有一隻紅瓷花瓶,還有一隻早幾百年的藍瓷,那是我們的工匠模仿波斯陶器工人的作品……斯蒂芬·亞伯特微笑著打量著我。我剛才說過,他身材很高,輪廓分明,灰眼睛,灰鬍子。他的神情有點像神甫,又有點像水手;後來他告訴我,「在想當漢學家之前」,他在天津當過傳教士。
我們落了座;我坐在一張低矮的長沙發上,他背朝著視窗和一個落地圓座鐘。我估計一小時之內追捕我的理查·馬登到不了這裡。我的不可挽回的決定可以等待。
「彭㝡的一生真令人驚異,」斯蒂芬·亞伯特說。「他當上家鄉省份的總督,精通天文、星占、經典詮估、棋藝,又是著名的詩人和書法家:他拋棄了這一切,去寫書、蓋迷宮。他拋棄了炙手可熱的官爵地位、嬌妻美妾、盛席瓊筵,甚至拋棄了治學,在明虛齋閉戶不出十三年。他死後,繼承人只找到一些雜亂無章的手稿。您也許知道,他家裡的人要把手稿燒掉;但是遺囑執行人——一個道士或和尚——堅持要刊行。」
「彭㝡的後人,」我插嘴說,「至今還在責怪那個道士。刊行是毫無道理的。那本書是一堆自相矛盾的草稿的彙編。我看過一次:主人公在第三回裡死了,第四回裡又活了過來。至於彭㝡的另一項工作,那座迷宮……」
「那就是迷宮,」他指著一個高高的漆櫃說。
「一個象牙雕刻的迷宮!」我失聲喊道。「一座微雕迷宮……」
「一座象徵的迷宮,」他糾正我說。「一座時間的無形迷宮。我這個英國蠻子有幸悟出了明顯的奧秘。經過一百多年之後,細節已無從查考,但不難猜測當時的情景。彭㝡有一次說:我引退後要寫一部小說。另一次說:我引退後要蓋一座迷宮。人們都以為是兩件事;誰都沒有想到書和迷宮是一件東西。明虛齋固然建在一個可以說是相當錯綜的花園的中央;這一事實使人們聯想起一座實實在在的迷宮。彭㝡死了;在他廣闊的地產中間,誰都沒有找到迷宮。兩個情況使我直截了當地解決了這個問題。一是關於彭㝡打算蓋一座絕對無邊無際的迷宮的奇怪的傳說。二是我找到的一封信的片斷。」
亞伯特站起來。他打開那個已經泛黑的金色櫃子,背朝著我有幾秒鐘之久。他轉身時手裡拿著一張有方格的薄紙,原先的大紅已經退成粉紅色。彭㝡一手好字名不虛傳。我熱切然而不甚了了地看著我一個先輩用蠅頭小楷寫的字:我將小徑分岔的花園留諸若干後世(並非所有後世)。我默默把那張紙還給亞伯特。他接著說:「在發現這封信之前,我曾自問:在什麼情況下一部書才能成為無限。我認為只有一種情況,那就是迴圈不已、周而復始。書的最後一頁要和第一頁雷同,才有可能沒完沒了地連續下去。我還想起一千零一夜正中間的那一夜,山魯佐德王后(由於抄寫員神秘的疏忽)開始一字不差地敘說一千零一夜的故事,這一來有可能又回到她講述的那一夜,從而變得無休無止。我又想到口頭文學作品,父子口授,代代相傳,每一個新的說書人加上新的章回或者虔敬地修改先輩的章節。我潛心琢磨這些假設;但是同彭㝡自相矛盾的章回怎麼也對不上號。正在我困惑的時候,牛津給我寄來您見到的手稿。很自然,我注意到這句話:我將小徑分岔的花園留諸若干後世(並非所有後世)。我幾乎當場就恍然大悟;小徑分岔的花園就是那部雜亂無章的小說;若干後世(並非所有後世)這句話向我揭示的形象是時間而非空間的分岔。我把那部作品再流覽一遍,證實了這一理論。在所有的虛構小說中,每逢一個人面臨幾個不同的選擇時,總是選擇一種可能,排除其他;在彭㝡的錯綜複雜的小說中,主人公卻選擇了所有的可能性。這一來,就產生了許多不同的後世,許多不同的時間,衍生不已,枝葉紛披。小說的矛盾就由此而起。比如說,方君有個秘密;一個陌生人找上門來;方君決心殺掉他。很自然,有幾個可能的結局:方君可能殺死不速之客,可能被他殺死,兩人可能都安然無恙,也可能都死,等等。在彭㝡的作品裡,各種結局都有;每一種結局是另一些分岔的起點。有時候,迷宮的小徑匯合了:比如說,您來到這裡,但是某一個可能的過去,您是我的敵人,在另一個過去的時期,您又是我的朋友。如果您能忍受我糟糕透頂的發音,咱們不妨念幾頁。」
在明快的燈光下,他的臉龐無疑是一張老人的臉,但有某種堅定不移的、甚至是不朽的神情。他緩慢而精確地朗讀同一章的兩種寫法。其一,一支軍隊翻越荒山投入戰鬥;困苦萬狀的山地行軍使他們不惜生命,因而輕而易舉地打了勝仗;其二,同一支軍隊穿過一座正在歡宴的宮殿,興高采烈的戰鬥像是宴會的繼續,他們也奪得了勝利。我帶著崇敬的心情聽著這些古老的故事,更使我驚異的是想出故事的人是我的祖先,為我把故事恢復原狀的是一個遙遠帝國的人,時間在一場孤注一擲的冒險過程之中,地點是一個西方島國。我還記得最後的語句,像神秘的戒律一樣在每種寫法中加以重複:英雄們就這樣戰鬥,可敬的心胸無畏無懼,手中的銅劍凌厲無比,只求殺死對手或者沙場捐軀。
從那一刻開始,我覺得周圍和我身體深處有一種看不見的、不可觸摸的躁動。不是那些分道揚鑣的、並行不悖的、最終匯合的軍隊的躁動,而是一種更難掌握、更隱秘的、已由那些軍隊預先展示的激動。斯蒂芬·亞伯特接著說:「我不信您顯赫的祖先會徒勞無益地玩弄不同的寫法。我認為他不可能把十三年光陰用於無休無止的修辭實驗。在您的國家,小說是次要的文學體裁;那時候被認為不登大雅。彭㝡是個天才的小說家,但也是一個文學家,他絕不會認為自己只是個寫小說的。和他同時代的人公認他對玄學和神秘主義的偏愛,他的一生也充分證實了這一點。哲學探討佔據他小說的許多篇幅。我知道,深不可測的時間問題是他最關心、最專注的問題。可是《花園》手稿中唯獨沒有出現這個問題。甚至連『時間』這個詞都沒有用過。您對這種故意迴避怎麼解釋呢?」
我提出幾種看法;都不足以解答。我們爭論不休;斯蒂芬·亞伯特最後說:「設一個謎底是『棋』的謎語時,謎面唯一不准用的字是什麼?」我想一會兒後說:「『棋』字。」
「一點不錯,」亞伯特說。「小徑分岔的花園是一個龐大的謎語,或者是寓言故事,謎底是時間;這一隱秘的原因不允許手稿中出現『時間』這個詞。自始至終刪掉一個詞,採用笨拙的隱喻、明顯的迂迴,也許是挑明謎語的最好辦法。彭㝡在他孜孜不倦創作的小說裡,每有轉折就用迂迴的手法。我核對了幾百頁手稿,勘正了抄寫員的疏漏錯誤,猜出雜亂的用意,恢復、或者我認為恢復了原來的順序,翻譯了整個作品;但從未發現有什麼地方用過『時間』這個詞。顯而易見,小徑分岔的花園是彭㝡心目中宇宙的不完整然而絕非虛假的形象。您的祖先和牛頓、叔本華不同的地方是他認為時間沒有同一性和絕對性。他認為時間有無數系列,背離的、匯合的和平行的時間織成一張不斷增長、錯綜複雜的網。由互相靠攏、分歧、交錯,或者永遠互不干擾的時間織成的網路包含了所有的可能性。在大部分時間裡,我們並不存在;在某些時間,有你而沒有我;在另一些時間,有我而沒有你;再有一些時間,你我都存在。目前這個時刻,偶然的機會使您光臨舍間;在另一個時刻,您穿過花園,發現我已死去;再在另一個時刻,我說著目前所說的話,不過我是個錯誤,是個幽靈。」
「在所有的時刻,」我微微一震說,「我始終感謝並且欽佩你重新創造了彭㝡的花園。」
「不可能在所有的時刻,」他一笑說。「因為時間永遠分岔,通向無數的將來。在將來的某個時刻,我可以成為您的敵人。」
我又感到剛才說過的躁動。我覺得房屋四周潮濕的花園充斥著無數看不見的人。那些人是亞伯特和我,隱蔽在時間的其他維度之中,忙忙碌碌,形形色色。我再抬起眼睛時,那層夢魘似的薄霧消散了。黃黑二色的花園裡只有一個人,但是那個人像塑像似的強大,在小徑上走來,他就是理查·馬登上尉。
「將來已經是眼前的事實,」我說。「不過我是您的朋友。我能再看看那封信嗎?」
亞伯特站起身。他身材高大,打開了那個高高櫃子的抽屜;有幾秒鐘工夫,他背朝著我。我已經握好手槍。我特別小心地扣下扳機:亞伯特當即倒了下去,哼都沒有哼一聲。我肯定他是立刻喪命的,是猝死。
其餘的事情微不足道,仿佛一場夢。馬登闖了進來,逮捕了我。我被判絞刑。我很糟糕地取得了勝利:我把那個應該攻擊的城市的保密名字通知了柏林。昨天他們進行轟炸;我是在報上看到的。報上還有一條消息說著名漢學家斯蒂芬·亞伯特被一個名叫余准的陌生人暗殺身死,暗殺動機不明,給英國出了一個謎。柏林的頭頭破了這個謎。他知道在戰火紛飛的時候我難以通報那個叫亞伯特的城市的名稱,除了殺掉一個叫那名字的人之外,找不出別的辦法。他不知道(誰都不可能知道)我的無限悔恨和厭倦。
無限迴圈寫法 在 Taipei Ethereum Meetup Facebook 的最佳貼文
📜 [專欄新文章] 類 Python 的合約語言 Vyper 開發入門:與 Solidity 差異、用 Truffle 部署、ERC20 賣幣合約實做
✍️ 田少谷 Shao
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
有鑒於個人近期關注的 Uniswap 及 Curve 皆用 Vyper 實作,索性瀏覽了官方文件並嘗試一些開發工具,希望此文能減少一些讀者初嘗 Vyper 會遇到的麻煩!
Vyper and Solidity
Outline
一. Vyper 極簡介二. 與 Solidity 語法差異三. 開發、開發環境設置 1. 語法高亮 2. 本地 Vyper compiler 安裝 3. 使用 Truffle 操作 ERC20 - 安裝 Truffle - 發幣 - 寫個簡易賣幣合約四. 已知 Remix 問題 五. 結語
一. Vyper 極簡介
Vyper 是除 Solidity 外,以太坊上的另一智能合約 (Smart contract) 語言。其語法和 Python 相近,但畢竟也是寫合約的語言,邏輯差異不大,所以若熟悉 Solidity 應該不難理解用 Vyper 寫出的合約!
Vyper 主要被設計和 Solidity 的區別是安全性及可讀性,這部分會在下一段落及後方的實作中舉例說明。
二. 與 Solidity 語法差異
Vyper 與 Solidity 的差異有許多,在本段只就個人認為感受較深的三點進行說明,其他差異只進行翻譯,有興趣的讀者可以到官方文件詳細了解:https://vyper.readthedocs.io/en/latest/index.html
1. 沒有 modifier
Solidity 常見的 onlyOwner() modifier; 由於 gist 沒有 Solidity 的語法高亮,故截圖
在 Vyper 中單純用 assert 及 assert_modifiable 來進行條件檢查,兩者差別為若要檢查函數執行後的返還值,要用後者,如下圖:
Vyper 寫法
2. 沒有 Class inheritance 繼承
繼承是物件導向程式設計 (OOP) 的核心概念,但各種繼承關係有時候確實很複雜。Vyper 沒有繼承,這無疑大幅地增加了程式可讀性及安全性,以及降低審計程式碼的難度。在此提供一個例子供不熟悉 OOP 複雜之處的讀者有個概念:
source: https://consensys.github.io/smart-contract-best-practices/recommendations/#multiple-inheritance-caution
在上例中,contract A 的 fee 值 (因繼承自 contract B 和 C,故有 fee 一值) 是 5、a 值也是 5 (因繼承自 contract Final,故有 a 一值)。原因是 A 先繼承 B 再繼承 C,因此 contract A 中的 setFee() 是使用了 contract C 的 setFee(),而 a 值是由於 C(5),這代表 contract C 的 constructor (舊版本中即 function C(),函式名稱同 contract 名稱) 被傳入的值為 5。
稍微延伸一下以上概念,將 contract A 改成:contract A is C, B。如此一來,a 值還有 fee 值都會是 3,因為這次 A 先繼承 C 再繼承 B,因此最終吃到的值是 contract B 的。
以上就是 OOP 繼承的複雜之處的簡單範例說明,應該能稍微感受到爲什麼除去繼承後會大幅提高可讀性及安全性,畢竟即使是熟悉 OOP 的人有時頭腦一混亂也會開始懷疑自己寫的程式碼繼承結構是否正確 …
3. 沒有 dynamic array 動態陣列
這應該是目前 Vyper 設計中爭議最大的部分。沒有動態陣列代表在宣告陣列時需要宣告其長度,也就是說 Solidity 中的寫法 uint[], bool[] 等等,這些是不會出現在 Vyper 的。在 Vyper 中只能出現諸如:
# Vyper 的變數宣告方式為 變數名稱: 存取範圍(變數型態(若為陣列給長度))
values: uint256[10]participants: public(address[20])
可以看到上方的 uint256 及 address 兩陣列皆需要宣告長度,不能不宣告而使其動態地配置空間。
沒有動態陣列固然可以確保執行運算的範圍、次數,但一來動態陣列真的很方便、二來在 Solidity 有此功能而 Vyper 卻沒有的情況下可能會造成麻煩,詳見此一討論串:點我。
4. 沒有 inline assembly,程式碼中不會有組合語言
5. 沒有 function overloading,函式不會因傳入的參數數目不同而結果不同
6. 沒有 operator overloading,運算符號不會有不同於預設的自定義功能
7. 沒有無限迴圈,可免於 gas limit attack
8. 十進位定點數 decimal fixed point 而非二進位 (binary) 定點數,詳見:點我
三. 開發、開發環境設置
結論先講
開發 Vyper 的最佳姿勢目前個人認為是在本地裝上 Vyper compiler、用 Truffle 部署,並在撰寫時將檔名後加上 .py 就能有 Python 的語法高亮👌
1. 語法高亮 (syntax highlighting)
有語法高亮絕對是舒服地寫程式的第一步。
Remix 有 Vyper 的語法高亮,但一來個人目前不推薦使用 Remix 來撰寫 Vyper,原因詳見下方 4. 已知 Remix 問題;二來 Remix 的語法高亮其實也沒有很清楚,因此個人推薦:在本地開發,將檔名後加上 .py 就會有 Python 的語法高亮。
2. 本地 Vyper compiler 安裝
照官方說明使用 Python 的虛擬環境 virtualenv:
source: https://vyper.readthedocs.io/en/latest/installing-vyper.html#installing-vyper
簡單兩點提醒:
如果中間那行報錯但確實已經有 Python,則可能是版本問題。依照自己電腦上的版本改成相應的即可,ex: python3.6 改成 python3
進入虛擬環境後(檔案路徑前方應有 vyper-venv 的提示),使用此指令: vyper {檔案名稱}.vy,即可編譯 .vy 檔;使用完畢後輸入 deactivate 即可退出
3. 使用 Truffle 操作 ERC20
安裝 Truffle
Truffle 雖有冗餘的 migration 但也別無他法,畢竟 Remix 目前仍不完善 :(
下載流程可以照官方文件,使用 vyper-example:
source: https://github.com/truffle-box/vyper-example-box
由於我們會接上測試網 Ropsten,因此還要下載 truffle-hdwallet-provider:
source: https://github.com/trufflesuite/truffle-hdwallet-provider
接者就可以開始使用 Vyper 寫合約了!
發幣
由於 Vyper 的官方文件中已經有許多優質範例,因此本文希望來點不一樣但大家卻又很熟悉的…以 ERC20 為例(這千篇一律的主題xD):
用 Curve 的 ERC20 程式碼為範本,發一個幣(又要發…)
寫一個簡易賣幣合約
選擇這個主題一方面畢竟 ERC20 是以太坊的最大宗應用之一,二來有興趣的讀者可以透過讀 ERC20 的程式碼來熟悉 Vyper,並在看過本文的流程後對於用 Vyper+Truffle 來操作 ERC20 有完整的概念!
好的,首先複製一份 Curve 的 ERC20 程式碼(看到就順手拿來用),並複製到 Truffle 所在路徑的 contracts 資料夾中:https://github.com/curvefi/curve-contract/blob/pool_compound/vyper/ERC20.vy
由於第一點希望著重在跑一次流程,因此不改動合約的程式碼。
將 ERC20.vy 複製到 contracts 資料夾中後,到 migrations 資料夾開啟 2_deploy_contracts.js,首先將 require() 中的參數改為 ERC20.vy 的檔名 ERC20,再來依照自己喜好決定幣的名稱、代號、小數點位數及發行總量,輸入於 deployer.deploy() 中。
接著,為了和測試網 Ropsten 互動,需要將以下程式碼寫入 truffle-config.js。
第二行的 privateKeys 是帳號的私鑰。以下實作需要兩個帳號來操作,因此請從錢包匯入兩組私鑰(並非助憶詞)。
在第 13 行中 HDWalletProvider 此函式的第三個參數代表要用第幾個帳號最為預設帳號(部署合約等),第四個函數代表總共匯入幾組帳號。而第二個參數則是需要至 Infura 申請一個 project 來得到串接 Ropsten 的連結。這兩步驟並非本文重點,因此不詳細解說步驟,Google 搜尋關鍵字應該就會找到方法!
接著,就可以輸入以下指令來將代幣發佈到 Ropsten:
truffle deploy --network ropsten
有進入虛擬環境才可以編譯 .vy 檔,若忘記就會收到如下的錯誤訊息:
記得打開虛擬環境才能編譯 .vy 檔
成功後就可以在 contract address 中看到代幣發佈的位置,加入到 Metamask 中就可以看到。本文的例子是維尼代幣 Winnie the Coin, WTC ;)
contract address 便是 ERC20 的所在
Winnie the Coin, WTC
好了,到此測試網上又多了一個測試用的垃圾廢幣。
寫個簡易賣幣合約
賣幣合約中我想要簡單有兩個功能就好:付錢買幣 、結束銷售,以下就是程式碼。買幣的部分就不寫太詳細,固定價格為 0.01 Ether 可以買 500 代幣。
簡單說明幾點:
Solidity 的 constructor() 在 Vyper 中為 Python 風的 __init__():
函式的屬性(public, private, payable 等等)放在函式上方,與 Python 的修飾器位置相同
總之寫法跟 Python 很像,次方也一樣是用兩次乘法代表:**
變數前加上 self 代表是當前合約的變數/全域變數,因此非常容易與函式中的變數/區域變數做區隔
由於已經在第一行匯入了 ERC20 那份合約,因此透過將地址傳入合約當參數,就可以呼叫在該地址的合約:ERC20(self.tokenAddress) 。並且,可以將部署的合約存成一個變數 erc20 較方便
寫完合約後一樣要更改 migrations 資料夾中的 2_deploy_contracts.js 如下,將代幣所在的地址作為參數輸入。
由於先前已經部署過一次了,因此要重置才能再部署第二次,輸入以下指令:
truffle deploy --reset --network ropsten
部署成功之後就要來試著買幣啦!輸入以下來進入 console:
truffle console --network ropsten
成功進入後應該會看到 truffle(ropsten)> 的字樣。接著,首先取得部署的兩合約,並查看是否有返回合約資訊:
# ERC20 及 SellToken 是先前在 2_deploy_contracts.js 中的變數名稱,代表被部署的合約
let instance1 = await ERC20.deployed()instance1 # 印出 instance1 的資訊
let instance2 = await SellToken.deployed()instance2 # 印出 instance2 的資訊
再來,為了讓 SellToken 可以賣幣,要先用 ERC20 的合約匯幣到 SellToken 的合約。因此,輸入以下指令:
instance1.transfer(instance2.address, 10000)
# 這裡數字只要設為 > 500 就可以
接著,我們要利用第二個帳號去買幣(第一個帳號為預設帳號,因此就是代幣擁有者)。將帳號的資訊存入變數 accounts 中,再指定送出交易的帳號是第二個帳號。由於我個人匯入私鑰的順序是將第一個帳號存在 truffle-config.js 的 privateKeys[0]、第二個帳號存在 privateKeys[1],因此第二個帳號的地址就會在 accounts[1] 的位置:
let accounts = await web3.eth.getAccounts()
instance2.buyToken({from: accounts[1], value: 10000000000000000})
# value 為 10^16 是因為在 SellToken 的 buyToken 函式中買一次要 0.01 Ether, 即為 10^16 wei
然後應該就會在自己的第二個帳號中看到匯入的幣了~
最後,由於合約中結束銷售就是一個自殺 selfdestruct 函式,因此可以呼叫看看,第一個帳戶錢包中的錢應該會增加,因為第二個帳戶有付款買幣;並且,可以到 Ropsten 上瀏覽,應該能看到相關提示:
中間 contract 的右上角有 Self Destruct 的樣式
四. 已知 Remix 問題
Remix 目前有兩個版本,只有新版有 Vyper 的編譯器。在此整理目前遇到的問題,如果有人也遇到可以對照一下本處,可以省去很多自我懷疑xD
不會報錯
Remix 的編譯結果有時會是錯的、和本地端編譯出來的結果不同
舉上方的 SellToken 合約為例,將其複製到 Remix 中使用左邊的 Remote Compiler 有錯,但又不報錯 q_q (ERC20 的合約有在同檔案目錄)
左方有紅色三角形,代表編譯失敗,但沒有報錯訊息可以看…
getter function 竟然要花錢
用 Solidity 寫的合約,查詢 public 變數的值應該是不用消耗 gas 的,但不知何故查詢 Vyper 寫的合約的 public 變數卻要消耗 gas,如下圖…
可以看到中下方有 22026 gas 的消耗
Local compiler 無法使用
圖中的 Local Compiler 此選項,個人雖照官方文件執行 vyper-serve 但卻失敗,因此若有讀者成功希望能留個言不吝分享!
五. 結語
Vyper 作為一個比 Solidity 更新的合約語言,在寫程式碼的方面沒什麼問題,但相關的開發工具、學習資源等都遠不及 Solidity。
Vyper 主打的兩個特色:可讀性的部分相信看完上面的讀者應該已經有些感覺;安全性…小白如作者我倒是沒有感受到顯著的不同。況且 Solidity 已經發展許久,很多錯誤的寫法、知名的安全漏洞大家應該也很熟悉了,還有 Openzeppelin 提供安全合約寫法的範本,因此有待以後高人解說安全性是否真的是 Vyper 較好。
有興趣者可以查看 Vyper 的安全報告:點我,大意是目前 Vyper 的編譯器仍有許多問題待改進! (感謝 Chih-Cheng Liang 的提供)
本文對 Vyper 的介紹及其與 Solidity 的差異只講了個大概,欲知更詳細的介紹還是要麻煩讀者前往官方文件了:https://vyper.readthedocs.io/en/latest/index.html
最後,如果本文有任何錯誤,請不吝提出,我會盡快做修正;而如果我的文章有幫助到你,可以看看我的其他文章,歡迎一起交流 :)
田少谷 Shao - Medium
類 Python 的合約語言 Vyper 開發入門:與 Solidity 差異、用 Truffle 部署、ERC20 賣幣合約實做 was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌