zilliqa 0.1.1

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.
data/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # Zilliqa - Zilliqa Blockchain Ruby Library
2
+
3
+ - [Zilliqa API doc](https://apidocs.zilliqa.com/)
4
+ - The project is still under development.
5
+
6
+
7
+
8
+ ## Table of Contents
9
+
10
+ <!-- TOC depthFrom:1 depthTo:6 withLinks:1 orderedList:0 -->
11
+
12
+ #### [Requirements](#requirement)
13
+ #### [Installation](#installation)
14
+ #### [Zilliqa KeyTool](#zilliqa-keytool)
15
+ #### [Transaction](#transaction-z)
16
+ #### [Wallet](#wallet-z)
17
+ - [Smart Contract](#contract)
18
+ - [Create](#contract-create)
19
+ - [Deploy](#contract-deploy)
20
+
21
+ <!-- /TOC -->
22
+
23
+
24
+
25
+ ## <a name="requirement"></a>Requirement
26
+
27
+ - Ruby(2.5.3)
28
+
29
+
30
+
31
+ ## <a name="installation"></a>Installation
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'zilliqa'
37
+ ```
38
+
39
+ And then execute:
40
+
41
+ $ bundle
42
+
43
+ Or install it yourself as:
44
+
45
+ $ gem install zilliqa
46
+
47
+
48
+
49
+ ## <a name="zilliqa-keytool"></a>Zilliqa KeyTool
50
+
51
+ ### Generate A new address
52
+ ```ruby
53
+ private_key = Zilliqa::Crypto::KeyTool.generate_private_key
54
+ public_key = Zilliqa::Crypto::KeyTool.get_public_key_from_private_key(private_key)
55
+ address = Zilliqa::Crypto::KeyTool.get_address_from_private_key(private_key)
56
+ ```
57
+
58
+ ### Validate an address
59
+ ```ruby
60
+ address = '2624B9EA4B1CD740630F6BF2FEA82AAC0067070B'
61
+ Zilliqa::Util::Validator.address?(address)
62
+ ```
63
+
64
+ ### Validate checksum address
65
+ ```ruby
66
+ checksum_address = '0x4BAF5faDA8e5Db92C3d3242618c5B47133AE003C'
67
+ Zilliqa::Util::Validator.checksum_address?(checksum_address)
68
+ ```
69
+
70
+
71
+ ## <a name="transaction-z"></a>Transaction
72
+
73
+ ```ruby
74
+ provider = Zilliqa::Jsonrpc::Provider.new('https://dev-api.zilliqa.com')
75
+ to_addr = 'zil1lpw9fc8p4tse55r85fa37gscnkxf6xq5ahe8uj'
76
+ pub_key = '032cfec301c57acc2a4b18f47247687a1ec51e61336a7d5936e455b7dab3ae712e'
77
+ testnet = 21_823_489
78
+
79
+ tx_params = {
80
+ version: testnet,
81
+ amount: '0',
82
+ to_addr: to_addr,
83
+ gas_price: '1000',
84
+ gas_limit: 1,
85
+ sender_pub_key: pub_key
86
+ }
87
+
88
+ wallet = Zilliqa::Account::Wallet.new(provider)
89
+ transaction = Zilliqa::Account::Transaction.new(tx_params, provider)
90
+
91
+ wallet.add_by_private_key(private_key)
92
+
93
+ tx = wallet.sign(transaction)
94
+ tx.submit!
95
+ ```
96
+
97
+ ## <a name="wallet-z"></a>Wallet
98
+
99
+ ```ruby
100
+ provider = Zilliqa::Jsonrpc::Provider.new('https://dev-api.zilliqa.com')
101
+ wallet = Zilliqa::Account::Wallet.new(provider)
102
+ wallet.add_by_private_key(private_key)
103
+ wallet.transfer('zil1lpw9fc8p4tse55r85fa37gscnkxf6xq5ahe8uj', 10 ** 12)
104
+ ```
105
+
106
+ ```
107
+ Successfull output
108
+ {
109
+ "ContractAddress"=>"1e366b36e5a17dec83c46f19d8d6b43434bd1dbb",
110
+ "Info"=>"Contract Creation txn, sent to shard",
111
+ "TranID"=>"411c1108800ac85118fcd9a44568d208276dcbdd5287c99119c69167912f344a"
112
+ }
113
+ ```
114
+
115
+ ## <a name="contract"> </a>Smart Contract
116
+
117
+
118
+ ### <a name="contract-create"> </a>Create Smart contract
119
+ ```ruby
120
+ private_key = "e19d05c5452598..."
121
+ provider = Zilliqa::Jsonrpc::Provider.new('https://dev-api.zilliqa.com')
122
+ wallet = Zilliqa::Account::Wallet.new(provider)
123
+ address = wallet.add_by_private_key(private_key)
124
+
125
+ factory = Zilliqa::Contract::ContractFactory.new(provider, wallet)
126
+
127
+ contract = factory.new_contract(TEST_CONTRACT, [
128
+ {
129
+ vname: 'owner',
130
+ type: 'ByStr20',
131
+ value: '0x124567890124567890124567890124567890',
132
+ },
133
+ ],
134
+ ABI,
135
+ )
136
+ ```
137
+
138
+ ### <a name="contract-deploy"> </a>Deploy contract
139
+
140
+ ##### [How to calculate gas limit for smart contract transaction](https://drive.google.com/file/d/1c0EJXELVe_MxhULPuJgwGvxFGenG7fmK/view?usp=sharing)
141
+
142
+ ```ruby
143
+ gas_limit = TEST_CONTRACT.bytes.size + ABI.to_s.bytes.size
144
+ ```
145
+
146
+ ```ruby
147
+ gas_price = 10 ** 12 # 1 zil
148
+ testnet_ver = 21_823_489
149
+ pub_key = '032cfec301...'
150
+
151
+ deploy_params = Zilliqa::Contract::DeployParams.new(nil, testnet_ver, nil, gas_price, gas_limit, pub_key)
152
+ tx, deployed = contract.deploy(deploy_params)
153
+
154
+ assert tx.confirmed?
155
+ assert deployed.deployed?
156
+ assert_equal Zilliqa::Contract::CONTRACT_STATUSES[:deployed], deployed.status
157
+
158
+ assert /[A-F0-9]+/ =~ contract.address
159
+
160
+ # call a deployed contract
161
+ call_tx = deployed.call(
162
+ 'setHello',
163
+ [
164
+ { vname: 'msg', type: 'String', value: 'Hello World!' },
165
+ ],
166
+ {
167
+ version: Zilliqa::Util.pack(8, 8),
168
+ amount: 0,
169
+ gasPrice: 1000,
170
+ gasLimit: 1000
171
+ })
172
+
173
+
174
+ receipt = call_tx.receipt
175
+ ```
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "zilliqa"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/zilliqa.rb ADDED
@@ -0,0 +1,21 @@
1
+ require "zilliqa/version"
2
+ require 'zilliqa/crypto/key_tool'
3
+ require 'zilliqa/crypto/key_store'
4
+ require 'zilliqa/crypto/schnorr'
5
+ require 'zilliqa/jsonrpc/provider'
6
+ require 'zilliqa/account/account'
7
+ require 'zilliqa/account/wallet'
8
+ require 'zilliqa/account/transaction_factory'
9
+ require 'zilliqa/account/transaction'
10
+ require 'zilliqa/proto/message_pb'
11
+ require 'zilliqa/contract/contract_factory'
12
+ require 'zilliqa/contract/contract'
13
+ require 'zilliqa/util/validator'
14
+ require 'zilliqa/util/util'
15
+ require 'zilliqa/util/unit'
16
+ require 'zilliqa/util/bech32'
17
+
18
+
19
+ module Zilliqa
20
+
21
+ end
@@ -0,0 +1,34 @@
1
+ module Zilliqa
2
+ module Account
3
+ class Account
4
+ attr_reader :private_key, :public_key, :address
5
+ def initialize(private_key)
6
+ @private_key = private_key
7
+ @public_key = Zilliqa::Crypto::KeyTool.get_public_key_from_private_key(private_key, true)
8
+ @address = Zilliqa::Crypto::KeyTool.get_address_from_public_key(@public_key)
9
+ end
10
+
11
+ # Takes a JSON-encoded keystore and passphrase, returning a fully
12
+ # instantiated Account instance.
13
+ def self.from_file(file, passphrase)
14
+ key_store = Zilliqa::Crypto::KeyStore.new
15
+ private_key = key_store.decrypt_private_key(file, passphrase)
16
+ Account.new(private_key)
17
+ end
18
+
19
+ # Convert an Account instance to a JSON-encoded keystore.
20
+ def to_file(passphrase, type)
21
+ key_store = Zilliqa::Crypto::KeyStore.new
22
+ json = key_store.encrypt_private_key(@private_key, passphrase, type);
23
+ end
24
+
25
+ # sign the passed in transaction with the account's private and public key
26
+ def sign_transaction(tx)
27
+ message = tx.bytes
28
+ message_hex = Util.encode_hex(message)
29
+
30
+ Zilliqa::Crypto::Schnorr.sign(message_hex, @private_key, @public_key)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zilliqa
4
+ module Account
5
+ #
6
+ # Transaction
7
+ #
8
+ # Transaction is a functor. Its purpose is to encode the possible states a
9
+ # Transaction can be in: Confirmed, Rejected, Pending, or Initialised (i.e., not broadcasted).
10
+ class Transaction
11
+ class StandardLengthError < StandardError
12
+ def initialize(msg = 'standard length exceeded for value')
13
+ super
14
+ end
15
+ end
16
+
17
+ class TrackTxError < StandardError; end
18
+ class TransactionError < JSONRPC::Error::ServerError; end
19
+
20
+ ATTRIBUTES = %i[id version nonce amount gas_price gas_limit signature receipt sender_pub_key to_addr code data to_ds].freeze
21
+ attr_accessor(*ATTRIBUTES)
22
+ attr_accessor :provider, :status
23
+
24
+ GET_TX_ATTEMPTS = 33
25
+ MAX_BIGINT_BYTES = 2**128 - 1
26
+ TX_STATUSES = {
27
+ initialized: 0,
28
+ pending: 1,
29
+ confirmed: 2,
30
+ rejected: 3
31
+ }.freeze
32
+
33
+ def initialize(tx_params, provider, status = TX_STATUSES[:initialized], to_ds = false)
34
+ unless tx_params.nil?
35
+ tx_params.each do |key, value|
36
+ next unless ATTRIBUTES.include?(key)
37
+ instance_variable_set("@#{key}", value)
38
+ end
39
+ end
40
+
41
+ @provider = provider
42
+ @status = status
43
+ @to_ds = to_ds
44
+ end
45
+
46
+ # constructs an already-confirmed transaction.
47
+ def self.confirm(tx_params, provider)
48
+ Transaction.new(tx_params, provider, TX_STATUSES[:confirmed])
49
+ end
50
+
51
+ # constructs an already-rejected transaction.
52
+ def self.reject(tx_params, provider)
53
+ Transaction.new(tx_params, provider, TX_STATUSES[:rejected])
54
+ end
55
+
56
+ def bytes
57
+ protocol = Zilliqa::Proto::ProtoTransactionCoreInfo.new
58
+ protocol.version = version.to_i
59
+ protocol.nonce = nonce.to_i
60
+ protocol.toaddr = Util.decode_hex(Wallet.to_checksum_address(to_addr).downcase.sub('0x', ''))
61
+ protocol.senderpubkey = Zilliqa::Proto::ByteArray.new(data: Util.decode_hex(sender_pub_key))
62
+
63
+ raise StandardLengthError if amount.to_i > MAX_BIGINT_BYTES
64
+
65
+ protocol.amount = Zilliqa::Proto::ByteArray.new(data: bigint_to_bytes(amount.to_i))
66
+ protocol.gasprice = Zilliqa::Proto::ByteArray.new(data: bigint_to_bytes(gas_price.to_i))
67
+ protocol.gaslimit = gas_limit.to_i
68
+ protocol.code = code if code
69
+ protocol.data = data if data
70
+
71
+ Zilliqa::Proto::ProtoTransactionCoreInfo.encode(protocol)
72
+ end
73
+
74
+ def to_payload
75
+ {
76
+ version: version.to_i,
77
+ nonce: nonce.to_i,
78
+ toAddr: Wallet.to_checksum_address(to_addr),
79
+ amount: amount.to_s,
80
+ pubKey: sender_pub_key,
81
+ gasPrice: gas_price.to_s,
82
+ gasLimit: gas_limit.to_i,
83
+ code: code,
84
+ data: data,
85
+ signature: signature
86
+ }
87
+ end
88
+
89
+ def pending?
90
+ @status == TX_STATUSES[:pending]
91
+ end
92
+
93
+ def initialised?
94
+ @status == TX_STATUSES[:initialized]
95
+ end
96
+
97
+ def confirmed?
98
+ @status == TX_STATUSES[:confirmed]
99
+ end
100
+
101
+ def rejected?
102
+ @status == TX_STATUSES[:rejected]
103
+ end
104
+
105
+ # This sets the Transaction instance to a state
106
+ # of pending. Calling this function kicks off a passive loop that polls the
107
+ # lookup node for confirmation on the txHash.
108
+ #
109
+ # The polls are performed with a linear backoff:
110
+ #
111
+ # This is a low-level method that you should generally not have to use
112
+ # directly.
113
+ def confirm(tx_hash, max_attempts = GET_TX_ATTEMPTS, interval = 1)
114
+ @status = TX_STATUSES[:pending]
115
+ 1.upto(max_attempts) do
116
+ return self if track_tx(tx_hash)
117
+
118
+ sleep(interval)
119
+ end
120
+
121
+ self.status = TX_STATUSES[:rejected]
122
+ throw 'The transaction is still not confirmed after ${maxAttempts} attempts.'
123
+ end
124
+
125
+ def track_tx(tx_hash)
126
+ begin
127
+ response = @provider.GetTransaction(tx_hash)
128
+ rescue TrackTxError
129
+ end
130
+
131
+ if response['error']
132
+ return false
133
+ end
134
+
135
+ self.id = response['result']['ID']
136
+ self.receipt = response['result']['receipt']
137
+ receipt['cumulative_gas'] = response['result']['receipt']['cumulative_gas'].to_i
138
+ self.status = receipt && receipt['success'] ? TX_STATUSES[:confirmed] : TX_STATUSES[:rejected]
139
+
140
+ true
141
+ end
142
+
143
+ def submit!
144
+ provider.CreateTransaction(to_payload)
145
+ rescue TransactionError => e
146
+ { error: e }
147
+ end
148
+
149
+ private
150
+
151
+ def bigint_to_bytes(value)
152
+ raise StandardLengthError if value > MAX_BIGINT_BYTES
153
+
154
+ bs = [value / (2**64), value % (2**64)].pack('Q>*')
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,16 @@
1
+ module Zilliqa
2
+ module Account
3
+ class TransactionFactory
4
+ attr_reader :provider, :signer
5
+
6
+ def initialize(provider, signer)
7
+ @provider = provider
8
+ @signer = signer
9
+ end
10
+
11
+ def new(tx_params, to_ds = false)
12
+ Transaction.new(tx_params, @provider, Zilliqa::Account::Transaction::TX_STATUSES[:initialized], to_ds)
13
+ end
14
+ end
15
+ end
16
+ end