tx proposal test. PrivateKey

This commit is contained in:
Matias Alejo Garcia 2014-04-10 02:16:57 -03:00
parent f1645945d3
commit c5b1fca910
6 changed files with 155 additions and 64 deletions

View File

@ -43,8 +43,6 @@ angular.module('copay.signin').controller('SigninController',
$location.path('peer'); $location.path('peer');
$rootScope.$digest(); $rootScope.$digest();
}, function() { }, function() {
console.log('[signin.js.46] SETTING MESSAGE'); //TODO
$rootScope.flashMessage = { message: 'Connection refussed', type: 'error'}; $rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
$location.path('home'); $location.path('home');
$rootScope.$digest(); $rootScope.$digest();

View File

@ -6,15 +6,19 @@ var bitcore = require('bitcore');
var BIP32 = bitcore.BIP32; var BIP32 = bitcore.BIP32;
var WalletKey = bitcore.WalletKey; var WalletKey = bitcore.WalletKey;
var networks = bitcore.networks; var networks = bitcore.networks;
var util = bitcore.util;
var PublicKeyRing = require('./PublicKeyRing'); var PublicKeyRing = require('./PublicKeyRing');
function PrivateKey(opts) { function PrivateKey(opts) {
this.id = opts.id;
this.network = opts.networkName === 'testnet' ? this.network = opts.networkName === 'testnet' ?
networks.testnet : networks.livenet; networks.testnet : networks.livenet;
this.BIP32 = opts.BIP32 || new BIP32(this.network.name); this.BIP32 = opts.BIP32 || new BIP32(opts.extendedPrivateKeyString || this.network.name);
this._calcId();
}; };
PrivateKey.prototype._calcId = function() {
this.id = util.ripe160(this.BIP32.extendedPublicKey).toString('hex');
};
PrivateKey.prototype.getBIP32 = function(index,isChange) { PrivateKey.prototype.getBIP32 = function(index,isChange) {
if (typeof index === 'undefined') { if (typeof index === 'undefined') {
@ -24,6 +28,21 @@ PrivateKey.prototype.getBIP32 = function(index,isChange) {
PublicKeyRing.ChangeBranch(index):PublicKeyRing.PublicBranch(index) ); PublicKeyRing.ChangeBranch(index):PublicKeyRing.PublicBranch(index) );
}; };
PrivateKey.fromObj = function(o) {
return new PrivateKey({
extendedPrivateKeyString: o.extendedPrivateKeyString,
networkName: o.networkName,
});
};
PrivateKey.prototype.toObj = function() {
return {
extendedPrivateKeyString: this.BIP32.extendedPrivateKeyString(),
networkName: this.network.name,
};
};
PrivateKey.prototype.get = function(index,isChange) { PrivateKey.prototype.get = function(index,isChange) {
var derivedBIP32 = this.getBIP32(index,isChange); var derivedBIP32 = this.getBIP32(index,isChange);
var wk = new WalletKey({network: this.network}); var wk = new WalletKey({network: this.network});
@ -34,6 +53,7 @@ PrivateKey.prototype.get = function(index,isChange) {
PrivateKey.prototype.getAll = function(addressIndex, changeAddressIndex) { PrivateKey.prototype.getAll = function(addressIndex, changeAddressIndex) {
var ret = []; var ret = [];
for(var i=0;i<addressIndex; i++) { for(var i=0;i<addressIndex; i++) {
ret.push(this.get(i,false)); ret.push(this.get(i,false));
} }

View File

@ -13,34 +13,53 @@ var Storage = imports.Storage || require('./Storage');
var storage = Storage.default(); var storage = Storage.default();
function TxProposal(opts) { function TxProposal(opts) {
this.tx = opts.tx; this.tx = opts.tx;
this.seenBy = {}; this.seenBy = opts.seenBy || {};
this.signedBy = {}; this.signedBy = opts.signedBy || {};
}; };
module.exports = require('soop')(TxProposal); module.exports = require('soop')(TxProposal);
function TxProposals(opts) { function TxProposals(opts) {
opts = opts || {}; opts = opts || {};
this.network = opts.networkName === 'livenet' ? this.network = opts.networkName === 'livenet' ?
bitcore.networks.livenet : bitcore.networks.testnet; bitcore.networks.livenet : bitcore.networks.testnet;
this.publicKeyRing = opts.publicKeyRing; this.publicKeyRing = opts.publicKeyRing;
this.requiredCopayers = opts.requiredCopayers || 3;
this.txs = []; this.txs = [];
this.dirty = 1;
} }
TxProposals.prototype.list = function() { TxProposals.fromObj = function(o) {
var ret = []; var ret = new TxProposals({
var ret = []; networkName: o.networkName,
this.txs.forEach(function(tx) {
}); });
o.txs.forEach(function(t) {
var tx = new Transaction;
tx.parse(t.txHex);
ret.txs.push({
seenBy: t.seenBy,
signedBy: t.signedBy,
tx: tx,
});
});
return ret;
}; };
TxProposals.prototype.create = function(toAddress, amountSat, utxos, privs) { TxProposals.prototype.toObj = function() {
var ret = [];
this.txs.forEach(function(t) {
ret.push({
seenBy: t.seenBy,
signedBy: t.signedBy,
txHex: t.tx.serialize(),
});
});
return {
txs: ret,
networkName: this.network.name,
};
};
TxProposals.prototype.create = function(toAddress, amountSat, utxos, priv) {
var pkr = this.publicKeyRing; var pkr = this.publicKeyRing;
if (! pkr.isComplete() ) { if (! pkr.isComplete() ) {
@ -57,18 +76,21 @@ TxProposals.prototype.create = function(toAddress, amountSat, utxos, privs) {
.setOutputs([{address: toAddress, amountSat: amountSat}]) .setOutputs([{address: toAddress, amountSat: amountSat}])
; ;
if (privs) { if (priv) {
b.sign(privs); //console.log('*** SIGNING IDX:', pkr.addressIndex, pkr.changeAddressIndex);
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
} }
var tx = b.build(); var tx = b.build();
var me = {};
if (priv)
me[priv.id] = Date.now();
this.txs.push( this.txs.push(
new TxProposal({ new TxProposal({
signedBy: { signedBy: me,
}, seenBy: me,
seenBy: { tx: tx,
},
tx: tx
}) })
); );
return tx; return tx;

View File

@ -42,30 +42,53 @@ angular.module('copay.network')
// TODO -> probably not in network.js // TODO -> probably not in network.js
var createWallet = function(walletId) { var createWallet = function(walletId) {
console.log('### CREATING WALLET. ID:' + walletId); console.log('### CREATING WALLET. ID:' + walletId);
var priv = new copay.PrivateKey({networkName: config.networkName});
console.log('\t### PrivateKey Initialized');
//TODO create a wallet and WalletId, not only pkr //TODO create a wallet and WalletId, not only pkr
var pkr = new copay.PublicKeyRing({ var pkr = new copay.PublicKeyRing({
network: config.networkName, networkName: config.networkName,
id: walletId, id: walletId,
}); });
pkr.addCopayer();
console.log('\t### PublicKeyRing Initialized:'); // Add self to the ring.
pkr.addCopayer(priv.getBIP32().extendedPublicKeyString());
console.log('\t### PublicKeyRing Initialized');
var txp = new copay.TxProposals({
networkName: config.networkName,
publicKeyRing: pkr,
});
console.log('\t### TxProposals Initialized');
Storage.addWalletId(pkr.id); Storage.addWalletId(pkr.id);
Storage.set(pkr.id, 'publicKeyRing', pkr.toObj()); Storage.set(pkr.id, 'publicKeyRing', pkr.toObj());
Storage.set(pkr.id, 'privateKey', priv.toObj());
Storage.set(pkr.id, 'txProposals', txp.toObj());
console.log('\t### Wallet Stored');
// Store it on rootScope
$rootScope.priv = priv; // TODO secure this.
$rootScope.walletId = pkr.id; $rootScope.walletId = pkr.id;
$rootScope.publicKeyRing = pkr; $rootScope.publicKeyRing = pkr;
$rootScope.txProposals = txp;
}; };
var openWallet = function (walletId) { var openWallet = function (walletId) {
var ret = false; var ret = false;
var pkr = Storage.get(walletId, 'publicKeyRing'); var pkr = Storage.get(walletId, 'publicKeyRing');
var priv = Storage.get(walletId, 'privateKey');
var txp = Storage.get(walletId, 'txProposals');
if (pkr) { if (pkr) {
console.log('### WALLET OPENED:', walletId, pkr); console.log('### WALLET OPENED:', walletId, pkr);
$rootScope.walletId = walletId; $rootScope.walletId = walletId;
$rootScope.publicKeyRing = new copay.PublicKeyRing.fromObj(pkr); $rootScope.publicKeyRing = new copay.PublicKeyRing.fromObj(pkr);
$rootScope.txProposals = new copay.TxProposals.fromObj(txp);
$rootScope.priv = new copay.PrivateKey.fromObj(priv); //TODO secure
ret = true; ret = true;
} }
return ret; return ret;
@ -133,7 +156,6 @@ angular.module('copay.network')
// public methods // public methods
var init = function(cb) { var init = function(cb) {
var cp = $rootScope.cp = new copay.CopayPeer({ var cp = $rootScope.cp = new copay.CopayPeer({
apiKey: config.p2pApiKey, apiKey: config.p2pApiKey,
debug: config.p2pDebug, debug: config.p2pDebug,
@ -141,7 +163,6 @@ angular.module('copay.network')
}); });
_setupHandlers(); _setupHandlers();
// inicia session
cp.start(function(peerId) { cp.start(function(peerId) {
return cb(); return cb();
}); });
@ -156,15 +177,15 @@ angular.module('copay.network')
_refreshUx(); _refreshUx();
}; };
var connect = function(peerId, openCallback, failCallBack) { var connect = function(peerId, openCallback, failCallback) {
if ($rootScope.cp) { if ($rootScope.cp) {
$rootScope.cp.connectTo(peerId, openCallback, function () { $rootScope.cp.connectTo(peerId, openCallback, function () {
disconnect(); disconnect();
failCallBack(); failCallback();
}); });
} }
else else
return failCallBack(); return failCallback();
}; };
return { return {

View File

@ -67,4 +67,25 @@ describe('PrivateKey model', function() {
} }
}); });
it('should calculate .id', function () {
var w1 = new PrivateKey(config);
should.exist(w1.id);
w1.id.length.should.equal(40);
});
it('fromObj toObj roundtrip', function () {
var w1 = new PrivateKey(config);
var w2 = PrivateKey.fromObj(w1.toObj());
w2.getBIP32().extendedPrivateKeyString().should.equal(w1.getBIP32().extendedPrivateKeyString());
w2.getBIP32().extendedPublicKeyString().should.equal(w1.getBIP32().extendedPublicKeyString());
w2.id.should.equal(w1.id);
w2.getBIP32(1,1).extendedPrivateKeyString().should
.equal(w1.getBIP32(1,1).extendedPrivateKeyString());
w2.getBIP32(1,0).extendedPrivateKeyString().should
.equal(w1.getBIP32(1,0).extendedPrivateKeyString());
});
}); });

View File

@ -44,11 +44,13 @@ var createW = function (bip32s) {
else else
w.addCopayer(); w.addCopayer();
} }
w.generateAddress(true); w.generateAddress(true);
w.generateAddress(true); w.generateAddress(true);
w.generateAddress(true); w.generateAddress(true);
w.generateAddress(false); w.generateAddress(false);
w.generateAddress(false); w.generateAddress(false);
w.generateAddress(false);
//3x3 indexes
return w; return w;
}; };
@ -91,57 +93,64 @@ describe('TxProposals model', function() {
networkName: config.networkName, networkName: config.networkName,
publicKeyRing: createW([priv.getBIP32()]), publicKeyRing: createW([priv.getBIP32()]),
}); });
should.exist(w);
w.network.name.should.equal('livenet');
var ts = Date.now();
for (var isChange=0; isChange<2; isChange++) { for (var isChange=0; isChange<2; isChange++) {
for (var index=0; index<3; index++) { for (var index=0; index<3; index++) {
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange); unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange);
unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer(); unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer();
var tx = w.create( var tx = w.create(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
bignum('123456789'), bignum('123456789'),
unspentTest, unspentTest,
[priv.get(index,isChange)] priv
); );
should.exist(tx); should.exist(tx);
tx.isComplete().should.equal(false); tx.isComplete().should.equal(false);
tx.countInputMissingSignatures(0).should.equal(2); tx.countInputMissingSignatures(0).should.equal(2);
(w.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
(w.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
} }
} }
}); });
it('#create. Signing with derivate keys block', function () { it('#toObj #fromObj roundtrip', function () {
var priv = new PrivateKey(config); var priv = new PrivateKey(config);
var privs = priv.getAll(3,3);
var w = new TxProposals({ var w = new TxProposals({
networkName: config.networkName, networkName: config.networkName,
publicKeyRing: createW([priv.getBIP32()]), publicKeyRing: createW([priv.getBIP32()]),
}); });
should.exist(w); var ts = Date.now();
w.network.name.should.equal('livenet'); var isChange=0;
var index=0;
for (var isChange=0; isChange<2; isChange++) { unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange);
for (var index=0; index<3; index++) { unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer();
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange); var tx = w.create(
unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer(); '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
var tx = w.create( bignum('123456789'),
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', unspentTest,
bignum('123456789'), priv
unspentTest, );
privs tx.isComplete().should.equal(false);
); tx.countInputMissingSignatures(0).should.equal(2);
should.exist(tx); (w.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
tx.isComplete().should.equal(false); (w.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
tx.countInputMissingSignatures(0).should.equal(2);
}
}
var o = w.toObj();
should.exist(o);
o.txs.length.should.equal(1);
should.exist(o.txs[0].txHex);
should.exist(o.txs[0].signedBy);
should.exist(o.txs[0].seenBy);
should.exist(o.txs[0].signedBy[priv.id]);
var w2 = TxProposals.fromObj(o);
var tx2 = w2.txs[0].tx;
tx2.isComplete().should.equal(false);
tx2.countInputMissingSignatures(0).should.equal(2);
(w2.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
(w2.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
}); });
}); });