Skip to content

Commit 2c9b79c

Browse files
authored
Merge pull request #6 from alexhanh/master
Improve clients
2 parents 42237f4 + 2335d48 commit 2c9b79c

File tree

8 files changed

+128
-94
lines changed

8 files changed

+128
-94
lines changed

.ruby-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

ethereum.gemspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
2929
spec.add_development_dependency "rake", "~> 10.0"
3030
spec.add_development_dependency "rspec"
3131
spec.add_development_dependency "pry"
32+
3233
spec.add_dependency "activesupport"
33-
spec.add_dependency "sha3-pure-ruby", "0.1.1"
34+
spec.add_dependency "digest-sha3", "~> 1.1"
3435
end

lib/ethereum.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require "ethereum/version"
22
require 'active_support'
33
require 'active_support/core_ext'
4-
require 'sha3-pure-ruby'
4+
require 'digest/sha3'
55

66
module Ethereum
77
require 'ethereum/client'

lib/ethereum/client.rb

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,65 @@
11
module Ethereum
22
class Client
3+
# https://github.com/ethereum/wiki/wiki/JSON-RPC
4+
RPC_COMMANDS = %w(web3_clientVersion web3_sha3 net_version net_peerCount net_listening eth_protocolVersion eth_syncing eth_coinbase eth_mining eth_hashrate eth_gasPrice eth_accounts eth_blockNumber eth_getBalance eth_getStorageAt eth_getTransactionCount eth_getBlockTransactionCountByHash eth_getBlockTransactionCountByNumber eth_getUncleCountByBlockHash eth_getUncleCountByBlockNumber eth_getCode eth_sign eth_sendTransaction eth_sendRawTransaction eth_call eth_estimateGas eth_getBlockByHash eth_getBlockByNumber eth_getTransactionByHash eth_getTransactionByBlockHashAndIndex eth_getTransactionByBlockNumberAndIndex eth_getTransactionReceipt eth_getUncleByBlockHashAndIndex eth_getUncleByBlockNumberAndIndex eth_getCompilers eth_compileLLL eth_compileSolidity eth_compileSerpent eth_newFilter eth_newBlockFilter eth_newPendingTransactionFilter eth_uninstallFilter eth_getFilterChanges eth_getFilterLogs eth_getLogs eth_getWork eth_submitWork eth_submitHashrate db_putString db_getString db_putHex db_getHex shh_post shh_version shh_newIdentity shh_hasIdentity shh_newGroup shh_addToGroup shh_newFilter shh_uninstallFilter shh_getFilterChanges shh_getMessages)
5+
# https://github.com/ethereum/go-ethereum/wiki/Management-APIs
6+
RPC_MANAGEMENT_COMMANDS = %w(admin_addPeer admin_datadir admin_nodeInfo admin_peers admin_setSolc admin_startRPC admin_startWS admin_stopRPC admin_stopWS debug_backtraceAt debug_blockProfile debug_cpuProfile debug_dumpBlock debug_gcStats debug_getBlockRlp debug_goTrace debug_memStats debug_seedHash debug_setHead debug_setBlockProfileRate debug_stacks debug_startCPUProfile debug_startGoTrace debug_stopCPUProfile debug_stopGoTrace debug_traceBlock debug_traceBlockByNumber debug_traceBlockByHash debug_traceBlockFromFile debug_traceTransaction debug_verbosity debug_vmodule debug_writeBlockProfile debug_writeMemProfile miner_hashrate miner_makeDAG miner_setExtra miner_setGasPrice miner_start miner_startAutoDAG miner_stop miner_stopAutoDAG personal_importRawKey personal_listAccounts personal_lockAccount personal_newAccount personal_unlockAccount personal_sendTransaction txpool_content txpool_inspect txpool_status)
37

4-
RPC_COMMANDS = %w(personal_newAccount personal_unlockAccount eth_accounts eth_blockNumber eth_getBalance eth_protocolVersion eth_coinbase eth_mining eth_gasPrice eth_getStorage eth_storageAt eth_getStorageAt eth_getTransactionCount eth_getBlockTransactionCountByHash eth_getBlockTransactionCountByNumber eth_getUncleCountByBlockHash eth_getUncleCountByBlockNumber eth_getData eth_getCode eth_sign eth_sendRawTransaction eth_sendTransaction eth_transact eth_estimateGas eth_call eth_flush eth_getBlockByHash eth_getBlockByNumber eth_getTransactionByHash eth_getTransactionByBlockNumberAndIndex eth_getTransactionByBlockHashAndIndex eth_getUncleByBlockHashAndIndex eth_getUncleByBlockNumberAndIndex eth_getCompilers eth_compileSolidity eth_newFilter eth_newBlockFilter eth_newPendingTransactionFilter eth_uninstallFilter eth_getFilterChanges eth_getFilterLogs eth_getLogs eth_hashrate eth_getWork eth_submitWork eth_resend eth_pendingTransactions eth_getTransactionReceipt)
8+
attr_accessor :command, :id, :log, :logger
9+
10+
def initialize(log = false)
11+
@id = 0
12+
@log = log
13+
@batch = nil
14+
15+
if @log == true
16+
@logger = Logger.new("/tmp/ethereum_ruby_http.log")
17+
end
18+
end
19+
20+
def batch
21+
@batch = []
22+
23+
yield
24+
result = send_batch(@batch)
25+
26+
@batch = nil
27+
reset_id
28+
29+
return result
30+
end
531

632
def get_id
7-
@id = @id + 1
33+
@id += 1
834
return @id
935
end
1036

11-
def clear_batch
12-
@batch = []
37+
def reset_id
38+
@id = 0
39+
end
40+
41+
(RPC_COMMANDS + RPC_MANAGEMENT_COMMANDS).each do |rpc_command|
42+
method_name = "#{rpc_command.underscore}"
43+
define_method method_name do |*args|
44+
command = rpc_command
45+
if command == "eth_call"
46+
args << "latest"
47+
end
48+
payload = {jsonrpc: "2.0", method: command, params: args, id: get_id}
49+
if @log == true
50+
@logger.info("Sending #{payload.to_json}")
51+
end
52+
53+
if @batch
54+
@batch << payload
55+
return true
56+
else
57+
read = send_single(payload.to_json)
58+
output = JSON.parse(read)
59+
reset_id
60+
return output
61+
end
62+
end
1363
end
1464

1565
end

lib/ethereum/http_client.rb

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,35 @@
11
require 'net/http'
22
module Ethereum
33
class HttpClient < Client
4-
attr_accessor :command, :id, :host, :port, :batch, :converted_transactions, :uri, :ssl, :logger, :log
4+
attr_accessor :host, :port, :uri, :ssl
55

6-
def initialize(host, port, ssl = false, log = true)
6+
def initialize(host, port, ssl = false, log = false)
7+
super(log)
78
@host = host
89
@port = port
9-
@id = 1
1010
@ssl = ssl
11-
@log = log
12-
if @log == true
13-
@logger = Logger.new("/tmp/ethereum_ruby_http.log")
14-
end
1511
if ssl
1612
@uri = URI("https://#{@host}:#{@port}")
1713
else
1814
@uri = URI("http://#{@host}:#{@port}")
1915
end
20-
@batch = []
2116
end
2217

23-
RPC_COMMANDS.each do |rpc_command|
24-
method_name = "#{rpc_command.split("_")[1].underscore}"
25-
define_method method_name do |*args|
26-
command = rpc_command
27-
if command == "eth_call"
28-
args << "latest"
29-
end
30-
payload = {jsonrpc: "2.0", method: command, params: args, id: get_id}
31-
http = ::Net::HTTP.new(@host, @port)
32-
if @ssl
33-
http.use_ssl = true
34-
end
35-
header = {'Content-Type' => 'application/json'}
36-
request = ::Net::HTTP::Post.new(uri, header)
37-
if @log == true
38-
@logger.info("Sending #{payload.to_json}")
39-
end
40-
request.body = payload.to_json
41-
response = http.request(request)
42-
return JSON.parse(response.body)
43-
end
44-
45-
define_method "#{method_name}_batch" do |*args|
46-
command = rpc_command
47-
payload = {jsonrpc: "2.0", method: command, params: args, id: get_id}
48-
@batch << payload.to_json
18+
def send_single(payload)
19+
http = ::Net::HTTP.new(@host, @port)
20+
if @ssl
21+
http.use_ssl = true
4922
end
23+
header = {'Content-Type' => 'application/json'}
24+
request = ::Net::HTTP::Post.new(uri, header)
25+
request.body = payload
26+
response = http.request(request)
27+
return response.body
5028
end
5129

52-
def send_batch
53-
30+
def send_batch(batch)
31+
raise NotImplementedError
5432
end
55-
5633
end
5734

5835
end

lib/ethereum/ipc_client.rb

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,35 @@
11
require 'socket'
22
module Ethereum
33
class IpcClient < Client
4-
attr_accessor :command, :id, :ipcpath, :batch, :converted_transactions, :log, :logger
4+
attr_accessor :ipcpath
55

6-
def initialize(ipcpath = "#{ENV['HOME']}/.ethereum/geth.ipc", log = true)
6+
def initialize(ipcpath = "#{ENV['HOME']}/.ethereum/geth.ipc", log = false)
7+
super(log)
78
@ipcpath = ipcpath
8-
@id = 1
9-
@batch = []
10-
@log = log
11-
if @log == true
12-
@logger = Logger.new("/tmp/ethereum_ruby_ipc.log")
13-
end
149
end
1510

16-
RPC_COMMANDS.each do |rpc_command|
17-
method_name = "#{rpc_command.split("_")[1].underscore}"
18-
define_method method_name do |*args|
19-
command = rpc_command
20-
if command == "eth_call"
21-
args << "latest"
22-
end
23-
payload = {jsonrpc: "2.0", method: command, params: args, id: get_id}
24-
socket = UNIXSocket.new(@ipcpath)
25-
socket.write(payload.to_json)
26-
if @log == true
27-
@logger.info("Sending #{payload.to_json}")
28-
end
29-
socket.close_write
30-
read = socket.read
31-
socket.close_read
32-
output = JSON.parse(read)
33-
return output
34-
end
35-
36-
define_method "#{method_name}_batch" do |*args|
37-
command = rpc_command
38-
payload = {jsonrpc: "2.0", method: command, params: args, id: get_id}
39-
@batch << payload.to_json
40-
end
41-
end
42-
43-
def send_batch
11+
def send_single(payload)
4412
socket = UNIXSocket.new(@ipcpath)
45-
socket.write(@batch.join(" "))
46-
socket.close_write
47-
read = socket.read
48-
collection = read.chop.split("}{").collect do |output|
49-
if output[0] == "{"
50-
JSON.parse("#{output}}")["result"]
51-
else
52-
JSON.parse("{#{output}}")["result"]
53-
end
54-
end
55-
return collection
13+
socket.puts(payload)
14+
read = socket.gets
15+
socket.close
16+
return read
5617
end
5718

58-
end
59-
end
19+
# TODO: Not sure if multithread safe
20+
# Note: Guarantees the results are in the same order as defined in batch call.
21+
# client.batch do
22+
# client.eth_block_number
23+
# client.eth_mining
24+
# end
25+
# => [{"jsonrpc"=>"2.0", "id"=>1, "result"=>"0x26"}, {"jsonrpc"=>"2.0", "id"=>2, "result"=>false}]
26+
def send_batch(batch)
27+
result = send_single(batch.to_json)
28+
result = JSON.parse(result)
6029

30+
# Make sure the order is the same as it was when batching calls
31+
# See 6 Batch here http://www.jsonrpc.org/specification
32+
return result.sort_by! { |c| c['id'] }
33+
end
34+
end
35+
end

lib/ethereum/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Ethereum
2-
VERSION = "0.5.2"
2+
VERSION = "1.5.2"
33
end

spec/client_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'spec_helper'
2+
3+
describe Ethereum do
4+
describe 'HttpClient' do
5+
it 'should work' do
6+
client = Ethereum::HttpClient.new('localhost', '8545')
7+
expect(client.eth_accounts['error']).to eq(nil)
8+
end
9+
end
10+
11+
describe 'IpcClient' do
12+
before(:each) do
13+
@client = Ethereum::IpcClient.new("#{ENV['HOME']}/EtherDev/data/geth.ipc")
14+
end
15+
16+
it 'should work' do
17+
expect(@client.eth_accounts['error']).to eq(nil)
18+
end
19+
20+
it 'should support batching' do
21+
response = @client.batch do
22+
@client.net_listening
23+
@client.eth_block_number
24+
end
25+
26+
expect(response).to be_a Array
27+
expect(response.length).to eq 2
28+
expect(response.first['result']).to be_in [true, false]
29+
expect(response.last['result']).to match /(0x)?[0-9a-f]+/i
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)