mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 04:05:52 +00:00
[FEATURE] improve memo support
- add MemoFormat property for memo - MemoFormat and MemoType must be valid ASCII - Memo content is converted on the serialization level - add parsed_* version of Memo content if the parser understand the format - support `text` and `json` MemoFormat [FIX] double serialization overriding Memo contents The copy made in from_json wasn't a deep copy
This commit is contained in:
@@ -1,8 +1,23 @@
|
||||
var utils = require('./testutils');
|
||||
var assert = require('assert');
|
||||
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
|
||||
var sjcl = require('./../src/js/ripple/utils').sjcl;
|
||||
|
||||
// Shortcuts
|
||||
var hex = sjcl.codec.hex;
|
||||
var bytes = sjcl.codec.bytes;
|
||||
var utf8 = sjcl.codec.utf8String;
|
||||
|
||||
describe('Serialized object', function() {
|
||||
|
||||
function convertStringToHex(string) {
|
||||
return hex.fromBits(utf8.toBits(string)).toUpperCase();
|
||||
}
|
||||
|
||||
function convertHexToString(hexString) {
|
||||
return utf8.fromBits(hex.toBits(hexString));
|
||||
}
|
||||
|
||||
describe('#from_json(v).to_json() == v', function(){
|
||||
it('outputs same as passed to from_json', function() {
|
||||
var input_json = {
|
||||
@@ -35,6 +50,7 @@ describe('Serialized object', function() {
|
||||
assert.deepEqual(input_json, output_json);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#from_json', function() {
|
||||
it('understands TransactionType as a Number', function() {
|
||||
var input_json = {
|
||||
@@ -52,6 +68,7 @@ describe('Serialized object', function() {
|
||||
assert.equal(0, input_json.TransactionType);
|
||||
assert.equal("Payment", output_json.TransactionType);
|
||||
});
|
||||
|
||||
it('understands LedgerEntryType as a Number', function() {
|
||||
var input_json = {
|
||||
// no, non required fields
|
||||
@@ -65,6 +82,7 @@ describe('Serialized object', function() {
|
||||
assert.equal(100, input_json.LedgerEntryType);
|
||||
assert.equal("DirectoryNode", output_json.LedgerEntryType);
|
||||
});
|
||||
|
||||
describe('Format validation', function() {
|
||||
// Peercover actually had a problem submitting transactions without a `Fee`
|
||||
// and rippled was only informing of "transaction is invalid"
|
||||
@@ -80,15 +98,231 @@ describe('Serialized object', function() {
|
||||
};
|
||||
assert.throws (
|
||||
function() {
|
||||
var output_json = SerializedObject.from_json(input_json);
|
||||
SerializedObject.from_json(input_json);
|
||||
},
|
||||
/Payment is missing fields: \["Fee"\]/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
describe('Memos', function() {
|
||||
|
||||
var input_json;
|
||||
|
||||
beforeEach(function() {
|
||||
input_json = {
|
||||
"Flags": 2147483648,
|
||||
"TransactionType": "Payment",
|
||||
"Account": "rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF",
|
||||
"Amount": "1",
|
||||
"Destination": "radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung",
|
||||
"Sequence": 281,
|
||||
"SigningPubKey": "03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51",
|
||||
"Fee": "12000",
|
||||
"LastLedgerSequence": 10074214,
|
||||
"TxnSignature": "304402201180636F2CE215CE97A29CD302618FAE60D63EBFC8903DE17A356E857A449C430220290F4A54F9DE4AC79034C8BEA5F1F8757F7505F1A6FF04D2E19B6D62E867256B"
|
||||
};
|
||||
});
|
||||
|
||||
it('should serialize and parse - full memo, all strings text/plain ', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "text",
|
||||
"MemoData": "some data"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'text';
|
||||
input_json.Memos[0].Memo.parsed_memo_data = 'some data';
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('text');
|
||||
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
|
||||
|
||||
assert.deepEqual(so, input_json);
|
||||
});
|
||||
|
||||
it('should serialize and parse - full memo, all strings, invalid MemoFormat', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "application/json",
|
||||
"MemoData": "some data"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'application/json';
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('application/json');
|
||||
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
|
||||
|
||||
assert.deepEqual(so, input_json);
|
||||
assert.strictEqual(input_json.Memos[0].Memo.parsed_memo_data, void(0));
|
||||
});
|
||||
|
||||
it('should throw an error - full memo, json data, invalid MemoFormat', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "text",
|
||||
"MemoData": {
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
assert.throws(function() {
|
||||
SerializedObject.from_json(input_json);
|
||||
}, /^Error: MemoData can only be a JSON object with a valid json MemoFormat \(Memo\) \(Memos\)/);
|
||||
});
|
||||
|
||||
it('should serialize and parse - full memo, json data, valid MemoFormat, ignored field', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "json",
|
||||
"ignored" : "ignored",
|
||||
"MemoData": {
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var so = SerializedObject.from_json(input_json).to_json();
|
||||
delete input_json.Memos[0].Memo.ignored;
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'json';
|
||||
input_json.Memos[0].Memo.parsed_memo_data = {
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
};
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
|
||||
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
|
||||
{
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
}
|
||||
));
|
||||
|
||||
assert.deepEqual(so, input_json);
|
||||
});
|
||||
|
||||
it('should serialize and parse - full memo, json data, valid MemoFormat', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "json",
|
||||
"MemoData": {
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'json';
|
||||
input_json.Memos[0].Memo.parsed_memo_data = {
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
};
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
|
||||
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
|
||||
{
|
||||
"string" : "some_string",
|
||||
"boolean" : true
|
||||
}
|
||||
));
|
||||
|
||||
assert.deepEqual(so, input_json);
|
||||
});
|
||||
|
||||
it('should serialize and parse - full memo, json data, valid MemoFormat, integer', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoFormat": "json",
|
||||
"MemoData": 3
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'json';
|
||||
input_json.Memos[0].Memo.parsed_memo_data = 3;
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
|
||||
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.parse(3));
|
||||
assert.deepEqual(so, input_json);
|
||||
});
|
||||
|
||||
it('should throw an error - invalid Memo field', function() {
|
||||
input_json.Memos = [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "test",
|
||||
"MemoParty": "json",
|
||||
"MemoData": 3
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
assert.throws(function() {
|
||||
SerializedObject.from_json(input_json);
|
||||
}, /^Error: JSON contains unknown field: "MemoParty" \(Memo\) \(Memos\)/);
|
||||
});
|
||||
|
||||
|
||||
it('should serialize json with memo - match hex output', function() {
|
||||
var input_json = {
|
||||
Flags: 2147483648,
|
||||
TransactionType: 'Payment',
|
||||
Account: 'rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF',
|
||||
Amount: '1',
|
||||
Destination: 'radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung',
|
||||
Memos: [
|
||||
{
|
||||
Memo: {
|
||||
MemoType: 'image'
|
||||
}
|
||||
}
|
||||
],
|
||||
Sequence: 294,
|
||||
SigningPubKey: '03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51',
|
||||
Fee: '12000',
|
||||
LastLedgerSequence: 10404607,
|
||||
TxnSignature: '304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9'
|
||||
};
|
||||
|
||||
var serializedHex = '12000022800000002400000126201B009EC2FF614000000000000001684000000000002EE0732103D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C517446304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9811426C4CFB3BD05A9AA23936F2E81634C66A9820C9483143DD06317D19C6110CAFF150AE528F58843BE2CA1F9EA7C05696D616765E1F1';
|
||||
assert.strictEqual(SerializedObject.from_json(input_json).to_hex(), serializedHex);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
@@ -1051,26 +1051,84 @@ describe('Transaction', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
transaction.addMemo('testkey', 'testvalue');
|
||||
transaction.addMemo('testkey2', 'testvalue2');
|
||||
transaction.addMemo('testkey3');
|
||||
transaction.addMemo(void(0), 'testvalue4');
|
||||
var memoType = 'message';
|
||||
var memoFormat = 'application/json';
|
||||
var memoData = {
|
||||
string: 'value',
|
||||
bool: true,
|
||||
integer: 1
|
||||
};
|
||||
|
||||
transaction.addMemo(memoType, memoFormat, memoData);
|
||||
|
||||
var expected = [
|
||||
{
|
||||
Memo:
|
||||
{
|
||||
MemoType: memoType,
|
||||
MemoFormat: memoFormat,
|
||||
MemoData: memoData
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, expected);
|
||||
});
|
||||
|
||||
it('Add Memo - by object', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
var memo = {
|
||||
memoType: 'type',
|
||||
memoData: 'data'
|
||||
};
|
||||
|
||||
transaction.addMemo(memo);
|
||||
|
||||
var expected = [
|
||||
{
|
||||
Memo: {
|
||||
MemoType: memo.memoType,
|
||||
MemoData: memo.memoData
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, expected);
|
||||
});
|
||||
|
||||
it('Add Memos', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
transaction.addMemo('testkey', void(0), 'testvalue');
|
||||
transaction.addMemo('testkey2', void(0), 'testvalue2');
|
||||
transaction.addMemo('testkey3', 'text/html');
|
||||
transaction.addMemo(void(0), void(0), 'testvalue4');
|
||||
transaction.addMemo('testkey4', 'text/html', '<html>');
|
||||
|
||||
var expected = [
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey').toString('hex'),
|
||||
MemoData: new Buffer('testvalue').toString('hex')
|
||||
MemoType: 'testkey',
|
||||
MemoData: 'testvalue'
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey2').toString('hex'),
|
||||
MemoData: new Buffer('testvalue2').toString('hex')
|
||||
MemoType: 'testkey2',
|
||||
MemoData: 'testvalue2'
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey3').toString('hex')
|
||||
MemoType: 'testkey3',
|
||||
MemoFormat: 'text/html'
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoData: new Buffer('testvalue4').toString('hex')
|
||||
} }
|
||||
MemoData: 'testvalue4'
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoType: 'testkey4',
|
||||
MemoFormat: 'text/html',
|
||||
MemoData: '<html>'
|
||||
}}
|
||||
];
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, expected);
|
||||
@@ -1085,13 +1143,76 @@ describe('Transaction', function() {
|
||||
}, /^Error: MemoType must be a string$/);
|
||||
});
|
||||
|
||||
it('Add Memo - invalid MemoData', function() {
|
||||
it('Add Memo - invalid ASCII MemoType', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.addMemo('key', 1);
|
||||
}, /^Error: MemoData must be a string$/);
|
||||
transaction.addMemo('한국어');
|
||||
}, /^Error: MemoType must be valid ASCII$/);
|
||||
});
|
||||
|
||||
it('Add Memo - invalid MemoFormat', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.addMemo(void(0), 1);
|
||||
}, /^Error: MemoFormat must be a string$/);
|
||||
});
|
||||
|
||||
it('Add Memo - invalid ASCII MemoFormat', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.addMemo(void(0), 'России');
|
||||
}, /^Error: MemoFormat must be valid ASCII$/);
|
||||
});
|
||||
|
||||
it('Add Memo - MemoData string', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
transaction.addMemo({memoData:'some_string'});
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, [
|
||||
{
|
||||
Memo: {
|
||||
MemoData: 'some_string'
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('Add Memo - MemoData complex object', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
var memo = {
|
||||
memoData: {
|
||||
string: 'string',
|
||||
int: 1,
|
||||
array: [
|
||||
{
|
||||
string: 'string'
|
||||
}
|
||||
],
|
||||
object: {
|
||||
string: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
transaction.addMemo(memo);
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, [
|
||||
{
|
||||
Memo: {
|
||||
MemoData: memo.memoData
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('Construct AccountSet transaction', function() {
|
||||
@@ -1268,7 +1389,7 @@ describe('Transaction', function() {
|
||||
var bid = '1/USD/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
|
||||
var ask = '1/EUR/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
|
||||
assert.throws(function() {
|
||||
var transaction = new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
|
||||
new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1301,13 +1422,13 @@ describe('Transaction', function() {
|
||||
|
||||
it('Construct SetRegularKey transaction - invalid account', function() {
|
||||
assert.throws(function() {
|
||||
var transaction = new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
});
|
||||
});
|
||||
|
||||
it('Construct SetRegularKey transaction - invalid regularKey', function() {
|
||||
assert.throws(function() {
|
||||
var transaction = new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user