Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 57 additions & 47 deletions js/coin.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
for (var i = 0; i < pubkeys.length; ++i) {
s.writeBytes(Crypto.util.hexToBytes(pubkeys[i]));
}
s.writeOp(81 + pubkeys.length - 1); //OP_1
s.writeOp(81 + pubkeys.length - 1); //OP_1
s.writeOp(174); //OP_CHECKMULTISIG
var x = ripemd160(Crypto.SHA256(s.buffer, {asBytes: true}), {asBytes: true});
x.unshift(coinjs.multisig);
Expand All @@ -144,7 +144,7 @@

/* new time locked address, provide the pubkey and time necessary to unlock the funds.
when time is greater than 500000000, it should be a unix timestamp (seconds since epoch),
otherwise it should be the block height required before this transaction can be released.
otherwise it should be the block height required before this transaction can be released.

may throw a string on failure!
*/
Expand Down Expand Up @@ -188,7 +188,7 @@
/* create a new segwit bech32 encoded address */
coinjs.bech32Address = function(pubkey){
var program = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), {asBytes: true}), {asBytes: true});
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return {'address':address, 'type':'bech32', 'redeemscript':Crypto.util.bytesToHex(program)};
}

Expand Down Expand Up @@ -285,11 +285,11 @@
alert("Stealth Multisig is currently not supported!");
return false;
};

o.spendkey = Crypto.util.bytesToHex(front.slice(36, 69));
o.m = front[69];
o.prefixlen = front[70];

if (o.prefixlen > 0) {
alert("Stealth Address Prefixes are currently not supported!");
return false;
Expand Down Expand Up @@ -366,7 +366,7 @@
ret.push(hrp.charCodeAt(p) & 31);
}
return ret;
}
}

coinjs. bech32_verifyChecksum = function(hrp, data) {
return coinjs.bech32_polymod(coinjs.bech32_hrpExpand(hrp).concat(data)) === 1;
Expand All @@ -378,7 +378,7 @@
var ret = [];
for (var p = 0; p < 6; ++p) {
ret.push((mod >> 5 * (5 - p)) & 31);
}
}
return ret;
}

Expand Down Expand Up @@ -972,39 +972,39 @@
r.addstealth = function(stealth, value){
var ephemeralKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(coinjs.newPrivkey()));
var curve = EllipticCurve.getSECCurveByName("secp256k1");

var p = EllipticCurve.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
var a = BigInteger.ZERO;
var b = EllipticCurve.fromHex("7");
var calccurve = new EllipticCurve.CurveFp(p, a, b);

var ephemeralPt = curve.getG().multiply(ephemeralKeyBigInt);
var scanPt = calccurve.decodePointHex(stealth.scankey);
var sharedPt = scanPt.multiply(ephemeralKeyBigInt);
var stealthindexKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.SHA256(sharedPt.getEncoded(true), {asBytes: true}));

var stealthindexPt = curve.getG().multiply(stealthindexKeyBigInt);
var spendPt = calccurve.decodePointHex(stealth.spendkey);
var addressPt = spendPt.add(stealthindexPt);

var sendaddress = coinjs.pubkey2address(Crypto.util.bytesToHex(addressPt.getEncoded(true)));


var OPRETBytes = [6].concat(Crypto.util.randomBytes(4)).concat(ephemeralPt.getEncoded(true)); // ephemkey data
var q = coinjs.script();
q.writeOp(106); // OP_RETURN
q.writeBytes(OPRETBytes);
v = {};
v.value = 0;
v.script = q;

this.outs.push(v);

var o = {};
o.value = new BigInteger('' + Math.round((value*1) * 1e8), 10);
var s = coinjs.script();
o.script = s.spendToScript(sendaddress);

return this.outs.push(o);
}

Expand Down Expand Up @@ -1055,7 +1055,7 @@
var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;

if(segwit){
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
It is not standard and removed during the signing procedure. */

s = coinjs.script();
Expand Down Expand Up @@ -1176,10 +1176,10 @@

/* generate a segwit transaction hash to sign from a transaction input */
r.transactionHashSegWitV0 = function(index, sigHashType){
/*
/*
Notice: coinb.in by default, deals with segwit transactions in a non-standard way.
Segwit transactions require that input values are included in the transaction hash.
To save wasting resources and potentially slowing down this service, we include the amount with the
To save wasting resources and potentially slowing down this service, we include the amount with the
redeem script to generate the transaction hash and remove it after its signed.
*/

Expand All @@ -1190,7 +1190,7 @@
}

if(extract['value'] == -1){
return {'result':0, 'fail':'value', 'response':'unable to generate a valid segwit hash without a value'};
return {'result':0, 'fail':'value', 'response':'unable to generate a valid segwit hash without a value'};
}

var scriptcode = Crypto.util.hexToBytes(extract['script']);
Expand All @@ -1216,21 +1216,21 @@
var version = coinjs.numToBytes(parseInt(this.version), 4);

var bufferTmp = [];
if(!(sigHashType >= 80)){ // not sighash anyonecanpay
if(!(sigHashType >= 80)){ // not sighash anyonecanpay
for(var i = 0; i < this.ins.length; i++){
bufferTmp = bufferTmp.concat(Crypto.util.hexToBytes(this.ins[i].outpoint.hash).reverse());
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].outpoint.index, 4));
}
}
var hashPrevouts = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;
var hashPrevouts = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;

var bufferTmp = [];
if(!(sigHashType >= 80) && sigHashType != 2 && sigHashType != 3){ // not sighash anyonecanpay & single & none
for(var i = 0; i < this.ins.length; i++){
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].sequence, 4));
}
}
var hashSequence = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;
var hashSequence = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;

var outpoint = Crypto.util.hexToBytes(this.ins[index].outpoint.hash).reverse();
outpoint = outpoint.concat(coinjs.numToBytes(this.ins[index].outpoint.index, 4));
Expand All @@ -1256,7 +1256,7 @@
var locktime = coinjs.numToBytes(this.lock_time, 4);
var sighash = coinjs.numToBytes(sigHashType, 4);

var buffer = [];
var buffer = [];
buffer = buffer.concat(version);
buffer = buffer.concat(hashPrevouts);
buffer = buffer.concat(hashSequence);
Expand All @@ -1281,7 +1281,7 @@
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48 && this.ins[index].script.chunks[1].length == 5 && this.ins[index].script.chunks[1][1]==177){//OP_CHECKLOCKTIMEVERIFY
// hodl script (signed)
return {'type':'hodl', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48){
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48){
// regular scriptPubkey (probably signed)
return {'type':'scriptpubkey', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if(this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177){//OP_CHECKLOCKTIMEVERIFY
Expand Down Expand Up @@ -1314,7 +1314,7 @@
}

/* generate a signature from a transaction hash */
r.transactionSig = function(index, wif, sigHashType, txhash){
r.transactionSig = function(index, wif, sigHashType, txhash, lowR){

function serializeSig(r, s) {
var rBa = r.toByteArraySigned();
Expand Down Expand Up @@ -1342,14 +1342,22 @@
var curve = EllipticCurve.getSECCurveByName("secp256k1");
var key = coinjs.wif2privkey(wif);
var priv = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(key['privkey']));
var bits255 = new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
var n = curve.getN();
var G = curve.getG();
var e = BigInteger.fromByteArrayUnsigned(hash);
var badrs = 0
do {
var k = this.deterministicK(wif, hash, badrs);
var G = curve.getG();
var Q = G.multiply(k);
var r = Q.getX().toBigInteger().mod(n);
var extraEntropy = undefined;
var counter = 0;
do {
if (counter === 1) extraEntropy = new Array(32).fill(0);
if (counter > 0) extraEntropy[0] = counter
var k = this.deterministicK(wif, hash, badrs, extraEntropy);
var Q = G.multiply(k);
var r = Q.getX().toBigInteger().mod(n);
counter++
} while (lowR && r.compareTo(bits255) > 0);
var s = k.modInverse(n).multiply(e.add(priv.multiply(r))).mod(n);
badrs++
} while (r.compareTo(BigInteger.ZERO) <= 0 || s.compareTo(BigInteger.ZERO) <= 0);
Expand All @@ -1370,7 +1378,7 @@
}

// https://tools.ietf.org/html/rfc6979#section-3.2
r.deterministicK = function(wif, hash, badrs) {
r.deterministicK = function(wif, hash, badrs, extraEntropy) {
// if r or s were invalid when this function was used in signing,
// we do not want to actually compute r, s here for efficiency, so,
// we can increment badrs. explained at end of RFC 6979 section 3.2
Expand All @@ -1381,10 +1389,12 @@

// some necessary things out of the way for clarity.
badrs = badrs || 0;
extraEntropy = extraEntropy || []
var key = coinjs.wif2privkey(wif);
var x = Crypto.util.hexToBytes(key['privkey'])
var curve = EllipticCurve.getSECCurveByName("secp256k1");
var N = curve.getN();
var bx = x.concat(hash).concat(extraEntropy)

// Step: a
// hash is a byteArray of the message digest. so h1 == hash in our case
Expand All @@ -1396,13 +1406,13 @@
var k = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

// Step: d
k = Crypto.HMAC(Crypto.SHA256, v.concat([0]).concat(x).concat(hash), k, { asBytes: true });
k = Crypto.HMAC(Crypto.SHA256, v.concat([0]).concat(bx), k, { asBytes: true });

// Step: e
v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true });

// Step: f
k = Crypto.HMAC(Crypto.SHA256, v.concat([1]).concat(x).concat(hash), k, { asBytes: true });
k = Crypto.HMAC(Crypto.SHA256, v.concat([1]).concat(bx), k, { asBytes: true });

// Step: g
v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true });
Expand Down Expand Up @@ -1435,7 +1445,7 @@
r.signinput = function(index, wif, sigHashType){
var key = coinjs.wif2pubkey(wif);
var shType = sigHashType || 1;
var signature = this.transactionSig(index, wif, shType);
var signature = this.transactionSig(index, wif, shType, null, true);
var s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(signature));
s.writeBytes(Crypto.util.hexToBytes(key['pubkey']));
Expand All @@ -1446,15 +1456,15 @@
/* signs a time locked / hodl input */
r.signhodl = function(index, wif, sigHashType){
var shType = sigHashType || 1;
var signature = this.transactionSig(index, wif, shType);
var signature = this.transactionSig(index, wif, shType, null, true);
var redeemScript = this.ins[index].script.buffer
var s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(signature));
s.writeBytes(redeemScript);
this.ins[index].script = s;
return true;
}

/* sign a multisig input */
r.signmultisig = function(index, wif, sigHashType){

Expand All @@ -1465,12 +1475,12 @@
}
return r;
}

function scriptListSigs(scriptSig){
var r = {};
var c = 0;
if (scriptSig.chunks[0]==0 && scriptSig.chunks[scriptSig.chunks.length-1][scriptSig.chunks[scriptSig.chunks.length-1].length-1]==174){
for(var i=1;i<scriptSig.chunks.length-1;i++){
for(var i=1;i<scriptSig.chunks.length-1;i++){
if (scriptSig.chunks[i] != 0){
c++;
r[c] = scriptSig.chunks[i];
Expand All @@ -1487,7 +1497,7 @@

var shType = sigHashType || 1;
var sighash = Crypto.util.hexToBytes(this.transactionHash(index, shType));
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType));
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType, null, true));

sigsList[coinjs.countObject(sigsList)+1] = signature;

Expand Down Expand Up @@ -1524,11 +1534,11 @@
if(txhash.result == 1){

var segwitHash = Crypto.util.hexToBytes(txhash.hash);
var signature = this.transactionSig(index, wif, shType, segwitHash);
var signature = this.transactionSig(index, wif, shType, segwitHash, true);

// remove any non standard data we store, i.e. input value
var script = coinjs.script();
script.writeBytes(this.ins[index].script.chunks[0]);
script.writeBytes(this.ins[index].script.chunks[0]);
this.ins[index].script = script;

if(!coinjs.isArray(this.witness)){
Expand All @@ -1537,9 +1547,9 @@

this.witness.push([signature, wif2['pubkey']]);

/* attempt to reorder witness data as best as we can.
data can't be easily validated at this stage as
we dont have access to the inputs value and
/* attempt to reorder witness data as best as we can.
data can't be easily validated at this stage as
we dont have access to the inputs value and
making a web call will be too slow. */

var witness_order = [];
Expand Down Expand Up @@ -1861,7 +1871,7 @@

var bytes = bi.toByteArrayUnsigned();
while (leadingZerosNum-- > 0) bytes.unshift(0);
return bytes;
return bytes;
}

/* raw ajax function to avoid needing bigger frame works like jquery, mootools etc */
Expand Down Expand Up @@ -1909,7 +1919,7 @@

coinjs.numToBytes = function(num,bytes) {
if (typeof bytes === "undefined") bytes = 8;
if (bytes == 0) {
if (bytes == 0) {
return [];
} else if (num == -1){
return Crypto.util.hexToBytes("ffffffffffffffff");
Expand All @@ -1919,7 +1929,7 @@
}

coinjs.numToByteArray = function(num) {
if (num <= 256) {
if (num <= 256) {
return [num];
} else {
return [num % 256].concat(coinjs.numToByteArray(Math.floor(num / 256)));
Expand Down
Loading