Raw-Transaction

rust-bitcoin 非規範 DER 簽名

  • January 23, 2021

我正在嘗試使用rust-bitcoin創建一個花費 P2PKH 輸出的原始交易,但是每當我將交易推送到測試網時,我都會收到以下錯誤mandatory-script-verify-flag-failed (Non-canonical DER signature) 我的簽名程式碼如下所示:

/// Sign a bitcoin transaction spending P2PKH outputs
/// 
/// # Arguments
/// 
/// * `tx` the transaction to be signed
/// * `script_pubkeys` list of script pubkeys of the inputs (has to be indexed in the same order as the inputs in the transaction)
/// * `skeys` list of secret keys with which to sign the inputs (has to be indexed in the same order as the inputs in the transaction)
/// * `pkeys` list of public keys for the inputs which are spent (has to be indexed in the same order as the inputs in the transaction)
/// * `curve` reference to the elliptic curve object used for signing
pub fn sign_transaction(tx : Transaction, script_pubkeys : Vec<Script>, skeys : Vec<PrivateKey>, pkeys: Vec<PublicKey>, curve : &Secp256k1<All>) -> Transaction {
   let mut signed_inp : Vec<TxIn> = Vec::new();

   for (i, unsigned_inp) in tx.input.iter().enumerate() {
       let script_pubkey = script_pubkeys.get(i).unwrap();
       let signing_key = skeys.get(i).unwrap().key;
       let pub_key = pkeys.get(i).unwrap();
       let sighash = tx.signature_hash(i, &script_pubkey, SIGHASH_ALL);
       let msg = Message::from_slice(&sighash.as_ref()).unwrap();
       let sig = curve.sign(&msg, &signing_key);
       // Standard P2PKH redeem script:
       // <sig> <pubkey>
       let redeem_script = Builder::new()
           .push_slice(&sig.serialize_der())
           .push_int(1)
           .push_key(&pub_key)
           .into_script();
       signed_inp.push(TxIn {
           previous_output : unsigned_inp.previous_output,
           script_sig: redeem_script,
           sequence : unsigned_inp.sequence,
           witness : unsigned_inp.witness.clone()
       });
       
   }
   
   Transaction {
       version : tx.version,
       lock_time : tx.lock_time,
       input : signed_inp,
       output: tx.output
   }
}

這是一個範例交易01000000010b41a0a10f36c39b057053281d1d4d9f67410c40a093c351024f57af4e133c21010000006a46304402202d99eb85a14f483f4679b93eb1cf0f67b64d788dc4cd5ad782e75540508922ce0220752a9237e320497ba289a91edd7135dd6c53f9332dc33c71a3020bf0555607fc512103202430a99091407e6c724c5c88504e69ef6917f042a65cb990537922f823d9dfffffffff02e80300000000000017a9141831af16119be20b532668d5995bd05ac955153b872a091e00000000001976a9146b538e889cf0df7b69112b50db8faadb4e457b9288ac00000000

並以解碼形式

{
  "result":{
     "txid":"b36457604e0ccd491e97c30c06a011fd906bb83d6fe81c676efb9fc60f17c5a5",
     "hash":"b36457604e0ccd491e97c30c06a011fd906bb83d6fe81c676efb9fc60f17c5a5",
     "version":1,
     "size":223,
     "vsize":223,
     "weight":892,
     "locktime":0,
     "vin":[
        {
           "txid":"213c134eaf574f0251c393a0400c41679f4d1d1d285370059bc3360fa1a0410b",
           "vout":1,
           "scriptSig":{
              "asm":"304402202d99eb85a14f483f4679b93eb1cf0f67b64d788dc4cd5ad782e75540508922ce0220752a9237e320497ba289a91edd7135dd6c53f9332dc33c71a3020bf0555607fc 1 03202430a99091407e6c724c5c88504e69ef6917f042a65cb990537922f823d9df",
              "hex":"46304402202d99eb85a14f483f4679b93eb1cf0f67b64d788dc4cd5ad782e75540508922ce0220752a9237e320497ba289a91edd7135dd6c53f9332dc33c71a3020bf0555607fc512103202430a99091407e6c724c5c88504e69ef6917f042a65cb990537922f823d9df"
           },
           "sequence":4294967295
        }
     ],
     "vout":[
        {
           "value":0.00001000,
           "n":0,
           "scriptPubKey":{
              "asm":"OP_HASH160 1831af16119be20b532668d5995bd05ac955153b OP_EQUAL",
              "hex":"a9141831af16119be20b532668d5995bd05ac955153b87",
              "reqSigs":1,
              "type":"scripthash",
              "addresses":[
                 "2MuT9izFXKft5umHqr1gviyhus6JGLCxGd6"
              ]
           }
        },
        {
           "value":0.01968426,
           "n":1,
           "scriptPubKey":{
              "asm":"OP_DUP OP_HASH160 6b538e889cf0df7b69112b50db8faadb4e457b92 OP_EQUALVERIFY OP_CHECKSIG",
              "hex":"76a9146b538e889cf0df7b69112b50db8faadb4e457b9288ac",
              "reqSigs":1,
              "type":"pubkeyhash",
              "addresses":[
                 "mqJShCuqM7FK5zPa7Z1PNJySMHTVPrK9bb"
              ]
           }
        }
     ]
  },
  "error":null,
  "id":"curltest"
}

非常感謝任何有關可能出錯的幫助

我相信你的意圖push_int(1)是增加SIGHASH_ALL交易。

然而,Script 的許多不必要的不​​靈活之一是 sighash 標誌實際上需要成為簽名本身的一部分,而不是作為腳本中的單獨堆棧元素。

這意味著您需要獲取Vec<u8>from 的返回Signature::serialize_der值,用 對其進行變異sig.push(1)然後將其添加到腳本堆棧中。通過手動編輯您的交易以將51( OP_1)更改為01(1 字節),並增加簽名操作碼中的長度以使用它,我能夠使您的交易通過測試網路上的驗證。(雖然我沒有送出它,以免您處理“輸入已花費”錯誤的煩惱!)

我對這個 API 並不感興趣——我想至少Signature應該有一個方法serialize_der_with_sighashflag或其他東西來消除呼叫者使用臨時變數的需要 and mut,但是手動建構這樣的腳本並不常見估計還沒出現

您可能會考慮查看rust-miniscript,它為您做了很多這樣的事情。

引用自:https://bitcoin.stackexchange.com/questions/101764