求教智能合约的设计

需求:
合约1、在2019年5月20日,如果账户 A 的余额大于100,则须支付100给账户 B;
合约2、如果合约1被成功执行,则账户 B 须支付60给账户 C;

预期达到效果:
A、B、C 初始余额都是0,在2019年5月20日那天,账户 A 得到一笔300块的收入,这时候合约1被触发执行,紧接着合约2也被触发执行,两个合约都被执行完以后,A 余额200,B 余额40,C 余额60

请问上述需求用智能合约能实现吗(尤其是合约2的条件是合约1被成功执行,不知道这种需求合理不)?具体该如何设计这样两个合约呢?

按照你的预期,应该是:

  1. A合约收到一笔大于 100 的收入,就转 100 给帐户 B 合约;
  2. 合约 B 收到一笔来自合约 A 的转帐,则转 60 给帐户 C;

对吗?

1、不完全对,还有时间条件需要满足,如果时间没到,A 收到10000也不该转给 B;
2、如果『收到一笔来自合约1的转账』代表合约1成功执行了,对的;

哦, 这个时间比较难精确的控制。
我想你可以通过出块速度,大概能计算出当时的块高,以块高做为控制条件,把这个合约 A 放到 自动执行 合约里;在一定的块高范围内:

  1. A合约收到一笔大于 100 的收入,就转 100 给帐户 B 合约;
  2. 合约 B 收到一笔来自合约 A 的转帐,则转 60 给帐户 C;

这样对吗?

那这样的两个合约该如何设计呢

我尝试写点伪代码:

合约 A:

contract A {
    function transfer_value () {
        // 假设 10000 块高时,正好是你所设定的时间
        if (block_hight > 10000) {
            // 收到 value 大于 100 的转帐时,转 60 给 AccountB
            if ( delta(self.balance) > 100 ) {
                  transfer(AccountB, 100);
            }
        }
    }
}

合约 B:

contract B {
    function transfer_value () {
        // 假设 10000 块高时,正好是你所设定的时间
        if (block_hight > 10000) {
            // 收到 value 大于或等于 100 的转帐时,转 60 给 AccountC.
            // 这里你只能知道帐户多了这么多,但却判断不了是谁转给你的。
            // 这个逻辑你自己设计一下。
            if ( delta(self.balance) >= 100 ) {
                  transfer(AccountC, 60);
            }
        }
    }
}

autoExe 合约:

contract AutoExec {
    uint public x;

    function autoExec() public {
        contractA. transfer_value();
        contractB. transfer_value();
    }
}

附:自动执行合约会在每个块都执行一次。

这好像不太智能 :sweat_smile:

@kaikai, 对于这个问题,你有更好的主意吗?

涉及到 schedule tx 的大概是 yaorong 讲的思路。如果想要时间(即外部的因素)控制,就需要 orcale 来与合约进行关于时间的交互。

我看了下你说的需求,

出去触发的因素(时间/块高),只要一个合约就行了。姑且叫做 gambling,这个合约记录下 A 与 B 的地址并收到两者的转账(分别100、60),想当于压注,合约是庄家。
gambling 合约注册自动执行,当条件触发时并且 A.balance 是否大于 100,则进行两笔转账。

这个合约的伪代码我稍后可以给出

contract Gambling is IAutoExec {

    address public A;
    address public B;
    address public C;
    IAutoExec autoExec = IAutoExec(0xffffffffffffffffffffffffffffffffff020013);

    constructor(address _A, address _B, address _C) {
        A = _A;
        B = _B;
        C = _C;
    }

    // msg.sender == A or B
    function Bet() returns (bool) {
        if (msg.sender == A && msg.value == 100)
            return true;

        if (msg.sender == B && msg.value == 60)
            return true;
        // for tx fee
        if (self.balance >= 140)
            // register auto-exec
            autoExec.register(self);
    }

    function autoExec() public {
        B.transfer(100);
        C.transfer(60);
    }
}

hi kaikai, 这个场景里面关键的因素其实是不知道后续的发展,比如说有可能 C 又与 D 签了个合约,在第二个合约执行后,C 需要给 D 转5块钱,而 A 与 B 一开始签合约的时候是不知道 C、D 的存在的,所以没法说在一个合约里面弄。我们希望能利用智能合约的刚性执行的特性,去将这一连串合约串起来,实现业务目的。

我明白了,你说想写一个平台合约,可以让各个有签约需求的使用你这个平台吗?

不知道平台合约的定义是啥,可能就是你认为的意思吧。
具体的场景差不多这样:

A 欠 B 100块,两人约定一个时间还钱,所以写了个合约(这里就涉及到时间点以及 A 要有足够钱这两个条件);
后来,B 又欠 C 60块,但是 B 暂时没钱,就跟 C 说,等 A 还我钱了我就立马还你,所以两人也签了个合约,条件是 A 还钱给 B 了(即第一个合约被执行了);
。。。
。。。
链条可以继续链下去

最后我们的诉求当然是,一旦 A 履行了约定,那所有链上的合约都应该被刚性执行,大家该还的钱都还了,该收的钱都收到。

以上是我们想象的智能合约应该做到的事情,不知道实际上能否做到。:smile:

可以(有条件)。

类似上面那个,签新的合约就是部署一个新的合约上去就行了。

因为自动执行只有一个合约入口,所以需要开发一个签约的合约提供注册 schedule tx 的接口。
这个合约保存所有的需要 auto-exec 的合约,条件触发依次执行。

看你的描述这里有些问题:

  1. 合约没办法触发除自己之外的转账行为,也就是说只能是合约本身有钱对外转账,无法控制第三方向别人或者向自己转账,这是一个条件,所有的设计都要考虑这个。所以你说的借钱和还钱,问题是钱放到哪里,B 怎么用,怎么还
  2. 这里有个链条,因为合约执行失败是会回退的,所以需要保证这个链条可以一直顺利跑通,怎么保证链接条件可以成立。换句话说,怎么保证刚性执行都可以成功

这一个场景得先想清楚怎么实现

第2个问题好办,后面链上去出金必须是小于等于上一笔入金,也就是说只要上一个合约执行成功,下一个合约肯定有足够钱去兑付,这个可以在生成合约时系统做校验控制;
第1个问题就有点颠覆认知了,解释一下什么叫合约本身有钱?

你想做刚性的有条件的转账是吧,有条件这里必然是要借助智能合约的,否则没办法限制说时间到了A必须还钱,这里的必须怎么保证执行?就是不还钱怎么办?

一些概念:

  1. 合约帐号和外部账户(我们的由私钥控制的)没有什么本质的区别,合约也可以像外部账户一样持有钱。
  2. 外部账户发交易(外部账户)来推动状态的更新。也就是合约需要由交易来触发执行。所以带有计划条件的执行不会是链的基本功能。前面说的自动执行功能就是一个解决这个问题的附加功能

思路:

条件和转账都必须在 合约 里,逻辑由合约执行。借钱很简单,把钱给合约就行了,到还钱时候怎么办。就是赖帐的惩罚措施:

  • 也是合约执行,那么就需要一些可以上链的东西做抵押。
  • 链上只作为存证,链下配合执行惩罚
  • 一个场景,A合约放 1000, 你可以向合约转 10 然后可以获取 使用权(不太准确,就是无法取走,可以向外部展示),一定时间内 1010 转给 A。类似放贷

@cipher 他说的这个场景有过研究吗

如果这个场景由 A 主动去触发执行该初始合约(主动还钱),那后面的链条是不是能自动走通呢?(姑且认为会有链下一些类似司法上的措施去强制让 A 执行或者制裁 A)

可以