├── LICENSE ├── compile.py ├── multisig-standalone.html ├── readme.md ├── src ├── css │ └── bootstrap.min.css ├── index.html └── js │ ├── bitcoinjs-1-5-7.js │ ├── bitcoinjs-extensions.js │ ├── bootstrap.min.js │ ├── discover.js │ ├── index.js │ ├── jquery.min.js │ ├── jquery.qrcode.min.js │ ├── part.js │ └── translate.js └── tests.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014-2016 Ian Coleman 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import datetime 4 | 5 | # This script generates the multisig-standalone.html file. 6 | 7 | # It removes script and style tags and replaces with the file content. 8 | 9 | f = open('src/index.html') 10 | page = f.read() 11 | f.close() 12 | 13 | 14 | # Script tags 15 | 16 | scriptsFinder = re.compile("""""") 17 | scripts = scriptsFinder.findall(page) 18 | 19 | for script in scripts: 20 | filename = os.path.join("src", script) 21 | s = open(filename) 22 | scriptContent = "" % s.read() 23 | s.close() 24 | scriptTag = """""" % script 25 | page = page.replace(scriptTag, scriptContent) 26 | 27 | 28 | # Style tags 29 | 30 | stylesFinder = re.compile("""""") 31 | styles = stylesFinder.findall(page) 32 | 33 | for style in styles: 34 | filename = os.path.join("src", style) 35 | s = open(filename) 36 | styleContent = "" % s.read() 37 | s.close() 38 | styleTag = """""" % style 39 | page = page.replace(styleTag, styleContent) 40 | 41 | 42 | # Write the standalone file 43 | 44 | f = open('multisig-standalone.html', 'w') 45 | f.write(page) 46 | f.close() 47 | 48 | print("%s - DONE" % datetime.datetime.now()) 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Multisig Tool 2 | 3 | A tool for creating multisig addresses and transactions. 4 | 5 | ## Online Version 6 | 7 | https://iancoleman.github.io/multisig/ 8 | 9 | ## Standalone offline version 10 | 11 | Download `multisig-standalone.html` 12 | 13 | Open the file in a browser by double clicking it. 14 | 15 | This can be compiled from source using the command `python compile.py` 16 | 17 | ## Usage 18 | 19 | Enter your public / private keys into the field. 20 | 21 | Click 'discover used combos' if required. 22 | 23 | Set the number of signatories required. 24 | 25 | Choose the ordering of the parts. 26 | 27 | Use the address and redeem script however you like. 28 | 29 | ## Donations 30 | 31 | Since this project is the efforts of many people, most of which don't appear in 32 | the obvious places like code or issues, donating to the project itself causes 33 | significant operational difficulties. 34 | 35 | As a result, if you would like to support this project financially you are 36 | encouraged to donate to one of the many groups that makes the internet a place 37 | amenable to projects such as this one. 38 | 39 | [Donation-accepting organizations and projects](https://en.bitcoin.it/wiki/Donation-accepting_organizations_and_projects) 40 | 41 | If the list is too difficult to choose from, the EFF is a good choice. 42 | 43 | [Electronic Frontier Foundation](https://supporters.eff.org/donate) 44 | 45 | or for a direct bitcoin address, consider donating to the 46 | [Free Software Foundation](https://www.fsf.org/about/ways-to-donate/) 47 | at 1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN 48 | 49 | ![alt text](https://static.fsf.org/nosvn/images/bitcoin_qrcodes/fsf.png "FSF Bitcoin Address") 50 | 51 | ## Making changes 52 | 53 | Please do not make modifications to `multisig-standalone.html`, since they will 54 | be overwritten by `compile.py`. 55 | 56 | Make changes in `src/*` and apply them using the command `python compile.py` 57 | 58 | # License 59 | 60 | This multisig tool is released under the terms of the MIT license. See LICENSE 61 | for more information or see https://opensource.org/licenses/MIT. 62 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Multisig 6 | 7 | 8 | 9 | 10 | 11 | 12 | 74 | 75 | 76 |
77 | 78 |

Multisig Tool

79 |

Create multisig addresses and transactions

80 |
81 |
82 |
83 |

Parts

84 |
85 |
86 |
87 |
88 |

Enter bitcoin public keys (hex) or private keys (WIF). Support for xpub and xprv keys coming soon.

89 |

Separate each part with spaces, newlines, commas or semicolons.

90 |
91 |
92 |
93 | 94 |
95 | 96 |
97 |
98 |
99 | 100 |
101 | 104 |
105 |
106 |
107 | 108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
Awill be taken from the parts above
B
...
124 |
125 |
126 |
127 |
128 |
129 | 130 |
131 | 132 |
133 |
134 |

Discover (Optional)

135 |

136 | If you're unsure of the correct order for the parts, you 137 | can test each combination here. 138 |

139 |

140 | Any combinations that have received a transaction 141 | previously will be listed in green (even if the balance is 142 | zero). 143 |

144 |

145 | Any combinations that have NOT received a transaction 146 | previously will be listed in red with a strikethrough. 147 |

148 |

149 | This feature uses a blockchain explorer to perform the 150 | discovery, which may pose a privacy issue for some users. 151 |

152 | 153 | 154 |
155 | 156 |
157 | 165 |
166 |
167 | 168 |
169 | 170 |
171 |
172 |

Details

173 |
174 |
175 | 176 |
177 | 181 | of 182 | N 183 |
184 |
185 |
186 | 187 |
188 | 191 |
192 |
193 |
194 | 195 |
196 | 197 |
198 |
199 |
200 | 201 |
202 | 203 |
204 |
205 |
206 | 207 |
208 | 209 |
210 |
211 |
212 |
213 |
214 |
215 |

216 | BIP45 forces lexicographical ordering 217 |

218 |
219 |
220 | 221 |
222 | 223 |
224 |
225 |
226 | 227 |
228 | 229 |
230 |
231 | 232 |
233 |
234 | 235 |
236 | 237 |
238 |
239 |

Transaction Builder

240 |

Coming soon 241 |

242 |
243 | 244 |
245 | 246 |
247 |
248 |

More info

249 |

License

250 |

251 | Please refer to the software license for more detail. 252 | 253 |

254 |

The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

255 |

Alternatives

256 | 282 |
283 |
284 | 285 |
286 | 287 |
288 |
289 | 290 |

Offline Usage

291 | 292 |

293 | You can use this tool without having to be online. 294 |

295 |

296 | In your browser, select file save-as, and save this page 297 | as a file. 298 |

299 |

300 | Double-click that file to open it in a browser 301 | on any offline computer. 302 |

303 |

304 | Alternatively, download the file from the repository 305 | - 306 | https://github.com/iancoleman/multisig 307 |

308 | 309 |
310 |
311 | 312 |
313 | 314 |
315 |
316 | 317 |

This project is 100% open-source code

318 | 319 |

320 | Get the source code from the repository 321 | - 322 | 323 | https://github.com/iancoleman/multisig 324 | 325 |

326 | 327 |

Libraries

328 | 329 |

330 | BitcoinJS - 331 | 332 | https://github.com/bitcoinjs/bitcoinjs-lib 333 | 334 |

335 | 336 |

337 | jQuery - 338 | 339 | https://jquery.com/ 340 | 341 |

342 | 343 |

344 | Twitter Bootstrap - 345 | 346 | http://getbootstrap.com/ 347 | 348 |

349 | 350 |
351 |
352 | 353 |
354 | 355 | 363 | 364 |
365 | 366 |
367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | -------------------------------------------------------------------------------- /src/js/bitcoinjs-extensions.js: -------------------------------------------------------------------------------- 1 | bitcoin.pubkeysToMultisig = function(M, pubkeys, network) { 2 | var r = {}; 3 | r.redeemScript = bitcoin.scripts.multisigOutput(M, pubkeys); 4 | r.scriptPubKey = bitcoin.scripts.scriptHashOutput(r.redeemScript.getHash()); 5 | r.address = bitcoin.Address.fromOutputScript(r.scriptPubKey, network).toString(); 6 | return r; 7 | } 8 | 9 | bitcoin.networks.shadow = { 10 | magicPrefix: '\x19ShadowCash Signed Message:\n', 11 | bip32: { 12 | public: 0xEE80286A, 13 | private: 0xEE8031E8 14 | }, 15 | pubKeyHash: 0x3f, 16 | scriptHash: 0x7d, 17 | wif: 0xbf, 18 | dustThreshold: 0, 19 | feePerKb: 1000, 20 | estimateFee: function() { return "unused in this app" }, 21 | }; 22 | 23 | bitcoin.networks.shadowtn = { 24 | magicPrefix: '\x19ShadowCash Signed Message:\n', 25 | bip32: { 26 | public: 0x76C0FDFB, 27 | private: 0x76C1077A 28 | }, 29 | pubKeyHash: 0x7f, 30 | scriptHash: 0xc4, 31 | wif: 0xff, 32 | dustThreshold: 0, 33 | feePerKb: 1000, 34 | estimateFee: function() { return "unused in this app" }, 35 | }; 36 | 37 | bitcoin.networks.dash = { 38 | bip32: { 39 | public: 0x0488b21e, 40 | private: 0x0488ade4 41 | }, 42 | pubKeyHash: 0x4c, 43 | scriptHash: 0x10, 44 | wif: 0xcc, 45 | }; 46 | 47 | bitcoin.networks.game = { 48 | bip32: { 49 | public: 0x0488b21e, 50 | private: 0x0488ade4 51 | }, 52 | pubKeyHash: 0x26, 53 | scriptHash: 0x05, 54 | wif: 0xa6, 55 | }; 56 | -------------------------------------------------------------------------------- /src/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('