mirror of https://github.com/ethereum/go-ethereum
commit
d92fde6980
@ -0,0 +1,12 @@ |
|||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com> |
||||||
|
|
||||||
|
Viktor Trón <viktor.tron@gmail.com> |
||||||
|
|
||||||
|
Joseph Goulden <joegoulden@gmail.com> |
||||||
|
|
||||||
|
Nick Savers <nicksavers@gmail.com> |
||||||
|
|
||||||
|
Maran Hidskes <maran.hidskes@gmail.com> |
@ -1,18 +0,0 @@ |
|||||||
# Ethereum JavaScript API |
|
||||||
|
|
||||||
This is the Ethereum compatible JavaScript API using `Promise`s |
|
||||||
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. |
|
||||||
|
|
||||||
For an example see `index.html`. |
|
||||||
|
|
||||||
**Please note this repo is in it's early stage.** |
|
||||||
|
|
||||||
If you'd like to run a WebSocket ethereum node check out |
|
||||||
[go-ethereum](https://github.com/ethereum/go-ethereum). |
|
||||||
|
|
||||||
To install ethereum and spawn a node: |
|
||||||
|
|
||||||
``` |
|
||||||
go get github.com/ethereum/go-ethereum/ethereum |
|
||||||
ethereum -ws -loglevel=4 |
|
||||||
``` |
|
@ -1,70 +0,0 @@ |
|||||||
(function () { |
|
||||||
var HttpRpcProvider = function (host) { |
|
||||||
this.handlers = []; |
|
||||||
this.host = host; |
|
||||||
}; |
|
||||||
|
|
||||||
function formatJsonRpcObject(object) { |
|
||||||
return { |
|
||||||
jsonrpc: '2.0', |
|
||||||
method: object.call, |
|
||||||
params: object.args, |
|
||||||
id: object._id |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
function formatJsonRpcMessage(message) {
|
|
||||||
var object = JSON.parse(message); |
|
||||||
|
|
||||||
return { |
|
||||||
_id: object.id, |
|
||||||
data: object.result |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) { |
|
||||||
var data = formatJsonRpcObject(payload); |
|
||||||
|
|
||||||
var request = new XMLHttpRequest(); |
|
||||||
request.open("POST", this.host, true); |
|
||||||
request.send(JSON.stringify(data)); |
|
||||||
request.onreadystatechange = function () { |
|
||||||
if (request.readyState === 4 && cb) { |
|
||||||
cb(request); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
HttpRpcProvider.prototype.send = function (payload) { |
|
||||||
var self = this; |
|
||||||
this.sendRequest(payload, function (request) { |
|
||||||
self.handlers.forEach(function (handler) { |
|
||||||
handler.call(self, formatJsonRpcMessage(request.responseText)); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
HttpRpcProvider.prototype.poll = function (payload, id) { |
|
||||||
var self = this; |
|
||||||
this.sendRequest(payload, function (request) { |
|
||||||
var parsed = JSON.parse(request.responseText); |
|
||||||
if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) { |
|
||||||
return; |
|
||||||
} |
|
||||||
self.handlers.forEach(function (handler) { |
|
||||||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { |
|
||||||
set: function (handler) { |
|
||||||
this.handlers.push(handler); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
if (typeof(web3) !== "undefined" && web3.providers !== undefined) { |
|
||||||
web3.providers.HttpRpcProvider = HttpRpcProvider; |
|
||||||
} |
|
||||||
})(); |
|
||||||
|
|
@ -1,33 +0,0 @@ |
|||||||
<!doctype> |
|
||||||
<html> |
|
||||||
|
|
||||||
<head> |
|
||||||
<script type="text/javascript" src="main.js"></script> |
|
||||||
<script type="text/javascript" src="websocket.js"></script> |
|
||||||
<script type="text/javascript" src="qt.js"></script> |
|
||||||
<script type="text/javascript" src="httprpc.js"></script> |
|
||||||
<script type="text/javascript"> |
|
||||||
function registerName() { |
|
||||||
var name = document.querySelector("#name").value; |
|
||||||
name = web3.fromAscii(name); |
|
||||||
|
|
||||||
var eth = web3.eth; |
|
||||||
eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) { |
|
||||||
document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through."; |
|
||||||
}, function(err) { |
|
||||||
console.log(err); |
|
||||||
}); |
|
||||||
} |
|
||||||
</script> |
|
||||||
</head> |
|
||||||
|
|
||||||
<body> |
|
||||||
|
|
||||||
<h1>std::name_reg</h1> |
|
||||||
<input type="text" id="name"></input> |
|
||||||
<input type="submit" onClick="registerName();"></input> |
|
||||||
<div id="result"></div> |
|
||||||
|
|
||||||
</body> |
|
||||||
|
|
||||||
</html> |
|
@ -1,432 +0,0 @@ |
|||||||
(function(window) { |
|
||||||
function isPromise(o) { |
|
||||||
return o instanceof Promise |
|
||||||
} |
|
||||||
|
|
||||||
function flattenPromise (obj) { |
|
||||||
if (obj instanceof Promise) { |
|
||||||
return Promise.resolve(obj); |
|
||||||
} |
|
||||||
|
|
||||||
if (obj instanceof Array) { |
|
||||||
return new Promise(function (resolve) { |
|
||||||
var promises = obj.map(function (o) { |
|
||||||
return flattenPromise(o); |
|
||||||
}); |
|
||||||
|
|
||||||
return Promise.all(promises).then(function (res) { |
|
||||||
for (var i = 0; i < obj.length; i++) { |
|
||||||
obj[i] = res[i]; |
|
||||||
} |
|
||||||
resolve(obj); |
|
||||||
}); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
if (obj instanceof Object) { |
|
||||||
return new Promise(function (resolve) { |
|
||||||
var keys = Object.keys(obj); |
|
||||||
var promises = keys.map(function (key) { |
|
||||||
return flattenPromise(obj[key]); |
|
||||||
}); |
|
||||||
|
|
||||||
return Promise.all(promises).then(function (res) { |
|
||||||
for (var i = 0; i < keys.length; i++) { |
|
||||||
obj[keys[i]] = res[i]; |
|
||||||
} |
|
||||||
resolve(obj); |
|
||||||
}); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
return Promise.resolve(obj); |
|
||||||
}; |
|
||||||
|
|
||||||
var ethMethods = function () { |
|
||||||
var blockCall = function (args) { |
|
||||||
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber"; |
|
||||||
}; |
|
||||||
|
|
||||||
var transactionCall = function (args) { |
|
||||||
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
|
|
||||||
}; |
|
||||||
|
|
||||||
var uncleCall = function (args) { |
|
||||||
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
|
|
||||||
}; |
|
||||||
|
|
||||||
var methods = [ |
|
||||||
{ name: 'balanceAt', call: 'balanceAt' }, |
|
||||||
{ name: 'stateAt', call: 'stateAt' }, |
|
||||||
{ name: 'countAt', call: 'countAt'}, |
|
||||||
{ name: 'codeAt', call: 'codeAt' }, |
|
||||||
{ name: 'transact', call: 'transact' }, |
|
||||||
{ name: 'call', call: 'call' }, |
|
||||||
{ name: 'block', call: blockCall }, |
|
||||||
{ name: 'transaction', call: transactionCall }, |
|
||||||
{ name: 'uncle', call: uncleCall }, |
|
||||||
{ name: 'compile', call: 'compile' } |
|
||||||
]; |
|
||||||
return methods; |
|
||||||
}; |
|
||||||
|
|
||||||
var ethProperties = function () { |
|
||||||
return [ |
|
||||||
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' }, |
|
||||||
{ name: 'listening', getter: 'listening', setter: 'setListening' }, |
|
||||||
{ name: 'mining', getter: 'mining', setter: 'setMining' }, |
|
||||||
{ name: 'gasPrice', getter: 'gasPrice' }, |
|
||||||
{ name: 'account', getter: 'account' }, |
|
||||||
{ name: 'accounts', getter: 'accounts' }, |
|
||||||
{ name: 'peerCount', getter: 'peerCount' }, |
|
||||||
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' }, |
|
||||||
{ name: 'number', getter: 'number'} |
|
||||||
]; |
|
||||||
}; |
|
||||||
|
|
||||||
var dbMethods = function () { |
|
||||||
return [ |
|
||||||
{ name: 'put', call: 'put' }, |
|
||||||
{ name: 'get', call: 'get' }, |
|
||||||
{ name: 'putString', call: 'putString' }, |
|
||||||
{ name: 'getString', call: 'getString' } |
|
||||||
]; |
|
||||||
}; |
|
||||||
|
|
||||||
var shhMethods = function () { |
|
||||||
return [ |
|
||||||
{ name: 'post', call: 'post' }, |
|
||||||
{ name: 'newIdentity', call: 'newIdentity' }, |
|
||||||
{ name: 'haveIdentity', call: 'haveIdentity' }, |
|
||||||
{ name: 'newGroup', call: 'newGroup' }, |
|
||||||
{ name: 'addToGroup', call: 'addToGroup' } |
|
||||||
]; |
|
||||||
}; |
|
||||||
|
|
||||||
var ethWatchMethods = function () { |
|
||||||
var newFilter = function (args) { |
|
||||||
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter'; |
|
||||||
}; |
|
||||||
|
|
||||||
return [ |
|
||||||
{ name: 'newFilter', call: newFilter }, |
|
||||||
{ name: 'uninstallFilter', call: 'uninstallFilter' }, |
|
||||||
{ name: 'getMessages', call: 'getMessages' } |
|
||||||
]; |
|
||||||
}; |
|
||||||
|
|
||||||
var shhWatchMethods = function () { |
|
||||||
return [ |
|
||||||
{ name: 'newFilter', call: 'shhNewFilter' }, |
|
||||||
{ name: 'uninstallFilter', call: 'shhUninstallFilter' }, |
|
||||||
{ name: 'getMessage', call: 'shhGetMessages' } |
|
||||||
]; |
|
||||||
}; |
|
||||||
|
|
||||||
var setupMethods = function (obj, methods) { |
|
||||||
methods.forEach(function (method) { |
|
||||||
obj[method.name] = function () { |
|
||||||
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { |
|
||||||
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
|
||||||
return {call: call, args: args}; |
|
||||||
}).then(function (request) { |
|
||||||
return new Promise(function (resolve, reject) { |
|
||||||
web3.provider.send(request, function (result) { |
|
||||||
//if (result || typeof result === "boolean") {
|
|
||||||
resolve(result); |
|
||||||
return; |
|
||||||
//}
|
|
||||||
//reject(result);
|
|
||||||
}); |
|
||||||
}); |
|
||||||
}).catch(function( err) { |
|
||||||
console.error(err); |
|
||||||
}); |
|
||||||
}; |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
var setupProperties = function (obj, properties) { |
|
||||||
properties.forEach(function (property) { |
|
||||||
var proto = {}; |
|
||||||
proto.get = function () { |
|
||||||
return new Promise(function(resolve, reject) { |
|
||||||
web3.provider.send({call: property.getter}, function(result) { |
|
||||||
resolve(result); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
if (property.setter) { |
|
||||||
proto.set = function (val) { |
|
||||||
return flattenPromise([val]).then(function (args) { |
|
||||||
return new Promise(function (resolve) { |
|
||||||
web3.provider.send({call: property.setter, args: args}, function (result) { |
|
||||||
resolve(result); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}).catch(function (err) { |
|
||||||
console.error(err); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
Object.defineProperty(obj, property.name, proto); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
var web3 = { |
|
||||||
_callbacks: {}, |
|
||||||
_events: {}, |
|
||||||
providers: {}, |
|
||||||
toHex: function(str) { |
|
||||||
var hex = ""; |
|
||||||
for(var i = 0; i < str.length; i++) { |
|
||||||
var n = str.charCodeAt(i).toString(16); |
|
||||||
hex += n.length < 2 ? '0' + n : n; |
|
||||||
} |
|
||||||
|
|
||||||
return hex; |
|
||||||
}, |
|
||||||
|
|
||||||
toAscii: function(hex) { |
|
||||||
// Find termination
|
|
||||||
var str = ""; |
|
||||||
var i = 0, l = hex.length; |
|
||||||
for(; i < l; i+=2) { |
|
||||||
var code = hex.charCodeAt(i) |
|
||||||
if(code == 0) { |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); |
|
||||||
} |
|
||||||
|
|
||||||
return str; |
|
||||||
}, |
|
||||||
|
|
||||||
toDecimal: function (val) { |
|
||||||
return parseInt(val, 16);
|
|
||||||
}, |
|
||||||
|
|
||||||
fromAscii: function(str, pad) { |
|
||||||
pad = pad === undefined ? 32 : pad; |
|
||||||
var hex = this.toHex(str); |
|
||||||
while(hex.length < pad*2) |
|
||||||
hex += "00"; |
|
||||||
return hex |
|
||||||
}, |
|
||||||
|
|
||||||
eth: { |
|
||||||
prototype: Object(), |
|
||||||
watch: function (params) { |
|
||||||
return new Filter(params, ethWatch); |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
db: { |
|
||||||
prototype: Object() |
|
||||||
}, |
|
||||||
|
|
||||||
shh: { |
|
||||||
prototype: Object(), |
|
||||||
watch: function (params) { |
|
||||||
return new Filter(params, shhWatch); |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
on: function(event, id, cb) { |
|
||||||
if(web3._events[event] === undefined) { |
|
||||||
web3._events[event] = {}; |
|
||||||
} |
|
||||||
|
|
||||||
web3._events[event][id] = cb; |
|
||||||
return this |
|
||||||
}, |
|
||||||
|
|
||||||
off: function(event, id) { |
|
||||||
if(web3._events[event] !== undefined) { |
|
||||||
delete web3._events[event][id]; |
|
||||||
} |
|
||||||
|
|
||||||
return this |
|
||||||
}, |
|
||||||
|
|
||||||
trigger: function(event, id, data) { |
|
||||||
var callbacks = web3._events[event]; |
|
||||||
if (!callbacks || !callbacks[id]) { |
|
||||||
return; |
|
||||||
} |
|
||||||
var cb = callbacks[id]; |
|
||||||
cb(data); |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
var eth = web3.eth; |
|
||||||
setupMethods(eth, ethMethods()); |
|
||||||
setupProperties(eth, ethProperties()); |
|
||||||
setupMethods(web3.db, dbMethods()); |
|
||||||
setupMethods(web3.shh, shhMethods()); |
|
||||||
|
|
||||||
var ethWatch = { |
|
||||||
changed: 'changed' |
|
||||||
}; |
|
||||||
setupMethods(ethWatch, ethWatchMethods()); |
|
||||||
var shhWatch = { |
|
||||||
changed: 'shhChanged' |
|
||||||
}; |
|
||||||
setupMethods(shhWatch, shhWatchMethods()); |
|
||||||
|
|
||||||
var ProviderManager = function() { |
|
||||||
this.queued = []; |
|
||||||
this.polls = []; |
|
||||||
this.ready = false; |
|
||||||
this.provider = undefined; |
|
||||||
this.id = 1; |
|
||||||
|
|
||||||
var self = this; |
|
||||||
var poll = function () { |
|
||||||
if (self.provider && self.provider.poll) { |
|
||||||
self.polls.forEach(function (data) { |
|
||||||
data.data._id = self.id;
|
|
||||||
self.id++; |
|
||||||
self.provider.poll(data.data, data.id); |
|
||||||
}); |
|
||||||
} |
|
||||||
setTimeout(poll, 12000); |
|
||||||
}; |
|
||||||
poll(); |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.send = function(data, cb) { |
|
||||||
data._id = this.id; |
|
||||||
if (cb) { |
|
||||||
web3._callbacks[data._id] = cb; |
|
||||||
} |
|
||||||
|
|
||||||
data.args = data.args || []; |
|
||||||
this.id++; |
|
||||||
|
|
||||||
if(this.provider !== undefined) { |
|
||||||
this.provider.send(data); |
|
||||||
} else { |
|
||||||
console.warn("provider is not set"); |
|
||||||
this.queued.push(data); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.set = function(provider) { |
|
||||||
if(this.provider !== undefined && this.provider.unload !== undefined) { |
|
||||||
this.provider.unload(); |
|
||||||
} |
|
||||||
|
|
||||||
this.provider = provider; |
|
||||||
this.ready = true; |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.sendQueued = function() { |
|
||||||
for(var i = 0; this.queued.length; i++) { |
|
||||||
// Resend
|
|
||||||
this.send(this.queued[i]); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.installed = function() { |
|
||||||
return this.provider !== undefined; |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.startPolling = function (data, pollId) { |
|
||||||
if (!this.provider || !this.provider.poll) { |
|
||||||
return; |
|
||||||
} |
|
||||||
this.polls.push({data: data, id: pollId}); |
|
||||||
}; |
|
||||||
|
|
||||||
ProviderManager.prototype.stopPolling = function (pollId) { |
|
||||||
for (var i = this.polls.length; i--;) { |
|
||||||
var poll = this.polls[i]; |
|
||||||
if (poll.id === pollId) { |
|
||||||
this.polls.splice(i, 1); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
web3.provider = new ProviderManager(); |
|
||||||
|
|
||||||
web3.setProvider = function(provider) { |
|
||||||
provider.onmessage = messageHandler; |
|
||||||
web3.provider.set(provider); |
|
||||||
web3.provider.sendQueued(); |
|
||||||
}; |
|
||||||
|
|
||||||
var Filter = function(options, impl) { |
|
||||||
this.impl = impl; |
|
||||||
this.callbacks = []; |
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.promise = impl.newFilter(options);
|
|
||||||
this.promise.then(function (id) { |
|
||||||
self.id = id; |
|
||||||
web3.on(impl.changed, id, self.trigger.bind(self)); |
|
||||||
web3.provider.startPolling({call: impl.changed, args: [id]}, id); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.arrived = function(callback) { |
|
||||||
this.changed(callback); |
|
||||||
} |
|
||||||
|
|
||||||
Filter.prototype.changed = function(callback) { |
|
||||||
var self = this; |
|
||||||
this.promise.then(function(id) { |
|
||||||
self.callbacks.push(callback); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.trigger = function(messages) { |
|
||||||
for(var i = 0; i < this.callbacks.length; i++) { |
|
||||||
this.callbacks[i].call(this, messages); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.uninstall = function() { |
|
||||||
var self = this; |
|
||||||
this.promise.then(function (id) { |
|
||||||
self.impl.uninstallFilter(id); |
|
||||||
web3.provider.stopPolling(id); |
|
||||||
web3.off(impl.changed, id); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.messages = function() { |
|
||||||
var self = this;
|
|
||||||
return this.promise.then(function (id) { |
|
||||||
return self.impl.getMessages(id); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
function messageHandler(data) { |
|
||||||
if(data._event !== undefined) { |
|
||||||
web3.trigger(data._event, data._id, data.data); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if(data._id) { |
|
||||||
var cb = web3._callbacks[data._id]; |
|
||||||
if (cb) { |
|
||||||
cb.call(this, data.data) |
|
||||||
delete web3._callbacks[data._id]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
// Install default provider
|
|
||||||
if(!web3.provider.installed()) { |
|
||||||
var sock = new web3.WebSocket("ws://localhost:40404/eth"); |
|
||||||
|
|
||||||
web3.setProvider(sock); |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
window.web3 = web3; |
|
||||||
|
|
||||||
})(this); |
|
@ -1,27 +0,0 @@ |
|||||||
(function() { |
|
||||||
var QtProvider = function() { |
|
||||||
this.handlers = []; |
|
||||||
|
|
||||||
var self = this; |
|
||||||
navigator.qt.onmessage = function (message) { |
|
||||||
self.handlers.forEach(function (handler) { |
|
||||||
handler.call(self, JSON.parse(message.data)); |
|
||||||
}); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
QtProvider.prototype.send = function(payload) { |
|
||||||
navigator.qt.postMessage(JSON.stringify(payload)); |
|
||||||
}; |
|
||||||
|
|
||||||
Object.defineProperty(QtProvider.prototype, "onmessage", { |
|
||||||
set: function(handler) { |
|
||||||
this.handlers.push(handler); |
|
||||||
}, |
|
||||||
});
|
|
||||||
|
|
||||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) { |
|
||||||
web3.providers.QtProvider = QtProvider; |
|
||||||
} |
|
||||||
})(); |
|
||||||
|
|
@ -1,51 +0,0 @@ |
|||||||
(function() { |
|
||||||
var WebSocketProvider = function(host) { |
|
||||||
// onmessage handlers
|
|
||||||
this.handlers = []; |
|
||||||
// queue will be filled with messages if send is invoked before the ws is ready
|
|
||||||
this.queued = []; |
|
||||||
this.ready = false; |
|
||||||
|
|
||||||
this.ws = new WebSocket(host); |
|
||||||
|
|
||||||
var self = this; |
|
||||||
this.ws.onmessage = function(event) { |
|
||||||
for(var i = 0; i < self.handlers.length; i++) { |
|
||||||
self.handlers[i].call(self, JSON.parse(event.data), event) |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
this.ws.onopen = function() { |
|
||||||
self.ready = true; |
|
||||||
|
|
||||||
for(var i = 0; i < self.queued.length; i++) { |
|
||||||
// Resend
|
|
||||||
self.send(self.queued[i]); |
|
||||||
} |
|
||||||
}; |
|
||||||
}; |
|
||||||
WebSocketProvider.prototype.send = function(payload) { |
|
||||||
if(this.ready) { |
|
||||||
var data = JSON.stringify(payload); |
|
||||||
|
|
||||||
this.ws.send(data); |
|
||||||
} else { |
|
||||||
this.queued.push(payload); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
WebSocketProvider.prototype.onMessage = function(handler) { |
|
||||||
this.handlers.push(handler); |
|
||||||
}; |
|
||||||
|
|
||||||
WebSocketProvider.prototype.unload = function() { |
|
||||||
this.ws.close(); |
|
||||||
}; |
|
||||||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { |
|
||||||
set: function(provider) { this.onMessage(provider); } |
|
||||||
}); |
|
||||||
|
|
||||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) { |
|
||||||
web3.providers.WebSocketProvider = WebSocketProvider; |
|
||||||
} |
|
||||||
})(); |
|
@ -1,312 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public
|
|
||||||
// License as published by the Free Software Foundation; either
|
|
||||||
// version 2.1 of the License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this library; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
// MA 02110-1301 USA
|
|
||||||
|
|
||||||
// Main Ethereum library
|
|
||||||
window.eth = { |
|
||||||
prototype: Object(), |
|
||||||
_callbacks: {}, |
|
||||||
_onCallbacks: {}, |
|
||||||
|
|
||||||
test: function() { |
|
||||||
var t = undefined; |
|
||||||
postData({call: "test"}) |
|
||||||
navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; } |
|
||||||
for(;;) { |
|
||||||
if(t !== undefined) { |
|
||||||
return t |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
mutan: function(code, cb) { |
|
||||||
postData({call: "mutan", args: [code]}, cb) |
|
||||||
}, |
|
||||||
|
|
||||||
toHex: function(str) { |
|
||||||
var hex = ""; |
|
||||||
for(var i = 0; i < str.length; i++) { |
|
||||||
var n = str.charCodeAt(i).toString(16); |
|
||||||
hex += n.length < 2 ? '0' + n : n; |
|
||||||
} |
|
||||||
|
|
||||||
return hex; |
|
||||||
}, |
|
||||||
|
|
||||||
toAscii: function(hex) { |
|
||||||
// Find termination
|
|
||||||
var str = ""; |
|
||||||
var i = 0, l = hex.length; |
|
||||||
for(; i < l; i+=2) { |
|
||||||
var code = hex.charCodeAt(i) |
|
||||||
if(code == 0) { |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); |
|
||||||
} |
|
||||||
|
|
||||||
return str; |
|
||||||
}, |
|
||||||
|
|
||||||
fromAscii: function(str, pad) { |
|
||||||
if(pad === undefined) { |
|
||||||
pad = 32 |
|
||||||
} |
|
||||||
|
|
||||||
var hex = this.toHex(str); |
|
||||||
|
|
||||||
while(hex.length < pad*2) |
|
||||||
hex += "00"; |
|
||||||
|
|
||||||
return hex |
|
||||||
}, |
|
||||||
|
|
||||||
|
|
||||||
// Retrieve block
|
|
||||||
//
|
|
||||||
// Either supply a number or a string. Type is determent for the lookup method
|
|
||||||
// string - Retrieves the block by looking up the hash
|
|
||||||
// number - Retrieves the block by looking up the block number
|
|
||||||
getBlock: function(numberOrHash, cb) { |
|
||||||
var func; |
|
||||||
if(typeof numberOrHash == "string") { |
|
||||||
func = "getBlockByHash"; |
|
||||||
} else { |
|
||||||
func = "getBlockByNumber"; |
|
||||||
} |
|
||||||
postData({call: func, args: [numberOrHash]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
// Create transaction
|
|
||||||
//
|
|
||||||
// Transact between two state objects
|
|
||||||
transact: function(params, cb) { |
|
||||||
if(params === undefined) { |
|
||||||
params = {}; |
|
||||||
} |
|
||||||
|
|
||||||
if(params.endowment !== undefined) |
|
||||||
params.value = params.endowment; |
|
||||||
if(params.code !== undefined) |
|
||||||
params.data = params.code; |
|
||||||
|
|
||||||
// Make sure everything is string
|
|
||||||
var fields = ["to", "from", "value", "gas", "gasPrice"]; |
|
||||||
for(var i = 0; i < fields.length; i++) { |
|
||||||
if(params[fields[i]] === undefined) { |
|
||||||
params[fields[i]] = ""; |
|
||||||
} |
|
||||||
params[fields[i]] = params[fields[i]].toString(); |
|
||||||
} |
|
||||||
|
|
||||||
var data; |
|
||||||
if(typeof params.data === "object") { |
|
||||||
data = ""; |
|
||||||
for(var i = 0; i < params.data.length; i++) { |
|
||||||
data += params.data[i] |
|
||||||
} |
|
||||||
} else { |
|
||||||
data = params.data; |
|
||||||
} |
|
||||||
|
|
||||||
postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getMessages: function(filter, cb) { |
|
||||||
postData({call: "messages", args: [filter]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getStorageAt: function(address, storageAddress, cb) { |
|
||||||
postData({call: "getStorage", args: [address, storageAddress]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getEachStorageAt: function(address, cb){ |
|
||||||
postData({call: "getEachStorage", args: [address]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getKey: function(cb) { |
|
||||||
postData({call: "getKey"}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getTxCountAt: function(address, cb) { |
|
||||||
postData({call: "getTxCountAt", args: [address]}, cb); |
|
||||||
}, |
|
||||||
getIsMining: function(cb){ |
|
||||||
postData({call: "getIsMining"}, cb) |
|
||||||
}, |
|
||||||
getIsListening: function(cb){ |
|
||||||
postData({call: "getIsListening"}, cb) |
|
||||||
}, |
|
||||||
getCoinBase: function(cb){ |
|
||||||
postData({call: "getCoinBase"}, cb); |
|
||||||
}, |
|
||||||
getPeerCount: function(cb){ |
|
||||||
postData({call: "getPeerCount"}, cb); |
|
||||||
}, |
|
||||||
getBalanceAt: function(address, cb) { |
|
||||||
postData({call: "getBalance", args: [address]}, cb); |
|
||||||
}, |
|
||||||
getTransactionsFor: function(address, cb) { |
|
||||||
postData({call: "getTransactionsFor", args: [address]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
getSecretToAddress: function(sec, cb) { |
|
||||||
postData({call: "getSecretToAddress", args: [sec]}, cb); |
|
||||||
}, |
|
||||||
|
|
||||||
/* |
|
||||||
watch: function(address, storageAddrOrCb, cb) { |
|
||||||
var ev; |
|
||||||
if(cb === undefined) { |
|
||||||
cb = storageAddrOrCb; |
|
||||||
storageAddrOrCb = ""; |
|
||||||
ev = "object:"+address; |
|
||||||
} else { |
|
||||||
ev = "storage:"+address+":"+storageAddrOrCb; |
|
||||||
} |
|
||||||
|
|
||||||
eth.on(ev, cb) |
|
||||||
|
|
||||||
postData({call: "watch", args: [address, storageAddrOrCb]}); |
|
||||||
}, |
|
||||||
|
|
||||||
disconnect: function(address, storageAddrOrCb, cb) { |
|
||||||
var ev; |
|
||||||
if(cb === undefined) { |
|
||||||
cb = storageAddrOrCb; |
|
||||||
storageAddrOrCb = ""; |
|
||||||
ev = "object:"+address; |
|
||||||
} else { |
|
||||||
ev = "storage:"+address+":"+storageAddrOrCb; |
|
||||||
} |
|
||||||
|
|
||||||
eth.off(ev, cb) |
|
||||||
|
|
||||||
postData({call: "disconnect", args: [address, storageAddrOrCb]}); |
|
||||||
}, |
|
||||||
*/ |
|
||||||
|
|
||||||
watch: function(options) { |
|
||||||
var filter = new Filter(options); |
|
||||||
filter.number = newWatchNum().toString() |
|
||||||
|
|
||||||
postData({call: "watch", args: [options, filter.number]}) |
|
||||||
|
|
||||||
return filter; |
|
||||||
}, |
|
||||||
|
|
||||||
set: function(props) { |
|
||||||
postData({call: "set", args: props}); |
|
||||||
}, |
|
||||||
|
|
||||||
on: function(event, cb) { |
|
||||||
if(eth._onCallbacks[event] === undefined) { |
|
||||||
eth._onCallbacks[event] = []; |
|
||||||
} |
|
||||||
|
|
||||||
eth._onCallbacks[event].push(cb); |
|
||||||
|
|
||||||
return this |
|
||||||
}, |
|
||||||
|
|
||||||
off: function(event, cb) { |
|
||||||
if(eth._onCallbacks[event] !== undefined) { |
|
||||||
var callbacks = eth._onCallbacks[event]; |
|
||||||
for(var i = 0; i < callbacks.length; i++) { |
|
||||||
if(callbacks[i] === cb) { |
|
||||||
delete callbacks[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return this |
|
||||||
}, |
|
||||||
|
|
||||||
trigger: function(event, data) { |
|
||||||
var callbacks = eth._onCallbacks[event]; |
|
||||||
if(callbacks !== undefined) { |
|
||||||
for(var i = 0; i < callbacks.length; i++) { |
|
||||||
// Figure out whether the returned data was an array
|
|
||||||
// array means multiple return arguments (multiple params)
|
|
||||||
if(data instanceof Array) { |
|
||||||
callbacks[i].apply(this, data); |
|
||||||
} else { |
|
||||||
callbacks[i].call(this, data); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
var Filter = function(options) { |
|
||||||
this.options = options; |
|
||||||
}; |
|
||||||
Filter.prototype.changed = function(callback) { |
|
||||||
// Register the watched:<number>. Qml will call the appropriate event if anything
|
|
||||||
// interesting happens in the land of Go.
|
|
||||||
eth.on("watched:"+this.number, callback) |
|
||||||
} |
|
||||||
Filter.prototype.getMessages = function(cb) { |
|
||||||
return eth.getMessages(this.options, cb) |
|
||||||
} |
|
||||||
|
|
||||||
var watchNum = 0; |
|
||||||
function newWatchNum() { |
|
||||||
return watchNum++; |
|
||||||
} |
|
||||||
|
|
||||||
function postData(data, cb) { |
|
||||||
data._seed = Math.floor(Math.random() * 1000000) |
|
||||||
if(cb) { |
|
||||||
eth._callbacks[data._seed] = cb; |
|
||||||
} |
|
||||||
|
|
||||||
if(data.args === undefined) { |
|
||||||
data.args = []; |
|
||||||
} |
|
||||||
|
|
||||||
navigator.qt.postMessage(JSON.stringify(data)); |
|
||||||
} |
|
||||||
|
|
||||||
navigator.qt.onmessage = function(ev) { |
|
||||||
var data = JSON.parse(ev.data) |
|
||||||
|
|
||||||
if(data._event !== undefined) { |
|
||||||
eth.trigger(data._event, data.data); |
|
||||||
} else { |
|
||||||
if(data._seed) { |
|
||||||
var cb = eth._callbacks[data._seed]; |
|
||||||
if(cb) { |
|
||||||
cb.call(this, data.data) |
|
||||||
|
|
||||||
// Remove the "trigger" callback
|
|
||||||
delete eth._callbacks[ev._seed]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
eth.on("chain:changed", function() { |
|
||||||
}) |
|
||||||
|
|
||||||
eth.on("messages", { /* filters */}, function(messages){ |
|
||||||
}) |
|
||||||
|
|
||||||
eth.on("pending:changed", function() { |
|
||||||
}) |
|
||||||
|
|
@ -0,0 +1,5 @@ |
|||||||
|
{ |
||||||
|
"directory": "example/js/", |
||||||
|
"cwd": "./", |
||||||
|
"analytics": false |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
root = true |
||||||
|
|
||||||
|
[*] |
||||||
|
indent_style = space |
||||||
|
indent_size = 4 |
||||||
|
end_of_line = lf |
||||||
|
charset = utf-8 |
||||||
|
trim_trailing_whitespace = true |
||||||
|
insert_final_newline = true |
||||||
|
|
||||||
|
[*.md] |
||||||
|
trim_trailing_whitespace = false |
@ -0,0 +1,50 @@ |
|||||||
|
{ |
||||||
|
"predef": [ |
||||||
|
"console", |
||||||
|
"require", |
||||||
|
"equal", |
||||||
|
"test", |
||||||
|
"testBoth", |
||||||
|
"testWithDefault", |
||||||
|
"raises", |
||||||
|
"deepEqual", |
||||||
|
"start", |
||||||
|
"stop", |
||||||
|
"ok", |
||||||
|
"strictEqual", |
||||||
|
"module", |
||||||
|
"expect", |
||||||
|
"reject", |
||||||
|
"impl" |
||||||
|
], |
||||||
|
|
||||||
|
"esnext": true, |
||||||
|
"proto": true, |
||||||
|
"node" : true, |
||||||
|
"browser" : true, |
||||||
|
"browserify" : true, |
||||||
|
|
||||||
|
"boss" : true, |
||||||
|
"curly": false, |
||||||
|
"debug": true, |
||||||
|
"devel": true, |
||||||
|
"eqeqeq": true, |
||||||
|
"evil": true, |
||||||
|
"forin": false, |
||||||
|
"immed": false, |
||||||
|
"laxbreak": false, |
||||||
|
"newcap": true, |
||||||
|
"noarg": true, |
||||||
|
"noempty": false, |
||||||
|
"nonew": false, |
||||||
|
"nomen": false, |
||||||
|
"onevar": false, |
||||||
|
"plusplus": false, |
||||||
|
"regexp": false, |
||||||
|
"undef": true, |
||||||
|
"sub": true, |
||||||
|
"strict": false, |
||||||
|
"white": false, |
||||||
|
"shadow": true, |
||||||
|
"eqnull": true |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
example/js |
||||||
|
node_modules |
||||||
|
test |
||||||
|
.gitignore |
||||||
|
.editorconfig |
||||||
|
.travis.yml |
||||||
|
.npmignore |
||||||
|
component.json |
||||||
|
testling.html |
@ -0,0 +1,11 @@ |
|||||||
|
language: node_js |
||||||
|
node_js: |
||||||
|
- "0.11" |
||||||
|
- "0.10" |
||||||
|
before_script: |
||||||
|
- npm install |
||||||
|
- npm install jshint |
||||||
|
script: |
||||||
|
- "jshint *.js lib" |
||||||
|
after_script: |
||||||
|
- npm run-script gulp |
@ -0,0 +1,14 @@ |
|||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. |
@ -0,0 +1,79 @@ |
|||||||
|
# Ethereum JavaScript API |
||||||
|
|
||||||
|
This is the Ethereum compatible JavaScript API using `Promise`s |
||||||
|
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js |
||||||
|
|
||||||
|
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url] |
||||||
|
|
||||||
|
<!-- [![browser support](https://ci.testling.com/ethereum/ethereum.js.png)](https://ci.testling.com/ethereum/ethereum.js) --> |
||||||
|
|
||||||
|
## Installation |
||||||
|
|
||||||
|
### Node.js |
||||||
|
|
||||||
|
npm install ethereum.js |
||||||
|
|
||||||
|
### For browser |
||||||
|
Bower |
||||||
|
|
||||||
|
bower install ethereum.js |
||||||
|
|
||||||
|
Component |
||||||
|
|
||||||
|
component install ethereum/ethereum.js |
||||||
|
|
||||||
|
* Include `ethereum.min.js` in your html file. |
||||||
|
* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6. |
||||||
|
|
||||||
|
## Usage |
||||||
|
Require the library: |
||||||
|
|
||||||
|
var web3 = require('web3'); |
||||||
|
|
||||||
|
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider) |
||||||
|
|
||||||
|
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth')); |
||||||
|
|
||||||
|
There you go, now you can use it: |
||||||
|
|
||||||
|
``` |
||||||
|
web3.eth.coinbase.then(function(result){ |
||||||
|
console.log(result); |
||||||
|
return web3.eth.balanceAt(result); |
||||||
|
}).then(function(balance){ |
||||||
|
console.log(web3.toDecimal(balance)); |
||||||
|
}).catch(function(err){ |
||||||
|
console.log(err); |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
For another example see `example/index.html`. |
||||||
|
|
||||||
|
## Building |
||||||
|
|
||||||
|
* `gulp build` |
||||||
|
|
||||||
|
|
||||||
|
### Testing |
||||||
|
|
||||||
|
**Please note this repo is in it's early stage.** |
||||||
|
|
||||||
|
If you'd like to run a WebSocket ethereum node check out |
||||||
|
[go-ethereum](https://github.com/ethereum/go-ethereum). |
||||||
|
|
||||||
|
To install ethereum and spawn a node: |
||||||
|
|
||||||
|
``` |
||||||
|
go get github.com/ethereum/go-ethereum/ethereum |
||||||
|
ethereum -ws -loglevel=4 |
||||||
|
``` |
||||||
|
|
||||||
|
[npm-image]: https://badge.fury.io/js/ethereum.js.png |
||||||
|
[npm-url]: https://npmjs.org/package/ethereum.js |
||||||
|
[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg |
||||||
|
[travis-url]: https://travis-ci.org/ethereum/ethereum.js |
||||||
|
[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg |
||||||
|
[dep-url]: https://david-dm.org/ethereum/ethereum.js |
||||||
|
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg |
||||||
|
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies |
@ -0,0 +1,51 @@ |
|||||||
|
{ |
||||||
|
"name": "ethereum.js", |
||||||
|
"namespace": "ethereum", |
||||||
|
"version": "0.0.3", |
||||||
|
"description": "Ethereum Compatible JavaScript API", |
||||||
|
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], |
||||||
|
"dependencies": { |
||||||
|
"es6-promise": "#master" |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "https://github.com/ethereum/ethereum.js.git" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/ethereum/ethereum.js", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/ethereum/ethereum.js/issues" |
||||||
|
}, |
||||||
|
"keywords": [ |
||||||
|
"ethereum", |
||||||
|
"javascript", |
||||||
|
"API" |
||||||
|
], |
||||||
|
"authors": [ |
||||||
|
{ |
||||||
|
"name": "Marek Kotewicz", |
||||||
|
"email": "marek@ethdev.com", |
||||||
|
"homepage": "https://github.com/debris" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Marian Oancea", |
||||||
|
"email": "marian@ethdev.com", |
||||||
|
"homepage": "https://github.com/cubedro" |
||||||
|
} |
||||||
|
], |
||||||
|
"license": "LGPL-3.0", |
||||||
|
"ignore": [ |
||||||
|
"example", |
||||||
|
"lib", |
||||||
|
"node_modules", |
||||||
|
"package.json", |
||||||
|
".bowerrc", |
||||||
|
".editorconfig", |
||||||
|
".gitignore", |
||||||
|
".jshintrc", |
||||||
|
".npmignore", |
||||||
|
".travis.yml", |
||||||
|
"gulpfile.js", |
||||||
|
"index.js", |
||||||
|
"**/*.txt" |
||||||
|
] |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,41 @@ |
|||||||
|
<!doctype> |
||||||
|
<html> |
||||||
|
|
||||||
|
<head> |
||||||
|
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script> |
||||||
|
<script type="text/javascript" src="../dist/ethereum.js"></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
|
||||||
|
var web3 = require('web3'); |
||||||
|
web3.setProvider(new web3.providers.AutoProvider()); |
||||||
|
|
||||||
|
function watchBalance() { |
||||||
|
var coinbase = web3.eth.coinbase; |
||||||
|
var originalBalance = 0; |
||||||
|
|
||||||
|
web3.eth.balanceAt(coinbase).then(function (balance) { |
||||||
|
originalBalance = web3.toDecimal(balance); |
||||||
|
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...'; |
||||||
|
}); |
||||||
|
|
||||||
|
web3.eth.watch({altered: coinbase}).changed(function() { |
||||||
|
web3.eth.balanceAt(coinbase).then(function (balance) { |
||||||
|
var currentBalance = web3.toDecimal(balance); |
||||||
|
document.getElementById("current").innerText = 'current: ' + currentBalance; |
||||||
|
document.getElementById("diff").innerText = 'diff: ' + (currentBalance - originalBalance); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>coinbase balance</h1> |
||||||
|
<button type="button" onClick="watchBalance();">watch balance</button> |
||||||
|
<div></div> |
||||||
|
<div id="original"></div> |
||||||
|
<div id="current"></div> |
||||||
|
<div id="diff"></div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
@ -0,0 +1,75 @@ |
|||||||
|
<!doctype> |
||||||
|
<html> |
||||||
|
|
||||||
|
<head> |
||||||
|
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script> |
||||||
|
<script type="text/javascript" src="../dist/ethereum.js"></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
|
||||||
|
var web3 = require('web3'); |
||||||
|
web3.setProvider(new web3.providers.AutoProvider()); |
||||||
|
|
||||||
|
// solidity source code |
||||||
|
var source = "" + |
||||||
|
"contract test {\n" + |
||||||
|
" function multiply(uint a) returns(uint d) {\n" + |
||||||
|
" return a * 7;\n" + |
||||||
|
" }\n" + |
||||||
|
"}\n"; |
||||||
|
|
||||||
|
// contract description, this will be autogenerated somehow |
||||||
|
var desc = [{ |
||||||
|
"name": "multiply", |
||||||
|
"inputs": [ |
||||||
|
{ |
||||||
|
"name": "a", |
||||||
|
"type": "uint256" |
||||||
|
} |
||||||
|
], |
||||||
|
"outputs": [ |
||||||
|
{ |
||||||
|
"name": "d", |
||||||
|
"type": "uint256" |
||||||
|
} |
||||||
|
] |
||||||
|
}]; |
||||||
|
|
||||||
|
var contract; |
||||||
|
|
||||||
|
function createExampleContract() { |
||||||
|
// hide create button |
||||||
|
document.getElementById('create').style.visibility = 'hidden'; |
||||||
|
document.getElementById('source').innerText = source; |
||||||
|
|
||||||
|
// create contract |
||||||
|
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) { |
||||||
|
contract = web3.contract(address, desc); |
||||||
|
document.getElementById('call').style.visibility = 'visible'; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function callExampleContract() { |
||||||
|
// this should be generated by ethereum |
||||||
|
var param = parseInt(document.getElementById('value').value); |
||||||
|
|
||||||
|
// call the contract |
||||||
|
contract.multiply(param).call().then(function(res) { |
||||||
|
document.getElementById('result').innerText = res[0]; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>contract</h1> |
||||||
|
<div id="source"></div> |
||||||
|
<div id='create'> |
||||||
|
<button type="button" onClick="createExampleContract();">create example contract</button> |
||||||
|
</div> |
||||||
|
<div id='call' style='visibility: hidden;'> |
||||||
|
<input type="number" id="value" onkeyup='callExampleContract()'></input> |
||||||
|
</div> |
||||||
|
<div id="result"></div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
@ -0,0 +1,16 @@ |
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('es6-promise').polyfill(); |
||||||
|
|
||||||
|
var web3 = require("../index.js"); |
||||||
|
|
||||||
|
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080')); |
||||||
|
|
||||||
|
web3.eth.coinbase.then(function(result){ |
||||||
|
console.log(result); |
||||||
|
return web3.eth.balanceAt(result); |
||||||
|
}).then(function(balance){ |
||||||
|
console.log(web3.toDecimal(balance)); |
||||||
|
}).catch(function(err){ |
||||||
|
console.log(err); |
||||||
|
}); |
@ -0,0 +1,104 @@ |
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
'use strict'; |
||||||
|
|
||||||
|
var path = require('path'); |
||||||
|
|
||||||
|
var del = require('del'); |
||||||
|
var gulp = require('gulp'); |
||||||
|
var browserify = require('browserify'); |
||||||
|
var jshint = require('gulp-jshint'); |
||||||
|
var uglify = require('gulp-uglify'); |
||||||
|
var rename = require('gulp-rename'); |
||||||
|
var envify = require('envify/custom'); |
||||||
|
var unreach = require('unreachable-branch-transform'); |
||||||
|
var source = require('vinyl-source-stream'); |
||||||
|
var exorcist = require('exorcist'); |
||||||
|
var bower = require('bower'); |
||||||
|
|
||||||
|
var DEST = './dist/'; |
||||||
|
|
||||||
|
var build = function(src, dst, ugly) { |
||||||
|
var result = browserify({ |
||||||
|
debug: true, |
||||||
|
insert_global_vars: false, |
||||||
|
detectGlobals: false, |
||||||
|
bundleExternal: false |
||||||
|
}) |
||||||
|
.require('./' + src + '.js', {expose: 'web3'}) |
||||||
|
.add('./' + src + '.js') |
||||||
|
.transform('envify', { |
||||||
|
NODE_ENV: 'build' |
||||||
|
}) |
||||||
|
.transform('unreachable-branch-transform'); |
||||||
|
|
||||||
|
if (ugly) { |
||||||
|
result = result.transform('uglifyify', { |
||||||
|
mangle: false, |
||||||
|
compress: { |
||||||
|
dead_code: false, |
||||||
|
conditionals: true, |
||||||
|
unused: false, |
||||||
|
hoist_funs: true, |
||||||
|
hoist_vars: true, |
||||||
|
negate_iife: false |
||||||
|
}, |
||||||
|
beautify: true, |
||||||
|
warnings: true |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return result.bundle() |
||||||
|
.pipe(exorcist(path.join( DEST, dst + '.js.map'))) |
||||||
|
.pipe(source(dst + '.js')) |
||||||
|
.pipe(gulp.dest( DEST )); |
||||||
|
}; |
||||||
|
|
||||||
|
var uglifyFile = function(file) { |
||||||
|
return gulp.src( DEST + file + '.js') |
||||||
|
.pipe(uglify()) |
||||||
|
.pipe(rename(file + '.min.js')) |
||||||
|
.pipe(gulp.dest( DEST )); |
||||||
|
}; |
||||||
|
|
||||||
|
gulp.task('bower', function(cb){ |
||||||
|
bower.commands.install().on('end', function (installed){ |
||||||
|
console.log(installed); |
||||||
|
cb(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('clean', ['lint'], function(cb) { |
||||||
|
del([ DEST ], cb); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('lint', function(){ |
||||||
|
return gulp.src(['./*.js', './lib/*.js']) |
||||||
|
.pipe(jshint()) |
||||||
|
.pipe(jshint.reporter('default')); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('build', ['clean'], function () { |
||||||
|
return build('index', 'ethereum', true); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('buildDev', ['clean'], function () { |
||||||
|
return build('index', 'ethereum', false); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('uglify', ['build'], function(){ |
||||||
|
return uglifyFile('ethereum'); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('uglify', ['buildDev'], function(){ |
||||||
|
return uglifyFile('ethereum'); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('watch', function() { |
||||||
|
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']); |
||||||
|
}); |
||||||
|
|
||||||
|
gulp.task('release', ['bower', 'lint', 'build', 'uglify']); |
||||||
|
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']); |
||||||
|
gulp.task('default', ['dev']); |
||||||
|
|
@ -0,0 +1,8 @@ |
|||||||
|
var web3 = require('./lib/web3'); |
||||||
|
web3.providers.WebSocketProvider = require('./lib/websocket'); |
||||||
|
web3.providers.HttpRpcProvider = require('./lib/httprpc'); |
||||||
|
web3.providers.QtProvider = require('./lib/qt'); |
||||||
|
web3.providers.AutoProvider = require('./lib/autoprovider'); |
||||||
|
web3.contract = require('./lib/contract'); |
||||||
|
|
||||||
|
module.exports = web3; |
@ -0,0 +1,267 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file abi.js |
||||||
|
* @authors: |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* Gav Wood <g@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') { |
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
} |
||||||
|
|
||||||
|
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
|
||||||
|
var hexToDec = function (hex) { |
||||||
|
return parseInt(hex, 16).toString(); |
||||||
|
}; |
||||||
|
|
||||||
|
var decToHex = function (dec) { |
||||||
|
return parseInt(dec).toString(16); |
||||||
|
}; |
||||||
|
|
||||||
|
var findIndex = function (array, callback) { |
||||||
|
var end = false; |
||||||
|
var i = 0; |
||||||
|
for (; i < array.length && !end; i++) { |
||||||
|
end = callback(array[i]); |
||||||
|
} |
||||||
|
return end ? i - 1 : -1; |
||||||
|
}; |
||||||
|
|
||||||
|
var findMethodIndex = function (json, methodName) { |
||||||
|
return findIndex(json, function (method) { |
||||||
|
return method.name === methodName; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var padLeft = function (string, chars) { |
||||||
|
return new Array(chars - string.length + 1).join("0") + string; |
||||||
|
}; |
||||||
|
|
||||||
|
var calcBitPadding = function (type, expected) { |
||||||
|
var value = type.slice(expected.length); |
||||||
|
if (value === "") { |
||||||
|
return 32; |
||||||
|
} |
||||||
|
return parseInt(value) / 8; |
||||||
|
}; |
||||||
|
|
||||||
|
var calcBytePadding = function (type, expected) { |
||||||
|
var value = type.slice(expected.length); |
||||||
|
if (value === "") { |
||||||
|
return 32; |
||||||
|
} |
||||||
|
return parseInt(value); |
||||||
|
}; |
||||||
|
|
||||||
|
var calcRealPadding = function (type, expected) { |
||||||
|
var value = type.slice(expected.length); |
||||||
|
if (value === "") { |
||||||
|
return 32; |
||||||
|
} |
||||||
|
var sizes = value.split('x'); |
||||||
|
for (var padding = 0, i = 0; i < sizes; i++) { |
||||||
|
padding += (sizes[i] / 8); |
||||||
|
} |
||||||
|
return padding; |
||||||
|
}; |
||||||
|
|
||||||
|
var setupInputTypes = function () { |
||||||
|
|
||||||
|
var prefixedType = function (prefix, calcPadding) { |
||||||
|
return function (type, value) { |
||||||
|
var expected = prefix; |
||||||
|
if (type.indexOf(expected) !== 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
var padding = calcPadding(type, expected); |
||||||
|
if (typeof value === "number") |
||||||
|
value = value.toString(16); |
||||||
|
else if (typeof value === "string") |
||||||
|
value = web3.toHex(value);
|
||||||
|
else if (value.indexOf('0x') === 0) |
||||||
|
value = value.substr(2); |
||||||
|
else |
||||||
|
value = (+value).toString(16); |
||||||
|
return padLeft(value, padding * 2); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
var namedType = function (name, padding, formatter) { |
||||||
|
return function (type, value) { |
||||||
|
if (type !== name) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return padLeft(formatter ? formatter(value) : value, padding * 2); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
var formatBool = function (value) { |
||||||
|
return value ? '0x1' : '0x0'; |
||||||
|
}; |
||||||
|
|
||||||
|
return [ |
||||||
|
prefixedType('uint', calcBitPadding), |
||||||
|
prefixedType('int', calcBitPadding), |
||||||
|
prefixedType('hash', calcBitPadding), |
||||||
|
prefixedType('string', calcBytePadding), |
||||||
|
prefixedType('real', calcRealPadding), |
||||||
|
prefixedType('ureal', calcRealPadding), |
||||||
|
namedType('address', 20), |
||||||
|
namedType('bool', 1, formatBool), |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var inputTypes = setupInputTypes(); |
||||||
|
|
||||||
|
var toAbiInput = function (json, methodName, params) { |
||||||
|
var bytes = ""; |
||||||
|
var index = findMethodIndex(json, methodName); |
||||||
|
|
||||||
|
if (index === -1) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
bytes = "0x" + padLeft(index.toString(16), 2); |
||||||
|
var method = json[index]; |
||||||
|
|
||||||
|
for (var i = 0; i < method.inputs.length; i++) { |
||||||
|
var found = false; |
||||||
|
for (var j = 0; j < inputTypes.length && !found; j++) { |
||||||
|
found = inputTypes[j](method.inputs[i].type, params[i]); |
||||||
|
} |
||||||
|
if (!found) { |
||||||
|
console.error('unsupported json type: ' + method.inputs[i].type); |
||||||
|
} |
||||||
|
bytes += found; |
||||||
|
} |
||||||
|
return bytes; |
||||||
|
}; |
||||||
|
|
||||||
|
var setupOutputTypes = function () { |
||||||
|
|
||||||
|
var prefixedType = function (prefix, calcPadding) { |
||||||
|
return function (type) { |
||||||
|
var expected = prefix; |
||||||
|
if (type.indexOf(expected) !== 0) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
var padding = calcPadding(type, expected); |
||||||
|
return padding * 2; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
var namedType = function (name, padding) { |
||||||
|
return function (type) { |
||||||
|
return name === type ? padding * 2 : -1; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
var formatInt = function (value) { |
||||||
|
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); |
||||||
|
}; |
||||||
|
|
||||||
|
var formatHash = function (value) { |
||||||
|
return "0x" + value; |
||||||
|
}; |
||||||
|
|
||||||
|
var formatBool = function (value) { |
||||||
|
return value === '1' ? true : false; |
||||||
|
}; |
||||||
|
|
||||||
|
var formatString = function (value) { |
||||||
|
return web3.toAscii(value); |
||||||
|
}; |
||||||
|
|
||||||
|
return [ |
||||||
|
{ padding: prefixedType('uint', calcBitPadding), format: formatInt }, |
||||||
|
{ padding: prefixedType('int', calcBitPadding), format: formatInt }, |
||||||
|
{ padding: prefixedType('hash', calcBitPadding), format: formatHash }, |
||||||
|
{ padding: prefixedType('string', calcBytePadding), format: formatString }, |
||||||
|
{ padding: prefixedType('real', calcRealPadding), format: formatInt }, |
||||||
|
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt }, |
||||||
|
{ padding: namedType('address', 20) }, |
||||||
|
{ padding: namedType('bool', 1), format: formatBool } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var outputTypes = setupOutputTypes(); |
||||||
|
|
||||||
|
var fromAbiOutput = function (json, methodName, output) { |
||||||
|
var index = findMethodIndex(json, methodName); |
||||||
|
|
||||||
|
if (index === -1) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
output = output.slice(2); |
||||||
|
|
||||||
|
var result = []; |
||||||
|
var method = json[index]; |
||||||
|
for (var i = 0; i < method.outputs.length; i++) { |
||||||
|
var padding = -1; |
||||||
|
for (var j = 0; j < outputTypes.length && padding === -1; j++) { |
||||||
|
padding = outputTypes[j].padding(method.outputs[i].type); |
||||||
|
} |
||||||
|
|
||||||
|
if (padding === -1) { |
||||||
|
// not found output parsing
|
||||||
|
continue; |
||||||
|
} |
||||||
|
var res = output.slice(0, padding); |
||||||
|
var formatter = outputTypes[j - 1].format; |
||||||
|
result.push(formatter ? formatter(res) : ("0x" + res)); |
||||||
|
output = output.slice(padding); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
}; |
||||||
|
|
||||||
|
var inputParser = function (json) { |
||||||
|
var parser = {}; |
||||||
|
json.forEach(function (method) { |
||||||
|
parser[method.name] = function () { |
||||||
|
var params = Array.prototype.slice.call(arguments); |
||||||
|
return toAbiInput(json, method.name, params); |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
return parser; |
||||||
|
}; |
||||||
|
|
||||||
|
var outputParser = function (json) { |
||||||
|
var parser = {}; |
||||||
|
json.forEach(function (method) { |
||||||
|
parser[method.name] = function (output) { |
||||||
|
return fromAbiOutput(json, method.name, output); |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
return parser; |
||||||
|
}; |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") { |
||||||
|
module.exports = { |
||||||
|
inputParser: inputParser, |
||||||
|
outputParser: outputParser |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file autoprovider.js |
||||||
|
* @authors: |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* Marian Oancea <marian@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
* @brief if qt object is available, uses QtProvider, |
||||||
|
* if not tries to connect over websockets |
||||||
|
* if it fails, it uses HttpRpcProvider |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') { |
||||||
|
var WebSocket = require('ws'); // jshint ignore:line
|
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
} |
||||||
|
|
||||||
|
var AutoProvider = function (userOptions) { |
||||||
|
if (web3.haveProvider()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// before we determine what provider we are, we have to cache request
|
||||||
|
this.sendQueue = []; |
||||||
|
this.onmessageQueue = []; |
||||||
|
|
||||||
|
if (navigator.qt) { |
||||||
|
this.provider = new web3.providers.QtProvider(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
userOptions = userOptions || {}; |
||||||
|
var options = { |
||||||
|
httprpc: userOptions.httprpc || 'http://localhost:8080', |
||||||
|
websockets: userOptions.websockets || 'ws://localhost:40404/eth' |
||||||
|
}; |
||||||
|
|
||||||
|
var self = this; |
||||||
|
var closeWithSuccess = function (success) { |
||||||
|
ws.close(); |
||||||
|
if (success) { |
||||||
|
self.provider = new web3.providers.WebSocketProvider(options.websockets); |
||||||
|
} else { |
||||||
|
self.provider = new web3.providers.HttpRpcProvider(options.httprpc); |
||||||
|
self.poll = self.provider.poll.bind(self.provider); |
||||||
|
} |
||||||
|
self.sendQueue.forEach(function (payload) { |
||||||
|
self.provider(payload); |
||||||
|
}); |
||||||
|
self.onmessageQueue.forEach(function (handler) { |
||||||
|
self.provider.onmessage = handler; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var ws = new WebSocket(options.websockets); |
||||||
|
|
||||||
|
ws.onopen = function() { |
||||||
|
closeWithSuccess(true); |
||||||
|
}; |
||||||
|
|
||||||
|
ws.onerror = function() { |
||||||
|
closeWithSuccess(false); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
AutoProvider.prototype.send = function (payload) { |
||||||
|
if (this.provider) { |
||||||
|
this.provider.send(payload); |
||||||
|
return; |
||||||
|
} |
||||||
|
this.sendQueue.push(payload); |
||||||
|
}; |
||||||
|
|
||||||
|
Object.defineProperty(AutoProvider.prototype, 'onmessage', { |
||||||
|
set: function (handler) { |
||||||
|
if (this.provider) { |
||||||
|
this.provider.onmessage = handler; |
||||||
|
return; |
||||||
|
} |
||||||
|
this.onmessageQueue.push(handler); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = AutoProvider; |
@ -0,0 +1,66 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file contract.js |
||||||
|
* @authors: |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') { |
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
} |
||||||
|
|
||||||
|
var abi = require('./abi'); |
||||||
|
|
||||||
|
var contract = function (address, desc) { |
||||||
|
var inputParser = abi.inputParser(desc); |
||||||
|
var outputParser = abi.outputParser(desc); |
||||||
|
|
||||||
|
var contract = {}; |
||||||
|
|
||||||
|
desc.forEach(function (method) { |
||||||
|
contract[method.name] = function () { |
||||||
|
var params = Array.prototype.slice.call(arguments); |
||||||
|
var parsed = inputParser[method.name].apply(null, params); |
||||||
|
|
||||||
|
var onSuccess = function (result) { |
||||||
|
return outputParser[method.name](result); |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
call: function (extra) { |
||||||
|
extra = extra || {}; |
||||||
|
extra.to = address; |
||||||
|
extra.data = parsed; |
||||||
|
return web3.eth.call(extra).then(onSuccess); |
||||||
|
}, |
||||||
|
transact: function (extra) { |
||||||
|
extra = extra || {}; |
||||||
|
extra.to = address; |
||||||
|
extra.data = parsed; |
||||||
|
return web3.eth.transact(extra).then(onSuccess); |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
return contract; |
||||||
|
}; |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = contract; |
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file httprpc.js |
||||||
|
* @authors: |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* Marian Oancea <marian@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') { |
||||||
|
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
||||||
|
} |
||||||
|
|
||||||
|
var HttpRpcProvider = function (host) { |
||||||
|
this.handlers = []; |
||||||
|
this.host = host; |
||||||
|
}; |
||||||
|
|
||||||
|
function formatJsonRpcObject(object) { |
||||||
|
return { |
||||||
|
jsonrpc: '2.0', |
||||||
|
method: object.call, |
||||||
|
params: object.args, |
||||||
|
id: object._id |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function formatJsonRpcMessage(message) { |
||||||
|
var object = JSON.parse(message); |
||||||
|
|
||||||
|
return { |
||||||
|
_id: object.id, |
||||||
|
data: object.result, |
||||||
|
error: object.error |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
HttpRpcProvider.prototype.sendRequest = function (payload, cb) { |
||||||
|
var data = formatJsonRpcObject(payload); |
||||||
|
|
||||||
|
var request = new XMLHttpRequest(); |
||||||
|
request.open("POST", this.host, true); |
||||||
|
request.send(JSON.stringify(data)); |
||||||
|
request.onreadystatechange = function () { |
||||||
|
if (request.readyState === 4 && cb) { |
||||||
|
cb(request); |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
HttpRpcProvider.prototype.send = function (payload) { |
||||||
|
var self = this; |
||||||
|
this.sendRequest(payload, function (request) { |
||||||
|
self.handlers.forEach(function (handler) { |
||||||
|
handler.call(self, formatJsonRpcMessage(request.responseText)); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
HttpRpcProvider.prototype.poll = function (payload, id) { |
||||||
|
var self = this; |
||||||
|
this.sendRequest(payload, function (request) { |
||||||
|
var parsed = JSON.parse(request.responseText); |
||||||
|
if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
self.handlers.forEach(function (handler) { |
||||||
|
handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { |
||||||
|
set: function (handler) { |
||||||
|
this.handlers.push(handler); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = HttpRpcProvider; |
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file qt.js |
||||||
|
* @authors: |
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com> |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
var QtProvider = function() { |
||||||
|
this.handlers = []; |
||||||
|
|
||||||
|
var self = this; |
||||||
|
navigator.qt.onmessage = function (message) { |
||||||
|
self.handlers.forEach(function (handler) { |
||||||
|
handler.call(self, JSON.parse(message.data)); |
||||||
|
}); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
QtProvider.prototype.send = function(payload) { |
||||||
|
navigator.qt.postMessage(JSON.stringify(payload)); |
||||||
|
}; |
||||||
|
|
||||||
|
Object.defineProperty(QtProvider.prototype, "onmessage", { |
||||||
|
set: function(handler) { |
||||||
|
this.handlers.push(handler); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = QtProvider; |
@ -0,0 +1,509 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file main.js |
||||||
|
* @authors: |
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com> |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* Marian Oancea <marian@ethdev.com> |
||||||
|
* Gav Wood <g@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
function flattenPromise (obj) { |
||||||
|
if (obj instanceof Promise) { |
||||||
|
return Promise.resolve(obj); |
||||||
|
} |
||||||
|
|
||||||
|
if (obj instanceof Array) { |
||||||
|
return new Promise(function (resolve) { |
||||||
|
var promises = obj.map(function (o) { |
||||||
|
return flattenPromise(o); |
||||||
|
}); |
||||||
|
|
||||||
|
return Promise.all(promises).then(function (res) { |
||||||
|
for (var i = 0; i < obj.length; i++) { |
||||||
|
obj[i] = res[i]; |
||||||
|
} |
||||||
|
resolve(obj); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
if (obj instanceof Object) { |
||||||
|
return new Promise(function (resolve) { |
||||||
|
var keys = Object.keys(obj); |
||||||
|
var promises = keys.map(function (key) { |
||||||
|
return flattenPromise(obj[key]); |
||||||
|
}); |
||||||
|
|
||||||
|
return Promise.all(promises).then(function (res) { |
||||||
|
for (var i = 0; i < keys.length; i++) { |
||||||
|
obj[keys[i]] = res[i]; |
||||||
|
} |
||||||
|
resolve(obj); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return Promise.resolve(obj); |
||||||
|
} |
||||||
|
|
||||||
|
var web3Methods = function () { |
||||||
|
return [ |
||||||
|
{ name: 'sha3', call: 'web3_sha3' } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var ethMethods = function () { |
||||||
|
var blockCall = function (args) { |
||||||
|
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; |
||||||
|
}; |
||||||
|
|
||||||
|
var transactionCall = function (args) { |
||||||
|
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; |
||||||
|
}; |
||||||
|
|
||||||
|
var uncleCall = function (args) { |
||||||
|
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; |
||||||
|
}; |
||||||
|
|
||||||
|
var methods = [ |
||||||
|
{ name: 'balanceAt', call: 'eth_balanceAt' }, |
||||||
|
{ name: 'stateAt', call: 'eth_stateAt' }, |
||||||
|
{ name: 'storageAt', call: 'eth_storageAt' }, |
||||||
|
{ name: 'countAt', call: 'eth_countAt'}, |
||||||
|
{ name: 'codeAt', call: 'eth_codeAt' }, |
||||||
|
{ name: 'transact', call: 'eth_transact' }, |
||||||
|
{ name: 'call', call: 'eth_call' }, |
||||||
|
{ name: 'block', call: blockCall }, |
||||||
|
{ name: 'transaction', call: transactionCall }, |
||||||
|
{ name: 'uncle', call: uncleCall }, |
||||||
|
{ name: 'compilers', call: 'eth_compilers' }, |
||||||
|
{ name: 'lll', call: 'eth_lll' }, |
||||||
|
{ name: 'solidity', call: 'eth_solidity' }, |
||||||
|
{ name: 'serpent', call: 'eth_serpent' }, |
||||||
|
{ name: 'logs', call: 'eth_logs' } |
||||||
|
]; |
||||||
|
return methods; |
||||||
|
}; |
||||||
|
|
||||||
|
var ethProperties = function () { |
||||||
|
return [ |
||||||
|
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, |
||||||
|
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, |
||||||
|
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, |
||||||
|
{ name: 'gasPrice', getter: 'eth_gasPrice' }, |
||||||
|
{ name: 'account', getter: 'eth_account' }, |
||||||
|
{ name: 'accounts', getter: 'eth_accounts' }, |
||||||
|
{ name: 'peerCount', getter: 'eth_peerCount' }, |
||||||
|
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, |
||||||
|
{ name: 'number', getter: 'eth_number'} |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var dbMethods = function () { |
||||||
|
return [ |
||||||
|
{ name: 'put', call: 'db_put' }, |
||||||
|
{ name: 'get', call: 'db_get' }, |
||||||
|
{ name: 'putString', call: 'db_putString' }, |
||||||
|
{ name: 'getString', call: 'db_getString' } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var shhMethods = function () { |
||||||
|
return [ |
||||||
|
{ name: 'post', call: 'shh_post' }, |
||||||
|
{ name: 'newIdentity', call: 'shh_newIdentity' }, |
||||||
|
{ name: 'haveIdentity', call: 'shh_haveIdentity' }, |
||||||
|
{ name: 'newGroup', call: 'shh_newGroup' }, |
||||||
|
{ name: 'addToGroup', call: 'shh_addToGroup' } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var ethWatchMethods = function () { |
||||||
|
var newFilter = function (args) { |
||||||
|
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; |
||||||
|
}; |
||||||
|
|
||||||
|
return [ |
||||||
|
{ name: 'newFilter', call: newFilter }, |
||||||
|
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' }, |
||||||
|
{ name: 'getMessages', call: 'eth_filterLogs' } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var shhWatchMethods = function () { |
||||||
|
return [ |
||||||
|
{ name: 'newFilter', call: 'shh_newFilter' }, |
||||||
|
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' }, |
||||||
|
{ name: 'getMessages', call: 'shh_getMessages' } |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
var setupMethods = function (obj, methods) { |
||||||
|
methods.forEach(function (method) { |
||||||
|
obj[method.name] = function () { |
||||||
|
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { |
||||||
|
var call = typeof method.call === "function" ? method.call(args) : method.call; |
||||||
|
return {call: call, args: args}; |
||||||
|
}).then(function (request) { |
||||||
|
return new Promise(function (resolve, reject) { |
||||||
|
web3.provider.send(request, function (err, result) { |
||||||
|
if (!err) { |
||||||
|
resolve(result); |
||||||
|
return; |
||||||
|
} |
||||||
|
reject(err); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}).catch(function(err) { |
||||||
|
console.error(err); |
||||||
|
}); |
||||||
|
}; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var setupProperties = function (obj, properties) { |
||||||
|
properties.forEach(function (property) { |
||||||
|
var proto = {}; |
||||||
|
proto.get = function () { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
web3.provider.send({call: property.getter}, function(err, result) { |
||||||
|
if (!err) { |
||||||
|
resolve(result); |
||||||
|
return; |
||||||
|
} |
||||||
|
reject(err); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
if (property.setter) { |
||||||
|
proto.set = function (val) { |
||||||
|
return flattenPromise([val]).then(function (args) { |
||||||
|
return new Promise(function (resolve) { |
||||||
|
web3.provider.send({call: property.setter, args: args}, function (err, result) { |
||||||
|
if (!err) { |
||||||
|
resolve(result); |
||||||
|
return; |
||||||
|
} |
||||||
|
reject(err); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}).catch(function (err) { |
||||||
|
console.error(err); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
Object.defineProperty(obj, property.name, proto); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// TODO: import from a dependency, don't duplicate.
|
||||||
|
var hexToDec = function (hex) { |
||||||
|
return parseInt(hex, 16).toString(); |
||||||
|
}; |
||||||
|
|
||||||
|
var decToHex = function (dec) { |
||||||
|
return parseInt(dec).toString(16); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
var web3 = { |
||||||
|
_callbacks: {}, |
||||||
|
_events: {}, |
||||||
|
providers: {}, |
||||||
|
|
||||||
|
toHex: function(str) { |
||||||
|
var hex = ""; |
||||||
|
for(var i = 0; i < str.length; i++) { |
||||||
|
var n = str.charCodeAt(i).toString(16); |
||||||
|
hex += n.length < 2 ? '0' + n : n; |
||||||
|
} |
||||||
|
|
||||||
|
return hex; |
||||||
|
}, |
||||||
|
|
||||||
|
toAscii: function(hex) { |
||||||
|
// Find termination
|
||||||
|
var str = ""; |
||||||
|
var i = 0, l = hex.length; |
||||||
|
if (hex.substring(0, 2) === '0x') |
||||||
|
i = 2; |
||||||
|
for(; i < l; i+=2) { |
||||||
|
var code = hex.charCodeAt(i); |
||||||
|
if(code === 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); |
||||||
|
} |
||||||
|
|
||||||
|
return str; |
||||||
|
}, |
||||||
|
|
||||||
|
fromAscii: function(str, pad) { |
||||||
|
pad = pad === undefined ? 32 : pad; |
||||||
|
var hex = this.toHex(str); |
||||||
|
while(hex.length < pad*2) |
||||||
|
hex += "00"; |
||||||
|
return "0x" + hex; |
||||||
|
}, |
||||||
|
|
||||||
|
toDecimal: function (val) { |
||||||
|
return hexToDec(val.substring(2)); |
||||||
|
}, |
||||||
|
|
||||||
|
fromDecimal: function (val) { |
||||||
|
return "0x" + decToHex(val); |
||||||
|
}, |
||||||
|
|
||||||
|
toEth: function(str) { |
||||||
|
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; |
||||||
|
var unit = 0; |
||||||
|
var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ]; |
||||||
|
while (val > 3000 && unit < units.length - 1) |
||||||
|
{ |
||||||
|
val /= 1000; |
||||||
|
unit++; |
||||||
|
} |
||||||
|
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); |
||||||
|
var replaceFunction = function($0, $1, $2) { |
||||||
|
return $1 + ',' + $2; |
||||||
|
}; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
var o = s; |
||||||
|
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); |
||||||
|
if (o === s) |
||||||
|
break; |
||||||
|
} |
||||||
|
return s + ' ' + units[unit]; |
||||||
|
}, |
||||||
|
|
||||||
|
eth: { |
||||||
|
prototype: Object(), // jshint ignore:line
|
||||||
|
watch: function (params) { |
||||||
|
return new Filter(params, ethWatch); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
db: { |
||||||
|
prototype: Object() // jshint ignore:line
|
||||||
|
}, |
||||||
|
|
||||||
|
shh: { |
||||||
|
prototype: Object(), // jshint ignore:line
|
||||||
|
watch: function (params) { |
||||||
|
return new Filter(params, shhWatch); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
on: function(event, id, cb) { |
||||||
|
if(web3._events[event] === undefined) { |
||||||
|
web3._events[event] = {}; |
||||||
|
} |
||||||
|
|
||||||
|
web3._events[event][id] = cb; |
||||||
|
return this; |
||||||
|
}, |
||||||
|
|
||||||
|
off: function(event, id) { |
||||||
|
if(web3._events[event] !== undefined) { |
||||||
|
delete web3._events[event][id]; |
||||||
|
} |
||||||
|
|
||||||
|
return this; |
||||||
|
}, |
||||||
|
|
||||||
|
trigger: function(event, id, data) { |
||||||
|
var callbacks = web3._events[event]; |
||||||
|
if (!callbacks || !callbacks[id]) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var cb = callbacks[id]; |
||||||
|
cb(data); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
setupMethods(web3, web3Methods()); |
||||||
|
setupMethods(web3.eth, ethMethods()); |
||||||
|
setupProperties(web3.eth, ethProperties()); |
||||||
|
setupMethods(web3.db, dbMethods()); |
||||||
|
setupMethods(web3.shh, shhMethods()); |
||||||
|
|
||||||
|
var ethWatch = { |
||||||
|
changed: 'eth_changed' |
||||||
|
}; |
||||||
|
setupMethods(ethWatch, ethWatchMethods()); |
||||||
|
var shhWatch = { |
||||||
|
changed: 'shh_changed' |
||||||
|
}; |
||||||
|
setupMethods(shhWatch, shhWatchMethods()); |
||||||
|
|
||||||
|
var ProviderManager = function() { |
||||||
|
this.queued = []; |
||||||
|
this.polls = []; |
||||||
|
this.ready = false; |
||||||
|
this.provider = undefined; |
||||||
|
this.id = 1; |
||||||
|
|
||||||
|
var self = this; |
||||||
|
var poll = function () { |
||||||
|
if (self.provider && self.provider.poll) { |
||||||
|
self.polls.forEach(function (data) { |
||||||
|
data.data._id = self.id; |
||||||
|
self.id++; |
||||||
|
self.provider.poll(data.data, data.id); |
||||||
|
}); |
||||||
|
} |
||||||
|
setTimeout(poll, 12000); |
||||||
|
}; |
||||||
|
poll(); |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.send = function(data, cb) { |
||||||
|
data._id = this.id; |
||||||
|
if (cb) { |
||||||
|
web3._callbacks[data._id] = cb; |
||||||
|
} |
||||||
|
|
||||||
|
data.args = data.args || []; |
||||||
|
this.id++; |
||||||
|
|
||||||
|
if(this.provider !== undefined) { |
||||||
|
this.provider.send(data); |
||||||
|
} else { |
||||||
|
console.warn("provider is not set"); |
||||||
|
this.queued.push(data); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.set = function(provider) { |
||||||
|
if(this.provider !== undefined && this.provider.unload !== undefined) { |
||||||
|
this.provider.unload(); |
||||||
|
} |
||||||
|
|
||||||
|
this.provider = provider; |
||||||
|
this.ready = true; |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.sendQueued = function() { |
||||||
|
for(var i = 0; this.queued.length; i++) { |
||||||
|
// Resend
|
||||||
|
this.send(this.queued[i]); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.installed = function() { |
||||||
|
return this.provider !== undefined; |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.startPolling = function (data, pollId) { |
||||||
|
if (!this.provider || !this.provider.poll) { |
||||||
|
return; |
||||||
|
} |
||||||
|
this.polls.push({data: data, id: pollId}); |
||||||
|
}; |
||||||
|
|
||||||
|
ProviderManager.prototype.stopPolling = function (pollId) { |
||||||
|
for (var i = this.polls.length; i--;) { |
||||||
|
var poll = this.polls[i]; |
||||||
|
if (poll.id === pollId) { |
||||||
|
this.polls.splice(i, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
web3.provider = new ProviderManager(); |
||||||
|
|
||||||
|
web3.setProvider = function(provider) { |
||||||
|
provider.onmessage = messageHandler; |
||||||
|
web3.provider.set(provider); |
||||||
|
web3.provider.sendQueued(); |
||||||
|
}; |
||||||
|
|
||||||
|
web3.haveProvider = function() { |
||||||
|
return !!web3.provider.provider; |
||||||
|
}; |
||||||
|
|
||||||
|
var Filter = function(options, impl) { |
||||||
|
this.impl = impl; |
||||||
|
this.callbacks = []; |
||||||
|
|
||||||
|
var self = this; |
||||||
|
this.promise = impl.newFilter(options); |
||||||
|
this.promise.then(function (id) { |
||||||
|
self.id = id; |
||||||
|
web3.on(impl.changed, id, self.trigger.bind(self)); |
||||||
|
web3.provider.startPolling({call: impl.changed, args: [id]}, id); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.arrived = function(callback) { |
||||||
|
this.changed(callback); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) { |
||||||
|
var self = this; |
||||||
|
this.promise.then(function(id) { |
||||||
|
self.callbacks.push(callback); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.trigger = function(messages) { |
||||||
|
for(var i = 0; i < this.callbacks.length; i++) { |
||||||
|
this.callbacks[i].call(this, messages); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() { |
||||||
|
var self = this; |
||||||
|
this.promise.then(function (id) { |
||||||
|
self.impl.uninstallFilter(id); |
||||||
|
web3.provider.stopPolling(id); |
||||||
|
web3.off(impl.changed, id); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.messages = function() { |
||||||
|
var self = this; |
||||||
|
return this.promise.then(function (id) { |
||||||
|
return self.impl.getMessages(id); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.logs = function () { |
||||||
|
return this.messages(); |
||||||
|
}; |
||||||
|
|
||||||
|
function messageHandler(data) { |
||||||
|
if(data._event !== undefined) { |
||||||
|
web3.trigger(data._event, data._id, data.data); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(data._id) { |
||||||
|
var cb = web3._callbacks[data._id]; |
||||||
|
if (cb) { |
||||||
|
cb.call(this, data.error, data.data); |
||||||
|
delete web3._callbacks[data._id]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = web3; |
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
This file is part of ethereum.js. |
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** @file websocket.js |
||||||
|
* @authors: |
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com> |
||||||
|
* Marek Kotewicz <marek@ethdev.com> |
||||||
|
* Marian Oancea <marian@ethdev.com> |
||||||
|
* @date 2014 |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') { |
||||||
|
var WebSocket = require('ws'); // jshint ignore:line
|
||||||
|
} |
||||||
|
|
||||||
|
var WebSocketProvider = function(host) { |
||||||
|
// onmessage handlers
|
||||||
|
this.handlers = []; |
||||||
|
// queue will be filled with messages if send is invoked before the ws is ready
|
||||||
|
this.queued = []; |
||||||
|
this.ready = false; |
||||||
|
|
||||||
|
this.ws = new WebSocket(host); |
||||||
|
|
||||||
|
var self = this; |
||||||
|
this.ws.onmessage = function(event) { |
||||||
|
for(var i = 0; i < self.handlers.length; i++) { |
||||||
|
self.handlers[i].call(self, JSON.parse(event.data), event); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.ws.onopen = function() { |
||||||
|
self.ready = true; |
||||||
|
|
||||||
|
for(var i = 0; i < self.queued.length; i++) { |
||||||
|
// Resend
|
||||||
|
self.send(self.queued[i]); |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
WebSocketProvider.prototype.send = function(payload) { |
||||||
|
if(this.ready) { |
||||||
|
var data = JSON.stringify(payload); |
||||||
|
|
||||||
|
this.ws.send(data); |
||||||
|
} else { |
||||||
|
this.queued.push(payload); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
WebSocketProvider.prototype.onMessage = function(handler) { |
||||||
|
this.handlers.push(handler); |
||||||
|
}; |
||||||
|
|
||||||
|
WebSocketProvider.prototype.unload = function() { |
||||||
|
this.ws.close(); |
||||||
|
}; |
||||||
|
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { |
||||||
|
set: function(provider) { this.onMessage(provider); } |
||||||
|
}); |
||||||
|
|
||||||
|
if (typeof(module) !== "undefined") |
||||||
|
module.exports = WebSocketProvider; |
@ -0,0 +1,67 @@ |
|||||||
|
{ |
||||||
|
"name": "ethereum.js", |
||||||
|
"namespace": "ethereum", |
||||||
|
"version": "0.0.6", |
||||||
|
"description": "Ethereum Compatible JavaScript API", |
||||||
|
"main": "./index.js", |
||||||
|
"directories": { |
||||||
|
"lib": "./lib" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"es6-promise": "*", |
||||||
|
"ws": "*", |
||||||
|
"xmlhttprequest": "*" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"bower": ">=1.3.0", |
||||||
|
"browserify": ">=6.0", |
||||||
|
"del": ">=0.1.1", |
||||||
|
"envify": "^3.0.0", |
||||||
|
"exorcist": "^0.1.6", |
||||||
|
"gulp": ">=3.4.0", |
||||||
|
"gulp-jshint": ">=1.5.0", |
||||||
|
"gulp-rename": ">=1.2.0", |
||||||
|
"gulp-uglify": ">=1.0.0", |
||||||
|
"jshint": ">=2.5.0", |
||||||
|
"uglifyify": "^2.6.0", |
||||||
|
"unreachable-branch-transform": "^0.1.0", |
||||||
|
"vinyl-source-stream": "^1.0.0" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"build": "gulp", |
||||||
|
"watch": "gulp watch", |
||||||
|
"lint": "gulp lint" |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "https://github.com/ethereum/ethereum.js.git" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/ethereum/ethereum.js", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/ethereum/ethereum.js/issues" |
||||||
|
}, |
||||||
|
"keywords": [ |
||||||
|
"ethereum", |
||||||
|
"javascript", |
||||||
|
"API" |
||||||
|
], |
||||||
|
"author": "ethdev.com", |
||||||
|
"authors": [ |
||||||
|
{ |
||||||
|
"name": "Jeffery Wilcke", |
||||||
|
"email": "jeff@ethdev.com", |
||||||
|
"url": "https://github.com/obscuren" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Marek Kotewicz", |
||||||
|
"email": "marek@ethdev.com", |
||||||
|
"url": "https://github.com/debris" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Marian Oancea", |
||||||
|
"email": "marian@ethdev.com", |
||||||
|
"url": "https://github.com/cubedro" |
||||||
|
} |
||||||
|
], |
||||||
|
"license": "LGPL-3.0" |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/* |
||||||
|
This file is part of go-ethereum |
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
go-ethereum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @authors |
||||||
|
* Gustav Simonsson <gustav.simonsson@gmail.com> |
||||||
|
* @date 2015 |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
package crypto |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"code.google.com/p/go-uuid/uuid" |
||||||
|
"crypto/ecdsa" |
||||||
|
"crypto/elliptic" |
||||||
|
"encoding/json" |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
type Key struct { |
||||||
|
Id *uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||||
|
// we only store privkey as pubkey/address can be derived from it
|
||||||
|
// privkey in this struct is always in plaintext
|
||||||
|
PrivateKey *ecdsa.PrivateKey |
||||||
|
} |
||||||
|
|
||||||
|
type plainKeyJSON struct { |
||||||
|
Id []byte |
||||||
|
PrivateKey []byte |
||||||
|
} |
||||||
|
|
||||||
|
type cipherJSON struct { |
||||||
|
Salt []byte |
||||||
|
IV []byte |
||||||
|
CipherText []byte |
||||||
|
} |
||||||
|
|
||||||
|
type encryptedKeyJSON struct { |
||||||
|
Id []byte |
||||||
|
Crypto cipherJSON |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Key) Address() []byte { |
||||||
|
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) |
||||||
|
return Sha3(pubBytes)[12:] |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Key) MarshalJSON() (j []byte, err error) { |
||||||
|
jStruct := plainKeyJSON{ |
||||||
|
*k.Id, |
||||||
|
FromECDSA(k.PrivateKey), |
||||||
|
} |
||||||
|
j, err = json.Marshal(jStruct) |
||||||
|
return j, err |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Key) UnmarshalJSON(j []byte) (err error) { |
||||||
|
keyJSON := new(plainKeyJSON) |
||||||
|
err = json.Unmarshal(j, &keyJSON) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
u := new(uuid.UUID) |
||||||
|
*u = keyJSON.Id |
||||||
|
k.Id = u |
||||||
|
|
||||||
|
k.PrivateKey = ToECDSA(keyJSON.PrivateKey) |
||||||
|
|
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func NewKey(rand io.Reader) *Key { |
||||||
|
randBytes := make([]byte, 32) |
||||||
|
_, err := rand.Read(randBytes) |
||||||
|
if err != nil { |
||||||
|
panic("key generation: could not read from random source: " + err.Error()) |
||||||
|
} |
||||||
|
reader := bytes.NewReader(randBytes) |
||||||
|
_, x, y, err := elliptic.GenerateKey(S256(), reader) |
||||||
|
if err != nil { |
||||||
|
panic("key generation: elliptic.GenerateKey failed: " + err.Error()) |
||||||
|
} |
||||||
|
privateKeyMarshalled := elliptic.Marshal(S256(), x, y) |
||||||
|
privateKeyECDSA := ToECDSA(privateKeyMarshalled) |
||||||
|
|
||||||
|
key := new(Key) |
||||||
|
id := uuid.NewRandom() |
||||||
|
key.Id = &id |
||||||
|
key.PrivateKey = privateKeyECDSA |
||||||
|
return key |
||||||
|
} |
@ -0,0 +1,245 @@ |
|||||||
|
/* |
||||||
|
This file is part of go-ethereum |
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
go-ethereum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @authors |
||||||
|
* Gustav Simonsson <gustav.simonsson@gmail.com> |
||||||
|
* @date 2015 |
||||||
|
* |
||||||
|
*/ |
||||||
|
/* |
||||||
|
|
||||||
|
This key store behaves as KeyStorePlain with the difference that |
||||||
|
the private key is encrypted and on disk uses another JSON encoding. |
||||||
|
|
||||||
|
Cryptography: |
||||||
|
|
||||||
|
1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters |
||||||
|
(work factors) [1][2] are defined as constants below. |
||||||
|
2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. |
||||||
|
3. Checksum is SHA3 of the private key bytes. |
||||||
|
4. Plaintext is concatenation of private key bytes and checksum. |
||||||
|
5. Encryption algo is AES 256 CBC [3][4] |
||||||
|
6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. |
||||||
|
7. Plaintext padding is PKCS #7 [5][6] |
||||||
|
|
||||||
|
Encoding: |
||||||
|
|
||||||
|
1. On disk, ciphertext, salt and IV are encoded in a nested JSON object. |
||||||
|
cat a key file to see the structure. |
||||||
|
2. byte arrays are base64 JSON strings. |
||||||
|
3. The EC private key bytes are in uncompressed form [7]. |
||||||
|
They are a big-endian byte slice of the absolute value of D [8][9]. |
||||||
|
4. The checksum is the last 32 bytes of the plaintext byte array and the |
||||||
|
private key is the preceeding bytes. |
||||||
|
|
||||||
|
References: |
||||||
|
|
||||||
|
1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf
|
||||||
|
2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors
|
||||||
|
3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||||
|
4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
|
||||||
|
5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||||
|
6. http://tools.ietf.org/html/rfc2315
|
||||||
|
7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key
|
||||||
|
8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey
|
||||||
|
9. https://golang.org/pkg/math/big/#Int.Bytes
|
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
package crypto |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"code.google.com/p/go-uuid/uuid" |
||||||
|
"code.google.com/p/go.crypto/scrypt" |
||||||
|
"crypto/aes" |
||||||
|
"crypto/cipher" |
||||||
|
crand "crypto/rand" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
|
||||||
|
scryptN = 1 << 18 |
||||||
|
scryptr = 8 |
||||||
|
scryptp = 1 |
||||||
|
scryptdkLen = 32 |
||||||
|
) |
||||||
|
|
||||||
|
type keyStorePassphrase struct { |
||||||
|
keysDirPath string |
||||||
|
} |
||||||
|
|
||||||
|
func NewKeyStorePassphrase(path string) KeyStore2 { |
||||||
|
return &keyStorePassphrase{path} |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { |
||||||
|
return GenerateNewKeyDefault(ks, rand, auth) |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { |
||||||
|
keyBytes, err := DecryptKey(ks, keyId, auth) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
key = &Key{ |
||||||
|
Id: keyId, |
||||||
|
PrivateKey: ToECDSA(keyBytes), |
||||||
|
} |
||||||
|
return key, err |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { |
||||||
|
authArray := []byte(auth) |
||||||
|
salt := getEntropyCSPRNG(32) |
||||||
|
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
keyBytes := FromECDSA(key.PrivateKey) |
||||||
|
keyBytesHash := Sha3(keyBytes) |
||||||
|
toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...)) |
||||||
|
|
||||||
|
AES256Block, err := aes.NewCipher(derivedKey) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
iv := getEntropyCSPRNG(aes.BlockSize) // 16
|
||||||
|
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) |
||||||
|
cipherText := make([]byte, len(toEncrypt)) |
||||||
|
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) |
||||||
|
|
||||||
|
cipherStruct := cipherJSON{ |
||||||
|
salt, |
||||||
|
iv, |
||||||
|
cipherText, |
||||||
|
} |
||||||
|
keyStruct := encryptedKeyJSON{ |
||||||
|
*key.Id, |
||||||
|
cipherStruct, |
||||||
|
} |
||||||
|
keyJSON, err := json.Marshal(keyStruct) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { |
||||||
|
// only delete if correct passphrase is given
|
||||||
|
_, err = DecryptKey(ks, keyId, auth) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
keyDirPath := path.Join(ks.keysDirPath, keyId.String()) |
||||||
|
return os.RemoveAll(keyDirPath) |
||||||
|
} |
||||||
|
|
||||||
|
func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) { |
||||||
|
fileContent, err := GetKeyFile(ks.keysDirPath, keyId) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
keyProtected := new(encryptedKeyJSON) |
||||||
|
err = json.Unmarshal(fileContent, keyProtected) |
||||||
|
|
||||||
|
salt := keyProtected.Crypto.Salt |
||||||
|
|
||||||
|
iv := keyProtected.Crypto.IV |
||||||
|
|
||||||
|
cipherText := keyProtected.Crypto.CipherText |
||||||
|
|
||||||
|
authArray := []byte(auth) |
||||||
|
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
AES256Block, err := aes.NewCipher(derivedKey) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) |
||||||
|
paddedPlainText := make([]byte, len(cipherText)) |
||||||
|
AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText) |
||||||
|
|
||||||
|
plainText := PKCS7Unpad(paddedPlainText) |
||||||
|
if plainText == nil { |
||||||
|
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
keyBytes = plainText[:len(plainText)-32] |
||||||
|
keyBytesHash := plainText[len(plainText)-32:] |
||||||
|
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { |
||||||
|
err = errors.New("Decryption failed: checksum mismatch") |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return keyBytes, err |
||||||
|
} |
||||||
|
|
||||||
|
func getEntropyCSPRNG(n int) []byte { |
||||||
|
mainBuff := make([]byte, n) |
||||||
|
_, err := io.ReadFull(crand.Reader, mainBuff) |
||||||
|
if err != nil { |
||||||
|
panic("key generation: reading from crypto/rand failed: " + err.Error()) |
||||||
|
} |
||||||
|
return mainBuff |
||||||
|
} |
||||||
|
|
||||||
|
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||||
|
func PKCS7Pad(in []byte) []byte { |
||||||
|
padding := 16 - (len(in) % 16) |
||||||
|
if padding == 0 { |
||||||
|
padding = 16 |
||||||
|
} |
||||||
|
for i := 0; i < padding; i++ { |
||||||
|
in = append(in, byte(padding)) |
||||||
|
} |
||||||
|
return in |
||||||
|
} |
||||||
|
|
||||||
|
func PKCS7Unpad(in []byte) []byte { |
||||||
|
if len(in) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
padding := in[len(in)-1] |
||||||
|
if int(padding) > len(in) || padding > aes.BlockSize { |
||||||
|
return nil |
||||||
|
} else if padding == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { |
||||||
|
if in[i] != padding { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
return in[:len(in)-int(padding)] |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
This file is part of go-ethereum |
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
go-ethereum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @authors |
||||||
|
* Gustav Simonsson <gustav.simonsson@gmail.com> |
||||||
|
* @date 2015 |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
package crypto |
||||||
|
|
||||||
|
import ( |
||||||
|
"code.google.com/p/go-uuid/uuid" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"os/user" |
||||||
|
"path" |
||||||
|
) |
||||||
|
|
||||||
|
// TODO: rename to KeyStore when replacing existing KeyStore
|
||||||
|
type KeyStore2 interface { |
||||||
|
// create new key using io.Reader entropy source and optionally using auth string
|
||||||
|
GenerateNewKey(io.Reader, string) (*Key, error) |
||||||
|
GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string
|
||||||
|
StoreKey(*Key, string) error // store key optionally using auth string
|
||||||
|
DeleteKey(*uuid.UUID, string) error // delete key by id and auth string
|
||||||
|
} |
||||||
|
|
||||||
|
type keyStorePlain struct { |
||||||
|
keysDirPath string |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: copied from cmd/ethereum/flags.go
|
||||||
|
func DefaultDataDir() string { |
||||||
|
usr, _ := user.Current() |
||||||
|
return path.Join(usr.HomeDir, ".ethereum") |
||||||
|
} |
||||||
|
|
||||||
|
func NewKeyStorePlain(path string) KeyStore2 { |
||||||
|
return &keyStorePlain{path} |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { |
||||||
|
return GenerateNewKeyDefault(ks, rand, auth) |
||||||
|
} |
||||||
|
|
||||||
|
func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key, err error) { |
||||||
|
defer func() { |
||||||
|
if r := recover(); r != nil { |
||||||
|
err = fmt.Errorf("GenerateNewKey error: %v", r) |
||||||
|
} |
||||||
|
}() |
||||||
|
key = NewKey(rand) |
||||||
|
err = ks.StoreKey(key, auth) |
||||||
|
return key, err |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { |
||||||
|
fileContent, err := GetKeyFile(ks.keysDirPath, keyId) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
key = new(Key) |
||||||
|
err = json.Unmarshal(fileContent, key) |
||||||
|
return key, err |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) { |
||||||
|
keyJSON, err := json.Marshal(key) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) { |
||||||
|
keyDirPath := path.Join(ks.keysDirPath, keyId.String()) |
||||||
|
err = os.RemoveAll(keyDirPath) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { |
||||||
|
id := keyId.String() |
||||||
|
return ioutil.ReadFile(path.Join(keysDirPath, id, id)) |
||||||
|
} |
||||||
|
|
||||||
|
func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) { |
||||||
|
keyDirPath := path.Join(keysDirPath, id) |
||||||
|
keyFilePath := path.Join(keyDirPath, id) |
||||||
|
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
|
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
|
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package crypto |
||||||
|
|
||||||
|
import ( |
||||||
|
crand "crypto/rand" |
||||||
|
"reflect" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestKeyStorePlain(t *testing.T) { |
||||||
|
ks := NewKeyStorePlain(DefaultDataDir()) |
||||||
|
pass := "" // not used but required by API
|
||||||
|
k1, err := ks.GenerateNewKey(crand.Reader, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
k2 := new(Key) |
||||||
|
k2, err = ks.GetKey(k1.Id, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
if !reflect.DeepEqual(k1.Id, k2.Id) { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
err = ks.DeleteKey(k2.Id, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestKeyStorePassphrase(t *testing.T) { |
||||||
|
ks := NewKeyStorePassphrase(DefaultDataDir()) |
||||||
|
pass := "foo" |
||||||
|
k1, err := ks.GenerateNewKey(crand.Reader, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
k2 := new(Key) |
||||||
|
k2, err = ks.GetKey(k1.Id, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
if !reflect.DeepEqual(k1.Id, k2.Id) { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
err = ks.DeleteKey(k2.Id, pass) // also to clean up created files
|
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestKeyStorePassphraseDecryptionFail(t *testing.T) { |
||||||
|
ks := NewKeyStorePassphrase(DefaultDataDir()) |
||||||
|
pass := "foo" |
||||||
|
k1, err := ks.GenerateNewKey(crand.Reader, pass) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase
|
||||||
|
if err == nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase
|
||||||
|
if err == nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
err = ks.DeleteKey(k1.Id, pass) // to clean up
|
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
@ -1,12 +0,0 @@ |
|||||||
package ar |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/trie" |
|
||||||
) |
|
||||||
|
|
||||||
type Block interface { |
|
||||||
Trie() *trie.Trie |
|
||||||
Diff() *big.Int |
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
package ar |
|
||||||
|
|
||||||
import "math/big" |
|
||||||
|
|
||||||
const lenops int64 = 9 |
|
||||||
|
|
||||||
type OpsFunc func(a, b *big.Int) *big.Int |
|
||||||
|
|
||||||
var ops [lenops]OpsFunc |
|
||||||
|
|
||||||
func init() { |
|
||||||
ops[0] = Add |
|
||||||
ops[1] = Mul |
|
||||||
ops[2] = Mod |
|
||||||
ops[3] = Xor |
|
||||||
ops[4] = And |
|
||||||
ops[5] = Or |
|
||||||
ops[6] = Sub1 |
|
||||||
ops[7] = XorSub |
|
||||||
ops[8] = Rsh |
|
||||||
} |
|
||||||
|
|
||||||
func Add(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Add(x, y) |
|
||||||
} |
|
||||||
func Mul(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Mul(x, y) |
|
||||||
} |
|
||||||
func Mod(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Mod(x, y) |
|
||||||
} |
|
||||||
func Xor(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Xor(x, y) |
|
||||||
} |
|
||||||
func And(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).And(x, y) |
|
||||||
} |
|
||||||
func Or(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Or(x, y) |
|
||||||
} |
|
||||||
func Sub1(x, y *big.Int) *big.Int { |
|
||||||
a := big.NewInt(-1) |
|
||||||
a.Sub(a, x) |
|
||||||
|
|
||||||
return a |
|
||||||
} |
|
||||||
func XorSub(x, y *big.Int) *big.Int { |
|
||||||
t := Sub1(x, nil) |
|
||||||
|
|
||||||
return t.Xor(t, y) |
|
||||||
} |
|
||||||
func Rsh(x, y *big.Int) *big.Int { |
|
||||||
return new(big.Int).Rsh(x, uint(y.Uint64()%64)) |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue