From f289899636111aa06820384baee80028756d00d2 Mon Sep 17 00:00:00 2001 From: bip32jp Date: Tue, 30 Dec 2014 12:40:30 +0900 Subject: [PATCH] Add ability to send to stealth address Currently works with sending to Dark Wallet Mainnet stealth addresses. --- README.md | 1 + js/coin.js | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ js/coinbin.js | 12 +++++-- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 07811bf..cc042d6 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Coinb.in supports a number of key features such as: - Broadcast transactions. - nLockTime support. - Add custom data to transactions with the use of OP_RETURN. +- Support current Dark Wallet Stealth Address structure (as of version Alpha 7) for outputs. - Brain wallet support. - Compatible with bitcoin-qt - An offical .onion address for tor users. diff --git a/js/coin.js b/js/coin.js index a303f31..2782227 100644 --- a/js/coin.js +++ b/js/coin.js @@ -189,6 +189,57 @@ } } + /* parse stealth address and return an object with the parts */ + coinjs.stealthDecode = function(stealth){ + try { + var bytes = coinjs.base58decode(stealth); + var front = bytes.slice(0, bytes.length-4); + var back = bytes.slice(bytes.length-4); + var checksum = Crypto.SHA256(Crypto.SHA256(front, {asBytes: true}), {asBytes: true}).slice(0, 4); + if (checksum+"" == back+"") { + var o = {}; + + o.version = front[0]; + + if (o.version != 42) { + return false; + }; + + o.option = front[1]; + + if (o.option != 0) { + alert("Stealth Address option other than 0 is currently not supported!"); + return false; + }; + + o.scankey = Crypto.util.bytesToHex(front.slice(2, 35)); + o.n = front[35]; + + if (o.n > 1) { + 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; + }; + + o.prefix = front.slice(71) + + return o; + } else { + return false; + } + } catch(e) { + return false; + } + } + /* retreive the balance from a given address */ coinjs.addressBalance = function(address, callback){ coinjs.ajax(coinjs.host+'?uid='+coinjs.uid+'&key='+coinjs.key+'&setmodule=addresses&request=bal&address='+address+'&r='+Math.random(), callback, "GET"); @@ -387,6 +438,46 @@ return this.outs.push(o); } + /* add two outputs for stealth addresses to a transaction */ + 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); + } + /* add data to a transaction */ r.adddata = function(data){ var r = false; diff --git a/js/coinbin.js b/js/coinbin.js index a411b67..fa4718e 100644 --- a/js/coinbin.js +++ b/js/coinbin.js @@ -364,9 +364,17 @@ $(document).ready(function() { $("#recipients .row").removeClass('has-error'); $.each($("#recipients .row"), function(i,o){ - var a = ($(".address",o).val()).substr(0,80); - if(((a!="") && coinjs.addressDecode(a)) && $(".amount",o).val()!=""){ // address + var a = ($(".address",o).val()); + var ad = coinjs.addressDecode(a) + if(((a!="") && (ad.version === 0 || ad.version === 5)) && $(".amount",o).val()!=""){ // address tx.addoutput(a, $(".amount",o).val()); + } else if (((a!="") && ad.version === 42) && $(".amount",o).val()!=""){ // stealth address + var stealth = coinjs.stealthDecode(a); + if (stealth == false) { + $(o).addClass('has-error'); + return; + } + tx.addstealth(stealth, $(".amount",o).val()); } else if (((($("#opReturn").is(":checked")) && a.match(/^[a-f0-9]+$/ig)) && a.length<80) && (a.length%2)==0) { // data tx.adddata(a); } else { // neither address nor data