2018-01-12 23:18:44 +00:00
// Copyright (c) 2014-2017, MyMonero.com
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original Author: Lucas Jones
// Modified to remove jQuery dep and support modular inclusion of deps by Paul Shapiro (2016)
// Modified by luigi1111 2017
var cnUtil = ( function ( initConfig ) {
var config = { } ; // shallow copy of initConfig
for ( var key in initConfig ) {
config [ key ] = initConfig [ key ] ;
}
config . coinUnits = new JSBigInt ( 10 ) . pow ( config . coinUnitPlaces ) ;
var HASH _STATE _BYTES = 200 ;
var HASH _SIZE = 32 ;
var ADDRESS _CHECKSUM _SIZE = 4 ;
var INTEGRATED _ID _SIZE = 8 ;
var ENCRYPTED _PAYMENT _ID _TAIL = 141 ;
var CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX = config . addressPrefix ;
var CRYPTONOTE _PUBLIC _INTEGRATED _ADDRESS _BASE58 _PREFIX = config . integratedAddressPrefix ;
var CRYPTONOTE _PUBLIC _SUBADDRESS _BASE58 _PREFIX = config . subAddressPrefix ;
if ( config . testnet === true )
{
CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX = config . addressPrefixTestnet ;
CRYPTONOTE _PUBLIC _INTEGRATED _ADDRESS _BASE58 _PREFIX = config . integratedAddressPrefixTestnet ;
CRYPTONOTE _PUBLIC _SUBADDRESS _BASE58 _PREFIX = config . subAddressPrefixTestnet ;
}
2018-03-09 00:36:21 +00:00
if ( config . stagenet === true )
{
CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX = config . addressPrefixStagenet ;
CRYPTONOTE _PUBLIC _INTEGRATED _ADDRESS _BASE58 _PREFIX = config . integratedAddressPrefixStagenet ;
CRYPTONOTE _PUBLIC _SUBADDRESS _BASE58 _PREFIX = config . subAddressPrefixStagenet ;
}
2018-01-12 23:18:44 +00:00
var UINT64 _MAX = new JSBigInt ( 2 ) . pow ( 64 ) ;
var CURRENT _TX _VERSION = 2 ;
var OLD _TX _VERSION = 1 ;
var RCTTypeFull = 1 ;
var RCTTypeSimple = 2 ;
var TX _EXTRA _NONCE _MAX _COUNT = 255 ;
var TX _EXTRA _TAGS = {
PADDING : '00' ,
PUBKEY : '01' ,
NONCE : '02' ,
MERGE _MINING : '03'
} ;
var TX _EXTRA _NONCE _TAGS = {
PAYMENT _ID : '00' ,
ENCRYPTED _PAYMENT _ID : '01'
} ;
var KEY _SIZE = 32 ;
var STRUCT _SIZES = {
GE _P3 : 160 ,
GE _P2 : 120 ,
GE _P1P1 : 160 ,
GE _CACHED : 160 ,
EC _SCALAR : 32 ,
EC _POINT : 32 ,
KEY _IMAGE : 32 ,
GE _DSMP : 160 * 8 , // ge_cached * 8
SIGNATURE : 64 // ec_scalar * 2
} ;
//RCT vars
var H = "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" ; //base H for amounts
var l = JSBigInt ( "7237005577332262213973186563042994240857116359379907606001950938285454250989" ) ; //curve order (not RCT specific)
var I = "0100000000000000000000000000000000000000000000000000000000000000" ; //identity element
var Z = "0000000000000000000000000000000000000000000000000000000000000000" ; //zero scalar
//H2 object to speed up some operations
var H2 = [ "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" , "8faa448ae4b3e2bb3d4d130909f55fcd79711c1c83cdbccadd42cbe1515e8712" ,
"12a7d62c7791654a57f3e67694ed50b49a7d9e3fc1e4c7a0bde29d187e9cc71d" , "789ab9934b49c4f9e6785c6d57a498b3ead443f04f13df110c5427b4f214c739" ,
"771e9299d94f02ac72e38e44de568ac1dcb2edc6edb61f83ca418e1077ce3de8" , "73b96db43039819bdaf5680e5c32d741488884d18d93866d4074a849182a8a64" ,
"8d458e1c2f68ebebccd2fd5d379f5e58f8134df3e0e88cad3d46701063a8d412" , "09551edbe494418e81284455d64b35ee8ac093068a5f161fa6637559177ef404" ,
"d05a8866f4df8cee1e268b1d23a4c58c92e760309786cdac0feda1d247a9c9a7" , "55cdaad518bd871dd1eb7bc7023e1dc0fdf3339864f88fdd2de269fe9ee1832d" ,
"e7697e951a98cfd5712b84bbe5f34ed733e9473fcb68eda66e3788df1958c306" , "f92a970bae72782989bfc83adfaa92a4f49c7e95918b3bba3cdc7fe88acc8d47" ,
"1f66c2d491d75af915c8db6a6d1cb0cd4f7ddcd5e63d3ba9b83c866c39ef3a2b" , "3eec9884b43f58e93ef8deea260004efea2a46344fc5965b1a7dd5d18997efa7" ,
"b29f8f0ccb96977fe777d489d6be9e7ebc19c409b5103568f277611d7ea84894" , "56b1f51265b9559876d58d249d0c146d69a103636699874d3f90473550fe3f2c" ,
"1d7a36575e22f5d139ff9cc510fa138505576b63815a94e4b012bfd457caaada" , "d0ac507a864ecd0593fa67be7d23134392d00e4007e2534878d9b242e10d7620" ,
"f6c6840b9cf145bb2dccf86e940be0fc098e32e31099d56f7fe087bd5deb5094" , "28831a3340070eb1db87c12e05980d5f33e9ef90f83a4817c9f4a0a33227e197" ,
"87632273d629ccb7e1ed1a768fa2ebd51760f32e1c0b867a5d368d5271055c6e" , "5c7b29424347964d04275517c5ae14b6b5ea2798b573fc94e6e44a5321600cfb" ,
"e6945042d78bc2c3bd6ec58c511a9fe859c0ad63fde494f5039e0e8232612bd5" , "36d56907e2ec745db6e54f0b2e1b2300abcb422e712da588a40d3f1ebbbe02f6" ,
"34db6ee4d0608e5f783650495a3b2f5273c5134e5284e4fdf96627bb16e31e6b" , "8e7659fb45a3787d674ae86731faa2538ec0fdf442ab26e9c791fada089467e9" ,
"3006cf198b24f31bb4c7e6346000abc701e827cfbb5df52dcfa42e9ca9ff0802" , "f5fd403cb6e8be21472e377ffd805a8c6083ea4803b8485389cc3ebc215f002a" ,
"3731b260eb3f9482e45f1c3f3b9dcf834b75e6eef8c40f461ea27e8b6ed9473d" , "9f9dab09c3f5e42855c2de971b659328a2dbc454845f396ffc053f0bb192f8c3" ,
"5e055d25f85fdb98f273e4afe08464c003b70f1ef0677bb5e25706400be620a5" , "868bcf3679cb6b500b94418c0b8925f9865530303ae4e4b262591865666a4590" ,
"b3db6bd3897afbd1df3f9644ab21c8050e1f0038a52f7ca95ac0c3de7558cb7a" , "8119b3a059ff2cac483e69bcd41d6d27149447914288bbeaee3413e6dcc6d1eb" ,
"10fc58f35fc7fe7ae875524bb5850003005b7f978c0c65e2a965464b6d00819c" , "5acd94eb3c578379c1ea58a343ec4fcff962776fe35521e475a0e06d887b2db9" ,
"33daf3a214d6e0d42d2300a7b44b39290db8989b427974cd865db011055a2901" , "cfc6572f29afd164a494e64e6f1aeb820c3e7da355144e5124a391d06e9f95ea" ,
"d5312a4b0ef615a331f6352c2ed21dac9e7c36398b939aec901c257f6cbc9e8e" , "551d67fefc7b5b9f9fdbf6af57c96c8a74d7e45a002078a7b5ba45c6fde93e33" ,
"d50ac7bd5ca593c656928f38428017fc7ba502854c43d8414950e96ecb405dc3" , "0773e18ea1be44fe1a97e239573cfae3e4e95ef9aa9faabeac1274d3ad261604" ,
"e9af0e7ca89330d2b8615d1b4137ca617e21297f2f0ded8e31b7d2ead8714660" , "7b124583097f1029a0c74191fe7378c9105acc706695ed1493bb76034226a57b" ,
"ec40057b995476650b3db98e9db75738a8cd2f94d863b906150c56aac19caa6b" , "01d9ff729efd39d83784c0fe59c4ae81a67034cb53c943fb818b9d8ae7fc33e5" ,
"00dfb3c696328c76424519a7befe8e0f6c76f947b52767916d24823f735baf2e" , "461b799b4d9ceea8d580dcb76d11150d535e1639d16003c3fb7e9d1fd13083a8" ,
"ee03039479e5228fdc551cbde7079d3412ea186a517ccc63e46e9fcce4fe3a6c" , "a8cfb543524e7f02b9f045acd543c21c373b4c9b98ac20cec417a6ddb5744e94" ,
"932b794bf89c6edaf5d0650c7c4bad9242b25626e37ead5aa75ec8c64e09dd4f" , "16b10c779ce5cfef59c7710d2e68441ea6facb68e9b5f7d533ae0bb78e28bf57" ,
"0f77c76743e7396f9910139f4937d837ae54e21038ac5c0b3fd6ef171a28a7e4" , "d7e574b7b952f293e80dde905eb509373f3f6cd109a02208b3c1e924080a20ca" ,
"45666f8c381e3da675563ff8ba23f83bfac30c34abdde6e5c0975ef9fd700cb9" , "b24612e454607eb1aba447f816d1a4551ef95fa7247fb7c1f503020a7177f0dd" ,
"7e208861856da42c8bb46a7567f8121362d9fb2496f131a4aa9017cf366cdfce" , "5b646bff6ad1100165037a055601ea02358c0f41050f9dfe3c95dccbd3087be0" ,
"746d1dccfed2f0ff1e13c51e2d50d5324375fbd5bf7ca82a8931828d801d43ab" , "cb98110d4a6bb97d22feadbc6c0d8930c5f8fc508b2fc5b35328d26b88db19ae" ,
"60b626a033b55f27d7676c4095eababc7a2c7ede2624b472e97f64f96b8cfc0e" , "e5b52bc927468df71893eb8197ef820cf76cb0aaf6e8e4fe93ad62d803983104" ,
"056541ae5da9961be2b0a5e895e5c5ba153cbb62dd561a427bad0ffd41923199" , "f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888" ] ;
//begin rct new functions
//creates a Pedersen commitment from an amount (in scalar form) and a mask
//C = bG + aH where b = mask, a = amount
function commit ( amount , mask ) {
if ( ! valid _hex ( mask ) || mask . length !== 64 || ! valid _hex ( amount ) || amount . length !== 64 ) {
throw "invalid amount or mask!" ;
}
var C = this . ge _double _scalarmult _base _vartime ( amount , H , mask ) ;
return C ;
}
function zeroCommit ( amount ) {
if ( ! valid _hex ( amount ) || amount . length !== 64 ) {
throw "invalid amount!" ;
}
var C = this . ge _double _scalarmult _base _vartime ( amount , H , I ) ;
return C ;
}
this . decode _rct _ecdh = function ( ecdh , key ) {
var first = this . hash _to _scalar ( key ) ;
var second = this . hash _to _scalar ( first ) ;
return {
"mask" : this . sc _sub ( ecdh . mask , first ) ,
"amount" : this . sc _sub ( ecdh . amount , second )
} ;
} ;
this . encode _rct _ecdh = function ( ecdh , key ) {
var first = this . hash _to _scalar ( key ) ;
var second = this . hash _to _scalar ( first ) ;
return {
"mask" : this . sc _add ( ecdh . mask , first ) ,
"amount" : this . sc _add ( ecdh . amount , second )
} ;
} ;
//switch byte order for hex string
function swapEndian ( hex ) {
if ( hex . length % 2 !== 0 ) { return "length must be a multiple of 2!" ; }
var data = "" ;
for ( var i = 1 ; i <= hex . length / 2 ; i ++ ) {
data += hex . substr ( 0 - 2 * i , 2 ) ;
}
return data ;
}
//switch byte order charwise
function swapEndianC ( string ) {
var data = "" ;
for ( var i = 1 ; i <= string . length ; i ++ ) {
data += string . substr ( 0 - i , 1 ) ;
}
return data ;
}
//for most uses you'll also want to swapEndian after conversion
//mainly to convert integer "scalars" to usable hexadecimal strings
function d2h ( integer ) {
if ( typeof integer !== "string" && integer . toString ( ) . length > 15 ) { throw "integer should be entered as a string for precision" ; }
var padding = "" ;
for ( i = 0 ; i < 63 ; i ++ ) {
padding += "0" ;
}
return ( padding + JSBigInt ( integer ) . toString ( 16 ) . toLowerCase ( ) ) . slice ( - 64 ) ;
}
//integer (string) to scalar
function d2s ( integer ) {
return swapEndian ( d2h ( integer ) ) ;
}
//scalar to integer (string)
function s2d ( scalar ) {
return JSBigInt . parse ( swapEndian ( scalar ) , 16 ) . toString ( ) ;
}
//convert integer string to 64bit "binary" little-endian string
function d2b ( integer ) {
if ( typeof integer !== "string" && integer . toString ( ) . length > 15 ) { throw "integer should be entered as a string for precision" ; }
var padding = "" ;
for ( i = 0 ; i < 63 ; i ++ ) {
padding += "0" ;
}
var a = new JSBigInt ( integer ) ;
if ( a . toString ( 2 ) . length > 64 ) { throw "amount overflows uint64!" ; }
return swapEndianC ( ( padding + a . toString ( 2 ) ) . slice ( - 64 ) ) ;
}
//convert integer string to 64bit base 4 little-endian string
function d2b4 ( integer ) {
if ( typeof integer !== "string" && integer . toString ( ) . length > 15 ) { throw "integer should be entered as a string for precision" ; }
var padding = "" ;
for ( i = 0 ; i < 31 ; i ++ ) {
padding += "0" ;
}
var a = new JSBigInt ( integer ) ;
if ( a . toString ( 2 ) . length > 64 ) { throw "amount overflows uint64!" ; }
return swapEndianC ( ( padding + a . toString ( 4 ) ) . slice ( - 32 ) ) ;
}
//end rct new functions
this . valid _hex = function ( hex ) {
var exp = new RegExp ( "[0-9a-fA-F]{" + hex . length + "}" ) ;
return exp . test ( hex ) ;
} ;
//simple exclusive or function for two hex inputs
this . hex _xor = function ( hex1 , hex2 ) {
if ( ! hex1 || ! hex2 || hex1 . length !== hex2 . length || hex1 . length % 2 !== 0 || hex2 . length % 2 !== 0 ) { throw "Hex string(s) is/are invalid!" ; }
var bin1 = hextobin ( hex1 ) ;
var bin2 = hextobin ( hex2 ) ;
var xor = new Uint8Array ( bin1 . length ) ;
for ( i = 0 ; i < xor . length ; i ++ ) {
xor [ i ] = bin1 [ i ] ^ bin2 [ i ] ;
}
return bintohex ( xor ) ;
} ;
function hextobin ( hex ) {
if ( hex . length % 2 !== 0 ) throw "Hex string has invalid length!" ;
var res = new Uint8Array ( hex . length / 2 ) ;
for ( var i = 0 ; i < hex . length / 2 ; ++ i ) {
res [ i ] = parseInt ( hex . slice ( i * 2 , i * 2 + 2 ) , 16 ) ;
}
return res ;
}
function bintohex ( bin ) {
var out = [ ] ;
for ( var i = 0 ; i < bin . length ; ++ i ) {
out . push ( ( "0" + bin [ i ] . toString ( 16 ) ) . slice ( - 2 ) ) ;
}
return out . join ( "" ) ;
}
// Generate a 256-bit crypto random
this . rand _32 = function ( ) {
return mn _random ( 256 ) ;
} ;
// Generate a 128-bit crypto random
this . rand _16 = function ( ) {
return mn _random ( 128 ) ;
} ;
// Generate a 64-bit crypto random
this . rand _8 = function ( ) {
return mn _random ( 64 ) ;
} ;
this . encode _varint = function ( i ) {
i = new JSBigInt ( i ) ;
var out = '' ;
// While i >= b10000000
while ( i . compare ( 0x80 ) >= 0 ) {
// out.append i & b01111111 | b10000000
out += ( "0" + ( ( i . lowVal ( ) & 0x7f ) | 0x80 ) . toString ( 16 ) ) . slice ( - 2 ) ;
i = i . divide ( new JSBigInt ( 2 ) . pow ( 7 ) ) ;
}
out += ( "0" + i . toJSValue ( ) . toString ( 16 ) ) . slice ( - 2 ) ;
return out ;
} ;
this . sc _reduce = function ( hex ) {
var input = hextobin ( hex ) ;
if ( input . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "sc_reduce: Invalid hextobin(hex) input length" ;
2018-01-12 23:18:44 +00:00
}
var mem = Module . _malloc ( 64 ) ;
Module . HEAPU8 . set ( input , mem ) ;
Module . ccall ( 'sc_reduce' , 'void' , [ 'number' ] , [ mem ] ) ;
var output = Module . HEAPU8 . subarray ( mem , mem + 64 ) ;
Module . _free ( mem ) ;
return bintohex ( output ) ;
} ;
this . sc _reduce32 = function ( hex ) {
var input = hextobin ( hex ) ;
if ( input . length !== 32 ) {
2018-01-28 00:19:23 +00:00
throw "sc_reduce32: Invalid hextobin(hex) input length" ;
2018-01-12 23:18:44 +00:00
}
var mem = Module . _malloc ( 32 ) ;
Module . HEAPU8 . set ( input , mem ) ;
Module . ccall ( 'sc_reduce32' , 'void' , [ 'number' ] , [ mem ] ) ;
var output = Module . HEAPU8 . subarray ( mem , mem + 32 ) ;
Module . _free ( mem ) ;
return bintohex ( output ) ;
} ;
this . cn _fast _hash = function ( input , inlen ) {
/ * i f ( i n l e n = = = u n d e f i n e d | | ! i n l e n ) {
inlen = Math . floor ( input . length / 2 ) ;
} * /
if ( input . length % 2 !== 0 || ! this . valid _hex ( input ) ) {
2018-01-28 00:19:23 +00:00
throw "cn_fast_hash: Input invalid" ;
2018-01-12 23:18:44 +00:00
}
//update to use new keccak impl (approx 45x faster)
//var state = this.keccak(input, inlen, HASH_STATE_BYTES);
//return state.substr(0, HASH_SIZE * 2);
return keccak _256 ( hextobin ( input ) ) ;
} ;
//many functions below are commented out now, and duplicated with the faster nacl impl --luigi1111
// to be removed completely later
/ * t h i s . s e c _ k e y _ t o _ p u b = f u n c t i o n ( s e c ) {
var input = hextobin ( sec ) ;
if ( input . length !== 32 ) {
throw "Invalid input length" ;
}
var input _mem = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( input , input _mem ) ;
var ge _p3 = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var out _mem = Module . _malloc ( KEY _SIZE ) ;
Module . ccall ( 'ge_scalarmult_base' , 'void' , [ 'number' , 'number' ] , [ ge _p3 , input _mem ] ) ;
Module . ccall ( 'ge_p3_tobytes' , 'void' , [ 'number' , 'number' ] , [ out _mem , ge _p3 ] ) ;
var output = Module . HEAPU8 . subarray ( out _mem , out _mem + KEY _SIZE ) ;
Module . _free ( ge _p3 ) ;
Module . _free ( input _mem ) ;
Module . _free ( out _mem ) ;
return bintohex ( output ) ;
} ; * /
this . sec _key _to _pub = function ( sec ) {
if ( sec . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "sec_key_to_pub: Invalid sec length" ;
2018-01-12 23:18:44 +00:00
}
return bintohex ( nacl . ll . ge _scalarmult _base ( hextobin ( sec ) ) ) ;
} ;
//alias
this . ge _scalarmult _base = function ( sec ) {
return this . sec _key _to _pub ( sec ) ;
} ;
//accepts arbitrary point, rather than G
/ * t h i s . g e _ s c a l a r m u l t = f u n c t i o n ( p u b , s e c ) {
if ( pub . length !== 64 || sec . length !== 64 ) {
throw "Invalid input length" ;
}
var pub _b = hextobin ( pub ) ;
var sec _b = hextobin ( sec ) ;
var pub _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( pub _b , pub _m ) ;
var sec _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( sec _b , sec _m ) ;
var ge _p3 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var ge _p2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "bool" , [ "number" , "number" ] , [ ge _p3 _m , pub _m ] ) !== 0 ) {
throw "ge_frombytes_vartime returned non-zero error code" ;
}
Module . ccall ( "ge_scalarmult" , "void" , [ "number" , "number" , "number" ] , [ ge _p2 _m , sec _m , ge _p3 _m ] ) ;
var derivation _m = Module . _malloc ( KEY _SIZE ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ derivation _m , ge _p2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( derivation _m , derivation _m + KEY _SIZE ) ;
Module . _free ( pub _m ) ;
Module . _free ( sec _m ) ;
Module . _free ( ge _p3 _m ) ;
Module . _free ( ge _p2 _m ) ;
Module . _free ( derivation _m ) ;
return bintohex ( res ) ;
} ; * /
this . ge _scalarmult = function ( pub , sec ) {
if ( pub . length !== 64 || sec . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "ge_scalarmult: Invalid pub or sec input lengths" ;
2018-01-12 23:18:44 +00:00
}
return bintohex ( nacl . ll . ge _scalarmult ( hextobin ( pub ) , hextobin ( sec ) ) ) ;
} ;
this . pubkeys _to _string = function ( spend , view ) {
var prefix = this . encode _varint ( CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX ) ;
var data = prefix + spend + view ;
var checksum = this . cn _fast _hash ( data ) ;
return cnBase58 . encode ( data + checksum . slice ( 0 , ADDRESS _CHECKSUM _SIZE * 2 ) ) ;
} ;
this . get _account _integrated _address = function ( address , payment _id8 ) {
var decoded _address = decode _address ( address ) ;
var prefix = this . encode _varint ( CRYPTONOTE _PUBLIC _INTEGRATED _ADDRESS _BASE58 _PREFIX ) ;
var data = prefix + decoded _address . spend + decoded _address . view + payment _id8 ;
var checksum = this . cn _fast _hash ( data ) ;
return cnBase58 . encode ( data + checksum . slice ( 0 , ADDRESS _CHECKSUM _SIZE * 2 ) ) ;
} ;
this . decrypt _payment _id = function ( payment _id8 , tx _public _key , acc _prv _view _key ) {
2018-01-28 00:19:23 +00:00
if ( payment _id8 . length !== 16 ) throw "decrypt_payment_id: Invalid payment_id8 length!" ;
2018-01-12 23:18:44 +00:00
var key _derivation = this . generate _key _derivation ( tx _public _key , acc _prv _view _key ) ;
var pid _key = this . cn _fast _hash ( key _derivation + ENCRYPTED _PAYMENT _ID _TAIL . toString ( 16 ) ) . slice ( 0 , INTEGRATED _ID _SIZE * 2 ) ;
var decrypted _payment _id = this . hex _xor ( payment _id8 , pid _key ) ;
return decrypted _payment _id ;
}
// Generate keypair from seed
this . generate _keys _old = function ( seed ) {
if ( seed . length !== 64 ) throw "Invalid input length!" ;
var sec = this . sc _reduce32 ( seed ) ;
var pub = this . sec _key _to _pub ( sec ) ;
return {
'sec' : sec ,
'pub' : pub
} ;
} ;
// Generate keypair from seed 2
// as in simplewallet
this . generate _keys = function ( seed ) {
2018-01-28 00:19:23 +00:00
if ( seed . length !== 64 ) throw "generate_keys: Invalid seed input length!" ;
2018-01-12 23:18:44 +00:00
var sec = this . sc _reduce32 ( seed ) ;
var pub = this . sec _key _to _pub ( sec ) ;
return {
'sec' : sec ,
'pub' : pub
} ;
} ;
this . random _keypair = function ( ) {
return this . generate _keys ( this . rand _32 ( ) ) ;
} ;
// Random 32-byte ec scalar
this . random _scalar = function ( ) {
//var rand = this.sc_reduce(mn_random(64 * 8));
//return rand.slice(0, STRUCT_SIZES.EC_SCALAR * 2);
return this . sc _reduce32 ( this . rand _32 ( ) ) ;
} ;
/ * n o l o n g e r u s e d
this . keccak = function ( hex , inlen , outlen ) {
var input = hextobin ( hex ) ;
if ( input . length !== inlen ) {
throw "Invalid input length" ;
}
if ( outlen <= 0 ) {
throw "Invalid output length" ;
}
var input _mem = Module . _malloc ( inlen ) ;
Module . HEAPU8 . set ( input , input _mem ) ;
var out _mem = Module . _malloc ( outlen ) ;
Module . _keccak ( input _mem , inlen | 0 , out _mem , outlen | 0 ) ;
var output = Module . HEAPU8 . subarray ( out _mem , out _mem + outlen ) ;
Module . _free ( input _mem ) ;
Module . _free ( out _mem ) ;
return bintohex ( output ) ;
} ; * /
this . create _address = function ( seed ) {
var keys = { } ;
var first ;
if ( seed . length !== 64 ) {
first = this . cn _fast _hash ( seed ) ;
} else {
first = seed ; //only input reduced seeds or this will not give you the result you want
}
keys . spend = this . generate _keys ( first ) ;
if ( seed . length !== 64 ) {
var second = this . cn _fast _hash ( first ) ;
} else {
var second = this . cn _fast _hash ( keys . spend . sec ) ;
}
keys . view = this . generate _keys ( second ) ;
keys . public _addr = this . pubkeys _to _string ( keys . spend . pub , keys . view . pub ) ;
return keys ;
} ;
this . create _addr _prefix = function ( seed ) {
var first ;
if ( seed . length !== 64 ) {
first = this . cn _fast _hash ( seed ) ;
} else {
first = seed ;
}
var spend = this . generate _keys ( first ) ;
var prefix = this . encode _varint ( CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX ) ;
return cnBase58 . encode ( prefix + spend . pub ) . slice ( 0 , 44 ) ;
} ;
this . is _subaddress = function ( address ) {
var dec = cnBase58 . decode ( address ) ;
var expectedPrefixSub = this . encode _varint ( CRYPTONOTE _PUBLIC _SUBADDRESS _BASE58 _PREFIX ) ;
var prefix = dec . slice ( 0 , expectedPrefixSub . length ) ;
return ( prefix === expectedPrefixSub ) ;
}
this . decode _address = function ( address ) {
var dec = cnBase58 . decode ( address ) ;
var expectedPrefix = this . encode _varint ( CRYPTONOTE _PUBLIC _ADDRESS _BASE58 _PREFIX ) ;
var expectedPrefixInt = this . encode _varint ( CRYPTONOTE _PUBLIC _INTEGRATED _ADDRESS _BASE58 _PREFIX ) ;
var expectedPrefixSub = this . encode _varint ( CRYPTONOTE _PUBLIC _SUBADDRESS _BASE58 _PREFIX ) ;
var prefix = dec . slice ( 0 , expectedPrefix . length ) ;
if ( prefix !== expectedPrefix && prefix !== expectedPrefixInt && prefix !== expectedPrefixSub ) {
throw "Invalid address prefix" ;
}
dec = dec . slice ( expectedPrefix . length ) ;
var spend = dec . slice ( 0 , 64 ) ;
var view = dec . slice ( 64 , 128 ) ;
if ( prefix === expectedPrefixInt ) {
var intPaymentId = dec . slice ( 128 , 128 + ( INTEGRATED _ID _SIZE * 2 ) ) ;
var checksum = dec . slice ( 128 + ( INTEGRATED _ID _SIZE * 2 ) , 128 + ( INTEGRATED _ID _SIZE * 2 ) + ( ADDRESS _CHECKSUM _SIZE * 2 ) ) ;
var expectedChecksum = this . cn _fast _hash ( prefix + spend + view + intPaymentId ) . slice ( 0 , ADDRESS _CHECKSUM _SIZE * 2 ) ;
} else if ( prefix === expectedPrefix ) {
var checksum = dec . slice ( 128 , 128 + ( ADDRESS _CHECKSUM _SIZE * 2 ) ) ;
var expectedChecksum = this . cn _fast _hash ( prefix + spend + view ) . slice ( 0 , ADDRESS _CHECKSUM _SIZE * 2 ) ;
} else {
// if its not regular address, nor integrated, than it must be subaddress
var checksum = dec . slice ( 128 , 128 + ( ADDRESS _CHECKSUM _SIZE * 2 ) ) ;
var expectedChecksum = this . cn _fast _hash ( prefix + spend + view ) . slice ( 0 , ADDRESS _CHECKSUM _SIZE * 2 ) ;
}
if ( checksum !== expectedChecksum ) {
throw "Invalid checksum" ;
}
if ( intPaymentId ) {
return {
spend : spend ,
view : view ,
intPaymentId : intPaymentId
} ;
} else {
return {
spend : spend ,
view : view
} ;
}
} ;
this . valid _keys = function ( view _pub , view _sec , spend _pub , spend _sec ) {
var expected _view _pub = this . sec _key _to _pub ( view _sec ) ;
var expected _spend _pub = this . sec _key _to _pub ( spend _sec ) ;
return ( expected _spend _pub === spend _pub ) && ( expected _view _pub === view _pub ) ;
} ;
this . hash _to _scalar = function ( buf ) {
var hash = this . cn _fast _hash ( buf ) ;
var scalar = this . sc _reduce32 ( hash ) ;
return scalar ;
} ;
/ * t h i s . g e n e r a t e _ k e y _ d e r i v a t i o n = f u n c t i o n ( p u b , s e c ) {
if ( pub . length !== 64 || sec . length !== 64 ) {
throw "Invalid input length" ;
}
var pub _b = hextobin ( pub ) ;
var sec _b = hextobin ( sec ) ;
var pub _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( pub _b , pub _m ) ;
var sec _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( sec _b , sec _m ) ;
var ge _p3 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var ge _p2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var ge _p1p1 _m = Module . _malloc ( STRUCT _SIZES . GE _P1P1 ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "bool" , [ "number" , "number" ] , [ ge _p3 _m , pub _m ] ) !== 0 ) {
throw "ge_frombytes_vartime returned non-zero error code" ;
}
Module . ccall ( "ge_scalarmult" , "void" , [ "number" , "number" , "number" ] , [ ge _p2 _m , sec _m , ge _p3 _m ] ) ;
Module . ccall ( "ge_mul8" , "void" , [ "number" , "number" ] , [ ge _p1p1 _m , ge _p2 _m ] ) ;
Module . ccall ( "ge_p1p1_to_p2" , "void" , [ "number" , "number" ] , [ ge _p2 _m , ge _p1p1 _m ] ) ;
var derivation _m = Module . _malloc ( KEY _SIZE ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ derivation _m , ge _p2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( derivation _m , derivation _m + KEY _SIZE ) ;
Module . _free ( pub _m ) ;
Module . _free ( sec _m ) ;
Module . _free ( ge _p3 _m ) ;
Module . _free ( ge _p2 _m ) ;
Module . _free ( ge _p1p1 _m ) ;
Module . _free ( derivation _m ) ;
return bintohex ( res ) ;
} ; * /
this . generate _key _derivation = function ( pub , sec ) {
if ( pub . length !== 64 || sec . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "generate_key_derivation: Invalid pub or sec keys lengths" ;
2018-01-12 23:18:44 +00:00
}
var P = this . ge _scalarmult ( pub , sec ) ;
return this . ge _scalarmult ( P , d2s ( 8 ) ) ; //mul8 to ensure group
} ;
this . derivation _to _scalar = function ( derivation , output _index ) {
var buf = "" ;
if ( derivation . length !== ( STRUCT _SIZES . EC _POINT * 2 ) ) {
2018-01-28 00:19:23 +00:00
throw "derivation_to_scalar: Invalid derivation length!" ;
2018-01-12 23:18:44 +00:00
}
buf += derivation ;
var enc = encode _varint ( output _index ) ;
if ( enc . length > 10 * 2 ) {
throw "output_index didn't fit in 64-bit varint" ;
}
buf += enc ;
return this . hash _to _scalar ( buf ) ;
} ;
this . derive _secret _key = function ( derivation , out _index , sec ) {
if ( derivation . length !== 64 || sec . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "derive_secret_key: Invalid derivation or sec input length!" ;
2018-01-12 23:18:44 +00:00
}
var scalar _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var scalar _b = hextobin ( this . derivation _to _scalar ( derivation , out _index ) ) ;
Module . HEAPU8 . set ( scalar _b , scalar _m ) ;
var base _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sec ) , base _m ) ;
var derived _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
Module . ccall ( "sc_add" , "void" , [ "number" , "number" , "number" ] , [ derived _m , base _m , scalar _m ] ) ;
var res = Module . HEAPU8 . subarray ( derived _m , derived _m + STRUCT _SIZES . EC _SCALAR ) ;
Module . _free ( scalar _m ) ;
Module . _free ( base _m ) ;
Module . _free ( derived _m ) ;
return bintohex ( res ) ;
} ;
/ * t h i s . d e r i v e _ p u b l i c _ k e y = f u n c t i o n ( d e r i v a t i o n , o u t _ i n d e x , p u b ) {
if ( derivation . length !== 64 || pub . length !== 64 ) {
throw "Invalid input length!" ;
}
var derivation _m = Module . _malloc ( KEY _SIZE ) ;
var derivation _b = hextobin ( derivation ) ;
Module . HEAPU8 . set ( derivation _b , derivation _m ) ;
var base _m = Module . _malloc ( KEY _SIZE ) ;
var base _b = hextobin ( pub ) ;
Module . HEAPU8 . set ( base _b , base _m ) ;
var point1 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var point2 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var point3 _m = Module . _malloc ( STRUCT _SIZES . GE _CACHED ) ;
var point4 _m = Module . _malloc ( STRUCT _SIZES . GE _P1P1 ) ;
var point5 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var derived _key _m = Module . _malloc ( KEY _SIZE ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "bool" , [ "number" , "number" ] , [ point1 _m , base _m ] ) !== 0 ) {
throw "ge_frombytes_vartime returned non-zero error code" ;
}
var scalar _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var scalar _b = hextobin ( this . derivation _to _scalar ( bintohex ( Module . HEAPU8 . subarray ( derivation _m , derivation _m + STRUCT _SIZES . EC _POINT ) ) , out _index ) ) ;
Module . HEAPU8 . set ( scalar _b , scalar _m ) ;
Module . ccall ( "ge_scalarmult_base" , "void" , [ "number" , "number" ] , [ point2 _m , scalar _m ] ) ;
Module . ccall ( "ge_p3_to_cached" , "void" , [ "number" , "number" ] , [ point3 _m , point2 _m ] ) ;
Module . ccall ( "ge_add" , "void" , [ "number" , "number" , "number" ] , [ point4 _m , point1 _m , point3 _m ] ) ;
Module . ccall ( "ge_p1p1_to_p2" , "void" , [ "number" , "number" ] , [ point5 _m , point4 _m ] ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ derived _key _m , point5 _m ] ) ;
var res = Module . HEAPU8 . subarray ( derived _key _m , derived _key _m + KEY _SIZE ) ;
Module . _free ( derivation _m ) ;
Module . _free ( base _m ) ;
Module . _free ( scalar _m ) ;
Module . _free ( point1 _m ) ;
Module . _free ( point2 _m ) ;
Module . _free ( point3 _m ) ;
Module . _free ( point4 _m ) ;
Module . _free ( point5 _m ) ;
Module . _free ( derived _key _m ) ;
return bintohex ( res ) ;
} ; * /
this . derive _public _key = function ( derivation , out _index , pub ) {
if ( derivation . length !== 64 || pub . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "derive_public_key: Invalid derivation or pub key input lengths!" ;
2018-01-12 23:18:44 +00:00
}
var s = this . derivation _to _scalar ( derivation , out _index ) ;
return bintohex ( nacl . ll . ge _add ( hextobin ( pub ) , hextobin ( this . ge _scalarmult _base ( s ) ) ) ) ;
} ;
this . hash _to _ec = function ( key ) {
if ( key . length !== ( KEY _SIZE * 2 ) ) {
2018-01-28 00:19:23 +00:00
throw "hash_to_ec: Invalid key input length" ;
2018-01-12 23:18:44 +00:00
}
var h _m = Module . _malloc ( HASH _SIZE ) ;
var point _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var point2 _m = Module . _malloc ( STRUCT _SIZES . GE _P1P1 ) ;
var res _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var hash = hextobin ( this . cn _fast _hash ( key , KEY _SIZE ) ) ;
Module . HEAPU8 . set ( hash , h _m ) ;
Module . ccall ( "ge_fromfe_frombytes_vartime" , "void" , [ "number" , "number" ] , [ point _m , h _m ] ) ;
Module . ccall ( "ge_mul8" , "void" , [ "number" , "number" ] , [ point2 _m , point _m ] ) ;
Module . ccall ( "ge_p1p1_to_p3" , "void" , [ "number" , "number" ] , [ res _m , point2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( res _m , res _m + STRUCT _SIZES . GE _P3 ) ;
Module . _free ( h _m ) ;
Module . _free ( point _m ) ;
Module . _free ( point2 _m ) ;
Module . _free ( res _m ) ;
return bintohex ( res ) ;
} ;
//returns a 32 byte point via "ge_p3_tobytes" rather than a 160 byte "p3", otherwise same as above;
this . hash _to _ec _2 = function ( key ) {
if ( key . length !== ( KEY _SIZE * 2 ) ) {
2018-01-28 00:19:23 +00:00
throw "hash_to_ec_2: Invalid key input length" ;
2018-01-12 23:18:44 +00:00
}
var h _m = Module . _malloc ( HASH _SIZE ) ;
var point _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var point2 _m = Module . _malloc ( STRUCT _SIZES . GE _P1P1 ) ;
var res _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var hash = hextobin ( this . cn _fast _hash ( key , KEY _SIZE ) ) ;
var res2 _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hash , h _m ) ;
Module . ccall ( "ge_fromfe_frombytes_vartime" , "void" , [ "number" , "number" ] , [ point _m , h _m ] ) ;
Module . ccall ( "ge_mul8" , "void" , [ "number" , "number" ] , [ point2 _m , point _m ] ) ;
Module . ccall ( "ge_p1p1_to_p3" , "void" , [ "number" , "number" ] , [ res _m , point2 _m ] ) ;
Module . ccall ( "ge_p3_tobytes" , "void" , [ "number" , "number" ] , [ res2 _m , res _m ] ) ;
var res = Module . HEAPU8 . subarray ( res2 _m , res2 _m + KEY _SIZE ) ;
Module . _free ( h _m ) ;
Module . _free ( point _m ) ;
Module . _free ( point2 _m ) ;
Module . _free ( res _m ) ;
Module . _free ( res2 _m ) ;
return bintohex ( res ) ;
} ;
this . generate _key _image _2 = function ( pub , sec ) {
if ( ! pub || ! sec || pub . length !== 64 || sec . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "generate_key_image_2: Invalid pub or sec keys input length" ;
2018-01-12 23:18:44 +00:00
}
var pub _m = Module . _malloc ( KEY _SIZE ) ;
var sec _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( pub ) , pub _m ) ;
Module . HEAPU8 . set ( hextobin ( sec ) , sec _m ) ;
if ( Module . ccall ( "sc_check" , "number" , [ "number" ] , [ sec _m ] ) !== 0 ) {
throw "sc_check(sec) != 0" ;
}
var point _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var point2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var point _b = hextobin ( this . hash _to _ec ( pub ) ) ;
Module . HEAPU8 . set ( point _b , point _m ) ;
var image _m = Module . _malloc ( STRUCT _SIZES . KEY _IMAGE ) ;
Module . ccall ( "ge_scalarmult" , "void" , [ "number" , "number" , "number" ] , [ point2 _m , sec _m , point _m ] ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ image _m , point2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( image _m , image _m + STRUCT _SIZES . KEY _IMAGE ) ;
Module . _free ( pub _m ) ;
Module . _free ( sec _m ) ;
Module . _free ( point _m ) ;
Module . _free ( point2 _m ) ;
Module . _free ( image _m ) ;
return bintohex ( res ) ;
} ;
this . generate _key _image = function ( tx _pub , view _sec , spend _pub , spend _sec , output _index ) {
if ( tx _pub . length !== 64 ) {
throw "Invalid tx_pub length" ;
}
if ( view _sec . length !== 64 ) {
throw "Invalid view_sec length" ;
}
if ( spend _pub . length !== 64 ) {
throw "Invalid spend_pub length" ;
}
if ( spend _sec . length !== 64 ) {
throw "Invalid spend_sec length" ;
}
var recv _derivation = this . generate _key _derivation ( tx _pub , view _sec ) ;
var ephemeral _pub = this . derive _public _key ( recv _derivation , output _index , spend _pub ) ;
var ephemeral _sec = this . derive _secret _key ( recv _derivation , output _index , spend _sec ) ;
var k _image = this . generate _key _image _2 ( ephemeral _pub , ephemeral _sec ) ;
return {
ephemeral _pub : ephemeral _pub ,
key _image : k _image
} ;
} ;
this . generate _key _image _helper _rct = function ( keys , tx _pub _key , out _index , enc _mask ) {
var recv _derivation = this . generate _key _derivation ( tx _pub _key , keys . view . sec ) ;
if ( ! recv _derivation ) throw "Failed to generate key image" ;
var mask ;
if ( enc _mask === I )
{
// this is for ringct coinbase txs (rct type 0). they are ringct tx that have identity mask
mask = enc _mask ; // enc_mask is idenity mask returned by backend.
}
else
{
// for other ringct types or for non-ringct txs to this.
mask = enc _mask ? sc _sub ( enc _mask , hash _to _scalar ( derivation _to _scalar ( recv _derivation , out _index ) ) ) : I ; //decode mask, or d2s(1) if no mask
}
var ephemeral _pub = this . derive _public _key ( recv _derivation , out _index , keys . spend . pub ) ;
if ( ! ephemeral _pub ) throw "Failed to generate key image" ;
var ephemeral _sec = this . derive _secret _key ( recv _derivation , out _index , keys . spend . sec ) ;
var image = this . generate _key _image _2 ( ephemeral _pub , ephemeral _sec ) ;
return {
in _ephemeral : {
pub : ephemeral _pub ,
sec : ephemeral _sec ,
mask : mask
} ,
image : image
} ;
} ;
//curve and scalar functions; split out to make their host functions cleaner and more readable
//inverts X coordinate -- this seems correct ^_^ -luigi1111
this . ge _neg = function ( point ) {
if ( point . length !== 64 ) {
throw "expected 64 char hex string" ;
}
return point . slice ( 0 , 62 ) + ( ( parseInt ( point . slice ( 62 , 63 ) , 16 ) + 8 ) % 16 ) . toString ( 16 ) + point . slice ( 63 , 64 ) ;
} ;
//adds two points together, order does not matter
/ * t h i s . g e _ a d d 2 = f u n c t i o n ( p o i n t 1 , p o i n t 2 ) {
var point1 _m = Module . _malloc ( KEY _SIZE ) ;
var point2 _m = Module . _malloc ( KEY _SIZE ) ;
var point1 _m2 = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var point2 _m2 = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
Module . HEAPU8 . set ( hextobin ( point1 ) , point1 _m ) ;
Module . HEAPU8 . set ( hextobin ( point2 ) , point2 _m ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "bool" , [ "number" , "number" ] , [ point1 _m2 , point1 _m ] ) !== 0 ) {
throw "ge_frombytes_vartime returned non-zero error code" ;
}
if ( Module . ccall ( "ge_frombytes_vartime" , "bool" , [ "number" , "number" ] , [ point2 _m2 , point2 _m ] ) !== 0 ) {
throw "ge_frombytes_vartime returned non-zero error code" ;
}
var sum _m = Module . _malloc ( KEY _SIZE ) ;
var p2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var p1 _m = Module . _malloc ( STRUCT _SIZES . GE _P1P1 ) ;
var p3 _m = Module . _malloc ( STRUCT _SIZES . GE _CACHED ) ;
Module . ccall ( "ge_p3_to_cached" , "void" , [ "number" , "number" ] , [ p3 _m , point2 _m2 ] ) ;
Module . ccall ( "ge_add" , "void" , [ "number" , "number" , "number" ] , [ p1 _m , point1 _m2 , p3 _m ] ) ;
Module . ccall ( "ge_p1p1_to_p2" , "void" , [ "number" , "number" ] , [ p2 _m , p1 _m ] ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ sum _m , p2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( sum _m , sum _m + KEY _SIZE ) ;
Module . _free ( point1 _m ) ;
Module . _free ( point1 _m2 ) ;
Module . _free ( point2 _m ) ;
Module . _free ( point2 _m2 ) ;
Module . _free ( p2 _m ) ;
Module . _free ( p1 _m ) ;
Module . _free ( sum _m ) ;
Module . _free ( p3 _m ) ;
return bintohex ( res ) ;
} ; * /
this . ge _add = function ( p1 , p2 ) {
if ( p1 . length !== 64 || p2 . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "ge_add: Invalid p1 or p2 input lengths!" ;
2018-01-12 23:18:44 +00:00
}
return bintohex ( nacl . ll . ge _add ( hextobin ( p1 ) , hextobin ( p2 ) ) ) ;
} ;
//order matters
this . ge _sub = function ( point1 , point2 ) {
point2n = ge _neg ( point2 ) ;
return ge _add ( point1 , point2n ) ;
} ;
//adds two scalars together
this . sc _add = function ( scalar1 , scalar2 ) {
if ( scalar1 . length !== 64 || scalar2 . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "sc_add: Invalid scalar1 or scalar2 input length!" ;
2018-01-12 23:18:44 +00:00
}
var scalar1 _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var scalar2 _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
Module . HEAPU8 . set ( hextobin ( scalar1 ) , scalar1 _m ) ;
Module . HEAPU8 . set ( hextobin ( scalar2 ) , scalar2 _m ) ;
var derived _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
Module . ccall ( "sc_add" , "void" , [ "number" , "number" , "number" ] , [ derived _m , scalar1 _m , scalar2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( derived _m , derived _m + STRUCT _SIZES . EC _SCALAR ) ;
Module . _free ( scalar1 _m ) ;
Module . _free ( scalar2 _m ) ;
Module . _free ( derived _m ) ;
return bintohex ( res ) ;
} ;
//subtracts one scalar from another
this . sc _sub = function ( scalar1 , scalar2 ) {
if ( scalar1 . length !== 64 || scalar2 . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "sc_sub: Invalid scalar1 or scalar2 input lengths!" ;
2018-01-12 23:18:44 +00:00
}
var scalar1 _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var scalar2 _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
Module . HEAPU8 . set ( hextobin ( scalar1 ) , scalar1 _m ) ;
Module . HEAPU8 . set ( hextobin ( scalar2 ) , scalar2 _m ) ;
var derived _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
Module . ccall ( "sc_sub" , "void" , [ "number" , "number" , "number" ] , [ derived _m , scalar1 _m , scalar2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( derived _m , derived _m + STRUCT _SIZES . EC _SCALAR ) ;
Module . _free ( scalar1 _m ) ;
Module . _free ( scalar2 _m ) ;
Module . _free ( derived _m ) ;
return bintohex ( res ) ;
} ;
//fun mul function
this . sc _mul = function ( scalar1 , scalar2 ) {
if ( scalar1 . length !== 64 || scalar2 . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "sc_mul: Invalid scalar1 or scalar2 input lengths!" ;
2018-01-12 23:18:44 +00:00
}
return d2s ( JSBigInt ( s2d ( scalar1 ) ) . multiply ( JSBigInt ( s2d ( scalar2 ) ) ) . remainder ( l ) . toString ( ) ) ;
} ;
//res = c - (ab) mod l; argument names copied from the signature implementation
this . sc _mulsub = function ( sigc , sec , k ) {
if ( k . length !== KEY _SIZE * 2 || sigc . length !== KEY _SIZE * 2 || sec . length !== KEY _SIZE * 2 || ! this . valid _hex ( k ) || ! this . valid _hex ( sigc ) || ! this . valid _hex ( sec ) ) {
throw "bad scalar" ;
}
var sec _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sec ) , sec _m ) ;
var sigc _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sigc ) , sigc _m ) ;
var k _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( k ) , k _m ) ;
var res _m = Module . _malloc ( KEY _SIZE ) ;
Module . ccall ( "sc_mulsub" , "void" , [ "number" , "number" , "number" , "number" ] , [ res _m , sigc _m , sec _m , k _m ] ) ;
res = Module . HEAPU8 . subarray ( res _m , res _m + KEY _SIZE ) ;
Module . _free ( k _m ) ;
Module . _free ( sec _m ) ;
Module . _free ( sigc _m ) ;
Module . _free ( res _m ) ;
return bintohex ( res ) ;
} ;
//res = aB + cG; argument names copied from the signature implementation
/ * t h i s . g e _ d o u b l e _ s c a l a r m u l t _ b a s e _ v a r t i m e = f u n c t i o n ( s i g c , p u b , s i g r ) {
var pub _m = Module . _malloc ( KEY _SIZE ) ;
var pub2 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
Module . HEAPU8 . set ( hextobin ( pub ) , pub _m ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "void" , [ "number" , "number" ] , [ pub2 _m , pub _m ] ) !== 0 ) {
throw "Failed to call ge_frombytes_vartime" ;
}
var sigc _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sigc ) , sigc _m ) ;
var sigr _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sigr ) , sigr _m ) ;
if ( Module . ccall ( "sc_check" , "number" , [ "number" ] , [ sigc _m ] ) !== 0 || Module . ccall ( "sc_check" , "number" , [ "number" ] , [ sigr _m ] ) !== 0 ) {
throw "bad scalar(s)" ;
}
var tmp _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var res _m = Module . _malloc ( KEY _SIZE ) ;
Module . ccall ( "ge_double_scalarmult_base_vartime" , "void" , [ "number" , "number" , "number" , "number" ] , [ tmp _m , sigc _m , pub2 _m , sigr _m ] ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ res _m , tmp _m ] ) ;
var res = Module . HEAPU8 . subarray ( res _m , res _m + KEY _SIZE ) ;
Module . _free ( pub _m ) ;
Module . _free ( pub2 _m ) ;
Module . _free ( sigc _m ) ;
Module . _free ( sigr _m ) ;
Module . _free ( tmp _m ) ;
Module . _free ( res _m ) ;
return bintohex ( res ) ;
} ; * /
this . ge _double _scalarmult _base _vartime = function ( c , P , r ) {
if ( c . length !== 64 || P . length !== 64 || r . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "ge_double_scalarmult_base_vartime: Invalid c, P or r input lengths!" ;
2018-01-12 23:18:44 +00:00
}
return bintohex ( nacl . ll . ge _double _scalarmult _base _vartime ( hextobin ( c ) , hextobin ( P ) , hextobin ( r ) ) ) ;
} ;
//res = a * Hp(B) + c*D
//res = sigr * Hp(pub) + sigc * k_image; argument names also copied from the signature implementation; note precomp AND hash_to_ec are done internally!!
/ * t h i s . g e _ d o u b l e _ s c a l a r m u l t _ p o s t c o m p _ v a r t i m e = f u n c t i o n ( s i g r , p u b , s i g c , k _ i m a g e ) {
var image _m = Module . _malloc ( STRUCT _SIZES . KEY _IMAGE ) ;
Module . HEAPU8 . set ( hextobin ( k _image ) , image _m ) ;
var image _unp _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var image _pre _m = Module . _malloc ( STRUCT _SIZES . GE _DSMP ) ;
var tmp3 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var sigr _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var sigc _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var tmp2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var res _m = Module . _malloc ( STRUCT _SIZES . EC _POINT ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "void" , [ "number" , "number" ] , [ image _unp _m , image _m ] ) !== 0 ) {
throw "Failed to call ge_frombytes_vartime" ;
}
Module . ccall ( "ge_dsm_precomp" , "void" , [ "number" , "number" ] , [ image _pre _m , image _unp _m ] ) ;
var ec = this . hash _to _ec ( pub ) ;
Module . HEAPU8 . set ( hextobin ( ec ) , tmp3 _m ) ;
Module . HEAPU8 . set ( hextobin ( sigc ) , sigc _m ) ;
Module . HEAPU8 . set ( hextobin ( sigr ) , sigr _m ) ;
Module . ccall ( "ge_double_scalarmult_precomp_vartime" , "void" , [ "number" , "number" , "number" , "number" , "number" ] , [ tmp2 _m , sigr _m , tmp3 _m , sigc _m , image _pre _m ] ) ;
Module . ccall ( "ge_tobytes" , "void" , [ "number" , "number" ] , [ res _m , tmp2 _m ] ) ;
var res = Module . HEAPU8 . subarray ( res _m , res _m + STRUCT _SIZES . EC _POINT ) ;
Module . _free ( image _m ) ;
Module . _free ( image _unp _m ) ;
Module . _free ( image _pre _m ) ;
Module . _free ( tmp3 _m ) ;
Module . _free ( sigr _m ) ;
Module . _free ( sigc _m ) ;
Module . _free ( tmp2 _m ) ;
Module . _free ( res _m ) ;
return bintohex ( res ) ;
} ; * /
this . ge _double _scalarmult _postcomp _vartime = function ( r , P , c , I ) {
if ( c . length !== 64 || P . length !== 64 || r . length !== 64 || I . length !== 64 ) {
2018-01-28 00:19:23 +00:00
throw "ge_double_scalarmult_postcomp_vartime: Invalid r, p, c or I input lengths!" ;
2018-01-12 23:18:44 +00:00
}
var Pb = this . hash _to _ec _2 ( P ) ;
return bintohex ( nacl . ll . ge _double _scalarmult _postcomp _vartime ( hextobin ( r ) , hextobin ( Pb ) , hextobin ( c ) , hextobin ( I ) ) ) ;
} ;
//begin RCT functions
//xv: vector of secret keys, 1 per ring (nrings)
//pm: matrix of pubkeys, indexed by size first
//iv: vector of indexes, 1 per ring (nrings), can be a string
//size: ring size
//nrings: number of rings
//extensible borromean signatures
this . genBorromean = function ( xv , pm , iv , size , nrings ) {
if ( xv . length !== nrings ) {
throw "wrong xv length " + xv . length ;
}
if ( pm . length !== size ) {
throw "wrong pm size " + pm . length ;
}
for ( var i = 0 ; i < pm . length ; i ++ ) {
if ( pm [ i ] . length !== nrings ) {
throw "wrong pm[" + i + "] length " + pm [ i ] . length ;
}
}
if ( iv . length !== nrings ) {
throw "wrong iv length " + iv . length ;
}
for ( var i = 0 ; i < iv . length ; i ++ ) {
if ( iv [ i ] >= size ) {
throw "bad indices value at: " + i + ": " + iv [ i ] ;
}
}
//signature struct
var bb = {
s : [ ] ,
ee : ""
} ;
//signature pubkey matrix
var L = [ ] ;
//add needed sub vectors (1 per ring size)
for ( var i = 0 ; i < size ; i ++ ) {
bb . s [ i ] = [ ] ;
L [ i ] = [ ] ;
}
//compute starting at the secret index to the last row
var index ;
var alpha = [ ] ;
for ( var i = 0 ; i < nrings ; i ++ ) {
index = parseInt ( iv [ i ] ) ;
alpha [ i ] = random _scalar ( ) ;
L [ index ] [ i ] = ge _scalarmult _base ( alpha [ i ] ) ;
for ( var j = index + 1 ; j < size ; j ++ ) {
bb . s [ j ] [ i ] = random _scalar ( ) ;
var c = hash _to _scalar ( L [ j - 1 ] [ i ] ) ;
L [ j ] [ i ] = ge _double _scalarmult _base _vartime ( c , pm [ j ] [ i ] , bb . s [ j ] [ i ] ) ;
}
}
//hash last row to create ee
var ltemp = "" ;
for ( var i = 0 ; i < nrings ; i ++ ) {
ltemp += L [ size - 1 ] [ i ] ;
}
bb . ee = hash _to _scalar ( ltemp ) ;
//compute the rest from 0 to secret index
for ( var i = 0 ; i < nrings ; i ++ ) {
var cc = bb . ee
for ( var j = 0 ; j < iv [ i ] ; j ++ ) {
bb . s [ j ] [ i ] = random _scalar ( ) ;
var LL = ge _double _scalarmult _base _vartime ( cc , pm [ j ] [ i ] , bb . s [ j ] [ i ] ) ;
cc = hash _to _scalar ( LL ) ;
}
bb . s [ j ] [ i ] = sc _mulsub ( xv [ i ] , cc , alpha [ i ] ) ;
}
return bb ;
} ;
//proveRange
//proveRange gives C, and mask such that \sumCi = C
// c.f. http://eprint.iacr.org/2015/1098 section 5.1
// and Ci is a commitment to either 0 or s^i, i=0,...,n
// thus this proves that "amount" is in [0, s^n] (we assume s to be 4) (2 for now with v2 txes)
// mask is a such that C = aG + bH, and b = amount
//commitMaskObj = {C: commit, mask: mask}
this . proveRange = function ( commitMaskObj , amount , nrings , enc _seed , exponent ) {
var size = 2 ;
var C = I ; //identity
var mask = Z ; //zero scalar
var indices = d2b ( amount ) ; //base 2 for now
var sig = {
Ci : [ ]
//exp: exponent //doesn't exist for now
}
/ * p a y l o a d s t u f f - i g n o r e f o r n o w
seeds = new Array ( 3 ) ;
for ( var i = 0 ; i < seeds . length ; i ++ ) {
seeds [ i ] = new Array ( 1 ) ;
}
genSeeds ( seeds , enc _seed ) ;
* /
var ai = [ ] ;
var PM = [ ] ;
for ( var i = 0 ; i < size ; i ++ ) {
PM [ i ] = [ ] ;
}
var j ;
//start at index and fill PM left and right -- PM[0] holds Ci
for ( i = 0 ; i < nrings ; i ++ ) {
ai [ i ] = random _scalar ( ) ;
j = indices [ i ] ;
PM [ j ] [ i ] = ge _scalarmult _base ( ai [ i ] ) ;
while ( j > 0 ) {
j -- ;
PM [ j ] [ i ] = ge _add ( PM [ j + 1 ] [ i ] , H2 [ i ] ) ; //will need to use i*2 for base 4 (or different object)
}
j = indices [ i ] ;
while ( j < size - 1 ) {
j ++ ;
PM [ j ] [ i ] = ge _sub ( PM [ j - 1 ] [ i ] , H2 [ i ] ) ; //will need to use i*2 for base 4 (or different object)
}
mask = sc _add ( mask , ai [ i ] ) ;
}
/ *
* some more payload stuff here
* /
//copy commitments to sig and sum them to commitment
for ( i = 0 ; i < nrings ; i ++ ) {
//if (i < nrings - 1) //for later version
sig . Ci [ i ] = PM [ 0 ] [ i ] ;
C = ge _add ( C , PM [ 0 ] [ i ] ) ;
}
/ * e x p o n e n t s t u f f - i g n o r e f o r n o w
if ( exponent ) {
n = JSBigInt ( 10 ) ;
n = n . pow ( exponent ) . toString ( ) ;
mask = sc _mul ( mask , d2s ( n ) ) ; //new sum
}
* /
sig . bsig = this . genBorromean ( ai , PM , indices , size , nrings ) ;
commitMaskObj . C = C ;
commitMaskObj . mask = mask ;
return sig ;
} ;
function array _hash _to _scalar ( array ) {
var buf = ""
for ( var i = 0 ; i < array . length ; i ++ ) {
if ( typeof array [ i ] !== "string" ) { throw "unexpected array element" ; }
buf += array [ i ] ;
}
return hash _to _scalar ( buf ) ;
}
// Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
// we presently only support matrices of 2 rows (pubkey, commitment)
// this is a simplied MLSAG_Gen function to reflect that
// because we don't want to force same secret column for all inputs
this . MLSAG _Gen = function ( message , pk , xx , kimg , index ) {
var cols = pk . length ; //ring size
if ( index >= cols ) { throw "index out of range" ; }
var rows = pk [ 0 ] . length ; //number of signature rows (always 2)
if ( rows !== 2 ) { throw "wrong row count" ; }
for ( var i = 0 ; i < cols ; i ++ ) {
if ( pk [ i ] . length !== rows ) { throw "pk is not rectangular" ; }
}
if ( xx . length !== rows ) { throw "bad xx size" ; }
var c _old = "" ;
var alpha = [ ] ;
var rv = {
ss : [ ] ,
cc : null
} ;
for ( i = 0 ; i < cols ; i ++ ) {
rv . ss [ i ] = [ ] ;
}
var toHash = [ ] ; //holds 6 elements: message, pubkey, dsRow L, dsRow R, commitment, ndsRow L
toHash [ 0 ] = message ;
//secret index (pubkey section)
alpha [ 0 ] = random _scalar ( ) ; //need to save alphas for later
toHash [ 1 ] = pk [ index ] [ 0 ] ; //secret index pubkey
toHash [ 2 ] = ge _scalarmult _base ( alpha [ 0 ] ) ; //dsRow L
toHash [ 3 ] = generate _key _image _2 ( pk [ index ] [ 0 ] , alpha [ 0 ] ) ; //dsRow R (key image check)
//secret index (commitment section)
alpha [ 1 ] = random _scalar ( ) ;
toHash [ 4 ] = pk [ index ] [ 1 ] ; //secret index commitment
toHash [ 5 ] = ge _scalarmult _base ( alpha [ 1 ] ) ; //ndsRow L
c _old = array _hash _to _scalar ( toHash ) ;
i = ( index + 1 ) % cols ;
if ( i === 0 ) {
rv . cc = c _old ;
}
while ( i != index ) {
rv . ss [ i ] [ 0 ] = random _scalar ( ) ; //dsRow ss
rv . ss [ i ] [ 1 ] = random _scalar ( ) ; //ndsRow ss
//!secret index (pubkey section)
toHash [ 1 ] = pk [ i ] [ 0 ] ;
toHash [ 2 ] = ge _double _scalarmult _base _vartime ( c _old , pk [ i ] [ 0 ] , rv . ss [ i ] [ 0 ] ) ;
toHash [ 3 ] = ge _double _scalarmult _postcomp _vartime ( rv . ss [ i ] [ 0 ] , pk [ i ] [ 0 ] , c _old , kimg ) ;
//!secret index (commitment section)
toHash [ 4 ] = pk [ i ] [ 1 ] ;
toHash [ 5 ] = ge _double _scalarmult _base _vartime ( c _old , pk [ i ] [ 1 ] , rv . ss [ i ] [ 1 ] ) ;
c _old = array _hash _to _scalar ( toHash ) ; //hash to get next column c
i = ( i + 1 ) % cols ;
if ( i === 0 ) {
rv . cc = c _old ;
}
}
for ( i = 0 ; i < rows ; i ++ ) {
rv . ss [ index ] [ i ] = sc _mulsub ( c _old , xx [ i ] , alpha [ i ] ) ;
}
return rv ;
} ;
//prepares for MLSAG_Gen
this . proveRctMG = function ( message , pubs , inSk , kimg , mask , Cout , index ) {
var cols = pubs . length ;
if ( cols < 3 ) { throw "cols must be > 2 (mixin)" ; }
var xx = [ ] ;
var PK = [ ] ;
//fill pubkey matrix (copy destination, subtract commitments)
for ( var i = 0 ; i < cols ; i ++ ) {
PK [ i ] = [ ] ;
PK [ i ] [ 0 ] = pubs [ i ] . dest ;
PK [ i ] [ 1 ] = ge _sub ( pubs [ i ] . mask , Cout ) ;
}
xx [ 0 ] = inSk . x ;
xx [ 1 ] = sc _sub ( inSk . a , mask ) ;
return this . MLSAG _Gen ( message , PK , xx , kimg , index ) ;
} ;
this . get _pre _mlsag _hash = function ( rv ) {
var hashes = "" ;
hashes += rv . message ;
hashes += this . cn _fast _hash ( this . serialize _rct _base ( rv ) ) ;
var buf = serialize _range _proofs ( rv ) ;
hashes += this . cn _fast _hash ( buf ) ;
return this . cn _fast _hash ( hashes ) ;
}
function serialize _range _proofs ( rv ) {
var buf = "" ;
for ( var i = 0 ; i < rv . p . rangeSigs . length ; i ++ ) {
for ( var j = 0 ; j < rv . p . rangeSigs [ i ] . bsig . s . length ; j ++ ) {
for ( var l = 0 ; l < rv . p . rangeSigs [ i ] . bsig . s [ j ] . length ; l ++ ) {
buf += rv . p . rangeSigs [ i ] . bsig . s [ j ] [ l ] ;
}
}
buf += rv . p . rangeSigs [ i ] . bsig . ee ;
for ( j = 0 ; j < rv . p . rangeSigs [ i ] . Ci . length ; j ++ ) {
buf += rv . p . rangeSigs [ i ] . Ci [ j ] ;
}
}
return buf ;
}
//message is normal prefix hash
//inSk is vector of x,a
//kimg is vector of kimg
//destinations is vector of pubkeys (we skip and proxy outAmounts instead)
//inAmounts is vector of strings
//outAmounts is vector of strings
//mixRing is matrix of pubkey, commit (dest, mask)
//amountKeys is vector of scalars
//indices is vector
//txnFee is string
this . genRct = function ( message , inSk , kimg , /*destinations, */ inAmounts , outAmounts , mixRing , amountKeys , indices , txnFee ) {
if ( outAmounts . length !== amountKeys . length ) { throw "different number of amounts/amount_keys" ; }
for ( var i = 0 ; i < mixRing . length ; i ++ ) {
if ( mixRing [ i ] . length <= indices [ i ] ) { throw "bad mixRing/index size" ; }
}
if ( mixRing . length !== inSk . length ) { throw "mismatched mixRing/inSk" ; }
if ( inAmounts . length !== inSk . length ) { throw "mismatched inAmounts/inSk" ; }
if ( indices . length !== inSk . length ) { throw "mismatched indices/inSk" ; }
rv = {
type : inSk . length === 1 ? RCTTypeFull : RCTTypeSimple ,
message : message ,
outPk : [ ] ,
p : {
rangeSigs : [ ] ,
MGs : [ ]
} ,
ecdhInfo : [ ] ,
txnFee : txnFee . toString ( ) ,
pseudoOuts : [ ]
} ;
var sumout = Z ;
var cmObj = {
C : null ,
mask : null
} ;
var nrings = 64 ; //for base 2/current
//compute range proofs, etc
for ( i = 0 ; i < outAmounts . length ; i ++ ) {
var teststart = new Date ( ) . getTime ( ) ;
rv . p . rangeSigs [ i ] = this . proveRange ( cmObj , outAmounts [ i ] , nrings , 0 , 0 ) ;
var testfinish = new Date ( ) . getTime ( ) - teststart ;
console . log ( "Time take for range proof " + i + ": " + testfinish ) ;
rv . outPk [ i ] = cmObj . C ;
sumout = sc _add ( sumout , cmObj . mask ) ;
rv . ecdhInfo [ i ] = this . encode _rct _ecdh ( { mask : cmObj . mask , amount : d2s ( outAmounts [ i ] ) } , amountKeys [ i ] ) ;
}
//simple
if ( rv . type === 2 ) {
var ai = [ ] ;
var sumpouts = Z ;
//create pseudoOuts
for ( i = 0 ; i < inAmounts . length - 1 ; i ++ ) {
ai [ i ] = random _scalar ( ) ;
sumpouts = sc _add ( sumpouts , ai [ i ] ) ;
rv . pseudoOuts [ i ] = commit ( d2s ( inAmounts [ i ] ) , ai [ i ] ) ;
}
ai [ i ] = sc _sub ( sumout , sumpouts ) ;
rv . pseudoOuts [ i ] = commit ( d2s ( inAmounts [ i ] ) , ai [ i ] ) ;
var full _message = this . get _pre _mlsag _hash ( rv ) ;
for ( i = 0 ; i < inAmounts . length ; i ++ ) {
rv . p . MGs . push ( this . proveRctMG ( full _message , mixRing [ i ] , inSk [ i ] , kimg [ i ] , ai [ i ] , rv . pseudoOuts [ i ] , indices [ i ] ) ) ;
}
} else {
var sumC = I ;
//get sum of output commitments to use in MLSAG
for ( i = 0 ; i < rv . outPk . length ; i ++ ) {
sumC = ge _add ( sumC , rv . outPk [ i ] ) ;
}
sumC = ge _add ( sumC , ge _scalarmult ( H , d2s ( rv . txnFee ) ) ) ;
var full _message = this . get _pre _mlsag _hash ( rv ) ;
rv . p . MGs . push ( this . proveRctMG ( full _message , mixRing [ 0 ] , inSk [ 0 ] , kimg [ 0 ] , sumout , sumC , indices [ 0 ] ) ) ;
}
return rv ;
} ;
//end RCT functions
this . add _pub _key _to _extra = function ( extra , pubkey ) {
if ( pubkey . length !== 64 ) throw "Invalid pubkey length" ;
// Append pubkey tag and pubkey
extra += TX _EXTRA _TAGS . PUBKEY + pubkey ;
return extra ;
} ;
this . add _nonce _to _extra = function ( extra , nonce ) {
// Append extra nonce
if ( ( nonce . length % 2 ) !== 0 ) {
throw "Invalid extra nonce" ;
}
if ( ( nonce . length / 2 ) > TX _EXTRA _NONCE _MAX _COUNT ) {
throw "Extra nonce must be at most " + TX _EXTRA _NONCE _MAX _COUNT + " bytes" ;
}
// Add nonce tag
extra += TX _EXTRA _TAGS . NONCE ;
// Encode length of nonce
extra += ( '0' + ( nonce . length / 2 ) . toString ( 16 ) ) . slice ( - 2 ) ;
// Write nonce
extra += nonce ;
return extra ;
} ;
this . get _payment _id _nonce = function ( payment _id , pid _encrypt ) {
if ( payment _id . length !== 64 && payment _id . length !== 16 ) {
throw "Invalid payment id" ;
}
var res = '' ;
if ( pid _encrypt ) {
res += TX _EXTRA _NONCE _TAGS . ENCRYPTED _PAYMENT _ID ;
} else {
res += TX _EXTRA _NONCE _TAGS . PAYMENT _ID ;
}
res += payment _id ;
return res ;
} ;
this . abs _to _rel _offsets = function ( offsets ) {
if ( offsets . length === 0 ) return offsets ;
for ( var i = offsets . length - 1 ; i >= 1 ; -- i ) {
offsets [ i ] = new JSBigInt ( offsets [ i ] ) . subtract ( offsets [ i - 1 ] ) . toString ( ) ;
}
return offsets ;
} ;
this . get _tx _prefix _hash = function ( tx ) {
var prefix = this . serialize _tx ( tx , true ) ;
return this . cn _fast _hash ( prefix ) ;
} ;
this . get _tx _hash = function ( tx ) {
if ( typeof ( tx ) === 'string' ) {
return this . cn _fast _hash ( tx ) ;
} else {
return this . cn _fast _hash ( this . serialize _tx ( tx ) ) ;
}
} ;
this . serialize _tx = function ( tx , headeronly ) {
//tx: {
// version: uint64,
// unlock_time: uint64,
// extra: hex,
// vin: [{amount: uint64, k_image: hex, key_offsets: [uint64,..]},...],
// vout: [{amount: uint64, target: {key: hex}},...],
// signatures: [[s,s,...],...]
//}
if ( headeronly === undefined ) {
headeronly = false ;
}
var buf = "" ;
buf += this . encode _varint ( tx . version ) ;
buf += this . encode _varint ( tx . unlock _time ) ;
buf += this . encode _varint ( tx . vin . length ) ;
var i , j ;
for ( i = 0 ; i < tx . vin . length ; i ++ ) {
var vin = tx . vin [ i ] ;
switch ( vin . type ) {
case "input_to_key" :
buf += "02" ;
buf += this . encode _varint ( vin . amount ) ;
buf += this . encode _varint ( vin . key _offsets . length ) ;
for ( j = 0 ; j < vin . key _offsets . length ; j ++ ) {
buf += this . encode _varint ( vin . key _offsets [ j ] ) ;
}
buf += vin . k _image ;
break ;
default :
throw "Unhandled vin type: " + vin . type ;
}
}
buf += this . encode _varint ( tx . vout . length ) ;
for ( i = 0 ; i < tx . vout . length ; i ++ ) {
var vout = tx . vout [ i ] ;
buf += this . encode _varint ( vout . amount ) ;
switch ( vout . target . type ) {
case "txout_to_key" :
buf += "02" ;
buf += vout . target . key ;
break ;
default :
throw "Unhandled txout target type: " + vout . target . type ;
}
}
if ( ! this . valid _hex ( tx . extra ) ) {
throw "Tx extra has invalid hex" ;
}
buf += this . encode _varint ( tx . extra . length / 2 ) ;
buf += tx . extra ;
if ( ! headeronly ) {
if ( tx . vin . length !== tx . signatures . length ) {
throw "Signatures length != vin length" ;
}
for ( i = 0 ; i < tx . vin . length ; i ++ ) {
for ( j = 0 ; j < tx . signatures [ i ] . length ; j ++ ) {
buf += tx . signatures [ i ] [ j ] ;
}
}
}
return buf ;
} ;
this . serialize _rct _tx _with _hash = function ( tx ) {
var hashes = "" ;
var buf = "" ;
buf += this . serialize _tx ( tx , true ) ;
hashes += this . cn _fast _hash ( buf ) ;
var buf2 = this . serialize _rct _base ( tx . rct _signatures ) ;
hashes += this . cn _fast _hash ( buf2 ) ;
buf += buf2 ;
var buf3 = serialize _range _proofs ( tx . rct _signatures ) ;
//add MGs
for ( var i = 0 ; i < tx . rct _signatures . p . MGs . length ; i ++ ) {
for ( var j = 0 ; j < tx . rct _signatures . p . MGs [ i ] . ss . length ; j ++ ) {
buf3 += tx . rct _signatures . p . MGs [ i ] . ss [ j ] [ 0 ] ;
buf3 += tx . rct _signatures . p . MGs [ i ] . ss [ j ] [ 1 ] ;
}
buf3 += tx . rct _signatures . p . MGs [ i ] . cc ;
}
hashes += this . cn _fast _hash ( buf3 ) ;
buf += buf3 ;
var hash = this . cn _fast _hash ( hashes ) ;
return {
raw : buf ,
hash : hash ,
prvkey : tx . prvkey
} ;
} ;
this . serialize _rct _base = function ( rv ) {
var buf = "" ;
buf += this . encode _varint ( rv . type ) ;
buf += this . encode _varint ( rv . txnFee ) ;
if ( rv . type === 2 ) {
for ( var i = 0 ; i < rv . pseudoOuts . length ; i ++ ) {
buf += rv . pseudoOuts [ i ] ;
}
}
if ( rv . ecdhInfo . length !== rv . outPk . length ) {
throw "mismatched outPk/ecdhInfo!" ;
}
for ( i = 0 ; i < rv . ecdhInfo . length ; i ++ ) {
buf += rv . ecdhInfo [ i ] . mask ;
buf += rv . ecdhInfo [ i ] . amount ;
}
for ( i = 0 ; i < rv . outPk . length ; i ++ ) {
buf += rv . outPk [ i ] ;
}
return buf ;
} ;
this . generate _ring _signature = function ( prefix _hash , k _image , keys , sec , real _index ) {
if ( k _image . length !== STRUCT _SIZES . KEY _IMAGE * 2 ) {
throw "invalid key image length" ;
}
if ( sec . length !== KEY _SIZE * 2 ) {
throw "Invalid secret key length" ;
}
if ( prefix _hash . length !== HASH _SIZE * 2 || ! this . valid _hex ( prefix _hash ) ) {
throw "Invalid prefix hash" ;
}
if ( real _index >= keys . length || real _index < 0 ) {
throw "real_index is invalid" ;
}
var _ge _tobytes = Module . cwrap ( "ge_tobytes" , "void" , [ "number" , "number" ] ) ;
var _ge _p3 _tobytes = Module . cwrap ( "ge_p3_tobytes" , "void" , [ "number" , "number" ] ) ;
var _ge _scalarmult _base = Module . cwrap ( "ge_scalarmult_base" , "void" , [ "number" , "number" ] ) ;
var _ge _scalarmult = Module . cwrap ( "ge_scalarmult" , "void" , [ "number" , "number" , "number" ] ) ;
var _sc _add = Module . cwrap ( "sc_add" , "void" , [ "number" , "number" , "number" ] ) ;
var _sc _sub = Module . cwrap ( "sc_sub" , "void" , [ "number" , "number" , "number" ] ) ;
var _sc _mulsub = Module . cwrap ( "sc_mulsub" , "void" , [ "number" , "number" , "number" , "number" ] ) ;
var _sc _0 = Module . cwrap ( "sc_0" , "void" , [ "number" ] ) ;
var _ge _double _scalarmult _base _vartime = Module . cwrap ( "ge_double_scalarmult_base_vartime" , "void" , [ "number" , "number" , "number" , "number" ] ) ;
var _ge _double _scalarmult _precomp _vartime = Module . cwrap ( "ge_double_scalarmult_precomp_vartime" , "void" , [ "number" , "number" , "number" , "number" , "number" ] ) ;
var _ge _frombytes _vartime = Module . cwrap ( "ge_frombytes_vartime" , "number" , [ "number" , "number" ] ) ;
var _ge _dsm _precomp = Module . cwrap ( "ge_dsm_precomp" , "void" , [ "number" , "number" ] ) ;
var buf _size = STRUCT _SIZES . EC _POINT * 2 * keys . length ;
var buf _m = Module . _malloc ( buf _size ) ;
var sig _size = STRUCT _SIZES . SIGNATURE * keys . length ;
var sig _m = Module . _malloc ( sig _size ) ;
// Struct pointer helper functions
function buf _a ( i ) {
return buf _m + STRUCT _SIZES . EC _POINT * ( 2 * i ) ;
}
function buf _b ( i ) {
return buf _m + STRUCT _SIZES . EC _POINT * ( 2 * i + 1 ) ;
}
function sig _c ( i ) {
return sig _m + STRUCT _SIZES . EC _SCALAR * ( 2 * i ) ;
}
function sig _r ( i ) {
return sig _m + STRUCT _SIZES . EC _SCALAR * ( 2 * i + 1 ) ;
}
var image _m = Module . _malloc ( STRUCT _SIZES . KEY _IMAGE ) ;
Module . HEAPU8 . set ( hextobin ( k _image ) , image _m ) ;
var i ;
var image _unp _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var image _pre _m = Module . _malloc ( STRUCT _SIZES . GE _DSMP ) ;
var sum _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var k _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var h _m = Module . _malloc ( STRUCT _SIZES . EC _SCALAR ) ;
var tmp2 _m = Module . _malloc ( STRUCT _SIZES . GE _P2 ) ;
var tmp3 _m = Module . _malloc ( STRUCT _SIZES . GE _P3 ) ;
var pub _m = Module . _malloc ( KEY _SIZE ) ;
var sec _m = Module . _malloc ( KEY _SIZE ) ;
Module . HEAPU8 . set ( hextobin ( sec ) , sec _m ) ;
if ( _ge _frombytes _vartime ( image _unp _m , image _m ) != 0 ) {
throw "failed to call ge_frombytes_vartime" ;
}
_ge _dsm _precomp ( image _pre _m , image _unp _m ) ;
_sc _0 ( sum _m ) ;
for ( i = 0 ; i < keys . length ; i ++ ) {
if ( i === real _index ) {
// Real key
var rand = this . random _scalar ( ) ;
Module . HEAPU8 . set ( hextobin ( rand ) , k _m ) ;
_ge _scalarmult _base ( tmp3 _m , k _m ) ;
_ge _p3 _tobytes ( buf _a ( i ) , tmp3 _m ) ;
var ec = this . hash _to _ec ( keys [ i ] ) ;
Module . HEAPU8 . set ( hextobin ( ec ) , tmp3 _m ) ;
_ge _scalarmult ( tmp2 _m , k _m , tmp3 _m ) ;
_ge _tobytes ( buf _b ( i ) , tmp2 _m ) ;
} else {
Module . HEAPU8 . set ( hextobin ( this . random _scalar ( ) ) , sig _c ( i ) ) ;
Module . HEAPU8 . set ( hextobin ( this . random _scalar ( ) ) , sig _r ( i ) ) ;
Module . HEAPU8 . set ( hextobin ( keys [ i ] ) , pub _m ) ;
if ( Module . ccall ( "ge_frombytes_vartime" , "void" , [ "number" , "number" ] , [ tmp3 _m , pub _m ] ) !== 0 ) {
throw "Failed to call ge_frombytes_vartime" ;
}
_ge _double _scalarmult _base _vartime ( tmp2 _m , sig _c ( i ) , tmp3 _m , sig _r ( i ) ) ;
_ge _tobytes ( buf _a ( i ) , tmp2 _m ) ;
var ec = this . hash _to _ec ( keys [ i ] ) ;
Module . HEAPU8 . set ( hextobin ( ec ) , tmp3 _m ) ;
_ge _double _scalarmult _precomp _vartime ( tmp2 _m , sig _r ( i ) , tmp3 _m , sig _c ( i ) , image _pre _m ) ;
_ge _tobytes ( buf _b ( i ) , tmp2 _m ) ;
_sc _add ( sum _m , sum _m , sig _c ( i ) ) ;
}
}
var buf _bin = Module . HEAPU8 . subarray ( buf _m , buf _m + buf _size ) ;
var scalar = this . hash _to _scalar ( prefix _hash + bintohex ( buf _bin ) ) ;
Module . HEAPU8 . set ( hextobin ( scalar ) , h _m ) ;
_sc _sub ( sig _c ( real _index ) , h _m , sum _m ) ;
_sc _mulsub ( sig _r ( real _index ) , sig _c ( real _index ) , sec _m , k _m ) ;
var sig _data = bintohex ( Module . HEAPU8 . subarray ( sig _m , sig _m + sig _size ) ) ;
var sigs = [ ] ;
for ( var k = 0 ; k < keys . length ; k ++ ) {
sigs . push ( sig _data . slice ( STRUCT _SIZES . SIGNATURE * 2 * k , STRUCT _SIZES . SIGNATURE * 2 * ( k + 1 ) ) ) ;
}
Module . _free ( image _m ) ;
Module . _free ( image _unp _m ) ;
Module . _free ( image _pre _m ) ;
Module . _free ( sum _m ) ;
Module . _free ( k _m ) ;
Module . _free ( h _m ) ;
Module . _free ( tmp2 _m ) ;
Module . _free ( tmp3 _m ) ;
Module . _free ( buf _m ) ;
Module . _free ( sig _m ) ;
Module . _free ( pub _m ) ;
Module . _free ( sec _m ) ;
return sigs ;
} ;
// reargances array to specific indices.
this . rearrange = function ( arr , ind ) {
var new _arr = [ ] ;
for ( j = 0 ; j < ind . length ; j ++ ) {
new _arr . push ( arr [ ind [ j ] ] ) ;
}
return new _arr ;
} ;
this . construct _tx = function ( keys , sources , dsts , fee _amount , payment _id , pid _encrypt , realDestViewKey , unlock _time , rct ) {
//we move payment ID stuff here, because we need txkey to encrypt
var txkey = this . random _keypair ( ) ;
console . log ( txkey ) ;
var extra = '' ;
if ( payment _id ) {
if ( pid _encrypt && payment _id . length !== INTEGRATED _ID _SIZE * 2 ) {
throw "payment ID must be " + INTEGRATED _ID _SIZE + " bytes to be encrypted!" ;
}
console . log ( "Adding payment id: " + payment _id ) ;
if ( pid _encrypt ) { //get the derivation from our passed viewkey, then hash that + tail to get encryption key
var pid _key = this . cn _fast _hash ( this . generate _key _derivation ( realDestViewKey , txkey . sec ) + ENCRYPTED _PAYMENT _ID _TAIL . toString ( 16 ) ) . slice ( 0 , INTEGRATED _ID _SIZE * 2 ) ;
console . log ( "Txkeys:" , txkey , "Payment ID key:" , pid _key ) ;
payment _id = this . hex _xor ( payment _id , pid _key ) ;
}
var nonce = this . get _payment _id _nonce ( payment _id , pid _encrypt ) ;
console . log ( "Extra nonce: " + nonce ) ;
extra = this . add _nonce _to _extra ( extra , nonce ) ;
}
// for sending to a subaddress, need to generate new tx public key
//var sub_addr_decoded = this.decode_address(dsts[0].address); // for example dest[0] is subaddress
//txkey.pub = ge_scalarmult(sub_addr_decoded.spend, txkey.sec);
var tx = {
unlock _time : unlock _time ,
version : rct ? CURRENT _TX _VERSION : OLD _TX _VERSION ,
extra : extra ,
prvkey : '' ,
vin : [ ] ,
vout : [ ]
} ;
if ( rct ) {
tx . rct _signatures = { } ;
} else {
tx . signatures = [ ] ;
}
//tx.extra = this.add_pub_key_to_extra(tx.extra, txkey.pub);
tx . prvkey = txkey . sec ;
var in _contexts = [ ] ;
var is _rct _coinbases = [ ] ; // monkey patching to solve problem of
// not being able to spend coinbase ringct txs.
var inputs _money = JSBigInt . ZERO ;
var i , j ;
console . log ( 'Sources: ' ) ;
for ( i = 0 ; i < sources . length ; i ++ )
{
console . log ( i + ': ' + this . formatMoneyFull ( sources [ i ] . amount ) ) ;
if ( sources [ i ] . real _out >= sources [ i ] . outputs . length ) {
throw "real index >= outputs.length" ;
}
inputs _money = inputs _money . add ( sources [ i ] . amount ) ;
// sets res.mask among other things. mask is identity for non-rct transactions
// and for coinbase ringct (type = 0) txs.
var res = this . generate _key _image _helper _rct ( keys , sources [ i ] . real _out _tx _key , sources [ i ] . real _out _in _tx , sources [ i ] . mask ) ; //mask will be undefined for non-rct
in _contexts . push ( res . in _ephemeral ) ;
// now we mark if this is ringct coinbase txs. such transactions
// will have identity mask. Non-ringct txs will have sources[i].mask set to null.
// this only works if beckend will produce masks in get_unspent_outs for
// coinbaser ringct txs.
is _rct _coinbases . push ( ( sources [ i ] . mask ? sources [ i ] . mask === I : 0 ) ) ;
if ( res . in _ephemeral . pub !== sources [ i ] . outputs [ sources [ i ] . real _out ] . key ) {
throw "in_ephemeral.pub != source.real_out.key" ;
}
var input _to _key = { } ;
input _to _key . type = "input_to_key" ;
input _to _key . amount = sources [ i ] . amount ;
input _to _key . k _image = res . image ;
input _to _key . key _offsets = [ ] ;
for ( j = 0 ; j < sources [ i ] . outputs . length ; ++ j ) {
input _to _key . key _offsets . push ( sources [ i ] . outputs [ j ] . index ) ;
}
input _to _key . key _offsets = this . abs _to _rel _offsets ( input _to _key . key _offsets ) ;
tx . vin . push ( input _to _key ) ;
}
// new to sort key_imags and associated variables.
var ins _order = [ ] ;
for ( var i = 0 ; i < tx . vin . length ; i ++ ) {
ins _order . push ( i ) ;
}
2018-02-14 03:40:41 +00:00
// determine indexes which we should sort.
2018-01-12 23:18:44 +00:00
ins _order . sort ( function ( i0 , i1 ) {
if ( tx . vin [ i0 ] . k _image < tx . vin [ i1 ] . k _image ) {
return 1 ;
}
if ( tx . vin [ i0 ] . k _image > tx . vin [ i1 ] . k _image ) {
return - 1 ;
}
return 0 ;
} ) ;
// sort key images along with rources and contexts.
tx . vin = rearrange ( tx . vin , ins _order ) ;
sources = rearrange ( sources , ins _order ) ;
in _contexts = rearrange ( in _contexts , ins _order ) ;
var outputs _money = JSBigInt . ZERO ;
var out _index = 0 ;
var amountKeys = [ ] ; //rct only
for ( i = 0 ; i < dsts . length ; ++ i ) {
if ( new JSBigInt ( dsts [ i ] . amount ) . compare ( 0 ) < 0 ) {
throw "dst.amount < 0" ; //amount can be zero if no change
}
dsts [ i ] . keys = this . decode _address ( dsts [ i ] . address ) ;
if ( this . is _subaddress ( dsts [ i ] . address ) ) {
txkey . pub = ge _scalarmult ( dsts [ i ] . keys . spend , txkey . sec ) ;
}
var out _derivation ;
// if destination public view and spend keys matches our own public keys
// we send change to ourself
if ( dsts [ i ] . keys . view === keys . view . pub && dsts [ i ] . keys . spend === keys . spend . pub ) {
out _derivation = this . generate _key _derivation ( txkey . pub , keys . view . sec ) ;
}
else {
out _derivation = this . generate _key _derivation ( dsts [ i ] . keys . view , txkey . sec ) ;
}
if ( rct ) {
amountKeys . push ( this . derivation _to _scalar ( out _derivation , out _index ) ) ;
}
var out _ephemeral _pub = this . derive _public _key (
out _derivation , out _index , dsts [ i ] . keys . spend ) ;
var out = {
amount : dsts [ i ] . amount . toString ( )
} ;
// txout_to_key
out . target = {
type : "txout_to_key" ,
key : out _ephemeral _pub
} ;
tx . vout . push ( out ) ;
++ out _index ;
outputs _money = outputs _money . add ( dsts [ i ] . amount ) ;
}
tx . extra = this . add _pub _key _to _extra ( tx . extra , txkey . pub ) ;
if ( outputs _money . add ( fee _amount ) . compare ( inputs _money ) > 0 ) {
throw "outputs money (" + this . formatMoneyFull ( outputs _money ) + ") + fee (" + this . formatMoneyFull ( fee _amount ) + ") > inputs money (" + this . formatMoneyFull ( inputs _money ) + ")" ;
}
if ( ! rct ) {
for ( i = 0 ; i < sources . length ; ++ i ) {
var src _keys = [ ] ;
for ( j = 0 ; j < sources [ i ] . outputs . length ; ++ j ) {
src _keys . push ( sources [ i ] . outputs [ j ] . key ) ;
}
var sigs = this . generate _ring _signature ( this . get _tx _prefix _hash ( tx ) , tx . vin [ i ] . k _image , src _keys ,
in _contexts [ i ] . sec , sources [ i ] . real _out ) ;
tx . signatures . push ( sigs ) ;
}
} else { //rct
var txnFee = fee _amount ;
var keyimages = [ ] ;
var inSk = [ ] ;
var inAmounts = [ ] ;
var mixRing = [ ] ;
var indices = [ ] ;
for ( i = 0 ; i < tx . vin . length ; i ++ ) {
keyimages . push ( tx . vin [ i ] . k _image ) ;
inSk . push ( {
x : in _contexts [ i ] . sec ,
a : in _contexts [ i ] . mask
} ) ;
inAmounts . push ( tx . vin [ i ] . amount ) ;
//if (in_contexts[i].mask !== I) {//if input is rct (has a valid mask), 0 out amount
//tx.vin[i].amount = "0";
//}
if ( in _contexts [ i ] . mask !== I || is _rct _coinbases [ i ] === true )
{
// if input is rct (has a valid mask), 0 out amount
// coinbase ringct txs also have mask === I, so their amount
// must be set to zero when spending them.
tx . vin [ i ] . amount = "0" ;
}
mixRing [ i ] = [ ] ;
for ( j = 0 ; j < sources [ i ] . outputs . length ; j ++ ) {
mixRing [ i ] . push ( {
dest : sources [ i ] . outputs [ j ] . key ,
mask : sources [ i ] . outputs [ j ] . commit
} ) ;
}
indices . push ( sources [ i ] . real _out ) ;
}
var outAmounts = [ ] ;
for ( i = 0 ; i < tx . vout . length ; i ++ ) {
outAmounts . push ( tx . vout [ i ] . amount ) ;
tx . vout [ i ] . amount = "0" ; //zero out all rct outputs
}
var tx _prefix _hash = this . get _tx _prefix _hash ( tx ) ;
tx . rct _signatures = genRct ( tx _prefix _hash , inSk , keyimages , /*destinations, */ inAmounts , outAmounts , mixRing , amountKeys , indices , txnFee ) ;
}
console . log ( tx ) ;
return tx ;
} ;
this . create _transaction = function ( pub _keys , sec _keys , dsts , outputs , mix _outs , fake _outputs _count , fee _amount , payment _id , pid _encrypt , realDestViewKey , unlock _time , rct ) {
unlock _time = unlock _time || 0 ;
mix _outs = mix _outs || [ ] ;
var i , j ;
if ( dsts . length === 0 ) {
throw 'Destinations empty' ;
}
if ( mix _outs . length !== outputs . length && fake _outputs _count !== 0 ) {
throw 'Wrong number of mix outs provided (' + outputs . length + ' outputs, ' + mix _outs . length + ' mix outs)' ;
}
for ( i = 0 ; i < mix _outs . length ; i ++ ) {
if ( ( mix _outs [ i ] . outputs || [ ] ) . length < fake _outputs _count ) {
throw 'Not enough outputs to mix with' ;
}
}
var keys = {
view : {
pub : pub _keys . view ,
sec : sec _keys . view
} ,
spend : {
pub : pub _keys . spend ,
sec : sec _keys . spend
}
} ;
if ( ! this . valid _keys ( keys . view . pub , keys . view . sec , keys . spend . pub , keys . spend . sec ) ) {
throw "Invalid secret keys!" ;
}
var needed _money = JSBigInt . ZERO ;
for ( i = 0 ; i < dsts . length ; ++ i ) {
needed _money = needed _money . add ( dsts [ i ] . amount ) ;
if ( needed _money . compare ( UINT64 _MAX ) !== - 1 ) {
throw "Output overflow!" ;
}
}
var found _money = JSBigInt . ZERO ;
var sources = [ ] ;
console . log ( 'Selected transfers: ' , outputs ) ;
for ( i = 0 ; i < outputs . length ; ++ i ) {
found _money = found _money . add ( outputs [ i ] . amount ) ;
if ( found _money . compare ( UINT64 _MAX ) !== - 1 ) {
throw "Input overflow!" ;
}
var src = {
outputs : [ ]
} ;
src . amount = new JSBigInt ( outputs [ i ] . amount ) . toString ( ) ;
if ( mix _outs . length !== 0 ) {
// Sort fake outputs by global index
mix _outs [ i ] . outputs . sort ( function ( a , b ) {
return new JSBigInt ( a . global _index ) . compare ( b . global _index ) ;
} ) ;
j = 0 ;
while ( ( src . outputs . length < fake _outputs _count ) && ( j < mix _outs [ i ] . outputs . length ) ) {
var out = mix _outs [ i ] . outputs [ j ] ;
if ( out . global _index === outputs [ i ] . global _index ) {
console . log ( 'got mixin the same as output, skipping' ) ;
j ++ ;
continue ;
}
var oe = { } ;
oe . index = out . global _index . toString ( ) ;
oe . key = out . public _key ;
if ( rct ) {
if ( out . rct ) {
oe . commit = out . rct . slice ( 0 , 64 ) ; //add commitment from rct mix outs
} else {
if ( outputs [ i ] . rct ) { throw "mix rct outs missing commit" ; }
oe . commit = zeroCommit ( d2s ( src . amount ) ) ; //create identity-masked commitment for non-rct mix input
}
}
src . outputs . push ( oe ) ;
j ++ ;
}
}
var real _oe = { } ;
real _oe . index = new JSBigInt ( outputs [ i ] . global _index || 0 ) . toString ( ) ;
real _oe . key = outputs [ i ] . public _key ;
if ( rct ) {
if ( outputs [ i ] . rct ) {
real _oe . commit = outputs [ i ] . rct . slice ( 0 , 64 ) ; //add commitment for real input
} else {
real _oe . commit = zeroCommit ( d2s ( src . amount ) ) ; //create identity-masked commitment for non-rct input
}
}
var real _index = src . outputs . length ;
for ( j = 0 ; j < src . outputs . length ; j ++ ) {
if ( new JSBigInt ( real _oe . index ) . compare ( src . outputs [ j ] . index ) < 0 ) {
real _index = j ;
break ;
}
}
// Add real_oe to outputs
src . outputs . splice ( real _index , 0 , real _oe ) ;
src . real _out _tx _key = outputs [ i ] . tx _pub _key ;
// Real output entry index
src . real _out = real _index ;
src . real _out _in _tx = outputs [ i ] . index ;
if ( rct ) {
if ( outputs [ i ] . rct ) {
src . mask = outputs [ i ] . rct . slice ( 64 , 128 ) ; //encrypted or idenity mask for coinbase txs.
} else {
src . mask = null ; //will be set by generate_key_image_helper_rct
}
}
sources . push ( src ) ;
}
console . log ( 'sources: ' , sources ) ;
var change = {
amount : JSBigInt . ZERO
} ;
var cmp = needed _money . compare ( found _money ) ;
if ( cmp < 0 ) {
change . amount = found _money . subtract ( needed _money ) ;
if ( change . amount . compare ( fee _amount ) !== 0 ) {
throw "early fee calculation != later" ;
}
} else if ( cmp > 0 ) {
throw "Need more money than found! (have: " + cnUtil . formatMoney ( found _money ) + " need: " + cnUtil . formatMoney ( needed _money ) + ")" ;
}
return this . construct _tx ( keys , sources , dsts , fee _amount , payment _id , pid _encrypt , realDestViewKey , unlock _time , rct ) ;
} ;
this . estimateRctSize = function ( inputs , mixin , outputs ) {
var size = 0 ;
size += outputs * 6306 ;
size += ( ( mixin + 1 ) * 4 + 32 + 8 ) * inputs ; //key offsets + key image + amount
size += 64 * ( mixin + 1 ) * inputs + 64 * inputs ; //signature + pseudoOuts/cc
size += 74 ; //extra + whatever, assume long payment ID
return size ;
} ;
function trimRight ( str , char ) {
while ( str [ str . length - 1 ] == char ) str = str . slice ( 0 , - 1 ) ;
return str ;
}
function padLeft ( str , len , char ) {
while ( str . length < len ) {
str = char + str ;
}
return str ;
}
this . printDsts = function ( dsts ) {
for ( var i = 0 ; i < dsts . length ; i ++ ) {
console . log ( dsts [ i ] . address + ': ' + this . formatMoneyFull ( dsts [ i ] . amount ) ) ;
}
} ;
this . formatMoneyFull = function ( units ) {
units = units . toString ( ) ;
var symbol = units [ 0 ] === '-' ? '-' : '' ;
if ( symbol === '-' ) {
units = units . slice ( 1 ) ;
}
var decimal ;
if ( units . length >= config . coinUnitPlaces ) {
decimal = units . substr ( units . length - config . coinUnitPlaces , config . coinUnitPlaces ) ;
} else {
decimal = padLeft ( units , config . coinUnitPlaces , '0' ) ;
}
return symbol + ( units . substr ( 0 , units . length - config . coinUnitPlaces ) || '0' ) + '.' + decimal ;
} ;
this . formatMoneyFullSymbol = function ( units ) {
return this . formatMoneyFull ( units ) + ' ' + config . coinSymbol ;
} ;
this . formatMoney = function ( units ) {
var f = trimRight ( this . formatMoneyFull ( units ) , '0' ) ;
if ( f [ f . length - 1 ] === '.' ) {
return f . slice ( 0 , f . length - 1 ) ;
}
return f ;
} ;
this . formatMoneySymbol = function ( units ) {
return this . formatMoney ( units ) + ' ' + config . coinSymbol ;
} ;
this . parseMoney = function ( str ) {
if ( ! str ) return JSBigInt . ZERO ;
var negative = str [ 0 ] === '-' ;
if ( negative ) {
str = str . slice ( 1 ) ;
}
var decimalIndex = str . indexOf ( '.' ) ;
if ( decimalIndex == - 1 ) {
if ( negative ) {
return JSBigInt . multiply ( str , config . coinUnits ) . negate ( ) ;
}
return JSBigInt . multiply ( str , config . coinUnits ) ;
}
if ( decimalIndex + config . coinUnitPlaces + 1 < str . length ) {
str = str . substr ( 0 , decimalIndex + config . coinUnitPlaces + 1 ) ;
}
if ( negative ) {
return new JSBigInt ( str . substr ( 0 , decimalIndex ) ) . exp10 ( config . coinUnitPlaces )
. add ( new JSBigInt ( str . substr ( decimalIndex + 1 ) ) . exp10 ( decimalIndex + config . coinUnitPlaces - str . length + 1 ) ) . negate ;
}
return new JSBigInt ( str . substr ( 0 , decimalIndex ) ) . exp10 ( config . coinUnitPlaces )
. add ( new JSBigInt ( str . substr ( decimalIndex + 1 ) ) . exp10 ( decimalIndex + config . coinUnitPlaces - str . length + 1 ) ) ;
} ;
this . decompose _amount _into _digits = function ( amount ) {
/ * i f ( d u s t _ t h r e s h o l d = = = u n d e f i n e d ) {
dust _threshold = config . dustThreshold ;
} * /
amount = amount . toString ( ) ;
var ret = [ ] ;
while ( amount . length > 0 ) {
//split all the way down since v2 fork
/ * v a r r e m a i n i n g = n e w J S B i g I n t ( a m o u n t ) ;
if ( remaining . compare ( config . dustThreshold ) <= 0 ) {
if ( remaining . compare ( 0 ) > 0 ) {
ret . push ( remaining ) ;
}
break ;
} * /
//check so we don't create 0s
if ( amount [ 0 ] !== "0" ) {
var digit = amount [ 0 ] ;
while ( digit . length < amount . length ) {
digit += "0" ;
}
ret . push ( new JSBigInt ( digit ) ) ;
}
amount = amount . slice ( 1 ) ;
}
return ret ;
} ;
this . decompose _tx _destinations = function ( dsts , rct ) {
var out = [ ] ;
if ( rct ) {
for ( var i = 0 ; i < dsts . length ; i ++ ) {
out . push ( {
address : dsts [ i ] . address ,
amount : dsts [ i ] . amount
} ) ;
}
} else {
for ( var i = 0 ; i < dsts . length ; i ++ ) {
var digits = this . decompose _amount _into _digits ( dsts [ i ] . amount ) ;
for ( var j = 0 ; j < digits . length ; j ++ ) {
if ( digits [ j ] . compare ( 0 ) > 0 ) {
out . push ( {
address : dsts [ i ] . address ,
amount : digits [ j ]
} ) ;
}
}
}
}
return out . sort ( function ( a , b ) {
return a [ "amount" ] - b [ "amount" ] ;
} ) ;
} ;
this . is _tx _unlocked = function ( unlock _time , blockchain _height ) {
if ( ! config . maxBlockNumber ) {
throw "Max block number is not set in config!" ;
}
if ( unlock _time < config . maxBlockNumber ) {
// unlock time is block height
return blockchain _height >= unlock _time ;
} else {
// unlock time is timestamp
var current _time = Math . round ( new Date ( ) . getTime ( ) / 1000 ) ;
return current _time >= unlock _time ;
}
} ;
this . tx _locked _reason = function ( unlock _time , blockchain _height ) {
if ( unlock _time < config . maxBlockNumber ) {
// unlock time is block height
var numBlocks = unlock _time - blockchain _height ;
if ( numBlocks <= 0 ) {
return "Transaction is unlocked" ;
}
var unlock _prediction = moment ( ) . add ( numBlocks * config . avgBlockTime , 'seconds' ) ;
//return "Will be unlocked in " + numBlocks + " blocks, ~" + unlock_prediction.fromNow(true) + ", " + unlock_prediction.calendar() + "";
return "Will be unlocked in " + numBlocks + " blocks, ~" + unlock _prediction . fromNow ( true ) ;
} else {
// unlock time is timestamp
var current _time = Math . round ( new Date ( ) . getTime ( ) / 1000 ) ;
var time _difference = unlock _time - current _time ;
if ( time _difference <= 0 ) {
return "Transaction is unlocked" ;
}
var unlock _moment = moment ( unlock _time * 1000 ) ;
//return "Will be unlocked " + unlock_moment.fromNow() + ", " + unlock_moment.calendar();
return "Will be unlocked " + unlock _moment . fromNow ( ) ;
}
} ;
2018-01-21 03:12:59 +00:00
//decode amount and mask and check against commitment
// from https://xmr.llcoins.net/js/site.js
// from https://xmr.llcoins.net/js/site.js
this . decodeRct = function ( rv , i , der ) {
var key = derivation _to _scalar ( der , i ) ;
var ecdh = decode _rct _ecdh ( rv . ecdhInfo [ i ] , key ) ;
//console.log(ecdh);
var Ctmp = commit ( ecdh . amount , ecdh . mask ) ;
//console.log(Ctmp);
if ( Ctmp !== rv . outPk [ i ] ) {
throw "mismatched commitments!" ;
}
ecdh . amount = s2d ( ecdh . amount ) ;
return ecdh ;
} ;
2018-01-12 23:18:44 +00:00
function assert ( stmt , val ) {
if ( ! stmt ) {
throw "assert failed" + ( val !== undefined ? ': ' + val : '' ) ;
}
}
return this ;
} ) ( config ) ;