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
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