laksa 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +50 -0
- data/LICENSE.txt +674 -0
- data/README.md +94 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/laksa.gemspec +46 -0
- data/lib/laksa.rb +21 -0
- data/lib/laksa/account/account.rb +34 -0
- data/lib/laksa/account/transaction.rb +184 -0
- data/lib/laksa/account/transaction_factory.rb +16 -0
- data/lib/laksa/account/wallet.rb +129 -0
- data/lib/laksa/contract/contract.rb +148 -0
- data/lib/laksa/contract/contract_factory.rb +47 -0
- data/lib/laksa/crypto/key_store.rb +113 -0
- data/lib/laksa/crypto/key_tool.rb +61 -0
- data/lib/laksa/crypto/schnorr.rb +146 -0
- data/lib/laksa/jsonrpc/provider.rb +23 -0
- data/lib/laksa/proto/message.proto +44 -0
- data/lib/laksa/proto/message_pb.rb +46 -0
- data/lib/laksa/util/bech32.rb +28 -0
- data/lib/laksa/util/unit.rb +37 -0
- data/lib/laksa/util/util.rb +17 -0
- data/lib/laksa/util/validator.rb +40 -0
- data/lib/laksa/version.rb +3 -0
- metadata +201 -0
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Laksa
|
2
|
+
|
3
|
+
Laksa -- Zilliqa Blockchain Ruby Library
|
4
|
+
|
5
|
+
The project is still under development.
|
6
|
+
|
7
|
+
## Requirement
|
8
|
+
|
9
|
+
Ruby(2.5.3)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'laksa'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install laksa
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Generate A new address
|
30
|
+
```ruby
|
31
|
+
private_key = Laksa::Crypto::KeyTool.generate_private_key
|
32
|
+
public_key = Laksa::Crypto::KeyTool.get_public_key_from_private_key(private_key)
|
33
|
+
address = Laksa::Crypto::KeyTool.get_address_from_private_key(private_key)
|
34
|
+
```
|
35
|
+
|
36
|
+
### Validate an address
|
37
|
+
```ruby
|
38
|
+
address = '2624B9EA4B1CD740630F6BF2FEA82AAC0067070B'
|
39
|
+
Laksa::Util::Validator.address?(address)
|
40
|
+
```
|
41
|
+
|
42
|
+
### Validate checksum address
|
43
|
+
```ruby
|
44
|
+
checksum_address = '0x4BAF5faDA8e5Db92C3d3242618c5B47133AE003C'
|
45
|
+
Laksa::Util::Validator.checksum_address?(checksum_address)
|
46
|
+
```
|
47
|
+
|
48
|
+
### Deploy and Call a transaction
|
49
|
+
```ruby
|
50
|
+
private_key = "e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930"
|
51
|
+
|
52
|
+
provider = Laksa::Jsonrpc::Provider.new('https://dev-api.zilliqa.com')
|
53
|
+
wallet = Laksa::Account::Wallet.new(provider)
|
54
|
+
address = wallet.add_by_private_key(private_key)
|
55
|
+
|
56
|
+
factory = Laksa::Contract::ContractFactory.new(provider, wallet)
|
57
|
+
|
58
|
+
contract = factory.new_contract(TEST_CONTRACT, [
|
59
|
+
{
|
60
|
+
vname: 'owner',
|
61
|
+
type: 'ByStr20',
|
62
|
+
value: '0x124567890124567890124567890124567890',
|
63
|
+
},
|
64
|
+
],
|
65
|
+
ABI,
|
66
|
+
)
|
67
|
+
|
68
|
+
deploy_params = Laksa::Contract::DeployParams.new(nil, Laksa::Util.pack(8, 8), nil, 1000, 1000, nil)
|
69
|
+
tx, deployed = contract.deploy(deploy_params)
|
70
|
+
|
71
|
+
assert tx.confirmed?
|
72
|
+
assert deployed.deployed?
|
73
|
+
assert_equal Laksa::Contract::ContractStatus::DEPLOYED, deployed.status
|
74
|
+
|
75
|
+
assert /[A-F0-9]+/ =~ contract.address
|
76
|
+
|
77
|
+
# call a deployed contract
|
78
|
+
call_tx = deployed.call(
|
79
|
+
'setHello',
|
80
|
+
[
|
81
|
+
{ vname: 'msg', type: 'String', value: 'Hello World!' },
|
82
|
+
],
|
83
|
+
{
|
84
|
+
version: Laksa::Util.pack(8, 8),
|
85
|
+
amount: 0,
|
86
|
+
gasPrice: 1000,
|
87
|
+
gasLimit: 1000
|
88
|
+
})
|
89
|
+
|
90
|
+
|
91
|
+
receipt = call_tx.tx_params.receipt
|
92
|
+
```
|
93
|
+
|
94
|
+
the definition of [TEST_CONTRACT](https://github.com/FireStack-Lab/LaksaRuby/blob/master/test/contract/test_contract.rb) and [ABI](https://github.com/FireStack-Lab/LaksaRuby/blob/master/test/contract/test_abi.rb) can be found in this folder.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "laksa"
|
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
data/laksa.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "laksa/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "laksa"
|
8
|
+
spec.version = Laksa::VERSION
|
9
|
+
spec.authors = ["cenyongh"]
|
10
|
+
spec.email = ["cenyongh@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{LaksaRuby -- Zilliqa Blockchain Library}
|
13
|
+
spec.description = %q{LaksaRuby -- Zilliqa Blockchain Library}
|
14
|
+
spec.homepage = "https://github.com/FireStack-Lab/LaksaRuby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/FireStack-Lab/LaksaRuby"
|
22
|
+
spec.metadata["changelog_uri"] = "https://github.com/FireStack-Lab/LaksaRuby"
|
23
|
+
else
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
25
|
+
"public gem pushes."
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specify which files should be added to the gem when it is released.
|
29
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
31
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
32
|
+
end
|
33
|
+
spec.bindir = "exe"
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
|
37
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
38
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
39
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
40
|
+
spec.add_dependency "bitcoin-secp256k1"
|
41
|
+
spec.add_dependency "scrypt"
|
42
|
+
spec.add_dependency "pbkdf2-ruby"
|
43
|
+
spec.add_dependency "jsonrpc-client"
|
44
|
+
spec.add_dependency 'google-protobuf'
|
45
|
+
spec.add_dependency 'bitcoin-ruby'
|
46
|
+
end
|
data/lib/laksa.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "laksa/version"
|
2
|
+
require 'laksa/crypto/key_tool'
|
3
|
+
require 'laksa/crypto/key_store'
|
4
|
+
require 'laksa/crypto/schnorr'
|
5
|
+
require 'laksa/jsonrpc/provider'
|
6
|
+
require 'laksa/account/account'
|
7
|
+
require 'laksa/account/wallet'
|
8
|
+
require 'laksa/account/transaction_factory'
|
9
|
+
require 'laksa/account/transaction'
|
10
|
+
require 'laksa/proto/message_pb'
|
11
|
+
require 'laksa/contract/contract_factory'
|
12
|
+
require 'laksa/contract/contract'
|
13
|
+
require 'laksa/util/validator'
|
14
|
+
require 'laksa/util/util'
|
15
|
+
require 'laksa/util/unit'
|
16
|
+
require 'laksa/util/bech32'
|
17
|
+
|
18
|
+
|
19
|
+
module Laksa
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Laksa
|
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 = Laksa::Crypto::KeyTool.get_public_key_from_private_key(private_key, true)
|
8
|
+
@address = Laksa::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 = Laksa::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 = Laksa::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
|
+
Laksa::Crypto::Schnorr.sign(message_hex, @private_key, @public_key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module Laksa
|
2
|
+
module Account
|
3
|
+
#
|
4
|
+
# Transaction
|
5
|
+
#
|
6
|
+
# Transaction is a functor. Its purpose is to encode the possible states a
|
7
|
+
# Transaction can be in: Confirmed, Rejected, Pending, or Initialised (i.e., not broadcasted).
|
8
|
+
class Transaction
|
9
|
+
attr_accessor :id, :version, :nonce, :amount, :gas_price, :gas_limit, :signature, :receipt, :sender_pub_key, :to_addr, :code, :data, :to_ds
|
10
|
+
attr_accessor :provider, :status
|
11
|
+
|
12
|
+
GET_TX_ATTEMPTS = 33
|
13
|
+
|
14
|
+
def initialize(tx_params, provider, status = TxStatus::INITIALIZED, to_ds = false)
|
15
|
+
if tx_params
|
16
|
+
@version = tx_params.version;
|
17
|
+
@nonce = tx_params.nonce
|
18
|
+
@amount = tx_params.amount
|
19
|
+
@gas_price = tx_params.gas_price
|
20
|
+
@gas_limit = tx_params.gas_limit
|
21
|
+
@signature = tx_params.signature
|
22
|
+
@receipt = tx_params.receipt
|
23
|
+
@sender_pub_key = tx_params.sender_pub_key
|
24
|
+
@to_addr = tx_params.to_addr.downcase
|
25
|
+
@code = tx_params.code
|
26
|
+
@data = tx_params.data
|
27
|
+
end
|
28
|
+
|
29
|
+
@provider = provider
|
30
|
+
@status = status
|
31
|
+
@to_ds = to_ds
|
32
|
+
end
|
33
|
+
|
34
|
+
# constructs an already-confirmed transaction.
|
35
|
+
def self.confirm(tx_params, provider)
|
36
|
+
Transaction.new(tx_params, provider, TxStatus::CONFIRMED)
|
37
|
+
end
|
38
|
+
|
39
|
+
# constructs an already-rejected transaction.
|
40
|
+
def self.reject(tx_params, provider)
|
41
|
+
Transaction.new(tx_params, provider, TxStatus::REJECTED)
|
42
|
+
end
|
43
|
+
|
44
|
+
def bytes
|
45
|
+
protocol = Laksa::Proto::ProtoTransactionCoreInfo.new
|
46
|
+
protocol.version = self.version
|
47
|
+
protocol.nonce = self.nonce
|
48
|
+
protocol.toaddr = Util.decode_hex(self.to_addr.downcase.sub('0x',''))
|
49
|
+
protocol.senderpubkey = Laksa::Proto::ByteArray.new(data: Util.decode_hex(self.sender_pub_key))
|
50
|
+
|
51
|
+
raise 'standard length exceeded for value' if self.amount.to_i > 2 ** 128 - 1
|
52
|
+
|
53
|
+
protocol.amount = Laksa::Proto::ByteArray.new(data: bigint_to_bytes(self.amount.to_i))
|
54
|
+
protocol.gasprice = Laksa::Proto::ByteArray.new(data: bigint_to_bytes(self.gas_price.to_i))
|
55
|
+
protocol.gaslimit = self.gas_limit
|
56
|
+
protocol.code = self.code if self.code
|
57
|
+
protocol.data = self.data if self.data
|
58
|
+
|
59
|
+
Laksa::Proto::ProtoTransactionCoreInfo.encode(protocol)
|
60
|
+
end
|
61
|
+
|
62
|
+
def tx_params
|
63
|
+
tx_params = TxParams.new
|
64
|
+
|
65
|
+
tx_params.id = self.id
|
66
|
+
tx_params.version = self.version
|
67
|
+
tx_params.nonce = self.nonce
|
68
|
+
tx_params.amount = self.amount
|
69
|
+
tx_params.gas_price = self.gas_price
|
70
|
+
tx_params.gas_limit = self.gas_limit
|
71
|
+
tx_params.signature = self.signature
|
72
|
+
tx_params.receipt = self.receipt
|
73
|
+
tx_params.sender_pub_key = self.sender_pub_key
|
74
|
+
tx_params.to_addr = Wallet.to_checksum_address(self.to_addr)
|
75
|
+
tx_params.code = self.code
|
76
|
+
tx_params.data = self.data
|
77
|
+
|
78
|
+
tx_params
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_payload
|
82
|
+
{
|
83
|
+
version: self.version.to_i,
|
84
|
+
nonce: self.nonce.to_i,
|
85
|
+
toAddr: Wallet.to_checksum_address(self.to_addr),
|
86
|
+
amount: self.amount,
|
87
|
+
pubKey: self.sender_pub_key,
|
88
|
+
gasPrice: self.gas_price,
|
89
|
+
gasLimit: self.gas_limit,
|
90
|
+
code: self.code,
|
91
|
+
data: self.data,
|
92
|
+
signature: self.signature
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def pending?
|
97
|
+
@status == TxStatus::PENDING
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialised?
|
101
|
+
@status === TxStatus::INITIALIZED
|
102
|
+
end
|
103
|
+
|
104
|
+
def confirmed?
|
105
|
+
@status === TxStatus::CONFIRMED;
|
106
|
+
end
|
107
|
+
|
108
|
+
def rejected?
|
109
|
+
@status === TxStatus::REJECTED;
|
110
|
+
end
|
111
|
+
|
112
|
+
# This sets the Transaction instance to a state
|
113
|
+
# of pending. Calling this function kicks off a passive loop that polls the
|
114
|
+
# lookup node for confirmation on the txHash.
|
115
|
+
#
|
116
|
+
# The polls are performed with a linear backoff:
|
117
|
+
#
|
118
|
+
# This is a low-level method that you should generally not have to use
|
119
|
+
# directly.
|
120
|
+
def confirm(tx_hash, max_attempts = GET_TX_ATTEMPTS, interval = 1)
|
121
|
+
@status = TxStatus::PENDING
|
122
|
+
1.upto(max_attempts) do
|
123
|
+
if self.track_tx(tx_hash)
|
124
|
+
return self
|
125
|
+
else
|
126
|
+
sleep(interval)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
self.status = TxStatus::REJECTED
|
131
|
+
throw 'The transaction is still not confirmed after ${maxAttempts} attempts.'
|
132
|
+
end
|
133
|
+
|
134
|
+
def track_tx(tx_hash)
|
135
|
+
puts "tracking transaction: #{tx_hash}"
|
136
|
+
|
137
|
+
begin
|
138
|
+
response = @provider.GetTransaction(tx_hash)
|
139
|
+
rescue Exception => e
|
140
|
+
puts "transaction not confirmed yet"
|
141
|
+
puts e
|
142
|
+
end
|
143
|
+
|
144
|
+
if response['error']
|
145
|
+
puts "transaction not confirmed yet"
|
146
|
+
return false;
|
147
|
+
end
|
148
|
+
|
149
|
+
self.id = response['result']['ID']
|
150
|
+
self.receipt = response['result']['receipt']
|
151
|
+
self.receipt['cumulative_gas'] = response['result']['receipt']['cumulative_gas'].to_i
|
152
|
+
|
153
|
+
if self.receipt && self.receipt['success']
|
154
|
+
puts "Transaction confirmed!"
|
155
|
+
self.status = TxStatus::CONFIRMED
|
156
|
+
else
|
157
|
+
puts "Transaction rejected!"
|
158
|
+
self.status = TxStatus::REJECTED
|
159
|
+
end
|
160
|
+
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
def bigint_to_bytes(value)
|
166
|
+
raise 'standard length exceeded for value' if value > 2 ** 128 - 1
|
167
|
+
bs = [value / (2 ** 64), value % (2 ** 64)].pack('Q>*')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class TxParams
|
172
|
+
attr_accessor :id, :version, :nonce, :amount, :gas_price, :gas_limit, :signature, :receipt, :sender_pub_key, :to_addr, :code, :data
|
173
|
+
def initialize
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class TxStatus
|
178
|
+
INITIALIZED = 0
|
179
|
+
PENDING = 1
|
180
|
+
CONFIRMED = 2
|
181
|
+
REJECTED = 3
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Laksa
|
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, TxStatus::INITIALIZED, to_ds)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Laksa
|
4
|
+
module Account
|
5
|
+
class Wallet
|
6
|
+
# Takes an array of Account objects and instantiates a Wallet instance.
|
7
|
+
def initialize(provider = nil, accounts = {})
|
8
|
+
@provider = provider
|
9
|
+
@accounts = accounts
|
10
|
+
if accounts.length > 0
|
11
|
+
@default_account = accounts[0]
|
12
|
+
else
|
13
|
+
@default_account = nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Creates a new keypair with a randomly-generated private key. The new
|
18
|
+
# account is accessible by address.
|
19
|
+
def create
|
20
|
+
private_key = Laksa::Crypto::KeyTool.generate_private_key
|
21
|
+
account = Laksa::Account::Account.new(private_key)
|
22
|
+
|
23
|
+
@accounts[account.address] = account
|
24
|
+
|
25
|
+
@default_account = account unless @default_account
|
26
|
+
|
27
|
+
account.address
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds an account to the wallet by private key.
|
31
|
+
def add_by_private_key(private_key)
|
32
|
+
account = Laksa::Account::Account.new(private_key)
|
33
|
+
|
34
|
+
@accounts[account.address] = account
|
35
|
+
|
36
|
+
@default_account = account unless @default_account
|
37
|
+
|
38
|
+
account.address
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Adds an account by keystore
|
43
|
+
def add_by_keystore(keystore, passphrase)
|
44
|
+
account = Laksa::Account::Account.from_file(keystore, passphrase)
|
45
|
+
|
46
|
+
@accounts[account.address] = account
|
47
|
+
|
48
|
+
@default_account = account unless @default_account
|
49
|
+
|
50
|
+
account.address
|
51
|
+
end
|
52
|
+
|
53
|
+
# Removes an account from the wallet and returns boolean to indicate
|
54
|
+
# failure or success.
|
55
|
+
|
56
|
+
def remove(address)
|
57
|
+
if @accounts.has_key?(address)
|
58
|
+
@accounts.delete(address)
|
59
|
+
|
60
|
+
true
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sets the default account of the wallet.
|
67
|
+
def set_default(address)
|
68
|
+
@default_account = @accounts[address]
|
69
|
+
end
|
70
|
+
|
71
|
+
# to_checksum_address
|
72
|
+
#
|
73
|
+
# takes hex-encoded string and returns the corresponding address
|
74
|
+
#
|
75
|
+
# @param {string} address
|
76
|
+
# @returns {string}
|
77
|
+
def self.to_checksum_address(address)
|
78
|
+
address = address.downcase.gsub('0x', '')
|
79
|
+
|
80
|
+
s1 = Digest::SHA256.hexdigest(Util.decode_hex(address))
|
81
|
+
v = s1.to_i(base=16)
|
82
|
+
|
83
|
+
ret = ['0x']
|
84
|
+
address.each_char.each_with_index do |c, idx|
|
85
|
+
if '1234567890'.include?(c)
|
86
|
+
ret << c
|
87
|
+
else
|
88
|
+
ret << ((v & (2 ** (255 - 6 * idx))) < 1 ? c.downcase : c.upcase)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
ret.join
|
93
|
+
end
|
94
|
+
|
95
|
+
# signs an unsigned transaction with the default account.
|
96
|
+
def sign(tx)
|
97
|
+
tx_params = tx.tx_params
|
98
|
+
if tx_params.sender_pub_key
|
99
|
+
# attempt to find the address
|
100
|
+
address = Laksa::Crypto::KeyTool.get_address_from_public_key(tx_params.sender_pub_key)
|
101
|
+
account = @accounts[address]
|
102
|
+
raise 'Could not sign the transaction with address as it does not exist' unless account
|
103
|
+
|
104
|
+
self.sign_with(tx, address)
|
105
|
+
else
|
106
|
+
raise 'This wallet has no default account.' unless @default_account
|
107
|
+
|
108
|
+
self.sign_with(tx, @default_account.address)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def sign_with(tx, address)
|
113
|
+
account = @accounts[address]
|
114
|
+
|
115
|
+
raise 'The selected account does not exist on this Wallet instance.' unless account
|
116
|
+
|
117
|
+
if tx.nonce == nil
|
118
|
+
result = @provider.GetBalance(account.address)
|
119
|
+
tx.nonce = result['nonce'].to_i + 1
|
120
|
+
end
|
121
|
+
|
122
|
+
tx.sender_pub_key = account.public_key
|
123
|
+
sig = account.sign_transaction(tx)
|
124
|
+
tx.signature = sig.to_s
|
125
|
+
tx
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|