WIP. TODO: Make sure client is ingesting blobs in a bundle

This commit is contained in:
Netscape Navigator 2020-04-23 07:21:00 -05:00
parent e087888e94
commit 8fed4dc0bc
17 changed files with 189 additions and 116 deletions

60
.map Normal file
View File

@ -0,0 +1,60 @@
=> #<Pigeon::Database:0x0000559bd1590cc8
@who_am_i=#<Pigeon::LocalIdentity:0x0000559bd15d1890 @seed="\x1FL^9\x90e\xF8g\xF2\x83\xB4\xC4>c\x85\xAE2w\xBA&,@\xD3\x96\xCAlO*\xB9\x8DkX", @signing_key=#<Ed25519::SigningKey:0x0000559bd15d17c8>>,
@store=
#<Pigeon::Storage:0x0000559bd15909f8
@path="old.db",
@store=
#<PStore:0x0000559bd1590458
@abort=false,
@filename="pigeon.db",
@lock=#<Thread::Mutex:0x0000559bd1597988>,
@rdonly=true,
@table=
{"blocked"=>#<Set: {}>,
"conf"=>{"SEED"=>"\x1FL^9\x90e\xF8g\xF2\x83\xB4\xC4>c\x85\xAE2w\xBA&,@\xD3\x96\xCAlO*\xB9\x8DkX", "HEAD.draft"=>nil},
"messages.count"=>{"@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519"=>4},
"messages"=>
{"%8STBRYR0WYJJRSD8DRA9RT6RF6QD6AKARCDYV17X8REGACHZ41W0.sha256"=>
#<Pigeon::Message:0x0000559bd15eb448
@author=#<Pigeon::RemoteIdentity:0x0000559bd15ea4d0 @multihash="@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519">,
@body={"a"=>"\"b\""},
@depth=0,
@kind="unit_test",
@lipmaa=0,
@prev="NONE",
@signature="8581280WEPNQGJ4DA6K0M7W726YVK1QGW5T7EPGZ61V4HV2GD1RKAB5AN8FXREW0NZKYKFANHW64H3YT0RSMMY30T8TBDJJSR0G9E0G.sig.ed25519">,
"%T88VZKCWN33CVD0TFBRPJ13NJHXF19TFRWP8NFJ1JGE5RB5ETC0G.sha256"=>
#<Pigeon::Message:0x0000559bd160cb98
@author=#<Pigeon::RemoteIdentity:0x0000559bd160c7d8 @multihash="@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519">,
@body={"c"=>"\"d\""},
@depth=1,
@kind="unit_test",
@lipmaa=0,
@prev="%8STBRYR0WYJJRSD8DRA9RT6RF6QD6AKARCDYV17X8REGACHZ41W0.sha256",
@signature="60R873HBC5GHGVFWBC3RZBHXTQW0240GNF20JYBR71V4B1Z57HHXJ6N39Y4QZZPD34PQGZEQVMSAXTC71PATXAMWBVDCF2S9S45KM1G.sig.ed25519">,
"%JD4BETS4YH4ZA7CMP4ZHG0R62T5B0EAFDYHNM5S12M04SAF9AJ4G.sha256"=>
#<Pigeon::Message:0x0000559bd16165a8
@author=#<Pigeon::RemoteIdentity:0x0000559bd1616300 @multihash="@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519">,
@body={"e"=>"\"f\""},
@depth=2,
@kind="unit_test",
@lipmaa=1,
@prev="%T88VZKCWN33CVD0TFBRPJ13NJHXF19TFRWP8NFJ1JGE5RB5ETC0G.sha256",
@signature="7DH9ZXMVT6RJEX31BPNSJ3XRA3AMPV97A1CPWW4BV0N99FW2JEPSCVQNJP7SZWA1N7BT560Y9EAK4N7AWFHDEFZV9AJ7PS81CVRF600.sig.ed25519">,
"%WTRSWSVA84DK2QMX5FDSNCVSAKQ4AFNPBW6VE93X4SJ1R60WDYMG.sha256"=>
#<Pigeon::Message:0x0000559bd1638978
@author=#<Pigeon::RemoteIdentity:0x0000559bd1638590 @multihash="@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519">,
@body={"g"=>"\"h\""},
@depth=3,
@kind="unit_test",
@lipmaa=2,
@prev="%JD4BETS4YH4ZA7CMP4ZHG0R62T5B0EAFDYHNM5S12M04SAF9AJ4G.sha256",
@signature="6KZZ2816J8V28QZG5EFM1BZAB2TF1HC87YTCQRSN1Q7395K91C9E7KE7DATT99N7XGQEJ5MRCNDS7354C5KN7763C1XVARKWRDG4T1R.sig.ed25519">},
"messages.by_depth"=>
{"@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519.0"=>"%8STBRYR0WYJJRSD8DRA9RT6RF6QD6AKARCDYV17X8REGACHZ41W0.sha256",
"@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519.1"=>"%T88VZKCWN33CVD0TFBRPJ13NJHXF19TFRWP8NFJ1JGE5RB5ETC0G.sha256",
"@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519.2"=>"%JD4BETS4YH4ZA7CMP4ZHG0R62T5B0EAFDYHNM5S12M04SAF9AJ4G.sha256",
"@Y4ZFMFQPWCX0S5KD4C4NMFSRCRCT92FY410M4ACFBK3P24RH9MD0.ed25519.3"=>"%WTRSWSVA84DK2QMX5FDSNCVSAKQ4AFNPBW6VE93X4SJ1R60WDYMG.sha256"},
"peers"=>#<Set: {}>},
@thread_safe=false,
@ultra_safe=true>>>

View File

@ -91,6 +91,7 @@ TODO
- [X] Check block list before ingesting bundles.
- [X] Need a way of importing / exporting a feeds blobs. (see "Bundle Brainstorming" below)
- [X] Need a way of adding peers messages / gossip to bundles. (see "Bundle Brainstorming" below)
- [ ] Rename `who_am_i` as `get_who_am_i` to follow VERB + NOUN convention.
- [ ] Update README.md / tutorial.rb (user manual for `Pigeon::Database`).
- [ ] Update spec document CLI usage examples to reflect API changes in 2020.
- [ ] Publish to RubyGems

BIN
a.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
b.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -41,13 +41,13 @@ module Pigeon
raise ConfigAlreadyExists
end
$db = Pigeon::Database.new
puts db.local_identity.multihash
puts db.who_am_i.multihash
end
desc "show", "Prints your identiy string to STDOUT"
def show
puts db.local_identity.multihash
puts db.who_am_i.multihash
end
end
@ -56,7 +56,7 @@ module Pigeon
def set(data = "")
blob = (data != "") ? data : STDIN.read
puts db.put_blob(blob)
puts db.add_blob(blob)
end
desc "get", "Read arbitrary data from the database"
@ -103,9 +103,9 @@ module Pigeon
def append(key, raw_value = "")
v = (raw_value != "") ? raw_value : STDIN.read
if db.current_draft
if db.get_draft
db.update_draft(key, v)
puts db.current_draft.render_as_draft
puts db.get_draft.render_as_draft
else
bail("You must create a draft first")
end
@ -114,13 +114,13 @@ module Pigeon
desc "show", "Print current message to STDOUT."
def show
puts db.current_draft.render_as_draft
puts db.get_draft.render_as_draft
end
desc "sign", "Commit current DRAFT to local feed."
def sign
puts db.publish_draft(db.current_draft).render
puts db.publish_draft(db.get_draft).render
end
end
@ -128,13 +128,13 @@ module Pigeon
desc "create", "Create a pigeon bundle file"
def create(file_path = Pigeon::DEFAULT_BUNDLE_PATH)
db.create_bundle(file_path)
db.save_bundle(file_path)
end
desc "ingest", "Ingest a pigeon bundle file"
def ingest(file_path = Pigeon::DEFAULT_BUNDLE_PATH)
db.ingest_bundle(file_path)
db.publish_bundle(file_path)
end
end
@ -148,13 +148,13 @@ module Pigeon
desc "find-all", "Find all message IDs of a particular identity."
def find_all(author = nil)
puts db.find_all_messages(author).join(Pigeon::CR) + Pigeon::CR
puts db.all_messages(author).join(Pigeon::CR) + Pigeon::CR
end
desc "last", "Grab your last message. INTERNAL USE ONLY"
def last
me = db.local_identity
me = db.who_am_i
mcount = db.get_message_count_for(me.multihash)
multihash = db.get_message_by_depth(me.multihash, mcount - 1)
puts multihash
@ -165,7 +165,7 @@ module Pigeon
desc "status", "Show various information about the `.pgn` directory"
def status
me = db.local_identity.multihash
me = db.who_am_i.multihash
mine = db.get_message_count_for(me)
puts "
-`. Pigeon Protocol Ruby Client
@ -173,7 +173,7 @@ module Pigeon
_) ( Peers: #{db.all_peers.count}
/ ) Blocked: #{db.all_blocks.count}
/_,' / Msgs Published: #{mine}
\\ / Msgs Total: #{db.find_all_messages.count}
\\ / Msgs Total: #{db.all_messages.count}
===m\" \"m===
Your local identity hash:
#{me}

BIN
c.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@ -154,7 +154,7 @@ module Pigeon
end
def self.publish_draft(db, draft)
author = db.local_identity
author = db.who_am_i
mhash = author.multihash
template = MessageSerializer.new(draft)
depth = db.get_message_count_for(mhash)
@ -173,7 +173,7 @@ module Pigeon
end
def self.update_draft(db, key, value)
draft = db.current_draft
draft = db.get_draft
draft[key] = value
db.save_draft(draft)
return draft.body[key]

View File

@ -1,6 +1,6 @@
module Pigeon
class Database
attr_reader :local_identity
attr_reader :who_am_i
def initialize(path: PIGEON_DB_PATH)
@store = Pigeon::Storage.new(path: path)
@ -16,10 +16,10 @@ module Pigeon
def all_peers(); store.all_peers(); end
# === MESSAGES
def find_all_messages(mhash = nil); store.find_all_messages(mhash); end
def all_messages(mhash = nil); store.all_messages(mhash); end
def message_saved?(multihash); store.message_saved?(multihash); end
def save_message(msg_obj)
def _save_message(msg_obj)
store.insert_message(Helpers.verify_message(self, msg_obj))
end
@ -33,19 +33,19 @@ module Pigeon
store.get_message_by_depth(multihash, depth)
end
def create_message(kind, params)
def add_message(kind, params)
publish_draft(new_draft(kind: kind, body: params))
end
# Store a message that someone (not the LocalIdentity)
# has authored.
def ingest_message(author:,
body:,
depth:,
kind:,
lipmaa:,
prev:,
signature:)
def _ingest_message(author:,
body:,
depth:,
kind:,
lipmaa:,
prev:,
signature:)
msg = Message.new(author: RemoteIdentity.new(author),
kind: kind,
body: body,
@ -53,11 +53,11 @@ module Pigeon
lipmaa: lipmaa,
signature: signature,
depth: depth)
save_message(msg)
_save_message(msg)
end
# === DRAFTS
def reset_current_draft; set_config(CURRENT_DRAFT, nil); end
def reset_draft; add_config(CURRENT_DRAFT, nil); end
def new_draft(kind:, body: {})
old = get_config(CURRENT_DRAFT)
@ -68,11 +68,11 @@ module Pigeon
end
def save_draft(draft)
set_config(CURRENT_DRAFT, draft)
add_config(CURRENT_DRAFT, draft)
draft
end
def current_draft
def get_draft
draft = store.get_config(CURRENT_DRAFT)
if draft
return draft
@ -84,20 +84,20 @@ module Pigeon
def update_draft(k, v); Helpers.update_draft(self, k, v); end
def reset_draft
set_config(CURRENT_DRAFT, nil)
add_config(CURRENT_DRAFT, nil)
end
# Author a new message.
def publish_draft(draft = self.current_draft)
def publish_draft(draft = self.get_draft)
Helpers.publish_draft(self, draft)
end
# === BUNDLES
def create_bundle(file_path = DEFAULT_BUNDLE_PATH)
def save_bundle(file_path = DEFAULT_BUNDLE_PATH)
# Fetch messages for all peers
peers = all_peers + [local_identity.multihash]
peers = all_peers + [who_am_i.multihash]
messages = peers.map do |peer|
find_all_messages(peer)
all_messages(peer)
.map { |multihash| read_message(multihash) }
.sort_by(&:depth)
end.flatten
@ -114,10 +114,11 @@ module Pigeon
content = messages
.map { |message| message.render }
.join(BUNDLE_MESSAGE_SEPARATOR)
File.join(file_path, "gossip.pgn")
File.write(File.join(file_path, "gossip.pgn"), content + CR)
end
def ingest_bundle(file_path = DEFAULT_BUNDLE_PATH)
def publish_bundle(file_path = DEFAULT_BUNDLE_PATH)
bundle = File.read(File.join(file_path, "gossip.pgn"))
tokens = Pigeon::Lexer.tokenize(bundle)
Pigeon::Parser.parse(self, tokens)
@ -125,11 +126,11 @@ module Pigeon
# === BLOBS
def get_blob(b); store.get_blob(b); end
def put_blob(b); store.put_blob(b); end
def add_blob(b); store.add_blob(b); end
# === DB Management
def get_config(k); store.get_config(k); end
def set_config(k, v); store.set_config(k, v); end
def add_config(k, v); store.add_config(k, v); end
def reset_database; store.reset; init_ident; end
private
@ -139,11 +140,11 @@ module Pigeon
def init_ident
secret = get_config(SEED_CONFIG_KEY)
if secret
@local_identity = LocalIdentity.new(secret)
@who_am_i = LocalIdentity.new(secret)
else
new_seed = SecureRandom.random_bytes(Ed25519::KEY_SIZE)
set_config(SEED_CONFIG_KEY, new_seed)
@local_identity = LocalIdentity.new(new_seed)
add_config(SEED_CONFIG_KEY, new_seed)
@who_am_i = LocalIdentity.new(new_seed)
end
end
end

View File

@ -41,7 +41,7 @@ module Pigeon
def finish_this_message!
@scratchpad.freeze
unless @db.peer_blocked?(@scratchpad.fetch(:author))
@results.push(@db.ingest_message(**@scratchpad))
@results.push(@db._ingest_message(**@scratchpad))
end
reset_scratchpad
end

View File

@ -43,7 +43,7 @@ module Pigeon
read { store[CONF_NS][key] }
end
def set_config(key, value)
def add_config(key, value)
write do
a = store.fetch(CONF_NS)
raise "FIX SAVED DRAFTS" if value.instance_variable_get(:@db)
@ -51,7 +51,7 @@ module Pigeon
end
end
def put_blob(data)
def add_blob(data)
size = data.bytesize
if (size > BLOB_BYTE_LIMIT)
raise "Blob size limit is #{BLOB_BYTE_LIMIT} bytes. Got #{size}"
@ -79,7 +79,7 @@ module Pigeon
read { store[COUNT_INDEX_NS][mhash] || 0 }
end
def find_all_messages(author)
def all_messages(author)
if author
all = []
depth = -1

BIN
pigeon.db

Binary file not shown.

View File

@ -13,35 +13,41 @@ RSpec.describe Pigeon::Message do
end
def create_fake_messages
blobs = [db.create_message(db.put_blob("one"), { "a" => "b" }),
db.create_message("a", { db.put_blob("two") => "b" }),
db.create_message("a", { "b" => db.put_blob("three") })]
blobs = [db.add_message(db.add_blob("one"), { "a" => "b" }),
db.add_message("a", { db.add_blob("two") => "b" }),
db.add_message("a", { "b" => db.add_blob("three") })]
normal = (1..10)
.to_a
.map do |n| { "foo" => ["bar", "123", SecureRandom.uuid].sample } end
.map do |d| db.create_message(SecureRandom.uuid, d) end
.map do |d| db.add_message(SecureRandom.uuid, d) end
blobs + normal
end
it "creates a bundle" do
expected_bundle = create_fake_messages.map(&:render).join("\n\n") + "\n"
db.create_bundle
db.save_bundle
actual_bundle = File.read(File.join(Pigeon::DEFAULT_BUNDLE_PATH, "gossip.pgn"))
expect(expected_bundle).to eq(actual_bundle)
end
it "does not crash when ingesting old messages" do
create_fake_messages
db.create_bundle
db.ingest_bundle
db.save_bundle
db.publish_bundle
end
it "does not ingest messages from blocked peers" do
db.reset_database
antagonist = "@PPJQ3Q36W258VQ1NKYY2G7VW24J8NMAACHXCD83GCQ3K8F4C9X2G.ed25519"
db.block_peer(antagonist)
db.ingest_bundle("./spec/fixtures/x")
expect(db.find_all_messages.count).to eq(0)
db.publish_bundle("./spec/fixtures/x")
expect(db.all_messages.count).to eq(0)
end
it "ingests a bundle's blobs" do
db.reset_database
db.publish_bundle("./spec/fixtures/has_blobs")
expect(db.all_messages.count).to eq(0)
end
end

View File

@ -12,8 +12,8 @@ RSpec.describe Pigeon::Draft do
db.new_draft(kind: "unit_test")
logo = File.read("./logo.png")
db.update_draft("a", "bar")
db.update_draft("b", db.put_blob(logo))
db.current_draft
db.update_draft("b", db.add_blob(logo))
db.get_draft
end
MSG = [
@ -28,7 +28,7 @@ RSpec.describe Pigeon::Draft do
].join("\n")
it "renders a message" do
pk = db.local_identity.multihash
pk = db.who_am_i.multihash
actual = message.render_as_draft
expected = MSG.gsub("___", pk)
expect(actual).to start_with(expected)
@ -37,7 +37,7 @@ RSpec.describe Pigeon::Draft do
it "creates a new message" do
db.reset_draft
db.new_draft(kind: "unit_test")
hash = db.put_blob(File.read("./logo.png"))
hash = db.add_blob(File.read("./logo.png"))
expectations = {
kind: "unit_test",
body: {
@ -52,7 +52,7 @@ RSpec.describe Pigeon::Draft do
expect(message.kind).to eq("unit_test")
expect(message.body).to eq(expectations.fetch(:body))
expectations.map do |k, v|
left = db.current_draft.send(k)
left = db.get_draft.send(k)
expect(left).to eq(v)
end
end

View File

@ -4,10 +4,10 @@ RSpec.describe Pigeon::Message do
def reset_draft(params)
db.reset_draft
db.new_draft(kind: "unit_test", body: params)
db.current_draft
db.get_draft
end
def create_message(params)
def add_message(params)
draft = reset_draft(params)
db.publish_draft(draft)
end
@ -19,11 +19,11 @@ RSpec.describe Pigeon::Message do
end
let(:draft) do
hash = db.put_blob(File.read("./logo.png"))
hash = db.add_blob(File.read("./logo.png"))
reset_draft({ "a" => "bar", "b" => hash })
end
let(:templated_message) { create_message({ "a" => "b" }) }
let(:templated_message) { add_message({ "a" => "b" }) }
let (:template) do
Pigeon::MessageSerializer.new(templated_message)
@ -31,12 +31,12 @@ RSpec.describe Pigeon::Message do
it "discards a draft after signing" do
db.publish_draft(draft)
expect { db.current_draft }.to raise_error("THERE IS NO DRAFT. CREATE ONE FIRST.")
expect { db.get_draft }.to raise_error("THERE IS NO DRAFT. CREATE ONE FIRST.")
end
it "creates a single message" do
message = db.publish_draft(draft)
expect(message.author.multihash).to eq(db.local_identity.multihash)
expect(message.author.multihash).to eq(db.who_am_i.multihash)
expect(message.body).to eq(draft.body)
expect(message.depth).to eq(0)
expect(message.kind).to eq("unit_test")
@ -79,10 +79,10 @@ RSpec.describe Pigeon::Message do
end
it "verifies accuracy of hash chain" do
m1 = create_message({ "a" => "b" })
m2 = create_message({ "c" => "d" })
m3 = create_message({ "e" => "f" })
m4 = create_message({ "g" => "h" })
m1 = add_message({ "a" => "b" })
m2 = add_message({ "c" => "d" })
m3 = add_message({ "e" => "f" })
m4 = add_message({ "g" => "h" })
expect(m1.prev).to eq(Pigeon::NOTHING)
expect(m2.prev).to be
@ -100,7 +100,7 @@ RSpec.describe Pigeon::Message do
body[SecureRandom.hex(6)] = SecureRandom.hex(6)
end
expect do
create_message(body)
add_message(body)
end.to raise_error(Pigeon::Helpers::MessageSizeError, error)
end
@ -112,7 +112,7 @@ RSpec.describe Pigeon::Message do
plaintext = template.render_without_signature
# Make fake pairs of data for cross-checking
key1 = db.local_identity.instance_variable_get(:@signing_key)
key1 = db.who_am_i.instance_variable_get(:@signing_key)
key2 = Ed25519::SigningKey.new(secret)
sig1 = key1.sign(plaintext)

View File

@ -30,7 +30,7 @@ RSpec.describe Pigeon::Lexer do
end
it "ingests and reconstructs a bundle" do
messages = db.ingest_bundle("./spec/fixtures/normal")
messages = db.publish_bundle("./spec/fixtures/normal")
expect(messages.length).to eq(10)
expect(messages.map(&:class).uniq).to eq([Pigeon::Message])
re_bundled = messages.map(&:render).join("\n\n") + "\n"

View File

@ -12,22 +12,22 @@ RSpec.describe Pigeon::Storage do
end
it "sets a config" do
db.set_config("FOO", "BAR")
db.add_config("FOO", "BAR")
value = db.get_config("FOO")
expect(value).to eq("BAR")
db.set_config("FOO", nil)
db.add_config("FOO", nil)
value = db.get_config("FOO")
expect(value).to eq(nil)
end
it "manages configs" do
db.set_config("FOO", "BAR")
db.add_config("FOO", "BAR")
value = db.get_config("FOO")
expect(value).to eq("BAR")
end
it "manages blobs" do
logo_hash = db.put_blob(LOGO_BLOB)
logo_hash = db.add_blob(LOGO_BLOB)
expect(db.get_blob(logo_hash)).to eq(LOGO_BLOB)
end
@ -48,27 +48,27 @@ RSpec.describe Pigeon::Storage do
end
it "finds all authored by a particular feed" do
ingested_messages = db.ingest_bundle("./spec/fixtures/normal")
ingested_messages = db.publish_bundle("./spec/fixtures/normal")
author = ingested_messages.first.author.multihash
actual_messages = db.find_all_messages(author)
search_results = db.find_all_messages(author)
actual_messages = db.all_messages(author)
search_results = db.all_messages(author)
end
it "finds all messages" do
msgs = [
db.create_message("strings", {
db.add_message("strings", {
"example_1.1" => "This is a string.",
"example=_." => "A second string.",
}),
db.create_message("d", {
"e" => db.put_blob(File.read("./logo.png")),
db.add_message("d", {
"e" => db.add_blob(File.read("./logo.png")),
}),
db.create_message("g", {
"me_myself_and_i" => db.local_identity.multihash,
db.add_message("g", {
"me_myself_and_i" => db.who_am_i.multihash,
}),
]
me = db.local_identity.multihash
results = db.find_all_messages(me)
me = db.who_am_i.multihash
results = db.all_messages(me)
expect(results.length).to eq(3)
expect(msgs[0].multihash).to eq(results[0])
expect(msgs[1].multihash).to eq(results[1])

View File

@ -1,31 +1,36 @@
require "pigeon"
# add_blob
# add_config
# add_message
# add_peer
# all_blocks
# all_messages
# all_peers
# block_peer
# get_blob
# get_config
# get_draft
# get_message_by_depth
# get_message_count_for
# message_saved?
# peer_blocked?
# who_am_i
# new_draft
# publish_bundle
# publish_draft
# read_message
# remove_peer
# reset_database
# reset_draft
# save_bundle
# save_draft
# update_draft
require_relative "lib/pigeon"
require "pry"
db = Pigeon::Database.new(path: "my.db")
db.reset_draft
db.current_draft
db.reset_draft
db.publish_draft
db.save_draft
db.save_message
db.reset_current_draft
db.message_saved?
db.read_message
db.create_message
db.find_all_messages
db.get_message_by_depth
db.get_message_count_for
db.local_identity
db.remove_peer
db.add_peer
db.block_peer
db.all_peers
db.all_blocks
db.get_blob
db.put_blob
db.create_bundle
db.get_config
db.ingest_bundle
db.set_config
db.reset_database
files = %w(a.gif b.gif c.gif)
body = { "what" => "A simple bundle with a few blobs" }
db = Pigeon::Database.new(path: "new.db")
db.add_message("description", body)
files.map { |file| db.add_blob(file) }
binding.pry
db.save_bundle("./spec/fixtures/has_blobs")