Skip to content

Commit

Permalink
Merge pull request #6 from jeton-tech/price
Browse files Browse the repository at this point in the history
threshold module and example
  • Loading branch information
vinarmani authored May 15, 2020
2 parents d7b512d + 51af633 commit 74d153a
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 6 deletions.
116 changes: 116 additions & 0 deletions examples/threshold.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const jeton = require('../index')
const PrivateKey = jeton.PrivateKey
const ThresholdMessage = jeton.threshold.Message
const OutputScript = jeton.threshold.OutputScript
const Transaction = jeton.Transaction
const Signature = jeton.Signature

// Create keypairs for 2 players and an oracle
const priv1 = new PrivateKey("KzwmMwHjbmRRdtwVUowKpYmpnJmMaVyGTwYLmh2qmiWcqgd7W9fG")
const pub1 = priv1.toPublicKey()
const priv2 = new PrivateKey("KzyhHmmxwFbv2Mo8bQsJQwXhrCgAtjsCmuqBBmGZrcjfTn1Xvzw1")
const pub2 = priv2.toPublicKey()

var utxoForPub2 = new Transaction.UnspentOutput({
txid:
'b2b671f0cb3d7710b5d8a8420fff14b18173de876da710438dafa0ae6e8f5357',
vout: 1,
satoshis: 10200,
scriptPubKey: '76a9149383fa6588a176c2592cb2f4008d779293246adb88ac'
})

var utxoForPub1 = new Transaction.UnspentOutput({
txid:
'ee874221a431cf09d3373c4b9ffbb1e8fe80526d4304695e2f97541fc084c8f4',
vout: 1,
satoshis: 10200,
scriptPubKey: '76a914b011100d12d0537232692b3c113be5a8f505395588ac'
})

// Create array of UTXO arrays
var splitUtxos = [utxoForPub1, utxoForPub2]

const oraclePriv = new PrivateKey('L5FDo3MEb2QNs2aQJ5DVGSDE5eBzVsgZny15Ri649RjysWAeLkTs')
const oraclePub = oraclePriv.toPublicKey();
console.log('user 1 pubkey', pub1.toString())
console.log('user 2 pubkey', pub2.toString())
console.log('oracle pubkey', oraclePub.toString())

const blockheight = 660211
const price = 220.2098

// This is the ASM for the redeem script if done in CashScript
// 0556 f2120a 029207e74ee73342f9af30859c03f684da444344d957a949c768316519f9df6a36 02c3e42dd2a3806f1bc9a9f32c3a97b872ed03ce8a779242b8bf2dba636ce655b0 OP_6 OP_PICK OP_4 OP_SPLIT OP_DROP OP_BIN2NUM OP_7 OP_PICK OP_4 OP_SPLIT OP_NIP OP_BIN2NUM OP_OVER OP_5 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP OP_3 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_3 OP_ROLL OP_4 OP_ROLL OP_3 OP_ROLL OP_CHECKDATASIGVERIFY OP_CHECKSIG

let message = new ThresholdMessage(blockheight, price).message
console.log('message', message)
let oracleSig = Signature.signCDS(message, oraclePriv)
// console.log('sig DER', oracleSig.toDER())
let verify = ThresholdMessage.verifySignature(message, oracleSig, oraclePub)
// console.log('signature verified?', verify)

// Create the output script
var outputScriptData = {
threshold: 218,
blockheight: blockheight,
oraclePubKey: oraclePub,
parties: {
gt: {pubKey: pub1},
lte: {pubKey: pub2}
}
}

outScript = new OutputScript(outputScriptData)

let outputScript = outScript.toScript()

// console.log(outputScript)

let outScriptHex = outScript.toScript().toHex()

// console.log('destination P2SH', outScript.toAddress())

// Set miner fee and total amount to send (will be split between UTXOs from useUTXOs array)
var splitUtxoMinerFee = 200
var amountToSend = 20000

var splitTxSendAmount = (amountToSend / splitUtxos.length)

// Create two separate transactions from players 2 and 3 to fund the escrow
var sighash = (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID | Signature.SIGHASH_ANYONECANPAY)
var txArray = []
for (let i = 0; i < splitUtxos.length; i++) {
txArray[i] = new Transaction()
.from(splitUtxos[i]) // Feed information about what unspent outputs one can use
.toP2SH(outScript, amountToSend)
.sign([priv1, priv2], sighash) // Signs all the inputs it can
}

// Combine the transactions by merging the inputs
var fundTx = Transaction.mergeTransactionInputs(txArray)

// var itx = new Transaction(fundTx.toString())

// console.log('fundTx', itx.toObject())

// Now spend the escrow transaction...

var escrowUtxo = Transaction.utxoFromTxOutput(fundTx, 0)

// Make Transaction from escrow UTXO
sighash = (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID)

var spendTx = new Transaction()
.from(escrowUtxo)
.to(priv1.toAddress(), 19000)
.lockUntilBlockHeight(660211) // Must be after the blockheight in the script

//console.log(spendTx.toObject())

// Sign CDS input at index 0 as player 2
spendTx.signThreshold(0, priv1, message, oracleSig, outScript.toScript(), sighash)

// console.log(spendTx.toObject())
console.log('estimated size', spendTx._estimateSize())
console.log('verify tx full sig', spendTx.verify())
console.log('jeton signature verified?', spendTx.verifyScriptSig(0))
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ jeton.escrow = {}
jeton.escrow.InputScript = require('./lib/escrow/InputScript')
jeton.escrow.OutputScript = require('./lib/escrow/OutputScript')

// Threshold
jeton.threshold = require('./lib/threshold')
//jeton.escrow.InputScript = require('./lib/escrow/InputScript')
//jeton.escrow.OutputScript = require('./lib/escrow/OutputScript')

module.exports = jeton
39 changes: 39 additions & 0 deletions lib/Transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ const Sighash = Transaction.Sighash
const BN = bitcore.crypto.BN
const Signature = require('./Signature')
const InputScript = require('./escrow/InputScript')
const ThresholdInputScript = require('./threshold/InputScript')


Transaction.P2SHFlags = Interpreter.SCRIPT_VERIFY_P2SH
| Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY
| Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID
| Interpreter.SCRIPT_ENABLE_CHECKDATASIG
| Interpreter.SCRIPT_VERIFY_STRICTENC
Expand Down Expand Up @@ -75,6 +77,43 @@ Transaction.prototype.signEscrow = function (inputIndex, winnerPrivKey, refMsg,
}


/**
* Signs an threshold escrow transaction as defined in jeton.threshold.OutputScript
*
* @param {number} inputIndex - index of the input to sign
* @param {PrivateKey} winnerPrivKey - the private key of the escrow beneficiary
* @param {string} oracleMsg - the message for the outcome
* @param {Signature} oracleSig - the signature for the message and referee public key
* @param {Script} subscript - the non-P2SH (original) scriptPubKey
* @param {number} sighash - the type of signature
*
* @returns {Transaction}
*/
Transaction.prototype.signThreshold = function (inputIndex, winnerPrivKey, oracleMsg, oracleSig, subscript, sighash) {
sighash = sighash || (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID)

let input = this.toObject()['inputs'][inputIndex]
let p2pkhSig = Sighash.sign(this, winnerPrivKey, sighash, inputIndex, subscript, BN.fromNumber(input.output.satoshis), Transaction.P2SHFlags)

let winnerScript = Script.buildPublicKeyHashIn(winnerPrivKey.toPublicKey(), p2pkhSig, sighash)

// Generate scriptSig
let inputScriptData = {
oracleSig: oracleSig,
message: oracleMsg,
winnerScript: winnerScript,
outputScript: subscript
}

inScript = new ThresholdInputScript(inputScriptData)

// Set scriptSig for inputIndex
this.inputs[inputIndex].setScript(inScript.toScript())

return this
}


/**
* Verify that a P2SH input is properly signed
*
Expand Down
50 changes: 50 additions & 0 deletions lib/threshold/InputScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const bitcore = require('bitcore-lib-cash')
const Hash = bitcore.crypto.Hash
const Script = bitcore.Script
const PublicKey = bitcore.PublicKey


/**
* Instantiate an object to create escrow scriptSig
*
* @param {object} data - The encoded data in various formats
* @param {Signature} data.oracleSig
* @param {Buffer} data.message
* @param {Script} data.winnerScript - a P2PKH scriptSig for the transaction signed by escrow beneficiary
* @param {Script} data.outputScript - The original (non-P2SH) scriptPubKey for this input
*
* @constructor
*/
var InputScript = function (data) {

this.oracleSig = data.oracleSig
this.message = data.message
this.winnerScript = data.winnerScript
this.outputScript = data.outputScript
}


/**
* @returns {Script}
*/
InputScript.prototype.toScript = function () {
let outputBuf = this.outputScript.toBuffer()
let inScript = Script()
.add(this.winnerScript)
.add(this.oracleSig.toBuffer())
.add(this.message)
.add(outputBuf)

return inScript
}


/**
* @returns {Buffer}
*/
InputScript.prototype.toBuffer = function () {
let outScript = this.toScript()
return outScript.toBuffer()
}

module.exports = InputScript
47 changes: 47 additions & 0 deletions lib/threshold/Message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const bitcore = require('bitcore-lib-cash')
const Address = bitcore.Address
const Hash = bitcore.crypto.Hash
const ECDSA = bitcore.crypto.ECDSA
const Signature = bitcore.crypto.Signature
const Script = bitcore.Script
const PrivateKey = bitcore.PrivateKey
const PublicKey = bitcore.PublicKey
const ScriptNumber = require('./ScriptNumber')

var Message = function (blockHeight, thresholdValue) {

this.blockHeight = blockHeight
this.threshold = Math.ceil(thresholdValue)
this.message = this.createMessage()
}

/**
* Encode a blockHeight and threshold amount into a byte sequence of 8 bytes (4 bytes per value)
* This is compatible with the CashScript PriceOracle.ts
* https://github.com/Bitcoin-com/cashscript/blob/master/examples/PriceOracle.ts
*
*
*/
Message.prototype.createMessage = function () {
const lhs = Buffer.alloc(4, 0);
const rhs = Buffer.alloc(4, 0);
ScriptNumber.encode(this.blockHeight).copy(lhs);
ScriptNumber.encode(this.threshold).copy(rhs);
console.log('decoded price', ScriptNumber.decode(rhs, 4, false))
console.log('decoded threshold', ScriptNumber.decode(lhs, 4, false))
return Buffer.concat([lhs, rhs]);
}

Message.signMessage = function(message, privkey){
return new ECDSA().set({
hashbuf: Hash.sha256(message),
privkey: privkey
}).signRandomK().sig;
}

Message.verifySignature = function(message, sig, pubkey) {
let msgHash = Hash.sha256(message)
return ECDSA.verify(msgHash, sig, pubkey)
}

module.exports = Message
Loading

0 comments on commit 74d153a

Please sign in to comment.