Check unknown serialization fields

This commit is contained in:
wltsmrz
2015-05-20 18:09:02 -07:00
parent 65f7485497
commit 8596dcef21
3 changed files with 93 additions and 23 deletions

View File

@@ -224,7 +224,9 @@ var base = [
[ 'Fee' , REQUIRED ], [ 'Fee' , REQUIRED ],
[ 'OperationLimit' , OPTIONAL ], [ 'OperationLimit' , OPTIONAL ],
[ 'SigningPubKey' , REQUIRED ], [ 'SigningPubKey' , REQUIRED ],
[ 'TxnSignature' , OPTIONAL ] [ 'TxnSignature' , OPTIONAL ],
[ 'AccountTxnID' , OPTIONAL ],
[ 'Memos' , OPTIONAL ]
]; ];
exports.tx = { exports.tx = {

View File

@@ -97,39 +97,60 @@ SerializedObject.from_json = function(obj) {
} }
// ND: This from_*json* seems a reasonable place to put validation of `json` // ND: This from_*json* seems a reasonable place to put validation of `json`
SerializedObject.check_no_missing_fields(typedef, obj); SerializedObject.check_fields(typedef, obj);
so.serialize(typedef, obj); so.serialize(typedef, obj);
return so; return so;
}; };
SerializedObject.check_no_missing_fields = function(typedef, obj) { SerializedObject.check_fields = function(typedef, obj) {
var missing_fields = []; let missingFields = [];
let unknownFields = [];
let fieldsMap = {};
for (var i = typedef.length - 1; i >= 0; i--) { // Get missing required fields
var spec = typedef[i]; typedef.forEach(function(field) {
var field = spec[0]; var fieldName = field[0];
var requirement = spec[1]; var isRequired = field[1] === binformat.REQUIRED;
if (binformat.REQUIRED === requirement && obj[field] === undefined) { if (isRequired && obj[fieldName] === undefined) {
missing_fields.push(field); missingFields.push(fieldName);
}
}
if (missing_fields.length > 0) {
var object_name;
if (obj.TransactionType !== undefined) {
object_name = SerializedObject.lookup_type_tx(obj.TransactionType);
} else if (obj.LedgerEntryType !== undefined) {
object_name = SerializedObject.lookup_type_le(obj.LedgerEntryType);
} else { } else {
object_name = 'TransactionMetaData'; fieldsMap[fieldName] = true;
} }
});
throw new Error(object_name + ' is missing fields: ' + // Get fields that are not specified in format
JSON.stringify(missing_fields)); Object.keys(obj).forEach(function(key) {
if (!fieldsMap[key] && /^[A-Z]/.test(key)) {
unknownFields.push(key);
}
});
if (!(missingFields.length || unknownFields.length)) {
// No missing or unknown fields
return;
} }
var errorMessage;
if (obj.TransactionType !== undefined) {
errorMessage = SerializedObject.lookup_type_tx(obj.TransactionType);
} else if (obj.LedgerEntryType !== undefined) {
errorMessage = SerializedObject.lookup_type_le(obj.LedgerEntryType);
} else {
errorMessage = 'TransactionMetaData';
}
if (missingFields.length > 0) {
errorMessage += ' is missing fields: ' + JSON.stringify(missingFields);
}
if (unknownFields.length > 0) {
errorMessage += (missingFields.length ? ' and' : '')
+ ' has unknown fields: ' + JSON.stringify(unknownFields);
}
throw new Error(errorMessage);
}; };
SerializedObject.prototype.append = function(bytes) { SerializedObject.prototype.append = function(bytes) {

View File

@@ -12,6 +12,7 @@
/* eslint-disable quotes*/ /* eslint-disable quotes*/
var assert = require('assert'); var assert = require('assert');
var lodash = require('lodash');
var SerializedObject = require('ripple-lib').SerializedObject; var SerializedObject = require('ripple-lib').SerializedObject;
var Amount = require('ripple-lib').Amount; var Amount = require('ripple-lib').Amount;
var sjcl = require('ripple-lib').sjcl; var sjcl = require('ripple-lib').sjcl;
@@ -150,6 +151,52 @@ describe('Serialized object', function() {
assert.equal("DirectoryNode", output_json.LedgerEntryType); assert.equal("DirectoryNode", output_json.LedgerEntryType);
}); });
it('checks for missing required fields', function() {
var input_json = {
TransactionType: 'Payment',
// no non required fields
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: '274579388',
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee: '15',
Sequence: 351,
SigningPubKey: '02'
};
Object.keys(input_json).slice(1).forEach(function(k) {
var bad_json = lodash.merge({}, input_json);
delete bad_json[k];
assert.strictEqual(bad_json[k], undefined);
assert.throws(function() {
SerializedObject.from_json(bad_json);
}, new RegExp('Payment is missing fields: \\["' + k + '"\\]'));
});
});
it('checks for unknown fields', function() {
var input_json = {
TransactionType: 'Payment',
// no non required fields
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: '274579388',
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee: '15',
Sequence: 351,
SigningPubKey: '02'
};
Object.keys(input_json).slice(1).forEach(function(k) {
var bad_json = lodash.merge({}, input_json);
bad_json[k + 'z'] = bad_json[k];
assert.throws(function() {
SerializedObject.from_json(bad_json);
}, new RegExp('Payment has unknown fields: \\["' + k + 'z"\\]'));
});
});
describe('Format validation', function() { describe('Format validation', function() {
// Peercover actually had a problem submitting transactions without a `Fee` // Peercover actually had a problem submitting transactions without a `Fee`
// and rippled was only informing of "transaction is invalid" // and rippled was only informing of "transaction is invalid"