Browse Source
For spotting transactions to which you have the stealth key (or at least the scan key) and creating transactions to a stealth address. So far it is only partially working - you can see if a transaction is a stealth transaction (or at least one of a limited kind of stealth transactions), and you can see that you do not have the stealth key to spend one of these transactions. However, I have not yet tested whether you can see a stealth transaction that you actually have the key to. Also, it is not yet easy to spend to a stealth address.patch-2
Ryan X. Charles
10 years ago
2 changed files with 137 additions and 0 deletions
@ -0,0 +1,69 @@ |
|||||
|
var StealthAddress = require('./stealthaddress'); |
||||
|
var StealthKey = require('./stealthkey'); |
||||
|
var Transaction = require('../transaction'); |
||||
|
var Pubkey = require('../pubkey'); |
||||
|
|
||||
|
var StealthTx = function StealthTx(tx, sa, sk) { |
||||
|
if (!(this instanceof StealthTx)) |
||||
|
return new StealthTx(tx, sa, sk); |
||||
|
if (tx instanceof Transaction) { |
||||
|
this.tx = tx; |
||||
|
this.sa = sa; |
||||
|
this.sk = sk; |
||||
|
} else if (tx) { |
||||
|
var obj = tx; |
||||
|
this.set(obj); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
StealthTx.prototype.set = function(obj) { |
||||
|
this.sk = obj.sk || this.sk; |
||||
|
this.sa = obj.sa || this.sa; |
||||
|
this.tx = obj.tx || this.tx; |
||||
|
return this; |
||||
|
}; |
||||
|
|
||||
|
StealthTx.prototype.isForMe = function() { |
||||
|
if (!this.notMine()) |
||||
|
return true; |
||||
|
else |
||||
|
return false; |
||||
|
}; |
||||
|
|
||||
|
StealthTx.prototype.notMine = function() { |
||||
|
var err; |
||||
|
if (err = this.notStealth()) |
||||
|
return "Not stealth: " + err; |
||||
|
var txopbuf = this.tx.txouts[0].script.chunks[1].buf; |
||||
|
var parsed = StealthTx.parseOpReturnData(txopbuf); |
||||
|
var pubkey = parsed.pubkey; |
||||
|
var pubkeyhashbuf = this.tx.txouts[1].script.chunks[2].buf; |
||||
|
var sk = this.sk; |
||||
|
if (sk.isForMe(pubkey, pubkeyhashbuf)) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return "StealthTx not mine"; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
//For now, we only support a very limited variety of stealth tx
|
||||
|
StealthTx.prototype.notStealth = function() { |
||||
|
var txouts = this.tx.txouts; |
||||
|
if (!(txouts.length >= 2)) |
||||
|
return "Not enough txouts"; |
||||
|
if (!txouts[0].script.isOpReturn()) |
||||
|
return "First txout is not OP_RETURN"; |
||||
|
if (!txouts[1].script.isPubkeyhashOut()) |
||||
|
return "Second txout is not pubkeyhash"; |
||||
|
return false; |
||||
|
}; |
||||
|
|
||||
|
StealthTx.parseOpReturnData = function(buf) { |
||||
|
var parsed = {}; |
||||
|
parsed.version = buf[0]; |
||||
|
parsed.noncebuf = buf.slice(1, 5); |
||||
|
parsed.pubkey = Pubkey().fromBuffer(buf.slice(5, 5 + 33)); |
||||
|
return parsed; |
||||
|
}; |
||||
|
|
||||
|
module.exports = StealthTx; |
@ -0,0 +1,68 @@ |
|||||
|
var should = require('chai').should(); |
||||
|
var Txout = require('../lib/txout'); |
||||
|
var Stealthkey = require('../lib/expmt/stealthkey'); |
||||
|
var StealthTx = require('../lib/expmt/stealthtx'); |
||||
|
var Transaction = require('../lib/transaction'); |
||||
|
var Varint = require('../lib/varint'); |
||||
|
|
||||
|
describe('StealthTx', function() { |
||||
|
|
||||
|
var txhex = '0100000001c828ccce36eca04f96321ad488528af86c7598e67157c4f8e2f462a9e0e3af5f010000006a47304402204525eef6a56cc57fb184e53efdfdc1086d5265da21480d55c2184536440a64f70220349cdc6c66a8507dde0d172fe64aeb57ae56e014b50315f615086a6b85c5424e012102c0633ddb6bf2a8686e2ba4ce8026c94e1e27ef12e73f8fed6d6d2b97199f9b74ffffffff020000000000000000286a2606deadbeef0365b5a5b0ba059666e907b0b5e07b37fdb162d1399ed829315491fe1f30c87b3f905f0100000000001976a9142042d5e7ef9e82346419fbfe7df5ae52fe4bea3c88ac00000000'; |
||||
|
var txbuf = new Buffer(txhex, 'hex'); |
||||
|
var txidhex = '66da969fff214c329e27062beaf3baf20ed035801559b31f3e868c2de4cdfc5b'; |
||||
|
var tx = Transaction(txbuf); |
||||
|
|
||||
|
it('should make a new StealthTx', function() { |
||||
|
var stx = new StealthTx(); |
||||
|
should.exist(stx); |
||||
|
stx = StealthTx(); |
||||
|
should.exist(stx); |
||||
|
}); |
||||
|
|
||||
|
describe('#isForMe', function() { |
||||
|
|
||||
|
it('should return false for this known tx and random stealthkey', function() { |
||||
|
var sk = Stealthkey().fromRandom(); |
||||
|
var stx = StealthTx().set({sk: sk, tx: tx}); |
||||
|
stx.isForMe().should.equal(false); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#notMine', function() { |
||||
|
|
||||
|
it('should return true for this known tx and random stealthkey', function() { |
||||
|
var sk = Stealthkey().fromRandom(); |
||||
|
var stx = StealthTx().set({sk: sk, tx: tx}); |
||||
|
stx.notMine().should.equal("StealthTx not mine"); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#notStealth', function() { |
||||
|
|
||||
|
it('should know this is a stealth tx', function() { |
||||
|
var stx = StealthTx().set({tx: tx}); |
||||
|
stx.notStealth().should.equal(false); |
||||
|
}); |
||||
|
|
||||
|
it('should know this is not a stealth tx', function() { |
||||
|
var tx2 = Transaction(tx); |
||||
|
tx2.txouts.pop(); |
||||
|
tx2.txoutsvi = Varint(1); |
||||
|
var stx = StealthTx().set({tx: tx2}); |
||||
|
stx.notStealth().should.equal("Not enough txouts"); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('@parseOpReturnData', function() { |
||||
|
var txout = tx.txouts[0]; |
||||
|
var buf = txout.script.chunks[1].buf; |
||||
|
var parsed = StealthTx.parseOpReturnData(buf); |
||||
|
(typeof parsed.version).should.equal('number'); |
||||
|
parsed.noncebuf.length.should.be.above(0); |
||||
|
parsed.pubkey.toBuffer().length.should.equal(33); |
||||
|
}); |
||||
|
|
||||
|
}); |
Loading…
Reference in new issue