ton
内存布局-Cell
Ton链所有的合约,数据等信息都存在一种称为 Cell 的结构中,每个 Cell 只能存放 1023位大小的数据,但是每个 Cell 同时可以引用(单向)4个其它的 Cell,这意味着它可以无限扩展,支持任意大小的合约。
不过在设计数据的时候要注意,因为根据Ton的设计,每次我们必需通过引用从一个 Cell 跳到另一个 Cell ,从而进行一个 Cell 的解包;所以如果我们的系统有100万个用户存放在列表中,这样系统的开销会变得非常大,因为每一笔交易都会通过此合约。
解决方法是我们可以使用通证来表示这些用户的参与,并同时避免将用户的列表存放在合约当中。
信息
一旦合约接收到信息,将会有两个独立的程序分别处理外部信息和内部信息。
- 外部信息:是一串不知从何而来的数据,它本身没有附带任何以 Ton 币形式存在的货币,可以包含任何合约作者想携带的任何信息。
- 内部信息:是由合约发送给其它合约的信息,内部信息可以携带金额,这些数据将会通过智能合约发送的地址进行安全验证。
交易
交易就是智能合约状态变化的统合体,因此信息不是交易,它只是交易的输入。
交易的作用是改变状态,并作为 TVM 输出的结果,为合约创建一个新的状态,和输出操作列表,为了得出这个结果,交易有五个阶段。
- 存储:区块链会从合约里收取其在期间内所欠的所有租金。在交易开始之前,区块链就会开始从合约的余额里收取必要的租金金额,一旦合约上的钱用完了,剩下的合约不会执行,合约会被冻结
- 贷记:当存储阶段结束后。在贷记阶段,收到的信息将会记入合约的贷方,所以这是内部信息,因为外部信息不包含代币。
- 计算:此时程序才开始执行。TVM 会执行程序并验证每个操作,同时记录 gas 的使用情况。开发者可以进行限制,比如我们可以限制 gas 从转到我们余额的数量中扣除,这意味着这部分开销是转账者来支付。总之我们要确保 gas 不会花完。TVM 会在执行代码并收取 gas 的过程中会产生输出操作,如果我们的钱没用完,也没有违反 TVM 的规则,那么 TVM 就会执行完成,我们会得到一个输出列表,然后进入下一个阶段。
- 行动:此阶段最关注的就是智能合约的新状态,因为我们的合约可能会在执行过程中或执行完后为自己创建新的状态和新的存储空间,这些都会在合约执行完后被记录下来,但在列表中和这个阶段中还有其它操作,这一些操作是发出的信息,是合约向其它合约传达的信息,比如钱包合约肯定会有输出操作,向其它合约发送资金和指令。又例如我们想把钱转到另一个钱包,那么就会有一条输出信息,上边写着:请把这个数量的代币发送到时那个目标地址。第四个阶段的目标就是处理这些操作,同时记录新的状态并发送信息。
- 反弹:如果合约执行失败而接收到的信息有一个标记显示“我是反弹回来的信息”,就会发生这种情况。意思是当合约执行失败,而收到的信息中还有剩余的钱,那么合约就会创建一条发回给发件人的信息,将钱退回。这是一个安全功能,可以保证在合约出错或者其它故障的情况下把钱退回。我们开发的时候可以用这个功能,当某些条件无法触发的时候就用这个功能退回收到的信息,并退钱。
如何验证合约中的信息
- 签名验证: Ton 中的任何事件都是从外部信息开始,外部信息没有经过验证,它本身只是一堆数据,并以信息的形式到达某一个智能合约。大部分情况下,外部数据会进入属于每个用户的钱包合约当中。
其实钱包只做了3件事。1是存放用户的 Ton 余额。2是储存序列号,储存序列号可以避免并防止信息重复播放。3是储存公钥,每当外部信息进来时,它会读取64字节的数据,也就是信息其余部分的签名,用公钥验证签名,然后将信息的其余部分视为指令,发送其它信息给区块链内部的其它合约。 - 信息发送者身份验证:Ton 中所有的内部信息,都是由信息发送方标识的,而信息发送方也有 Ton 协议保证其正确性和安全性。所以每个合约收到内部信息时,都能确定是从哪个合约发来的。这比检查签名便宜得多,也强大得多。
- DNA验证:建立在信息发送者之上的,通证生态中的地址,不只是合约的唯一标识符,也是合约代码和数据的哈希值,或者说是初始代码和数据,所以当数据发生变化时它是不会变的,因此我们可以检查信息发送者来验证另一端是哪种代码。这种方式不是检查合约的地址,而是检查它们所拥有的任何数据是否与当前合约相同。这意味着可以相信另一信息发送方正确执行了自己协议的部分。
- 不验证,比如我们有一个必须能抵御审查的系统,这是一个分散的赌注池,第隔一段时间就要进行支付,而必需有人触发信息才能开始支付。如果这个信息是通过公钥或者合约验证的,那么这表示只有一个操作员可以控制支付的访问权限。从审查的角度来看,这是很不好的。比如信息发送者需要支付 gas,但我们不关心谁支付的。对于真正去中心化的应用程序,这是一种重要的模式。
交易成本
- gas费用:执行合约代码中的成本。如果合约执行失败,扣除的 gas 并不会返回,所以最好的策略就是发送信息的人来支付
- 租金: 合约在单位时间(秒)内存储单个数据的成本,是按每秒和每比特来计算的,因为两次合约交易之间状态并不会改变,所以租金的扣除是在下一次执行合约的时候用期间间隔的时间*占用的空间计算扣除。所以如果我们的钱包很久没有执行交易,那么在下次执行交易的时候会发现交易费用明显上升也是这个原因。(rent = current time - previous transaction time) * size of storage
- 导入和创建传出信息的费用:每一条信息和字节费用不一样。我们可以设计合约由数据发送者支付 gas,并把这个值设置高一点,没有用完的部分可以返回给发送方。