uport-lite
Advanced tools
Comparing version 0.2.4 to 1.0.0-alpha
@@ -76,3 +76,3 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 3); | ||
/******/ return __webpack_require__(__webpack_require__.s = 4); | ||
/******/ }) | ||
@@ -176,3 +176,3 @@ /************************************************************************/ | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3))) | ||
@@ -183,4 +183,28 @@ /***/ }), | ||
/* (ignored) */ | ||
module.exports = function(originalModule) { | ||
if(!originalModule.webpackPolyfill) { | ||
var module = Object.create(originalModule); | ||
// module.parent = undefined by default | ||
if(!module.children) module.children = []; | ||
Object.defineProperty(module, "loaded", { | ||
enumerable: true, | ||
get: function() { | ||
return module.l; | ||
} | ||
}); | ||
Object.defineProperty(module, "id", { | ||
enumerable: true, | ||
get: function() { | ||
return module.i; | ||
} | ||
}); | ||
Object.defineProperty(module, "exports", { | ||
enumerable: true, | ||
}); | ||
module.webpackPolyfill = 1; | ||
} | ||
return module; | ||
}; | ||
/***/ }), | ||
@@ -190,2 +214,8 @@ /* 2 */ | ||
/* (ignored) */ | ||
/***/ }), | ||
/* 3 */ | ||
/***/ (function(module, exports) { | ||
var g; | ||
@@ -215,5 +245,8 @@ | ||
/***/ }), | ||
/* 3 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
/* 4 */ | ||
/***/ (function(module, __webpack_exports__, __webpack_require__) { | ||
"use strict"; | ||
/* WEBPACK VAR INJECTION */(function(module) {Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); | ||
/* harmony export (immutable) */ __webpack_exports__["eaeDecode"] = eaeDecode; | ||
var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
@@ -223,5 +256,9 @@ var base58 = __webpack_require__(0)(BASE58) | ||
const XMLHttpRequest = (typeof window !== 'undefined') ? window.XMLHttpRequest : __webpack_require__(1).XMLHttpRequest | ||
const XMLHttpRequest = (typeof window !== 'undefined') ? window.XMLHttpRequest : __webpack_require__(2).XMLHttpRequest | ||
const functionSignature = '0x447885f0' | ||
// Legacy | ||
const getAttributesData = '0x446d5aa4000000000000000000000000' | ||
function http (opts, callback) { | ||
@@ -262,2 +299,37 @@ const request = new XMLHttpRequest() // eslint-disable-line | ||
function registryEncodingToIPFS (hexStr) { | ||
return base58.encode(hex.decode('1220' + hexStr.slice(2))) | ||
} | ||
// to avoid adding further dependencies we are not verifying checksum | ||
function eaeDecode (encoded) { | ||
const data = base58.decode(encoded) | ||
const netLength = data.length - 24 | ||
const network = data.slice(1, netLength) | ||
const address = data.slice(netLength, 20 + netLength) | ||
return { | ||
network: `0x${hex.encode(network)}`, | ||
address: `0x${hex.encode(address)}` | ||
} | ||
} | ||
const defaultNetworks = { | ||
'0x1': { | ||
registry: '0xab5c8051b9a1df1aab0149f8b0630848b7ecabf6', | ||
rpcUrl: 'https://mainnet.infura.io' | ||
}, | ||
'0x3': { | ||
registry: '0x41566e3a081f5032bdcad470adb797635ddfe1f0', | ||
rpcUrl: 'https://ropsten.infura.io' | ||
}, | ||
'0x2a': { | ||
registry: '0xeA8aC9cF5c3D02cfc30F65C04eD921D713062745', | ||
rpcUrl: 'https://kovan.infura.io' | ||
// }, | ||
// '0x16B2': { | ||
// registry: '', | ||
// rpcUrl: 'https://infuranet.infura.io' | ||
} | ||
} | ||
function toBase58 (hexStr) { | ||
@@ -268,7 +340,33 @@ return base58.encode(hex.decode(hexStr)) | ||
function UportLite (opts = {}) { | ||
const registryAddress = opts.registryAddress || '0xb9C1598e24650437a3055F7f66AC1820c419a679' | ||
const infuraKey = opts.infuraKey || 'uport-lite-library' | ||
const ipfsGw = opts.ipfsGw || 'https://ipfs.infura.io/ipfs/' | ||
const rpcUrl = opts.rpcUrl || 'https://ropsten.infura.io/uport-lite-library' | ||
const networks = opts.networks ? Object.assign({}, defaultNetworks, opts.networks) : defaultNetworks | ||
function asciiToHex (string, delim) { | ||
return string.split('').map(function (c) { | ||
return ('0' + c.charCodeAt(0).toString(16)).slice(-2) | ||
}).join(delim || '') | ||
} | ||
function callRegistry (address, callback) { | ||
function pad (pad, str, padLeft) { | ||
if (typeof str === 'undefined') { | ||
return pad | ||
} | ||
if (padLeft) { | ||
return (pad + str).slice(-pad.length) | ||
} else { | ||
return (str + pad).substring(0, pad.length) | ||
} | ||
} | ||
function encodeFunctionCall (functionSignature, registrationIdentifier, issuer, subject) { | ||
var callString = functionSignature | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', asciiToHex(registrationIdentifier)) | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', issuer.slice(2), true) | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', subject.slice(2), true) | ||
return callString | ||
} | ||
// TODO remove once feasible | ||
function callLegacyRegistry (address, callback) { | ||
const rpcUrl = `https://ropsten.infura.io/${infuraKey}` | ||
if (!address) return callback(null) | ||
@@ -281,3 +379,3 @@ return http({ | ||
params: [ | ||
{to: registryAddress, data: (getAttributesData + address.slice(2))}, | ||
{to: '0xb9C1598e24650437a3055F7f66AC1820c419a679', data: (getAttributesData + address.slice(2))}, | ||
'latest' | ||
@@ -295,2 +393,33 @@ ], | ||
function callRegistry (registrationIdentifier, issuerId, subjectId, callback) { | ||
const issuer = eaeDecode(issuerId) | ||
const subject = eaeDecode(subjectId) | ||
if (issuer.network !== subject.network) { | ||
throw new Error('Issuer and subject must be on the same network') | ||
} | ||
if (!networks[issuer.network]) { | ||
throw new Error(`Network id ${issuer.network} is not configured`) | ||
} | ||
const rpcUrl = networks[issuer.network].rpcUrl | ||
const registryAddress = networks[issuer.network].registry | ||
const callString = encodeFunctionCall(functionSignature, registrationIdentifier, issuer.address, subject.address) | ||
return http({ | ||
uri: `${rpcUrl}/${infuraKey}`, | ||
accept: 'application/json', | ||
data: { | ||
method: 'eth_call', | ||
params: [ | ||
{to: registryAddress, data: (callString)}, | ||
'latest' | ||
], | ||
id: 1, | ||
jsonrpc: '2.0' | ||
} | ||
}, (error, response) => { | ||
if (error) return callback(error) | ||
if (response.result == 0) return callback(error) | ||
return callback(null, registryEncodingToIPFS(response.result)) | ||
}) | ||
} | ||
function fetchIpfs (ipfsHash, callback) { | ||
@@ -301,9 +430,19 @@ if (!ipfsHash || ipfsHash === '0x') return callback() | ||
function getAttributes (address, callback) { | ||
return callRegistry(address, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
function get (issuer, callback, subjectAddress, registrationIdentifier = 'uPortProfileIPFS1220') { | ||
if (!issuer) return callback(null) | ||
const subject = subjectAddress || issuer | ||
if (issuer.match(/0x[0-9a-fA-F]{40}/)) { | ||
return callLegacyRegistry(issuer, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
} else { | ||
return callRegistry(registrationIdentifier, issuer, subject, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
} | ||
} | ||
return getAttributes | ||
return get | ||
} | ||
@@ -313,2 +452,3 @@ | ||
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(1)(module))) | ||
@@ -315,0 +455,0 @@ /***/ }) |
@@ -1,7 +0,13 @@ | ||
(function(d,a){"object"===typeof exports&&"object"===typeof module?module.exports=a():"function"===typeof define&&define.amd?define("UportLite",[],a):"object"===typeof exports?exports.UportLite=a():d.UportLite=a()})(this,function(){return function(d){function a(c){if(e[c])return e[c].exports;var b=e[c]={i:c,l:!1,exports:{}};d[c].call(b.exports,b,b.exports,a);b.l=!0;return b.exports}var e={};a.m=d;a.c=e;a.i=function(a){return a};a.d=function(c,b,d){a.o(c,b)||Object.defineProperty(c,b,{configurable:!1, | ||
enumerable:!0,get:d})};a.n=function(c){var b=c&&c.__esModule?function(){return c["default"]}:function(){return c};a.d(b,"a",b);return b};a.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};a.p="";return a(a.s=3)}([function(d,a,e){(function(a){var b=a&&a.Buffer?a.Buffer:Uint8Array;d.exports=function(a){function c(a){if(0===a.length)return new b(0);for(var h=[0],g=0;g<a.length;g++){var f=l[a[g]];if(void 0===f)return;for(var c=0;c<h.length;++c)f+=h[c]*d,h[c]=f&255,f>>=8;for(;0<f;)h.push(f& | ||
255),f>>=8}for(g=0;a[g]===p&&g<a.length-1;++g)h.push(0);return b.from(h.reverse())}for(var l={},d=a.length,p=a.charAt(0),k=0;k<a.length;k++){var e=a.charAt(k);if(void 0!==l[e])throw new TypeError(e+" is ambiguous");l[e]=k}return{encode:function(c){if(0===c.length)return"";for(var h=[0],g=0;g<c.length;++g){for(var f=0,b=c[g];f<h.length;++f)b+=h[f]<<8,h[f]=b%d,b=b/d|0;for(;0<b;)h.push(b%d),b=b/d|0}g="";for(f=0;0===c[f]&&f<c.length-1;++f)g+=a[0];for(c=h.length-1;0<=c;--c)g+=a[h[c]];return g},decodeUnsafe:c, | ||
decode:function(a){if(a=c(a))return a;throw Error("Non-base"+d+" character");}}}}).call(a,e(2))},function(d,a){},function(d,a){a=function(){return this}();try{a=a||Function("return this")()||(0,eval)("this")}catch(e){"object"===typeof window&&(a=window)}d.exports=a},function(d,a,e){function c(a,c){var b=new n,d=a||{};b.onreadystatechange=function(){if(4===b.readyState&&1!==b.timeout)if(200!==b.status)c(Error("[uport-lite] status "+b.status+": "+b.responseText));else try{c(null,JSON.parse(b.responseText))}catch(q){c(Error("[uport-lite] while parsing data: '"+ | ||
String(b.responseText)+"', error: "+String(q)))}};d.data?b.open("POST",a.uri):b.open("GET",a.uri);d.accept&&b.setRequestHeader("accept",d.accept);d.data?(b.setRequestHeader("Content-Type","application/json"),b.send(JSON.stringify(d.data))):b.send()}var b=e(0)("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"),m=e(0)("0123456789abcdef"),n="undefined"!==typeof window?window.XMLHttpRequest:e(1).XMLHttpRequest;d.exports=function(a){function d(a,d){return a?c({uri:l,accept:"application/json", | ||
data:{method:"eth_call",params:[{to:e,data:"0x446d5aa4000000000000000000000000"+a.slice(2)},"latest"],id:1,jsonrpc:"2.0"}},function(a,c){if(a)return d(a);a=c.result.slice(130).slice(0,68);return d(null,b.encode(m.decode(a)))}):d(null)}a=void 0===a?{}:a;var e=a.registryAddress||"0xb9C1598e24650437a3055F7f66AC1820c419a679",k=a.ipfsGw||"https://ipfs.infura.io/ipfs/",l=a.rpcUrl||"https://ropsten.infura.io/uport-lite-library";return function(a,b){return d(a,function(a,d){if(a)return b(a);d&&"0x"!==d?c({uri:""+ | ||
k+d},b):b()})}}}])}); | ||
var $jscomp={scope:{},owns:function(a,b){return Object.prototype.hasOwnProperty.call(a,b)}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(c.get||c.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this); | ||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var f=a[d];f in c||(c[f]={});c=c[f]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Object.assign",function(a){return a?a:function(b,a){for(var d=1;d<arguments.length;d++){var f=arguments[d];if(f)for(var c in f)$jscomp.owns(f,c)&&(b[c]=f[c])}return b}},"es6-impl","es3"); | ||
(function(a,b){"object"===typeof exports&&"object"===typeof module?module.exports=b():"function"===typeof define&&define.amd?define("UportLite",[],b):"object"===typeof exports?exports.UportLite=b():a.UportLite=b()})(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var f=c[d]={i:d,l:!1,exports:{}};a[d].call(f.exports,f,f.exports,b);f.l=!0;return f.exports}var c={};b.m=a;b.c=c;b.i=function(b){return b};b.d=function(d,a,c){b.o(d,a)||Object.defineProperty(d,a,{configurable:!1, | ||
enumerable:!0,get:c})};b.n=function(d){var a=d&&d.__esModule?function(){return d["default"]}:function(){return d};b.d(a,"a",a);return a};b.o=function(b,a){return Object.prototype.hasOwnProperty.call(b,a)};b.p="";return b(b.s=4)}([function(a,b,c){(function(b){var d=b&&b.Buffer?b.Buffer:Uint8Array;a.exports=function(b){function a(b){if(0===b.length)return new d(0);for(var a=[0],g=0;g<b.length;g++){var e=c[b[g]];if(void 0===e)return;for(var h=0;h<a.length;++h)e+=a[h]*f,a[h]=e&255,e>>=8;for(;0<e;)a.push(e& | ||
255),e>>=8}for(g=0;b[g]===l&&g<b.length-1;++g)a.push(0);return d.from(a.reverse())}for(var c={},f=b.length,l=b.charAt(0),e=0;e<b.length;e++){var q=b.charAt(e);if(void 0!==c[q])throw new TypeError(q+" is ambiguous");c[q]=e}return{encode:function(a){if(0===a.length)return"";for(var d=[0],c=0;c<a.length;++c){for(var g=0,e=a[c];g<d.length;++g)e+=d[g]<<8,d[g]=e%f,e=e/f|0;for(;0<e;)d.push(e%f),e=e/f|0}c="";for(g=0;0===a[g]&&g<a.length-1;++g)c+=b[0];for(a=d.length-1;0<=a;--a)c+=b[d[a]];return c},decodeUnsafe:a, | ||
decode:function(b){if(b=a(b))return b;throw Error("Non-base"+f+" character");}}}}).call(b,c(3))},function(a,b){a.exports=function(b){if(!b.webpackPolyfill){var a=Object.create(b);a.children||(a.children=[]);Object.defineProperty(a,"loaded",{enumerable:!0,get:function(){return a.l}});Object.defineProperty(a,"id",{enumerable:!0,get:function(){return a.i}});Object.defineProperty(a,"exports",{enumerable:!0});a.webpackPolyfill=1}return a}},function(a,b){},function(a,b){b=function(){return this}();try{b= | ||
b||Function("return this")()||(0,eval)("this")}catch(c){"object"===typeof window&&(b=window)}a.exports=b},function(a,b,c){(function(a){function d(a,b){var c=new p,d=a||{};c.onreadystatechange=function(){if(4===c.readyState&&1!==c.timeout)if(200!==c.status)b(Error("[uport-lite] status "+c.status+": "+c.responseText));else try{b(null,JSON.parse(c.responseText))}catch(u){b(Error("[uport-lite] while parsing data: '"+String(c.responseText)+"', error: "+String(u)))}};d.data?c.open("POST",a.uri):c.open("GET", | ||
a.uri);d.accept&&c.setRequestHeader("accept",d.accept);d.data?(c.setRequestHeader("Content-Type","application/json"),c.send(JSON.stringify(d.data))):c.send()}function m(a){var b=n.decode(a),c=b.length-24;a=b.slice(1,c);b=b.slice(c,20+c);return{network:"0x"+k.encode(a),address:"0x"+k.encode(b)}}Object.defineProperty(b,"__esModule",{value:!0});b.eaeDecode=m;var n=c(0)("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"),k=c(0)("0123456789abcdef"),p="undefined"!==typeof window?window.XMLHttpRequest: | ||
c(2).XMLHttpRequest,l={"0x1":{registry:"0xab5c8051b9a1df1aab0149f8b0630848b7ecabf6",rpcUrl:"https://mainnet.infura.io"},"0x3":{registry:"0x41566e3a081f5032bdcad470adb797635ddfe1f0",rpcUrl:"https://ropsten.infura.io"},"0x2a":{registry:"0xeA8aC9cF5c3D02cfc30F65C04eD921D713062745",rpcUrl:"https://kovan.infura.io"}};a.exports=function(a){function b(a,b){return a.split("").map(function(a){return("0"+a.charCodeAt(0).toString(16)).slice(-2)}).join(b||"")}function c(a,b,c){return"undefined"===typeof b?a: | ||
c?(a+b).slice(-a.length):(b+a).substring(0,a.length)}function f(a,d,f,e){a+=c("0000000000000000000000000000000000000000000000000000000000000000",b(d));a+=c("0000000000000000000000000000000000000000000000000000000000000000",f.slice(2),!0);return a+=c("0000000000000000000000000000000000000000000000000000000000000000",e.slice(2),!0)}function e(a,b){var c="https://ropsten.infura.io/"+t;return a?d({uri:c,accept:"application/json",data:{method:"eth_call",params:[{to:"0xb9C1598e24650437a3055F7f66AC1820c419a679", | ||
data:"0x446d5aa4000000000000000000000000"+a.slice(2)},"latest"],id:1,jsonrpc:"2.0"}},function(a,c){if(a)return b(a);a=c.result.slice(130).slice(0,68);return b(null,n.encode(k.decode(a)))}):b(null)}function p(a,b,c,e){b=m(b);var g=m(c);if(b.network!==g.network)throw Error("Issuer and subject must be on the same network");if(!r[b.network])throw Error("Network id "+b.network+" is not configured");c=r[b.network].rpcUrl;var v=r[b.network].registry;a=f("0x447885f0",a,b.address,g.address);return d({uri:c+ | ||
"/"+t,accept:"application/json",data:{method:"eth_call",params:[{to:v,data:a},"latest"],id:1,jsonrpc:"2.0"}},function(a,b){return a||0==b.result?e(a):e(null,n.encode(k.decode("1220"+b.result.slice(2))))})}function h(a,b){return a&&"0x"!==a?d({uri:""+w+a},b):b()}a=void 0===a?{}:a;var t=a.infuraKey||"uport-lite-library",w=a.ipfsGw||"https://ipfs.infura.io/ipfs/",r=a.networks?Object.assign({},l,a.networks):l;return function(a,b,c,d){d=void 0===d?"uPortProfileIPFS1220":d;if(!a)return b(null);c=c||a;return a.match(/0x[0-9a-fA-F]{40}/)? | ||
e(a,function(a,c){if(a)return b(a);h(c,b)}):p(d,a,c,function(a,c){if(a)return b(a);h(c,b)})}}}).call(b,c(1)(a))}])}); |
@@ -9,7 +9,17 @@ 'use strict'; | ||
var registry = (0, _index2.default)(); | ||
var registry = (0, _index2.default)({ | ||
networks: { | ||
'0x94365e3b': { | ||
registry: '0x41566e3a081f5032bdcad470adb797635ddfe1f0', | ||
rpcUrl: 'https://ropsten.infura.io' | ||
} | ||
} | ||
}); | ||
it('finds valid profile', function () { | ||
// uPortProfileIPFS1220 => 75506f727450726f66696c654950465331323230 | ||
//"uPortProfileIPFS1220","0xb08e78b8E17dC2874818d7F49055aBf08Ee9977D","0xB0F288F8EFA511962E11E37488DB0D2BCC7A5F304B1D4F3977EB0EC65814A52C" | ||
it('finds valid default profile for address', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', function (error, profile) { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', function (error, profile) { | ||
if (error) return reject(error); | ||
@@ -23,5 +33,27 @@ resolve(profile); | ||
it('finds valid default profile on private chain', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('5A8bRX9ShMLm7bXPn9SVX6hC7SG1HURF3tknToB', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}); | ||
}).then(function (profile) { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40'); | ||
}); | ||
}); | ||
it('finds valid advanced profile using new API', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}, '2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', 'uPortProfileIPFS1220'); | ||
}).then(function (profile) { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40'); | ||
}); | ||
}); | ||
it('returns null if it profile doesnt exist', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394d', function (error, profile) { | ||
registry('2oJvyeFwEbtLsuJ7G7bcUFPocuEU8AdeHKs', function (error, profile) { | ||
if (error) return reject(error); | ||
@@ -33,2 +65,48 @@ resolve(profile); | ||
}); | ||
}); | ||
it('returns error for unsupported network', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('5A8bRWU3F7j3REx3vkJWxdjQPp4tqmxFPmab1Tr', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}); | ||
}).catch(function (error) { | ||
return expect(error.message).toBe('Network id 0x94365e3a is not configured'); | ||
}); | ||
}); | ||
it('returns error if issuer and subject are on different networks', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}, '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX', 'uPortProfileIPFS1220'); | ||
}).catch(function (error) { | ||
return expect(error.message).toBe('Issuer and subject must be on the same network'); | ||
}); | ||
}); | ||
describe('legacy registry', function () { | ||
it('finds valid profile', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}); | ||
}).then(function (profile) { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40'); | ||
}); | ||
}); | ||
it('returns null if it profile doesnt exist', function () { | ||
return new Promise(function (resolve, reject) { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394d', function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}); | ||
}).then(function (profile) { | ||
return expect(profile).toBeUndefined(); | ||
}); | ||
}); | ||
}); |
129
lib/index.js
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.eaeDecode = eaeDecode; | ||
var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; | ||
@@ -9,3 +13,7 @@ var base58 = require('base-x-bytearray')(BASE58); | ||
var functionSignature = '0x447885f0'; | ||
// Legacy | ||
var getAttributesData = '0x446d5aa4000000000000000000000000'; | ||
function http(opts, callback) { | ||
@@ -46,2 +54,37 @@ var request = new XMLHttpRequest(); // eslint-disable-line | ||
function registryEncodingToIPFS(hexStr) { | ||
return base58.encode(hex.decode('1220' + hexStr.slice(2))); | ||
} | ||
// to avoid adding further dependencies we are not verifying checksum | ||
function eaeDecode(encoded) { | ||
var data = base58.decode(encoded); | ||
var netLength = data.length - 24; | ||
var network = data.slice(1, netLength); | ||
var address = data.slice(netLength, 20 + netLength); | ||
return { | ||
network: '0x' + hex.encode(network), | ||
address: '0x' + hex.encode(address) | ||
}; | ||
} | ||
var defaultNetworks = { | ||
'0x1': { | ||
registry: '0xab5c8051b9a1df1aab0149f8b0630848b7ecabf6', | ||
rpcUrl: 'https://mainnet.infura.io' | ||
}, | ||
'0x3': { | ||
registry: '0x41566e3a081f5032bdcad470adb797635ddfe1f0', | ||
rpcUrl: 'https://ropsten.infura.io' | ||
}, | ||
'0x2a': { | ||
registry: '0xeA8aC9cF5c3D02cfc30F65C04eD921D713062745', | ||
rpcUrl: 'https://kovan.infura.io' | ||
// }, | ||
// '0x16B2': { | ||
// registry: '', | ||
// rpcUrl: 'https://infuranet.infura.io' | ||
} | ||
}; | ||
function toBase58(hexStr) { | ||
@@ -54,7 +97,33 @@ return base58.encode(hex.decode(hexStr)); | ||
var registryAddress = opts.registryAddress || '0xb9C1598e24650437a3055F7f66AC1820c419a679'; | ||
var infuraKey = opts.infuraKey || 'uport-lite-library'; | ||
var ipfsGw = opts.ipfsGw || 'https://ipfs.infura.io/ipfs/'; | ||
var rpcUrl = opts.rpcUrl || 'https://ropsten.infura.io/uport-lite-library'; | ||
var networks = opts.networks ? Object.assign({}, defaultNetworks, opts.networks) : defaultNetworks; | ||
function asciiToHex(string, delim) { | ||
return string.split('').map(function (c) { | ||
return ('0' + c.charCodeAt(0).toString(16)).slice(-2); | ||
}).join(delim || ''); | ||
} | ||
function callRegistry(address, callback) { | ||
function pad(pad, str, padLeft) { | ||
if (typeof str === 'undefined') { | ||
return pad; | ||
} | ||
if (padLeft) { | ||
return (pad + str).slice(-pad.length); | ||
} else { | ||
return (str + pad).substring(0, pad.length); | ||
} | ||
} | ||
function encodeFunctionCall(functionSignature, registrationIdentifier, issuer, subject) { | ||
var callString = functionSignature; | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', asciiToHex(registrationIdentifier)); | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', issuer.slice(2), true); | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', subject.slice(2), true); | ||
return callString; | ||
} | ||
// TODO remove once feasible | ||
function callLegacyRegistry(address, callback) { | ||
var rpcUrl = 'https://ropsten.infura.io/' + infuraKey; | ||
if (!address) return callback(null); | ||
@@ -66,3 +135,3 @@ return http({ | ||
method: 'eth_call', | ||
params: [{ to: registryAddress, data: getAttributesData + address.slice(2) }, 'latest'], | ||
params: [{ to: '0xb9C1598e24650437a3055F7f66AC1820c419a679', data: getAttributesData + address.slice(2) }, 'latest'], | ||
id: 1, | ||
@@ -78,2 +147,30 @@ jsonrpc: '2.0' | ||
function callRegistry(registrationIdentifier, issuerId, subjectId, callback) { | ||
var issuer = eaeDecode(issuerId); | ||
var subject = eaeDecode(subjectId); | ||
if (issuer.network !== subject.network) { | ||
throw new Error('Issuer and subject must be on the same network'); | ||
} | ||
if (!networks[issuer.network]) { | ||
throw new Error('Network id ' + issuer.network + ' is not configured'); | ||
} | ||
var rpcUrl = networks[issuer.network].rpcUrl; | ||
var registryAddress = networks[issuer.network].registry; | ||
var callString = encodeFunctionCall(functionSignature, registrationIdentifier, issuer.address, subject.address); | ||
return http({ | ||
uri: rpcUrl + '/' + infuraKey, | ||
accept: 'application/json', | ||
data: { | ||
method: 'eth_call', | ||
params: [{ to: registryAddress, data: callString }, 'latest'], | ||
id: 1, | ||
jsonrpc: '2.0' | ||
} | ||
}, function (error, response) { | ||
if (error) return callback(error); | ||
if (response.result == 0) return callback(error); | ||
return callback(null, registryEncodingToIPFS(response.result)); | ||
}); | ||
} | ||
function fetchIpfs(ipfsHash, callback) { | ||
@@ -84,11 +181,23 @@ if (!ipfsHash || ipfsHash === '0x') return callback(); | ||
function getAttributes(address, callback) { | ||
return callRegistry(address, function (error, ipfsHash) { | ||
if (error) return callback(error); | ||
fetchIpfs(ipfsHash, callback); | ||
}); | ||
function get(issuer, callback, subjectAddress) { | ||
var registrationIdentifier = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'uPortProfileIPFS1220'; | ||
if (!issuer) return callback(null); | ||
var subject = subjectAddress || issuer; | ||
if (issuer.match(/0x[0-9a-fA-F]{40}/)) { | ||
return callLegacyRegistry(issuer, function (error, ipfsHash) { | ||
if (error) return callback(error); | ||
fetchIpfs(ipfsHash, callback); | ||
}); | ||
} else { | ||
return callRegistry(registrationIdentifier, issuer, subject, function (error, ipfsHash) { | ||
if (error) return callback(error); | ||
fetchIpfs(ipfsHash, callback); | ||
}); | ||
} | ||
} | ||
return getAttributes; | ||
return get; | ||
} | ||
module.exports = UportLite; |
{ | ||
"name": "uport-lite", | ||
"version": "0.2.4", | ||
"version": "1.0.0-alpha", | ||
"description": "Lightweight library for looking up public profiles on uport-registry", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
# Light weight library for fetching public uPort profiles | ||
A uPort is an ethereum address representing an identity of a person, thing or other entitity. | ||
A uPort is an ethereum address representing an identity of a person, thing or other entitity. We are using the NID address encoding scheme to support safe use of multiple networks. | ||
@@ -20,3 +20,3 @@ A public profile is stored on ipfs at a hash registered in the [uPortRegistry](https://github.com/ConsenSys/uport-registry). | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', (error, profile) => { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', (error, profile) => { | ||
console.log(profile) | ||
@@ -32,10 +32,15 @@ }) | ||
const registry = UportLite({ | ||
registryAddress: '0x022f41a91cb30d6a20ffcfde3f84be6c1fa70d60', | ||
ipfsGw: 'https://ipfs.infura.io/ipfs/', | ||
rpcUrl: 'https://mainnet.infura.io/INFURA_API_KEY' | ||
infuraKey: 'INFURA_API_KEY', | ||
networks: { | ||
'0x94365e3b': { | ||
rpcUrl: 'https://private.chain/rpc', | ||
address: '0x0101....' | ||
} | ||
} | ||
}) | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', (error, profile) => { | ||
registry('5A8bRWU3F7j3REx3vkJWxdjQPp4tqmxFPmab1Tr', (error, profile) => { | ||
console.log(profile) | ||
}) | ||
``` |
import UportLite from '../index' | ||
const registry = UportLite() | ||
const registry = UportLite({ | ||
networks: { | ||
'0x94365e3b': { | ||
registry: '0x41566e3a081f5032bdcad470adb797635ddfe1f0', | ||
rpcUrl: 'https://ropsten.infura.io' | ||
} | ||
} | ||
}) | ||
it('finds valid profile', () => { | ||
// uPortProfileIPFS1220 => 75506f727450726f66696c654950465331323230 | ||
//"uPortProfileIPFS1220","0xb08e78b8E17dC2874818d7F49055aBf08Ee9977D","0xB0F288F8EFA511962E11E37488DB0D2BCC7A5F304B1D4F3977EB0EC65814A52C" | ||
it('finds valid default profile for address', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', (error, profile) => { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', (error, profile) => { | ||
if (error) return reject(error) | ||
@@ -15,5 +25,27 @@ resolve(profile) | ||
it('finds valid default profile on private chain', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('5A8bRX9ShMLm7bXPn9SVX6hC7SG1HURF3tknToB', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}) | ||
}).then(profile => { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40') | ||
}) | ||
}) | ||
it('finds valid advanced profile using new API', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}, '2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', 'uPortProfileIPFS1220') | ||
}).then(profile => { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40') | ||
}) | ||
}) | ||
it('returns null if it profile doesnt exist', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394d', (error, profile) => { | ||
registry('2oJvyeFwEbtLsuJ7G7bcUFPocuEU8AdeHKs', (error, profile) => { | ||
if (error) return reject(error) | ||
@@ -26,1 +58,47 @@ resolve(profile) | ||
}) | ||
it('returns error for unsupported network', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('5A8bRWU3F7j3REx3vkJWxdjQPp4tqmxFPmab1Tr', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}) | ||
}).catch(error => { | ||
return expect(error.message).toBe('Network id 0x94365e3a is not configured') | ||
}) | ||
}) | ||
it('returns error if issuer and subject are on different networks', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('2oVdmcz7BkWozm2JE4hHixRV8s5y3STqhPG', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}, '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX', 'uPortProfileIPFS1220') | ||
}).catch(error => { | ||
return expect(error.message).toBe('Issuer and subject must be on the same network') | ||
}) | ||
}) | ||
describe('legacy registry', () => { | ||
it('finds valid profile', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}) | ||
}).then(profile => { | ||
return expect(profile.publicKey).toEqual('0x0482780d59037778ea03c7d5169dd7cf47a835cb6d57a606b4e6cf98000a28d20d6d6bfae223cc76fd2f63d8a382a1c054788c4fafb1062ee89e718b96e0896d40') | ||
}) | ||
}) | ||
it('returns null if it profile doesnt exist', () => { | ||
return new Promise((resolve, reject) => { | ||
registry('0x3b2631d8e15b145fd2bf99fc5f98346aecdc394d', (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}) | ||
}).then(profile => { | ||
return expect(profile).toBeUndefined() | ||
}) | ||
}) | ||
}) |
128
src/index.js
@@ -5,5 +5,9 @@ var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
const XMLHttpRequest = (typeof window !== 'undefined') ? window.XMLHttpRequest : require('xmlhttprequest').XMLHttpRequest | ||
const XMLHttpRequest = (typeof window !== 'undefined') ? window.XMLHttpRequest : require('xmlhttprequest').XMLHttpRequest | ||
const functionSignature = '0x447885f0' | ||
// Legacy | ||
const getAttributesData = '0x446d5aa4000000000000000000000000' | ||
function http (opts, callback) { | ||
@@ -44,2 +48,37 @@ const request = new XMLHttpRequest() // eslint-disable-line | ||
function registryEncodingToIPFS (hexStr) { | ||
return base58.encode(hex.decode('1220' + hexStr.slice(2))) | ||
} | ||
// to avoid adding further dependencies we are not verifying checksum | ||
export function eaeDecode (encoded) { | ||
const data = base58.decode(encoded) | ||
const netLength = data.length - 24 | ||
const network = data.slice(1, netLength) | ||
const address = data.slice(netLength, 20 + netLength) | ||
return { | ||
network: `0x${hex.encode(network)}`, | ||
address: `0x${hex.encode(address)}` | ||
} | ||
} | ||
const defaultNetworks = { | ||
'0x1': { | ||
registry: '0xab5c8051b9a1df1aab0149f8b0630848b7ecabf6', | ||
rpcUrl: 'https://mainnet.infura.io' | ||
}, | ||
'0x3': { | ||
registry: '0x41566e3a081f5032bdcad470adb797635ddfe1f0', | ||
rpcUrl: 'https://ropsten.infura.io' | ||
}, | ||
'0x2a': { | ||
registry: '0xeA8aC9cF5c3D02cfc30F65C04eD921D713062745', | ||
rpcUrl: 'https://kovan.infura.io' | ||
// }, | ||
// '0x16B2': { | ||
// registry: '', | ||
// rpcUrl: 'https://infuranet.infura.io' | ||
} | ||
} | ||
function toBase58 (hexStr) { | ||
@@ -50,7 +89,33 @@ return base58.encode(hex.decode(hexStr)) | ||
function UportLite (opts = {}) { | ||
const registryAddress = opts.registryAddress || '0xb9C1598e24650437a3055F7f66AC1820c419a679' | ||
const infuraKey = opts.infuraKey || 'uport-lite-library' | ||
const ipfsGw = opts.ipfsGw || 'https://ipfs.infura.io/ipfs/' | ||
const rpcUrl = opts.rpcUrl || 'https://ropsten.infura.io/uport-lite-library' | ||
const networks = opts.networks ? Object.assign({}, defaultNetworks, opts.networks) : defaultNetworks | ||
function asciiToHex (string, delim) { | ||
return string.split('').map(function (c) { | ||
return ('0' + c.charCodeAt(0).toString(16)).slice(-2) | ||
}).join(delim || '') | ||
} | ||
function callRegistry (address, callback) { | ||
function pad (pad, str, padLeft) { | ||
if (typeof str === 'undefined') { | ||
return pad | ||
} | ||
if (padLeft) { | ||
return (pad + str).slice(-pad.length) | ||
} else { | ||
return (str + pad).substring(0, pad.length) | ||
} | ||
} | ||
function encodeFunctionCall (functionSignature, registrationIdentifier, issuer, subject) { | ||
var callString = functionSignature | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', asciiToHex(registrationIdentifier)) | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', issuer.slice(2), true) | ||
callString += pad('0000000000000000000000000000000000000000000000000000000000000000', subject.slice(2), true) | ||
return callString | ||
} | ||
// TODO remove once feasible | ||
function callLegacyRegistry (address, callback) { | ||
const rpcUrl = `https://ropsten.infura.io/${infuraKey}` | ||
if (!address) return callback(null) | ||
@@ -63,3 +128,3 @@ return http({ | ||
params: [ | ||
{to: registryAddress, data: (getAttributesData + address.slice(2))}, | ||
{to: '0xb9C1598e24650437a3055F7f66AC1820c419a679', data: (getAttributesData + address.slice(2))}, | ||
'latest' | ||
@@ -77,2 +142,33 @@ ], | ||
function callRegistry (registrationIdentifier, issuerId, subjectId, callback) { | ||
const issuer = eaeDecode(issuerId) | ||
const subject = eaeDecode(subjectId) | ||
if (issuer.network !== subject.network) { | ||
throw new Error('Issuer and subject must be on the same network') | ||
} | ||
if (!networks[issuer.network]) { | ||
throw new Error(`Network id ${issuer.network} is not configured`) | ||
} | ||
const rpcUrl = networks[issuer.network].rpcUrl | ||
const registryAddress = networks[issuer.network].registry | ||
const callString = encodeFunctionCall(functionSignature, registrationIdentifier, issuer.address, subject.address) | ||
return http({ | ||
uri: `${rpcUrl}/${infuraKey}`, | ||
accept: 'application/json', | ||
data: { | ||
method: 'eth_call', | ||
params: [ | ||
{to: registryAddress, data: (callString)}, | ||
'latest' | ||
], | ||
id: 1, | ||
jsonrpc: '2.0' | ||
} | ||
}, (error, response) => { | ||
if (error) return callback(error) | ||
if (response.result == 0) return callback(error) | ||
return callback(null, registryEncodingToIPFS(response.result)) | ||
}) | ||
} | ||
function fetchIpfs (ipfsHash, callback) { | ||
@@ -83,11 +179,21 @@ if (!ipfsHash || ipfsHash === '0x') return callback() | ||
function getAttributes (address, callback) { | ||
return callRegistry(address, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
function get (issuer, callback, subjectAddress, registrationIdentifier = 'uPortProfileIPFS1220') { | ||
if (!issuer) return callback(null) | ||
const subject = subjectAddress || issuer | ||
if (issuer.match(/0x[0-9a-fA-F]{40}/)) { | ||
return callLegacyRegistry(issuer, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
} else { | ||
return callRegistry(registrationIdentifier, issuer, subject, (error, ipfsHash) => { | ||
if (error) return callback(error) | ||
fetchIpfs(ipfsHash, callback) | ||
}) | ||
} | ||
} | ||
return getAttributes | ||
return get | ||
} | ||
module.exports = UportLite |
43056
9
936
45