cryptos 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.travis.yml +13 -5
- data/Gemfile.lock +11 -1
- data/README.md +100 -4
- data/TODOs.org +1 -0
- data/bin/run +19 -0
- data/cryptos.gemspec +2 -0
- data/lib/cryptos.rb +4 -2
- data/lib/cryptos/address.rb +8 -4
- data/lib/cryptos/base58.rb +25 -20
- data/lib/cryptos/bitcoin/script.rb +4 -2
- data/lib/cryptos/connectors/cli.rb +63 -25
- data/lib/cryptos/der.rb +22 -18
- data/lib/cryptos/elliptic_curve.rb +11 -7
- data/lib/cryptos/input.rb +18 -7
- data/lib/cryptos/output.rb +12 -3
- data/lib/cryptos/private_key.rb +3 -0
- data/lib/cryptos/public_key.rb +23 -13
- data/lib/cryptos/script.rb +87 -11
- data/lib/cryptos/transaction.rb +41 -13
- data/lib/cryptos/utils/bytes.rb +20 -0
- data/lib/cryptos/utils/hashes.rb +21 -0
- data/lib/cryptos/utils/hexas.rb +39 -0
- data/lib/cryptos/version.rb +1 -1
- metadata +34 -4
- data/lib/cryptos/base.rb +0 -74
- data/lib/cryptos/hashing.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 821b977f9586beced716c6274e4c4de1b2bd59eacb2ca2d1f00ba7555cda7a46
|
4
|
+
data.tar.gz: aef2d8dcd9b5ef0155925df43d89fb8738fe09cfa1e99d9d78065a82ee3bcf6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc3b5bc76776036438e1bb65efa1988f7fd0c8c12adcd57feab46bf94103086a024b5e92b7774891e27eaa9ed330b48ea7e841ddd87bbe5e816d2762f2cee353
|
7
|
+
data.tar.gz: 51fb82e5739d2a160f7968dcc8fa37f72b805614e85407051789b3b060c08a49a8153e9cba83f212a8aa236c5325f3a2f7ff05ca91cc2b42c650924a2e8c22f6
|
data/.travis.yml
CHANGED
@@ -5,6 +5,13 @@ cache: bundler
|
|
5
5
|
rvm:
|
6
6
|
- 2.5.3
|
7
7
|
|
8
|
+
env:
|
9
|
+
global:
|
10
|
+
- CC_TEST_REPORTER_ID=38c8686e073a8123e72d8f7c580edbfae244922662e6202e9a964152b6b234a0
|
11
|
+
|
12
|
+
apt_packages:
|
13
|
+
- bitcoind
|
14
|
+
|
8
15
|
before_install:
|
9
16
|
- gem install bundler -v 1.16.6
|
10
17
|
- sudo apt-add-repository ppa:bitcoin/bitcoin -y
|
@@ -13,13 +20,14 @@ before_install:
|
|
13
20
|
- wget https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-x86_64-linux-gnu.tar.gz
|
14
21
|
- tar -xvf litecoin-0.16.3-x86_64-linux-gnu.tar.gz
|
15
22
|
- export PATH=./litecoin-0.16.3/bin:$PATH
|
16
|
-
-
|
17
|
-
|
18
|
-
apt_packages:
|
19
|
-
- bitcoind
|
23
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
24
|
+
- chmod +x ./cc-test-reporter
|
20
25
|
|
21
26
|
before_script:
|
22
|
-
# - mkdir -p /home/travis/.bitcoin && cp bitcoin.conf /home/travis/.bitcoin/bitcoin.conf
|
23
27
|
- bitcoind -regtest -daemon
|
24
28
|
- litecoind -regtest -daemon
|
25
29
|
- sleep 15
|
30
|
+
- ./cc-test-reporter before-build
|
31
|
+
|
32
|
+
after_script:
|
33
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cryptos (0.0.
|
4
|
+
cryptos (0.0.2)
|
5
5
|
hashie
|
6
6
|
httparty
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
bump (0.7.0)
|
11
12
|
coderay (1.1.2)
|
12
13
|
diff-lcs (1.3)
|
14
|
+
docile (1.3.1)
|
13
15
|
ecdsa (1.2.0)
|
14
16
|
hashie (3.6.0)
|
15
17
|
httparty (0.16.3)
|
16
18
|
mime-types (~> 3.0)
|
17
19
|
multi_xml (>= 0.5.2)
|
20
|
+
json (2.1.0)
|
18
21
|
method_source (0.9.2)
|
19
22
|
mime-types (3.2.2)
|
20
23
|
mime-types-data (~> 3.2015)
|
@@ -37,17 +40,24 @@ GEM
|
|
37
40
|
diff-lcs (>= 1.2.0, < 2.0)
|
38
41
|
rspec-support (~> 3.8.0)
|
39
42
|
rspec-support (3.8.0)
|
43
|
+
simplecov (0.16.1)
|
44
|
+
docile (~> 1.1)
|
45
|
+
json (>= 1.8, < 3)
|
46
|
+
simplecov-html (~> 0.10.0)
|
47
|
+
simplecov-html (0.10.2)
|
40
48
|
|
41
49
|
PLATFORMS
|
42
50
|
ruby
|
43
51
|
|
44
52
|
DEPENDENCIES
|
53
|
+
bump
|
45
54
|
bundler
|
46
55
|
cryptos!
|
47
56
|
ecdsa
|
48
57
|
pry
|
49
58
|
rake
|
50
59
|
rspec
|
60
|
+
simplecov
|
51
61
|
|
52
62
|
BUNDLED WITH
|
53
63
|
1.17.1
|
data/README.md
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# Cryptos
|
2
2
|
[](https://travis-ci.org/icostan/cryptos)
|
3
|
-
[](https://codeclimate.com/github/icostan/cryptos/maintainability)
|
4
|
+
[](https://codeclimate.com/github/icostan/cryptos/test_coverage)
|
5
|
+
[](https://badge.fury.io/rb/cryptos)
|
4
6
|
|
5
|
-
|
7
|
+
Cryptos project is meant to provide an unified Ruby API to work with different crypto-currencies but the ultimate goal is to support atomic-swaps between any two coins.
|
6
8
|
|
7
|
-
TODO: Delete this and the text above, and describe your gem
|
8
9
|
|
9
10
|
## Installation
|
10
11
|
|
@@ -24,7 +25,102 @@ Or install it yourself as:
|
|
24
25
|
|
25
26
|
## Usage
|
26
27
|
|
27
|
-
|
28
|
+
### Scenario 1: Spend coinbase transaction
|
29
|
+
|
30
|
+
Alright, let's begin, first thing first, lets generate private and public keys:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
2.5.3 :001 > private_key = Cryptos::PrivateKey.generate
|
34
|
+
=> #<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>
|
35
|
+
2.5.3 :002 > public_key = Cryptos::PublicKey.new private_key
|
36
|
+
=> #<Cryptos::PublicKey:0x00007f8cc105ed58 @private_key=#<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=107779388491921327681974754398507503201871466663959093103394577491037829153768, @y=78060352001932916201234328232450653863791592111885208305671830584742527863131>
|
37
|
+
```
|
38
|
+
|
39
|
+
Based in publik key above lets create a Bitcoin address:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
2.5.3 :003 > from_address = Cryptos::Bitcoin::Address.new public_key
|
43
|
+
=> #<Cryptos::Bitcoin::Address:0x00007f8cc12fc560 @public_key=#<Cryptos::PublicKey:0x00007f8cc105ed58 @private_key=#<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=107779388491921327681974754398507503201871466663959093103394577491037829153768, @y=78060352001932916201234328232450653863791592111885208305671830584742527863131>, @testnet=true>
|
44
|
+
```
|
45
|
+
|
46
|
+
Before going any further we need to install bitcoin-core daemon and start node in regtest mode:
|
47
|
+
|
48
|
+
```shell
|
49
|
+
# in MacOS
|
50
|
+
brew install bitcoin
|
51
|
+
|
52
|
+
# in Linux (Debian based)
|
53
|
+
apt-get install bitcoin
|
54
|
+
|
55
|
+
# start Bitcoin daemon in regtest mode
|
56
|
+
bitcoin-cli -regtest -printtoconsole
|
57
|
+
```
|
58
|
+
|
59
|
+
Now we create a simple Cli connector that will communicate to underlying bitcoin daemon.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
2.5.3 :004 > cli = Cryptos::Connectors::Cli.new
|
63
|
+
=> #<Cryptos::Connectors::Cli:0x00007f8cc12ece30 @program="bitcoin-cli", @network="regtest", @verbose=false>
|
64
|
+
```
|
65
|
+
|
66
|
+
Import address into node and generate 101 blocks. If you ask why 101 then it is because coinbase transactions are spendable after 100 confirmatinos.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
2.5.3 :005 > from_address.import cli
|
70
|
+
=> true
|
71
|
+
2.5.3 :006 > cli.generate_to_address from_address, blocks: 101
|
72
|
+
=> true
|
73
|
+
```
|
74
|
+
|
75
|
+
Generate and import destination address to send BTC to then check that it has no money in it.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
2.5.3 :007 > to_address = Cryptos::Bitcoin::Address.new Cryptos::PublicKey.new Cryptos::PrivateKey.generate
|
79
|
+
=> #<Cryptos::Bitcoin::Address:0x00007f8cc134f2b0 @public_key=#<Cryptos::PublicKey:0x00007f8cc128fa78 @private_key=#<Cryptos::PrivateKey:0x00007f8cc128faa0 @value=104555233989943463494354097619221894829574308702717051161491781222000198727347, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=1402024405898287938501468401055931693243587868828983898835308320263377717122, @y=89146164815925753866667564550747587615674131412309491381641677989226156891240>, @testnet=true>
|
80
|
+
2.5.3 :008 > to_address.import cli
|
81
|
+
=> true
|
82
|
+
2.5.3 :009 > cli.get_received_by_address to_address
|
83
|
+
=> "0.00000000"
|
84
|
+
```
|
85
|
+
|
86
|
+
Alright, now we get to real stuff, transactions: create input from our ```from_address```, send 123_456_789 Satoshis (1.23456789 BTC) to our ```to_address``` and change amount back to ```from_address```.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
2.5.3 :010 > input = Cryptos::Input.from_utxo cli, from_address
|
90
|
+
=> #<struct Cryptos::Input value=2500000000.0, tx_hash="33fc8506d7a5880cfddca3c950f95fa461398fb764da4527169d5574a7c00c7b", index=0, script_sig=nil, sequence=68719476735>
|
91
|
+
2.5.3 :011 > output = Cryptos::Output.p2pkh to_address, 123_456_789
|
92
|
+
=> #<struct Cryptos::Output value=123456789, script_pubkey=#<Cryptos::Script:0x00007f8cc12ed8a8 @script="OP_DUP OP_HASH160 9aae79929e4364ab3aabe1f83a875304d1b67a3a OP_EQUALVERIFY OP_CHECKSIG">>
|
93
|
+
2.5.3 :012 > change = Cryptos::Output.p2pkh_change from_address, input, output
|
94
|
+
=> #<struct Cryptos::Output value=2376533211.0, script_pubkey=#<Cryptos::Script:0x00007f8cc12cfdd0 @script="OP_DUP OP_HASH160 57a58e05aedfbb6bd97b373baf65ce7cc318351b OP_EQUALVERIFY OP_CHECKSIG">>
|
95
|
+
2.5.3 :013 > transaction = Cryptos::Transaction.from_ioc input, output, change
|
96
|
+
=> #<struct Cryptos::Transaction version=1, inputs=[#<struct Cryptos::Input value=2500000000.0, tx_hash="33fc8506d7a5880cfddca3c950f95fa461398fb764da4527169d5574a7c00c7b", index=0, script_sig=nil, sequence=68719476735>], outputs=[#<struct Cryptos::Output value=123456789, script_pubkey=#<Cryptos::Script:0x00007f8cc12ed8a8 @script="OP_DUP OP_HASH160 9aae79929e4364ab3aabe1f83a875304d1b67a3a OP_EQUALVERIFY OP_CHECKSIG">>, #<struct Cryptos::Output value=2376533211.0, script_pubkey=#<Cryptos::Script:0x00007f8cc12cfdd0 @script="OP_DUP OP_HASH160 57a58e05aedfbb6bd97b373baf65ce7cc318351b OP_EQUALVERIFY OP_CHECKSIG">>], locktime=0>
|
97
|
+
```
|
98
|
+
|
99
|
+
Sign and broadcast the transaction:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
2.5.3 :014 > transaction.sign_single_input from_address
|
103
|
+
=> "01000000017b0cc0a774559d162745da64b78f3961a45ff950c9a3dcfd0c88a5d70685fc33000000006a473044022020b53986c2ef08d54137e57f1c231a0c2fe1b6dc88c7208ecef6f7474bae985002203027db653202da53ce081da46431ef1f88f3e1bf47254940a58740a86506cbc3012103ee48f8db1d9a5dfc1b620dbe9566b77d995e0325b91d3b661a697272920f43e8ffffffff0215cd5b07000000001976a9149aae79929e4364ab3aabe1f83a875304d1b67a3a88acdb04a78d000000001976a91457a58e05aedfbb6bd97b373baf65ce7cc318351b88ac00000000"
|
104
|
+
2.5.3 :015 > transaction.broadcast cli
|
105
|
+
=> true
|
106
|
+
```
|
107
|
+
|
108
|
+
Mine new block that will contain our hand crafted transaction and VOILA! output amount was transafered to new address.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
2.5.3 :016 > cli.generate blocks: 1
|
112
|
+
=> true
|
113
|
+
2.5.3 :017 > cli.get_received_by_address to_address
|
114
|
+
=> "1.23456789"
|
115
|
+
```
|
116
|
+
|
117
|
+
### Scenario 2: Spend multisig transaction
|
118
|
+
|
119
|
+
TBD
|
120
|
+
|
121
|
+
### Scenario 3: Atomic swaps between BTC and LTC
|
122
|
+
|
123
|
+
TBD
|
28
124
|
|
29
125
|
## Development
|
30
126
|
|
data/TODOs.org
CHANGED
data/bin/run
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
private_key = Cryptos::PrivateKey.generate
|
4
|
+
public_key = Cryptos::PublicKey.new private_key
|
5
|
+
from_address = Cryptos::Bitcoin::Address.new public_key
|
6
|
+
cli = Cryptos::Connectors::Cli.new
|
7
|
+
from_address.import cli
|
8
|
+
cli.generate_to_address from_address, blocks: 101
|
9
|
+
to_address = Cryptos::Bitcoin::Address.new Cryptos::PublicKey.new Cryptos::PrivateKey.generate
|
10
|
+
to_address.import cli
|
11
|
+
cli.get_received_by_address to_address
|
12
|
+
input = Cryptos::Input.from_utxo cli, from_address
|
13
|
+
output = Cryptos::Output.p2pkh to_address, 123_456_789
|
14
|
+
change = Cryptos::Output.p2pkh_change from_address, input, output
|
15
|
+
transaction = Cryptos::Transaction.from_ioc input, output, change
|
16
|
+
transaction.sign_single_input from_address
|
17
|
+
transaction.broadcast cli
|
18
|
+
cli.generate blocks: 1
|
19
|
+
cli.get_received_by_address to_address
|
data/cryptos.gemspec
CHANGED
data/lib/cryptos.rb
CHANGED
@@ -3,9 +3,11 @@ require 'hashie'
|
|
3
3
|
|
4
4
|
require 'cryptos/version'
|
5
5
|
|
6
|
-
require 'cryptos/
|
6
|
+
require 'cryptos/utils/bytes'
|
7
|
+
require 'cryptos/utils/hexas'
|
8
|
+
require 'cryptos/utils/hashes'
|
9
|
+
|
7
10
|
require 'cryptos/base58'
|
8
|
-
require 'cryptos/hashing'
|
9
11
|
require 'cryptos/elliptic_curve'
|
10
12
|
require 'cryptos/connectors/cli'
|
11
13
|
|
data/lib/cryptos/address.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Cryptos
|
2
2
|
class Address
|
3
|
-
include Base58,
|
4
|
-
extend Base58,
|
3
|
+
include Base58, Utils::Hashes
|
4
|
+
extend Base58, Utils::Hashes
|
5
5
|
|
6
6
|
attr_reader :public_key
|
7
7
|
attr_reader :testnet
|
@@ -16,7 +16,7 @@ module Cryptos
|
|
16
16
|
#
|
17
17
|
def generate(network)
|
18
18
|
prefix = network.to_s(16).rjust 2, '0'
|
19
|
-
ripemd160 = hash160 public_key.
|
19
|
+
ripemd160 = hash160 public_key.to_sec
|
20
20
|
with_version = "#{prefix}#{ripemd160}"
|
21
21
|
checksum = hash256(with_version)[0, 8]
|
22
22
|
wrap_encode = "#{with_version}#{checksum}"
|
@@ -28,7 +28,7 @@ module Cryptos
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_hash160
|
31
|
-
hash160(public_key.
|
31
|
+
hash160(public_key.to_sec)
|
32
32
|
end
|
33
33
|
|
34
34
|
def p2pkh
|
@@ -39,6 +39,10 @@ module Cryptos
|
|
39
39
|
generate p2sh_prefix
|
40
40
|
end
|
41
41
|
|
42
|
+
def import(cli)
|
43
|
+
cli.import_address self
|
44
|
+
end
|
45
|
+
|
42
46
|
def to_s
|
43
47
|
p2pkh
|
44
48
|
end
|
data/lib/cryptos/base58.rb
CHANGED
@@ -1,24 +1,29 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module Cryptos
|
2
|
+
module Base58
|
3
|
+
include Utils::Bytes
|
4
|
+
|
5
|
+
def base58_encode(ripe160_hash)
|
6
|
+
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
7
|
+
value = ripe160_hash.to_i 16
|
8
|
+
output = ''
|
9
|
+
while value > 0
|
10
|
+
remainder = value % 58
|
11
|
+
value /= 58
|
12
|
+
output += alphabet[remainder]
|
13
|
+
end
|
14
|
+
output += alphabet[0] * [ripe160_hash].pack('H*').bytes.find_index{|b| b != 0}
|
15
|
+
output.reverse
|
10
16
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
|
18
|
+
def base58_decode(address)
|
19
|
+
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
20
|
+
int_val = 0
|
21
|
+
address.reverse.chars.each_with_index do |char, index|
|
22
|
+
char_index = alphabet.index(char)
|
23
|
+
int_val += char_index * 58**index
|
24
|
+
end
|
25
|
+
# TODO: hard coded 25 bytes?
|
26
|
+
bignum_to_bytes(int_val, 25).unpack('H*').first
|
20
27
|
end
|
21
|
-
# TODO: hard coded 25 bytes?
|
22
|
-
bignum_to_bytes(int_val, 25).unpack('H*').first
|
23
28
|
end
|
24
29
|
end
|
@@ -1,30 +1,68 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
1
|
+
module Cryptos
|
2
|
+
module Connectors
|
3
|
+
class Cli
|
4
|
+
attr_reader :program, :network, :verbose
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize(program: 'bitcoin-cli', network: 'regtest', verbose: false)
|
7
|
+
@program = program
|
8
|
+
@network = network
|
9
|
+
@verbose = verbose
|
10
|
+
end
|
11
|
+
|
12
|
+
def import_address(address, run_mode: :system)
|
13
|
+
run "importaddress #{address} '' false", run_mode: run_mode
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_to_address(address, blocks: 101, run_mode: :system, verbose: false)
|
17
|
+
run "generatetoaddress #{blocks} #{address}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate(blocks: 1, run_mode: :system)
|
21
|
+
run "generate #{blocks}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_unspent(address, run_mode: :inline)
|
25
|
+
run "listunspent 1 9999 \"[\\\"#{address}\\\"]\"", run_mode: run_mode
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_raw_transaction(rawtx, run_mode: :system)
|
29
|
+
run "sendrawtransaction #{rawtx}", run_mode: run_mode
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_raw_transaction(rawtx, run_mode: :inline)
|
33
|
+
run "testmempoolaccept '[\"#{rawtx}\"]'", run_mode: run_mode
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_block_count
|
37
|
+
run("getblockcount", run_mode: :inline).to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_received_by_address(address)
|
41
|
+
result = run "getreceivedbyaddress #{address}", run_mode: :inline
|
42
|
+
result.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
# private
|
9
46
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
47
|
+
def run(args, run_mode: :system, debug: false)
|
48
|
+
cmd = "#{program} -#{network} #{args}"
|
49
|
+
puts "==> #{cmd} run_mode=#{run_mode} verbose=#{debug}" if debug || verbose
|
50
|
+
case run_mode
|
51
|
+
when :inline
|
52
|
+
output = `#{cmd}`
|
53
|
+
puts output if debug || verbose
|
54
|
+
output
|
55
|
+
when :system
|
56
|
+
success = system cmd, out: '/dev/null'
|
57
|
+
fail "failed command: #{args}" unless success
|
58
|
+
success
|
59
|
+
when :daemon
|
60
|
+
pid = spawn cmd
|
61
|
+
sleep (ENV['BOOTSTRAP'] || 10).to_i
|
62
|
+
pid
|
63
|
+
else
|
64
|
+
raise "dont know how to run #{run_mode}"
|
65
|
+
end
|
28
66
|
end
|
29
67
|
end
|
30
68
|
end
|
data/lib/cryptos/der.rb
CHANGED
@@ -1,23 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
end
|
1
|
+
module Cryptos
|
2
|
+
Der = Struct.new :der, :length, :ri, :rl, :r, :si, :sl, :s, :sighash_type do
|
3
|
+
include Utils::Hashes, Utils::Hexas
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
if r_bytes.first & 0x80 == 128
|
9
|
-
r_bytes = [0x00] + r_bytes
|
10
|
-
self.length += 1
|
11
|
-
self.rl += 1
|
5
|
+
def initialize(der: 0x30, length: 0x44, ri: 0x02, rl: 0x20, r: nil, si: 0x02, sl: 0x20, s: nil, sighash_type: 0x01)
|
6
|
+
super der, length, ri, rl, r, si, sl, s, sighash_type
|
12
7
|
end
|
13
|
-
byte_to_hex(der) + byte_to_hex(length) +
|
14
|
-
byte_to_hex(ri) + byte_to_hex(rl) + bytes_to_hex(r_bytes) +
|
15
|
-
byte_to_hex(si) + byte_to_hex(sl) + to_hex(bignum_to_bytes(s, 32)) +
|
16
|
-
byte_to_hex(sighash_type)
|
17
|
-
end
|
18
8
|
|
19
|
-
|
20
|
-
|
21
|
-
|
9
|
+
def serialize
|
10
|
+
r_bytes = bignum_to_bytes(r, 32, false)
|
11
|
+
if r_bytes.first & 0x80 == 128
|
12
|
+
r_bytes = [0x00] + r_bytes
|
13
|
+
self.length += 1
|
14
|
+
self.rl += 1
|
15
|
+
end
|
16
|
+
byte_to_hex(der) + byte_to_hex(length) +
|
17
|
+
byte_to_hex(ri) + byte_to_hex(rl) + bytes_to_hex(r_bytes) +
|
18
|
+
byte_to_hex(si) + byte_to_hex(sl) + bignum_to_hex(s) +
|
19
|
+
byte_to_hex(sighash_type)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse(signature)
|
23
|
+
fields = *[signature].pack('H*').unpack('CCCCH66CCH64C')
|
24
|
+
Der.new r: fields[4], s: fields[7], sighash_type: fields[8]
|
25
|
+
end
|
22
26
|
end
|
23
27
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Cryptos
|
2
|
+
module EllipticCurve
|
3
|
+
Group = Struct.new :gx, :gy, :prime, :order, :cofactor
|
4
|
+
Secp256k1 = Group.new 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
|
5
|
+
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
|
6
|
+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
|
7
|
+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
|
8
|
+
1
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
12
|
EC_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
|
@@ -17,6 +19,8 @@ LOW_S = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
|
|
17
19
|
#
|
18
20
|
# Elliptic Curve
|
19
21
|
#
|
22
|
+
include Cryptos::Utils::Bytes
|
23
|
+
|
20
24
|
def extended_euclidean_algorithm(a, b)
|
21
25
|
s, old_s = 0, 1
|
22
26
|
t, old_t = 1, 0
|
data/lib/cryptos/input.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Cryptos
|
2
|
-
Input = Struct.new :value, :tx_hash, :index, :
|
3
|
-
|
4
|
-
|
2
|
+
Input = Struct.new :value, :tx_hash, :index, :script_sig, :sequence do
|
3
|
+
include Utils::Hexas, Utils::Hashes
|
4
|
+
|
5
|
+
def self.from_utxo(cli, address, index = 0, options = {debug: false})
|
6
|
+
utxos = cli.list_unspent address
|
7
|
+
utxo = JSON.parse(utxos)[index]
|
5
8
|
puts utxo if options[:debug]
|
6
9
|
txid = utxo['txid']
|
7
10
|
vout = utxo['vout']
|
@@ -9,12 +12,20 @@ module Cryptos
|
|
9
12
|
sequence = options[:sequence] || 0xfffffffff
|
10
13
|
Input.new amount * 10**8, txid, vout, sequence: sequence
|
11
14
|
end
|
12
|
-
|
13
|
-
|
15
|
+
|
16
|
+
def self.from_tx(transaction, index = 0, options = {})
|
17
|
+
amount = transaction.outputs[index].value
|
18
|
+
sequence = options[:sequence] || 0xfffffffff
|
19
|
+
Input.new amount, transaction.hash, index, sequence: sequence
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(value, tx_hash, index, script_sig: nil, sequence: 0xfffffffff)
|
23
|
+
super value, tx_hash, index, script_sig, sequence
|
14
24
|
end
|
25
|
+
|
15
26
|
def serialize
|
16
|
-
script_hex =
|
17
|
-
|
27
|
+
script_hex = script_sig ? script_sig.to_hex : ''
|
28
|
+
hex_to_little(tx_hash) + int_to_hex(index) +
|
18
29
|
byte_to_hex(hex_size(script_hex)) + script_hex + int_to_hex(sequence)
|
19
30
|
end
|
20
31
|
end
|
data/lib/cryptos/output.rb
CHANGED
@@ -1,15 +1,24 @@
|
|
1
1
|
module Cryptos
|
2
|
-
Output = Struct.new :value, :
|
2
|
+
Output = Struct.new :value, :script_pubkey do
|
3
|
+
include Utils::Hexas
|
4
|
+
|
3
5
|
def self.p2pkh(address, amount)
|
4
|
-
output_script = Cryptos::Script.
|
6
|
+
output_script = Cryptos::Script.p2pkh address
|
5
7
|
Output.new amount, output_script
|
6
8
|
end
|
9
|
+
|
10
|
+
def self.multisig(a1, a2, amount)
|
11
|
+
redeem_script = Cryptos::Script.multisig a1, a2
|
12
|
+
Output.new amount, Cryptos::Script.p2sh(redeem_script)
|
13
|
+
end
|
14
|
+
|
7
15
|
def self.p2pkh_change(address, input, output, fee = 10_000)
|
8
16
|
change_value = input.value - output.value - fee
|
9
17
|
Output.p2pkh address, change_value
|
10
18
|
end
|
19
|
+
|
11
20
|
def serialize
|
12
|
-
script_hex =
|
21
|
+
script_hex = script_pubkey.to_hex
|
13
22
|
long_to_hex(value) + byte_to_hex(hex_size(script_hex)) + script_hex
|
14
23
|
end
|
15
24
|
end
|
data/lib/cryptos/private_key.rb
CHANGED
@@ -4,6 +4,9 @@ module Cryptos
|
|
4
4
|
class PrivateKey
|
5
5
|
attr_reader :value, :order
|
6
6
|
|
7
|
+
# Generates new private key
|
8
|
+
# @param group [EllipticCurve::Group] EC group this
|
9
|
+
# @return the private key
|
7
10
|
def self.generate(group = EllipticCurve::Secp256k1)
|
8
11
|
value = 1 + SecureRandom.random_number(group.order - 1)
|
9
12
|
new value, group.order
|
data/lib/cryptos/public_key.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
module Cryptos
|
2
2
|
class PublicKey
|
3
|
-
|
3
|
+
include Utils::Bytes, Utils::Hexas
|
4
4
|
|
5
|
-
|
6
|
-
new *ec_multiply(private_key.value, EC_Gx, EC_Gy, EC_p), private_key
|
7
|
-
end
|
5
|
+
attr_reader :x, :y, :private_key
|
8
6
|
|
9
|
-
def initialize(
|
10
|
-
@x = x
|
11
|
-
@y = y
|
7
|
+
def initialize(private_key)
|
12
8
|
@private_key = private_key
|
9
|
+
@x, @y = *ec_multiply(private_key.value, EC_Gx, EC_Gy, EC_p)
|
13
10
|
end
|
14
11
|
|
15
12
|
def check!
|
16
13
|
(x**3 + 7 - y**2) % EC_p == 0 || raise('public key point is not on the curve')
|
17
14
|
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
# Serialize public key as SEC (Standards for Efficient Cryptography) format
|
17
|
+
# @param compressed [true, false] the format to return, either compressed or uncompressed
|
18
|
+
# @return address in SEC format
|
19
|
+
def to_sec(compressed = true)
|
20
|
+
if compressed
|
21
|
+
"#{y.even? ? '02' : '03'}#{x_to_sec}"
|
22
|
+
else
|
23
|
+
"04#{x_to_sec}#{y_to_sec}"
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def coordinates
|
@@ -31,5 +31,15 @@ module Cryptos
|
|
31
31
|
def to_s
|
32
32
|
coordinates.to_s
|
33
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def x_to_sec
|
38
|
+
bin_to_hex bignum_to_bytes(x, 32)
|
39
|
+
end
|
40
|
+
|
41
|
+
def y_to_sec
|
42
|
+
bin_to_hex bignum_to_bytes(y, 32)
|
43
|
+
end
|
34
44
|
end
|
35
45
|
end
|
data/lib/cryptos/script.rb
CHANGED
@@ -1,20 +1,96 @@
|
|
1
1
|
module Cryptos
|
2
2
|
class Script
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
include Utils::Hexas, Utils::Hashes
|
4
|
+
|
5
|
+
OPCODES = {
|
6
|
+
'OP_0' => 0x00,
|
7
|
+
'OP_1' => 0x51,
|
8
|
+
'OP_2' => 0x52,
|
9
|
+
'OP_DUP' => 0x76,
|
10
|
+
'OP_HASH160' => 0xA9,
|
11
|
+
'OP_EQUAL' => 0x87,
|
12
|
+
'OP_EQUALVERIFY' => 0x88,
|
13
|
+
'OP_CHECKSIG' => 0xAC,
|
14
|
+
'OP_CHECKMULTISIG' => 0xAE
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_reader :script
|
18
|
+
|
19
|
+
# scriptSig for pay-to-pubkey-hash outputs
|
20
|
+
# @param der - signature in der format
|
21
|
+
# @param public_key - the public key
|
22
|
+
# @return script - a Script object holding scriptSig
|
23
|
+
def self.sig_pubkey(der, public_key)
|
24
|
+
new "#{der.serialize} #{public_key.to_sec}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# scriptSig for pay-to-multisig-hash outputs
|
28
|
+
def self.sig_multisig(der1, der2, redeem_script)
|
29
|
+
new "OP_0 #{der1.serialize} #{der2.serialize} #{redeem_script.serialize}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.p2pkh(address_or_hex)
|
33
|
+
hash160 = if address_or_hex.is_a? String
|
34
|
+
Address.to_hash160 address_or_hex
|
35
|
+
else
|
36
|
+
address_or_hex.to_hash160
|
37
|
+
end
|
38
|
+
new "OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.p2sh(script)
|
42
|
+
new "OP_HASH160 #{script.to_hash160} OP_EQUAL"
|
43
|
+
end
|
44
|
+
|
45
|
+
# multisign redeem script
|
46
|
+
def self.multisig(address1, address2)
|
47
|
+
new "OP_2 #{address1.public_key.to_sec} #{address2.public_key.to_sec} OP_2 OP_CHECKMULTISIG"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.bare(script)
|
51
|
+
new script
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(script)
|
55
|
+
@script = script
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_hash160
|
59
|
+
hash160 to_hex
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_hex
|
63
|
+
@hex ||= to_asm.split.map { |token| token.start_with?('OP') ? opcode(token) : data(token) }.join
|
64
|
+
end
|
65
|
+
alias :serialize :to_hex
|
66
|
+
|
67
|
+
def to_asm
|
68
|
+
script.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
to_asm
|
73
|
+
end
|
74
|
+
|
75
|
+
def size
|
76
|
+
[to_hex].pack('H*').size
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def opcode(token)
|
82
|
+
raise "opcode #{token} not found" unless OPCODES.include?(token)
|
83
|
+
byte_to_hex OPCODES[token]
|
10
84
|
end
|
11
85
|
|
12
|
-
def
|
13
|
-
|
86
|
+
def data(token)
|
87
|
+
bin_size = data_size token
|
88
|
+
# TODO: data size is defined as 1-9 bytes
|
89
|
+
byte_to_hex(bin_size) + token
|
14
90
|
end
|
15
91
|
|
16
|
-
def
|
17
|
-
|
92
|
+
def data_size(token)
|
93
|
+
[token].pack('H*').size
|
18
94
|
end
|
19
95
|
end
|
20
96
|
end
|
data/lib/cryptos/transaction.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module Cryptos
|
2
2
|
Transaction = Struct.new :version, :inputs, :outputs, :locktime do
|
3
|
+
include Utils::Hexas, Utils::Hashes
|
4
|
+
|
3
5
|
def self.from_ioc(input, output, change, version: 1, locktime: 0)
|
4
6
|
new version, [input], [output, change], locktime
|
5
7
|
end
|
8
|
+
|
6
9
|
def serialize
|
7
10
|
inputs_hex = inputs.map(&:serialize).join
|
8
11
|
outputs_hex = outputs.map(&:serialize).join
|
@@ -11,33 +14,58 @@ module Cryptos
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def hash
|
14
|
-
|
17
|
+
hex_to_little sha256(sha256(serialize))
|
15
18
|
end
|
16
19
|
|
17
|
-
def signature_hash(
|
18
|
-
inputs.first.
|
20
|
+
def signature_hash(script_pubkey = nil, sighash_type = 0x01)
|
21
|
+
inputs.first.script_sig = script_pubkey if script_pubkey
|
19
22
|
hash = sha256(sha256(serialize + int_to_hex(sighash_type)))
|
20
23
|
[hash].pack('H*')
|
21
24
|
end
|
22
25
|
|
23
|
-
def sign(private_key, public_key,
|
24
|
-
bytes_string = signature_hash
|
26
|
+
def sign(private_key, public_key, script_pubkey, sighash_type = 0x01)
|
27
|
+
bytes_string = signature_hash script_pubkey, sighash_type
|
25
28
|
r, s = ecdsa_sign private_key.value, bytes_string
|
26
|
-
der = Der.new r: r, s: s
|
27
|
-
inputs.first.
|
29
|
+
der = Cryptos::Der.new r: r, s: s
|
30
|
+
inputs.first.script_sig = Script.sig_pubkey(der, public_key)
|
28
31
|
serialize
|
29
32
|
end
|
33
|
+
|
30
34
|
def sign_input(index, address, sighash_type = 0x01)
|
31
|
-
# TODO: get
|
32
|
-
|
33
|
-
bytes_string = signature_hash
|
35
|
+
# TODO: get script_pubkey from input?
|
36
|
+
script_pubkey = Cryptos::Script.p2pkh address
|
37
|
+
bytes_string = signature_hash script_pubkey, sighash_type
|
38
|
+
|
34
39
|
r, s = ecdsa_sign address.public_key.private_key.value, bytes_string
|
35
|
-
der = Der.new r: r, s: s
|
36
|
-
inputs[index].
|
40
|
+
der = Cryptos::Der.new r: r, s: s
|
41
|
+
inputs[index].script_sig = Script.sig_pubkey der, address.public_key
|
42
|
+
|
37
43
|
serialize
|
38
44
|
end
|
45
|
+
|
46
|
+
def sign_single_input(address)
|
47
|
+
sign_input 0, address
|
48
|
+
end
|
49
|
+
|
50
|
+
def multi_sign_input(index, address1, address2, sighash_type = 0x01)
|
51
|
+
redeem_script = Cryptos::Script.multisig address1, address2
|
52
|
+
bytes_string = signature_hash redeem_script, sighash_type
|
53
|
+
|
54
|
+
r, s = ecdsa_sign address1.public_key.private_key.value, bytes_string
|
55
|
+
der1 = Cryptos::Der.new r: r, s: s
|
56
|
+
r, s = ecdsa_sign address2.public_key.private_key.value, bytes_string
|
57
|
+
der2 = Cryptos::Der.new r: r, s: s
|
58
|
+
inputs[index].script_sig = Script.sig_multisig der1, der2, redeem_script
|
59
|
+
|
60
|
+
serialize
|
61
|
+
end
|
62
|
+
|
63
|
+
def broadcast(cli)
|
64
|
+
cli.send_raw_transaction serialize
|
65
|
+
end
|
66
|
+
|
39
67
|
def to_s
|
40
|
-
|
68
|
+
inputs.to_s + outputs.to_s
|
41
69
|
end
|
42
70
|
end
|
43
71
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cryptos
|
2
|
+
module Utils
|
3
|
+
module Bytes
|
4
|
+
def bytes_to_bignum(bytes_string)
|
5
|
+
bytes_string.bytes.reduce { |n, b| (n << 8) + b }
|
6
|
+
end
|
7
|
+
|
8
|
+
def bignum_to_bytes(n, length=nil, stringify=true)
|
9
|
+
a = []
|
10
|
+
while n > 0
|
11
|
+
a << (n & 0xFF)
|
12
|
+
n >>= 8
|
13
|
+
end
|
14
|
+
a.fill 0x00, a.length, length - a.length if length
|
15
|
+
bytes = a.reverse
|
16
|
+
stringify ? bytes.pack('C*') : bytes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Cryptos
|
4
|
+
module Utils
|
5
|
+
module Hashes
|
6
|
+
def sha256(data)
|
7
|
+
Digest::SHA256.hexdigest([data].pack('H*'))
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash160(data)
|
11
|
+
sha256 = Digest::SHA256.digest([data].pack('H*'))
|
12
|
+
Digest::RMD160.hexdigest sha256
|
13
|
+
end
|
14
|
+
|
15
|
+
def hash256(data)
|
16
|
+
sha256 = Digest::SHA256.digest([data].pack('H*'))
|
17
|
+
Digest::SHA256.hexdigest sha256
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Cryptos
|
2
|
+
module Utils
|
3
|
+
module Hexas
|
4
|
+
include Bytes
|
5
|
+
|
6
|
+
def bin_to_hex(binary_bytes)
|
7
|
+
binary_bytes.unpack('H*').first
|
8
|
+
end
|
9
|
+
|
10
|
+
def byte_to_hex(value)
|
11
|
+
bin_to_hex [value].pack('C')
|
12
|
+
end
|
13
|
+
|
14
|
+
def bytes_to_hex(value)
|
15
|
+
bin_to_hex value.pack('C*')
|
16
|
+
end
|
17
|
+
|
18
|
+
def int_to_hex(value)
|
19
|
+
bin_to_hex [value].pack('V')
|
20
|
+
end
|
21
|
+
|
22
|
+
def long_to_hex(value)
|
23
|
+
bin_to_hex [value].pack('Q<')
|
24
|
+
end
|
25
|
+
|
26
|
+
def bignum_to_hex(value, size = 32)
|
27
|
+
bin_to_hex bignum_to_bytes(value, size)
|
28
|
+
end
|
29
|
+
|
30
|
+
def hex_to_little(value)
|
31
|
+
bin_to_hex [value].pack('H*').reverse
|
32
|
+
end
|
33
|
+
|
34
|
+
def hex_size(hex)
|
35
|
+
[hex].pack('H*').size
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/cryptos/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cryptos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Iulian Costan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -108,6 +108,34 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: bump
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
111
139
|
description: The easiest way to craft your own transactions for multiple crypto currencies
|
112
140
|
email:
|
113
141
|
- iulian.costan@gmail.com
|
@@ -127,11 +155,11 @@ files:
|
|
127
155
|
- Rakefile
|
128
156
|
- TODOs.org
|
129
157
|
- bin/console
|
158
|
+
- bin/run
|
130
159
|
- bin/setup
|
131
160
|
- cryptos.gemspec
|
132
161
|
- lib/cryptos.rb
|
133
162
|
- lib/cryptos/address.rb
|
134
|
-
- lib/cryptos/base.rb
|
135
163
|
- lib/cryptos/base58.rb
|
136
164
|
- lib/cryptos/bitcoin.rb
|
137
165
|
- lib/cryptos/bitcoin/address.rb
|
@@ -141,7 +169,6 @@ files:
|
|
141
169
|
- lib/cryptos/der.rb
|
142
170
|
- lib/cryptos/deribit/client.rb
|
143
171
|
- lib/cryptos/elliptic_curve.rb
|
144
|
-
- lib/cryptos/hashing.rb
|
145
172
|
- lib/cryptos/input.rb
|
146
173
|
- lib/cryptos/litecoin.rb
|
147
174
|
- lib/cryptos/litecoin/address.rb
|
@@ -150,6 +177,9 @@ files:
|
|
150
177
|
- lib/cryptos/public_key.rb
|
151
178
|
- lib/cryptos/script.rb
|
152
179
|
- lib/cryptos/transaction.rb
|
180
|
+
- lib/cryptos/utils/bytes.rb
|
181
|
+
- lib/cryptos/utils/hashes.rb
|
182
|
+
- lib/cryptos/utils/hexas.rb
|
153
183
|
- lib/cryptos/version.rb
|
154
184
|
homepage: https://github.com/icostan/cryptos
|
155
185
|
licenses: []
|
data/lib/cryptos/base.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'digest'
|
3
|
-
|
4
|
-
class Struct
|
5
|
-
OPCODES = {
|
6
|
-
'OP_DUP' => 0x76,
|
7
|
-
'OP_HASH160' => 0xA9,
|
8
|
-
'OP_EQUAL' => 0x87,
|
9
|
-
'OP_EQUALVERIFY' => 0x88,
|
10
|
-
'OP_CHECKSIG' => 0xAC
|
11
|
-
}.freeze
|
12
|
-
|
13
|
-
def opcode(token)
|
14
|
-
raise "opcode #{token} not found" unless OPCODES.include?(token)
|
15
|
-
OPCODES[token].to_s 16
|
16
|
-
end
|
17
|
-
|
18
|
-
def data(token)
|
19
|
-
bin_size = hex_size token
|
20
|
-
# TODO: data size is defined as 1-9 bytes
|
21
|
-
byte_to_hex(bin_size) + token
|
22
|
-
end
|
23
|
-
|
24
|
-
def hex_size(hex)
|
25
|
-
[hex].pack('H*').size
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_hex(binary_bytes)
|
29
|
-
binary_bytes.unpack('H*').first
|
30
|
-
end
|
31
|
-
|
32
|
-
def hash_to_hex(value)
|
33
|
-
to_hex [value].pack('H*').reverse
|
34
|
-
end
|
35
|
-
|
36
|
-
def int_to_hex(value)
|
37
|
-
to_hex [value].pack('V')
|
38
|
-
end
|
39
|
-
|
40
|
-
def byte_to_hex(value)
|
41
|
-
to_hex [value].pack('C')
|
42
|
-
end
|
43
|
-
|
44
|
-
def bytes_to_hex(bytes)
|
45
|
-
to_hex bytes.pack('C*')
|
46
|
-
end
|
47
|
-
|
48
|
-
def long_to_hex(value)
|
49
|
-
to_hex [value].pack('Q<')
|
50
|
-
end
|
51
|
-
|
52
|
-
def script_to_hex(script_string)
|
53
|
-
script_string.split.map { |token| token.start_with?('OP') ? opcode(token) : data(token) }.join
|
54
|
-
end
|
55
|
-
|
56
|
-
def sha256(hex)
|
57
|
-
Digest::SHA256.hexdigest([hex].pack('H*'))
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def bytes_to_bignum(bytes_string)
|
62
|
-
bytes_string.bytes.reduce { |n, b| (n << 8) + b }
|
63
|
-
end
|
64
|
-
|
65
|
-
def bignum_to_bytes(n, length=nil, stringify=true)
|
66
|
-
a = []
|
67
|
-
while n > 0
|
68
|
-
a << (n & 0xFF)
|
69
|
-
n >>= 8
|
70
|
-
end
|
71
|
-
a.fill 0x00, a.length, length - a.length if length
|
72
|
-
bytes = a.reverse
|
73
|
-
stringify ? bytes.pack('C*') : bytes
|
74
|
-
end
|
data/lib/cryptos/hashing.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'digest'
|
2
|
-
|
3
|
-
module Hashing
|
4
|
-
def hash160(data)
|
5
|
-
sha256 = Digest::SHA256.digest([data].pack('H*'))
|
6
|
-
Digest::RMD160.hexdigest sha256
|
7
|
-
end
|
8
|
-
|
9
|
-
def hash256(data)
|
10
|
-
sha256 = Digest::SHA256.digest([data].pack('H*'))
|
11
|
-
Digest::SHA256.hexdigest sha256
|
12
|
-
end
|
13
|
-
end
|