@@ -24,6 +24,7 @@ var Currency = amount.Currency;
24
24
// Shortcuts
25
25
var hex = sjcl . codec . hex ;
26
26
var bytes = sjcl . codec . bytes ;
27
+ var utf8 = sjcl . codec . utf8String ;
27
28
28
29
var BigInteger = utils . jsbn . BigInteger ;
29
30
@@ -52,7 +53,7 @@ function isBigInteger(val) {
52
53
return val instanceof BigInteger ;
53
54
} ;
54
55
55
- function serialize_hex ( so , hexData , noLength ) {
56
+ function serializeHex ( so , hexData , noLength ) {
56
57
var byteData = bytes . fromBits ( hex . toBits ( hexData ) ) ;
57
58
if ( ! noLength ) {
58
59
SerializedType . serialize_varint ( so , byteData . length ) ;
@@ -63,10 +64,18 @@ function serialize_hex(so, hexData, noLength) {
63
64
/**
64
65
* parses bytes as hex
65
66
*/
66
- function convert_bytes_to_hex ( byte_array ) {
67
+ function convertByteArrayToHex ( byte_array ) {
67
68
return sjcl . codec . hex . fromBits ( sjcl . codec . bytes . toBits ( byte_array ) ) . toUpperCase ( ) ;
68
69
} ;
69
70
71
+ function convertStringToHex ( string ) {
72
+ return hex . fromBits ( utf8 . toBits ( string ) ) . toUpperCase ( ) ;
73
+ }
74
+
75
+ function convertHexToString ( hexString ) {
76
+ return utf8 . fromBits ( hex . toBits ( hexString ) ) ;
77
+ }
78
+
70
79
SerializedType . serialize_varint = function ( so , val ) {
71
80
if ( val < 0 ) {
72
81
throw new Error ( 'Variable integers are unsigned.' ) ;
@@ -115,7 +124,7 @@ SerializedType.prototype.parse_varint = function (so) {
115
124
*
116
125
* The result is appended to the serialized object ('so').
117
126
*/
118
- function append_byte_array ( so , val , bytes ) {
127
+ function convertIntegerToByteArray ( val , bytes ) {
119
128
if ( ! isNumber ( val ) ) {
120
129
throw new Error ( 'Value is not a number' , bytes ) ;
121
130
}
@@ -130,7 +139,7 @@ function append_byte_array(so, val, bytes) {
130
139
newBytes . unshift ( val >>> ( i * 8 ) & 0xff ) ;
131
140
}
132
141
133
- so . append ( newBytes ) ;
142
+ return newBytes ;
134
143
} ;
135
144
136
145
// Convert a certain number of bytes from the serialized object ('so') into an integer.
@@ -152,7 +161,7 @@ function readAndSum(so, bytes) {
152
161
153
162
var STInt8 = exports . Int8 = new SerializedType ( {
154
163
serialize : function ( so , val ) {
155
- append_byte_array ( so , val , 1 ) ;
164
+ so . append ( convertIntegerToByteArray ( val , 1 ) ) ;
156
165
} ,
157
166
parse : function ( so ) {
158
167
return readAndSum ( so , 1 ) ;
@@ -163,7 +172,7 @@ STInt8.id = 16;
163
172
164
173
var STInt16 = exports . Int16 = new SerializedType ( {
165
174
serialize : function ( so , val ) {
166
- append_byte_array ( so , val , 2 ) ;
175
+ so . append ( convertIntegerToByteArray ( val , 2 ) ) ;
167
176
} ,
168
177
parse : function ( so ) {
169
178
return readAndSum ( so , 2 ) ;
@@ -174,7 +183,7 @@ STInt16.id = 1;
174
183
175
184
var STInt32 = exports . Int32 = new SerializedType ( {
176
185
serialize : function ( so , val ) {
177
- append_byte_array ( so , val , 4 ) ;
186
+ so . append ( convertIntegerToByteArray ( val , 4 ) ) ;
178
187
} ,
179
188
parse : function ( so ) {
180
189
return readAndSum ( so , 4 ) ;
@@ -217,7 +226,7 @@ var STInt64 = exports.Int64 = new SerializedType({
217
226
hex = '0' + hex ;
218
227
}
219
228
220
- serialize_hex ( so , hex , true ) ; //noLength = true
229
+ serializeHex ( so , hex , true ) ; //noLength = true
221
230
} ,
222
231
parse : function ( so ) {
223
232
var bytes = so . read ( 8 ) ;
@@ -237,7 +246,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
237
246
if ( ! hash . is_valid ( ) ) {
238
247
throw new Error ( 'Invalid Hash128' ) ;
239
248
}
240
- serialize_hex ( so , hash . to_hex ( ) , true ) ; //noLength = true
249
+ serializeHex ( so , hash . to_hex ( ) , true ) ; //noLength = true
241
250
} ,
242
251
parse : function ( so ) {
243
252
return UInt128 . from_bytes ( so . read ( 16 ) ) ;
@@ -252,7 +261,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
252
261
if ( ! hash . is_valid ( ) ) {
253
262
throw new Error ( 'Invalid Hash256' ) ;
254
263
}
255
- serialize_hex ( so , hash . to_hex ( ) , true ) ; //noLength = true
264
+ serializeHex ( so , hash . to_hex ( ) , true ) ; //noLength = true
256
265
} ,
257
266
parse : function ( so ) {
258
267
return UInt256 . from_bytes ( so . read ( 32 ) ) ;
@@ -267,7 +276,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
267
276
if ( ! hash . is_valid ( ) ) {
268
277
throw new Error ( 'Invalid Hash160' ) ;
269
278
}
270
- serialize_hex ( so , hash . to_hex ( ) , true ) ; //noLength = true
279
+ serializeHex ( so , hash . to_hex ( ) , true ) ; //noLength = true
271
280
} ,
272
281
parse : function ( so ) {
273
282
return UInt160 . from_bytes ( so . read ( 20 ) ) ;
@@ -294,7 +303,7 @@ var STCurrency = new SerializedType({
294
303
// UInt160 value and consider it valid. But it doesn't, so for the
295
304
// deserialization to be usable, we need to allow invalid results for now.
296
305
//if (!currency.is_valid()) {
297
- // throw new Error('Invalid currency: '+convert_bytes_to_hex (bytes));
306
+ // throw new Error('Invalid currency: '+convertByteArrayToHex (bytes));
298
307
//}
299
308
return currency ;
300
309
}
@@ -409,15 +418,16 @@ STAmount.id = 6;
409
418
410
419
var STVL = exports . VariableLength = exports . VL = new SerializedType ( {
411
420
serialize : function ( so , val ) {
421
+
412
422
if ( typeof val === 'string' ) {
413
- serialize_hex ( so , val ) ;
423
+ serializeHex ( so , val ) ;
414
424
} else {
415
425
throw new Error ( 'Unknown datatype.' ) ;
416
426
}
417
427
} ,
418
428
parse : function ( so ) {
419
429
var len = this . parse_varint ( so ) ;
420
- return convert_bytes_to_hex ( so . read ( len ) ) ;
430
+ return convertByteArrayToHex ( so . read ( len ) ) ;
421
431
}
422
432
} ) ;
423
433
@@ -429,7 +439,7 @@ var STAccount = exports.Account = new SerializedType({
429
439
if ( ! account . is_valid ( ) ) {
430
440
throw new Error ( 'Invalid account!' ) ;
431
441
}
432
- serialize_hex ( so , account . to_hex ( ) ) ;
442
+ serializeHex ( so , account . to_hex ( ) ) ;
433
443
} ,
434
444
parse : function ( so ) {
435
445
var len = this . parse_varint ( so ) ;
@@ -441,7 +451,6 @@ var STAccount = exports.Account = new SerializedType({
441
451
var result = UInt160 . from_bytes ( so . read ( len ) ) ;
442
452
result . set_version ( Base . VER_ACCOUNT_ID ) ;
443
453
444
- //console.log('PARSED 160:', result.to_json());
445
454
if ( false && ! result . is_valid ( ) ) {
446
455
throw new Error ( 'Invalid Account' ) ;
447
456
}
@@ -593,6 +602,105 @@ var STVector256 = exports.Vector256 = new SerializedType({
593
602
594
603
STVector256 . id = 19 ;
595
604
605
+ // Internal
606
+ var STMemo = exports . STMemo = new SerializedType ( {
607
+ serialize : function ( so , val , no_marker ) {
608
+
609
+ var keys = [ ] ;
610
+
611
+ Object . keys ( val ) . forEach ( function ( key ) {
612
+ // Ignore lowercase field names - they're non-serializable fields by
613
+ // convention.
614
+ if ( key [ 0 ] === key [ 0 ] . toLowerCase ( ) ) {
615
+ return ;
616
+ }
617
+
618
+ if ( typeof binformat . fieldsInverseMap [ key ] === 'undefined' ) {
619
+ throw new Error ( 'JSON contains unknown field: "' + key + '"' ) ;
620
+ }
621
+
622
+ keys . push ( key ) ;
623
+ } ) ;
624
+
625
+ // Sort fields
626
+ keys = sort_fields ( keys ) ;
627
+
628
+ // store that we're dealing with json
629
+ var isJson = val . MemoFormat === 'json' ;
630
+
631
+ for ( var i = 0 ; i < keys . length ; i ++ ) {
632
+ var key = keys [ i ] ;
633
+ var value = val [ key ] ;
634
+ switch ( key ) {
635
+
636
+ // MemoType and MemoFormat are always ASCII strings
637
+ case 'MemoType' :
638
+ case 'MemoFormat' :
639
+ value = convertStringToHex ( value ) ;
640
+ break ;
641
+
642
+ // MemoData can be a JSON object, otherwise it's a string
643
+ case 'MemoData' :
644
+ if ( typeof value !== 'string' ) {
645
+ if ( isJson ) {
646
+ try {
647
+ value = convertStringToHex ( JSON . stringify ( value ) ) ;
648
+ } catch ( e ) {
649
+ throw new Error ( 'MemoFormat json with invalid JSON in MemoData field' ) ;
650
+ }
651
+ } else {
652
+ throw new Error ( 'MemoData can only be a JSON object with a valid json MemoFormat' ) ;
653
+ }
654
+ } else if ( isString ( value ) ) {
655
+ value = convertStringToHex ( value ) ;
656
+ }
657
+ break ;
658
+ }
659
+
660
+ serialize ( so , key , value ) ;
661
+ }
662
+
663
+ if ( ! no_marker ) {
664
+ //Object ending marker
665
+ STInt8 . serialize ( so , 0xe1 ) ;
666
+ }
667
+
668
+ } ,
669
+ parse : function ( so ) {
670
+ var output = { } ;
671
+ while ( so . peek ( 1 ) [ 0 ] !== 0xe1 ) {
672
+ var keyval = parse ( so ) ;
673
+ output [ keyval [ 0 ] ] = keyval [ 1 ] ;
674
+ }
675
+
676
+ if ( output [ 'MemoType' ] !== void ( 0 ) ) {
677
+ output [ 'parsed_memo_type' ] = convertHexToString ( output [ 'MemoType' ] ) ;
678
+ }
679
+
680
+ if ( output [ 'MemoFormat' ] !== void ( 0 ) ) {
681
+ output [ 'parsed_memo_format' ] = convertHexToString ( output [ 'MemoFormat' ] ) ;
682
+ }
683
+
684
+ if ( output [ 'MemoData' ] !== void ( 0 ) ) {
685
+
686
+ // see if we can parse JSON
687
+ if ( output [ 'parsed_memo_format' ] === 'json' ) {
688
+ try {
689
+ output [ 'parsed_memo_data' ] = JSON . parse ( convertHexToString ( output [ 'MemoData' ] ) ) ;
690
+ } catch ( e ) {
691
+ // fail, which is fine, we just won't add the memo_data field
692
+ }
693
+ } else if ( output [ 'parsed_memo_format' ] === 'text' ) {
694
+ output [ 'parsed_memo_data' ] = convertHexToString ( output [ 'MemoData' ] ) ;
695
+ }
696
+ }
697
+
698
+ so . read ( 1 ) ;
699
+ return output ;
700
+ }
701
+
702
+ } ) ;
703
+
596
704
exports . serialize = exports . serialize_whatever = serialize ;
597
705
598
706
function serialize ( so , field_name , value ) {
@@ -622,9 +730,15 @@ function serialize(so, field_name, value) {
622
730
STInt8 . serialize ( so , field_bits ) ;
623
731
}
624
732
625
- // Get the serializer class (ST...) for a field based on the type bits.
626
- var serialized_object_type = exports [ binformat . types [ type_bits ] ] ;
627
- //do something with val[keys] and val[keys[i]];
733
+ // Get the serializer class (ST...)
734
+ var serialized_object_type ;
735
+ if ( field_name === 'Memo' && typeof value === 'object' ) {
736
+ // for Memo we override the default behavior with our STMemo serializer
737
+ serialized_object_type = exports . STMemo ;
738
+ } else {
739
+ // for a field based on the type bits.
740
+ serialized_object_type = exports [ binformat . types [ type_bits ] ] ;
741
+ }
628
742
629
743
try {
630
744
serialized_object_type . serialize ( so , value ) ;
@@ -645,18 +759,21 @@ function parse(so) {
645
759
type_bits = so . read ( 1 ) [ 0 ] ;
646
760
}
647
761
648
- // Get the parser class (ST...) for a field based on the type bits.
649
- var type = exports [ binformat . types [ type_bits ] ] ;
650
-
651
- assert ( type , 'Unknown type - header byte is 0x' + tag_byte . toString ( 16 ) ) ;
652
762
653
763
var field_bits = tag_byte & 0x0f ;
654
764
var field_name = ( field_bits === 0 )
655
- ? field_name = binformat . fields [ type_bits ] [ so . read ( 1 ) [ 0 ] ]
656
- : field_name = binformat . fields [ type_bits ] [ field_bits ] ;
765
+ ? field_name = binformat . fields [ type_bits ] [ so . read ( 1 ) [ 0 ] ]
766
+ : field_name = binformat . fields [ type_bits ] [ field_bits ] ;
657
767
658
768
assert ( field_name , 'Unknown field - header byte is 0x' + tag_byte . toString ( 16 ) ) ;
659
769
770
+ // Get the parser class (ST...) for a field based on the type bits.
771
+ var type = ( field_name === 'Memo' )
772
+ ? exports . STMemo
773
+ : exports [ binformat . types [ type_bits ] ] ;
774
+
775
+ assert ( type , 'Unknown type - header byte is 0x' + tag_byte . toString ( 16 ) ) ;
776
+
660
777
return [ field_name , type . parse ( so ) ] ; //key, value
661
778
} ;
662
779
@@ -678,18 +795,20 @@ function sort_fields(keys) {
678
795
679
796
var STObject = exports . Object = new SerializedType ( {
680
797
serialize : function ( so , val , no_marker ) {
681
- var keys = Object . keys ( val ) ;
798
+ var keys = [ ] ;
682
799
683
- // Ignore lowercase field names - they're non-serializable fields by
684
- // convention.
685
- keys = keys . filter ( function ( key ) {
686
- return key [ 0 ] !== key [ 0 ] . toLowerCase ( ) ;
687
- } ) ;
800
+ Object . keys ( val ) . forEach ( function ( key ) {
801
+ // Ignore lowercase field names - they're non-serializable fields by
802
+ // convention.
803
+ if ( key [ 0 ] === key [ 0 ] . toLowerCase ( ) ) {
804
+ return ;
805
+ }
688
806
689
- keys . forEach ( function ( key ) {
690
807
if ( typeof binformat . fieldsInverseMap [ key ] === 'undefined' ) {
691
808
throw new Error ( 'JSON contains unknown field: "' + key + '"' ) ;
692
809
}
810
+
811
+ keys . push ( key ) ;
693
812
} ) ;
694
813
695
814
// Sort fields
0 commit comments