{"content":{"title":"金融特化，现金交易，浅析新公链Aptos与Move语言","body":"## 前言\r\n[Aptos](https:\/\/learnblockchain.cn\/article\/4473) 的 AIT3 注册快结束了，Alvan搞完节点也终于空出时间来整理一个关于Aptos和Move的分享，这篇文章会讲一下Aptos和Move的优势和特性，以及觉得它们中很有意思的地方。\r\n\r\n## Aptos的前世今生\r\n\r\n2019年有一个Libra项目后来改名diem，运营不善后diem项目组秽土转生出了分裂成了两个项目，aptos和sui，他们从diem继承的共同遗产就是move语言。\r\n\r\n这两个是一个专注于高性能高安全,不兼容evm的layer1，pos机制。一个承袭了老项目多一点，一个自由发挥多一点。\r\naptos目前还处于测试阶段，根据项目方透露今年万圣节之前可以主网上线。\r\n\r\n目前已经有四十多个项目已经宣布加入aptos生态，钱包和nft市场以及开发者工具之类项目量大管饱，但是目前真正完成并上线测试网并且开源的很少。一方面是由于现在版本迭代太快，不太稳定，另一方面就是很多solidity的团队需要时间去学习使用Move投入生产。\r\n\r\n## Aptos有哪些特性呢\r\n\r\nAptos其实写的很明白，它和其他传统区块链的最大区别就是高效(存疑)和安全。\r\n\r\n第一点是使用了流水线模式，增强了并行程度，根据开发团队声称tps可以到十万，但是测试网明显没有压榨出最大的效率，究竟能达到什么程度持保留意见，毕竟以太坊升级之后的理想状态也是超过十万的。\r\n\r\n第二点，我觉得这是它与以太坊差异最大的的设计，就像下图所示，\r\n\r\n\r\n![aptos分享稿.004.png](https:\/\/img.learnblockchain.cn\/attachments\/2022\/08\/931nsy3I6307995977fb3.png)\r\n\r\n以太坊的storage，是合约账户拥有的一块存储区，里边每个合约存储着不同人的账户信息，\r\naptos里的信息被称为resource资源，存在每一个外部账户的存储区里。\r\n这有什么区别呢，要实现一个A token的记账，以太坊的做法是在A token合约里存一个map，key是账户地址，value是账户余额，要空投币就是在合约内部去找key改value。而aptos的做法是在A token合约里声明一个叫coin的resoure，然后把resource发送给用户，这样A token就到了用户的存储区。如果对应现实世界的话，一种是记账模式，一种是现金模式。这个之后我们再讲。\r\n\r\n\r\n\r\n## Move语言又是何方神圣\r\n\r\n讲完aptos链本身，现在我们开始看看时薪1200刀的Move语言，它并不是一个新玩意了,Libra(Diem)时期就已经投产的语言，也被Sui和aptos继承了，现在最全的Move文档依然在Diem的网站下边挂着。\r\n\r\n它脱胎于rust,很多设计和概念都有相似。Move声称对solidity的优势是安全高效，实际上rust对Cpp和GC语言的优势也分别是安全和高效,也就会围绕着这两点介绍move的特性。可能会涉及到一些概念没接触过也不要紧，我会简单说明一下，并不会耽误理解。\r\n\r\n首先我们从安全说起，在我们编程的时候肯定会遇到一个重要的问题，如果它不是你编程时遇到的问题，那一定也是面试的时候一定会遇到问题，就是申请和回收内存。\r\n\r\nC++在处理变量或者指针时，需要全程手动，所以C++程序员的心智负担很重，他也不知道哪里出现一个悬垂指针哪里有一个内存泄漏，软件的可靠性就会极度依赖开发者的水平。\r\n\r\nJava和Go使用了GC机制，在运行的时候有一个GC程序监控记录引用情况，在变量失效时清除，这让程序员的日子好过不少，但是意味着一定会造成额外的性能开销。\r\n\r\n那么有没有既不增加开销还能自动管理内存的方案呢？答案是肯定的，Rust或者说Move的 生命周期+所有权 是一个用语法规范内存操作的方案，可以把内存安全检测放在编译期解决。\r\n\r\n简单来说像这段rust代码一样\r\n\r\n```rust\r\nfn timelife_and_ownership()->String{\r\n    let x :String = \"hello aptos\".to_string();\/\/x的生命周期开始，开辟内存\r\n    {\r\n        let y :String = \"hello move\".to_string();\/\/y的生命周期开始，开辟内存\r\n    }\/\/y的生命周期结束，回收内存\r\n    \/\/println!(\"{}\",y); error:cannot find value `y` in this scope\r\n    x\r\n}\/\/x的生命周期结束，回收内存\r\n```\r\n\r\n每一个变量有一个作用域，也就是一个大括号代码块。\r\n当程序运行出代码块时，就把这块里声明的内存清理掉了。这样在编译期就能解决指针乱飞的问题，也不会造成额外开销，同时是把它变得高效的根本原因。\r\n\r\n所有权又是什么呢？\r\n在rust里，每一个变量都有一个所有者，其他变量想要使用它需要复制或者借用\/引用。而move把这个思路用在了处理资源上，资源其实就是我们存储区上的数据，比如我们刚才说的A token。\r\n\r\n每一个资源有四个可选ability：Copy，Drop，Store，Key。Copy表示可以被复制，Drop的意思是可以销毁该资源，store表示可以存进全局也就是区块链，key表示该资源可以被检索。\r\n\r\n他们之间也有一定耦合，比如一个拥有key的结构体，它里边的资源肯定是store的，这是不言自明的。使用的时候就是在声明变量的后边加上\"has copy\"，‘has key’。\r\n\r\n```rust\r\nstruct MessageHolder has key {\r\n        message: string::String,\r\n        message_change_events: event::EventHandle<MessageChangeEvent>,\r\n    }\r\n\r\n    struct MessageChangeEvent has drop, store {\r\n        from_message: string::String,\r\n        to_message: string::String,\r\n    }\r\n```\r\n\r\n我们完全可以按照写程序的思维去理解它的顶层设计。上文提到了所有权和借用，当一个借用是不可变借用的时候它当然是安全的，如果是可变借用，那必须只有一个，不然这段代码就很难安全(最重要的是不知道什么时候释放)，尤其是在异步程序里。\r\n\r\n在move里可变引用和不可变引用分别是borrow_global 和 borrow_global_mut\r\n\r\naptos官方的coin设计里是这样的\r\n\r\n\r\n![aptos分享稿.011.png](https:\/\/img.learnblockchain.cn\/attachments\/2022\/08\/KB7cyDKZ630799698c2ad.png)\r\n\r\n\r\n\r\n某人持有的资金是存在自己存储区下的一个resource，我们暂且把它称为Coin，实现持有就是把amount放进一个没有copy和drop的资源Coin里，转账的时候是切割\/合并Coin资源来实现。这样资源相当于被实体化了，在同一时间里想要有可变的Coin资源只能被一个所有者修改，避免了很多安全问题。跟以太坊的账户转账相比一个是账本模式一种现金模式。感觉有点UTXO的味道。\r\n\r\n标准库代码：\r\n\r\n这是标准coin的transfer函数，可以看到分为两步，发送方withdraw，接收方deposit。\r\n\r\n```rust\r\n\/\/\/ Transfers `amount` of coins `CoinType` from `from` to `to`.\r\n    public entry fun transfer<CoinType>(\r\n        from: &signer,\r\n        to: address,\r\n        amount: u64,\r\n    ) acquires CoinStore {\r\n        let coin = withdraw<CoinType>(from, amount);\r\n        deposit(to, coin);\r\n    }\r\n```\r\n\r\nwithdraw与extract函数，请看注释\r\n\r\n```rust\r\n\/\/\/ Withdraw specifed `amount` of coin `CoinType` from the signing account.\r\n    public fun withdraw<CoinType>(\r\n        account: &signer,\r\n        amount: u64,\r\n    ): Coin<CoinType> acquires CoinStore {\r\n        let account_addr = signer::address_of(account);\r\n        assert!(\r\n            is_account_registered<CoinType>(account_addr),\r\n            error::not_found(ECOIN_STORE_NOT_PUBLISHED),\r\n        );\r\n        \/\/先用变量coin_store拿到account_addr中coin资源的所有权\r\n        let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);\r\n\r\n        event::emit_event<WithdrawEvent>(\r\n            &mut coin_store.withdraw_events,\r\n            WithdrawEvent { amount },\r\n        );\r\n\t\t\t\t\/\/分割coin，并返回一个coin\r\n        extract(&mut coin_store.coin, amount)\r\n    }\r\n    public fun extract<CoinType>(coin: &mut Coin<CoinType>, amount: u64): Coin<CoinType> {\r\n        assert!(coin.value >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));\r\n        \/\/修改传入coin的值\r\n        coin.value = coin.value - amount;\r\n        \/\/创建一个新coin并返回\r\n        Coin { value: amount }\r\n    }\r\n```\r\n\r\n接收方的deposit就是上述操作的逆操作而已。我们可以在上述代码中看到所有权的流动。\r\n\r\n我们再往下想从这个方面来讲，aptos里就没有了token和CryptoCurrency的高低贵贱之分，在以往以太坊里边eth和erc20本质上是不同的，在写代码时往往需要两种不同的分支处理，而在aptos里所有的coin理论上都是同等地位，因为他们都是resource，区别只是aptoscoin是0x1标准库合约发的币，而A token是0x114514合约发的币而已。\r\n\r\n从存储区的角度来说，一切信息存在用户处，合约反而成了一个纯粹的lib，这也使得合约的逻辑和存储天然分离，更有助于智能合约的去中心化。\r\n\r\n## 结语\r\n\r\nAptos的业务模型和Move合约的设计远远比高性能更让人拍案叫绝，在学习的时候忍不住把它们和rust联系起来(因为真的很像)，一点粗浅理解，还望诸君包涵！"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/9803","address":null},"history":null,"timestamp":1661506352,"version":1}