13 changed files with 1419 additions and 1419 deletions
@ -1,49 +1,49 @@ |
|||||
Bitcoin.Address = function (bytes) { |
Bitcoin.Address = function (bytes) { |
||||
if ("string" == typeof bytes) { |
if ("string" == typeof bytes) { |
||||
bytes = Bitcoin.Address.decodeString(bytes); |
bytes = Bitcoin.Address.decodeString(bytes); |
||||
} |
} |
||||
this.hash = bytes; |
this.hash = bytes; |
||||
|
|
||||
this.version = 0x00; |
this.version = 0x00; |
||||
}; |
}; |
||||
|
|
||||
Bitcoin.Address.prototype.toString = function () { |
Bitcoin.Address.prototype.toString = function () { |
||||
// Get a copy of the hash
|
// Get a copy of the hash
|
||||
var hash = this.hash.slice(0); |
var hash = this.hash.slice(0); |
||||
|
|
||||
// Version
|
// Version
|
||||
hash.unshift(this.version); |
hash.unshift(this.version); |
||||
|
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
||||
|
|
||||
var bytes = hash.concat(checksum.slice(0,4)); |
var bytes = hash.concat(checksum.slice(0,4)); |
||||
|
|
||||
return Bitcoin.Base58.encode(bytes); |
return Bitcoin.Base58.encode(bytes); |
||||
}; |
}; |
||||
|
|
||||
Bitcoin.Address.prototype.getHashBase64 = function () { |
Bitcoin.Address.prototype.getHashBase64 = function () { |
||||
return Crypto.util.bytesToBase64(this.hash); |
return Crypto.util.bytesToBase64(this.hash); |
||||
}; |
}; |
||||
|
|
||||
Bitcoin.Address.decodeString = function (string) { |
Bitcoin.Address.decodeString = function (string) { |
||||
var bytes = Bitcoin.Base58.decode(string); |
var bytes = Bitcoin.Base58.decode(string); |
||||
|
|
||||
var hash = bytes.slice(0, 21); |
var hash = bytes.slice(0, 21); |
||||
|
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
||||
|
|
||||
if (checksum[0] != bytes[21] || |
if (checksum[0] != bytes[21] || |
||||
checksum[1] != bytes[22] || |
checksum[1] != bytes[22] || |
||||
checksum[2] != bytes[23] || |
checksum[2] != bytes[23] || |
||||
checksum[3] != bytes[24]) { |
checksum[3] != bytes[24]) { |
||||
throw "Checksum validation failed!"; |
throw "Checksum validation failed!"; |
||||
} |
} |
||||
|
|
||||
var version = hash.shift(); |
var version = hash.shift(); |
||||
|
|
||||
if (version != 0) { |
if (version != 0) { |
||||
throw "Version "+version+" not supported!"; |
throw "Version "+version+" not supported!"; |
||||
} |
} |
||||
|
|
||||
return hash; |
return hash; |
||||
}; |
}; |
||||
|
@ -1,62 +1,62 @@ |
|||||
Bitcoin.ECKey = (function () { |
Bitcoin.ECKey = (function () { |
||||
var ECDSA = Bitcoin.ECDSA; |
var ECDSA = Bitcoin.ECDSA; |
||||
var ecparams = getSECCurveByName("secp256k1"); |
var ecparams = getSECCurveByName("secp256k1"); |
||||
var rng = new SecureRandom(); |
var rng = new SecureRandom(); |
||||
|
|
||||
var ECKey = function (input) { |
var ECKey = function (input) { |
||||
if (!input) { |
if (!input) { |
||||
// Generate new key
|
// Generate new key
|
||||
var n = ecparams.getN(); |
var n = ecparams.getN(); |
||||
this.priv = ECDSA.getBigRandom(n); |
this.priv = ECDSA.getBigRandom(n); |
||||
} else if (input instanceof BigInteger) { |
} else if (input instanceof BigInteger) { |
||||
// Input is a private key value
|
// Input is a private key value
|
||||
this.priv = input; |
this.priv = input; |
||||
} else if (Bitcoin.Util.isArray(input)) { |
} else if (Bitcoin.Util.isArray(input)) { |
||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(input); |
this.priv = BigInteger.fromByteArrayUnsigned(input); |
||||
} else if ("string" == typeof input) { |
} else if ("string" == typeof input) { |
||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); |
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.getPub = function () { |
ECKey.prototype.getPub = function () { |
||||
if (this.pub) return this.pub; |
if (this.pub) return this.pub; |
||||
|
|
||||
return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); |
return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.getPubKeyHash = function () { |
ECKey.prototype.getPubKeyHash = function () { |
||||
if (this.pubKeyHash) return this.pubKeyHash; |
if (this.pubKeyHash) return this.pubKeyHash; |
||||
|
|
||||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); |
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.getBitcoinAddress = function () { |
ECKey.prototype.getBitcoinAddress = function () { |
||||
var hash = this.getPubKeyHash(); |
var hash = this.getPubKeyHash(); |
||||
var addr = new Bitcoin.Address(hash); |
var addr = new Bitcoin.Address(hash); |
||||
return addr; |
return addr; |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.setPub = function (pub) { |
ECKey.prototype.setPub = function (pub) { |
||||
this.pub = pub; |
this.pub = pub; |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.toString = function (format) { |
ECKey.prototype.toString = function (format) { |
||||
if (format === "base64") { |
if (format === "base64") { |
||||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); |
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); |
||||
} else { |
} else { |
||||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); |
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.sign = function (hash) { |
ECKey.prototype.sign = function (hash) { |
||||
return ECDSA.sign(hash, this.priv); |
return ECDSA.sign(hash, this.priv); |
||||
}; |
}; |
||||
|
|
||||
ECKey.prototype.verify = function (hash, sig) { |
ECKey.prototype.verify = function (hash, sig) { |
||||
return ECDSA.verify(hash, sig, this.getPub()); |
return ECDSA.verify(hash, sig, this.getPub()); |
||||
}; |
}; |
||||
|
|
||||
return ECKey; |
return ECKey; |
||||
})(); |
})(); |
||||
|
@ -1,154 +1,154 @@ |
|||||
(function () { |
(function () { |
||||
var Opcode = Bitcoin.Opcode = function (num) { |
var Opcode = Bitcoin.Opcode = function (num) { |
||||
this.code = num; |
this.code = num; |
||||
}; |
}; |
||||
|
|
||||
Opcode.prototype.toString = function () { |
Opcode.prototype.toString = function () { |
||||
return Opcode.reverseMap[this.code]; |
return Opcode.reverseMap[this.code]; |
||||
}; |
}; |
||||
|
|
||||
Opcode.map = { |
Opcode.map = { |
||||
// push value
|
// push value
|
||||
OP_0 : 0, |
OP_0 : 0, |
||||
OP_FALSE : 0, |
OP_FALSE : 0, |
||||
OP_PUSHDATA1 : 76, |
OP_PUSHDATA1 : 76, |
||||
OP_PUSHDATA2 : 77, |
OP_PUSHDATA2 : 77, |
||||
OP_PUSHDATA4 : 78, |
OP_PUSHDATA4 : 78, |
||||
OP_1NEGATE : 79, |
OP_1NEGATE : 79, |
||||
OP_RESERVED : 80, |
OP_RESERVED : 80, |
||||
OP_1 : 81, |
OP_1 : 81, |
||||
OP_TRUE : 81, |
OP_TRUE : 81, |
||||
OP_2 : 82, |
OP_2 : 82, |
||||
OP_3 : 83, |
OP_3 : 83, |
||||
OP_4 : 84, |
OP_4 : 84, |
||||
OP_5 : 85, |
OP_5 : 85, |
||||
OP_6 : 86, |
OP_6 : 86, |
||||
OP_7 : 87, |
OP_7 : 87, |
||||
OP_8 : 88, |
OP_8 : 88, |
||||
OP_9 : 89, |
OP_9 : 89, |
||||
OP_10 : 90, |
OP_10 : 90, |
||||
OP_11 : 91, |
OP_11 : 91, |
||||
OP_12 : 92, |
OP_12 : 92, |
||||
OP_13 : 93, |
OP_13 : 93, |
||||
OP_14 : 94, |
OP_14 : 94, |
||||
OP_15 : 95, |
OP_15 : 95, |
||||
OP_16 : 96, |
OP_16 : 96, |
||||
|
|
||||
// control
|
// control
|
||||
OP_NOP : 97, |
OP_NOP : 97, |
||||
OP_VER : 98, |
OP_VER : 98, |
||||
OP_IF : 99, |
OP_IF : 99, |
||||
OP_NOTIF : 100, |
OP_NOTIF : 100, |
||||
OP_VERIF : 101, |
OP_VERIF : 101, |
||||
OP_VERNOTIF : 102, |
OP_VERNOTIF : 102, |
||||
OP_ELSE : 103, |
OP_ELSE : 103, |
||||
OP_ENDIF : 104, |
OP_ENDIF : 104, |
||||
OP_VERIFY : 105, |
OP_VERIFY : 105, |
||||
OP_RETURN : 106, |
OP_RETURN : 106, |
||||
|
|
||||
// stack ops
|
// stack ops
|
||||
OP_TOALTSTACK : 107, |
OP_TOALTSTACK : 107, |
||||
OP_FROMALTSTACK : 108, |
OP_FROMALTSTACK : 108, |
||||
OP_2DROP : 109, |
OP_2DROP : 109, |
||||
OP_2DUP : 110, |
OP_2DUP : 110, |
||||
OP_3DUP : 111, |
OP_3DUP : 111, |
||||
OP_2OVER : 112, |
OP_2OVER : 112, |
||||
OP_2ROT : 113, |
OP_2ROT : 113, |
||||
OP_2SWAP : 114, |
OP_2SWAP : 114, |
||||
OP_IFDUP : 115, |
OP_IFDUP : 115, |
||||
OP_DEPTH : 116, |
OP_DEPTH : 116, |
||||
OP_DROP : 117, |
OP_DROP : 117, |
||||
OP_DUP : 118, |
OP_DUP : 118, |
||||
OP_NIP : 119, |
OP_NIP : 119, |
||||
OP_OVER : 120, |
OP_OVER : 120, |
||||
OP_PICK : 121, |
OP_PICK : 121, |
||||
OP_ROLL : 122, |
OP_ROLL : 122, |
||||
OP_ROT : 123, |
OP_ROT : 123, |
||||
OP_SWAP : 124, |
OP_SWAP : 124, |
||||
OP_TUCK : 125, |
OP_TUCK : 125, |
||||
|
|
||||
// splice ops
|
// splice ops
|
||||
OP_CAT : 126, |
OP_CAT : 126, |
||||
OP_SUBSTR : 127, |
OP_SUBSTR : 127, |
||||
OP_LEFT : 128, |
OP_LEFT : 128, |
||||
OP_RIGHT : 129, |
OP_RIGHT : 129, |
||||
OP_SIZE : 130, |
OP_SIZE : 130, |
||||
|
|
||||
// bit logic
|
// bit logic
|
||||
OP_INVERT : 131, |
OP_INVERT : 131, |
||||
OP_AND : 132, |
OP_AND : 132, |
||||
OP_OR : 133, |
OP_OR : 133, |
||||
OP_XOR : 134, |
OP_XOR : 134, |
||||
OP_EQUAL : 135, |
OP_EQUAL : 135, |
||||
OP_EQUALVERIFY : 136, |
OP_EQUALVERIFY : 136, |
||||
OP_RESERVED1 : 137, |
OP_RESERVED1 : 137, |
||||
OP_RESERVED2 : 138, |
OP_RESERVED2 : 138, |
||||
|
|
||||
// numeric
|
// numeric
|
||||
OP_1ADD : 139, |
OP_1ADD : 139, |
||||
OP_1SUB : 140, |
OP_1SUB : 140, |
||||
OP_2MUL : 141, |
OP_2MUL : 141, |
||||
OP_2DIV : 142, |
OP_2DIV : 142, |
||||
OP_NEGATE : 143, |
OP_NEGATE : 143, |
||||
OP_ABS : 144, |
OP_ABS : 144, |
||||
OP_NOT : 145, |
OP_NOT : 145, |
||||
OP_0NOTEQUAL : 146, |
OP_0NOTEQUAL : 146, |
||||
|
|
||||
OP_ADD : 147, |
OP_ADD : 147, |
||||
OP_SUB : 148, |
OP_SUB : 148, |
||||
OP_MUL : 149, |
OP_MUL : 149, |
||||
OP_DIV : 150, |
OP_DIV : 150, |
||||
OP_MOD : 151, |
OP_MOD : 151, |
||||
OP_LSHIFT : 152, |
OP_LSHIFT : 152, |
||||
OP_RSHIFT : 153, |
OP_RSHIFT : 153, |
||||
|
|
||||
OP_BOOLAND : 154, |
OP_BOOLAND : 154, |
||||
OP_BOOLOR : 155, |
OP_BOOLOR : 155, |
||||
OP_NUMEQUAL : 156, |
OP_NUMEQUAL : 156, |
||||
OP_NUMEQUALVERIFY : 157, |
OP_NUMEQUALVERIFY : 157, |
||||
OP_NUMNOTEQUAL : 158, |
OP_NUMNOTEQUAL : 158, |
||||
OP_LESSTHAN : 159, |
OP_LESSTHAN : 159, |
||||
OP_GREATERTHAN : 160, |
OP_GREATERTHAN : 160, |
||||
OP_LESSTHANOREQUAL : 161, |
OP_LESSTHANOREQUAL : 161, |
||||
OP_GREATERTHANOREQUAL : 162, |
OP_GREATERTHANOREQUAL : 162, |
||||
OP_MIN : 163, |
OP_MIN : 163, |
||||
OP_MAX : 164, |
OP_MAX : 164, |
||||
|
|
||||
OP_WITHIN : 165, |
OP_WITHIN : 165, |
||||
|
|
||||
// crypto
|
// crypto
|
||||
OP_RIPEMD160 : 166, |
OP_RIPEMD160 : 166, |
||||
OP_SHA1 : 167, |
OP_SHA1 : 167, |
||||
OP_SHA256 : 168, |
OP_SHA256 : 168, |
||||
OP_HASH160 : 169, |
OP_HASH160 : 169, |
||||
OP_HASH256 : 170, |
OP_HASH256 : 170, |
||||
OP_CODESEPARATOR : 171, |
OP_CODESEPARATOR : 171, |
||||
OP_CHECKSIG : 172, |
OP_CHECKSIG : 172, |
||||
OP_CHECKSIGVERIFY : 173, |
OP_CHECKSIGVERIFY : 173, |
||||
OP_CHECKMULTISIG : 174, |
OP_CHECKMULTISIG : 174, |
||||
OP_CHECKMULTISIGVERIFY : 175, |
OP_CHECKMULTISIGVERIFY : 175, |
||||
|
|
||||
// expansion
|
// expansion
|
||||
OP_NOP1 : 176, |
OP_NOP1 : 176, |
||||
OP_NOP2 : 177, |
OP_NOP2 : 177, |
||||
OP_NOP3 : 178, |
OP_NOP3 : 178, |
||||
OP_NOP4 : 179, |
OP_NOP4 : 179, |
||||
OP_NOP5 : 180, |
OP_NOP5 : 180, |
||||
OP_NOP6 : 181, |
OP_NOP6 : 181, |
||||
OP_NOP7 : 182, |
OP_NOP7 : 182, |
||||
OP_NOP8 : 183, |
OP_NOP8 : 183, |
||||
OP_NOP9 : 184, |
OP_NOP9 : 184, |
||||
OP_NOP10 : 185, |
OP_NOP10 : 185, |
||||
|
|
||||
// template matching params
|
// template matching params
|
||||
OP_PUBKEYHASH : 253, |
OP_PUBKEYHASH : 253, |
||||
OP_PUBKEY : 254, |
OP_PUBKEY : 254, |
||||
OP_INVALIDOPCODE : 255, |
OP_INVALIDOPCODE : 255, |
||||
}; |
}; |
||||
|
|
||||
Opcode.reverseMap = []; |
Opcode.reverseMap = []; |
||||
|
|
||||
for (var i in Opcode.map) { |
for (var i in Opcode.map) { |
||||
Opcode.reverseMap[Opcode.map[i]] = i; |
Opcode.reverseMap[Opcode.map[i]] = i; |
||||
} |
} |
||||
})(); |
})(); |
||||
|
@ -1,183 +1,183 @@ |
|||||
(function () { |
(function () { |
||||
var Opcode = Bitcoin.Opcode; |
var Opcode = Bitcoin.Opcode; |
||||
|
|
||||
// Make opcodes available as pseudo-constants
|
// Make opcodes available as pseudo-constants
|
||||
for (var i in Opcode.map) { |
for (var i in Opcode.map) { |
||||
eval("var " + i + " = " + Opcode.map[i] + ";"); |
eval("var " + i + " = " + Opcode.map[i] + ";"); |
||||
} |
} |
||||
|
|
||||
var Script = Bitcoin.Script = function (data) { |
var Script = Bitcoin.Script = function (data) { |
||||
if (!data) { |
if (!data) { |
||||
this.buffer = []; |
this.buffer = []; |
||||
} else if ("string" == typeof data) { |
} else if ("string" == typeof data) { |
||||
this.buffer = Crypto.util.base64ToBytes(data); |
this.buffer = Crypto.util.base64ToBytes(data); |
||||
} else if (Bitcoin.Util.isArray(data)) { |
} else if (Bitcoin.Util.isArray(data)) { |
||||
this.buffer = data; |
this.buffer = data; |
||||
} else if (data instanceof Script) { |
} else if (data instanceof Script) { |
||||
this.buffer = data.buffer; |
this.buffer = data.buffer; |
||||
} else { |
} else { |
||||
throw new Error("Invalid script"); |
throw new Error("Invalid script"); |
||||
} |
} |
||||
|
|
||||
this.parse(); |
this.parse(); |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.parse = function () { |
Script.prototype.parse = function () { |
||||
var self = this; |
var self = this; |
||||
|
|
||||
this.chunks = []; |
this.chunks = []; |
||||
|
|
||||
// Cursor
|
// Cursor
|
||||
var i = 0; |
var i = 0; |
||||
|
|
||||
// Read n bytes and store result as a chunk
|
// Read n bytes and store result as a chunk
|
||||
function readChunk(n) { |
function readChunk(n) { |
||||
self.chunks.push(self.buffer.slice(i, i + n)); |
self.chunks.push(self.buffer.slice(i, i + n)); |
||||
i += n; |
i += n; |
||||
}; |
}; |
||||
|
|
||||
while (i < this.buffer.length) { |
while (i < this.buffer.length) { |
||||
var opcode = this.buffer[i++]; |
var opcode = this.buffer[i++]; |
||||
if (opcode >= 0xF0) { |
if (opcode >= 0xF0) { |
||||
// Two byte opcode
|
// Two byte opcode
|
||||
opcode = (opcode << 8) | this.buffer[i++]; |
opcode = (opcode << 8) | this.buffer[i++]; |
||||
} |
} |
||||
|
|
||||
var len; |
var len; |
||||
if (opcode > 0 && opcode < OP_PUSHDATA1) { |
if (opcode > 0 && opcode < OP_PUSHDATA1) { |
||||
// Read some bytes of data, opcode value is the length of data
|
// Read some bytes of data, opcode value is the length of data
|
||||
readChunk(opcode); |
readChunk(opcode); |
||||
} else if (opcode == OP_PUSHDATA1) { |
} else if (opcode == OP_PUSHDATA1) { |
||||
len = this.buffer[i++]; |
len = this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else if (opcode == OP_PUSHDATA2) { |
} else if (opcode == OP_PUSHDATA2) { |
||||
len = (this.buffer[i++] << 8) | this.buffer[i++]; |
len = (this.buffer[i++] << 8) | this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else if (opcode == OP_PUSHDATA4) { |
} else if (opcode == OP_PUSHDATA4) { |
||||
len = (this.buffer[i++] << 24) | |
len = (this.buffer[i++] << 24) | |
||||
(this.buffer[i++] << 16) | |
(this.buffer[i++] << 16) | |
||||
(this.buffer[i++] << 8) | |
(this.buffer[i++] << 8) | |
||||
this.buffer[i++]; |
this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else { |
} else { |
||||
this.chunks.push(opcode); |
this.chunks.push(opcode); |
||||
} |
} |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.getOutType = function () |
Script.prototype.getOutType = function () |
||||
{ |
{ |
||||
if (this.chunks.length == 5 && |
if (this.chunks.length == 5 && |
||||
this.chunks[0] == OP_DUP && |
this.chunks[0] == OP_DUP && |
||||
this.chunks[1] == OP_HASH160 && |
this.chunks[1] == OP_HASH160 && |
||||
this.chunks[3] == OP_EQUALVERIFY && |
this.chunks[3] == OP_EQUALVERIFY && |
||||
this.chunks[4] == OP_CHECKSIG) { |
this.chunks[4] == OP_CHECKSIG) { |
||||
|
|
||||
// Transfer to Bitcoin address
|
// Transfer to Bitcoin address
|
||||
return 'Address'; |
return 'Address'; |
||||
} else if (this.chunks.length == 2 && |
} else if (this.chunks.length == 2 && |
||||
this.chunks[1] == OP_CHECKSIG) { |
this.chunks[1] == OP_CHECKSIG) { |
||||
|
|
||||
// Transfer to IP address
|
// Transfer to IP address
|
||||
return 'Pubkey'; |
return 'Pubkey'; |
||||
} else { |
} else { |
||||
return 'Strange'; |
return 'Strange'; |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.simpleOutPubKeyHash = function () |
Script.prototype.simpleOutPubKeyHash = function () |
||||
{ |
{ |
||||
switch (this.getOutType()) { |
switch (this.getOutType()) { |
||||
case 'Address': |
case 'Address': |
||||
return this.chunks[2]; |
return this.chunks[2]; |
||||
case 'Pubkey': |
case 'Pubkey': |
||||
return Bitcoin.Util.sha256ripe160(this.chunks[0]); |
return Bitcoin.Util.sha256ripe160(this.chunks[0]); |
||||
default: |
default: |
||||
throw new Error("Encountered non-standard scriptPubKey"); |
throw new Error("Encountered non-standard scriptPubKey"); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.getInType = function () |
Script.prototype.getInType = function () |
||||
{ |
{ |
||||
if (this.chunks.length == 1) { |
if (this.chunks.length == 1) { |
||||
// Direct IP to IP transactions only have the public key in their scriptSig.
|
// Direct IP to IP transactions only have the public key in their scriptSig.
|
||||
return 'Pubkey'; |
return 'Pubkey'; |
||||
} else if (this.chunks.length == 2 && |
} else if (this.chunks.length == 2 && |
||||
Bitcoin.Util.isArray(this.chunks[0]) && |
Bitcoin.Util.isArray(this.chunks[0]) && |
||||
Bitcoin.Util.isArray(this.chunks[1])) { |
Bitcoin.Util.isArray(this.chunks[1])) { |
||||
return 'Address'; |
return 'Address'; |
||||
} else { |
} else { |
||||
console.log(this.chunks); |
console.log(this.chunks); |
||||
throw new Error("Encountered non-standard scriptSig"); |
throw new Error("Encountered non-standard scriptSig"); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.simpleInPubKey = function () |
Script.prototype.simpleInPubKey = function () |
||||
{ |
{ |
||||
switch (this.getInType()) { |
switch (this.getInType()) { |
||||
case 'Address': |
case 'Address': |
||||
return this.chunks[1]; |
return this.chunks[1]; |
||||
case 'Pubkey': |
case 'Pubkey': |
||||
return this.chunks[0]; |
return this.chunks[0]; |
||||
default: |
default: |
||||
throw new Error("Encountered non-standard scriptSig"); |
throw new Error("Encountered non-standard scriptSig"); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.simpleInPubKeyHash = function () |
Script.prototype.simpleInPubKeyHash = function () |
||||
{ |
{ |
||||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); |
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.writeOp = function (opcode) |
Script.prototype.writeOp = function (opcode) |
||||
{ |
{ |
||||
this.buffer.push(opcode); |
this.buffer.push(opcode); |
||||
this.chunks.push(opcode); |
this.chunks.push(opcode); |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.writeBytes = function (data) |
Script.prototype.writeBytes = function (data) |
||||
{ |
{ |
||||
if (data.length < OP_PUSHDATA1) { |
if (data.length < OP_PUSHDATA1) { |
||||
this.buffer.push(data.length); |
this.buffer.push(data.length); |
||||
} else if (data.length <= 0xff) { |
} else if (data.length <= 0xff) { |
||||
this.buffer.push(OP_PUSHDATA1); |
this.buffer.push(OP_PUSHDATA1); |
||||
this.buffer.push(data.length); |
this.buffer.push(data.length); |
||||
} else if (data.length <= 0xffff) { |
} else if (data.length <= 0xffff) { |
||||
this.buffer.push(OP_PUSHDATA2); |
this.buffer.push(OP_PUSHDATA2); |
||||
this.buffer.push(data.length & 0xff); |
this.buffer.push(data.length & 0xff); |
||||
this.buffer.push((data.length >>> 8) & 0xff); |
this.buffer.push((data.length >>> 8) & 0xff); |
||||
} else { |
} else { |
||||
this.buffer.push(OP_PUSHDATA4); |
this.buffer.push(OP_PUSHDATA4); |
||||
this.buffer.push(data.length & 0xff); |
this.buffer.push(data.length & 0xff); |
||||
this.buffer.push((data.length >>> 8) & 0xff); |
this.buffer.push((data.length >>> 8) & 0xff); |
||||
this.buffer.push((data.length >>> 16) & 0xff); |
this.buffer.push((data.length >>> 16) & 0xff); |
||||
this.buffer.push((data.length >>> 24) & 0xff); |
this.buffer.push((data.length >>> 24) & 0xff); |
||||
} |
} |
||||
this.buffer = this.buffer.concat(data); |
this.buffer = this.buffer.concat(data); |
||||
this.chunks.push(data); |
this.chunks.push(data); |
||||
}; |
}; |
||||
|
|
||||
Script.createOutputScript = function (address) |
Script.createOutputScript = function (address) |
||||
{ |
{ |
||||
var script = new Script(); |
var script = new Script(); |
||||
script.writeOp(OP_DUP); |
script.writeOp(OP_DUP); |
||||
script.writeOp(OP_HASH160); |
script.writeOp(OP_HASH160); |
||||
script.writeBytes(address.hash); |
script.writeBytes(address.hash); |
||||
script.writeOp(OP_EQUALVERIFY); |
script.writeOp(OP_EQUALVERIFY); |
||||
script.writeOp(OP_CHECKSIG); |
script.writeOp(OP_CHECKSIG); |
||||
return script; |
return script; |
||||
}; |
}; |
||||
|
|
||||
Script.createInputScript = function (signature, pubKey) |
Script.createInputScript = function (signature, pubKey) |
||||
{ |
{ |
||||
var script = new Script(); |
var script = new Script(); |
||||
script.writeBytes(signature); |
script.writeBytes(signature); |
||||
script.writeBytes(pubKey); |
script.writeBytes(pubKey); |
||||
return script; |
return script; |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.clone = function () |
Script.prototype.clone = function () |
||||
{ |
{ |
||||
return new Script(this.buffer); |
return new Script(this.buffer); |
||||
}; |
}; |
||||
})(); |
})(); |
||||
|
@ -1,245 +1,245 @@ |
|||||
Bitcoin.Wallet = (function () { |
Bitcoin.Wallet = (function () { |
||||
var Script = Bitcoin.Script, |
var Script = Bitcoin.Script, |
||||
TransactionIn = Bitcoin.TransactionIn, |
TransactionIn = Bitcoin.TransactionIn, |
||||
TransactionOut = Bitcoin.TransactionOut; |
TransactionOut = Bitcoin.TransactionOut; |
||||
|
|
||||
var Wallet = function () { |
var Wallet = function () { |
||||
// Keychain
|
// Keychain
|
||||
var keys = []; |
var keys = []; |
||||
this.addressHashes = []; |
this.addressHashes = []; |
||||
|
|
||||
// Transaction data
|
// Transaction data
|
||||
this.txIndex = {}; |
this.txIndex = {}; |
||||
this.unspentOuts = []; |
this.unspentOuts = []; |
||||
|
|
||||
// Other fields
|
// Other fields
|
||||
this.addressPointer = 0; |
this.addressPointer = 0; |
||||
|
|
||||
this.addKey = function (key, pub) { |
this.addKey = function (key, pub) { |
||||
if (!(key instanceof Bitcoin.ECKey)) { |
if (!(key instanceof Bitcoin.ECKey)) { |
||||
key = new Bitcoin.ECKey(key); |
key = new Bitcoin.ECKey(key); |
||||
} |
} |
||||
keys.push(key); |
keys.push(key); |
||||
|
|
||||
if (pub) { |
if (pub) { |
||||
if ("string" === typeof pub) { |
if ("string" === typeof pub) { |
||||
pub = Crypto.util.base64ToBytes(pub); |
pub = Crypto.util.base64ToBytes(pub); |
||||
} |
|
||||
key.pub = pub; |
|
||||
} |
|
||||
|
|
||||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); |
|
||||
}; |
|
||||
|
|
||||
this.addKeys = function (keys, pubs) { |
|
||||
if ("string" === typeof keys) { |
|
||||
keys = keys.split(','); |
|
||||
} |
|
||||
if ("string" === typeof pubs) { |
|
||||
pubs = pubs.split(','); |
|
||||
} |
|
||||
var i; |
|
||||
if (Array.isArray(pubs) && keys.length == pubs.length) { |
|
||||
for (i = 0; i < keys.length; i++) { |
|
||||
this.addKey(keys[i], pubs[i]); |
|
||||
} |
|
||||
} else { |
|
||||
for (i = 0; i < keys.length; i++) { |
|
||||
this.addKey(keys[i]); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
this.getKeys = function () { |
|
||||
var serializedWallet = []; |
|
||||
|
|
||||
for (var i = 0; i < keys.length; i++) { |
|
||||
serializedWallet.push(keys[i].toString('base64')); |
|
||||
} |
|
||||
|
|
||||
return serializedWallet; |
|
||||
}; |
|
||||
|
|
||||
this.getPubKeys = function () { |
|
||||
var pubs = []; |
|
||||
|
|
||||
for (var i = 0; i < keys.length; i++) { |
|
||||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); |
|
||||
} |
|
||||
|
|
||||
return pubs; |
|
||||
}; |
|
||||
|
|
||||
this.clear = function () { |
|
||||
keys = []; |
|
||||
}; |
|
||||
|
|
||||
this.getLength = function () { |
|
||||
return keys.length; |
|
||||
}; |
|
||||
|
|
||||
this.getAllAddresses = function () { |
|
||||
var addresses = []; |
|
||||
for (var i = 0; i < keys.length; i++) { |
|
||||
addresses.push(keys[i].getBitcoinAddress()); |
|
||||
} |
|
||||
return addresses; |
|
||||
}; |
|
||||
|
|
||||
this.getCurAddress = function () { |
|
||||
if (keys[this.addressPointer]) { |
|
||||
return keys[this.addressPointer].getBitcoinAddress(); |
|
||||
} else { |
|
||||
return null; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
this.getNextAddress = function () { |
|
||||
this.addressPointer++; |
|
||||
if(!keys[this.addressPointer]) { |
|
||||
this.generateAddress(); |
|
||||
} |
|
||||
return keys[this.addressPointer].getBitcoinAddress(); |
|
||||
}; |
|
||||
|
|
||||
this.signWithKey = function (pubKeyHash, hash) { |
|
||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); |
|
||||
for (var i = 0; i < this.addressHashes.length; i++) { |
|
||||
if (this.addressHashes[i] == pubKeyHash) { |
|
||||
return keys[i].sign(hash); |
|
||||
} |
|
||||
} |
|
||||
throw new Error("Missing key for signature"); |
|
||||
}; |
|
||||
|
|
||||
this.getPubKeyFromHash = function (pubKeyHash) { |
|
||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); |
|
||||
for (var i = 0; i < this.addressHashes.length; i++) { |
|
||||
if (this.addressHashes[i] == pubKeyHash) { |
|
||||
return keys[i].getPub(); |
|
||||
} |
|
||||
} |
|
||||
throw new Error("Hash unknown"); |
|
||||
}; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.generateAddress = function () { |
|
||||
this.addKey(new Bitcoin.ECKey()); |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.process = function (tx) { |
|
||||
if (this.txIndex[tx.hash]) return; |
|
||||
|
|
||||
var j; |
|
||||
var k; |
|
||||
var hash; |
|
||||
// Gather outputs
|
|
||||
for (j = 0; j < tx.outs.length; j++) { |
|
||||
var txout = new TransactionOut(tx.outs[j]); |
|
||||
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); |
|
||||
for (k = 0; k < this.addressHashes.length; k++) { |
|
||||
if (this.addressHashes[k] === hash) { |
|
||||
this.unspentOuts.push({tx: tx, index: j, out: txout}); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Remove spent outputs
|
|
||||
for (j = 0; j < tx.ins.length; j++) { |
|
||||
var txin = new TransactionIn(tx.ins[j]); |
|
||||
var pubkey = txin.script.simpleInPubKey(); |
|
||||
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); |
|
||||
for (k = 0; k < this.addressHashes.length; k++) { |
|
||||
if (this.addressHashes[k] === hash) { |
|
||||
for (var l = 0; l < this.unspentOuts.length; l++) { |
|
||||
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && |
|
||||
txin.outpoint.index == this.unspentOuts[l].index) { |
|
||||
this.unspentOuts.splice(l, 1); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
key.pub = pub; |
||||
|
} |
||||
|
|
||||
// Index transaction
|
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); |
||||
this.txIndex[tx.hash] = tx; |
|
||||
}; |
}; |
||||
|
|
||||
Wallet.prototype.getBalance = function () { |
this.addKeys = function (keys, pubs) { |
||||
var balance = BigInteger.valueOf(0); |
if ("string" === typeof keys) { |
||||
for (var i = 0; i < this.unspentOuts.length; i++) { |
keys = keys.split(','); |
||||
var txout = this.unspentOuts[i].out; |
} |
||||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value)); |
if ("string" === typeof pubs) { |
||||
|
pubs = pubs.split(','); |
||||
|
} |
||||
|
var i; |
||||
|
if (Array.isArray(pubs) && keys.length == pubs.length) { |
||||
|
for (i = 0; i < keys.length; i++) { |
||||
|
this.addKey(keys[i], pubs[i]); |
||||
} |
} |
||||
return balance; |
} else { |
||||
}; |
for (i = 0; i < keys.length; i++) { |
||||
|
this.addKey(keys[i]); |
||||
Wallet.prototype.createSend = function (address, sendValue, feeValue) { |
|
||||
var selectedOuts = []; |
|
||||
var txValue = sendValue.add(feeValue); |
|
||||
var availableValue = BigInteger.ZERO; |
|
||||
var i; |
|
||||
for (i = 0; i < this.unspentOuts.length; i++) { |
|
||||
selectedOuts.push(this.unspentOuts[i]); |
|
||||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); |
|
||||
|
|
||||
if (availableValue.compareTo(txValue) >= 0) break; |
|
||||
} |
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
if (availableValue.compareTo(txValue) < 0) { |
this.getKeys = function () { |
||||
throw new Error('Insufficient funds.'); |
var serializedWallet = []; |
||||
} |
|
||||
|
|
||||
|
|
||||
var changeValue = availableValue.subtract(txValue); |
|
||||
|
|
||||
var sendTx = new Bitcoin.Transaction(); |
for (var i = 0; i < keys.length; i++) { |
||||
|
serializedWallet.push(keys[i].toString('base64')); |
||||
|
} |
||||
|
|
||||
for (i = 0; i < selectedOuts.length; i++) { |
return serializedWallet; |
||||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); |
}; |
||||
} |
|
||||
|
|
||||
sendTx.addOutput(address, sendValue); |
this.getPubKeys = function () { |
||||
if (changeValue.compareTo(BigInteger.ZERO) > 0) { |
var pubs = []; |
||||
sendTx.addOutput(this.getNextAddress(), changeValue); |
|
||||
} |
|
||||
|
|
||||
var hashType = 1; // SIGHASH_ALL
|
for (var i = 0; i < keys.length; i++) { |
||||
|
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); |
||||
|
} |
||||
|
|
||||
for (i = 0; i < sendTx.ins.length; i++) { |
return pubs; |
||||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); |
}; |
||||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); |
|
||||
var signature = this.signWithKey(pubKeyHash, hash); |
|
||||
|
|
||||
// Append hash type
|
this.clear = function () { |
||||
signature.push(parseInt(hashType, 10)); |
keys = []; |
||||
|
}; |
||||
|
|
||||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); |
this.getLength = function () { |
||||
} |
return keys.length; |
||||
|
}; |
||||
|
|
||||
return sendTx; |
this.getAllAddresses = function () { |
||||
|
var addresses = []; |
||||
|
for (var i = 0; i < keys.length; i++) { |
||||
|
addresses.push(keys[i].getBitcoinAddress()); |
||||
|
} |
||||
|
return addresses; |
||||
}; |
}; |
||||
|
|
||||
Wallet.prototype.clearTransactions = function () { |
this.getCurAddress = function () { |
||||
this.txIndex = {}; |
if (keys[this.addressPointer]) { |
||||
this.unspentOuts = []; |
return keys[this.addressPointer].getBitcoinAddress(); |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
}; |
}; |
||||
|
|
||||
/** |
this.getNextAddress = function () { |
||||
* Check to see if a pubKeyHash belongs to this wallet. |
this.addressPointer++; |
||||
*/ |
if(!keys[this.addressPointer]) { |
||||
Wallet.prototype.hasHash = function (hash) { |
this.generateAddress(); |
||||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); |
} |
||||
|
return keys[this.addressPointer].getBitcoinAddress(); |
||||
|
}; |
||||
|
|
||||
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
this.signWithKey = function (pubKeyHash, hash) { |
||||
for (var k = 0; k < this.addressHashes.length; k++) { |
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); |
||||
if (this.addressHashes[k] === hash) return true; |
for (var i = 0; i < this.addressHashes.length; i++) { |
||||
|
if (this.addressHashes[i] == pubKeyHash) { |
||||
|
return keys[i].sign(hash); |
||||
} |
} |
||||
return false; |
} |
||||
|
throw new Error("Missing key for signature"); |
||||
}; |
}; |
||||
|
|
||||
return Wallet; |
this.getPubKeyFromHash = function (pubKeyHash) { |
||||
|
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); |
||||
|
for (var i = 0; i < this.addressHashes.length; i++) { |
||||
|
if (this.addressHashes[i] == pubKeyHash) { |
||||
|
return keys[i].getPub(); |
||||
|
} |
||||
|
} |
||||
|
throw new Error("Hash unknown"); |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.generateAddress = function () { |
||||
|
this.addKey(new Bitcoin.ECKey()); |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.process = function (tx) { |
||||
|
if (this.txIndex[tx.hash]) return; |
||||
|
|
||||
|
var j; |
||||
|
var k; |
||||
|
var hash; |
||||
|
// Gather outputs
|
||||
|
for (j = 0; j < tx.outs.length; j++) { |
||||
|
var txout = new TransactionOut(tx.outs[j]); |
||||
|
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); |
||||
|
for (k = 0; k < this.addressHashes.length; k++) { |
||||
|
if (this.addressHashes[k] === hash) { |
||||
|
this.unspentOuts.push({tx: tx, index: j, out: txout}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Remove spent outputs
|
||||
|
for (j = 0; j < tx.ins.length; j++) { |
||||
|
var txin = new TransactionIn(tx.ins[j]); |
||||
|
var pubkey = txin.script.simpleInPubKey(); |
||||
|
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); |
||||
|
for (k = 0; k < this.addressHashes.length; k++) { |
||||
|
if (this.addressHashes[k] === hash) { |
||||
|
for (var l = 0; l < this.unspentOuts.length; l++) { |
||||
|
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && |
||||
|
txin.outpoint.index == this.unspentOuts[l].index) { |
||||
|
this.unspentOuts.splice(l, 1); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Index transaction
|
||||
|
this.txIndex[tx.hash] = tx; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.getBalance = function () { |
||||
|
var balance = BigInteger.valueOf(0); |
||||
|
for (var i = 0; i < this.unspentOuts.length; i++) { |
||||
|
var txout = this.unspentOuts[i].out; |
||||
|
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value)); |
||||
|
} |
||||
|
return balance; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.createSend = function (address, sendValue, feeValue) { |
||||
|
var selectedOuts = []; |
||||
|
var txValue = sendValue.add(feeValue); |
||||
|
var availableValue = BigInteger.ZERO; |
||||
|
var i; |
||||
|
for (i = 0; i < this.unspentOuts.length; i++) { |
||||
|
selectedOuts.push(this.unspentOuts[i]); |
||||
|
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); |
||||
|
|
||||
|
if (availableValue.compareTo(txValue) >= 0) break; |
||||
|
} |
||||
|
|
||||
|
if (availableValue.compareTo(txValue) < 0) { |
||||
|
throw new Error('Insufficient funds.'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
var changeValue = availableValue.subtract(txValue); |
||||
|
|
||||
|
var sendTx = new Bitcoin.Transaction(); |
||||
|
|
||||
|
for (i = 0; i < selectedOuts.length; i++) { |
||||
|
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); |
||||
|
} |
||||
|
|
||||
|
sendTx.addOutput(address, sendValue); |
||||
|
if (changeValue.compareTo(BigInteger.ZERO) > 0) { |
||||
|
sendTx.addOutput(this.getNextAddress(), changeValue); |
||||
|
} |
||||
|
|
||||
|
var hashType = 1; // SIGHASH_ALL
|
||||
|
|
||||
|
for (i = 0; i < sendTx.ins.length; i++) { |
||||
|
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); |
||||
|
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); |
||||
|
var signature = this.signWithKey(pubKeyHash, hash); |
||||
|
|
||||
|
// Append hash type
|
||||
|
signature.push(parseInt(hashType, 10)); |
||||
|
|
||||
|
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); |
||||
|
} |
||||
|
|
||||
|
return sendTx; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.clearTransactions = function () { |
||||
|
this.txIndex = {}; |
||||
|
this.unspentOuts = []; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Check to see if a pubKeyHash belongs to this wallet. |
||||
|
*/ |
||||
|
Wallet.prototype.hasHash = function (hash) { |
||||
|
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); |
||||
|
|
||||
|
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
||||
|
for (var k = 0; k < this.addressHashes.length; k++) { |
||||
|
if (this.addressHashes[k] === hash) return true; |
||||
|
} |
||||
|
return false; |
||||
|
}; |
||||
|
|
||||
|
return Wallet; |
||||
})(); |
})(); |
||||
|
|
||||
|
Loading…
Reference in new issue