作者:Figo, IOSG Ventures
命令式與聲明式編程:範式的轉變
在區塊鏈世界中,編程範式定義了開發人員如何與技術交互以及用戶如何體驗結果。讓我們使用一個相關的類比來分析傳統命令式方法和新興聲明式方法之間的主要區別——在您最喜歡的咖啡館訂購定製咖啡。
命令式編程:遵循菜譜
想象一下,你走進一家咖啡館,向咖啡師遞上一份製作咖啡的詳細步驟清單 — “研磨 20 克咖啡豆,使用 200 毫升 90°C 的水,沖泡 4 分鐘,倒入陶瓷杯中。”你需要對整個過程進行細緻入微的管理,確保每一步都嚴格遵循,才能製作出你想要的咖啡。
這就是命令式編程在區塊鏈中的工作方式。開發人員編寫智能合約來規定流程的每個步驟,從獲取數據到執行交易。雖然這可以確保流程按預期進行,但它可能僵化且效率低下,尤其是在出現意外情況時。
聲明式編程:定義結果
現在,想象一下你走進同一家咖啡館,簡單地說:“我想要一杯中濃咖啡,不加糖,用陶瓷杯裝。”咖啡師知道你想要什麼,並可以選擇最佳的沖泡方式,無論是使用不同的沖泡方法還是根據可用的咖啡豆調整研磨尺寸。
這就是聲明式編程的精髓。您無需指定每個步驟,只需定義所需的結果,系統就會找出實現該結果的最佳方法。這種方法更加靈活,可以根據當前條件和用戶偏好進行調整。
下表重點介紹了主要區別:
為什麼區塊鏈應用程序需要意圖?
理解意圖
在傳統的區塊鏈設置中,您需要提供分步說明來實現特定的狀態更改。但是,如果您可以指定最終目標,並讓系統找出實現該目標的最佳方法,情況會怎樣?這就是區塊鏈意圖背後的想法——用戶聲明他們想要什麼,系統處理其餘的事情。
例如,用戶可能不會告訴區塊鏈“以最佳價格出售 1 ETH ”,而是表明“用 1 ETH換取至少 2500 USDT ”的意圖。然後,系統會找到滿足這一請求的最佳方式,可能跨越多個交易所或流動資金池。
命令式區塊鏈的挑戰
命令式編程的僵化常常導致幾個問題:
- 不確定的結果:用戶通常在交易確認之前不知道交易的結果,這可能會帶來壓力和風險。
- 市場效率低下:當對特定結果的需求很高時,命令式執行可能會遇到瓶頸,例如在 DeFi 交易中。
- 安全風險:更詳細的步驟意味著更多的潛在故障點,從而增加了出現漏洞和漏洞的風險。
這些問題在 DeFi 這樣的環境中尤為普遍,用戶面臨交易失敗、高滑點和 MEV(礦工可提取價值)風險等問題。
聲明式區塊鏈的興起
像 Essential 這樣的聲明式區塊鏈旨在通過關注結果而不是過程來解決這些問題。通過定義最終狀態並使用意圖來實現它,這些系統提供了幾個優點:
- 可預測的結果:用戶可以相信區塊鏈將提供所需的結果。
- 提高安全性:步驟更少意味著出錯的機會更少。
- 可擴展性:通過優化結果,聲明式區塊鏈可以處理更復雜的操作,而不需要更多的計算資源。
以意圖為中心的語言
隨著以意圖為中心的編程在區塊鏈領域越來越受歡迎, Pint (由 Essential 開發)和Juvix (由 Anoma 開發)等語言應運而生,它們可以幫助開發人員專注於結果而不是具體步驟。這些語言提供了一種區塊鏈開發的新方法,優先考慮系統的理想狀態,而不是實現該狀態的程序指令。
在本節中,我們將深入研究 Essential 聲明式區塊鏈背後的語言Pint ,探索它的運作方式以及與我們習慣的不同之處。
Pint 是一種專門為 Essential 以意圖為中心的區塊鏈設計的約束建模語言。與傳統的命令式智能合約語言不同,Pint 強調定義狀態變化的期望結果,而不指定實現這些結果的具體步驟。這種重點轉移使區塊鏈應用程序的開發和執行方式更加靈活、安全和高效。
Pint 有何與眾不同之處?
Pint 提供了一種處理區塊鏈邏輯的獨特方法,它專注於約束和謂詞,而不是傳統的執行模型。它的工作原理如下:
- 合約:在 Pint 中,合約定義了更新區塊鏈狀態的規則。與傳統合約不同,合約不指定如何執行更改;相反,合約驗證提議的狀態更改是否符合某些標準。
- 謂詞:謂詞是狀態改變有效所需滿足的條件。它們充當過濾器,確定特定狀態轉換是否可以發生。
- 約束:約束是謂詞的組成部分。這些布爾表達式的計算結果必須為 True,謂詞才能通過。例如,約束可能要求計數器正好增加 1,以確保狀態更新的一致性。
- 狀態:狀態變量表示區塊鏈上的值。與傳統的命令式語言不同,開發人員編寫一些應用於前狀態的執行來確定後狀態,而 Pint 則同時提供前狀態和後狀態作為輸入。對於給定的聲明狀態 foo,foo 指的是前狀態,而 foo' 指的是後狀態。
- 決策變量:決策變量允許求解器提供滿足謂詞約束所需的額外數據。例如,代幣合約的轉移謂詞可能需要有效簽名。
初探 Pint:簡單的反例
為了更好地理解 Pint 的工作原理,讓我們看一個簡單的例子——反向合約。
貯存 {
計數器:int,
}
謂詞增量 {
狀態計數器:int = storage::counter; // 讀取計數器的當前值
約束(計數器 == nil && 計數器' == 1)|| 計數器' == 計數器 + 1;
// 該約束確保如果未設置計數器,則從 1 開始。
// 否則,將當前值增加 1。
}
存儲塊:
- 存儲塊定義了一個將存儲在區塊鏈上的單個整數變量計數器。這是計數器當前值保存的地方。
謂詞增量:
- 謂詞 Increment 定義了增加計數器的邏輯。
- 線狀態計數器:int = storage::counter;從存儲中讀取計數器的當前值。
- 約束 (counter == nil && counter' == 1) || counter' == counter + 1; 確保如果計數器尚未初始化(即,它為 nil),則它從 1 開始。否則,計數器將增加 1。
簡單來說,這個合約規定,“如果計數器尚不存在,則從 1 開始。如果計數器存在,則將當前值加 1。”這個例子說明了 Pint 如何允許開發人員定義所需的結果(增加計數器),而無需指定每個步驟(檢查計數器、添加計數器ETC)。
從頭開始構建簡單的加密貨幣
現在來看一個更復雜的例子?讓我們一步一步地看看如何使用 Pint 從基本原理開始構建基本的加密貨幣。假設我們的任務是創建一個數字貨幣系統。我們需要什麼?
步驟 1:定義核心組件
首先,我們需要確定加密貨幣的基本要素。從最基本的層面上講,我們需要:
- 貨幣總供應量的記錄:這跟蹤了貨幣的存在數量。
- 跟蹤餘額的方法:我們需要知道每個賬戶持有多少貨幣。
在 Pint 中,我們可以使用存儲塊定義這些:
貯存 {
總供給量:int,
餘額:(b256 => int),
}
- total_supply :這個整數跟蹤貨幣的總量。
- balances :這是一個映射(或字典),將每個地址(用 b256 表示,即 256 位哈希值)與其對應的餘額關聯起來。
第 2 步:創建新貨幣
接下來,我們需要一種創建新貨幣的方法——也稱為鑄造。鑄造貨幣時,我們希望增加總供應量並更新接收者的餘額。
讓我們思考一下以下步驟:
- 我們需要明確誰將獲得新鑄造的貨幣以及他們將獲得多少。
- 然後,我們需要相應地更新總供應量和收件人的餘額。
這種邏輯在 Pint 中表現如下:
謂詞 Mint {
var接收器:b256;
變量金額:int;
狀態接收器平衡 = mut 存儲::balances[接收器];
狀態總供應 = mut 存儲::總供應;
約束總供應量'==總供應量+數量;
約束接收方餘額'==接收方餘額+金額;
}
分解如下:
- 決策變量:接收者和金額代表接收者的地址和要鑄造的貨幣數量。
- 狀態變量:我們引用 total_supply 和receiver_balance 的狀態,聲明它們是可變的(mut),這意味著它們可以被更新。
- 限制:
— 第一個約束確保總供應量按照鑄造量增加。
— 第二個約束確保接收者的餘額根據鑄造的金額進行更新。
但是,需要注意的是,此示例已簡化以便於理解。合同本身沒有內置身份驗證。這意味著任何人都可以鑄造新幣或將其從任何賬戶轉移到另一個賬戶。在現實世界中,這種缺乏安全性將是一個重大問題。在後面的部分中,我們將探討如何添加身份驗證以確保只有授權用戶才能鑄造或轉移貨幣,從而使系統更加安全。
步驟 3:用戶之間轉移貨幣
現在我們可以創建貨幣了,下一步就是讓用戶能夠互相發送貨幣。為此,我們需要:
- 檢查發送者是否有足夠的貨幣進行發送。
- 從發送者的餘額中扣除該金額。
- 將金額添加到收款人的餘額中。
這是相應的 Pint 代碼:
謂詞發送 {
var 來自:b256;
var接收器:b256;
變量金額:int;
狀態 from_balance = mut storage::balances[from];
狀態接收器平衡 = mut 存儲::balances[接收器];
約束金額 < from_balance;
約束 from_balance' == from_balance - 金額;
約束接收方餘額'==接收方餘額+金額;
}
分解如下:
- 決策變量:發件人、接收者和金額指定誰發送貨幣、誰接收貨幣以及發送金額。
- 狀態變量:from_balance 和receiver_balance 是發送者和接收者的當前餘額,是可變的。
- 限制:
— 第一個約束檢查發送者是否有足夠的餘額來支付轉賬。
— 第二個限制從發送者的餘額中扣除金額。
— 第三個約束將金額添加到收件人的餘額中。
再次強調,這裡的簡單性忽略了身份驗證,而身份驗證在實踐中對於防止未經授權的轉賬至關重要。添加這樣的安全層將涉及更復雜的謂詞,在允許交易之前驗證用戶的身份。
以下是完整的合約,結合了鑄造和轉讓功能:
貯存 {
總供給量:int,
餘額:(b256 => int),
}
謂詞 Mint {
var接收器:b256;
變量金額:int;
狀態接收器平衡 = mut 存儲::balances[接收器];
狀態總供應 = mut 存儲::總供應;
約束總供應量'==總供應量+數量;
約束接收方餘額'==接收方餘額+金額;
}
謂詞發送 {
var 來自:b256;
var接收器:b256;
變量金額:int;
狀態 from_balance = mut storage::balances[from];
狀態接收器平衡 = mut 存儲::balances[接收器];
約束金額 < from_balance;
約束 from_balance' == from_balance - 金額;
約束接收方餘額'==接收方餘額+金額;
}
該子貨幣合約為基本加密貨幣提供了基礎框架,說明了意圖中心編程如何通過關注每個操作的期望結果來簡化流程。
建立NFT合約
現在我們已經構建了一個基本的加密貨幣,讓我們探索一個更復雜、更微妙的應用程序:管理 NFT(非同質化代幣)。與每個單位都相同的同質化代幣不同,NFT 代表獨特的資產,需要更復雜的合約設計。
以下是在 Pint 中構建 NFT 系統的方法:
使用 std::lib::PredicateAddress;
使用 std::auth::@auth;
使用 std::lib::@safe_increment;
使用 std::lib::@mut_keys;
貯存 {
所有者:(int => b256),
nonce: (b256 => int),
}
接口認證{
謂詞 謂詞 {
// 授權謂詞正在輸出的地址。
// 這將授權謂詞指向該集合中的謂詞。
// 通過設置此地址,授權就不能與錯誤的謂詞一起使用。
pub var addr:{ contract:b256, addr:b256 };
}
}
謂詞 Mint {
var 令牌:int;
var 新所有者:b256;
狀態所有者 = mut storage::owners[token];
約束所有者==nil;
約束所有者'==新所有者;
}
謂詞轉移 {
// 發送金額的地址。
公共變量鍵:b256;
// 發送金額的地址。
出版變量為:b256;
// 正在轉移的令牌。
酒吧 var 令牌: int;
狀態所有者 = mut storage::owners[token];
狀態 nonce = mut storage::nonce[key];
約束所有者==鍵;
約束所有者'==至;
約束@safe_increment(nonce);
// 檢查授權謂詞。
var auth_addr:謂詞地址;
接口 AuthI = Auth(auth_addr.contract);
謂詞A = AuthI::Predicate(auth_addr.addr);
@auth(key; A::addr; auth_addr; @transfer());
}
謂詞取消 {
// 取消轉賬或者銷燬的賬戶。
公共變量鍵:b256;
狀態 nonce = mut storage::nonce[key];
// 增加 nonce,以便任何待處理的轉賬或
// 燒燬無效。
約束@safe_increment(nonce);
// 檢查授權謂詞。
var auth_addr:謂詞地址;
接口 AuthI = Auth(auth_addr.contract);
謂詞A = AuthI::Predicate(auth_addr.addr);
@auth(key; A::addr; auth_addr; @cancel());
}
宏@transfer(){/{合同:signed::ADDRESS,地址:signed::Transfer::ADDRESS}}
宏@cancel(){ { contract:signed::ADDRESS, addr:signed::Cancel::ADDRESS }}
存儲塊:
- 所有者:將令牌 ID 映射到其各自的所有者。
- nonce:跟蹤每個賬戶的 nonce,以確保交易是唯一的且不可重放。
謂詞薄荷:
- 通過為新所有者分配代幣 ID 來鑄造新的 NFT。
- 約束確保令牌 ID 之前未分配過(owner == nil),然後將其分配給 new_owner。
謂詞轉移:
- 管理 NFT 從一個所有者到另一個所有者的轉移。
- 它包括授權檢查,以確保只有當前所有者可以轉移令牌。
- @auth 宏與外部授權合約集成以驗證交易。
謂詞取消:
- 允許帳戶通過增加隨機數來取消待處理的轉賬或銷燬,這會使任何先前簽署的交易無效。
- 它還與授權系統集成,以確保只有授權用戶才能取消交易。
這份 NFT 合約展示了 Pint 如何處理更復雜的現實世界應用程序,同時集成授權和交易完整性檢查等基本安全功能。它展示了以意圖為中心的編程如何通過專注於系統應實現的目標,將底層流程管理留給語言及其求解器,從而簡化高級區塊鏈應用程序的開發。
為什麼開發人員應該關心
以 Pint 為例的以意圖為中心的編程代表了區塊鏈開發的一種新思維方式。通過抽象執行細節並專注於結果,開發人員可以創建更強大、更安全、更靈活的應用程序。這種範式轉變降低了出錯的可能性,簡化了編碼過程,併為更具創新性和更高效的解決方案打開了大門。
隨著區塊鏈技術的不斷發展,掌握像 Pint 這樣的語言對於想要保持領先地位並構建下一代分散式應用程序的開發人員來說至關重要。
結束語
以意圖為中心的編程不僅僅是一種編寫智能合約的新方法,它還是對區塊鏈應用程序的設計和執行方式的根本性重新思考。通過關注結果而不是指令,開發人員可以創建更安全、可擴展且用戶友好的應用程序,這些應用程序更適合現代區塊鏈的動態和複雜環境。
隨著以意圖為中心的編程變得越來越普遍,我們可能會看到它的原理從區塊鏈擴展到軟件開發的其他領域,例如物聯網、人工智能,甚至傳統的網絡應用程序。通過關注結果而不是程序,各行各業的開發人員可以解鎖新的效率、安全性和靈活性水平。軟件的未來很可能由這種範式轉變來定義。
要了解有關 Pint 的更多信息並深入瞭解以意圖為中心的編程,請查看Pint 文檔以獲取詳細指南和示例。對於那些有興趣在 Essential 上部署的人,請訪問Essential 的官方文檔,開始構建和部署您自己的以意圖為中心的應用程序。
初探以意圖為中心的編程 ft. Pint最初發表在 Medium 上的IOSG Ventures中,人們通過突出顯示和回應這個故事來繼續討論。