Permalink
Browse files

Added multisignature tools

  • Loading branch information...
ttutdxh-nubits committed Nov 29, 2015
1 parent ca224de commit bad5f8acc3853394c833fb90e225e68ea84df620
Showing with 206 additions and 40 deletions.
  1. +51 −0 index.html
  2. +100 −38 js/coin.js
  3. +55 −2 js/cointoolkit.js
View
@@ -1251,6 +1251,57 @@ <h4 class="modal-title"><b>Warning High Fee!</b></h4>
</div>
</div>
<!-- warning (fee) modal -->
+
+ <!-- multisig modal -->
+ <div class="modal fade" id="modalMultisig" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+ <h4 class="modal-title">Multisignature checker</h4>
+ </div>
+
+ <div class="modal-body">
+ <p>Here you can see the public keys and the correspondent addresses of the public keys with signing rights on this input.</p>
+
+ <p>Signatures already found in the current transaction are shown in green.</p>
+ <div class="row">
+ <div class="col-md-12">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th class="col-md-3">Address</th>
+ <th class="col-md-5">Pubkey</th>
+ <th class="col-md-4">Identity <span class="text-muted">(if <a href="known-pubkeys.js" target="_blank">known</a>)</span></th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <br>
+ <div class="row">
+ <div class="col-md-12 combine">
+ <label><abbr title="When signers do not sign sequentially, and they end with multiple versions or forks of the same transaction. This only works if the rest of the transaction is the exactly the same">Combine with the same transaction signed by other pubkeys:</abbr></label>
+ <textarea class="form-control address" value="" style="height: 125px; display: block;"></textarea>
+
+ <div class="alert alert-danger hidden">
+ <span class="glyphicon glyphicon-exclamation-sign"></span> This is not the same transaction. You only can combine the same transaction signed by diferent pubkeys.
+ </div>
+ </div>
+ </div>
+ <br>
+ </div>
+
+ <div class="modal-footer">
+ <button class="btn btn-primary combineTx" type="button">Combine transaction</button>
+ <button type="button" class="btn btn-default" data-dismiss="modal" id="mediatorClose">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!-- multisig modal -->
<div class="hidden" id="entropybucket"></div>
</body>
View
@@ -252,6 +252,7 @@
return false;
}
} catch(e) {
+ if (coinjs.debug) {console.log(e)};
return false;
}
}
@@ -922,9 +923,10 @@
return {'type':'scriptpubkey', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174) { // OP_CHECKMULTISIG
// multisig script, with signature(s) included
- var count = -1;
+
+ var count = -1; // To account for serialized redeemScript
for (var i = 0; i < this.ins[index].script.chunks.length; i++) {
- if(this.ins[index].script.chunks[i]){
+ if(this.ins[index].script.chunks[i]){ // omit 0 signature placeholder
++count;
}
}
@@ -1074,27 +1076,26 @@
return true;
}
- /* sign a multisig input */
- r.signmultisig = function(index, wif){
-
- function scriptListPubkey(redeemScript){
- var r = {};
- for(var i=1;i<redeemScript.chunks.length-2;i++){
- r[i] = Crypto.util.hexToBytes(coinjs.pubkeydecompress(Crypto.util.bytesToHex(redeemScript.chunks[i])));
- }
- return r;
+ r.scriptListPubkey = function(redeemScript){
+ var r = [];
+ for(var i=1;i<redeemScript.chunks.length-2;i++){
+ r.push(Crypto.util.bytesToHex(redeemScript.chunks[i]));
}
-
- function scriptListSigs(scriptSig){
- var r = {};
- 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++){
- r[i] = scriptSig.chunks[i];
- }
+ return r;
+ }
+
+ r.scriptListSigs = function(scriptSig){
+ var r = [];
+ 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++){
+ r.push(scriptSig.chunks[i]);
}
- return r;
}
-
+ return r;
+ }
+
+ /* sign a multisig input */
+ r.signmultisig = function(index, wif){
var redeemScript = (this.ins[index].script.chunks[this.ins[index].script.chunks.length-1]==174) ? this.ins[index].script.buffer : this.ins[index].script.chunks[this.ins[index].script.chunks.length-1];
var sighash = Crypto.util.hexToBytes(this.transactionHash(index));
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif));
@@ -1106,15 +1107,13 @@
s.writeBytes(signature);
} else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174){
- var pubkeyList = scriptListPubkey(coinjs.script(redeemScript));
- var sigsList = scriptListSigs(this.ins[index].script);
- sigsList[coinjs.countObject(sigsList)+1] = signature;
-
- for(x in pubkeyList){
- for(y in sigsList){
- if(coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])){
- s.writeBytes(sigsList[y]);
- }
+ var pubkeyList = this.scriptListPubkey(coinjs.script(redeemScript));
+ var sigsList = this.scriptListSigs(this.ins[index].script);
+ sigsList[sigsList.length] = signature;
+
+ for (var y = 0; y < sigsList.length; y++) {
+ if(coinjs.verifySignature(sighash, sigsList[y], pubkeyList)){
+ s.writeBytes(sigsList[y]);
}
}
@@ -1124,6 +1123,62 @@
this.ins[index].script = s;
return true;
}
+
+ r.listMultiSignature = function (index) {
+ var list = {};
+
+ var s = this.extractScriptKey(index);
+ if(s['type'] != 'multisig') return false;
+
+ var sighash = Crypto.util.hexToBytes(this.transactionHash(index));
+ var pubkeyList = this.scriptListPubkey(coinjs.script(Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1])));
+ var sigsList = this.scriptListSigs(this.ins[index].script);
+
+
+ for (var x = 0; x < pubkeyList.length; x++) {
+ list[pubkeyList[x]] = false;
+ }
+
+ var pubkey = false;
+ for (var y = 0; y < sigsList.length; y++) {
+ pubkey = coinjs.verifySignature(sighash, sigsList[y], pubkeyList);
+ if (pubkey){
+ list[pubkey] = Crypto.util.bytesToHex(sigsList[y]);
+ }
+ }
+
+ return list;
+ }
+
+ r.combineMultiSignature = function (tx) {
+ var tx2 = this.deserialize(tx);
+
+ if (this.transactionHash(0) != tx2.transactionHash(0)) return false;
+
+ var newTx = coinjs.clone(this);
+ var newTxList = {};
+ for (var index = 0; index < this.ins.length; index++) {
+ var s = this.extractScriptKey(index);
+ if (s['type'] == 'multisig') {
+ var listThis = this.listMultiSignature(index);
+ var list2 = tx2.listMultiSignature(index);
+
+ var s = coinjs.script();
+ s.writeOp(0);
+ for (var pubkey in listThis) {
+ var valid = (listThis[pubkey])?listThis[pubkey]:list2[pubkey];
+ if (valid) {
+ s.writeBytes(Crypto.util.hexToBytes(valid));
+ }
+ }
+
+ s.writeBytes(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1]); // redeemScript
+
+ newTx.ins[index].script = s
+ }
+ }
+ return newTx;
+ }
/* sign inputs */
r.sign = function(wif){
@@ -1262,7 +1317,10 @@
/* start of signature vertification functions */
- coinjs.verifySignature = function (hash, sig, pubkey) {
+ coinjs.verifySignature = function (hash, sig, pubkeys) {
+ if (!coinjs.isArray(pubkeys)) {
+ throw "Invalid format for pubkeys list";
+ }
function parseSig (sig) {
var cursor;
@@ -1302,16 +1360,20 @@
throw "Invalid value for signature";
}
- var Q;
- if (coinjs.isArray(pubkey)) {
+ var pubkey;
+ for (var i = 0; i < pubkeys.length; i++) {
+ pubkey = pubkeys[i];
+
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
- Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), pubkey);
- } else {
- throw "Invalid format for pubkey value, must be byte array";
- }
- var e = BigInteger.fromByteArrayUnsigned(hash);
+ var Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), Crypto.util.hexToBytes(coinjs.pubkeydecompress(pubkey)));
- return coinjs.verifySignatureRaw(e, r, s, Q);
+ var e = BigInteger.fromByteArrayUnsigned(hash);
+
+ if (coinjs.verifySignatureRaw(e, r, s, Q)) {
+ return pubkey;
+ }
+ }
+ return false;
}
coinjs.verifySignatureRaw = function (e, r, s, Q) {
View
@@ -840,7 +840,6 @@ $(document).ready(function() {
var tx = coinjs.transaction();
try {
var decode = tx.deserialize($("#verifyScript").val());
- if (coinjs.debug) {console.log(decode)};
$("#verifyTransactionData .transactionVersion").html(decode['version']);
$("#verifyTransactionData .transactionTime").html(decode['nTime']);
$("#verifyTransactionData .transactionSize").html(decode.size()+' <i>bytes</i>');
@@ -860,16 +859,21 @@ $(document).ready(function() {
h += '<td><input class="form-control" type="text" value="'+o.outpoint.hash+'" readonly></td>';
h += '<td class="col-xs-1">'+o.outpoint.index+'</td>';
h += '<td class="col-xs-2"><input class="form-control" type="text" value="'+Crypto.util.bytesToHex(o.script.buffer)+'" readonly></td>';
- h += '<td class="col-xs-1"> <span class="glyphicon glyphicon-'+((s.signed=='true')?'ok':'remove')+'-circle"></span>';
+ h += '<td class="col-xs-1">';
+ h += '<span class="glyphicon glyphicon-'+((s.signed=='true')?'ok':'remove')+'-circle"></span>';
if(s['type']=='multisig' && s['signatures']>=1){
+ h += '<a href="#" data-index="'+i+'">';
h += ' '+s['signatures'];
+ h += '</a>';
}
h += '</td>';
h += '<td class="col-xs-1">';
if(s['type']=='multisig'){
+ h += '<a href="#" data-index="'+i+'">';
var script = coinjs.script();
var rs = script.decodeRedeemScript(s.script);
h += rs['signaturesRequired']+' of '+rs['pubkeys'].length;
+ h += '</a>';
} else {
h += '<span class="glyphicon glyphicon-remove-circle"></span>';
}
@@ -878,6 +882,11 @@ $(document).ready(function() {
});
$(h).appendTo("#verifyTransactionData .ins tbody");
+
+ $("#verifyTransactionData .ins tbody").on( "click", "a[data-index]", function(e) {
+ e.preventDefault();
+ decodeMultiSig(decode, $(this).attr("data-index"));
+ });
h = '';
$.each(decode.outs, function(i,o){
@@ -937,6 +946,50 @@ $(document).ready(function() {
return false;
}
}
+
+ function decodeMultiSig(tx, i) {
+ var html = '';
+ var list = tx.listMultiSignature(i);
+ for (var pubkey in list) {
+ identity = "";
+ if (known.pubKey[pubkey]) {
+ identity = known.pubKey[pubkey].name;
+ }
+
+ var address = coinjs.pubkey2address(pubkey);
+ html += '<tr style="'+((list[pubkey])?'background-color: rgb(223, 240, 216);':'')+'">\
+ <td width="30%">\
+ <input type="text" class="form-control" value="'+address+'" readonly>\
+ </td>\
+ <td>\
+ <input type="text" class="form-control" value="'+pubkey+'" readonly>\
+ </td>\
+ <td>\
+ <input type="text" class="form-control" value="'+identity+'" readonly>\
+ </td>\
+ </tr>';
+ }
+
+ $("#modalMultisig table tbody").html(html);
+ $("#modalMultisig .combine .alert").addClass("hidden");
+
+ $("#modalMultisig .combineTx").click(function() {
+ var newTx = tx.combineMultiSignature($("#modalMultisig .combine textarea").val());
+ if (newTx) {
+ console.log(newTx, newTx.serialize());
+ $("#verifyScript").val(newTx.serialize()).fadeOut().fadeIn();
+ $("#verifyBtn").click();
+
+ $("#modalMultisig .combine .alert").addClass("hidden");
+ $("#modalMultisig").modal("hide");
+
+ window.location.hash = "#verify";
+ } else {
+ $("#modalMultisig .combine .alert").removeClass("hidden");
+ }
+ });
+ $("#modalMultisig").modal("show");
+ }
function hex2ascii(hex) {
var str = '';

0 comments on commit bad5f8a

Please sign in to comment.