mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 10:13:26 +01:00
Successfully creating p2pk and multisignature scriptSig digital signatures - they evaluate to true when run through the ScriptInterpreter
This commit is contained in:
parent
40697b23e0
commit
71a50be511
@ -46,3 +46,27 @@ trait TransactionSignatureComponent {
|
|||||||
*/
|
*/
|
||||||
def flags : Seq[ScriptFlag]
|
def flags : Seq[ScriptFlag]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object TransactionSignatureComponent {
|
||||||
|
|
||||||
|
private sealed case class TransactionSignatureComponentImpl(transaction : Transaction, inputIndex : UInt32,
|
||||||
|
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]) extends TransactionSignatureComponent
|
||||||
|
|
||||||
|
def apply(transaction : Transaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
|
||||||
|
flags : Seq[ScriptFlag]) : TransactionSignatureComponent = {
|
||||||
|
TransactionSignatureComponentImpl(transaction,inputIndex,scriptPubKey, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This factory method is used for changing the scriptPubKey inside of a txSignatureComponent
|
||||||
|
*
|
||||||
|
* @param oldTxSignatureComponent
|
||||||
|
* @param scriptPubKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def apply(oldTxSignatureComponent : TransactionSignatureComponent, scriptPubKey : ScriptPubKey) : TransactionSignatureComponent = {
|
||||||
|
TransactionSignatureComponentImpl(oldTxSignatureComponent.transaction,
|
||||||
|
oldTxSignatureComponent.inputIndex,scriptPubKey, oldTxSignatureComponent.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package org.bitcoins.core.crypto
|
||||||
|
|
||||||
|
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||||
|
import org.bitcoins.core.script.crypto.HashType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by chris on 7/21/16.
|
||||||
|
*/
|
||||||
|
trait TransactionSignatureCreator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a signature from a tx signature component
|
||||||
|
* @param txSignatureComponent contains the tx, inputIndex which specify which input we are creating a sig for
|
||||||
|
* @param privateKey the private key which we are signing the hash with
|
||||||
|
* @param scriptPubKey the scriptPubKey which we are spending
|
||||||
|
* @param hashType the procedure to use for hashing to transaction
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def createSig(txSignatureComponent: TransactionSignatureComponent, privateKey: ECPrivateKey,
|
||||||
|
scriptPubKey: ScriptPubKey, hashType: HashType): ECDigitalSignature = {
|
||||||
|
val hash = TransactionSignatureSerializer.hashForSignature(txSignatureComponent,scriptPubKey,hashType)
|
||||||
|
val signature = privateKey.sign(hash)
|
||||||
|
//append 1 byte hash type onto the end
|
||||||
|
ECDigitalSignature(signature.bytes ++ Seq(hashType.byte))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TransactionSignatureCreator extends TransactionSignatureCreator
|
@ -160,6 +160,17 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper function for hashForSignature
|
||||||
|
* @param txSignatureComponent this contains the transaction and inputIndex for hashForSignature
|
||||||
|
* @param scriptPubKey
|
||||||
|
* @param hashType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def hashForSignature(txSignatureComponent: TransactionSignatureComponent, scriptPubKey: ScriptPubKey, hashType: HashType): DoubleSha256Digest = {
|
||||||
|
hashForSignature(txSignatureComponent.transaction,txSignatureComponent.inputIndex,scriptPubKey.asm,hashType)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bitcoins.core.script
|
package org.bitcoins.core.script
|
||||||
|
|
||||||
|
|
||||||
import org.bitcoins.core.crypto.{TransactionSignatureComponentFactory, TransactionSignatureComponent}
|
import org.bitcoins.core.crypto.{TransactionSignatureComponent}
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.protocol.script.{ScriptSignature, ScriptPubKey}
|
import org.bitcoins.core.protocol.script.{ScriptSignature, ScriptPubKey}
|
||||||
import org.bitcoins.core.protocol.transaction.Transaction
|
import org.bitcoins.core.protocol.transaction.Transaction
|
||||||
@ -358,7 +358,7 @@ object ScriptProgram {
|
|||||||
*/
|
*/
|
||||||
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
|
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
|
||||||
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
|
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
|
||||||
val txSignatureComponent = TransactionSignatureComponentFactory.factory(transaction,inputIndex,scriptPubKey,flags)
|
val txSignatureComponent = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags)
|
||||||
PreExecutionScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
|
PreExecutionScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,30 @@ trait Base58 extends BitcoinSLogger {
|
|||||||
|
|
||||||
def encode(byte : Byte) : String = encode(Seq(byte))
|
def encode(byte : Byte) : String = encode(Seq(byte))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a pubkey hash to a base 58 address on the corresponding network
|
||||||
|
* @param hash the result of Sha256(RipeMD160(pubkey))
|
||||||
|
* @param network the network on which this address is being generated for
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def encodePubKeyHashToAddress(hash: Sha256Hash160Digest, network: NetworkParameters): Address = {
|
||||||
|
val versionByte: Byte = network.p2pkhNetworkByte
|
||||||
|
val bytes = Seq(versionByte) ++ hash.bytes
|
||||||
|
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
|
||||||
|
Address(encode(bytes ++ checksum))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the version byte of an address given the address type and network
|
||||||
|
* @param addressType "pubkey" or "script"
|
||||||
|
* @param isTestnet Boolean
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private def findVersionByte(addressType : String, isTestnet : Boolean) : Byte = isTestnet match {
|
||||||
|
case true => if (addressType == "pubkey") TestNet3.p2pkhNetworkByte else TestNet3.p2shNetworkByte
|
||||||
|
case false => if (addressType == "pubkey") MainNet.p2pkhNetworkByte else MainNet.p2shNetworkByte
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes a private key into Wallet Import Format (WIF)
|
* Encodes a private key into Wallet Import Format (WIF)
|
||||||
* https://en.bitcoin.it/wiki/Wallet_import_format
|
* https://en.bitcoin.it/wiki/Wallet_import_format
|
||||||
|
@ -70,7 +70,7 @@ class ECPrivateKeyTest extends FlatSpec with MustMatchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it must "create a fresh private key" in {
|
it must "create a fresh private key" in {
|
||||||
ECPrivateKey.apply().isInstanceOf[ECPrivateKey] must be (true)
|
ECPrivateKey() must not equal (ECPrivateKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
package org.bitcoins.core.crypto
|
||||||
|
|
||||||
|
import org.bitcoins.core.number.UInt32
|
||||||
|
import org.bitcoins.core.policy.Policy
|
||||||
|
import org.bitcoins.core.protocol.script._
|
||||||
|
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||||
|
import org.bitcoins.core.script.ScriptProgram
|
||||||
|
import org.bitcoins.core.script.crypto.SIGHASH_ALL
|
||||||
|
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
||||||
|
import org.bitcoins.core.script.result.ScriptOk
|
||||||
|
import org.bitcoins.core.util.{BitcoinSLogger, TransactionTestUtil}
|
||||||
|
import org.scalatest.{FlatSpec, MustMatchers}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by chris on 7/21/16.
|
||||||
|
*/
|
||||||
|
class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with BitcoinSLogger {
|
||||||
|
|
||||||
|
"TransactionSignatureCreator" must "create a signature for a scriptSignature in a transaction" in {
|
||||||
|
//this is a signed tx, but since TransactionSignatureSerializer removes scriptSigs, it will work for testing this
|
||||||
|
//from fe6ef8e20a9ca9cb5d59cb1c0f30eff2b23be2e3cc2bf4b4cfff519414e9a300 on testnet
|
||||||
|
/* val expectedSig = ECDigitalSignature("30440220357864ae2beba3d6ec34c0ce42262c1c12939502f0f8f4bd338c9d8b307593420220656687c327589dc3e464700fa7b784c7efc2b465c627a60c2f1ce402d05fc39d01")
|
||||||
|
val rawTx = "01000000021d50bf7c05b6169ea8d8fb5b79dd2978bbd2ac756a656a777279da43b19fd9d9000000006b4830450221008f2c818a55045a1c9dcda54fcd5b6377f5d09723a9ccd8c71df76ee4bdf7c16802201817cbd71d8148a5d53b11d33c9c58ad1086fe7ddf308da2a7cceb7d85df293e01210381c82dc267a958be06f1c920dc635bcd191d698c167e67a45a882a551c57ce1dfeffffffd4a6a37abfe003a9d10155df215e662f88d5b878b908d1a3772a9fbd195d008d010000006a4730440220357864ae2beba3d6ec34c0ce42262c1c12939502f0f8f4bd338c9d8b307593420220656687c327589dc3e464700fa7b784c7efc2b465c627a60c2f1ce402d05fc39d0121036301d848aec3dfc47789a63ee3c85c6d3bf757162ef77cb1580981b422838ed7feffffff0200e1f505000000001976a9146d39bac171d0bf450698fa0ebd93f51e79dcb6ac88ac35a96d00000000001976a914e11753f499ac7a910148e53156ab273557ed517e88acd6090b00"
|
||||||
|
val transaction = Transaction(rawTx)
|
||||||
|
val scriptPubKey = ScriptPubKey("76a914d7b4717a934386601ac3f980d01b48c83b8a0b4b88ac")
|
||||||
|
val txSignatureComponent = TransactionSignatureComponent(transaction,UInt32.one,scriptPubKey, Policy.standardScriptVerifyFlags)
|
||||||
|
val privateKey = ECPrivateKey.fromWIFToPrivateKey("cTPg4Zc5Jis2EZXy3NXShgbn487GWBTapbU63BerLDZM3w2hQSjC")
|
||||||
|
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,scriptPubKey, SIGHASH_ALL())
|
||||||
|
txSignature.r must be (expectedSig.r)
|
||||||
|
txSignature.s must be (expectedSig.s)
|
||||||
|
txSignature.hex must be (expectedSig.hex)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
it must "create a p2pk scriptPubKey, create a crediting tx for scriptPubKey, " +
|
||||||
|
"then create spending tx and make sure it evaluates to true in the interpreter" in {
|
||||||
|
val privateKey = ECPrivateKey()
|
||||||
|
val publicKey = privateKey.publicKey
|
||||||
|
val scriptPubKey = P2PKScriptPubKey(publicKey)
|
||||||
|
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
|
||||||
|
val scriptSig = P2PKScriptSignature(EmptyDigitalSignature)
|
||||||
|
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
|
||||||
|
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
|
||||||
|
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,scriptPubKey, SIGHASH_ALL())
|
||||||
|
|
||||||
|
//add the signature to the scriptSig instead of having an empty scriptSig
|
||||||
|
val signedScriptSig = P2PKScriptSignature(txSignature)
|
||||||
|
val (signedTx,_) = TransactionTestUtil.buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
|
||||||
|
|
||||||
|
//run it through the interpreter
|
||||||
|
val program = ScriptProgram(signedTx,scriptPubKey,inputIndex, Policy.standardScriptVerifyFlags)
|
||||||
|
|
||||||
|
val result = ScriptInterpreter.run(program)
|
||||||
|
|
||||||
|
result must be (ScriptOk)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "create a p2pkh scriptPubKey, create a crediting tx for the scriptPubkey" +
|
||||||
|
"then create a spending tx and make sure it evaluates to true in the interpreter" in {
|
||||||
|
/* val privateKey = ECPrivateKey()
|
||||||
|
val publicKey = privateKey.publicKey
|
||||||
|
val scriptPubKey = P2PKHScriptPubKey(publicKey)
|
||||||
|
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
|
||||||
|
val scriptSig = P2PKHScriptSignature(EmptyDigitalSignature,publicKey)
|
||||||
|
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
|
||||||
|
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
|
||||||
|
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,scriptPubKey, SIGHASH_ALL())
|
||||||
|
|
||||||
|
//add the signature to the scriptSig instead of having an empty scriptSig
|
||||||
|
val signedScriptSig = P2PKHScriptSignature(txSignature,publicKey)
|
||||||
|
val (signedTx,_) = TransactionTestUtil.buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
|
||||||
|
|
||||||
|
//run it through the interpreter
|
||||||
|
val program = ScriptProgram(signedTx,scriptPubKey,inputIndex, Policy.standardScriptVerifyFlags)
|
||||||
|
|
||||||
|
val result = ScriptInterpreter.run(program)
|
||||||
|
|
||||||
|
result must be (ScriptOk)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "create a multisignature scriptPubKey, create a crediting tx for the scriptPubkey, " +
|
||||||
|
"then create a spending tx and make sure it evaluates to true in the interpreter" in {
|
||||||
|
val privateKey = ECPrivateKey()
|
||||||
|
val publicKey = privateKey.publicKey
|
||||||
|
val scriptPubKey = MultiSignatureScriptPubKey(1,Seq(publicKey))
|
||||||
|
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
|
||||||
|
val scriptSig = MultiSignatureScriptSignature(Seq(EmptyDigitalSignature))
|
||||||
|
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
|
||||||
|
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
|
||||||
|
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,scriptPubKey, SIGHASH_ALL())
|
||||||
|
|
||||||
|
//add the signature to the scriptSig instead of having an empty scriptSig
|
||||||
|
val signedScriptSig = MultiSignatureScriptSignature(Seq(txSignature))
|
||||||
|
val (signedTx,_) = TransactionTestUtil.buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
|
||||||
|
|
||||||
|
//run it through the interpreter
|
||||||
|
val program = ScriptProgram(signedTx,scriptPubKey,inputIndex, Policy.standardScriptVerifyFlags)
|
||||||
|
|
||||||
|
val result = ScriptInterpreter.run(program)
|
||||||
|
|
||||||
|
result must be (ScriptOk)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -100,28 +100,15 @@ class Base58Test extends FlatSpec with MustMatchers with BitcoinSLogger {
|
|||||||
for {
|
for {
|
||||||
testCase <- testCases
|
testCase <- testCases
|
||||||
} yield {
|
} yield {
|
||||||
testCase must be (Base58ValidTestCaseImpl(testCase.addressOrWIFPrivKey, testCase.hashOrPrivKey, testCase.configParams))
|
|
||||||
//if testCase is an Address, it must have a valid base58 representation
|
//if testCase is an Address, it must have a valid base58 representation
|
||||||
if (testCase.addressOrWIFPrivKey.isLeft) {
|
if (testCase.addressOrWIFPrivKey.isLeft) {
|
||||||
Base58.isValid(testCase.addressOrWIFPrivKey.left.get.value) must be (true)
|
Base58.isValid(testCase.addressOrWIFPrivKey.left.get.value) must be (true)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//logger.debug(testCase.addressOrWIFPrivKey.right.get + " should be true, but showed as " + Base58.isValid(testCase.addressOrWIFPrivKey.right.get))
|
logger.info(testCase.addressOrWIFPrivKey.right.get + " should be true, but showed as " + Base58.isValid(testCase.addressOrWIFPrivKey.right.get))
|
||||||
Base58.isValid(testCase.addressOrWIFPrivKey.right.get) must be (true)
|
Base58.isValid(testCase.addressOrWIFPrivKey.right.get) must be (true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//first, second and 48th test cases:
|
|
||||||
testCases.head must be (Base58ValidTestCaseImpl(Left(Address("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")),
|
|
||||||
Left(Sha256Hash160Digest("65a16059864a2fdbc7c99a4723a8395bc6f188eb")),
|
|
||||||
ConfigParamsImpl(Left("pubkey"), false, false)))
|
|
||||||
|
|
||||||
testCases(1) must be (Base58ValidTestCaseImpl(Left(Address("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou")),
|
|
||||||
Left(Sha256Hash160Digest("74f209f6ea907e2ea48f74fae05782ae8a665257")), ConfigParamsImpl(Left("script"), false, false)))
|
|
||||||
|
|
||||||
testCases(47) must be (Base58ValidTestCaseImpl(Right("cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h"),
|
|
||||||
Right(ECPrivateKey("0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c")),
|
|
||||||
ConfigParamsImpl(Right(true), true, true)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it must "read base58_keys_invalid.json and return each as an invalid base58 string" in {
|
it must "read base58_keys_invalid.json and return each as an invalid base58 string" in {
|
||||||
@ -152,7 +139,7 @@ class Base58Test extends FlatSpec with MustMatchers with BitcoinSLogger {
|
|||||||
Base58.decodeCheck("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou").isSuccess must be (true)
|
Base58.decodeCheck("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou").isSuccess must be (true)
|
||||||
}
|
}
|
||||||
|
|
||||||
it must "encode a private key to WIF, then decode it from WIF to hex private key" in {
|
it must "decode a private key from WIF, then encode that decoded private key to WIF" in {
|
||||||
val WIF = "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr"
|
val WIF = "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr"
|
||||||
val privateKey = ECPrivateKey.fromWIFToPrivateKey(WIF)
|
val privateKey = ECPrivateKey.fromWIFToPrivateKey(WIF)
|
||||||
Base58.encodePrivateKeyToWIF(privateKey, false, false) must be (WIF)
|
Base58.encodePrivateKeyToWIF(privateKey, false, false) must be (WIF)
|
||||||
|
@ -9,7 +9,7 @@ import org.bitcoins.core.crypto.{ECPrivateKey}
|
|||||||
*/
|
*/
|
||||||
trait CryptoTestUtil {
|
trait CryptoTestUtil {
|
||||||
def privateKeyBase58 = "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT"
|
def privateKeyBase58 = "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT"
|
||||||
def privateKeyBytes = Base58.decode(privateKeyBase58)
|
def privateKeyBytes = Base58.decodeCheck(privateKeyBase58).get
|
||||||
def privateKeyHex = BitcoinSUtil.encodeHex(privateKeyBytes)
|
def privateKeyHex = BitcoinSUtil.encodeHex(privateKeyBytes)
|
||||||
def bitcoinjDumpedPrivateKey = new DumpedPrivateKey(BitcoinJTestUtil.params,privateKeyBase58)
|
def bitcoinjDumpedPrivateKey = new DumpedPrivateKey(BitcoinJTestUtil.params,privateKeyBase58)
|
||||||
def bitcoinjPrivateKey = bitcoinjDumpedPrivateKey.getKey
|
def bitcoinjPrivateKey = bitcoinjDumpedPrivateKey.getKey
|
||||||
|
Loading…
Reference in New Issue
Block a user