Move是一門強類型的位元組碼語言,基於棧式虛擬機設計,受Linear Logic類型系統的啟發,將資源(數字資產)作為第一等公民,藉助所有權轉移和最多一次可變引用等規則保證資產安全。名字Move的來歷也就自然而然可以理解了。

三個大特點

1. first-class resouces. 用資源表示數字資產是一等公民,然後通過語法藉助borrow check等思路在合約編譯期間保證資產的不可雙花,不可消失,必有歸屬性;

2. flexibility 通過交易腳本來定義單個交易裡面的一次性(不可重用)合約邏輯,交易腳本定義了合約的main函數,可以插入多個module實現複雜邏輯和可重用邏輯。合約的結構原語modules/resources/procedure,類比與面向對象的class/object/method,同時通過module做合約資源的聲明周期管理,極大的提升了合約可復用性和安全性。

3. 強類型的位元組碼,在位元組碼層面的靜態代碼檢查保證運行時的大多數錯誤都在編譯期間被發現。Move沒有動態指派(dynamic dispath),函數調用完全是在編譯期間確定,沒有什麼類似c++的RTTI的機制,這樣驗證工具可以快速構建調用圖驗證,borrow check保證資源任何時候只有一個muttable引用,這樣寫操作就可以被嚴格檢驗。保證足夠安全。

Move實例介紹

先舉個Move寫的合約例子:

public main(payee: address, amount: u64) {
let coin: 0x0.Currency.Coin = 0x0.Currency.withdraw_from_sender(copy(amount));
0x0.Currency.deposit(copy(payee), move(coin));
}

合約接受2個參數轉賬接收人payee和轉賬金額amount。0x0表示賬戶地址,Currency表示module, 0x0.Currency.Coin表示資源類型,0x0.Currency.withdraw_from_sender這個procedure(過程)返回一個0x0.Currency.Coin類型的值coin,然後通過deposit這個過程,將coin轉移到payee的地址下面去。 藉助於linear logic的轉移原則, 限制資源(數字資產的)的不可重用(只能轉移一次),不可複製(不能copy資源)以及不可丟失(轉移之後必有地址接受)。

Move通過一個地址到賬戶的map來表示global state。如下:

包含3個賬戶的global state的示意圖

在一個賬戶裡面,可以包含多個module或者resouces,但是不能同名,雖然不能同名,但是可以在一個賬戶裡面,同時持有2個地址下面相同類型名的實例。例如:

resource TwoCoins { c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin })

例如聲明一個名叫Coin資源如下:

module Currency {
resource Coin { value: u64 }
// ...
}

默認情況下,Coin是private的,外部需要通過module暴露的其他介面(實際操作的語義最終也只能是move)才能被訪問,並且許可權完全由module的創建者控制。

deposit的實現如下:

public deposit(payee: address, to_deposit: Coin) {
let to_deposit_value: u64 = Unpack<Coin>(move(to_deposit));
let coin_ref: &mut Coin = BorrowGlobal<Coin>(move(payee));
let coin_value_ref: &mut u64 = &mut move(coin_ref).value;
let coin_value: u64 = *move(coin_value_ref);
*move(coin_value_ref) = move(coin_value) + move(to_deposit_value);
}

詳細解釋為:

  1. move(to_deposit)將銷毀sender的to_deposit這個資源,並且將其存儲在一個局部變數to_deposit_value上;
  2. 在接受人的空間下面創建一個引用coin_ref,然後創建一個存儲coin_ref的value的可變引用coin_value_ref,
  3. 取出coin_value_ref的value,將其跟to_deposit_value相加,將結果存回到coin_value_ref。

其中注意的是,Unpack<T>是Move內嵌的用戶銷毀類型為T的變數,然後返回T的具體欄位的值的procedure。BorrowGlobal返回一個Coin的資源的引用。

然後在看下withdraw_from_sender的實現:

public withdraw_from_sender(amount: u64): Coin {
let transaction_sender_address: address = GetTxnSenderAddress();
let coin_ref: &mut Coin = BorrowGlobal<Coin>(move(transaction_sender_address));
let coin_value_ref: &mut u64 = &mut move(coin_ref).value;
let coin_value: u64 = *move(coin_value_ref);
RejectUnless(copy(coin_value) >= copy(amount));
*move(coin_value_ref) = move(coin_value) - copy(amount);
let new_coin: Coin = Pack<Coin>(move(amount));
return move(new_coin);
}

幾乎是deposit的逆過程,流程如下:

  1. 獲得轉賬發起方的地址,然後獲得其Coin資源的實際的value, code_value;
  2. 從coin_value減去amount個幣;
  3. 然後條用Pack創建一個新的Coin資源並且傳回去。

綜上可以看到,BorrowGlobal可以驗證account是否有許可權獲得一個資源的引用(意味著馬上要進行修改),然後通過Unpack實際的資源的value然後銷毀資源,或者Pack來新建新的資源。然後上面各種語法&mut之類的,建議大家看看rust就好懂了。

下一節介紹Move語言的語法。

推薦閱讀:

相关文章