bitcoin-ruby 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -7
- data/COPYING +1 -1
- data/Gemfile +2 -6
- data/Gemfile.lock +34 -0
- data/README.rdoc +16 -68
- data/Rakefile +3 -6
- data/bin/bitcoin_shell +0 -1
- data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
- data/lib/bitcoin.rb +350 -296
- data/lib/bitcoin/builder.rb +3 -1
- data/lib/bitcoin/connection.rb +2 -1
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
- data/lib/bitcoin/ffi/openssl.rb +98 -2
- data/lib/bitcoin/ffi/secp256k1.rb +144 -0
- data/lib/bitcoin/key.rb +12 -2
- data/lib/bitcoin/logger.rb +3 -12
- data/lib/bitcoin/protocol/block.rb +3 -9
- data/lib/bitcoin/protocol/parser.rb +6 -2
- data/lib/bitcoin/protocol/tx.rb +44 -13
- data/lib/bitcoin/protocol/txin.rb +4 -2
- data/lib/bitcoin/protocol/txout.rb +2 -2
- data/lib/bitcoin/script.rb +212 -37
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +32 -3
- data/spec/bitcoin/builder_spec.rb +18 -0
- data/spec/bitcoin/contracthash_spec.rb +45 -0
- data/spec/bitcoin/dogecoin_spec.rb +176 -0
- data/spec/bitcoin/ffi_openssl.rb +45 -0
- data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
- data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
- data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
- data/spec/bitcoin/fixtures/coinbase.json +24 -0
- data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/bitcoin/protocol/block_spec.rb +0 -22
- data/spec/bitcoin/protocol/tx_spec.rb +145 -2
- data/spec/bitcoin/script/script_spec.rb +282 -0
- data/spec/bitcoin/secp256k1_spec.rb +48 -0
- data/spec/bitcoin/spec_helper.rb +0 -51
- data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
- metadata +48 -98
- data/bin/bitcoin_dns_seed +0 -130
- data/bin/bitcoin_gui +0 -80
- data/bin/bitcoin_node +0 -153
- data/bin/bitcoin_node_cli +0 -81
- data/bin/bitcoin_wallet +0 -402
- data/doc/CONFIG.rdoc +0 -66
- data/doc/EXAMPLES.rdoc +0 -13
- data/doc/NAMECOIN.rdoc +0 -34
- data/doc/NODE.rdoc +0 -225
- data/doc/STORAGE.rdoc +0 -33
- data/doc/WALLET.rdoc +0 -102
- data/examples/balance.rb +0 -66
- data/examples/forwarder.rb +0 -73
- data/examples/index_nhash.rb +0 -24
- data/examples/reindex_p2sh_addrs.rb +0 -44
- data/examples/relay_tx.rb +0 -22
- data/examples/verify_tx.rb +0 -57
- data/lib/bitcoin/config.rb +0 -58
- data/lib/bitcoin/gui/addr_view.rb +0 -44
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
- data/lib/bitcoin/gui/conn_view.rb +0 -38
- data/lib/bitcoin/gui/connection.rb +0 -70
- data/lib/bitcoin/gui/em_gtk.rb +0 -30
- data/lib/bitcoin/gui/gui.builder +0 -1643
- data/lib/bitcoin/gui/gui.rb +0 -292
- data/lib/bitcoin/gui/helpers.rb +0 -115
- data/lib/bitcoin/gui/tree_view.rb +0 -84
- data/lib/bitcoin/gui/tx_view.rb +0 -69
- data/lib/bitcoin/namecoin.rb +0 -280
- data/lib/bitcoin/network/command_client.rb +0 -104
- data/lib/bitcoin/network/command_handler.rb +0 -570
- data/lib/bitcoin/network/connection_handler.rb +0 -387
- data/lib/bitcoin/network/node.rb +0 -565
- data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
- data/lib/bitcoin/storage/models.rb +0 -171
- data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
- data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
- data/lib/bitcoin/storage/storage.rb +0 -517
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
- data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
- data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
- data/lib/bitcoin/validation.rb +0 -400
- data/lib/bitcoin/wallet/coinselector.rb +0 -33
- data/lib/bitcoin/wallet/keygenerator.rb +0 -77
- data/lib/bitcoin/wallet/keystore.rb +0 -207
- data/lib/bitcoin/wallet/txdp.rb +0 -118
- data/lib/bitcoin/wallet/wallet.rb +0 -281
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
- data/spec/bitcoin/namecoin_spec.rb +0 -182
- data/spec/bitcoin/node/command_api_spec.rb +0 -663
- data/spec/bitcoin/storage/models_spec.rb +0 -104
- data/spec/bitcoin/storage/reorg_spec.rb +0 -236
- data/spec/bitcoin/storage/storage_spec.rb +0 -387
- data/spec/bitcoin/storage/validation_spec.rb +0 -300
- data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
- data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
- data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
- data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
- data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
@@ -0,0 +1,130 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# port of https://github.com/trezor/python-mnemonic/blob/master/mnemonic/mnemonic.py
|
3
|
+
|
4
|
+
require 'openssl'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'digest'
|
7
|
+
|
8
|
+
module Bitcoin
|
9
|
+
module Trezor
|
10
|
+
|
11
|
+
module Mnemonic
|
12
|
+
def self.to_seed(mnemonic, passphrase='')
|
13
|
+
OpenSSL::PKCS5.pbkdf2_hmac(mnemonic, 'mnemonic'+passphrase, 2048, 64, OpenSSL::Digest::SHA512.new).unpack("H*")[0]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.generate(strength_bits = 256)
|
17
|
+
raise ArgumentError, 'Strength should be divisible by 32, but it is not (%d)' % [strength_bits] if (strength_bits % 32) > 0
|
18
|
+
data = SecureRandom.random_bytes( (strength_bits / 8).floor )
|
19
|
+
to_mnemonic(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.wordlist
|
23
|
+
return @wordlist if @wordlist
|
24
|
+
@wordlist = WORDLIST_ENGLISH.split(/[ \n]/).map{|i| i.strip }
|
25
|
+
if @wordlist.size != (radix=2048)
|
26
|
+
raise ArgumentError, 'Wordlist should contain %d words, but it contains %d words.' % [radix, wordlist.size]
|
27
|
+
end
|
28
|
+
@wordlist
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.to_mnemonic(data)
|
32
|
+
if (data.bytesize % 4) > 0
|
33
|
+
raise ArgumentError, 'Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' % [data.bytesize, data.bytesize * 8]
|
34
|
+
end
|
35
|
+
b = data.unpack("B*")[0] + Digest::SHA256.digest(data).unpack("B*")[0].ljust(256, '0')[0...(data.bytesize * 8 / 32)]
|
36
|
+
|
37
|
+
(0...(b.bytesize / 11)).map{|i|
|
38
|
+
idx = b[ (i * 11)...((i+1) * 11) ].to_i(2)
|
39
|
+
wordlist[idx]
|
40
|
+
}.join(" ")
|
41
|
+
end
|
42
|
+
|
43
|
+
WORDLIST_ENGLISH = <<-TEXT
|
44
|
+
abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor actress actual adapt
|
45
|
+
add addict address adjust admit adult advance advice aerobic affair afford afraid again age agent agree ahead aim air airport aisle alarm album alcohol alert
|
46
|
+
alien all alley allow almost alone alpha already also alter always amateur amazing among amount amused analyst anchor ancient anger angle angry animal ankle announce
|
47
|
+
annual another answer antenna antique anxiety any apart apology appear apple approve april arch arctic area arena argue arm armed armor army around arrange arrest
|
48
|
+
arrive arrow art artefact artist artwork ask aspect assault asset assist assume asthma athlete atom attack attend attitude attract auction audit august aunt author auto
|
49
|
+
autumn average avocado avoid awake aware away awesome awful awkward axis baby bachelor bacon badge bag balance balcony ball bamboo banana banner bar barely bargain
|
50
|
+
barrel base basic basket battle beach bean beauty because become beef before begin behave behind believe below belt bench benefit best betray better between beyond
|
51
|
+
bicycle bid bike bind biology bird birth bitter black blade blame blanket blast bleak bless blind blood blossom blouse blue blur blush board boat body
|
52
|
+
boil bomb bone bonus book boost border boring borrow boss bottom bounce box boy bracket brain brand brass brave bread breeze brick bridge brief bright
|
53
|
+
bring brisk broccoli broken bronze broom brother brown brush bubble buddy budget buffalo build bulb bulk bullet bundle bunker burden burger burst bus business busy
|
54
|
+
butter buyer buzz cabbage cabin cable cactus cage cake call calm camera camp can canal cancel candy cannon canoe canvas canyon capable capital captain car
|
55
|
+
carbon card cargo carpet carry cart case cash casino castle casual cat catalog catch category cattle caught cause caution cave ceiling celery cement census century
|
56
|
+
cereal certain chair chalk champion change chaos chapter charge chase chat cheap check cheese chef cherry chest chicken chief child chimney choice choose chronic chuckle
|
57
|
+
chunk churn cigar cinnamon circle citizen city civil claim clap clarify claw clay clean clerk clever click client cliff climb clinic clip clock clog close
|
58
|
+
cloth cloud clown club clump cluster clutch coach coast coconut code coffee coil coin collect color column combine come comfort comic common company concert conduct
|
59
|
+
confirm congress connect consider control convince cook cool copper copy coral core corn correct cost cotton couch country couple course cousin cover coyote crack cradle
|
60
|
+
craft cram crane crash crater crawl crazy cream credit creek crew cricket crime crisp critic crop cross crouch crowd crucial cruel cruise crumble crunch crush
|
61
|
+
cry crystal cube culture cup cupboard curious current curtain curve cushion custom cute cycle dad damage damp dance danger daring dash daughter dawn day deal
|
62
|
+
debate debris decade december decide decline decorate decrease deer defense define defy degree delay deliver demand demise denial dentist deny depart depend deposit depth deputy
|
63
|
+
derive describe desert design desk despair destroy detail detect develop device devote diagram dial diamond diary dice diesel diet differ digital dignity dilemma dinner dinosaur
|
64
|
+
direct dirt disagree discover disease dish dismiss disorder display distance divert divide divorce dizzy doctor document dog doll dolphin domain donate donkey donor door dose
|
65
|
+
double dove draft dragon drama drastic draw dream dress drift drill drink drip drive drop drum dry duck dumb dune during dust dutch duty dwarf
|
66
|
+
dynamic eager eagle early earn earth easily east easy echo ecology economy edge edit educate effort egg eight either elbow elder electric elegant element elephant
|
67
|
+
elevator elite else embark embody embrace emerge emotion employ empower empty enable enact end endless endorse enemy energy enforce engage engine enhance enjoy enlist enough
|
68
|
+
enrich enroll ensure enter entire entry envelope episode equal equip era erase erode erosion error erupt escape essay essence estate eternal ethics evidence evil evoke
|
69
|
+
evolve exact example excess exchange excite exclude excuse execute exercise exhaust exhibit exile exist exit exotic expand expect expire explain expose express extend extra eye
|
70
|
+
eyebrow fabric face faculty fade faint faith fall false fame family famous fan fancy fantasy farm fashion fat fatal father fatigue fault favorite feature february
|
71
|
+
federal fee feed feel female fence festival fetch fever few fiber fiction field figure file film filter final find fine finger finish fire firm first
|
72
|
+
fiscal fish fit fitness fix flag flame flash flat flavor flee flight flip float flock floor flower fluid flush fly foam focus fog foil fold
|
73
|
+
follow food foot force forest forget fork fortune forum forward fossil foster found fox fragile frame frequent fresh friend fringe frog front frost frown frozen
|
74
|
+
fruit fuel fun funny furnace fury future gadget gain galaxy gallery game gap garage garbage garden garlic garment gas gasp gate gather gauge gaze general
|
75
|
+
genius genre gentle genuine gesture ghost giant gift giggle ginger giraffe girl give glad glance glare glass glide glimpse globe gloom glory glove glow glue
|
76
|
+
goat goddess gold good goose gorilla gospel gossip govern gown grab grace grain grant grape grass gravity great green grid grief grit grocery group grow
|
77
|
+
grunt guard guess guide guilt guitar gun gym habit hair half hammer hamster hand happy harbor hard harsh harvest hat have hawk hazard head health
|
78
|
+
heart heavy hedgehog height hello helmet help hen hero hidden high hill hint hip hire history hobby hockey hold hole holiday hollow home honey hood
|
79
|
+
hope horn horror horse hospital host hotel hour hover hub huge human humble humor hundred hungry hunt hurdle hurry hurt husband hybrid ice icon idea
|
80
|
+
identify idle ignore ill illegal illness image imitate immense immune impact impose improve impulse inch include income increase index indicate indoor industry infant inflict inform
|
81
|
+
inhale inherit initial inject injury inmate inner innocent input inquiry insane insect inside inspire install intact interest into invest invite involve iron island isolate issue
|
82
|
+
item ivory jacket jaguar jar jazz jealous jeans jelly jewel job join joke journey joy judge juice jump jungle junior junk just kangaroo keen keep
|
83
|
+
ketchup key kick kid kidney kind kingdom kiss kit kitchen kite kitten kiwi knee knife knock know lab label labor ladder lady lake lamp language
|
84
|
+
laptop large later latin laugh laundry lava law lawn lawsuit layer lazy leader leaf learn leave lecture left leg legal legend leisure lemon lend length
|
85
|
+
lens leopard lesson letter level liar liberty library license life lift light like limb limit link lion liquid list little live lizard load loan lobster
|
86
|
+
local lock logic lonely long loop lottery loud lounge love loyal lucky luggage lumber lunar lunch luxury lyrics machine mad magic magnet maid mail main
|
87
|
+
major make mammal man manage mandate mango mansion manual maple marble march margin marine market marriage mask mass master match material math matrix matter maximum
|
88
|
+
maze meadow mean measure meat mechanic medal media melody melt member memory mention menu mercy merge merit merry mesh message metal method middle midnight milk
|
89
|
+
million mimic mind minimum minor minute miracle mirror misery miss mistake mix mixed mixture mobile model modify mom moment monitor monkey monster month moon moral
|
90
|
+
more morning mosquito mother motion motor mountain mouse move movie much muffin mule multiply muscle museum mushroom music must mutual myself mystery myth naive name
|
91
|
+
napkin narrow nasty nation nature near neck need negative neglect neither nephew nerve nest net network neutral never news next nice night noble noise nominee
|
92
|
+
noodle normal north nose notable note nothing notice novel now nuclear number nurse nut oak obey object oblige obscure observe obtain obvious occur ocean october
|
93
|
+
odor off offer office often oil okay old olive olympic omit once one onion online only open opera opinion oppose option orange orbit orchard order
|
94
|
+
ordinary organ orient original orphan ostrich other outdoor outer output outside oval oven over own owner oxygen oyster ozone pact paddle page pair palace palm
|
95
|
+
panda panel panic panther paper parade parent park parrot party pass patch path patient patrol pattern pause pave payment peace peanut pear peasant pelican pen
|
96
|
+
penalty pencil people pepper perfect permit person pet phone photo phrase physical piano picnic picture piece pig pigeon pill pilot pink pioneer pipe pistol pitch
|
97
|
+
pizza place planet plastic plate play please pledge pluck plug plunge poem poet point polar pole police pond pony pool popular portion position possible post
|
98
|
+
potato pottery poverty powder power practice praise predict prefer prepare present pretty prevent price pride primary print priority prison private prize problem process produce profit
|
99
|
+
program project promote proof property prosper protect proud provide public pudding pull pulp pulse pumpkin punch pupil puppy purchase purity purpose purse push put puzzle
|
100
|
+
pyramid quality quantum quarter question quick quit quiz quote rabbit raccoon race rack radar radio rail rain raise rally ramp ranch random range rapid rare
|
101
|
+
rate rather raven raw razor ready real reason rebel rebuild recall receive recipe record recycle reduce reflect reform refuse region regret regular reject relax release
|
102
|
+
relief rely remain remember remind remove render renew rent reopen repair repeat replace report require rescue resemble resist resource response result retire retreat return reunion
|
103
|
+
reveal review reward rhythm rib ribbon rice rich ride ridge rifle right rigid ring riot ripple risk ritual rival river road roast robot robust rocket
|
104
|
+
romance roof rookie room rose rotate rough round route royal rubber rude rug rule run runway rural sad saddle sadness safe sail salad salmon salon
|
105
|
+
salt salute same sample sand satisfy satoshi sauce sausage save say scale scan scare scatter scene scheme school science scissors scorpion scout scrap screen script
|
106
|
+
scrub sea search season seat second secret section security seed seek segment select sell seminar senior sense sentence series service session settle setup seven shadow
|
107
|
+
shaft shallow share shed shell sheriff shield shift shine ship shiver shock shoe shoot shop short shoulder shove shrimp shrug shuffle shy sibling sick side
|
108
|
+
siege sight sign silent silk silly silver similar simple since sing siren sister situate six size skate sketch ski skill skin skirt skull slab slam
|
109
|
+
sleep slender slice slide slight slim slogan slot slow slush small smart smile smoke smooth snack snake snap sniff snow soap soccer social sock soda
|
110
|
+
soft solar soldier solid solution solve someone song soon sorry sort soul sound soup source south space spare spatial spawn speak special speed spell spend
|
111
|
+
sphere spice spider spike spin spirit split spoil sponsor spoon sport spot spray spread spring spy square squeeze squirrel stable stadium staff stage stairs stamp
|
112
|
+
stand start state stay steak steel stem step stereo stick still sting stock stomach stone stool story stove strategy street strike strong struggle student stuff
|
113
|
+
stumble style subject submit subway success such sudden suffer sugar suggest suit summer sun sunny sunset super supply supreme sure surface surge surprise surround survey
|
114
|
+
suspect sustain swallow swamp swap swarm swear sweet swift swim swing switch sword symbol symptom syrup system table tackle tag tail talent talk tank tape
|
115
|
+
target task taste tattoo taxi teach team tell ten tenant tennis tent term test text thank that theme then theory there they thing this thought
|
116
|
+
three thrive throw thumb thunder ticket tide tiger tilt timber time tiny tip tired tissue title toast tobacco today toddler toe together toilet token tomato
|
117
|
+
tomorrow tone tongue tonight tool tooth top topic topple torch tornado tortoise toss total tourist toward tower town toy track trade traffic tragic train transfer
|
118
|
+
trap trash travel tray treat tree trend trial tribe trick trigger trim trip trophy trouble truck true truly trumpet trust truth try tube tuition tumble
|
119
|
+
tuna tunnel turkey turn turtle twelve twenty twice twin twist two type typical ugly umbrella unable unaware uncle uncover under undo unfair unfold unhappy uniform
|
120
|
+
unique unit universe unknown unlock until unusual unveil update upgrade uphold upon upper upset urban urge usage use used useful useless usual utility vacant vacuum
|
121
|
+
vague valid valley valve van vanish vapor various vast vault vehicle velvet vendor venture venue verb verify version very vessel veteran viable vibrant vicious victory
|
122
|
+
video view village vintage violin virtual virus visa visit visual vital vivid vocal voice void volcano volume vote voyage wage wagon wait walk wall walnut
|
123
|
+
want warfare warm warrior wash wasp waste water wave way wealth weapon wear weasel weather web wedding weekend weird welcome west wet whale what wheat
|
124
|
+
wheel when where whip whisper wide width wife wild will win window wine wing wink winner winter wire wisdom wise wish witness wolf woman wonder
|
125
|
+
wood wool word work world worry worth wrap wreck wrestle wrist write wrong yard year yellow you young youth zebra zero zone zoo
|
126
|
+
TEXT
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
data/lib/bitcoin/version.rb
CHANGED
@@ -163,6 +163,14 @@ describe 'Bitcoin Address/Hash160/PubKey' do
|
|
163
163
|
success.should == true
|
164
164
|
end
|
165
165
|
|
166
|
+
it 'validate bitcoin public key' do
|
167
|
+
key = Bitcoin::Key.generate
|
168
|
+
Bitcoin.valid_pubkey?(key.pub_compressed).should == true
|
169
|
+
Bitcoin.valid_pubkey?(key.pub_uncompressed).should == true
|
170
|
+
Bitcoin.valid_pubkey?(key.addr).should == false
|
171
|
+
Bitcoin.valid_pubkey?(key.priv).should == false
|
172
|
+
end
|
173
|
+
|
166
174
|
it 'validate p2sh address' do
|
167
175
|
Bitcoin.network = :testnet
|
168
176
|
Bitcoin.valid_address?("2MyLngQnhzjzatKsB7XfHYoP9e2XUXSiBMM").should == true
|
@@ -318,6 +326,13 @@ describe 'Bitcoin Address/Hash160/PubKey' do
|
|
318
326
|
Bitcoin.hash_mrkl_tree(["aa", "bb", "cc"]).last.should !=
|
319
327
|
Bitcoin.hash_mrkl_tree(["aa", "bb", "cc", "cc"]).last
|
320
328
|
end
|
329
|
+
|
330
|
+
it 'return a value even if a merkle branch is empty' do
|
331
|
+
branch = []
|
332
|
+
mrkl_index = 0
|
333
|
+
target = "089b911f5e471c0e1800f3384281ebec5b372fbb6f358790a92747ade271ccdf"
|
334
|
+
Bitcoin.mrkl_branch_root(branch.map(&:hth), target, mrkl_index).should == target
|
335
|
+
end
|
321
336
|
|
322
337
|
it 'nonce compact bits to bignum hex' do
|
323
338
|
Bitcoin.decode_compact_bits( "1b00b5ac".to_i(16) ).index(/[^0]/).should == 12
|
@@ -339,6 +354,9 @@ describe 'Bitcoin Address/Hash160/PubKey' do
|
|
339
354
|
Bitcoin.decode_compact_bits(target).should ==
|
340
355
|
"0000000065465700000000000000000000000000000000000000000000000000"
|
341
356
|
Bitcoin.encode_compact_bits( Bitcoin.decode_compact_bits( target ) ).should == target
|
357
|
+
|
358
|
+
#Bitcoin.network = :dogecoin
|
359
|
+
#Bitcoin.decode_compact_bits( "01fedcba".to_i(16) ).to_i(16).should == -0x7e
|
342
360
|
end
|
343
361
|
|
344
362
|
it '#block_hash' do
|
@@ -421,12 +439,13 @@ describe 'Bitcoin Address/Hash160/PubKey' do
|
|
421
439
|
"030b4c866585dd868a9d62348a9cd008d6a312937048fff31670e7e920cfc7a744"]
|
422
440
|
].each{|address, privkey, pubkey|
|
423
441
|
key = Bitcoin.open_key(privkey)
|
424
|
-
16.times.
|
442
|
+
16.times.each { |n|
|
425
443
|
#10_000.times.all?{|n|
|
426
444
|
# puts 'RAM USAGE: ' + `pmap #{Process.pid} | tail -1`[10,40].strip if (n % 1_000) == 0
|
427
445
|
s = Bitcoin.sign_message(key.private_key_hex, key.public_key_hex, "Very secret message %d: 11" % n)
|
428
|
-
Bitcoin.verify_message(s['address'],
|
429
|
-
|
446
|
+
Bitcoin.verify_message(s['address'], 'invalid-signature', s['message']).should == false
|
447
|
+
Bitcoin.verify_message(s['address'], s['signature'], s['message']).should == true
|
448
|
+
}
|
430
449
|
}
|
431
450
|
end
|
432
451
|
rescue LoadError
|
@@ -488,6 +507,16 @@ describe 'Bitcoin Address/Hash160/PubKey' do
|
|
488
507
|
Bitcoin.block_difficulty(436835377).should == "1751454.5353407"
|
489
508
|
end
|
490
509
|
|
510
|
+
it 'should calculate retarget difficulty' do
|
511
|
+
prev_height = 201599
|
512
|
+
prev_block_time = 1349227021
|
513
|
+
prev_block_bits = 0x1a05db8b
|
514
|
+
last_retarget_time = 1348092851
|
515
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
516
|
+
|
517
|
+
Bitcoin.decode_compact_bits(new_difficulty.should) == Bitcoin.decode_compact_bits(0x1a057e08)
|
518
|
+
end
|
519
|
+
|
491
520
|
it '#block_hashes_to_win' do
|
492
521
|
Bitcoin.block_hashes_to_win(436835377).should == 7522554734795001
|
493
522
|
end
|
@@ -78,11 +78,18 @@ describe "Bitcoin::Builder" do
|
|
78
78
|
tx2 = build_tx do |t|
|
79
79
|
t.input {|i| i.prev_out @block.tx[0], 0; i.signature_key @keys[0] }
|
80
80
|
t.output {|o| o.value 123; o.script {|s| s.recipient @keys[1].addr } }
|
81
|
+
t.output {|o| o.to @keys[1].addr; o.value 321 }
|
82
|
+
t.output {|o| o.to "deadbeef", :op_return }
|
81
83
|
end
|
82
84
|
tx2.in[0].prev_out.should == tx.in[0].prev_out
|
83
85
|
tx2.in[0].prev_out_index.should == tx.in[0].prev_out_index
|
84
86
|
tx2.out[0].value.should == tx.out[0].value
|
85
87
|
tx2.out[0].pk_script.should == tx.out[0].pk_script
|
88
|
+
Bitcoin::Script.new(tx2.out[0].pk_script).to_string.should ==
|
89
|
+
"OP_DUP OP_HASH160 #{@keys[1].hash160} OP_EQUALVERIFY OP_CHECKSIG"
|
90
|
+
Bitcoin::Script.new(tx2.out[0].pk_script).to_string.should ==
|
91
|
+
"OP_DUP OP_HASH160 #{@keys[1].hash160} OP_EQUALVERIFY OP_CHECKSIG"
|
92
|
+
tx2.out[2].value.should == 0
|
86
93
|
end
|
87
94
|
|
88
95
|
it "should allow txin.prev_out as tx or hash" do
|
@@ -225,6 +232,17 @@ describe "Bitcoin::Builder" do
|
|
225
232
|
tx.out.map(&:value).inject(:+).should == 50e8 - 10000
|
226
233
|
end
|
227
234
|
|
235
|
+
it "should build op_return output" do
|
236
|
+
builder = TxOutBuilder.new
|
237
|
+
builder.to "deadbeef", :op_return
|
238
|
+
builder.txout.parsed_script.to_string.should == "OP_RETURN deadbeef"
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should build op_return script" do
|
242
|
+
s = script {|s| s.type :op_return; s.recipient "deadbeef" }
|
243
|
+
Bitcoin::Script.new(s).to_string.should == "OP_RETURN deadbeef"
|
244
|
+
end
|
245
|
+
|
228
246
|
it "should build address script" do
|
229
247
|
key = Bitcoin::Key.generate
|
230
248
|
s = script {|s| s.type :address; s.recipient key.addr }
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require_relative 'spec_helper.rb'
|
4
|
+
|
5
|
+
# https://github.com/aalness/contracthashtool-ruby
|
6
|
+
# ruby port of https://github.com/Blockstream/contracthashtool
|
7
|
+
|
8
|
+
describe 'Bitcoin::ContractHash' do
|
9
|
+
it 'should generate and claim' do
|
10
|
+
Bitcoin::network = :testnet3
|
11
|
+
|
12
|
+
# Example parameters from the original tool's usage().
|
13
|
+
|
14
|
+
redeem_script_template = '5121038695b28f1649c711aedb1fec8df54874334cfb7ddf31ba3132a94d00bdc9715251ae'
|
15
|
+
payee_address = 'mqWkEAFeQdrQvyaWNRn5vijPJeiQAjtxL2'
|
16
|
+
nonce_hex = '3a11be476485a6273fad4a0e09117d42'
|
17
|
+
private_key_wif = 'cMcpaCT6pHkyS4347i4rSmecaQtLiu1eH28NWmBiePn8bi6N4kzh'
|
18
|
+
|
19
|
+
# Someone wanting to send funds to the sidechain would call this
|
20
|
+
# to calculate a P2SH address to send to. They would then send the
|
21
|
+
# MDFs (mutually distrusting functionaries) the target address
|
22
|
+
# and nonce so they are able to locate the subsequent transaction.
|
23
|
+
# The caller would then send the desired amount of coin to the P2SH
|
24
|
+
# address to initiate the peg protocol.
|
25
|
+
|
26
|
+
nonce, redeem_script, p2sh_address = Bitcoin::ContractHash.generate(redeem_script_template, payee_address, nonce_hex)
|
27
|
+
|
28
|
+
nonce.should == "3a11be476485a6273fad4a0e09117d42"
|
29
|
+
p2sh_address.should == "2MvGPFfDXbJZyH79u187VNZbuCgyRBhcdsw"
|
30
|
+
redeem_script.should == "512102944aba05d40d8df1724f8ab2f5f3a58d052d26aedc93e175534cb782becc8ff751ae"
|
31
|
+
|
32
|
+
# Each MDF would call this to derive a private key to redeem the
|
33
|
+
# locked transaction.
|
34
|
+
|
35
|
+
key = Bitcoin::ContractHash.claim(private_key_wif, payee_address, nonce)
|
36
|
+
key.to_base58.should == "cSBD8yM62R82RfbugiGK8Lui9gdMB81NtZBckxe5YxRsDSKySwHK"
|
37
|
+
|
38
|
+
# Verify homomorphic derivation was successful.
|
39
|
+
|
40
|
+
signature = key.sign_message(message="derp")
|
41
|
+
script = Bitcoin::Script.new([redeem_script].pack("H*"))
|
42
|
+
pubkey = Bitcoin::Key.new(nil, script.get_multisig_pubkeys.first.unpack("H*").first)
|
43
|
+
pubkey.verify_message(signature, message).should == true
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require_relative 'spec_helper.rb'
|
4
|
+
require 'bitcoin/script'
|
5
|
+
|
6
|
+
include Bitcoin
|
7
|
+
include Bitcoin::Builder
|
8
|
+
|
9
|
+
describe 'Bitcoin::Dogecoin' do
|
10
|
+
it 'validate dogecoin-address' do
|
11
|
+
|
12
|
+
Bitcoin::network = :dogecoin_testnet
|
13
|
+
|
14
|
+
# Testnet address
|
15
|
+
Bitcoin.valid_address?("nUtMFED5VRg5xuj9QCrNFt9mVPFDXo7TTE").should == true
|
16
|
+
# Livenet address
|
17
|
+
Bitcoin.valid_address?("DSpgzjPyfQB6ZzeSbMWpaZiTTxGf2oBCs4").should == false
|
18
|
+
# Broken address
|
19
|
+
Bitcoin.valid_address?("DRjyUS2uuieEPkhZNdQz8hE5YycxVEqSXA").should == false
|
20
|
+
|
21
|
+
Bitcoin::network = :dogecoin
|
22
|
+
|
23
|
+
# Testnet address
|
24
|
+
Bitcoin.valid_address?("nUtMFED5VRg5xuj9QCrNFt9mVPFDXo7TTE").should == false
|
25
|
+
# Livenet address
|
26
|
+
Bitcoin.valid_address?("DSpgzjPyfQB6ZzeSbMWpaZiTTxGf2oBCs4").should == true
|
27
|
+
# Broken address
|
28
|
+
Bitcoin.valid_address?("DRjyUS2uuieEPkhZNdQz8hE5YycxVEqSXA").should == false
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should calculate retarget difficulty' do
|
32
|
+
Bitcoin::network = :dogecoin
|
33
|
+
|
34
|
+
prev_height = 239
|
35
|
+
prev_block_time = 1386475638 # Block 239
|
36
|
+
prev_block_bits = 0x1e0ffff0
|
37
|
+
last_retarget_time = 1386474927 # Block 1
|
38
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
39
|
+
new_difficulty.to_s(16).should == 0x1e00ffff.to_s(16)
|
40
|
+
|
41
|
+
prev_height = 479
|
42
|
+
prev_block_time = 1386475840
|
43
|
+
prev_block_bits = 0x1e0fffff
|
44
|
+
last_retarget_time = 1386475638 # Block 239
|
45
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
46
|
+
new_difficulty.to_s(16).should == 0x1e00ffff.to_s(16)
|
47
|
+
|
48
|
+
prev_height = 9_599
|
49
|
+
prev_block_time = 1386954113
|
50
|
+
prev_block_bits = 0x1c1a1206
|
51
|
+
last_retarget_time = 1386942008 # Block 9359
|
52
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
53
|
+
new_difficulty.to_s(16).should == 0x1c15ea59.to_s(16)
|
54
|
+
|
55
|
+
# First hard-fork at 145,000, which applies to block 145,001 onwards
|
56
|
+
prev_height = 145_000
|
57
|
+
prev_block_time = 1395094679
|
58
|
+
prev_block_bits = 0x1b499dfd
|
59
|
+
last_retarget_time = 1395094427
|
60
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
61
|
+
new_difficulty.to_s(16).should == 0x1b671062.to_s(16)
|
62
|
+
|
63
|
+
# Test case for correct rounding of modulated time - by default C++ and Ruby do not
|
64
|
+
# necessarily round identically
|
65
|
+
prev_height = 145_001
|
66
|
+
prev_block_time = 1395094727
|
67
|
+
prev_block_bits = 0x1b671062
|
68
|
+
last_retarget_time = 1395094679
|
69
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
70
|
+
new_difficulty.to_s(16).should == 0x1b6558a4.to_s(16)
|
71
|
+
|
72
|
+
# Test the second hard-fork at 371,337 as well
|
73
|
+
prev_height = 371336
|
74
|
+
prev_block_time = 1410464569
|
75
|
+
prev_block_bits = 0x1b2fdf75
|
76
|
+
last_retarget_time = 1410464445
|
77
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
78
|
+
new_difficulty.to_s(16).should == 0x1b364184.to_s(16)
|
79
|
+
|
80
|
+
prev_height = 408_596
|
81
|
+
prev_block_time = 1412800112
|
82
|
+
prev_block_bits = 0x1b033d8b
|
83
|
+
last_retarget_time = 1412799989 # Block 408,595
|
84
|
+
new_difficulty = Bitcoin.block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
|
85
|
+
new_difficulty.to_s(16).should == 0x1b039e52.to_s(16)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should calculate reward upper bounds' do
|
89
|
+
Bitcoin::network = :dogecoin
|
90
|
+
|
91
|
+
Bitcoin.block_creation_reward(99000).should == 1000000 * COIN # Note this is the maximum possible, not actual reward
|
92
|
+
Bitcoin.block_creation_reward(144999).should == 500000 * COIN
|
93
|
+
Bitcoin.block_creation_reward(145000).should == 250000 * COIN # Hard-forked to remove random rewards
|
94
|
+
Bitcoin.block_creation_reward(199999).should == 250000 * COIN
|
95
|
+
Bitcoin.block_creation_reward(299999).should == 125000 * COIN
|
96
|
+
Bitcoin.block_creation_reward(399999).should == 62500 * COIN
|
97
|
+
Bitcoin.block_creation_reward(499999).should == 31250 * COIN
|
98
|
+
Bitcoin.block_creation_reward(599999).should == 15625 * COIN
|
99
|
+
Bitcoin.block_creation_reward(600000).should == 10000 * COIN
|
100
|
+
Bitcoin.block_creation_reward(700000).should == 10000 * COIN
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should calculate merkle root from AuxPoW transaction branch' do
|
104
|
+
# Taken directly from Dogecoin block #403,931
|
105
|
+
|
106
|
+
# Branch stored as bytes to reflect how data is stored in AuxPow class
|
107
|
+
branch = [
|
108
|
+
"\xbe\x07\x90\x78\x86\x93\x99\xfa\xcc\xaa\x76\x4c\x10\xe9\xdf\x6e\x99\x81\x70\x17\x59\xad\x18\xe1\x37\x24\xd9\xca\x58\x83\x13\x48",
|
109
|
+
"\x5f\x5b\xfb\x2c\x79\x54\x17\x78\x49\x9c\xab\x95\x6a\x10\x38\x87\x14\x7f\x2a\xb5\xd4\xa7\x17\xf3\x2f\x9e\xee\xbd\x29\xe1\xf8\x94",
|
110
|
+
"\xd8\xc6\xfe\x42\xca\x25\x07\x61\x59\xcd\x12\x1a\x5e\x20\xc4\x8c\x1b\xc5\x3a\xb9\x07\x30\x08\x3e\x44\xa3\x34\x56\x6e\xa6\xbb\xcb"
|
111
|
+
]
|
112
|
+
mrkl_index = 0
|
113
|
+
target = "089b911f5e471c0e1800f3384281ebec5b372fbb6f358790a92747ade271ccdf" # Coinbase TX ID
|
114
|
+
Bitcoin.mrkl_branch_root(branch.map(&:hth), target, mrkl_index).should == "f29cd14243ed542d9a0b495efcb9feca1b208bb5b717dc5ac04f068d2fef595a"
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should calculate merkle root from AuxPoW branch' do
|
118
|
+
# Taken directly from Dogecoin block #403,931
|
119
|
+
|
120
|
+
# Branch stored as bytes to reflect how data is stored in AuxPow class
|
121
|
+
aux_branch = [
|
122
|
+
"\x47\xa0\x22\x8b\x06\xc9\x36\x8f\x96\xc5\xf0\x4e\xb1\x09\xf8\x2c\xef\x36\xda\xe7\xc1\xbf\x25\x4c\x1a\x3f\x78\x61\x5e\xb0\xbe\x83",
|
123
|
+
"\xee\x67\xde\x31\x75\x76\x58\xdd\xd7\x40\x3e\x1a\x35\xd9\xc0\x6a\x5a\x13\xe6\x68\x98\x44\x3b\x45\x8c\xd6\xa7\x1b\x66\x27\x41\x6c",
|
124
|
+
"\xab\x9e\xf9\xbd\xa0\x2c\xad\x27\x90\xef\x9b\xb7\xc9\xa0\x7f\xe1\x79\x1a\x9d\x5a\xe0\x43\x09\xc0\xe9\x06\x48\x19\x19\x4c\x28\x31",
|
125
|
+
"\xff\x51\x61\x01\x80\xf6\x4d\x33\xa8\xc1\xba\x1d\xd9\xa9\xd0\x40\x48\x88\xc9\x6e\xaf\xd1\x57\x03\x64\x35\x8b\xbe\x99\x8f\x2d\xfe",
|
126
|
+
"\x9e\xe4\x18\x36\x7c\x3b\xce\x06\x5e\x7c\x01\x61\x29\x6e\xaa\x0d\x54\x96\xf9\x0f\x8b\x7b\x24\xeb\xf7\x2c\xc4\xba\xa5\x60\x9a\x1f",
|
127
|
+
"\x0b\x35\xf3\x73\x10\xe1\xde\x3f\xa4\xe1\x37\x7c\x02\x12\x62\x20\xe1\x64\xfa\x59\xec\xfe\xdc\xf4\x71\x4e\x61\xad\x74\xcc\x4b\x08"
|
128
|
+
]
|
129
|
+
aux_mrkl_index = 56
|
130
|
+
target = "0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609" # Block hash
|
131
|
+
Bitcoin.mrkl_branch_root(aux_branch.map(&:hth), target, aux_mrkl_index).should == "ce3040fdb7e37484f6a1ca4f8f5da81e6b7e404ec91102315a233e03a0c39c95" # Merkle root in coinbase script
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'parse AuxPoW' do
|
135
|
+
Bitcoin::network = :dogecoin
|
136
|
+
|
137
|
+
block_hash = "60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053"
|
138
|
+
data = fixtures_file("dogecoin-block-#{block_hash}.bin")
|
139
|
+
block = P::Block.new(data)
|
140
|
+
aux_pow = block.aux_pow
|
141
|
+
aux_pow.nil?.should == false
|
142
|
+
aux_pow.mrkl_index.should == 0
|
143
|
+
|
144
|
+
parent_block_merkle_root = Bitcoin.mrkl_branch_root(aux_pow.branch.map(&:reverse_hth), aux_pow.tx.hash, aux_pow.mrkl_index)
|
145
|
+
parent_block_merkle_root.should == aux_pow.parent_block.mrkl_root.reverse.unpack("H*")[0]
|
146
|
+
|
147
|
+
# Find the merged mining header in the coinbase input script
|
148
|
+
merged_mining_header = "\xfa\xbemm"
|
149
|
+
script = aux_pow.tx.in[0].script
|
150
|
+
header_idx = script.index(merged_mining_header)
|
151
|
+
|
152
|
+
header_idx.should == 4
|
153
|
+
|
154
|
+
chain_merkle_root = Bitcoin.mrkl_branch_root(aux_pow.aux_branch.map(&:reverse_hth), block_hash, aux_pow.aux_index)
|
155
|
+
|
156
|
+
# Drop everything up to the merged mining data
|
157
|
+
script = script.slice(header_idx + merged_mining_header.length, chain_merkle_root.length / 2 + 8)
|
158
|
+
|
159
|
+
tx_root_hash = script.slice(0, chain_merkle_root.length / 2).unpack("H*")[0]
|
160
|
+
chain_merkle_root.should == tx_root_hash
|
161
|
+
|
162
|
+
merkle_branch_size = script.slice(chain_merkle_root.length / 2, 4).unpack("V")[0]
|
163
|
+
merkle_branch_size.should == (1 << aux_pow.aux_branch.length)
|
164
|
+
|
165
|
+
# Choose a pseudo-random slot in the chain merkle tree
|
166
|
+
# but have it be fixed for a size/nonce/chain combination.
|
167
|
+
nonce = script.slice(chain_merkle_root.length / 2 + 4, 4).unpack("V")[0]
|
168
|
+
rand = nonce
|
169
|
+
rand = rand * 1103515245 + 12345
|
170
|
+
rand += Bitcoin.network[:auxpow_chain_id]
|
171
|
+
rand = rand * 1103515245 + 12345
|
172
|
+
|
173
|
+
aux_pow.aux_index.should == (rand % merkle_branch_size)
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|