UInt160 can be an account or a plain hash.

The UInt160 class used to be hardcoded to be an Account. This commit changes it
so it can be used as an account or a plain hash. It will try to automatically
self-classify based on how it is initialized.

In the future we may want to have some dedicated classes rather than a
single configurable UInt160.
This commit is contained in:
Stefan Thomas
2013-11-12 15:51:50 -08:00
parent ae68e3a1a6
commit 05f3a97042
5 changed files with 135 additions and 20 deletions

View File

@@ -15,6 +15,7 @@ var sjcl = utils.sjcl;
var UInt128 = require('./uint128').UInt128;
var UInt160 = require('./uint160').UInt160;
var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
var amount = require('./amount');
var Amount = amount.Amount;
@@ -381,6 +382,7 @@ var STAmount = exports.Amount = new SerializedType({
var currency = STCurrency.parse(so);
var issuer_bytes = so.read(20);
var issuer = UInt160.from_bytes(issuer_bytes);
issuer.set_version(Base.VER_ACCOUNT_ID);
var offset = ((value_bytes[0] & 0x3f) << 2) + (value_bytes[1] >>> 6) - 97;
var mantissa_bytes = value_bytes.slice(1);
mantissa_bytes[0] &= 0x3f;
@@ -441,6 +443,7 @@ var STAccount = exports.Account = new SerializedType({
}
var result = UInt160.from_bytes(so.read(len));
result.set_version(Base.VER_ACCOUNT_ID);
//console.log('PARSED 160:', result.to_json());
if (false && !result.is_valid()) {
@@ -529,6 +532,7 @@ var STPathSet = exports.PathSet = new SerializedType({
/*var bta = so.read(20);
console.log('BTA:', bta);*/
entry.account = STHash160.parse(so);
entry.account.set_version(Base.VER_ACCOUNT_ID);
}
if (tag_byte & this.typeCurrency) {
//console.log('entry.currency');
@@ -541,6 +545,7 @@ var STPathSet = exports.PathSet = new SerializedType({
if (tag_byte & this.typeIssuer) {
//console.log('entry.issuer');
entry.issuer = STHash160.parse(so); //should know to use Base58?
entry.issuer.set_version(Base.VER_ACCOUNT_ID);
//console.log('DONE WITH ISSUER!');
}

View File

@@ -129,9 +129,6 @@ UInt.prototype.parse_generic = function (j) {
if ('string' !== typeof j) {
this._value = NaN;
}
else if (j[0] === "r") {
this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j);
}
else if (this.constructor.width === j.length) {
this._value = new BigInteger(utils.stringToArray(j), 256);
}

View File

@@ -15,6 +15,7 @@ var Base = require('./base').Base;
var UInt160 = extend(function () {
// Internal form: NaN or BigInteger
this._value = NaN;
this._version_byte = void(0);
}, UInt);
UInt160.width = 20;
@@ -28,6 +29,16 @@ var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000
var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);
var STR_ONE = UInt160.STR_ONE = utils.hexToString(HEX_ONE);
UInt160.prototype.set_version = function (j) {
this._version_byte = j;
return this;
};
UInt160.prototype.get_version = function () {
return this._version_byte;
};
// value = NaN on error.
UInt160.prototype.parse_json = function (j) {
// Canonicalize and validate
@@ -36,13 +47,30 @@ UInt160.prototype.parse_json = function (j) {
}
if (typeof j === 'number' && !isNaN(j)) {
// Allow raw numbers - DEPRECATED
// This is used mostly by the test suite and is supported
// as a legacy feature only. DO NOT RELY ON THIS BEHAVIOR.
this._value = new BigInteger(String(j));
this._version_byte = Base.VER_ACCOUNT_ID;
} else if (typeof j !== 'string') {
this._value = NaN;
} else if (j[0] === 'r') {
this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j);
this._version_byte = Base.VER_ACCOUNT_ID;
} else {
this._value = NaN;
this.parse_hex(j);
}
return this;
};
UInt160.prototype.parse_generic = function (j) {
UInt.prototype.parse_generic.call(this, j);
if (isNaN(this._value)) {
if ("string" === typeof j && j[0] === 'r') {
this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j);
}
}
return this;
@@ -50,17 +78,22 @@ UInt160.prototype.parse_json = function (j) {
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
UInt160.prototype.to_json = function (opts) {
var opts = opts || {};
var output = NaN;
opts = opts || {};
if (this._value instanceof BigInteger) {
output = Base.encode_check(Base.VER_ACCOUNT_ID, this.to_bytes());
// If this value has a type, return a Base58 encoded string.
if ("number" === typeof this._version_byte) {
var output = Base.encode_check(this._version_byte, this.to_bytes());
if (opts.gateways && output in opts.gateways) {
output = opts.gateways[output];
}
}
return output;
} else {
return this.to_hex();
}
}
return NaN;
};
exports.UInt160 = UInt160;

View File

@@ -36,7 +36,7 @@ describe('Amount', function() {
assert.deepEqual(new BigInteger(), UInt160.from_generic('0')._value);
});
it('Parse 0 export', function () {
assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_generic('0').to_json());
assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_generic('0').set_version(0).to_json());
});
it('Parse 1', function () {
assert.deepEqual(new BigInteger([1]), UInt160.from_generic('1')._value);

View File

@@ -378,19 +378,37 @@ describe('Serialized types', function() {
describe('Hash160', function() {
it('Serialize 0', function () {
var hex = '0000000000000000000000000000000000000000';
var base58 = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
var so = new SerializedObject();
types.Hash160.serialize(so, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
assert.strictEqual(so.to_hex(), '0000000000000000000000000000000000000000');
types.Hash160.serialize(so, base58);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject();
types.Hash160.serialize(so, hex);
assert.strictEqual(so.to_hex(), hex);
});
it('Serialize 1', function () {
var hex = '0000000000000000000000000000000000000001';
var base58 = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
var so = new SerializedObject();
types.Hash160.serialize(so, 'rrrrrrrrrrrrrrrrrrrrBZbvji');
assert.strictEqual(so.to_hex(), '0000000000000000000000000000000000000001');
types.Hash160.serialize(so, base58);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject();
types.Hash160.serialize(so, hex);
assert.strictEqual(so.to_hex(), hex);
});
it('Serialize FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', function () {
var hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
var base58 = 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi';
var so = new SerializedObject();
types.Hash160.serialize(so, 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi');
assert.strictEqual(so.to_hex(), 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
types.Hash160.serialize(so, base58);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject();
types.Hash160.serialize(so, hex);
assert.strictEqual(so.to_hex(), hex);
});
it('Parse 0', function () {
var val = '0000000000000000000000000000000000000000';
@@ -410,6 +428,14 @@ describe('Serialized types', function() {
var num = types.Hash160.parse(so);
assert.strictEqual(num.to_hex(), val);
});
it('Parse 0 as JSON', function () {
// Hash160 should be returned as hex in JSON, unlike
// addresses.
var val = '0000000000000000000000000000000000000000';
var so = new SerializedObject(val);
var num = types.Hash160.parse(so);
assert.strictEqual(num.to_json(), val);
});
});
describe('Hash256', function() {
@@ -539,6 +565,60 @@ describe('Serialized types', function() {
});
});
describe('Account', function() {
it('Serialize 0', function () {
var hex = '0000000000000000000000000000000000000000';
var base58 = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
var so = new SerializedObject();
types.Account.serialize(so, base58);
assert.strictEqual(so.to_hex(), "14"+hex);
so = new SerializedObject();
types.Account.serialize(so, hex);
assert.strictEqual(so.to_hex(), "14"+hex);
});
it('Serialize 1', function () {
var hex = '0000000000000000000000000000000000000001';
var base58 = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
var so = new SerializedObject();
types.Account.serialize(so, base58);
assert.strictEqual(so.to_hex(), "14"+hex);
so = new SerializedObject();
types.Account.serialize(so, hex);
assert.strictEqual(so.to_hex(), "14"+hex);
});
it('Serialize FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', function () {
var hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
var base58 = 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi';
var so = new SerializedObject();
types.Account.serialize(so, base58);
assert.strictEqual(so.to_hex(), "14"+hex);
so = new SerializedObject();
types.Account.serialize(so, hex);
assert.strictEqual(so.to_hex(), "14"+hex);
});
it('Parse 0', function () {
var val = '140000000000000000000000000000000000000000';
var so = new SerializedObject(val);
var num = types.Account.parse(so);
assert.strictEqual(num.to_json(), 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
});
it('Parse 1', function () {
var val = '140000000000000000000000000000000000000001';
var so = new SerializedObject(val);
var num = types.Account.parse(so);
assert.strictEqual(num.to_json(), 'rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Parse HASH160_MAX', function () {
var val = '14FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
var so = new SerializedObject(val);
var num = types.Account.parse(so);
assert.strictEqual(num.to_json(), 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi');
});
});
describe('PathSet', function() {
it('Serialize single empty path [[]]', function () {
var so = new SerializedObject();