Skip to content

Price Prediction Oracle

由预言机结算的价格预测支付。

txt
import std.schnorr

# 签名价格报告决定胜方,并要求输出 0 接收上一轮全部金额。
# 报告会绑定资产 ID、时间戳和价格,再进入支付校验。
Contract PricePrediction:

    Struct Script:
        SuffixData: string
        PartialHash: string
        Size: number

    Struct Output:
        Value: number
        LockingScript: Script

    Struct Input:
        Data: {txid: hex32, vout: hex4, sequence: hex4}

    Struct CurrentTX:
        Outputs: Output[3]

    Struct PreTX:
        VLIO: string
        Inputs: Input[3]
        UnlockingScriptHash: string
        Outputs: Output[3]

    def settle(msg: hex, R: hex, s: hex, ctx: CurrentTX, pretx: PreTX):
        # 当前输入会指出哪一个上一轮输出金额参与结算。
        utxoData: {txid: hex32, vout: hex4, sequence: hex4}
        utxoData = Push(BVM.unlockingInput)
        vout = BinToNum(utxoData.vout)
        Delete(utxoData.sequence)
        Delete(utxoData.txid)

        # 先保存上一轮金额,稍后用于校验输出 0。
        pre_value = pretx.Outputs[vout].Value.Clone()
        SetAlt(pre_value)

        # 根据传入的交易片段重新计算上一笔交易的 txid。
        tx_data = Push(0)
        SetAlt(tx_data)
        for i in Range(2, -1, -1):
            size_copy = pretx.Outputs[i].LockingScript.Size.Clone()
            if size_copy != 0:
                tx_data_temp = PartialHash(pretx.Outputs[i].LockingScript.SuffixData, pretx.Outputs[i].LockingScript.PartialHash, pretx.Outputs[i].LockingScript.Size)
                tx_data_temp = Cat(pretx.Outputs[i].Value, tx_data_temp)
                SetMain(tx_data)
                tx_data = Cat(tx_data_temp, tx_data)
                SetAlt(tx_data)
                Keep(tx_data)
            else:
                Delete(pretx.Outputs[i].LockingScript.Size)
                Delete(pretx.Outputs[i].LockingScript.PartialHash)
                Delete(pretx.Outputs[i].LockingScript.SuffixData)
                Delete(pretx.Outputs[i].Value)

        SetMain(tx_data)
        tx_data = Sha256(tx_data)
        tx_data = Cat(pretx.UnlockingScriptHash, tx_data)
        SetAlt(tx_data)

        tx_input_data = Push(0)
        for i in Range(2, -1, -1):
            tx_input_data = Cat(pretx.Inputs[i].Data, tx_input_data)
        tx_input_hash = Sha256(tx_input_data)

        SetMain(tx_data)
        tx_data = Cat(tx_input_hash, tx_data)
        tx_data = Cat(pretx.VLIO, tx_data)
        txid = Hash256(tx_data)
        preTXID = BVM.unlockingInput.Slice(0, 32)
        EqualVerify(txid, preTXID)

        # Schnorr 验签会消耗原始报告字节,因此先克隆一份。
        msg_for_parse = msg.Clone()
        SetAlt(msg_for_parse)
        schnorrVerify(msg, R, s, self.oraclePubKey, self.generator, self.modulus)
        SetMain(msg_for_parse)

        # 按 assetId(32) | timestamp(8) | price(8) 解析报告。
        {msg_assetId, r1}       = Split(msg_for_parse, 32)
        {msg_ts_b, msg_price_b} = Split(r1, 8)

        # 报告中的资产 ID 必须与配置值一致。
        EqualVerify(msg_assetId, self.assetId)

        # 时间戳按无符号数处理,并且必须达到结算时间。
        msg_ts_num   = BinToNum(Cat(msg_ts_b, 0x00))
        settle_clone = self.settleTime.Clone()
        settle_num   = BinToNum(Cat(settle_clone, 0x00))
        NumEqualVerify(GreaterOrEqual(msg_ts_num, settle_num), 1)

        # 价格达到阈值则选择 Alice,否则选择 Bob。
        price_num       = BinToNum(Cat(msg_price_b, 0x00))
        threshold_clone = self.threshold.Clone()
        threshold_num   = BinToNum(Cat(threshold_clone, 0x00))
        winner_flag     = GreaterOrEqual(price_num, threshold_num)

        # 输出 0 必须是 P2PKH,并支付给被选中胜方的公钥哈希。
        out0_suffix = ctx.Outputs[0].LockingScript.SuffixData.Clone()
        { out0_prefix, out0_suffix } = Split(out0_suffix, 3)
        EqualVerify(out0_prefix, 0x76a914)
        { out0_pkh, out0_tail } = Split(out0_suffix, 20)
        EqualVerify(out0_tail, 0x88ac)
        if winner_flag == 1:
            EqualVerify(out0_pkh, self.alice)
        else:
            EqualVerify(out0_pkh, self.bob)

        # 胜方收到的金额必须等于上一轮锁定金额。
        SetMain(pre_value)
        out0_value = ctx.Outputs[0].Value.Clone()
        EqualVerify(out0_value, pre_value)

        # 重建所有声明输出,并绑定到交易输出哈希。
        outputs_data = Push(0)
        SetAlt(outputs_data)
        for i in Range(2, -1, -1):
            size = ctx.Outputs[i].LockingScript.Size.Clone()
            if size != 0:
                output_temp = PartialHash(ctx.Outputs[i].LockingScript.SuffixData, ctx.Outputs[i].LockingScript.PartialHash, ctx.Outputs[i].LockingScript.Size)
                output_temp = Cat(ctx.Outputs[i].Value, output_temp)
                SetMain(outputs_data)
                outputs_data = Cat(output_temp, outputs_data)
                SetAlt(outputs_data)
                Keep(outputs_data)
            else:
                Delete(ctx.Outputs[i].LockingScript.Size)
                Delete(ctx.Outputs[i].LockingScript.PartialHash)
                Delete(ctx.Outputs[i].LockingScript.SuffixData)
                Delete(ctx.Outputs[i].Value)
        SetMain(outputs_data)
        outputs_data = Sha256(outputs_data)
        EqualVerify(outputs_data, BVM.outputsHash)