Transaction-Verification

驗證比特幣交易

  • January 23, 2022

繼偉大的文章 [需要逐步兌換原始交易範例,我想出了下面的程式碼,但驗證失敗。

// pubkeyBytes, len: 65
// 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9

// data, len: 32
// 30f10a6468b7d98257af63fb40dfcf2cefe991346fd37c67cf7b51ff8d4404d3

// signatureBytes, len: 71
// 30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c

   ECPublicKey publicKey = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false);;
   Secp256k1.setCommonCurveParameters(publicKey);
   publicKey.setW(pubkeyBytes, (short) 0, (short) pubkeyBytes.length);

   Signature eccSign = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
   eccSign.init(publicKey, Signature.MODE_VERIFY);
   boolean verified = eccSign.verify(data, (short)0, (short) data.length,
               signatureBytes, (short)0, (short)signatureBytes.length
           );
   Assert.assertTrue(verified);

斷言最終失敗。

被困了好幾個小時。想不通。非常感謝任何幫助。謝謝。

static void test_sig ( )
{
 const MyByteArray pubkey ( QByteArray::fromHex ( "042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9" ) );
 const MyByteArray signat ( QByteArray::fromHex ( "30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c" ) );
 const MyKey32     digest ( QByteArray::fromHex ( "30f10a6468b7d98257af63fb40dfcf2cefe991346fd37c67cf7b51ff8d4404d3" ) );
 qDebug ( ) << ( digest.verify ( pubkey, signat ) ? "passed" : "failed" );
}

我的結果是::通過

signature正確的digest並且public key

您的驗證碼可能有問題,解碼十六進制值和/或摘要中的字節順序?

通常問題是使用的簽名驗證算法。比特幣使用雙 SHA-256 作為 ECDSA 的散列算法,因此 Z 值是未簽名交易的雙 SHA-256 散列。查看您正在使用的簽名雜湊算法:

簽名算法 ALG_ECDSA_SHA_256 生成 32 字節 SHA-256 摘要並使用 ECDSA 和 ECKey 參數中定義的曲線對摘要進行簽名/驗證

您已經獲取了 Z 值,但隨後僅使用 1 輪 SHA-256 再次對其進行散列。您可以嘗試傳入只有 1 個 SHA-256 散列的原像,然後讓 Signature 對象處理第二輪,但我對使用標準 Java 安全庫的這種方法不太滿意。為了在 Java 中完成比特幣交易簽名/驗證,您需要 (1) 實現自己的 ECDSA 簽名/驗證,或者 (2) 使用諸如 BitcoinJ 之類的庫。以下是我在我的 ECPubKey 類中我自己的項目中使用的程式碼(請注意,我正在使用大量其他自定義程式碼,您會從中看到方法呼叫,但未包括在此處,但您將了解要點):

/**
* @param preimage the Preimage to verify
* @param signature An ECDSA Signature (R/S Value pair)
* @return whether this Public Key was used to generate the signature for the given preimage
*/
public boolean verify(String preimage, ECSignature signature) {
   String sigHash = HashUtil.doubleSha256(preimage);
   BigInteger z = new BigInteger(1, ByteUtil.hex2bytes(sigHash));
   BigInteger r = signature.getR();
   BigInteger s = signature.getS();
   BigInteger sInv = s.modInverse(CurveParams.n);
   BigInteger u1 = z.multiply(sInv).mod(CurveParams.n);
   BigInteger u2 = r.multiply(sInv).mod(CurveParams.n);
   ECPoint u1Point = ScalarMultiply.scalmult(CurveParams.G, u1);
   ECPoint u2Point = ScalarMultiply.scalmult(this.getPoint(), u2);
   ECPoint sigPoint = ScalarMultiply.addPoint(u1Point, u2Point);
   return r.compareTo(sigPoint.getAffineX().mod(CurveParams.n)) == 0;
}

該方法基本上直接從ECDSA 維基百科頁面複製並翻譯成 Java。

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