platon 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
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
File without changes
@@ -0,0 +1,7 @@
1
+ module Platon
2
+ class Secp256k1
3
+
4
+ N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
5
+
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ module Platon
2
+ module Sedes
3
+ include RLP::Sedes
4
+
5
+ extend self
6
+
7
+ def address
8
+ Binary.fixed_length(20, allow_empty: true)
9
+ end
10
+
11
+ def int20
12
+ BigEndianInt.new(20)
13
+ end
14
+
15
+ def int32
16
+ BigEndianInt.new(32)
17
+ end
18
+
19
+ def int256
20
+ BigEndianInt.new(256)
21
+ end
22
+
23
+ def hash32
24
+ Binary.fixed_length(32)
25
+ end
26
+
27
+ def trie_root
28
+ Binary.fixed_length(32, allow_empty: true)
29
+ end
30
+
31
+ def big_endian_int
32
+ RLP::Sedes.big_endian_int
33
+ end
34
+
35
+ def binary
36
+ RLP::Sedes.binary
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ module Platon
2
+
3
+ class SegwitAddr
4
+
5
+ HRP_MAINNET = 'lat' ### TODO
6
+ HRP_TESTNET = 'atp'
7
+ HRP_REGTEST = 'atx'
8
+
9
+ attr_accessor :hrp # human-readable part
10
+ attr_accessor :ver # witness version
11
+ attr_accessor :prog # witness program
12
+
13
+ def initialize(addr = nil)
14
+ @hrp = HRP_TESTNET ## TODO
15
+ parse_addr(addr) if addr
16
+ end
17
+
18
+ # Returns segwit script pubkey which generated from witness version and witness program.
19
+ def to_script_pubkey
20
+ (prog.map{|p|[p].pack("C")}.join).unpack('H*').first
21
+ end
22
+
23
+ # parse script pubkey into witness version and witness program
24
+ def script_pubkey=(script_pubkey)
25
+ @prog = [script_pubkey].pack('H*').unpack("C*")
26
+ end
27
+
28
+ # Returns segwit address string which generated from hrp, witness version and witness program.
29
+ def addr
30
+ Bech32.encode(hrp, convert_bits(prog, 8, 5), 1)
31
+ end
32
+
33
+ private
34
+
35
+ def parse_addr(addr)
36
+ @hrp, data, spec = Bech32.decode(addr)
37
+ raise 'Invalid address.' if hrp.nil? || data[0].nil? || ![HRP_MAINNET, HRP_TESTNET, HRP_REGTEST].include?(hrp) #TODO
38
+ @ver = data[0]
39
+ @prog = convert_bits(data, 5, 8, false)
40
+ end
41
+
42
+ def convert_bits(data, from, to, padding=true)
43
+ acc = 0
44
+ bits = 0
45
+ ret = []
46
+ maxv = (1 << to) - 1
47
+ max_acc = (1 << (from + to - 1)) - 1
48
+ data.each do |v|
49
+ return nil if v < 0 || (v >> from) != 0
50
+ acc = ((acc << from) | v) & max_acc
51
+ bits += from
52
+ while bits >= to
53
+ bits -= to
54
+ ret << ((acc >> bits) & maxv)
55
+ end
56
+ end
57
+ if padding
58
+ ret << ((acc << (to - bits)) & maxv) unless bits == 0
59
+ elsif bits >= from || ((acc << (to - bits)) & maxv) != 0
60
+ return nil
61
+ end
62
+ ret
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ class Platon::Singleton
2
+
3
+ class << self
4
+
5
+ attr_accessor :client, :ipcpath, :host, :log, :instance, :default_account
6
+
7
+ def instance
8
+ @instance ||= configure_instance(create_instance)
9
+ end
10
+
11
+ def setup
12
+ yield(self)
13
+ end
14
+
15
+ def reset
16
+ @instance = nil
17
+ @client = nil
18
+ @host = nil
19
+ @log = nil
20
+ @ipcpath = nil
21
+ @default_account = nil
22
+ end
23
+
24
+ private
25
+ def create_instance
26
+ return Platon::IpcClient.new(@ipcpath) if @client == :ipc
27
+ return Platon::HttpClient.new(@host) if @client == :http
28
+ Platon::IpcClient.new
29
+ end
30
+
31
+ def configure_instance(instance)
32
+ instance.tap do |i|
33
+ i.default_account = @default_account if @default_account.present?
34
+ end
35
+ end
36
+ end
37
+
38
+
39
+ end
@@ -0,0 +1,40 @@
1
+ require 'tmpdir'
2
+ require 'open3'
3
+
4
+ module Platon
5
+ class CompilationError < StandardError;
6
+ def initialize(msg)
7
+ super
8
+ end
9
+ end
10
+
11
+ class Solidity
12
+
13
+ OUTPUT_REGEXP = /======= (\S*):(\S*) =======\s*Binary:\s*(\S*)\s*Contract JSON ABI\s*(.*)/
14
+
15
+ def initialize(bin_path = "solc")
16
+ @bin_path = bin_path
17
+ @args = "--bin --abi --optimize"
18
+ end
19
+
20
+ def compile(filename)
21
+ result = {}
22
+ execute_solc(filename).scan(OUTPUT_REGEXP).each do |match|
23
+ _file, name, bin, abi = match
24
+ result[name] = {}
25
+ result[name]["abi"] = abi
26
+ result[name]["bin"] = bin
27
+ end
28
+ result
29
+ end
30
+
31
+ private
32
+ def execute_solc(filename)
33
+ p cmd = "#{@bin_path} #{@args} '#{filename}'"
34
+ out, stderr, status = Open3.capture3(cmd)
35
+ raise SystemCallError, "Unanable to run solc compliers" if status.exitstatus == 127
36
+ raise CompilationError, stderr unless status.exitstatus == 0
37
+ out
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Platon
2
+
3
+ class Transaction
4
+
5
+ DEFAULT_TIMEOUT = 300.seconds
6
+ DEFAULT_STEP = 5.seconds
7
+
8
+ attr_accessor :id, :mined, :connection, :input, :input_parameters
9
+
10
+ def initialize(id, connection, data, input_parameters = [])
11
+ @mined = false
12
+ @connection = connection
13
+ @id = id
14
+ @input = data
15
+ @input_parameters = input_parameters
16
+ end
17
+
18
+ def address
19
+ @id
20
+ end
21
+
22
+ def mined?
23
+ return true if @mined
24
+ tx = @connection.platon_get_transaction_by_hash(@id)
25
+ @mined = !tx.nil? && tx["blockNumber"].present?
26
+ end
27
+
28
+ def wait_for_miner(timeout: DEFAULT_TIMEOUT, step: DEFAULT_STEP)
29
+ start_time = Time.now
30
+ loop do
31
+ raise Timeout::Error if ((Time.now - start_time) > timeout)
32
+ return true if self.mined?
33
+ sleep step
34
+ end
35
+ end
36
+
37
+ def self.from_blockchain(address, connection = IpcClient.new)
38
+ Transaction.new(address, connection, nil, nil)
39
+ end
40
+ end
41
+ end
data/lib/platon/tx.rb ADDED
@@ -0,0 +1,201 @@
1
+ module Platon
2
+ class Tx
3
+
4
+ include RLP::Sedes::Serializable
5
+ extend Sedes
6
+
7
+ set_serializable_fields({
8
+ nonce: big_endian_int,
9
+ gas_price: big_endian_int,
10
+ gas_limit: big_endian_int,
11
+ to: address,
12
+ value: big_endian_int,
13
+ data_bin: binary,
14
+ v: big_endian_int,
15
+ r: big_endian_int,
16
+ s: big_endian_int
17
+ })
18
+
19
+ attr_writer :signature
20
+
21
+ def self.decode(data)
22
+ data = Utils.hex_to_bin(data) if data.match(/\A(?:0x)?\h+\Z/)
23
+ txh = deserialize(RLP.decode data).to_h
24
+
25
+ txh[:chain_id] = Platon.chain_id_from_signature(txh)
26
+
27
+ self.new txh
28
+ end
29
+
30
+ def initialize(params)
31
+ fields = {v: 0, r: 0, s: 0}.merge params
32
+ fields[:to] = Utils.normalize_address(fields[:to])
33
+
34
+ self.chain_id = (params[:chain_id]) ? params.delete(:chain_id) : nil
35
+
36
+ if params[:data]
37
+ self.data = params.delete(:data)
38
+ fields[:data_bin] = data_bin
39
+ end
40
+ serializable_initialize fields
41
+
42
+ check_transaction_validity
43
+ end
44
+
45
+ def unsigned_encoded
46
+ us = unsigned
47
+ RLP.encode(us, sedes: us.sedes)
48
+ end
49
+
50
+ def signing_data
51
+ Utils.bin_to_prefixed_hex unsigned_encoded
52
+ end
53
+
54
+ def encoded
55
+ RLP.encode self
56
+ end
57
+
58
+ def hex
59
+ Utils.bin_to_prefixed_hex encoded
60
+ end
61
+
62
+ def sign(key)
63
+ sig = key.sign(unsigned_encoded)
64
+ vrs = Utils.v_r_s_for sig
65
+ self.v = self.chain_id ? ((self.chain_id * 2) + vrs[0] + 8) : vrs[0]
66
+ self.r = vrs[1]
67
+ self.s = vrs[2]
68
+
69
+ clear_signature
70
+ self
71
+ end
72
+
73
+ def to_h
74
+ hash_keys.inject({}) do |hash, field|
75
+ hash[field] = send field
76
+ hash
77
+ end
78
+ end
79
+
80
+ def from
81
+ if ecdsa_signature
82
+ public_key = OpenSsl.recover_compact(signature_hash, ecdsa_signature)
83
+ Utils.public_key_to_address(public_key) if public_key
84
+ end
85
+ end
86
+
87
+ def signature
88
+ return @signature if @signature
89
+ @signature = { v: v, r: r, s: s } if [v, r, s].all? && (v > 0)
90
+ end
91
+
92
+ def ecdsa_signature
93
+ return @ecdsa_signature if @ecdsa_signature
94
+
95
+ if [v, r, s].all? && (v > 0)
96
+ s_v = (self.chain_id) ? (v - (self.chain_id * 2) - 8) : v
97
+ @ecdsa_signature = [
98
+ Utils.int_to_base256(s_v),
99
+ Utils.zpad_int(r),
100
+ Utils.zpad_int(s),
101
+ ].join
102
+ end
103
+ end
104
+
105
+ def hash
106
+ "0x#{Utils.bin_to_hex Utils.keccak256_rlp(self)}"
107
+ end
108
+ alias_method :id, :hash
109
+
110
+ def data_hex
111
+ Utils.bin_to_prefixed_hex data_bin
112
+ end
113
+
114
+ def data_hex=(hex)
115
+ self.data_bin = Utils.hex_to_bin(hex)
116
+ end
117
+
118
+ def data
119
+ true ? data_hex : data_bin
120
+ # Platon.tx_data_hex? ? data_hex : data_bin
121
+
122
+ end
123
+
124
+ def data=(string)
125
+ true ? self.data_hex=(string) : self.data_bin=(string)
126
+ # Platon.tx_data_hex? ? self.data_hex=(string) : self.data_bin=(string)
127
+
128
+ end
129
+
130
+ def chain_id
131
+ @chain_id
132
+ end
133
+
134
+ def chain_id=(cid)
135
+ if cid != @chain_id
136
+ self.v = 0
137
+ self.r = 0
138
+ self.s = 0
139
+
140
+ clear_signature
141
+ end
142
+
143
+ @chain_id = (cid == 0) ? nil : cid
144
+ end
145
+
146
+ def prevent_replays?
147
+ !self.chain_id.nil?
148
+ end
149
+
150
+ private
151
+
152
+ def clear_signature
153
+ @signature = nil
154
+ @ecdsa_signature = nil
155
+ end
156
+
157
+ def hash_keys
158
+ keys = self.class.serializable_fields.keys
159
+ keys.delete(:data_bin)
160
+ keys + [:data, :chain_id]
161
+ end
162
+
163
+ def check_transaction_validity
164
+ if [gas_price, gas_limit, value, nonce].max > UINT_MAX
165
+ raise InvalidTransaction, "Values way too high!"
166
+ elsif gas_limit < intrinsic_gas_used
167
+ raise InvalidTransaction, "Gas limit too low"
168
+ end
169
+ end
170
+
171
+ def intrinsic_gas_used
172
+ num_zero_bytes = data_bin.count(BYTE_ZERO)
173
+ num_non_zero_bytes = data_bin.size - num_zero_bytes
174
+
175
+ Gas::GTXCOST +
176
+ Gas::GTXDATAZERO * num_zero_bytes +
177
+ Gas::GTXDATANONZERO * num_non_zero_bytes
178
+ end
179
+
180
+ def signature_hash
181
+ Utils.keccak256 unsigned_encoded
182
+ end
183
+
184
+ def unsigned
185
+ Tx.new to_h.merge(v: (self.chain_id) ? self.chain_id : 0, r: 0, s: 0)
186
+ end
187
+
188
+ protected
189
+
190
+ def sedes
191
+ if self.prevent_replays? && !(Platon.replayable_v? v)
192
+ self.class
193
+ else
194
+ UnsignedTx
195
+ end
196
+ end
197
+
198
+ end
199
+
200
+ UnsignedTx = Tx.exclude([:v, :r, :s])
201
+ end
@@ -0,0 +1,180 @@
1
+ module Platon
2
+ module Utils
3
+
4
+ extend self
5
+
6
+ def normalize_address(address)
7
+ if address.nil? || address == ''
8
+ ''
9
+ elsif address.size == 40
10
+ hex_to_bin address
11
+ elsif address.size == 42 && address[0..1] == '0x'
12
+ hex_to_bin address[2..-1]
13
+ else
14
+ address
15
+ end
16
+ end
17
+
18
+ def bin_to_hex(string)
19
+ RLP::Utils.encode_hex string
20
+ end
21
+
22
+ def hex_to_bin(string)
23
+ RLP::Utils.decode_hex remove_hex_prefix(string)
24
+ end
25
+
26
+ def base256_to_int(str)
27
+ RLP::Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, '')
28
+ end
29
+
30
+ def int_to_base256(int)
31
+ RLP::Sedes.big_endian_int.serialize int
32
+ end
33
+
34
+ def v_r_s_for(signature)
35
+ [
36
+ signature[0].bytes[0],
37
+ Utils.base256_to_int(signature[1..32]),
38
+ Utils.base256_to_int(signature[33..65]),
39
+ ]
40
+ end
41
+
42
+ def prefix_hex(hex)
43
+ hex.match(/\A0x/) ? hex : "0x#{hex}"
44
+ end
45
+
46
+ def remove_hex_prefix(s)
47
+ s[0,2] == '0x' ? s[2..-1] : s
48
+ end
49
+
50
+ def bin_to_prefixed_hex(binary)
51
+ prefix_hex bin_to_hex(binary)
52
+ end
53
+
54
+ def prefix_message(message)
55
+ "\x19Platon Signed Message:\n#{message.length}#{message}"
56
+ # "\x19Ethereum Signed Message:\n#{message.length}#{message}"
57
+ end
58
+
59
+ def public_key_to_address(hex)
60
+ bytes = hex_to_bin(hex)
61
+ address_bytes = Utils.keccak256(bytes[1..-1])[-20..-1]
62
+ format_address bin_to_prefixed_hex(address_bytes)
63
+ end
64
+
65
+ # bech32 address to bin
66
+ def bech32_to_bin(bech32Address)
67
+ address = decode_bech32_address(bech32Address)
68
+ hex_to_bin address
69
+ end
70
+
71
+ def is_bech32_address?(bech32Address)
72
+ return false if bech32Address.length != 42
73
+ hrp,data,spec = Bech32.decode bech32Address
74
+ return false if data == nil
75
+ return true
76
+ end
77
+
78
+
79
+ # Resolve the bech32 address
80
+ #
81
+ # @method decode_bech32_address
82
+ # @param {String} bech32Address
83
+ # @return {String} formatted address
84
+ # eg: Platon::Utils.decode_bech32_address("atp1kh9dktnszj04zn6d8ae9edhqfmt4awx94n6h4m")
85
+ def decode_bech32_address(bech32Address)
86
+ if is_bech32_address?(bech32Address) ## is_bech32_address? ## TODO
87
+
88
+ segwit_addr = SegwitAddr.new(bech32Address)
89
+ address = segwit_addr.to_script_pubkey
90
+ if address
91
+ return "0x" + address
92
+ end
93
+ end
94
+ return ''
95
+ end
96
+
97
+ # Transforms given string to bech32 address
98
+ #
99
+ # @method to_bech32_address
100
+ # @param {String} hrp
101
+ # @param {String} address
102
+ # @return {String} formatted bech32 address
103
+
104
+ # Platon::Utils.to_bech32_address("atp","0xb5cadb2e70149f514f4d3f725cb6e04ed75eb8c5")
105
+ def to_bech32_address(hrp,address)
106
+ if true ## isAddress
107
+ segwit_addr = SegwitAddr.new
108
+ segwit_addr.hrp = hrp
109
+ segwit_addr.script_pubkey = remove_hex_prefix(address) ## remove 0x
110
+ segwit_addr.addr
111
+ end
112
+ end
113
+
114
+ def sha256(x)
115
+ Digest::SHA256.digest x
116
+ end
117
+
118
+ def keccak256(x)
119
+ Digest::SHA3.new(256).digest(x)
120
+ end
121
+
122
+ def keccak512(x)
123
+ Digest::SHA3.new(512).digest(x)
124
+ end
125
+
126
+ def keccak256_rlp(x)
127
+ keccak256 RLP.encode(x)
128
+ end
129
+
130
+ def ripemd160(x)
131
+ Digest::RMD160.digest x
132
+ end
133
+
134
+ def hash160(x)
135
+ ripemd160 sha256(x)
136
+ end
137
+
138
+ def zpad(x, l)
139
+ lpad x, BYTE_ZERO, l
140
+ end
141
+
142
+ def zunpad(x)
143
+ x.sub(/\A\x00+/, '')
144
+ end
145
+
146
+ def zpad_int(n, l=32)
147
+ zpad encode_int(n), l
148
+ end
149
+
150
+ def zpad_hex(s, l=32)
151
+ zpad decode_hex(s), l
152
+ end
153
+
154
+ def valid_address?(address)
155
+ Address.new(address).valid?
156
+ end
157
+
158
+ def format_address(address)
159
+ Address.new(address).checksummed
160
+ end
161
+
162
+
163
+
164
+ private
165
+
166
+ def lpad(x, symbol, l)
167
+ return x if x.size >= l
168
+ symbol * (l - x.size) + x
169
+ end
170
+
171
+ def encode_int(n)
172
+ unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
173
+ raise ArgumentError, "Integer invalid or out of range: #{n}"
174
+ end
175
+
176
+ int_to_base256 n
177
+ end
178
+
179
+ end
180
+ end