counterparty_ruby 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +46 -0
- data/README.md +197 -0
- data/Rakefile +28 -0
- data/counterparty_ruby.gemspec +26 -0
- data/lib/counterparty/connection.rb +200 -0
- data/lib/counterparty/raw_tx.rb +130 -0
- data/lib/counterparty/resource.rb +179 -0
- data/lib/counterparty/resources.rb +713 -0
- data/lib/counterparty/version.rb +4 -0
- data/lib/counterparty_ruby.rb +70 -0
- data/spec/config.yml +3 -0
- data/spec/connection_spec.rb +14 -0
- data/spec/exceptions_spec.rb +38 -0
- data/spec/rawtx_decode_spec.rb +122 -0
- data/spec/resource_create_spec.rb +69 -0
- data/spec/resource_read_spec.rb +91 -0
- data/spec/spec_helper.rb +29 -0
- metadata +185 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'bitcoin'
|
5
|
+
|
6
|
+
require 'counterparty/raw_tx'
|
7
|
+
require 'counterparty/version'
|
8
|
+
require 'counterparty/resource'
|
9
|
+
require 'counterparty/resources'
|
10
|
+
require 'counterparty/connection'
|
11
|
+
|
12
|
+
# The main module, under which all classes in the library are defined.
|
13
|
+
module Counterparty
|
14
|
+
# One XCP, in units of Satoshi
|
15
|
+
ONE_XCP = 100_000_000
|
16
|
+
|
17
|
+
# One BTC, in units of Satoshi
|
18
|
+
ONE_BTC = 100_000_000
|
19
|
+
|
20
|
+
# This exception is typically raised by errors related to the params and/or
|
21
|
+
# request format
|
22
|
+
class JsonResponseError < StandardError; end
|
23
|
+
|
24
|
+
# This exception comes from an error relating to a proper request, but an
|
25
|
+
# inability to complete the request via the counterpartyd api
|
26
|
+
class ResponseError < StandardError
|
27
|
+
attr_reader :data_type
|
28
|
+
attr_reader :data_args
|
29
|
+
attr_reader :data_message
|
30
|
+
attr_reader :code
|
31
|
+
attr_reader :message_class
|
32
|
+
|
33
|
+
def initialize(json)
|
34
|
+
@message_class, @code = json['message'], json['code']
|
35
|
+
|
36
|
+
json['data'].each_pair do |(k,v)|
|
37
|
+
instance_variable_set '@data_%s' % k, v
|
38
|
+
end if json.has_key? 'data'
|
39
|
+
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def message
|
44
|
+
'%s: %s' % [@message_class,@data_message]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
# Sets/Gets the default connection object
|
50
|
+
attr_writer :connection
|
51
|
+
|
52
|
+
# Returns the current default connection object, or creates a new test-mode
|
53
|
+
# connection, if none has been defined
|
54
|
+
def connection
|
55
|
+
@connection || Connection.new
|
56
|
+
end
|
57
|
+
|
58
|
+
# Establishes the default connection for new objects as being the default
|
59
|
+
# counterparty production mode port/user/ip
|
60
|
+
def production!
|
61
|
+
@connection = Connection.new 4000
|
62
|
+
end
|
63
|
+
|
64
|
+
# Establishes the default connection for new objects as being the default
|
65
|
+
# counterparty test mode port/user/ip
|
66
|
+
def test!
|
67
|
+
@connection = Connection.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/config.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Counterparty::Connection do
|
4
|
+
describe "#new" do
|
5
|
+
# It should default to the test network parameters
|
6
|
+
subject{Counterparty::Connection.new}
|
7
|
+
|
8
|
+
its(:host){ should eq('localhost') }
|
9
|
+
its(:port){ should eq(14000) }
|
10
|
+
its(:username){ should eq('rpc') }
|
11
|
+
its(:password){ should eq('1234') }
|
12
|
+
its(:api_url){ should eq('http://rpc:1234@localhost:14000/api/') }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Counterparty::ResponseError do
|
4
|
+
include_context 'globals'
|
5
|
+
|
6
|
+
let(:bad_issuance) do
|
7
|
+
Counterparty::Issuance.new source: source_address,
|
8
|
+
asset: 'THISASSETNAMEISFARTOOLONGANDINVALID',
|
9
|
+
quantity: 1000, description: "my asset is uncool",
|
10
|
+
allow_unconfirmed_inputs: true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should fail on to_raw_tx" do
|
14
|
+
expect{ bad_issuance.to_raw_tx }.to raise_error Counterparty::ResponseError
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should fail on save!" do
|
18
|
+
expect{ bad_issuance.save! }.to raise_error Counterparty::ResponseError
|
19
|
+
end
|
20
|
+
|
21
|
+
subject do
|
22
|
+
begin
|
23
|
+
bad_issuance.save!
|
24
|
+
rescue => error
|
25
|
+
error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
its(:data_type) { should eq('AssetNameError') }
|
30
|
+
its(:data_args) { should eq(["long asset names must be numeric"]) }
|
31
|
+
its(:data_message) { should eq("long asset names must be numeric") }
|
32
|
+
its(:code) { should eq(-32000) }
|
33
|
+
its(:message) { should eq("Server error: long asset names must be numeric") }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe Counterparty::JsonResponseError do
|
37
|
+
pending
|
38
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RawTx do
|
4
|
+
describe ".double_sha256" do
|
5
|
+
# http://bitcoin.stackexchange.com/questions/2177/how-to-calculate-a-hash-of-a-tx
|
6
|
+
genesis_block = '01000000010000000000000000000000000000000000000000000'+
|
7
|
+
'000000000000000000000FFFFFFFF4D04FFFF001D0104455468652054696D657320'+
|
8
|
+
'30332F4A616E2F32303039204368616E63656C6C6F72206F6E206272696E6B206F6'+
|
9
|
+
'6207365636F6E64206261696C6F757420666F722062616E6B73FFFFFFFF0100F205'+
|
10
|
+
'2A01000000434104678AFDB0FE5548271967F1A67130B7105CD6A828E03909A6796'+
|
11
|
+
'2E0EA1F61DEB649F6BC3F4CEF38C4F35504E51EC112DE5C384DF7BA0B8D578A4C70'+
|
12
|
+
'2B6BF11D5FAC00000000'
|
13
|
+
genesis_double_sha = '4A5E1E4BAAB89F3A32518A88C31BC87F618F76673E2CC77AB2127B7AFDEDA33B'
|
14
|
+
|
15
|
+
pending
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ".nibbles_to_ui" do
|
19
|
+
it "should fail on byte arrays larger than 32 bits" do
|
20
|
+
expect{ RawTx.nibbles_to_ui([1,0,0,0,0]) }.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Remember that the least significant byte is first here:
|
24
|
+
it ("should convert 0x10 to 1") { expect(RawTx.nibbles_to_ui([1,0])).to eq(1) }
|
25
|
+
it ("should convert 0x01 to 16") { expect(RawTx.nibbles_to_ui([0,1])).to eq(16) }
|
26
|
+
it ("should convert 0xf to 15") { expect(RawTx.nibbles_to_ui([0xf])).to eq(15) }
|
27
|
+
it ("should convert 0x11 to 17") { expect(RawTx.nibbles_to_ui([1,1])).to eq(17) }
|
28
|
+
it ("should convert 0xf1 to 31") { expect(RawTx.nibbles_to_ui([0xf,1])).to eq(31) }
|
29
|
+
it ("should convert 0xff to 255") { expect(RawTx.nibbles_to_ui([0xf,0xf])).to eq(255) }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".bytes_to_base64_s" do
|
33
|
+
it "should convert 0x14FB9C03D97E to FPucA91+" do
|
34
|
+
bytes = [0x14, 0xFB, 0x9C, 0x03, 0xD9, 0x7E]
|
35
|
+
expect(RawTx.bytes_to_base64_s bytes).to eq("FPucA9l+")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should convert 0x14FB9C03D9 to FPucA9k=" do
|
39
|
+
bytes = [0x14, 0xFB, 0x9C, 0x03, 0xD9]
|
40
|
+
expect(RawTx.bytes_to_base64_s bytes).to eq("FPucA9k=")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should convert 0x14FB9C03 to FPucAw==" do
|
44
|
+
bytes = [0x14, 0xFB, 0x9C, 0x03]
|
45
|
+
expect(RawTx.bytes_to_base64_s bytes).to eq("FPucAw==")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#to_hash" do
|
50
|
+
# NOTE that these spec values were obtained via:
|
51
|
+
# bitcoind -testnet decoderawtransaction [subject]
|
52
|
+
subject{ RawTx.new(
|
53
|
+
"0100000001ec549bdf53cfb319201d672fc0933500e48505724010dad615a9344b99" +
|
54
|
+
"1721cc010000001976a9148025b288cb325d88bcd7ef5d1ab1f8827778d5ee88acff" +
|
55
|
+
"ffffff03781e0000000000006951210286cdf9ec8742c452bd13299771fe40130124" +
|
56
|
+
"038e2198ffc402204c48cc2d32da21033de0df1555e81783f42e00458d0b542ff293" +
|
57
|
+
"d9d04fbc7704650448ee07728a8a210286b1e4f15de57fd34bde19cf64ad9302e454" +
|
58
|
+
"c4c377677581a951579a124b86e753ae781e00000000000069512102a2cdf9ec8742" +
|
59
|
+
"c452bd2214fe01c9f33b0d0066ed0efb90aa715400038c1c621921034f89bc707587" +
|
60
|
+
"71a393416c21a12b651db3def9851bff574904762b86365caaea210286b1e4f15de5" +
|
61
|
+
"7fd34bde19cf64ad9302e454c4c377677581a951579a124b86e753aec0ba770d0000" +
|
62
|
+
"00001976a9148025b288cb325d88bcd7ef5d1ab1f8827778d5ee88ac00000000").to_hash }
|
63
|
+
|
64
|
+
its(['ver']){should eq(1)}
|
65
|
+
its(['lock_time']){should eq(0)}
|
66
|
+
its(['size']){should eq(338)}
|
67
|
+
its(['vin_sz']){should eq(1)}
|
68
|
+
its(['vout_sz']){should eq(3)}
|
69
|
+
|
70
|
+
its(['vin']) do
|
71
|
+
# If we're testing to base64, the hash would look like:
|
72
|
+
# RawTx.bytes_to_base64_s(out_hash.scan(/../).collect(&:hex))
|
73
|
+
should eq([
|
74
|
+
{ "txid" => "cc2117994b34a915d6da1040720585e4003593c02f671d2019b3cf53df9b54ec",
|
75
|
+
"vout" => 1,
|
76
|
+
"scriptSig" => {
|
77
|
+
"hex" => "76a9148025b288cb325d88bcd7ef5d1ab1f8827778d5ee88ac",
|
78
|
+
"asm" => %w(OP_DUP OP_HASH160
|
79
|
+
8025b288cb325d88bcd7ef5d1ab1f8827778d5ee OP_EQUALVERIFY
|
80
|
+
OP_CHECKSIG).join(' ')
|
81
|
+
}, "sequence" => 4294967295 }])
|
82
|
+
end
|
83
|
+
|
84
|
+
its(['vout']) do
|
85
|
+
should eq( [
|
86
|
+
{ "value" => 0.00007800, "n" => 0,
|
87
|
+
"scriptPubKey" => {
|
88
|
+
"hex" => "51210286cdf9ec8742c452bd13299771fe40130124038e2198ffc40"+
|
89
|
+
"2204c48cc2d32da21033de0df1555e81783f42e00458d0b542ff293d9d04f"+
|
90
|
+
"bc7704650448ee07728a8a210286b1e4f15de57fd34bde19cf64ad9302e45"+
|
91
|
+
"4c4c377677581a951579a124b86e753ae",
|
92
|
+
"asm" => "1 0286cdf9ec8742c452bd13299771fe40130124038e2198ffc402204c48cc2d32da 033de0df1555e81783f42e00458d0b542ff293d9d04fbc7704650448ee07728a8a 0286b1e4f15de57fd34bde19cf64ad9302e454c4c377677581a951579a124b86e7 3 OP_CHECKMULTISIG" }
|
93
|
+
},
|
94
|
+
{ "value" => 0.00007800, "n" => 1,
|
95
|
+
"scriptPubKey" => {
|
96
|
+
"hex" => "512102a2cdf9ec8742c452bd2214fe01c9f33b0d0066ed0efb90aa71"+
|
97
|
+
"5400038c1c621921034f89bc70758771a393416c21a12b651db3def9851bf"+
|
98
|
+
"f574904762b86365caaea210286b1e4f15de57fd34bde19cf64ad9302e454"+
|
99
|
+
"c4c377677581a951579a124b86e753ae",
|
100
|
+
"asm" => "1 02a2cdf9ec8742c452bd2214fe01c9f33b0d0066ed0efb90aa715400038c1c6219 034f89bc70758771a393416c21a12b651db3def9851bff574904762b86365caaea 0286b1e4f15de57fd34bde19cf64ad9302e454c4c377677581a951579a124b86e7 3 OP_CHECKMULTISIG" }
|
101
|
+
},
|
102
|
+
{ "value" => 2.25950400, "n" => 2,
|
103
|
+
"scriptPubKey" => {
|
104
|
+
"hex" => "76a9148025b288cb325d88bcd7ef5d1ab1f8827778d5ee88ac",
|
105
|
+
"asm" => "OP_DUP OP_HASH160 8025b288cb325d88bcd7ef5d1ab1f8827778d5ee OP_EQUALVERIFY OP_CHECKSIG" } } ] )
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe("#to_hash coinbase") do
|
110
|
+
|
111
|
+
subject{ RawTx.new(
|
112
|
+
"01000000010000000000000000000000000000000000000000000000000000000000" +
|
113
|
+
"000000ffffffff53038349040d00456c69676975730052d8f72ffabe6d6dd991088d" +
|
114
|
+
"ecd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b040000000000" +
|
115
|
+
"0000002f73733331312f00906b570400000000e4050000ffffffff01bf2087950000" +
|
116
|
+
"00001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000").to_hash }
|
117
|
+
|
118
|
+
pending
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# NOTE: Most of these examples are from:
|
4
|
+
# https://github.com/CounterpartyXCP/counterpartyd/blob/master/docs/API.rst#id8
|
5
|
+
# The tests have to run in the specified order, as we populate our test account
|
6
|
+
# before running test operations
|
7
|
+
describe Counterparty do
|
8
|
+
include_context 'globals'
|
9
|
+
|
10
|
+
before(:all) { Counterparty.test! }
|
11
|
+
|
12
|
+
# TODO: deprecate?
|
13
|
+
#it "Ensure test account has BTC" do
|
14
|
+
#expect(bitcoin.getreceivedbyaddress(source_address)).to be > 0
|
15
|
+
#end
|
16
|
+
|
17
|
+
describe "Ensure test account has XCP" do
|
18
|
+
subject do
|
19
|
+
Counterparty::Balance.find( filters:
|
20
|
+
{ field: 'address', op: '==', value: source_address}
|
21
|
+
).find{|b| b.asset == 'XCP' }
|
22
|
+
end
|
23
|
+
|
24
|
+
its(:quantity){ should be > Counterparty::ONE_XCP }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Send 1 XCP (specified in satoshis) from one address to another (you must have
|
28
|
+
# the sending address in your bitcoind wallet and it will be broadcast as a
|
29
|
+
# multisig transaction
|
30
|
+
describe "#do_send" do
|
31
|
+
subject do
|
32
|
+
Counterparty::Send.new source: source_address, destination: destination_address,
|
33
|
+
asset: "XCP", quantity: Counterparty::ONE_XCP,
|
34
|
+
allow_unconfirmed_inputs: true
|
35
|
+
end
|
36
|
+
|
37
|
+
its(:to_raw_tx) { should_not be_empty }
|
38
|
+
its(:save!) { should_not be_empty }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#do_issuance" do
|
42
|
+
subject do
|
43
|
+
Counterparty::Issuance.new source: source_address, asset: unique_asset_name,
|
44
|
+
quantity: 1000, description: "my asset is cool", divisible: true,
|
45
|
+
allow_unconfirmed_inputs: true
|
46
|
+
end
|
47
|
+
|
48
|
+
its(:to_raw_tx) { should_not be_empty }
|
49
|
+
its(:save!) { should_not be_empty }
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "signed #create_broadcast" do
|
53
|
+
subject do
|
54
|
+
# We want the save(private_key) syntax here
|
55
|
+
Counterparty::Broadcast.new source: source_address, fee_fraction: 0.05,
|
56
|
+
text: "Price of gold, 12AM UTC March1. 1=inc 2=dec/const", value: 2.0,
|
57
|
+
timestamp: 1418926641,
|
58
|
+
allow_unconfirmed_inputs: true
|
59
|
+
end
|
60
|
+
|
61
|
+
its(:to_raw_tx) { should_not be_empty }
|
62
|
+
|
63
|
+
it "should persist using a provided key" do
|
64
|
+
# TODO: Make this work
|
65
|
+
expect(subject.save!(source_privkey)).to_not be_empty
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# NOTE: Most of these examples are from:
|
4
|
+
# https://github.com/CounterpartyXCP/counterpartyd/blob/master/docs/API.rst#id8
|
5
|
+
describe Counterparty do
|
6
|
+
include_context 'globals'
|
7
|
+
|
8
|
+
before(:all) { Counterparty.production! }
|
9
|
+
|
10
|
+
# Get all burns between blocks 280537 and 280539 where greater than .2 BTC was
|
11
|
+
# burned, sorting by tx_hash (ascending order) With this (and the rest of the
|
12
|
+
# examples below) we use positional arguments, instead of keyword-based arguments
|
13
|
+
describe "#get_burns" do
|
14
|
+
burn_params = {
|
15
|
+
order_by: 'tx_hash',
|
16
|
+
order_dir: 'asc',
|
17
|
+
start_block: 280537,
|
18
|
+
end_block: 280539 }
|
19
|
+
|
20
|
+
subject{ Counterparty::Burn.find burn_params }
|
21
|
+
|
22
|
+
it("should equal get_burns") do
|
23
|
+
expect(subject).to eq(Counterparty.connection.get_burns(burn_params))
|
24
|
+
end
|
25
|
+
|
26
|
+
its('length'){ should eq(10) }
|
27
|
+
its('first') do
|
28
|
+
should eq(Counterparty::Burn.new(
|
29
|
+
tx_index: 1096,
|
30
|
+
source: '1ADpYypUcnbezuuYpCyRCY7G4KD6a9YXiF',
|
31
|
+
block_index: 280537,
|
32
|
+
earned: 129754545455,
|
33
|
+
status: "valid",
|
34
|
+
burned: 100000000,
|
35
|
+
tx_hash: '6e905ec73870d6c6cdc8f4e64767ec41f74c8f07a24cc7c54811134f5b6aa6a7'
|
36
|
+
) )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fetch all balances for all assets for both of two addresses, using keyword-
|
41
|
+
# based arguments
|
42
|
+
describe "#get_balances" do
|
43
|
+
balance_params = { filters: {
|
44
|
+
field: 'address', op: '==', value: "14qqz8xpzzEtj6zLs3M1iASP7T4mj687yq" } }
|
45
|
+
|
46
|
+
subject{ Counterparty::Balance.find balance_params }
|
47
|
+
|
48
|
+
it("should equal get_balances") do
|
49
|
+
expect(subject).to eq(Counterparty.connection.get_balances(balance_params))
|
50
|
+
end
|
51
|
+
|
52
|
+
its('length'){ should eq(1) }
|
53
|
+
its('first') do
|
54
|
+
should eq(Counterparty::Balance.new(
|
55
|
+
quantity: 0,
|
56
|
+
asset: "XCP",
|
57
|
+
address: "14qqz8xpzzEtj6zLs3M1iASP7T4mj687yq" ) )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Fetch all debits for > 2 XCP between blocks 280537 and 280539, sorting the
|
62
|
+
# results by quantity (descending order)
|
63
|
+
describe "#get_debits" do
|
64
|
+
debit_params = {
|
65
|
+
filters: [
|
66
|
+
{field: 'asset', op: '==', value: "XCP"},
|
67
|
+
{field: 'quantity', op: '>', value: 200000000}
|
68
|
+
],
|
69
|
+
filterop: 'AND',
|
70
|
+
order_by: 'quantity',
|
71
|
+
order_dir: 'desc'}
|
72
|
+
|
73
|
+
subject{ Counterparty::Debit.find debit_params }
|
74
|
+
|
75
|
+
it("should equal get_debits") do
|
76
|
+
expect(subject).to eq(Counterparty.connection.get_debits(debit_params))
|
77
|
+
end
|
78
|
+
|
79
|
+
its('length'){ should eq(1000) }
|
80
|
+
its('first') do
|
81
|
+
should eq(Counterparty::Debit.new(
|
82
|
+
block_index: 318845,
|
83
|
+
asset: "XCP",
|
84
|
+
address: "1FxhdSid1TUfzfTyveMcsUhF3ePjRK6qqa",
|
85
|
+
action: "send",
|
86
|
+
event: "64c26f4dc12fbbdbead62962a9428d4a6b17a44c061d202ff525e2f513cd34e8",
|
87
|
+
quantity: 6184957374430
|
88
|
+
) )
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$:<< File.join(File.dirname(__FILE__), '..','lib')
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'rspec/its'
|
5
|
+
require 'counterparty_ruby'
|
6
|
+
|
7
|
+
shared_context 'globals' do
|
8
|
+
let(:config) do
|
9
|
+
YAML.load File.open([File.dirname(__FILE__),'config.yml'].join('/')).read
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:source_address) { config['source_address'] }
|
13
|
+
let(:source_privkey) { config['source_privkey'] }
|
14
|
+
let(:destination_address) { config['spend_destination'] }
|
15
|
+
|
16
|
+
# Since asset names have to be unique, we try our best to create a unique
|
17
|
+
# one here. This asset is composed of the timestamp, plus the machine
|
18
|
+
# name we're running on. Be advised this might be a small privacy breach
|
19
|
+
# forsensitive operations.
|
20
|
+
# There might be some collisions here due to my not handling fixed-width
|
21
|
+
# integers greater than 26. I don't think I care about that right now
|
22
|
+
let!(:unique_asset_name) do
|
23
|
+
base26_time = Time.now.strftime('%y %m %d %H %M %S').split(' ').collect{|c| c.to_i.to_s(26)}
|
24
|
+
alpha_encode = base26_time.join.tr((('0'..'9').to_a+('a'..'q').to_a).join, ('A'..'Z').to_a.join)
|
25
|
+
|
26
|
+
[alpha_encode,`hostname`.upcase.tr('^A-Z','')].join[0...12]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: counterparty_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris DeRose
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rest_client
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bitcoin-ruby
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: ffi
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec-its
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rdoc
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
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
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: This gem is designed to abstract communications with the counterpartyd
|
127
|
+
api server in an ActiveRecord-esque object model
|
128
|
+
email:
|
129
|
+
- chris@chrisderose.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- Gemfile
|
136
|
+
- Gemfile.lock
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- counterparty_ruby.gemspec
|
140
|
+
- lib/counterparty/connection.rb
|
141
|
+
- lib/counterparty/raw_tx.rb
|
142
|
+
- lib/counterparty/resource.rb
|
143
|
+
- lib/counterparty/resources.rb
|
144
|
+
- lib/counterparty/version.rb
|
145
|
+
- lib/counterparty_ruby.rb
|
146
|
+
- spec/config.yml
|
147
|
+
- spec/connection_spec.rb
|
148
|
+
- spec/exceptions_spec.rb
|
149
|
+
- spec/rawtx_decode_spec.rb
|
150
|
+
- spec/resource_create_spec.rb
|
151
|
+
- spec/resource_read_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
homepage: https://github.com/brighton36/counterparty_ruby
|
154
|
+
licenses:
|
155
|
+
- LGPL
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '1.9'
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
|
+
none: false
|
168
|
+
requirements:
|
169
|
+
- - ! '>='
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
requirements: []
|
173
|
+
rubyforge_project:
|
174
|
+
rubygems_version: 1.8.23
|
175
|
+
signing_key:
|
176
|
+
specification_version: 3
|
177
|
+
summary: An ActiveRecord-esque abstraction of the Counterparty API
|
178
|
+
test_files:
|
179
|
+
- spec/config.yml
|
180
|
+
- spec/connection_spec.rb
|
181
|
+
- spec/exceptions_spec.rb
|
182
|
+
- spec/rawtx_decode_spec.rb
|
183
|
+
- spec/resource_create_spec.rb
|
184
|
+
- spec/resource_read_spec.rb
|
185
|
+
- spec/spec_helper.rb
|