(四) 区块链数据结构 – 脚本

2023-11-06

脚本是交易数据中的核心部分。可用于锁定输出和解锁输入。

当向某人支付比特币时,我们要为交易输入设置解锁脚本,向别人证明我们有全力使用该输入,同时我们还需要对交易输出添加锁定脚本,确保只有接收者能解锁该输出。

脚本

比特币系统专门设计了一套脚本语言,用于完成诸如加锁、解锁的操作(但不限于这些操作)。这套脚本语言是基于堆栈的解释型语言。

举个例子,比如脚本:2 3 ADD 5 EQUAL,则该脚本的执行过程如下:
1. 将数据 2 压入栈顶
2. 将数据 3 压入栈顶
3. 从栈顶取出两条数据相加,并把相加的结果压入栈顶
4. 从栈顶取出两条数据,判断这两条数据是否相同,并把判断结果压入栈顶

在实现环节,脚本由多个操作单元组成,这些单元可以是数据单元,也可以是计算单元。仍以上例为例:
2、3、5是数据单元、ADD、EQUAL是计算单元。因此脚本2 3 ADD 5 EQUAL会被封装成5个操作单元。

脚本的逻辑结构如下(实例):

脚本逻辑结构

核心代码 - 变量定义
    // The program is a set of chunks where each element is either [opcode] or [data, data, data ...]
    // 脚本程序中包含的脚本单元集合。脚本单元可能是具体操作,也可能是数据
    protected List<ScriptChunk> chunks;
    // Unfortunately, scripts are not ever re-serialized or canonicalized when used in signature hashing. Thus we
    // must preserve the exact bytes that we read off the wire, along with the parsed form.
    // 脚本程序的原始字节数组(在签名验证时会使用,因此需要精确的保留下来)
    protected byte[] program;

核心代码 - 数据解析

    //标准的脚本单元,包括:
    //复制栈顶数据(OP_DUP),对栈顶数据进行HASH160运算(OP_HASH160)、
    //比较栈顶的两个值是否相等(OP_EQUALVERIFY)、检查签名是否正确(OP_CHECKSIG)
    private static final ScriptChunk[] STANDARD_TRANSACTION_SCRIPT_CHUNKS = {
        new ScriptChunk(ScriptOpCodes.OP_DUP, null, 0),
        new ScriptChunk(ScriptOpCodes.OP_HASH160, null, 1),
        new ScriptChunk(ScriptOpCodes.OP_EQUALVERIFY, null, 23),
        new ScriptChunk(ScriptOpCodes.OP_CHECKSIG, null, 24),
    };

    /**
     * 解析脚本数据,生成脚本对象
     * <p>To run a script, first we parse it which breaks it up into chunks representing pushes of data or logical
     * opcodes. Then we can run the parsed chunks.</p>
     * 为了运行脚本,首先我们要将脚本分割成多个单元,每个单元用于表示单条数据和单个操作。之后我们在堆栈上逐一运行每个脚本单元。
     *
     * <p>The reason for this split, instead of just interpreting directly, is to make it easier
     * to reach into a programs structure and pull out bits of data without having to run it.
     * This is necessary to render the to/from addresses of transactions in a user interface.
     * Bitcoin Core does something similar.</p>
     * 不直接解释脚本,而是将脚本分割成多个单元的原因是,可以更方便地了解程序结构并抽出数据,而无需运行它。
     */
    private void parse(byte[] program) throws ScriptException {
        //定义脚本中包含的单元集合,初始化设置数组长度为5,既包含5个单元
        chunks = new ArrayList<>(5);   // Common size.
        ByteArrayInputStream bis = new ByteArrayInputStream(program);
        int initialSize = bis.available();
        while (bis.available() > 0) {

            //计算读取的起始位置
            int startLocationInProgram = initialSize - bis.available();

            int opcode = bis.read();    //读取操作符
            long dataToRead = -1;       //待读取的数据长度

            //如果操作符的值小于OP_PUSHDATA1,则待读取的数据长度为dataToRead
            //如果操作符的值等于OP_PUSHDATA1,则读取之后的一个字节数据,作为待读取的数据长度
            //如果操作符的值等于OP_PUSHDATA2,则读取之后的两个字节数据,作为待读取的数据长度
            //如果操作符的值等于OP_PUSHDATA4,则读取之后的四个字节数据,作为待读取的数据长度
            //上面的这些操作都是将数据压入栈
            if (opcode >= 0 && opcode < OP_PUSHDATA1) {
                // Read some bytes of data, where how many is the opcode value itself.
                dataToRead = opcode;
            } else if (opcode == OP_PUSHDATA1) {
                if (bis.available() < 1) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
                dataToRead = bis.read();
            } else if (opcode == OP_PUSHDATA2) {
                // Read a short, then read that many bytes of data.
                if (bis.available() < 2) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
                dataToRead = Utils.readUint16FromStream(bis);
            } else if (opcode == OP_PUSHDATA4) {
                // Read a uint32, then read that many bytes of data.
                // Though this is allowed, because its value cannot be > 520, it should never actually be used
                if (bis.available() < 4) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
                dataToRead = Utils.readUint32FromStream(bis);
            }

            ScriptChunk chunk;
            //如果待读取的数据长度为-1,则表明不是将外部数据压入栈,则操作符可能是出栈或控制操作
            //如果待读取的数据长度不为-1,则代表该操作为将外部数据压入栈的操作
            if (dataToRead == -1) {
                chunk = new ScriptChunk(opcode, null, startLocationInProgram);
            } else {
                if (dataToRead > bis.available())
                    throw new ScriptException(ScriptError.SCRIPT_ERR_BAD_OPCODE, "Push of data element that is larger than remaining data");
                byte[] data = new byte[(int)dataToRead];
                //读取并校验数据
                checkState(dataToRead == 0 || bis.read(data, 0, (int)dataToRead) == dataToRead);
                chunk = new ScriptChunk(opcode, data, startLocationInProgram);
            }
            // Save some memory by eliminating redundant copies of the same chunk objects.
            // 通过排除多余的操作副本,来节约内存(内存中会缓存一些常见的操作单元,从而减少重复创建,来节约内存)
            for (ScriptChunk c : STANDARD_TRANSACTION_SCRIPT_CHUNKS) {
                if (c.equals(chunk)) chunk = c;
            }

            //将脚本单元添加到脚本的单元集合中
            chunks.add(chunk);
        }
    }

操作单元

操作单元分为数据单元和运算单元。
- 运算单元 用于将数据压入栈,待后续进行运算
- 运算单元 用于从栈中取出数据,并进行运算,并将运算的结果压入栈

操作单元的逻辑结构如下:

操作单元逻辑结构

核心代码 - 变量定义
    /** Operation to be executed. Opcodes are defined in {@link ScriptOpCodes}. */
    public final int opcode;    //操作符,不同的操作符代表不通过的运算,参见ScriptOpCodes

    /**
     * For push operations, this is the vector to be pushed on the stack. For {@link ScriptOpCodes#OP_0}, the vector is
     * empty. Null for non-push operations.
     * 如果是数据单元,则代表将数据压入栈顶,如果是运算单元,则该字段为空
     */
    @Nullable
    public final byte[] data;

    private int startLocationInProgram; //该单元在脚本中的起始位置

核心代码 - 操作符

    // push value
    public static final int OP_0 = 0x00; // push empty vector 空操作(占位使用)

    //数据操作符,用于将数据压入栈
    public static final int OP_FALSE = OP_0;    
    public static final int OP_PUSHDATA1 = 0x4c;
    public static final int OP_PUSHDATA2 = 0x4d;
    public static final int OP_PUSHDATA4 = 0x4e;
    public static final int OP_1NEGATE = 0x4f;
    public static final int OP_RESERVED = 0x50;
    public static final int OP_1 = 0x51;
    public static final int OP_TRUE = OP_1;
    public static final int OP_2 = 0x52;
    public static final int OP_3 = 0x53;
    public static final int OP_4 = 0x54;
    public static final int OP_5 = 0x55;
    public static final int OP_6 = 0x56;
    public static final int OP_7 = 0x57;
    public static final int OP_8 = 0x58;
    public static final int OP_9 = 0x59;
    public static final int OP_10 = 0x5a;
    public static final int OP_11 = 0x5b;
    public static final int OP_12 = 0x5c;
    public static final int OP_13 = 0x5d;
    public static final int OP_14 = 0x5e;
    public static final int OP_15 = 0x5f;
    public static final int OP_16 = 0x60;

    // control 控制操作符,IF、ELSE等
    public static final int OP_NOP = 0x61;
    public static final int OP_VER = 0x62;
    public static final int OP_IF = 0x63;
    public static final int OP_NOTIF = 0x64;
    public static final int OP_VERIF = 0x65;
    public static final int OP_VERNOTIF = 0x66;
    public static final int OP_ELSE = 0x67;
    public static final int OP_ENDIF = 0x68;
    public static final int OP_VERIFY = 0x69;
    public static final int OP_RETURN = 0x6a;

    // stack ops 栈操作符 复制栈顶、出栈等
    public static final int OP_TOALTSTACK = 0x6b;
    public static final int OP_FROMALTSTACK = 0x6c;
    public static final int OP_2DROP = 0x6d;
    public static final int OP_2DUP = 0x6e;
    public static final int OP_3DUP = 0x6f;
    public static final int OP_2OVER = 0x70;
    public static final int OP_2ROT = 0x71;
    public static final int OP_2SWAP = 0x72;
    public static final int OP_IFDUP = 0x73;
    public static final int OP_DEPTH = 0x74;
    public static final int OP_DROP = 0x75;
    public static final int OP_DUP = 0x76;
    public static final int OP_NIP = 0x77;
    public static final int OP_OVER = 0x78;
    public static final int OP_PICK = 0x79;
    public static final int OP_ROLL = 0x7a;
    public static final int OP_ROT = 0x7b;
    public static final int OP_SWAP = 0x7c;
    public static final int OP_TUCK = 0x7d;

    // splice ops   字符串处理操作
    public static final int OP_CAT = 0x7e;
    public static final int OP_SUBSTR = 0x7f;
    public static final int OP_LEFT = 0x80;
    public static final int OP_RIGHT = 0x81;
    public static final int OP_SIZE = 0x82;

    // bit logic    逻辑运算操作符
    public static final int OP_INVERT = 0x83;
    public static final int OP_AND = 0x84;
    public static final int OP_OR = 0x85;
    public static final int OP_XOR = 0x86;
    public static final int OP_EQUAL = 0x87;
    public static final int OP_EQUALVERIFY = 0x88;
    public static final int OP_RESERVED1 = 0x89;
    public static final int OP_RESERVED2 = 0x8a;

    // numeric  数值运算操作符
    public static final int OP_1ADD = 0x8b;
    public static final int OP_1SUB = 0x8c;
    public static final int OP_2MUL = 0x8d;
    public static final int OP_2DIV = 0x8e;
    public static final int OP_NEGATE = 0x8f;
    public static final int OP_ABS = 0x90;
    public static final int OP_NOT = 0x91;
    public static final int OP_0NOTEQUAL = 0x92;
    public static final int OP_ADD = 0x93;
    public static final int OP_SUB = 0x94;
    public static final int OP_MUL = 0x95;
    public static final int OP_DIV = 0x96;
    public static final int OP_MOD = 0x97;
    public static final int OP_LSHIFT = 0x98;
    public static final int OP_RSHIFT = 0x99;
    public static final int OP_BOOLAND = 0x9a;
    public static final int OP_BOOLOR = 0x9b;
    public static final int OP_NUMEQUAL = 0x9c;
    public static final int OP_NUMEQUALVERIFY = 0x9d;
    public static final int OP_NUMNOTEQUAL = 0x9e;
    public static final int OP_LESSTHAN = 0x9f;
    public static final int OP_GREATERTHAN = 0xa0;
    public static final int OP_LESSTHANOREQUAL = 0xa1;
    public static final int OP_GREATERTHANOREQUAL = 0xa2;
    public static final int OP_MIN = 0xa3;
    public static final int OP_MAX = 0xa4;
    public static final int OP_WITHIN = 0xa5;

    // crypto 加解密操作符
    public static final int OP_RIPEMD160 = 0xa6;
    public static final int OP_SHA1 = 0xa7;
    public static final int OP_SHA256 = 0xa8;
    public static final int OP_HASH160 = 0xa9;
    public static final int OP_HASH256 = 0xaa;
    public static final int OP_CODESEPARATOR = 0xab;
    public static final int OP_CHECKSIG = 0xac;
    public static final int OP_CHECKSIGVERIFY = 0xad;
    public static final int OP_CHECKMULTISIG = 0xae;
    public static final int OP_CHECKMULTISIGVERIFY = 0xaf;

    // block state  区块状态
    /** Check lock time of the block. Introduced in BIP 65, replacing OP_NOP2 */
    public static final int OP_CHECKLOCKTIMEVERIFY = 0xb1;
    public static final int OP_CHECKSEQUENCEVERIFY = 0xb2;

    // expansion    扩展操作符

public static final int OP_NOP1 = 0xb0;

上一篇:(三) 区块链数据结构 – 交易

下一篇:区块链算法 – Secp256k1 签名生成和验证算法 以及证明过程

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(四) 区块链数据结构 – 脚本 的相关文章

  • 下载blob地址或m3u8格式视频方法以及常见问题解决

    STEP1 在一些视频播放网站视频下方没有直接的下载按钮 此时可以先用电脑F12键查看元素 会出现类似如下界面 STEP2 该界面左上角的位置有一个指针形状的按钮 选用这个按钮 点一下网页里视频播放的区域 审查元素界面会跳转到类似如下的界面
  • Pandas知识点-合并操作join

    Pandas知识点 合并操作join 在Pandas中 join 方法也可以用于实现合并操作 本文介绍join 方法的具体用法 一 基础合并操作 join other 将一个或多个DataFrame加入到当前DataFrame中 实现合并的
  • Redis基础

    文章目录 1 Redis入门 Redis简介 Redis下载与安装 Redis启动与停止 设置Redis密码和允许远程连接 2 Redis数据类型 3 Redis常用命令 字符串string操作命令 哈希hash操作命令 列表list操作命
  • Windows防止程序多开并在多开时弹出已运行程序

    Windows下防止程序多开并在多开时弹出已运行的程序 1 功能说明 在Windows客户端开发时 往往需要禁止客户多开程序的情况 并且在客户再次双击启动图标时显示已启动的程序界面 故而需要下面的功能 使用CreateMutex禁止程序多开
  • CNN网络,CNN+SVM网络对故障分类(python代码)

    1 数据集可以使用多种数据集验证 例如 CWRU PU IMS JNU SEU PHM2010等等 这里使用的是IMS 辛辛那提 正常 内圈故障 外圈故障 滚动体故障 原始数据下载官网 Prognostics Center of Excel

随机推荐

  • CentOS7 彻底清除MySQL

    MySQL完全删除 参考文档 http www centoscn com mysql 2017 0517 8791 html 查看已经安装的服务 rpm qa grep i mysql i 作用是不区分大小写 删除这两个服务 去掉后缀 rp
  • [思维模式-7]:《如何系统思考》-3- 认识篇 - 什么是系统?系统的特征?

    目录 第1章 什么是系统 1 1 万事万物都是一个有机的系统 1 2 系统的科学定义 1 3 系统的构成 1 4 系统的分类 第2章 动态复杂系统的八大特征 2 1 目的性 2 8 边界 2 3 结构影响行为 2 4 总体大于部分之和 2
  • 数据大屏适配方案 (scale)

    目录 适配方案分析 vw vh 什么是vw和vh vw和百分比的区别是什么 vw怎么使用 实现思路 案例 scale方案 一 scale 方法 1 scaleX x 2 scaleY y 3 scale x y 案例 大屏之关键 前期的自适
  • TemplateSyntaxError at /statistics/ ‘staticfiles‘ is not a registered tag li

    报错django template exceptions TemplateSyntaxError staticfiles is not a registered tag library Mustbe one of admin list ad
  • app基本控件

    一个完整的APP包括四大类 各种 栏 内容视图 控制元素 临时视图 各种 栏 状态栏 导航栏 标签栏 工具栏 搜索栏 范围栏 内容视图 列表视图 卡片视图 集合视图 图片视图 文本视图 控制元素 用于控制产品行为或显示的信息 临时视图 警告
  • IDEA设置启动选择项目

    IDEA设置启动选择项目 IDEA2019 3 5启动后自行选择项目 而不是进入上一次关闭的项目 通过下面的设置修改 将Reopen last project on startup勾选去除
  • 从零开始学Qt(四)信号与槽

    信号与槽 书不记 熟读可记 义不精 细思可精 1 信号 槽是啥 古有 烽火狼烟 传递消息 敌人来犯的消息迅速传达开来 是多么的聪慧啊 烟就是信号 下一个燃火台看到烟后就点燃燃料 这个就槽 换种方法说就是 一个按钮被点击了 会触发一个点击的信
  • 校园网连路由器

    1 校园网 并记住账号及其密码 2 一台电脑 Windows 系统 3 一台路由器 4 两条网线 准备完成 下面开始正式干活 第一步 电脑插上网线 路由器通电插上网线 在你的电脑登陆你的校园网 第二步 打开你的电脑搜索CMD 并以管理员权限
  • QString和QByteArray的区别

    QString和QByteArray的区别 本质 格式转换 QString字符串格式化打印 长度 本质 QString是对QByteArray的再次封装 QString可以通过char 来构造 也可以通过QByteArray来构造 QByt
  • CentripetalNet: Pursuing High-quality Keypoint Pairs for Object Detection解读

    摘要 基于关键点的检测器得到了不错的性能 但关键点匹配错误是普遍性存在的 严重的影响了其检测器的性能 作者通过centripetal shif将相同的类别的实例分开 CentripetalNet是预测角点的位置和向心偏移 通过向向心偏移来对
  • Web API: URL.createObjectURL()实践

    1 问题 URL createObjectURL的介绍如下 The URL createObjectURL static method creates a DOMString containing a URL representing th
  • 图形学数学基础之1D采样分布计算方法Inverse Method

    作者 i dovelemon 日期 2017 09 04 来源 CSDN 主题 Rendering Equation Probability Density Function Cumulative Density Function 引言 前
  • Android开发中Javassist的妙用

    Javassist Java字节码以二进制的形式存储在 class文件中 每一个class文件包含一个Java类或接口 Javassist框架就是一个用来处理Java字节码的类库 它可以在一个已经编译好的类中添加新的方法 或者修改已有的方法
  • pytorch 下载

    pytorch 下载 使用anconda 直接下载pytorch的朋友应该是知道pytorch是个啥的 所以直接上教程 anconda下载 anconda是一个用于下载和管理python依赖包和环境的一个工具 下载详情可以去看这篇文章 an
  • VMware虚拟机nat模式连不上网

    我的虚拟机总是各种连不上网 每次都要折腾一番 现在我把虚拟机连不上网的原因总体排查一下 按照流程一步步来 基本上可以解决大部分人的问题 首先 在VMware的编辑 gt 虚拟网络编辑器重新建立 网络 之前的要删掉 新建的同样选择 就可以 如
  • 线性回归总结

    向量相似理论 线性回归 比如预测房价中学区属性0 4 居住体验0 2 通勤距离0 2 商业环境0 2等因素 在同一价格区间 只有样本特征与上述属性分布一致时 各方面都加权均衡 才能取得高分 任一单一属性过高 必然导致其他属性降低 通常意义上
  • Flutter音频播放之just_audio

    just audio的使用 just audio 它是一个用于播放音频的 Flutter 插件 安装和导入 just audio 要使用 just audio 库 需要将其添加到项目的 pubspec yaml 文件中 dependenci
  • Update your application to remove the dependency cycle between beans

    Spring 高版本循环依赖问题 问题描述 提示 Spring boot 应用启动报错 Relying upon circular references is discouraged and they are prohibited by d
  • 三大战略引擎加速转动,微盟驶入智慧商业服务深水区

    2023年3月30日 微盟披露了2022年财报 经调整总收入18 39亿元 经调整毛利11 2亿元 在业务层面 订阅解决方案业务表现亮眼 其中智慧零售板块营收5 13亿元 同比内生增长45 5 拉动每用户平均收益同比增长12 3 达1296
  • (四) 区块链数据结构 – 脚本

    脚本是交易数据中的核心部分 可用于锁定输出和解锁输入 当向某人支付比特币时 我们要为交易输入设置解锁脚本 向别人证明我们有全力使用该输入 同时我们还需要对交易输出添加锁定脚本 确保只有接收者能解锁该输出 脚本 比特币系统专门设计了一套脚本语