Bitcoin Transaction - Part One

Transaction是什么?

我们随意看一个最简单的Transaction,看看什么是Transaction。在block exploper中经过简单的查询一个经典的txid 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18,可以看到,他到底是什么。

0100000001524d288f25cada331c298e21995ad070e1d1a0793e818f2f7cfb5f6122ef3e71000000008c493046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d014104e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8dffffffff0180969800000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000000

上边是一个Transaction的hex,但是到底是什么意思呢?如何将其decode,可以看的比较明白。

{
    hash 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
    inputs
    {
        input
        {
            address_hash 54a28c6ba2bebdb694fe487a87e3e8ed4eab1502
            previous_output
            {
                hash 713eef22615ffb7c2f8f813e79a0d1e170d05a99218e291c33daca258f284d52
                index 0
            }
            script "[3046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d01] [04e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8d]"
            sequence 4294967295
        }
    }
    lock_time 0
    outputs
    {
        output
        {
            address_hash 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8
            script "dup hash160 [7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8] equalverify checksig"
            value 10000000
        }
    }
    version 1
}

一个典型的bitcoin transaction由两部分组成,input和output,input是这笔transaction的输入,output是输出。首先看input,因为bitcoin是utxo model所以一个的输出是另一transaction的输出。仔细看上边的trasaction,input中有一个script,output中另外一个script,那么script是什么呢?

input中的script是unlocking script, output中的script是locking script。为什么说input中的script是unlocking script呢?因为他是解锁上一笔transaction的output。也就是说矿工在verify transaction的时候,会找到utxo(上一笔交易的output中的locking script)如果unlocking script + locking script 返回的结果是true那么这个钱就能花了。具体来看看是怎么回事。

下边是一个例子是典型的一个P2PKH Transaction的unlocking script + locking script,假设Alice在第n个transaction中,有一笔utxo,然后她在弟n+1笔transaction中将其花出。那么她的unlocking script是在第n+1笔 transaction的input中,locking script是在第n的output中。这一点一定要注意一下。

unlocking + locking

在图中的PubkeyHash是Alice的公钥Hash,Sig是Alice的签名,Pubkey是Alice的公钥。所以如果手动运行一下这个script,那么得到的结果是什么呢?

执行结果

如果手动运行一下得到的结果是True。所以那么Alice就可以用这笔钱了。所以现在比较明确了为什么output中的script是locking script,因为这个transaction将这个笔钱lock在一个publicKey中,只有你证明你是这个私钥的owner你才能动这笔钱,反过来这个key的owner因为有私钥,通过签名可以解锁这笔钱。所以通过这样的方式把钱lock了起来,Locking script由此得名。Brilliant!

是不是有另一问题?

上边介绍transaction的基本的结构,那么这里有一个问题是,那么签名到底签的是什么,如果可以随意签名,例如签名一个hello world,miner如何验证呢?第二如果签名数据可以是一个任意的数据那么如果hacker or other任意修改locking script呢?本来是给A的,hacker or others给了B?这样是否可能?或者如何应对这个问题?

第一个问题,首先签名签的是transaction 或者说是transaction的一部分 or hash,这样miner就可以知道如何验证签名了。

第二问题,这就引出了sign的type。bitcoin中的sign hash Type有这几种。

SIGHASH flag Value Description 类比
ALL 0x01 Signature applies to all inputs and outputs 基本类型,from 和 to都签名
NONE 0x02 Signature applies to all inputs, none of the outputs 空白支票,收款人随便填
SINGLE 0x03 Signature applies to all inputs, none of the outputs inputs全签,output只签一个

另外还有其他的modifier

SIGHASH flag Value Description 类比
ALL_ANYONECANPAY 0x81 Signature applies to one input and all outputs 募资transaction,outputs锁定
NONE_ANYONECANPAY 0x82 Signature applies to one input, none of the outputs 空白支票,output随便
SINGLE_ANYONECANPAY 0x83 Signature applies to one input and the output with the same index number inputs全签,output只签一个

一般说最近基本的transaction sign hash type 就是ALL,看一下具体的代码中的实现。

// TODO: remove keyPair.network matching in 4.0.0
  if (keyPair.network && keyPair.network !== network)
    throw new TypeError('Inconsistent network');
  if (!inputs[vin]) throw new Error('No input at index: ' + vin);

  hashType = hashType || Transaction.SIGHASH_ALL;
  if (needsOutputs(hashType)) throw new Error('Transaction needs outputs');

  const input = inputs[vin];

  // if redeemScript was previously provided, enforce consistency
  if (
    input.redeemScript !== undefined &&
    redeemScript &&
    !input.redeemScript.equals(redeemScript)
  ) {
    throw new Error('Inconsistent redeemScript');
  }

tx_builder

    const txTmp = this.clone();

    // SIGHASH_NONE: ignore all outputs? (wildcard payee)
    if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
      txTmp.outs = [];

      // ignore sequence numbers (except at inIndex)
      txTmp.ins.forEach((input, i) => {
        if (i === inIndex) return;

        input.sequence = 0;
      });

      // SIGHASH_SINGLE: ignore all outputs, except at the same index?
    } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
      // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
      if (inIndex >= this.outs.length) return ONE;

      // truncate outputs after
      txTmp.outs.length = inIndex + 1;

      // "blank" outputs before
      for (let i = 0; i < inIndex; i++) {
        txTmp.outs[i] = BLANK_OUTPUT;
      }

      // ignore sequence numbers (except at inIndex)
      txTmp.ins.forEach((input, y) => {
        if (y === inIndex) return;

        input.sequence = 0;
      });
    }

    // SIGHASH_ANYONECANPAY: ignore inputs entirely?
    if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
      txTmp.ins = [txTmp.ins[inIndex]];
      txTmp.ins[0].script = ourScript;

      // SIGHASH_ALL: only ignore input scripts, leave all the outputs
    } else {
      // "blank" others input scripts 
      txTmp.ins.forEach(input => {
        input.script = EMPTY_SCRIPT;
      });
      txTmp.ins[inIndex].script = ourScript;
    }

    // serialize and hash
    const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4);
    buffer.writeInt32LE(hashType, buffer.length - 4);
    txTmp.__toBuffer(buffer, 0, false);

tx_build_sign

private __byteLength(_ALLOW_WITNESS: boolean): number {
    const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();

    return (
      (hasWitnesses ? 10 : 8) +
      varuint.encodingLength(this.ins.length) +
      varuint.encodingLength(this.outs.length) +
      this.ins.reduce((sum, input) => {
        return sum + 40 + varSliceSize(input.script);
      }, 0) +
      this.outs.reduce((sum, output) => {
        return sum + 8 + varSliceSize(output.script);
      }, 0) +
      (hasWitnesses
        ? this.ins.reduce((sum, input) => {
            return sum + vectorSize(input.witness);
          }, 0)
        : 0)
    );
  }

tx, outs

上述的的代码实现中,可以看出默认的hashType是ALL,所以上边提的问题,就可以通过这样的方式解决,但是这么看hash Type中各种形式可以用于实现不同的功能。

最后看看unlocking scirpt的中签名是什么。

3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301

签名是DER格式的。

  • 0x30—indicating the start of a DER sequence

  • 0x45—the length of the sequence (69 bytes)

  • 0x02—an integer value follows

  • 0x21—the length of the integer (33 bytes)

  • R—00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb

  • 0x02—another integer follows

  • 0x20—the length of the integer (32 bytes)

  • S—4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813

  • A suffix (0x01) indicating the type of hash used (SIGHASH_ALL)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容