Coo... coo...
This commit is contained in:
commit
5ae1a0a943
|
@ -0,0 +1,2 @@
|
||||||
|
coverage/
|
||||||
|
.pigeon/
|
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "ed25519"
|
||||||
|
gem "thor"
|
||||||
|
|
||||||
|
group :dev do
|
||||||
|
gem "rspec"
|
||||||
|
gem "pry"
|
||||||
|
gem "simplecov"
|
||||||
|
end
|
|
@ -0,0 +1,44 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
coderay (1.1.2)
|
||||||
|
diff-lcs (1.3)
|
||||||
|
docile (1.3.2)
|
||||||
|
ed25519 (1.2.4)
|
||||||
|
json (2.2.0)
|
||||||
|
method_source (0.9.2)
|
||||||
|
pry (0.12.2)
|
||||||
|
coderay (~> 1.1.0)
|
||||||
|
method_source (~> 0.9.0)
|
||||||
|
rspec (3.8.0)
|
||||||
|
rspec-core (~> 3.8.0)
|
||||||
|
rspec-expectations (~> 3.8.0)
|
||||||
|
rspec-mocks (~> 3.8.0)
|
||||||
|
rspec-core (3.8.2)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-expectations (3.8.4)
|
||||||
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-mocks (3.8.1)
|
||||||
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-support (3.8.2)
|
||||||
|
simplecov (0.17.1)
|
||||||
|
docile (~> 1.1)
|
||||||
|
json (>= 1.8, < 3)
|
||||||
|
simplecov-html (~> 0.10.0)
|
||||||
|
simplecov-html (0.10.2)
|
||||||
|
thor (0.20.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
ed25519
|
||||||
|
pry
|
||||||
|
rspec
|
||||||
|
simplecov
|
||||||
|
thor
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.0.2
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Pigeon Ruby
|
||||||
|
|
||||||
|
A WIP pigeon protocol client.
|
||||||
|
|
||||||
|
# How to Use
|
||||||
|
|
||||||
|
This is a pre-release skeleton project. There is no gem yet. The gem will be released after we are fully compliant with the spec and have high test coverage stats.
|
||||||
|
|
||||||
|
To get started, clone this repo and run `./pigeon-cli` in place of `pigeon`.
|
||||||
|
|
||||||
|
Eg: `pigeon identity show` becomes `./pigeon-cli show`.
|
||||||
|
|
||||||
|
# Current Status
|
||||||
|
|
||||||
|
- [X] pigeon status
|
||||||
|
- [X] pigeon identity new
|
||||||
|
- [X] pigeon identity show
|
||||||
|
|
||||||
|
- [ ] pigeon blob set
|
||||||
|
- [ ] pigeon blob get
|
||||||
|
|
||||||
|
- [ ] pigeon message new
|
||||||
|
- [ ] pigeon message current
|
||||||
|
- [ ] pigeon message append
|
||||||
|
- [ ] pigeon message save
|
||||||
|
- [ ] pigeon message find
|
||||||
|
- [ ] pigeon message find-all
|
||||||
|
|
||||||
|
- [ ] pigeon peer add
|
||||||
|
- [ ] pigeon peer remove
|
||||||
|
- [ ] pigeon peer block
|
||||||
|
- [ ] pigeon peer all
|
||||||
|
|
||||||
|
- [ ] pigeon bundle create
|
||||||
|
- [ ] pigeon bundle consume
|
|
@ -0,0 +1,100 @@
|
||||||
|
I need to implement these.
|
||||||
|
|
||||||
|
I'm adding them here for quick reference.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pigeon status
|
||||||
|
# => BLOBS: 10,234
|
||||||
|
# => PEERS: 26
|
||||||
|
# => VERSION: 0.0.1
|
||||||
|
# => FOO: BAR
|
||||||
|
|
||||||
|
pigeon identity new
|
||||||
|
# => @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
|
||||||
|
pigeon identity show
|
||||||
|
# => @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
|
||||||
|
pigeon blob set '"Lol, data"'
|
||||||
|
# => &2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
|
||||||
|
# Or use echo for big files:
|
||||||
|
echo "Lol, data"' > pigeon blob set
|
||||||
|
# => &2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
|
||||||
|
|
||||||
|
pigeon blob get "&2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256"
|
||||||
|
# => "Lol, data"
|
||||||
|
|
||||||
|
pigeon message new my_message
|
||||||
|
# => "Switched to message `my_message`
|
||||||
|
|
||||||
|
pigeon message current # Show active log entry.
|
||||||
|
# => author: @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
# => depth: 1
|
||||||
|
# => kind: &82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92.sha256
|
||||||
|
# => prev: %jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# =>
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon message append --name=2e7a0bc3 --value=2e7a0bc3
|
||||||
|
# => \n
|
||||||
|
# => This needs to be cleaner.
|
||||||
|
# => No one likes the way it is right now.
|
||||||
|
# => We will come back to this monstrosity later.
|
||||||
|
|
||||||
|
pigeon message save
|
||||||
|
# => author: @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
# => depth: 1
|
||||||
|
# => kind: &82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92.sha256
|
||||||
|
# => prev: %jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# =>
|
||||||
|
# => &ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb.sha256:&2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon message find %g0Fs9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# => author: @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
# => depth: 1
|
||||||
|
# => kind: &82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92.sha256
|
||||||
|
# => prev: %jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# =>
|
||||||
|
# => &ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb.sha256:&2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon message find-all --author=@ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519 --since=1
|
||||||
|
# => author: @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
# => depth: 1
|
||||||
|
# => kind: &82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92.sha256
|
||||||
|
# => prev: %jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# =>
|
||||||
|
# => &ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb.sha256:&2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
# =>
|
||||||
|
# => author: @ajgdylxeifojlxpbmen3exlnsbx8buspsjh37b/ipvi=.ed25519
|
||||||
|
# => depth: 2
|
||||||
|
# => kind: &82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92.sha256
|
||||||
|
# => prev: %jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
|
||||||
|
# =>
|
||||||
|
# => &ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb.sha256:&2e7a0bc31f3c4fe6114051c3a56c8ed8a030b3b394df7d29d37648e9b8cbf54b.sha256
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon peer add @m0LEP+0NrGqu1wT8/4a3nOPuRBM+DrMpUahDZ3/cDi8=.ed25519
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon peer remove @78daXMc/BOq5F1RWLMN4zgPVBVLqA4ShkLgE6z9OUGQ=.ed25519
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon peer block @GOl+398b2kWeLi6+DCcU0i3AWD6vWmUtocBVYbpkpNk=.ed25519
|
||||||
|
# =>
|
||||||
|
|
||||||
|
pigeon peer all
|
||||||
|
# => @c8hovH5OOzNJ1SXUsIN+zI23xMcvGdEbs3ZJgzpthrw=.ed25519
|
||||||
|
# => @GOl+398b2kWeLi6+DCcU0i3AWD6vWmUtocBVYbpkpNk=.ed25519
|
||||||
|
# => @m0LEP+0NrGqu1wT8/4a3nOPuRBM+DrMpUahDZ3/cDi8=.ed25519
|
||||||
|
|
||||||
|
pigeon bundle create
|
||||||
|
# => (creates @GOl+398b2kWeLi6+DCcU0i3AWD6vWmUtocBVYbpkpNk=.ed25519.pigeon)
|
||||||
|
|
||||||
|
pigeon bundle consume @GOl+398b2kWeLi6+DCcU0i3AWD6vWmUtocBVYbpkpNk=.ed25519.pigeon
|
||||||
|
# =>
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,6 @@
|
||||||
|
require_relative File.join("pigeon", "config.rb")
|
||||||
|
require_relative File.join("pigeon", "storage.rb")
|
||||||
|
require_relative File.join("pigeon", "key_pair.rb")
|
||||||
|
|
||||||
|
module Pigeon
|
||||||
|
end
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
module Pigeon
|
||||||
|
module Config
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,43 @@
|
||||||
|
require "ed25519"
|
||||||
|
require "securerandom"
|
||||||
|
require "base64"
|
||||||
|
|
||||||
|
module Pigeon
|
||||||
|
# This is a wrapper around the `ed25519` gem to
|
||||||
|
# help us maintain our sanity when the Gem's API
|
||||||
|
# changes.
|
||||||
|
class KeyPair
|
||||||
|
HEADER, FOOTER = ["@", ".ed25519"]
|
||||||
|
|
||||||
|
def self.current
|
||||||
|
raise "TODO"
|
||||||
|
end
|
||||||
|
|
||||||
|
# `seed` is a 32-byte seed value from which
|
||||||
|
# the key should be derived
|
||||||
|
def initialize(seed = SecureRandom.random_bytes(Ed25519::KEY_SIZE))
|
||||||
|
@seed = seed
|
||||||
|
@raw_key = Ed25519::SigningKey.new(seed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def private_key
|
||||||
|
@private_key ||= Base64.strict_encode64(@seed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_key
|
||||||
|
bytes = @raw_key.verify_key.to_bytes
|
||||||
|
b64 = Base64.strict_encode64(bytes)
|
||||||
|
|
||||||
|
@public_key ||= [HEADER, b64, FOOTER].join("")
|
||||||
|
end
|
||||||
|
|
||||||
|
def save!
|
||||||
|
{
|
||||||
|
public_key: public_key,
|
||||||
|
private_key: private_key,
|
||||||
|
}.map do |k, v|
|
||||||
|
Pigeon::Storage.current.save_conf(k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,75 @@
|
||||||
|
require "pry"
|
||||||
|
require "digest"
|
||||||
|
|
||||||
|
module Pigeon
|
||||||
|
class Storage
|
||||||
|
ROOT_DIR = ".pigeon"
|
||||||
|
CONF_DIR = "conf"
|
||||||
|
BLOB_DIR = "blobs"
|
||||||
|
|
||||||
|
def self.current
|
||||||
|
@current ||= self.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
unless initialized?
|
||||||
|
create_root_dir
|
||||||
|
create_conf_dir
|
||||||
|
create_blob_dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_conf(key, value)
|
||||||
|
path = conf_path_for(key)
|
||||||
|
File.write(path, value.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_conf(key)
|
||||||
|
File.read(conf_path_for(key))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_blob(data)
|
||||||
|
hash = Digest::SHA256.hexdigest(data)
|
||||||
|
path = blob_path_for(hash)
|
||||||
|
|
||||||
|
File.write(path, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialized?
|
||||||
|
File.directory?(root_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def blob_dir
|
||||||
|
@blob_dir ||= File.join(ROOT_DIR, BLOB_DIR, "sha256")
|
||||||
|
end
|
||||||
|
|
||||||
|
def root_dir
|
||||||
|
@root_dir ||= File.join(ROOT_DIR)
|
||||||
|
end
|
||||||
|
|
||||||
|
# WARNING: Side effects. Im in a hurry. -RC
|
||||||
|
def blob_path_for(hex_hash_string)
|
||||||
|
first_part = File.join(blob_dir, hex_hash_string[0, 2])
|
||||||
|
FileUtils.mkdir_p(first_part)
|
||||||
|
File.join(first_part, hex_hash_string[2..-1])
|
||||||
|
end
|
||||||
|
|
||||||
|
def conf_path_for(key)
|
||||||
|
File.join(conf_dir, key.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_conf_dir
|
||||||
|
FileUtils.mkdir_p(File.join(ROOT_DIR, CONF_DIR))
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_blob_dir
|
||||||
|
FileUtils.mkdir_p(blob_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_root_dir
|
||||||
|
FileUtils.mkdir_p(root_dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require_relative File.join("dist", "pigeon")
|
||||||
|
|
||||||
|
require "thor"
|
||||||
|
|
||||||
|
module Pigeon
|
||||||
|
class Identity < Thor
|
||||||
|
class RoostAlreadyExists < StandardError; end
|
||||||
|
|
||||||
|
desc "new", "Creates a new identiy in `.pigeon` directory if none exists"
|
||||||
|
|
||||||
|
def new
|
||||||
|
# TODO: --force flag
|
||||||
|
# TODO: --seed flag
|
||||||
|
if Dir.exist?(Pigeon::Storage::ROOT_DIR)
|
||||||
|
puts "Pigeon has detected a `.pigeon` directory.
|
||||||
|
Refusing to overwrite existing Pigeon config.
|
||||||
|
Remove roost or switch to a different directory."
|
||||||
|
raise RoostAlreadyExists
|
||||||
|
end
|
||||||
|
kp = Pigeon::KeyPair.new()
|
||||||
|
kp.save!
|
||||||
|
puts kp.public_key
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "show", "Prints a base64 identiy string to STDOUT"
|
||||||
|
|
||||||
|
def show
|
||||||
|
puts Pigeon::Storage.current.get_conf("public_key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Blob < Thor
|
||||||
|
desc "set", "Copy arbitrary binary data into the roost"
|
||||||
|
|
||||||
|
def set(data)
|
||||||
|
Pigeon::Storage.current.set_blob(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "get", "Read arbitrary data from the roost"
|
||||||
|
|
||||||
|
def get
|
||||||
|
raise "WIP"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CLI < Thor
|
||||||
|
desc "status", "Show various information about the `.pigeon` directory"
|
||||||
|
|
||||||
|
def status
|
||||||
|
puts "Version: #{Config::VERSION}"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "identity SUBCOMMAND ...ARGS", "Manage `.pigeon` identity"
|
||||||
|
subcommand "identity", Identity
|
||||||
|
|
||||||
|
desc "blob SUBCOMMAND ...ARGS", "Manage blob storage"
|
||||||
|
subcommand "blob", Blob
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Pigeon::CLI.start(ARGV)
|
|
@ -0,0 +1,7 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.describe Pigeon::Config do
|
||||||
|
it "has a `foo`" do
|
||||||
|
expect(Pigeon::Config::VERSION).to eq("0.0.1")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.describe Pigeon::KeyPair do
|
||||||
|
FAKE_SEED = "\x15\xB1\xA8\x1D\xE1\x1Cx\xF0" \
|
||||||
|
"\xC6\xDCK\xDE\x9A\xB7>\x86o\x92\xEF\xB7\x17" \
|
||||||
|
")\xFF\x01E\b$b)\xC9\x82\b"
|
||||||
|
|
||||||
|
let(:kp) { Pigeon::KeyPair.new(FAKE_SEED) }
|
||||||
|
|
||||||
|
it "generates a pair from a seed" do
|
||||||
|
x = "@7n/g0ca9FFWvMkXy2TMwM7bdMn6tNiEHKzrFX+CzAmQ=.ed25519"
|
||||||
|
expect(kp.public_key).to eq(x)
|
||||||
|
y = "FbGoHeEcePDG3Evemrc+hm+S77cXKf8BRQgkYinJggg="
|
||||||
|
expect(kp.private_key).to eq(y)
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Add fakefs https://github.com/fakefs/fakefss
|
||||||
|
it "saves keypairs to disk"
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
require "pry"
|
||||||
|
require "simplecov"
|
||||||
|
SimpleCov.start
|
||||||
|
require_relative File.join("..", "dist", "pigeon")
|
||||||
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.disable_monkey_patching!
|
||||||
|
config.order = :random
|
||||||
|
end
|
Loading…
Reference in New Issue