Merge pull request #5 from bip32JP/sendstealth
Add ability to send to stealth address
This commit is contained in:
commit
184635fb34
3 changed files with 102 additions and 2 deletions
|
@ -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.
|
||||
|
|
91
js/coin.js
91
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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue