mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 09:52:09 +01:00
Implementing OP_RIPEMD160, OP_SHA256, OP_HASH256 script operations, fixing bug inside of CryptoUtil with serializing to and from hex
This commit is contained in:
parent
a9926e6cba
commit
d2cf8df898
@ -12,17 +12,62 @@ import org.scalacoin.util.{CryptoUtil, ScalacoinUtil}
|
||||
*/
|
||||
trait CryptoInterpreter extends ScalacoinUtil {
|
||||
|
||||
def hash160(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.headOption.isDefined, "The top of the stack must be defined")
|
||||
/**
|
||||
* The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opHash160(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.headOption.isDefined, "The top of the stack must be defined for OP_HASH160")
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_HASH160, "Script operation must be OP_HASH160")
|
||||
val stackTop = program.stack.head
|
||||
val hash = stackTop match {
|
||||
case ScriptConstantImpl(x) => CryptoUtil.sha256Hash160(x)
|
||||
case _ => throw new RuntimeException("Stack top should be of type ScriptConstant to call hash160 on it")
|
||||
}
|
||||
val hash = ScriptConstantImpl(ScalacoinUtil.encodeHex(CryptoUtil.sha256Hash160(stackTop.bytes)))
|
||||
ScriptProgramImpl(hash :: program.stack, program.script.tail,program.transaction, program.altStack)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The input is hashed using RIPEMD-160.
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opRipeMd160(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.headOption.isDefined, "The top of the stack must be defined for OP_RIPEMD160")
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_RIPEMD160, "Script operation must be OP_RIPEMD160")
|
||||
val stackTop = program.stack.head
|
||||
val hash = CryptoUtil.ripeMd160(stackTop.bytes)
|
||||
val newStackTop = ScriptConstantImpl(ScalacoinUtil.encodeHex(hash))
|
||||
ScriptProgramImpl(newStackTop :: program.stack.tail, program.script.tail,program.transaction, program.altStack)
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed using SHA-256.
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opSha256(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.headOption.isDefined, "The top of the stack must be defined for OP_SHA256")
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SHA256, "Script operation must be OP_SHA256")
|
||||
val stackTop = program.stack.head
|
||||
val hash = CryptoUtil.sha256(stackTop.bytes)
|
||||
val newStackTop = ScriptConstantImpl(ScalacoinUtil.encodeHex(hash))
|
||||
ScriptProgramImpl(newStackTop :: program.stack.tail, program.script.tail,program.transaction, program.altStack)
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed two times with SHA-256.
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opHash256(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.headOption.isDefined, "The top of the stack must be defined for OP_HASH256")
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_HASH256, "Script operation must be OP_HASH256")
|
||||
val stackTop = program.stack.head
|
||||
val hash = CryptoUtil.doubleSHA256(stackTop.bytes)
|
||||
val newStackTop = ScriptConstantImpl(ScalacoinUtil.encodeHex(hash))
|
||||
ScriptProgramImpl(newStackTop :: program.stack.tail, program.script.tail,program.transaction, program.altStack)
|
||||
}
|
||||
|
||||
/**
|
||||
* The entire transaction's outputs, inputs, and script (from the most
|
||||
* recently-executed OP_CODESEPARATOR to the end) are hashed.
|
||||
@ -70,10 +115,10 @@ trait CryptoInterpreter extends ScalacoinUtil {
|
||||
???
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The input is hashed using SHA-1.
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opSha1(program : ScriptProgram) : ScriptProgram = {
|
||||
|
@ -8,7 +8,7 @@ import org.scalacoin.script.arithmetic._
|
||||
import org.scalacoin.script.bitwise.{OP_EQUAL, BitwiseInterpreter, OP_EQUALVERIFY}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.control._
|
||||
import org.scalacoin.script.crypto.{OP_SHA1, OP_CHECKSIG, OP_HASH160, CryptoInterpreter}
|
||||
import org.scalacoin.script.crypto._
|
||||
import org.scalacoin.script.reserved.NOP
|
||||
import org.scalacoin.script.stack._
|
||||
import org.slf4j.LoggerFactory
|
||||
@ -121,10 +121,12 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
||||
else false
|
||||
|
||||
//crypto operations
|
||||
case OP_HASH160 :: t => loop(hash160(program))
|
||||
case OP_HASH160 :: t => loop(opHash160(program))
|
||||
case OP_CHECKSIG :: t => checkSig(program.stack,program.script,fullScript)
|
||||
case OP_SHA1 :: t => loop(opSha1(program))
|
||||
|
||||
case OP_RIPEMD160 :: t => loop(opRipeMd160(program))
|
||||
case OP_SHA256 :: t => loop(opSha256(program))
|
||||
case OP_HASH256 :: t => loop(opHash256(program))
|
||||
//reserved operations
|
||||
case (nop : NOP) :: t => loop(ScriptProgramImpl(program.stack,t,program.transaction,program.altStack))
|
||||
|
||||
|
@ -4,6 +4,7 @@ import java.security.MessageDigest
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash
|
||||
import org.scalacoin.script.constant.{ScriptConstantImpl, ScriptConstant}
|
||||
import org.spongycastle.crypto.digests.RIPEMD160Digest
|
||||
|
||||
/**
|
||||
* Created by chris on 1/14/16.
|
||||
@ -17,30 +18,43 @@ trait CryptoUtil extends ScalacoinUtil {
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
def sha256Hash160(hex : String) : ScriptConstant = {
|
||||
val bytes = decodeHex(hex)
|
||||
def sha256Hash160(hex : String) : List[Byte] = sha256Hash160(decodeHex(hex))
|
||||
|
||||
def sha256Hash160(bytes : List[Byte]) = {
|
||||
val hash = org.bitcoinj.core.Utils.sha256hash160(bytes.toArray)
|
||||
ScriptConstantImpl(encodeHex(hash))
|
||||
|
||||
hash.toList
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs sha256(sha256(hex))
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
def doubleSHA256(hex : String) : String = {
|
||||
doubleSHA256(decodeHex(hex))
|
||||
}
|
||||
|
||||
def doubleSHA256(hex : String) : List[Byte] = doubleSHA256(decodeHex(hex))
|
||||
/**
|
||||
* Performs sha256(sha256(hex))
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
def doubleSHA256(bytes : List[Byte]) : String = {
|
||||
def doubleSHA256(bytes : List[Byte]) : List[Byte] = {
|
||||
val hash : List[Byte] = Sha256Hash.hashTwice(bytes.toArray).toList
|
||||
encodeHex(hash.reverse)
|
||||
hash
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes sha256(hex)
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
def sha256(hex : String) : List[Byte] = sha256(decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Takes sha256(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def sha256(bytes : List[Byte]) : List[Byte] = {
|
||||
val hash : List[Byte] = Sha256Hash.hash(bytes.toArray).toList
|
||||
hash
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,11 +67,37 @@ trait CryptoUtil extends ScalacoinUtil {
|
||||
|
||||
|
||||
/**
|
||||
* Performs SHA1(str)
|
||||
* Performs SHA1(hex)
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
def sha1(str : String) : List[Byte] = sha1(str.map(_.toByte).toList)
|
||||
def sha1(hex : String) : List[Byte] = sha1(decodeHex(hex))
|
||||
|
||||
|
||||
/**
|
||||
* Performs RIPEMD160(hex)
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
def ripeMd160(hex : String) : List[Byte] = ripeMd160(decodeHex(hex))
|
||||
|
||||
|
||||
/**
|
||||
* Performs RIPEMD160(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def ripeMd160(bytes : List[Byte]) : List[Byte] = {
|
||||
//from this tutorial http://rosettacode.org/wiki/RIPEMD-160#Scala
|
||||
val messageDigest = new RIPEMD160Digest
|
||||
val raw = bytes.toArray
|
||||
messageDigest.update(raw, 0, raw.length)
|
||||
val out = Array.fill[Byte](messageDigest.getDigestSize())(0)
|
||||
messageDigest.doFinal(out, 0)
|
||||
out.toList
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
object CryptoUtil extends CryptoUtil
|
||||
|
@ -13,8 +13,28 @@ class CryptoUtilTest extends FlatSpec with MustMatchers {
|
||||
ScalacoinUtil.encodeHex(hash) must be ("da39a3ee5e6b4b0d3255bfef95601890afd80709")
|
||||
}
|
||||
|
||||
it must "perform the correct SHA-1 hash on a mneomnic seed" in {
|
||||
val str = "The quick brown fox jumps over the lazy dog"
|
||||
ScalacoinUtil.encodeHex(CryptoUtil.sha1(str)) must be ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")
|
||||
|
||||
it must "perform the correct RIPEMD160 on a string" in {
|
||||
val str = ""
|
||||
val expectedDigest = "9c1185a5c5e9fc54612808977ee8f548b2258d31"
|
||||
ScalacoinUtil.encodeHex(CryptoUtil.ripeMd160(str)) must be (expectedDigest)
|
||||
}
|
||||
|
||||
it must "perform a RIPEMD160 on a SHA256 hash to generate a bitcoin address" in {
|
||||
//https://bitcoin.stackexchange.com/questions/37040/ripemd160sha256publickey-where-am-i-going-wrong
|
||||
val str = "ea571f53cb3a9865d3dc74735e0c16643d319c6ad81e199b9c8408cecbcec7bb"
|
||||
val expected = "5238c71458e464d9ff90299abca4a1d7b9cb76ab"
|
||||
ScalacoinUtil.encodeHex(CryptoUtil.ripeMd160(str)) must be (expected)
|
||||
}
|
||||
|
||||
it must "perform a single SHA256 hash" in {
|
||||
val hex = ""
|
||||
val strBytes = ScalacoinUtil.decodeHex(hex)
|
||||
val expected = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
ScalacoinUtil.encodeHex(CryptoUtil.sha256(strBytes)) must be (expected)
|
||||
ScalacoinUtil.encodeHex(CryptoUtil.sha256(hex)) must be (expected)
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user