platon 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +18 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +10 -0
  6. data/Gemfile +12 -0
  7. data/Gemfile.lock +69 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +216 -0
  10. data/Rakefile +12 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +8 -0
  13. data/doc/zh-cn.md +1762 -0
  14. data/lib/bech32.rb +82 -0
  15. data/lib/platon.rb +77 -0
  16. data/lib/platon/abi.rb +32 -0
  17. data/lib/platon/address.rb +62 -0
  18. data/lib/platon/client.rb +175 -0
  19. data/lib/platon/contract.rb +351 -0
  20. data/lib/platon/contract_event.rb +24 -0
  21. data/lib/platon/contract_initializer.rb +54 -0
  22. data/lib/platon/decoder.rb +99 -0
  23. data/lib/platon/deployment.rb +49 -0
  24. data/lib/platon/encoder.rb +120 -0
  25. data/lib/platon/explorer_url_helper.rb +0 -0
  26. data/lib/platon/formatter.rb +142 -0
  27. data/lib/platon/function.rb +36 -0
  28. data/lib/platon/function_input.rb +13 -0
  29. data/lib/platon/function_output.rb +14 -0
  30. data/lib/platon/gas.rb +9 -0
  31. data/lib/platon/http_client.rb +55 -0
  32. data/lib/platon/initializer.rb +0 -0
  33. data/lib/platon/ipc_client.rb +45 -0
  34. data/lib/platon/key.rb +105 -0
  35. data/lib/platon/key/decrypter.rb +113 -0
  36. data/lib/platon/key/encrypter.rb +128 -0
  37. data/lib/platon/open_ssl.rb +267 -0
  38. data/lib/platon/ppos.rb +344 -0
  39. data/lib/platon/railtie.rb +0 -0
  40. data/lib/platon/secp256k1.rb +7 -0
  41. data/lib/platon/sedes.rb +40 -0
  42. data/lib/platon/segwit_addr.rb +66 -0
  43. data/lib/platon/singleton.rb +39 -0
  44. data/lib/platon/solidity.rb +40 -0
  45. data/lib/platon/transaction.rb +41 -0
  46. data/lib/platon/tx.rb +201 -0
  47. data/lib/platon/utils.rb +180 -0
  48. data/lib/platon/version.rb +5 -0
  49. data/lib/tasks/platon_contract.rake +27 -0
  50. data/platon-ruby-logo.png +0 -0
  51. data/platon.gemspec +50 -0
  52. metadata +235 -0
@@ -0,0 +1,36 @@
1
+ module Platon
2
+ class Function
3
+
4
+ attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
5
+
6
+ def initialize(data)
7
+ @name = data["name"]
8
+ @constant = data["constant"]
9
+ @inputs = data["inputs"].map do |input|
10
+ Platon::FunctionInput.new(input)
11
+ end
12
+ @outputs = data["outputs"].collect do |output|
13
+ Platon::FunctionOutput.new(output)
14
+ end
15
+ @function_string = self.class.calc_signature(@name, @inputs)
16
+ @signature = self.class.calc_id(@function_string)
17
+ end
18
+
19
+ def self.to_canonical_type(type)
20
+ type
21
+ .gsub(/(int)(\z|\D)/, '\1256\2')
22
+ .gsub(/(uint)(\z|\D)/, '\1256\2')
23
+ .gsub(/(fixed)(\z|\D)/, '\1128x128\2')
24
+ .gsub(/(ufixed)(\z|\D)/, '\1128x128\2')
25
+ end
26
+
27
+ def self.calc_signature(name, inputs)
28
+ "#{name}(#{inputs.collect {|x| self.to_canonical_type(x.type) }.join(",")})"
29
+ end
30
+
31
+ def self.calc_id(signature)
32
+ Digest::SHA3.hexdigest(signature, 256)[0..7]
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module Platon
2
+ class FunctionInput
3
+
4
+ attr_accessor :type, :name
5
+
6
+ def initialize(data)
7
+ @type = data["type"]
8
+ @name = data["name"]
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ module Platon
2
+
3
+ class FunctionOutput
4
+
5
+ attr_accessor :type, :name
6
+
7
+ def initialize(data)
8
+ @type = data["type"]
9
+ @name = data["name"]
10
+ end
11
+
12
+ end
13
+
14
+ end
data/lib/platon/gas.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Platon
2
+ class Gas
3
+
4
+ GTXCOST = 21000 # TX BASE GAS COST
5
+ GTXDATANONZERO = 68 # TX DATA NON ZERO BYTE GAS COST
6
+ GTXDATAZERO = 4 # TX DATA ZERO BYTE GAS COST
7
+
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ module Platon
4
+ class HttpClient < Client
5
+ attr_accessor :host, :port, :uri, :ssl, :proxy
6
+
7
+ def initialize(host,chain_name, proxy = nil, log = false)
8
+ super(chain_name,log)
9
+ uri = URI.parse(host)
10
+ raise ArgumentError unless ['http', 'https'].include? uri.scheme
11
+ @host = uri.host
12
+ @port = uri.port
13
+ @proxy = proxy
14
+
15
+ @ssl = uri.scheme == 'https'
16
+ @uri = URI("#{uri.scheme}://#{@host}:#{@port}#{uri.path}")
17
+ end
18
+
19
+ def update_setting(params) # :host,:proxy, :chain_id, :hrp
20
+ if params[:host]
21
+ uri = URI.parse(params[:host])
22
+ raise ArgumentError unless ['http', 'https'].include? uri.scheme
23
+ @host = uri.host
24
+ @port = uri.port
25
+ end
26
+ @proxy = proxy if params[:proxy]
27
+ super(params)
28
+ end
29
+
30
+ def send_single(payload)
31
+ if @proxy.present?
32
+ _, p_username, p_password, p_host, p_port = @proxy.gsub(/(:|\/|@)/,' ').squeeze(' ').split
33
+ http = ::Net::HTTP.new(@host, @port, p_host, p_port, p_username, p_password)
34
+ else
35
+ http = ::Net::HTTP.new(@host, @port)
36
+ end
37
+
38
+ if @ssl
39
+ http.use_ssl = true
40
+ end
41
+ header = {'Content-Type' => 'application/json'}
42
+ request = ::Net::HTTP::Post.new(uri, header)
43
+ request.body = payload
44
+ response = http.request(request)
45
+ response.body
46
+ end
47
+
48
+ def send_batch(batch)
49
+ result = send_single(batch.to_json)
50
+ result = JSON.parse(result)
51
+ result.sort_by! { |c| c['id'] }
52
+ end
53
+ end
54
+
55
+ end
File without changes
@@ -0,0 +1,45 @@
1
+ require 'socket'
2
+ module Platon
3
+ class IpcClient < Client
4
+ attr_accessor :ipcpath
5
+
6
+ IPC_PATHS = [
7
+ "#{ENV['HOME']}/platon-node/data/platon.ipc",
8
+ "#{ENV['HOME']}/alaya-node/data/platon.ipc"
9
+ ]
10
+
11
+ def initialize(ipcpath = nil, log = true)
12
+ super(nil,log)
13
+ ipcpath ||= IpcClient.default_path
14
+ @ipcpath = ipcpath
15
+ end
16
+
17
+ def self.default_path(paths = IPC_PATHS)
18
+ path = paths.select { |path| File.exist?(path) }.first
19
+ path || raise("Ipc file not found. Please pass in the file path explicitly to IpcClient initializer")
20
+ end
21
+
22
+ def send_single(payload)
23
+ socket = UNIXSocket.new(@ipcpath)
24
+ socket.puts(payload)
25
+ read = socket.recvmsg(nil)[0]
26
+ socket.close
27
+ return read
28
+ end
29
+
30
+ # Note: Guarantees the results are in the same order as defined in batch call.
31
+ # client.batch do
32
+ # client.platon_block_number
33
+ # client.platon_mining
34
+ # end
35
+ # => [{"jsonrpc"=>"2.0", "id"=>1, "result"=>"0x26"}, {"jsonrpc"=>"2.0", "id"=>2, "result"=>false}]
36
+ def send_batch(batch)
37
+ result = send_single(batch.to_json)
38
+ result = JSON.parse(result)
39
+
40
+ # Make sure the order is the same as it was when batching calls
41
+ # See 6 Batch here http://www.jsonrpc.org/specification
42
+ return result.sort_by! { |c| c['id'] }
43
+ end
44
+ end
45
+ end
data/lib/platon/key.rb ADDED
@@ -0,0 +1,105 @@
1
+ module Platon
2
+ class Key
3
+ autoload :Decrypter, 'platon/key/decrypter'
4
+ autoload :Encrypter, 'platon/key/encrypter'
5
+
6
+ attr_reader :private_key, :public_key
7
+
8
+ def self.encrypt(key, password)
9
+ key = new(priv: key) unless key.is_a?(Key)
10
+
11
+ Encrypter.perform key.private_hex, password
12
+ end
13
+
14
+ def self.decrypt(data, password)
15
+ priv = Decrypter.perform data, password
16
+ new priv: priv
17
+ end
18
+
19
+ def self.encrypt_and_save(key,password,keypath=nil)
20
+ encrypted_key_info = encrypt(key,password)
21
+ ## PlatON似乎打算兼容以太地址, 暂时默认以 0x 地址命名
22
+ if keypath==nil || keypath==""
23
+ address = key.address
24
+ dirname = "#{ENV['HOME']}/.platon/keystore/"
25
+ FileUtils.mkdir_p(dirname) unless Dir.exists?(dirname)
26
+ File.write File.join(dirname, "#{key.address}.json") , encrypted_key_info
27
+ else
28
+ File.write keypath , encrypted_key_info
29
+ end
30
+ return encrypted_key_info
31
+ end
32
+
33
+ def self.list_wallets(keypath=nil)
34
+ if keypath==nil || keypath==""
35
+ Dir.glob("#{ENV['HOME']}/.platon/keystore/*").select { |e| File.file? e }
36
+ else
37
+ Dir.glob(keypath + "/*").select { |e| File.file? e }
38
+ end
39
+ end
40
+
41
+ def self.personal_recover(message, signature)
42
+ bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack('c*')
43
+ OpenSsl.recover_compact(Utils.keccak256(Utils.prefix_message(message)), bin_signature)
44
+ end
45
+
46
+ def initialize(priv: nil)
47
+ @private_key = MoneyTree::PrivateKey.new key: priv
48
+ @public_key = MoneyTree::PublicKey.new private_key, compressed: false
49
+ end
50
+
51
+ def private_hex
52
+ private_key.to_hex
53
+ end
54
+
55
+ def public_bytes
56
+ public_key.to_bytes
57
+ end
58
+
59
+ def public_hex
60
+ public_key.to_hex
61
+ end
62
+
63
+ def address
64
+ Utils.public_key_to_address public_hex
65
+ end
66
+
67
+ def bech32_address(hrp:"atp")
68
+ Utils.to_bech32_address(hrp,address) ##TODO
69
+ end
70
+ alias_method :to_address, :address
71
+
72
+ def sign(message)
73
+ sign_hash message_hash(message)
74
+ end
75
+
76
+ def sign_hash(hash)
77
+ loop do
78
+ signature = OpenSsl.sign_compact hash, private_hex, public_hex
79
+ return signature if valid_s? signature
80
+ end
81
+ end
82
+
83
+ def verify_signature(message, signature)
84
+ hash = message_hash(message)
85
+ public_hex == OpenSsl.recover_compact(hash, signature)
86
+ end
87
+
88
+ def personal_sign(message)
89
+ Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack('c*'))
90
+ end
91
+
92
+
93
+ private
94
+
95
+ def message_hash(message)
96
+ Utils.keccak256 message
97
+ end
98
+
99
+ def valid_s?(signature)
100
+ s_value = Utils.v_r_s_for(signature).last
101
+ s_value <= Secp256k1::N/2 && s_value != 0
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,113 @@
1
+ require 'json'
2
+ require 'scrypt'
3
+
4
+ class Platon::Key::Decrypter
5
+ include Platon::Utils
6
+
7
+ def self.perform(data, password)
8
+ new(data, password).perform
9
+ end
10
+
11
+ def initialize(data, password)
12
+ @data = JSON.parse(data)
13
+ @password = password
14
+ end
15
+
16
+ def perform
17
+ derive_key password
18
+ check_macs
19
+ bin_to_hex decrypted_data
20
+ end
21
+
22
+
23
+ private
24
+
25
+ attr_reader :data, :key, :password
26
+
27
+ def derive_key(password)
28
+ case kdf
29
+ when 'pbkdf2'
30
+ @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
31
+ when 'scrypt'
32
+ # OpenSSL 1.1 inclues OpenSSL::KDF.scrypt, but it is not available usually, otherwise we could do: OpenSSL::KDF.scrypt(password, salt: salt, N: n, r: r, p: p, length: key_length)
33
+ @key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
34
+ else
35
+ raise "Unsupported key derivation function: #{kdf}!"
36
+ end
37
+ end
38
+
39
+ def check_macs
40
+ mac1 = keccak256(key[(key_length/2), key_length] + ciphertext)
41
+ mac2 = hex_to_bin crypto_data['mac']
42
+
43
+ if mac1 != mac2
44
+ raise "Message Authentications Codes do not match!"
45
+ end
46
+ end
47
+
48
+ def decrypted_data
49
+ @decrypted_data ||= cipher.update(ciphertext) + cipher.final
50
+ end
51
+
52
+ def crypto_data
53
+ @crypto_data ||= data['crypto'] || data['Crypto']
54
+ end
55
+
56
+ def ciphertext
57
+ hex_to_bin crypto_data['ciphertext']
58
+ end
59
+
60
+ def cipher_name
61
+ "aes-128-ctr"
62
+ end
63
+
64
+ def cipher
65
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
66
+ cipher.decrypt
67
+ cipher.key = key[0, (key_length/2)]
68
+ cipher.iv = iv
69
+ end
70
+ end
71
+
72
+ def iv
73
+ hex_to_bin crypto_data['cipherparams']['iv']
74
+ end
75
+
76
+ def salt
77
+ hex_to_bin crypto_data['kdfparams']['salt']
78
+ end
79
+
80
+ def iterations
81
+ crypto_data['kdfparams']['c'].to_i
82
+ end
83
+
84
+ def kdf
85
+ crypto_data['kdf']
86
+ end
87
+
88
+ def key_length
89
+ crypto_data['kdfparams']['dklen'].to_i
90
+ end
91
+
92
+ def n
93
+ crypto_data['kdfparams']['n'].to_i
94
+ end
95
+
96
+ def r
97
+ crypto_data['kdfparams']['r'].to_i
98
+ end
99
+
100
+ def p
101
+ crypto_data['kdfparams']['p'].to_i
102
+ end
103
+
104
+ def digest
105
+ OpenSSL::Digest.new digest_name
106
+ end
107
+
108
+ def digest_name
109
+ "sha256"
110
+ end
111
+
112
+
113
+ end
@@ -0,0 +1,128 @@
1
+ require 'json'
2
+ require 'securerandom'
3
+
4
+ class Platon::Key::Encrypter
5
+ include Platon::Utils
6
+
7
+ def self.perform(key, password, options = {})
8
+ new(key, options).perform(password)
9
+ end
10
+
11
+ def initialize(key, options = {})
12
+ @key = key
13
+ @options = options
14
+ end
15
+
16
+ def perform(password)
17
+ derive_key password
18
+ encrypt
19
+
20
+ data.to_json
21
+ end
22
+
23
+ def data
24
+ {
25
+ crypto: {
26
+ cipher: cipher_name,
27
+ cipherparams: {
28
+ iv: bin_to_hex(iv),
29
+ },
30
+ ciphertext: bin_to_hex(encrypted_key),
31
+ kdf: "pbkdf2",
32
+ kdfparams: {
33
+ c: iterations,
34
+ dklen: 32,
35
+ prf: prf,
36
+ salt: bin_to_hex(salt),
37
+ },
38
+ mac: bin_to_hex(mac),
39
+ },
40
+ id: id,
41
+ version: 3,
42
+ }.tap do |data|
43
+ data[:address] = address unless options[:skip_address]
44
+ end
45
+ end
46
+
47
+ def id
48
+ @id ||= options[:id] || SecureRandom.uuid
49
+ end
50
+
51
+
52
+ private
53
+
54
+ attr_reader :derived_key, :encrypted_key, :key, :options
55
+
56
+ def cipher
57
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
58
+ cipher.encrypt
59
+ cipher.iv = iv
60
+ cipher.key = derived_key[0, (key_length/2)]
61
+ end
62
+ end
63
+
64
+ def digest
65
+ @digest ||= OpenSSL::Digest.new digest_name
66
+ end
67
+
68
+ def derive_key(password)
69
+ @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
70
+ end
71
+
72
+ def encrypt
73
+ @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
74
+ end
75
+
76
+ def mac
77
+ keccak256(derived_key[(key_length/2), key_length] + encrypted_key)
78
+ end
79
+
80
+ def cipher_name
81
+ "aes-128-ctr"
82
+ end
83
+
84
+ def digest_name
85
+ "sha256"
86
+ end
87
+
88
+ def prf
89
+ "hmac-#{digest_name}"
90
+ end
91
+
92
+ def key_length
93
+ 32
94
+ end
95
+
96
+ def salt_length
97
+ 32
98
+ end
99
+
100
+ def iv_length
101
+ 16
102
+ end
103
+
104
+ def iterations
105
+ options[:iterations] || 262_144
106
+ end
107
+
108
+ def salt
109
+ @salt ||= if options[:salt]
110
+ hex_to_bin options[:salt]
111
+ else
112
+ SecureRandom.random_bytes(salt_length)
113
+ end
114
+ end
115
+
116
+ def iv
117
+ @iv ||= if options[:iv]
118
+ hex_to_bin options[:iv]
119
+ else
120
+ SecureRandom.random_bytes(iv_length)
121
+ end
122
+ end
123
+
124
+ def address
125
+ Platon::Key.new(priv: key).address
126
+ end
127
+
128
+ end