architecture refactor
This commit is contained in:
parent
6d90020205
commit
59c00da592
17 changed files with 151 additions and 47 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -46,3 +46,5 @@ public/css/main.css
|
||||||
README.html
|
README.html
|
||||||
|
|
||||||
lib
|
lib
|
||||||
|
|
||||||
|
js/copayBundle.js
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ module.exports = function(grunt) {
|
||||||
tasks: ['markdown']
|
tasks: ['markdown']
|
||||||
},
|
},
|
||||||
scripts: {
|
scripts: {
|
||||||
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js', '!lib/**js', '!browser/vendor-bundle.js'],
|
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js', '!lib/**js', '!browser/vendor-bundle.js', '!js/copayBundle.js'],
|
||||||
tasks: ['shell'],
|
tasks: ['shell'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
16
copay.js
16
copay.js
|
|
@ -1,8 +1,12 @@
|
||||||
|
|
||||||
module.exports.Storage = require('./js/models/Storage');
|
// core
|
||||||
module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
|
module.exports.PublicKeyRing = require('./js/models/core/PublicKeyRing');
|
||||||
module.exports.Wallet = require('./js/models/Wallet');
|
module.exports.TxProposals = require('./js/models/core/TxProposals');
|
||||||
module.exports.TxProposals = require('./js/models/TxProposals');
|
module.exports.PrivateKey = require('./js/models/core/PrivateKey');
|
||||||
module.exports.CopayPeer = require('./js/models/CopayPeer');
|
|
||||||
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
// components
|
||||||
|
module.exports.Network = require('./js/models/network/WebRTC');
|
||||||
|
module.exports.Storage = require('./js/models/storage/Plain');
|
||||||
|
|
||||||
|
// test
|
||||||
module.exports.FakeStorage = require('./test/FakeStorage');
|
module.exports.FakeStorage = require('./test/FakeStorage');
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ var coinUtil = bitcore.util;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
||||||
var Storage = imports.Storage || require('./Storage');
|
var Storage = imports.Storage || require('../storage/Base.js');
|
||||||
var storage = Storage.default();
|
var storage = Storage.default();
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ var Builder = bitcore.TransactionBuilder;
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
||||||
var Storage = imports.Storage || require('./Storage');
|
var Storage = imports.Storage || require('../storage/Base');
|
||||||
var storage = Storage.default();
|
var storage = Storage.default();
|
||||||
|
|
||||||
function TxProposal(opts) {
|
function TxProposal(opts) {
|
||||||
41
js/models/network/Base.js
Normal file
41
js/models/network/Base.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emits
|
||||||
|
* 'networkChange'
|
||||||
|
* when network layout has change (new/lost peers, etc)
|
||||||
|
*
|
||||||
|
* 'data'
|
||||||
|
* when an unknown data type arrives
|
||||||
|
*
|
||||||
|
* Provides
|
||||||
|
* send(toPeerIds, {data}, cb?)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Network(opts) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
Network.parent = EventEmitter;
|
||||||
|
// Allows subscribing to the following events:
|
||||||
|
// Network#on('networkChange', listener);
|
||||||
|
// Network#on('data', listener);
|
||||||
|
Network.prototype.start = function(callback) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
Network.prototype.send = function(peerIds, data, cb) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
Network.prototype.connectTo = function(peerId, openCallback, closeCallback) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
Network.prototype.disconnect = function(peerId, cb) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = require('soop')(Network);
|
||||||
|
|
@ -15,7 +15,7 @@ var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function CopayPeer(opts) {
|
function Network(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
this.peerId = opts.peerId;
|
this.peerId = opts.peerId;
|
||||||
this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
|
this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
|
||||||
|
|
@ -24,10 +24,10 @@ function CopayPeer(opts) {
|
||||||
this.connectedPeers = [];
|
this.connectedPeers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
CopayPeer.parent=EventEmitter;
|
Network.parent=EventEmitter;
|
||||||
|
|
||||||
// Array helpers
|
// Array helpers
|
||||||
CopayPeer._arrayDiff = function(a, b) {
|
Network._arrayDiff = function(a, b) {
|
||||||
var seen = [];
|
var seen = [];
|
||||||
var diff = [];
|
var diff = [];
|
||||||
|
|
||||||
|
|
@ -41,20 +41,20 @@ CopayPeer._arrayDiff = function(a, b) {
|
||||||
return diff;
|
return diff;
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer._inArray = function(el, array) {
|
Network._inArray = function(el, array) {
|
||||||
return array.indexOf(el) > -1;
|
return array.indexOf(el) > -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer._arrayPushOnce = function(el, array) {
|
Network._arrayPushOnce = function(el, array) {
|
||||||
var ret = false;
|
var ret = false;
|
||||||
if (!CopayPeer._inArray(el, array)) {
|
if (!Network._inArray(el, array)) {
|
||||||
array.push(el);
|
array.push(el);
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer._arrayRemove = function(el, array) {
|
Network._arrayRemove = function(el, array) {
|
||||||
var pos = array.indexOf(el);
|
var pos = array.indexOf(el);
|
||||||
if (pos >= 0) array.splice(pos, 1);
|
if (pos >= 0) array.splice(pos, 1);
|
||||||
|
|
||||||
|
|
@ -62,21 +62,21 @@ CopayPeer._arrayRemove = function(el, array) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
CopayPeer.prototype._showConnectedPeers = function() {
|
Network.prototype._showConnectedPeers = function() {
|
||||||
console.log("### CONNECTED PEERS", this.connectedPeers);
|
console.log("### CONNECTED PEERS", this.connectedPeers);
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._onClose = function(peerId) {
|
Network.prototype._onClose = function(peerId) {
|
||||||
console.log('[CopayPeer.js.70] _onClose'); //TODO
|
console.log('[Network.js.70] _onClose'); //TODO
|
||||||
this.connectedPeers = CopayPeer._arrayRemove(peerId, this.connectedPeers);
|
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
|
||||||
this._notify();
|
this._notify();
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._connectToPeers = function(peerIds) {
|
Network.prototype._connectToPeers = function(peerIds) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ret = false;
|
var ret = false;
|
||||||
var arrayDiff1= CopayPeer._arrayDiff(peerIds, this.connectedPeers);
|
var arrayDiff1= Network._arrayDiff(peerIds, this.connectedPeers);
|
||||||
var arrayDiff = CopayPeer._arrayDiff(arrayDiff1, [this.peerId]);
|
var arrayDiff = Network._arrayDiff(arrayDiff1, [this.peerId]);
|
||||||
arrayDiff.forEach(function(peerId) {
|
arrayDiff.forEach(function(peerId) {
|
||||||
console.log('### CONNECTING TO:', peerId);
|
console.log('### CONNECTING TO:', peerId);
|
||||||
self.connectTo(peerId);
|
self.connectTo(peerId);
|
||||||
|
|
@ -85,7 +85,7 @@ CopayPeer.prototype._connectToPeers = function(peerIds) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._onData = function(data, isInbound) {
|
Network.prototype._onData = function(data, isInbound) {
|
||||||
var obj;
|
var obj;
|
||||||
try {
|
try {
|
||||||
obj = JSON.parse(data);
|
obj = JSON.parse(data);
|
||||||
|
|
@ -108,7 +108,7 @@ CopayPeer.prototype._onData = function(data, isInbound) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._sendPeers = function(peerIds) {
|
Network.prototype._sendPeers = function(peerIds) {
|
||||||
console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds?peerIds: 'ALL');
|
console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds?peerIds: 'ALL');
|
||||||
this.send(peerIds, {
|
this.send(peerIds, {
|
||||||
type: 'peerList',
|
type: 'peerList',
|
||||||
|
|
@ -116,9 +116,9 @@ CopayPeer.prototype._sendPeers = function(peerIds) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._addPeer = function(peerId, isInbound) {
|
Network.prototype._addPeer = function(peerId, isInbound) {
|
||||||
|
|
||||||
var hasChanged = CopayPeer._arrayPushOnce(peerId, this.connectedPeers);
|
var hasChanged = Network._arrayPushOnce(peerId, this.connectedPeers);
|
||||||
|
|
||||||
|
|
||||||
if (isInbound && hasChanged) {
|
if (isInbound && hasChanged) {
|
||||||
|
|
@ -131,13 +131,13 @@ CopayPeer.prototype._addPeer = function(peerId, isInbound) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._setupConnectionHandlers = function(
|
Network.prototype._setupConnectionHandlers = function(
|
||||||
dataConn, isInbound, openCallback, closeCallback) {
|
dataConn, isInbound, openCallback, closeCallback) {
|
||||||
|
|
||||||
var self=this;
|
var self=this;
|
||||||
|
|
||||||
dataConn.on('open', function() {
|
dataConn.on('open', function() {
|
||||||
if (!CopayPeer._inArray(dataConn.peer, self.connectedPeers)) {
|
if (!Network._inArray(dataConn.peer, self.connectedPeers)) {
|
||||||
|
|
||||||
console.log('### DATA CONNECTION READY TO: ADDING PEER: %s (inbound: %s)',
|
console.log('### DATA CONNECTION READY TO: ADDING PEER: %s (inbound: %s)',
|
||||||
dataConn.peer, isInbound);
|
dataConn.peer, isInbound);
|
||||||
|
|
@ -165,13 +165,13 @@ CopayPeer.prototype._setupConnectionHandlers = function(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._notify = function(newPeer) {
|
Network.prototype._notify = function(newPeer) {
|
||||||
console.log('[CopayPeer.js.168:_notify:]'); //TODO
|
console.log('[Network.js.168:_notify:]'); //TODO
|
||||||
this._showConnectedPeers();
|
this._showConnectedPeers();
|
||||||
this.emit('networkChange', newPeer);
|
this.emit('networkChange', newPeer);
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._setupPeerHandlers = function(openCallback) {
|
Network.prototype._setupPeerHandlers = function(openCallback) {
|
||||||
var self=this;
|
var self=this;
|
||||||
var p = this.peer;
|
var p = this.peer;
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ CopayPeer.prototype._setupPeerHandlers = function(openCallback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype.start = function(openCallback) {
|
Network.prototype.start = function(openCallback) {
|
||||||
// Start PeerJS Peer
|
// Start PeerJS Peer
|
||||||
this.peer = new Peer(this.peerId, {
|
this.peer = new Peer(this.peerId, {
|
||||||
key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
|
key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
|
||||||
|
|
@ -218,7 +218,7 @@ CopayPeer.prototype.start = function(openCallback) {
|
||||||
this._setupPeerHandlers(openCallback);
|
this._setupPeerHandlers(openCallback);
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._sendToOne = function(peerId, data, cb) {
|
Network.prototype._sendToOne = function(peerId, data, cb) {
|
||||||
if (peerId !== this.peerId) {
|
if (peerId !== this.peerId) {
|
||||||
var conns = this.peer.connections[peerId];
|
var conns = this.peer.connections[peerId];
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ CopayPeer.prototype._sendToOne = function(peerId, data, cb) {
|
||||||
if (typeof cb === 'function') cb();
|
if (typeof cb === 'function') cb();
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype.send = function(peerIds, data, cb) {
|
Network.prototype.send = function(peerIds, data, cb) {
|
||||||
var self=this;
|
var self=this;
|
||||||
|
|
||||||
if (!peerIds) {
|
if (!peerIds) {
|
||||||
|
|
@ -258,7 +258,7 @@ CopayPeer.prototype.send = function(peerIds, data, cb) {
|
||||||
self._sendToOne(peerIds, data, cb);
|
self._sendToOne(peerIds, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
|
Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
console.log('### STARTING TO CONNECT TO:' + peerId );
|
console.log('### STARTING TO CONNECT TO:' + peerId );
|
||||||
|
|
@ -273,14 +273,14 @@ CopayPeer.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
|
||||||
self._setupConnectionHandlers(dataConn, false, openCallback, closeCallback);
|
self._setupConnectionHandlers(dataConn, false, openCallback, closeCallback);
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype.disconnect = function(peerId, cb) {
|
Network.prototype.disconnect = function(peerId, cb) {
|
||||||
console.log('[CopayPeer.js.268:disconnect:]'); //TODO
|
console.log('[Network.js.268:disconnect:]'); //TODO
|
||||||
var self = this;
|
var self = this;
|
||||||
self.closing = 1;
|
self.closing = 1;
|
||||||
|
|
||||||
this.send(null, { type: 'disconnect' }, function() {
|
this.send(null, { type: 'disconnect' }, function() {
|
||||||
|
|
||||||
console.log('[CopayPeer.js.273] disconnect CB'); //TODO
|
console.log('[Network.js.273] disconnect CB'); //TODO
|
||||||
self.connectedPeers = [];
|
self.connectedPeers = [];
|
||||||
self.peerId = null;
|
self.peerId = null;
|
||||||
if (self.peer) {
|
if (self.peer) {
|
||||||
|
|
@ -293,4 +293,4 @@ console.log('[CopayPeer.js.273] disconnect CB'); //TODO
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = require('soop')(CopayPeer);
|
module.exports = require('soop')(Network);
|
||||||
24
js/models/storage/Base.js
Normal file
24
js/models/storage/Base.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
|
function Storage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// get value by key
|
||||||
|
Storage.prototype.get = function(k) {
|
||||||
|
};
|
||||||
|
|
||||||
|
// set value for key
|
||||||
|
Storage.prototype.set = function(k,v) {
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove value for key
|
||||||
|
Storage.prototype.remove = function(k) {
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove all values
|
||||||
|
Storage.prototype.clearAll = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = require('soop')(Storage);
|
||||||
29
js/models/storage/Encrypted.js
Normal file
29
js/models/storage/Encrypted.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
|
function Storage() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// get value by key
|
||||||
|
Storage.prototype.get = function(k) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
// set value for key
|
||||||
|
Storage.prototype.set = function(k,v) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove value for key
|
||||||
|
Storage.prototype.remove = function(k) {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove all values
|
||||||
|
Storage.prototype.clearAll = function() {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = require('soop')(Storage);
|
||||||
|
|
@ -6,19 +6,22 @@ function Storage() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get value by key
|
||||||
Storage.prototype.get = function(k) {
|
Storage.prototype.get = function(k) {
|
||||||
return JSON.parse(localStorage.getItem(k));
|
return JSON.parse(localStorage.getItem(k));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// set value for key
|
||||||
Storage.prototype.set = function(k,v) {
|
Storage.prototype.set = function(k,v) {
|
||||||
localStorage.setItem(k, JSON.stringify(v));
|
localStorage.setItem(k, JSON.stringify(v));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// remove value for key
|
||||||
Storage.prototype.remove = function(k) {
|
Storage.prototype.remove = function(k) {
|
||||||
localStorage.removeItem(k);
|
localStorage.removeItem(k);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// remove all values
|
||||||
Storage.prototype.clearAll = function() {
|
Storage.prototype.clearAll = function() {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
};
|
};
|
||||||
|
|
@ -244,7 +244,7 @@ console.log('[network.js.178:pkr:]',pkr); //TODO
|
||||||
|
|
||||||
// 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.Network({
|
||||||
apiKey: config.p2pApiKey,
|
apiKey: config.p2pApiKey,
|
||||||
debug: config.p2pDebug,
|
debug: config.p2pDebug,
|
||||||
maxPeers: config.maxPeers, // TODO: This should be on wallet configuration
|
maxPeers: config.maxPeers, // TODO: This should be on wallet configuration
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ var networks = bitcore.networks;
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var BitcorePrivateKey = bitcore.PrivateKey;
|
var BitcorePrivateKey = bitcore.PrivateKey;
|
||||||
var copay = copay || require('../copay');
|
var copay = copay || require('../copay');
|
||||||
var PrivateKey = copay.PrivateKey || require('../js/models/PrivateKey');
|
var PrivateKey = copay.PrivateKey || require('../js/models/core/PrivateKey');
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
networkName:'livenet',
|
networkName:'livenet',
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ var copay = copay || require('../copay');
|
||||||
var fakeStorage = copay.FakeStorage;
|
var fakeStorage = copay.FakeStorage;
|
||||||
var PrivateKey = copay.PrivateKey || require('../js/models/PrivateKey');
|
var PrivateKey = copay.PrivateKey || require('../js/models/PrivateKey');
|
||||||
var TxProposals = copay.TxProposals || require('../js/models/TxProposal');
|
var TxProposals = copay.TxProposals || require('../js/models/TxProposal');
|
||||||
var PublicKeyRing = (typeof process.versions === 'undefined') ? copay.PublicKeyRing :
|
var PublicKeyRing = is_browser ? copay.PublicKeyRing :
|
||||||
require('soop').load('../js/models/PublicKeyRing', {Storage: fakeStorage});
|
require('soop').load('../js/models/core/PublicKeyRing', {Storage: fakeStorage});
|
||||||
|
var is_browser = (typeof process.versions === 'undefined')
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
networkName:'livenet',
|
networkName:'livenet',
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ var chai = chai || require('chai');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
var bitcore = bitcore || require('bitcore');
|
var bitcore = bitcore || require('bitcore');
|
||||||
var copay = copay || require('../copay');
|
var copay = copay || require('../copay');
|
||||||
var Wallet = copay.Wallet || require('soop').load('../js/models/Wallet');
|
var Wallet = copay.Wallet || require('soop').load('../js/models/core/Wallet');
|
||||||
|
|
||||||
var ID = '933bf321393459b7';
|
var ID = '933bf321393459b7';
|
||||||
var copayers = [
|
var copayers = [
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue