antd 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +228 -10
- data/lib/antd/client.rb +282 -27
- data/lib/antd/errors.rb +353 -0
- data/lib/antd/grpc_client.rb +245 -2
- data/lib/antd/grpc_manifests.rb +128 -0
- data/lib/antd/manifests.rb +300 -0
- data/lib/antd/models.rb +221 -0
- data/lib/antd/v1/chunks_pb.rb +1 -1
- data/lib/antd/v1/common_pb.rb +2 -1
- data/lib/antd/v1/data_pb.rb +1 -1
- data/lib/antd/v1/health_pb.rb +1 -1
- data/lib/antd/v1/manifests_pb.rb +22 -0
- data/lib/antd/v1/manifests_services_pb.rb +39 -0
- data/lib/antd/v1/pointers_pb.rb +38 -0
- data/lib/antd/v1/pointers_services_pb.rb +59 -0
- data/lib/antd/v1/upload_pb.rb +2 -1
- data/lib/antd/v1/upload_services_pb.rb +4 -2
- data/lib/antd/version.rb +1 -1
- data/lib/antd.rb +2 -0
- metadata +8 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "manifests"
|
|
4
|
+
require_relative "grpc_client"
|
|
5
|
+
require_relative "v1/manifests_services_pb"
|
|
6
|
+
|
|
7
|
+
# The manifest methods on +Antd::GrpcClient+, over ManifestService. The
|
|
8
|
+
# models and the entry check are in manifests.rb, shared with REST.
|
|
9
|
+
module Antd
|
|
10
|
+
# Channel arguments for the manifest stub's own channel. grpc-ruby has no
|
|
11
|
+
# per-call message limit, so ManifestService gets a channel of its own
|
|
12
|
+
# with both limits raised to +MANIFEST_GRPC_MAX_MESSAGE+; the other
|
|
13
|
+
# services keep gRPC's defaults.
|
|
14
|
+
MANIFEST_GRPC_CHANNEL_ARGS = {
|
|
15
|
+
"grpc.max_receive_message_length" => MANIFEST_GRPC_MAX_MESSAGE,
|
|
16
|
+
"grpc.max_send_message_length" => MANIFEST_GRPC_MAX_MESSAGE
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
class GrpcClient
|
|
20
|
+
# --- Manifests ---
|
|
21
|
+
#
|
|
22
|
+
# The same methods and models as the REST +Client+ (see there). The
|
|
23
|
+
# `.ant` bytes travel raw over gRPC; REST's base64 never shows. Unset
|
|
24
|
+
# optionals (proto3 +optional+ without a value, or an empty string) read
|
|
25
|
+
# as nil, as REST's omitted fields do.
|
|
26
|
+
|
|
27
|
+
# Mirrors +Client#build_manifest+. Nothing is paid for.
|
|
28
|
+
#
|
|
29
|
+
# Needs antd 0.16.0 or later.
|
|
30
|
+
#
|
|
31
|
+
# @return [BuiltManifest]
|
|
32
|
+
# @raise [BadRequestError] an entry with both or neither of +data_map+ and
|
|
33
|
+
# +address+, or a bad +size+, before anything is sent
|
|
34
|
+
# @raise [BadRequestError] an entry the daemon rejects (INVALID_ARGUMENT);
|
|
35
|
+
# the message names +entries[i]+
|
|
36
|
+
def build_manifest(entries, name: nil, torrent_info_hashes: nil, embed_roots: true)
|
|
37
|
+
inputs = Antd.manifest_entry_inputs(entries)
|
|
38
|
+
req = Antd::V1::BuildManifestRequest.new(
|
|
39
|
+
torrent_info_hashes: Array(torrent_info_hashes),
|
|
40
|
+
# Always sent, so "unset means true" never depends on the daemon.
|
|
41
|
+
embed_roots: embed_roots ? true : false,
|
|
42
|
+
entries: inputs.map { |e| manifest_entry_input_pb(e) }
|
|
43
|
+
)
|
|
44
|
+
req.name = name unless name.nil? || name.empty?
|
|
45
|
+
|
|
46
|
+
resp = grpc_call { manifest_stub.build(req) }
|
|
47
|
+
BuiltManifest.new(
|
|
48
|
+
manifest: resp.manifest,
|
|
49
|
+
link: manifest_pb_string(resp.link),
|
|
50
|
+
entries: resp.entries.map { |e| manifest_entry_info_from_pb(e) }
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Mirrors +Client#decode_manifest+. The bytes are sent raw.
|
|
55
|
+
#
|
|
56
|
+
# Needs antd 0.16.0 or later.
|
|
57
|
+
#
|
|
58
|
+
# @return [DecodedManifest]
|
|
59
|
+
def decode_manifest(manifest, resolve: false)
|
|
60
|
+
raise BadRequestError, MANIFEST_DECODE_NEEDS_INPUT if manifest.nil? || manifest.empty?
|
|
61
|
+
|
|
62
|
+
decode_manifest_pb(Antd::V1::DecodeManifestRequest.new(manifest: manifest.b, resolve: resolve ? true : false))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Mirrors +Client#decode_manifest_link+.
|
|
66
|
+
#
|
|
67
|
+
# Needs antd 0.16.0 or later.
|
|
68
|
+
#
|
|
69
|
+
# @return [DecodedManifest]
|
|
70
|
+
def decode_manifest_link(link, resolve: false)
|
|
71
|
+
raise BadRequestError, MANIFEST_DECODE_NEEDS_INPUT if link.nil? || link.empty?
|
|
72
|
+
|
|
73
|
+
decode_manifest_pb(Antd::V1::DecodeManifestRequest.new(link: link, resolve: resolve ? true : false))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# ManifestService on its own channel, with +MANIFEST_GRPC_CHANNEL_ARGS+.
|
|
79
|
+
# Made on first use, so +initialize+ stays as it is.
|
|
80
|
+
def manifest_stub
|
|
81
|
+
@manifest_stub ||= Antd::V1::ManifestService::Stub.new(
|
|
82
|
+
@target, :this_channel_is_insecure, channel_args: MANIFEST_GRPC_CHANNEL_ARGS
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def decode_manifest_pb(req)
|
|
87
|
+
resp = grpc_call { manifest_stub.decode(req) }
|
|
88
|
+
DecodedManifest.new(
|
|
89
|
+
source: resp.source,
|
|
90
|
+
name: resp.has_name? ? manifest_pb_string(resp.name) : nil,
|
|
91
|
+
torrent_info_hash_v1: manifest_pb_string(resp.torrent_info_hash_v1),
|
|
92
|
+
torrent_info_hash_v2: manifest_pb_string(resp.torrent_info_hash_v2),
|
|
93
|
+
entries: resp.entries.map { |e| manifest_entry_info_from_pb(e) }
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def manifest_entry_input_pb(e)
|
|
98
|
+
attrs = {}
|
|
99
|
+
attrs[:path] = e.path unless e.path.nil? || e.path.empty?
|
|
100
|
+
attrs[:size] = e.size unless e.size.nil?
|
|
101
|
+
if e.data_map.nil? || e.data_map.empty?
|
|
102
|
+
attrs[:address] = e.address
|
|
103
|
+
else
|
|
104
|
+
attrs[:data_map] = e.data_map
|
|
105
|
+
end
|
|
106
|
+
Antd::V1::ManifestEntryInput.new(**attrs)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# proto3 +optional+ sizes are checked for presence: unset is nil, not 0.
|
|
110
|
+
def manifest_entry_info_from_pb(e)
|
|
111
|
+
ManifestEntryInfo.new(
|
|
112
|
+
effective_name: e.effective_name,
|
|
113
|
+
path: e.has_path? ? manifest_pb_string(e.path) : nil,
|
|
114
|
+
size: e.has_size? ? e.size : nil,
|
|
115
|
+
kind: e.kind,
|
|
116
|
+
content_address: e.content_address,
|
|
117
|
+
data_map: manifest_pb_string(e.data_map),
|
|
118
|
+
exact_size: e.has_exact_size? ? e.exact_size : nil,
|
|
119
|
+
error: manifest_pb_string(e.error)
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# A proto string, or nil when empty.
|
|
124
|
+
def manifest_pb_string(value)
|
|
125
|
+
value.nil? || value.empty? ? nil : value
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require_relative "models"
|
|
5
|
+
require_relative "errors"
|
|
6
|
+
require_relative "client"
|
|
7
|
+
|
|
8
|
+
# Manifests (ADR-0006): `.ant` files and `ant://` links that describe a set
|
|
9
|
+
# of files, each with an optional path and size and either an embedded
|
|
10
|
+
# DataMap or a public DataMap address. The daemon builds and decodes them
|
|
11
|
+
# through ant-core, so callers never re-implement the format. Nothing is
|
|
12
|
+
# uploaded or paid for.
|
|
13
|
+
#
|
|
14
|
+
# This file holds the models and the REST methods on +Antd::Client+;
|
|
15
|
+
# grpc_manifests.rb adds the same methods to +Antd::GrpcClient+.
|
|
16
|
+
module Antd
|
|
17
|
+
# +ManifestEntryInfo#kind+ values.
|
|
18
|
+
module ManifestEntryKind
|
|
19
|
+
# The manifest carries the entry's DataMap: it is the key to the file.
|
|
20
|
+
EMBEDDED = "embedded"
|
|
21
|
+
# The manifest carries a public file's DataMap address.
|
|
22
|
+
PUBLIC = "public"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# +DecodedManifest#source+ values.
|
|
26
|
+
module ManifestSource
|
|
27
|
+
# `.ant` bytes or an `ant://manifest/` link.
|
|
28
|
+
MANIFEST = "manifest"
|
|
29
|
+
# A file link or bare address, reported as one public entry with no path.
|
|
30
|
+
FILE_LINK = "file_link"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# One file for +build_manifest+. Set exactly one of +data_map+ or
|
|
34
|
+
# +address+; a Hash with the same keys works too.
|
|
35
|
+
#
|
|
36
|
+
# - +path+: the relative path the entry extracts under, subject to the
|
|
37
|
+
# portable path rules. nil: it extracts under its content address.
|
|
38
|
+
# - +size+: the plaintext size to record, as given (the creator's claim).
|
|
39
|
+
# nil: none is recorded; readers take the exact size from an embedded
|
|
40
|
+
# root DataMap.
|
|
41
|
+
# - +data_map+: a hex DataMap, as the upload methods return it. The entry
|
|
42
|
+
# embeds it.
|
|
43
|
+
# - +address+: a public file's DataMap address (hex, 32 bytes).
|
|
44
|
+
ManifestEntryInput = Struct.new(:path, :size, :data_map, :address, keyword_init: true) do
|
|
45
|
+
def initialize(path: nil, size: nil, data_map: nil, address: nil)
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# What a manifest says about one entry. Unset fields are nil on both
|
|
51
|
+
# transports (REST omits them; gRPC sends no value or an empty string),
|
|
52
|
+
# so a +size+ or +exact_size+ the manifest lacks is nil, never 0.
|
|
53
|
+
#
|
|
54
|
+
# - +effective_name+: +path+, or the content address in hex when there is
|
|
55
|
+
# none.
|
|
56
|
+
# - +path+: the entry's path, or nil.
|
|
57
|
+
# - +size+: the size recorded in the manifest (the creator's claim), or nil.
|
|
58
|
+
# - +kind+: a +ManifestEntryKind+ string.
|
|
59
|
+
# - +content_address+: hex. The file's public DataMap address, whether or
|
|
60
|
+
# not it was ever made public; the same for private and public uploads
|
|
61
|
+
# of one file.
|
|
62
|
+
# - +data_map+: the embedded DataMap in the hex form the download methods
|
|
63
|
+
# accept. Decode only; nil for public entries.
|
|
64
|
+
# - +exact_size+: the exact plaintext size, from an embedded root DataMap
|
|
65
|
+
# or after a resolving decode; nil when unknown.
|
|
66
|
+
# - +error+: why a resolving decode failed for this entry, or nil. A
|
|
67
|
+
# failed entry does not fail the call.
|
|
68
|
+
ManifestEntryInfo = Struct.new(
|
|
69
|
+
:effective_name, :path, :size, :kind, :content_address, :data_map, :exact_size, :error,
|
|
70
|
+
keyword_init: true
|
|
71
|
+
) do
|
|
72
|
+
def initialize(effective_name: "", path: nil, size: nil, kind: "", content_address: "",
|
|
73
|
+
data_map: nil, exact_size: nil, error: nil)
|
|
74
|
+
super
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Result of +build_manifest+.
|
|
79
|
+
#
|
|
80
|
+
# - +manifest+: the `.ant` bytes (a binary String).
|
|
81
|
+
# - +link+: the `ant://manifest/` link, or nil when it would not fit the
|
|
82
|
+
# recommended 1,500 bytes.
|
|
83
|
+
# - +entries+: +ManifestEntryInfo+s in encoded order (sorted by effective
|
|
84
|
+
# name).
|
|
85
|
+
BuiltManifest = Struct.new(:manifest, :link, :entries, keyword_init: true)
|
|
86
|
+
|
|
87
|
+
# Result of +decode_manifest+ and +decode_manifest_link+.
|
|
88
|
+
#
|
|
89
|
+
# - +source+: a +ManifestSource+ string.
|
|
90
|
+
# - +name+: the suggested root directory name, or nil.
|
|
91
|
+
# - +torrent_info_hash_v1+, +torrent_info_hash_v2+: hex, or nil when absent.
|
|
92
|
+
# - +entries+: +ManifestEntryInfo+s.
|
|
93
|
+
DecodedManifest = Struct.new(
|
|
94
|
+
:source, :name, :torrent_info_hash_v1, :torrent_info_hash_v2, :entries,
|
|
95
|
+
keyword_init: true
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# The gRPC message ceiling for manifest calls, both ways: a manifest can be
|
|
99
|
+
# 64 MiB, and a decode response carries its DataMaps in hex. gRPC's default
|
|
100
|
+
# receive limit is 4 MiB.
|
|
101
|
+
MANIFEST_GRPC_MAX_MESSAGE = 256 * 1024 * 1024
|
|
102
|
+
|
|
103
|
+
# The daemon's cap on the entry errors one 400 names.
|
|
104
|
+
MANIFEST_MAX_REPORTED_ENTRY_ERRORS = 20
|
|
105
|
+
private_constant :MANIFEST_MAX_REPORTED_ENTRY_ERRORS
|
|
106
|
+
|
|
107
|
+
# The daemon's message for empty decode input (empty bytes or link).
|
|
108
|
+
MANIFEST_DECODE_NEEDS_INPUT = "needs a manifest (base64 .ant bytes) or a link"
|
|
109
|
+
|
|
110
|
+
# Normalises +build_manifest+ entries to +ManifestEntryInput+s before
|
|
111
|
+
# anything is sent. A refusal is the +BadRequestError+ (400) the daemon
|
|
112
|
+
# would send, with its wording: every bad entry (both or neither of
|
|
113
|
+
# +data_map+ and +address+, nil and "" counting as unset; or a +size+ that
|
|
114
|
+
# is not a non-negative Integer) is collected and named as
|
|
115
|
+
# "N invalid entries: entries[i]: ...; entries[j]: ..." (at most 20, then
|
|
116
|
+
# "; and K more").
|
|
117
|
+
#
|
|
118
|
+
# @api private
|
|
119
|
+
# @param entries [Array<ManifestEntryInput, Hash>]
|
|
120
|
+
# @return [Array<ManifestEntryInput>]
|
|
121
|
+
# @raise [BadRequestError]
|
|
122
|
+
def self.manifest_entry_inputs(entries)
|
|
123
|
+
raise BadRequestError, "entries must be an Array" unless entries.is_a?(Array)
|
|
124
|
+
|
|
125
|
+
errors = []
|
|
126
|
+
inputs = entries.each_with_index.map do |e, i|
|
|
127
|
+
e = ManifestEntryInput.new(**e.transform_keys(&:to_sym)) if e.is_a?(Hash)
|
|
128
|
+
unless e.is_a?(ManifestEntryInput)
|
|
129
|
+
errors << "entries[#{i}]: must be a ManifestEntryInput or Hash"
|
|
130
|
+
next
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
has_data_map = !(e.data_map.nil? || e.data_map.empty?)
|
|
134
|
+
has_address = !(e.address.nil? || e.address.empty?)
|
|
135
|
+
if has_data_map && has_address
|
|
136
|
+
errors << "entries[#{i}]: give data_map or address, not both"
|
|
137
|
+
elsif !has_data_map && !has_address
|
|
138
|
+
errors << "entries[#{i}]: needs a data_map or an address"
|
|
139
|
+
end
|
|
140
|
+
unless e.size.nil? || (e.size.is_a?(Integer) && e.size >= 0)
|
|
141
|
+
errors << "entries[#{i}]: size must be a non-negative Integer or nil"
|
|
142
|
+
end
|
|
143
|
+
e
|
|
144
|
+
rescue ArgumentError
|
|
145
|
+
# A Hash with a key ManifestEntryInput does not have.
|
|
146
|
+
errors << "entries[#{i}]: unknown field (expected path, size, data_map, address)"
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
raise BadRequestError, Antd.manifest_entry_errors_message(errors) unless errors.empty?
|
|
150
|
+
|
|
151
|
+
inputs
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The daemon's +entry_errors_message+.
|
|
155
|
+
#
|
|
156
|
+
# @api private
|
|
157
|
+
def self.manifest_entry_errors_message(errors)
|
|
158
|
+
shown = errors.first(MANIFEST_MAX_REPORTED_ENTRY_ERRORS).join("; ")
|
|
159
|
+
message = "#{errors.length} invalid entries: #{shown}"
|
|
160
|
+
message += "; and #{errors.length - MANIFEST_MAX_REPORTED_ENTRY_ERRORS} more" if errors.length > MANIFEST_MAX_REPORTED_ENTRY_ERRORS
|
|
161
|
+
message
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class Client
|
|
165
|
+
# --- Manifests ---
|
|
166
|
+
|
|
167
|
+
# Build a `.ant` manifest from DataMaps and public addresses already in
|
|
168
|
+
# hand (POST /v1/manifests/build). Nothing is uploaded or paid for.
|
|
169
|
+
#
|
|
170
|
+
# By default the daemon resolves each shrunk DataMap's root (fetching its
|
|
171
|
+
# wrapper records) and embeds it when it is at most 64 KiB, so recipients
|
|
172
|
+
# skip that fetch (ADR-0006's policy). That reads from the network, so the
|
|
173
|
+
# call can time out; it is safe to retry.
|
|
174
|
+
#
|
|
175
|
+
# Needs antd 0.16.0 or later.
|
|
176
|
+
#
|
|
177
|
+
# @param entries [Array<ManifestEntryInput, Hash>] each with exactly one
|
|
178
|
+
# of +data_map+ or +address+; checked before anything is sent
|
|
179
|
+
# @param name [String, nil] suggested root directory name (one portable
|
|
180
|
+
# path component)
|
|
181
|
+
# @param torrent_info_hashes [Array<String>, nil] BitTorrent info hashes
|
|
182
|
+
# of the same files: hex, 40 characters for v1 and/or 64 for v2
|
|
183
|
+
# @param embed_roots [Boolean] false embeds shrunk DataMaps exactly as
|
|
184
|
+
# given (the wire's +embed_roots+; default true)
|
|
185
|
+
# @return [BuiltManifest]
|
|
186
|
+
# @raise [BadRequestError] an entry with both or neither of +data_map+ and
|
|
187
|
+
# +address+, or a bad +size+, worded as the daemon words it
|
|
188
|
+
# @raise [BadRequestError] an entry the daemon rejects; the message names
|
|
189
|
+
# +entries[i]+
|
|
190
|
+
def build_manifest(entries, name: nil, torrent_info_hashes: nil, embed_roots: true)
|
|
191
|
+
inputs = Antd.manifest_entry_inputs(entries)
|
|
192
|
+
wire = inputs.map do |e|
|
|
193
|
+
m = {}
|
|
194
|
+
m[:path] = e.path unless e.path.nil? || e.path.empty?
|
|
195
|
+
m[:size] = e.size unless e.size.nil?
|
|
196
|
+
if e.data_map.nil? || e.data_map.empty?
|
|
197
|
+
m[:address] = e.address
|
|
198
|
+
else
|
|
199
|
+
m[:data_map] = e.data_map
|
|
200
|
+
end
|
|
201
|
+
m
|
|
202
|
+
end
|
|
203
|
+
body = { entries: wire, embed_roots: embed_roots ? true : false }
|
|
204
|
+
body[:name] = name unless name.nil? || name.empty?
|
|
205
|
+
hashes = Array(torrent_info_hashes)
|
|
206
|
+
body[:torrent_info_hashes] = hashes unless hashes.empty?
|
|
207
|
+
|
|
208
|
+
j = do_json(:post, "/v1/manifests/build", body)
|
|
209
|
+
BuiltManifest.new(
|
|
210
|
+
manifest: manifest_bytes_from_b64(j["manifest"]),
|
|
211
|
+
link: manifest_string(j["link"]),
|
|
212
|
+
entries: manifest_entry_infos(j["entries"])
|
|
213
|
+
)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Decode `.ant` bytes (POST /v1/manifests/decode). With +resolve+, the
|
|
217
|
+
# daemon fetches each entry's DataMap (and wrapper records) to report
|
|
218
|
+
# +exact_size+ and confirm the DataMap is on the network; content chunks
|
|
219
|
+
# are not fetched, and one entry's failure is reported in its +error+,
|
|
220
|
+
# not as a failed call. Nothing is paid for; safe to retry.
|
|
221
|
+
#
|
|
222
|
+
# Needs antd 0.16.0 or later.
|
|
223
|
+
#
|
|
224
|
+
# @param manifest [String] the `.ant` bytes
|
|
225
|
+
# @param resolve [Boolean]
|
|
226
|
+
# @return [DecodedManifest]
|
|
227
|
+
# @raise [BadRequestError] an empty +manifest+
|
|
228
|
+
# @raise [BadRequestError] bytes that are not a manifest
|
|
229
|
+
def decode_manifest(manifest, resolve: false)
|
|
230
|
+
raise BadRequestError, MANIFEST_DECODE_NEEDS_INPUT if manifest.nil? || manifest.empty?
|
|
231
|
+
|
|
232
|
+
post_manifest_decode({ manifest: b64_encode(manifest), resolve: resolve ? true : false })
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Decode an `ant://` manifest link, file link or bare 64-hex address
|
|
236
|
+
# (POST /v1/manifests/decode). See +decode_manifest+ for +resolve+.
|
|
237
|
+
#
|
|
238
|
+
# Needs antd 0.16.0 or later.
|
|
239
|
+
#
|
|
240
|
+
# @param link [String]
|
|
241
|
+
# @param resolve [Boolean]
|
|
242
|
+
# @return [DecodedManifest]
|
|
243
|
+
# @raise [BadRequestError] an empty +link+
|
|
244
|
+
# @raise [BadRequestError] a link the daemon cannot read
|
|
245
|
+
def decode_manifest_link(link, resolve: false)
|
|
246
|
+
raise BadRequestError, MANIFEST_DECODE_NEEDS_INPUT if link.nil? || link.empty?
|
|
247
|
+
|
|
248
|
+
post_manifest_decode({ link: link, resolve: resolve ? true : false })
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
def post_manifest_decode(body)
|
|
254
|
+
j = do_json(:post, "/v1/manifests/decode", body)
|
|
255
|
+
DecodedManifest.new(
|
|
256
|
+
source: j["source"] || "",
|
|
257
|
+
name: manifest_string(j["name"]),
|
|
258
|
+
torrent_info_hash_v1: manifest_string(j["torrent_info_hash_v1"]),
|
|
259
|
+
torrent_info_hash_v2: manifest_string(j["torrent_info_hash_v2"]),
|
|
260
|
+
entries: manifest_entry_infos(j["entries"])
|
|
261
|
+
)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# The build response's base64 `.ant` bytes, raw.
|
|
265
|
+
def manifest_bytes_from_b64(value)
|
|
266
|
+
b64_decode(value.to_s)
|
|
267
|
+
rescue ArgumentError => e
|
|
268
|
+
raise InternalError, "build_manifest: manifest is not valid base64: #{e.message}"
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def manifest_entry_infos(raw)
|
|
272
|
+
return [] unless raw.is_a?(Array)
|
|
273
|
+
|
|
274
|
+
raw.filter_map do |e|
|
|
275
|
+
next unless e.is_a?(Hash)
|
|
276
|
+
|
|
277
|
+
ManifestEntryInfo.new(
|
|
278
|
+
effective_name: e["effective_name"] || "",
|
|
279
|
+
path: manifest_string(e["path"]),
|
|
280
|
+
size: manifest_size(e["size"]),
|
|
281
|
+
kind: e["kind"] || "",
|
|
282
|
+
content_address: e["content_address"] || "",
|
|
283
|
+
data_map: manifest_string(e["data_map"]),
|
|
284
|
+
exact_size: manifest_size(e["exact_size"]),
|
|
285
|
+
error: manifest_string(e["error"])
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# An optional size: a JSON integer, or nil when absent (never 0).
|
|
291
|
+
def manifest_size(value)
|
|
292
|
+
value.is_a?(Integer) && value >= 0 ? value : nil
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# An optional string: nil when absent or empty.
|
|
296
|
+
def manifest_string(value)
|
|
297
|
+
value.is_a?(String) && !value.empty? ? value : nil
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
data/lib/antd/models.rb
CHANGED
|
@@ -195,4 +195,225 @@ module Antd
|
|
|
195
195
|
end
|
|
196
196
|
alias_method :is_meta, :meta?
|
|
197
197
|
end
|
|
198
|
+
|
|
199
|
+
# One +payments+ quote in full signed form, returned by +pointer_prepare+
|
|
200
|
+
# when called with +include_signed_quotes: true+. +quote+ and
|
|
201
|
+
# +commitment_sidecar+ are opaque base64 strings on both transports (the
|
|
202
|
+
# gRPC client encodes the proto's bytes the same way); only antd parses
|
|
203
|
+
# them. +commitment_sidecar+ is nil for baseline quotes.
|
|
204
|
+
SignedQuoteEntry = Struct.new(:quote_hash, :quote, :commitment_sidecar, keyword_init: true)
|
|
205
|
+
|
|
206
|
+
# --- Pointers (ADR-0016 / ADR-0018 in ant-node) ---
|
|
207
|
+
#
|
|
208
|
+
# A pointer is a small, owner-signed, mutable reference. Its address is
|
|
209
|
+
# derived from the owner's key and never changes; what it points at (a
|
|
210
|
+
# chunk, such as a public file's data map, or another pointer) can be
|
|
211
|
+
# changed by the owner, and the owner can hand it over to another pointer
|
|
212
|
+
# for good. antd signs with owner keys it loaded at startup
|
|
213
|
+
# (+--pointer-keys+), so a write names the pointer by its address (+owner+),
|
|
214
|
+
# never by a key.
|
|
215
|
+
#
|
|
216
|
+
# Every write pays, and every attempt pays again: never retry a create,
|
|
217
|
+
# update, transfer or finalize automatically.
|
|
218
|
+
|
|
219
|
+
# Counter of a pointer's final state, after a transfer or a freeze:
|
|
220
|
+
# 2^64 - 1 (u64::MAX). Nothing can replace a state at this counter.
|
|
221
|
+
POINTER_FINAL_COUNTER = 18_446_744_073_709_551_615
|
|
222
|
+
|
|
223
|
+
# What a pointer's target is. Pass as +kind:+ to the pointer writes; the
|
|
224
|
+
# daemon defaults to +CHUNK+ when it is omitted.
|
|
225
|
+
module PointerKind
|
|
226
|
+
# An immutable chunk, such as a public file's data map. The default.
|
|
227
|
+
CHUNK = "chunk"
|
|
228
|
+
# Another pointer: a forwarding the owner can still change. Use
|
|
229
|
+
# +pointer_transfer+ to hand a pointer over for good.
|
|
230
|
+
POINTER = "pointer"
|
|
231
|
+
|
|
232
|
+
# The kind a raw +kind_tag+ names (0 chunk, 1 pointer), or nil for a tag
|
|
233
|
+
# this SDK does not know.
|
|
234
|
+
#
|
|
235
|
+
# @param tag [Integer]
|
|
236
|
+
# @return [String, nil]
|
|
237
|
+
def self.from_tag(tag)
|
|
238
|
+
case tag
|
|
239
|
+
when 0 then CHUNK
|
|
240
|
+
when 1 then POINTER
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# The write +pointer_prepare+ signs for an external signer.
|
|
246
|
+
module PointerOp
|
|
247
|
+
# Create the pointer at +target+. Refused if it already exists.
|
|
248
|
+
CREATE = "create"
|
|
249
|
+
# Point the pointer at +target+, creating it if it does not exist.
|
|
250
|
+
UPDATE = "update"
|
|
251
|
+
# Hand the pointer over for good to the pointer at +recipient+, which must
|
|
252
|
+
# already exist. Irreversible once stored.
|
|
253
|
+
TRANSFER = "transfer"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Where a pointer stands with respect to finality (+PointerFinality#status+).
|
|
257
|
+
#
|
|
258
|
+
# The status is the daemon's wire string. These are the values this SDK
|
|
259
|
+
# knows; a newer daemon may send another, which is kept verbatim (the
|
|
260
|
+
# "unknown" arm: +PointerFinality#status_known?+ is false). Give a +case+
|
|
261
|
+
# over the status an +else+ branch.
|
|
262
|
+
module PointerFinalityStatus
|
|
263
|
+
# No peer that answered holds a final state: the owner can still move the
|
|
264
|
+
# pointer. Rules a final state out only when every peer answered.
|
|
265
|
+
OPEN = "open"
|
|
266
|
+
# One final state, not yet held by a majority: a transfer still
|
|
267
|
+
# spreading, or a write that fell short.
|
|
268
|
+
SETTLING = "settling"
|
|
269
|
+
# One final state held by a majority, but not every peer answered, so a
|
|
270
|
+
# rival could still be unseen. Ask again.
|
|
271
|
+
UNCONFIRMED = "unconfirmed"
|
|
272
|
+
# One final state held by a majority, every peer answered, no rival. The
|
|
273
|
+
# only status a recipient should rely on.
|
|
274
|
+
FINAL = "final"
|
|
275
|
+
# The owner signed two or more different final states.
|
|
276
|
+
FORKED = "forked"
|
|
277
|
+
|
|
278
|
+
# Every status this SDK knows.
|
|
279
|
+
KNOWN = [OPEN, SETTLING, UNCONFIRMED, FINAL, FORKED].freeze
|
|
280
|
+
|
|
281
|
+
# @param status [String]
|
|
282
|
+
# @return [Boolean] whether this SDK knows +status+
|
|
283
|
+
def self.known?(status)
|
|
284
|
+
KNOWN.include?(status)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# The state a pointer write stored (+pointer_create+ / +pointer_update+).
|
|
289
|
+
# +counter+ is an Integer; a final state sits at +POINTER_FINAL_COUNTER+.
|
|
290
|
+
PointerWriteResult = Struct.new(:address, :state_id, :counter, keyword_init: true)
|
|
291
|
+
|
|
292
|
+
# A pointer's current state, as the network serves it (+pointer_get+).
|
|
293
|
+
#
|
|
294
|
+
# - +owner+: the owner's ML-DSA-65 public key (hex, 1,952 bytes). The
|
|
295
|
+
# address is derived from it, so it is the proof of who owns the pointer.
|
|
296
|
+
# - +counter+: Integer, +POINTER_FINAL_COUNTER+ once final.
|
|
297
|
+
# - +kind+: "chunk", "pointer", or "unknown" for a kind the daemon does not
|
|
298
|
+
# know; +kind_tag+ carries the raw value either way.
|
|
299
|
+
# - +is_final+: the state is at the final counter, so nothing can replace it.
|
|
300
|
+
# - +transferred_to+: the pointer this one was handed over to, or nil.
|
|
301
|
+
PointerRecord = Struct.new(
|
|
302
|
+
:address, :owner, :counter, :kind, :kind_tag, :target, :state_id,
|
|
303
|
+
:is_final, :transferred_to,
|
|
304
|
+
keyword_init: true
|
|
305
|
+
) do
|
|
306
|
+
# @return [Boolean] true at the final counter: transferred or frozen
|
|
307
|
+
def final?
|
|
308
|
+
is_final == true
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# @return [String, nil] the target's +PointerKind+, if this SDK knows its tag
|
|
312
|
+
def target_kind
|
|
313
|
+
PointerKind.from_tag(kind_tag)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Where a chain of pointers ends (+pointer_resolve+): the target's +kind+
|
|
318
|
+
# ("chunk", or "unknown" for a kind the daemon does not know), its raw
|
|
319
|
+
# +kind_tag+ and the +target+ address.
|
|
320
|
+
PointerTarget = Struct.new(:kind, :kind_tag, :target, keyword_init: true) do
|
|
321
|
+
# @return [String, nil] the target's +PointerKind+, if this SDK knows its tag
|
|
322
|
+
def target_kind
|
|
323
|
+
PointerKind.from_tag(kind_tag)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# One final state, as a finality check reports it. +holders+ is how many
|
|
328
|
+
# peers of the close group hold it; +transferred_to+ is nil unless the
|
|
329
|
+
# state is a transfer.
|
|
330
|
+
PointerFinalState = Struct.new(
|
|
331
|
+
:state_id, :kind, :kind_tag, :target, :holders, :transferred_to,
|
|
332
|
+
keyword_init: true
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Where a pointer stands, as its whole close group reports it
|
|
336
|
+
# (+pointer_finality+). Check it before relying on a transfer: only
|
|
337
|
+
# +final?+ means the transfer is beyond dispute.
|
|
338
|
+
#
|
|
339
|
+
# - +group+ / +answered+: peers in the close group, and peers that answered.
|
|
340
|
+
# - +status+: a +PointerFinalityStatus+ string (see there for unknown ones).
|
|
341
|
+
# - +counter+: +open+ only, the Integer counter a read would return, or nil
|
|
342
|
+
# when no state is corroborated.
|
|
343
|
+
# - +state+: +settling+, +unconfirmed+ and +final+, the one final state seen
|
|
344
|
+
# (+PointerFinalState+), else nil.
|
|
345
|
+
# - +states+ / +majority+: +forked+ only, every final state seen (most-held
|
|
346
|
+
# first) and the one a majority holds, which reads return. +states+ is
|
|
347
|
+
# empty and +majority+ nil otherwise.
|
|
348
|
+
# - +transferred_to+: the pointer this one was handed over to, once that is
|
|
349
|
+
# beyond dispute (status +final+), else nil.
|
|
350
|
+
PointerFinality = Struct.new(
|
|
351
|
+
:address, :group, :answered, :status, :counter, :state, :states,
|
|
352
|
+
:majority, :transferred_to,
|
|
353
|
+
keyword_init: true
|
|
354
|
+
) do
|
|
355
|
+
# @return [Boolean] one final state, held by a majority, every peer
|
|
356
|
+
# answering and no rival seen
|
|
357
|
+
def final?
|
|
358
|
+
status == PointerFinalityStatus::FINAL
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# @return [Boolean] false for a status a newer daemon added
|
|
362
|
+
def status_known?
|
|
363
|
+
PointerFinalityStatus.known?(status)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Whose pointer decides what an address resolves to, after following every
|
|
368
|
+
# transfer (+pointer_controller+). +transfers+ lists every pointer the
|
|
369
|
+
# address was handed over to, in order; +frozen+ is true when +pointer+ is
|
|
370
|
+
# final without being handed over, so nobody can move it.
|
|
371
|
+
PointerController = Struct.new(:pointer, :transfers, :frozen, keyword_init: true)
|
|
372
|
+
|
|
373
|
+
# What a transfer stored, and where the close group says it stands.
|
|
374
|
+
# +finality+ (a +PointerFinality+) is nil when that check failed, and
|
|
375
|
+
# +finality_error+ says why; the transfer itself is stored and final either
|
|
376
|
+
# way. Ask again with +pointer_finality+.
|
|
377
|
+
PointerTransferResult = Struct.new(:address, :state_id, :finality, :finality_error, keyword_init: true)
|
|
378
|
+
|
|
379
|
+
# A pointer write signed by the daemon and quoted for an external signer
|
|
380
|
+
# (+pointer_prepare+). Pay +payments+ with +payForQuotes()+, then call
|
|
381
|
+
# +pointer_finalize+. +op+ is a +PointerOp+ string, +counter+ an Integer,
|
|
382
|
+
# +payment_type+ always "wave_batch" (a pointer state is paid with one
|
|
383
|
+
# quote). +signed_quotes+ (+SignedQuoteEntry+) is filled only when the
|
|
384
|
+
# prepare asked for it, else empty.
|
|
385
|
+
#
|
|
386
|
+
# While it awaits payment, other writes to the pointer raise
|
|
387
|
+
# +ConflictError+ until it is finalized or expires (one hour).
|
|
388
|
+
PointerPrepareResult = Struct.new(
|
|
389
|
+
:upload_id, :op, :address, :state_id, :counter, :payment_type,
|
|
390
|
+
:payments, :total_amount, :payment_vault_address, :payment_token_address,
|
|
391
|
+
:rpc_url, :signed_quotes,
|
|
392
|
+
keyword_init: true
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# The state a pointer finalize stored. +finality+ is set for a transfer
|
|
396
|
+
# (where the close group says it stands), else nil.
|
|
397
|
+
PointerFinalizeResult = Struct.new(:address, :state_id, :counter, :finality, keyword_init: true)
|
|
398
|
+
|
|
399
|
+
# Parses a pointer counter. The daemon sends counters as decimal strings on
|
|
400
|
+
# both transports, because a final state sits at 2^64 - 1, past what a JSON
|
|
401
|
+
# number keeps exactly in many languages. Ruby integers are unbounded, so
|
|
402
|
+
# the value is checked against the daemon's u64 range here.
|
|
403
|
+
#
|
|
404
|
+
# Strict: only a String of ASCII digits within u64 parses. +Integer()+ alone
|
|
405
|
+
# would also take a sign, underscores or a radix prefix, and a malformed
|
|
406
|
+
# counter must never read as 0.
|
|
407
|
+
#
|
|
408
|
+
# @param field [String] the field name, for the error message
|
|
409
|
+
# @param value [Object] the wire value
|
|
410
|
+
# @return [Integer]
|
|
411
|
+
# @raise [InternalError] when +value+ is not a decimal u64
|
|
412
|
+
def self.parse_pointer_counter(field, value)
|
|
413
|
+
if value.is_a?(String) && value.match?(/\A[0-9]+\z/)
|
|
414
|
+
n = Integer(value, 10)
|
|
415
|
+
return n if n <= POINTER_FINAL_COUNTER
|
|
416
|
+
end
|
|
417
|
+
raise InternalError, "invalid pointer #{field} #{value.inspect}: expected a decimal u64"
|
|
418
|
+
end
|
|
198
419
|
end
|
data/lib/antd/v1/chunks_pb.rb
CHANGED
|
@@ -7,7 +7,7 @@ require 'google/protobuf'
|
|
|
7
7
|
require 'antd/v1/common_pb'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
descriptor_data = "\n\x14\x61ntd/v1/chunks.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\"\n\x0fGetChunkRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\" \n\x10GetChunkResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1f\n\x0fPutChunkRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"@\n\x10PutChunkResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"
|
|
10
|
+
descriptor_data = "\n\x14\x61ntd/v1/chunks.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\"\n\x0fGetChunkRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\" \n\x10GetChunkResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1f\n\x0fPutChunkRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"@\n\x10PutChunkResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"B\n\x13PrepareChunkRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x1d\n\x15include_signed_quotes\x18\x02 \x01(\x08\"\xa8\x02\n\x14PrepareChunkResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x16\n\x0e\x61lready_stored\x18\x02 \x01(\x08\x12\x11\n\tupload_id\x18\x03 \x01(\t\x12\x14\n\x0cpayment_type\x18\x04 \x01(\t\x12\'\n\x08payments\x18\x05 \x03(\x0b\x32\x15.antd.v1.PaymentEntry\x12\x14\n\x0ctotal_amount\x18\x06 \x01(\t\x12\x1d\n\x15payment_vault_address\x18\x07 \x01(\t\x12\x1d\n\x15payment_token_address\x18\x08 \x01(\t\x12\x0f\n\x07rpc_url\x18\t \x01(\t\x12\x30\n\rsigned_quotes\x18\n \x03(\x0b\x32\x19.antd.v1.SignedQuoteEntry\"\x9a\x01\n\x14\x46inalizeChunkRequest\x12\x11\n\tupload_id\x18\x01 \x01(\t\x12>\n\ttx_hashes\x18\x02 \x03(\x0b\x32+.antd.v1.FinalizeChunkRequest.TxHashesEntry\x1a/\n\rTxHashesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"(\n\x15\x46inalizeChunkResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t2\xa3\x02\n\x0c\x43hunkService\x12:\n\x03Get\x12\x18.antd.v1.GetChunkRequest\x1a\x19.antd.v1.GetChunkResponse\x12:\n\x03Put\x12\x18.antd.v1.PutChunkRequest\x1a\x19.antd.v1.PutChunkResponse\x12K\n\x0cPrepareChunk\x12\x1c.antd.v1.PrepareChunkRequest\x1a\x1d.antd.v1.PrepareChunkResponse\x12N\n\rFinalizeChunk\x12\x1d.antd.v1.FinalizeChunkRequest\x1a\x1e.antd.v1.FinalizeChunkResponseBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3"
|
|
11
11
|
|
|
12
12
|
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
13
|
pool.add_serialized_file(descriptor_data)
|
data/lib/antd/v1/common_pb.rb
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
require 'google/protobuf'
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
descriptor_data = "\n\x14\x61ntd/v1/common.proto\x12\x07\x61ntd.v1\"y\n\x04\x43ost\x12\x13\n\x0b\x61tto_tokens\x18\x01 \x01(\t\x12\x11\n\tfile_size\x18\x02 \x01(\x04\x12\x13\n\x0b\x63hunk_count\x18\x03 \x01(\r\x12\x1e\n\x16\x65stimated_gas_cost_wei\x18\x04 \x01(\t\x12\x14\n\x0cpayment_mode\x18\x05 \x01(\t\"\x16\n\x07\x41\x64\x64ress\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0ePublicKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0eSecretKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"K\n\x0cPaymentEntry\x12\x12\n\nquote_hash\x18\x01 \x01(\t\x12\x17\n\x0frewards_address\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\
|
|
8
|
+
descriptor_data = "\n\x14\x61ntd/v1/common.proto\x12\x07\x61ntd.v1\"y\n\x04\x43ost\x12\x13\n\x0b\x61tto_tokens\x18\x01 \x01(\t\x12\x11\n\tfile_size\x18\x02 \x01(\x04\x12\x13\n\x0b\x63hunk_count\x18\x03 \x01(\r\x12\x1e\n\x16\x65stimated_gas_cost_wei\x18\x04 \x01(\t\x12\x14\n\x0cpayment_mode\x18\x05 \x01(\t\"\x16\n\x07\x41\x64\x64ress\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0ePublicKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0eSecretKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"K\n\x0cPaymentEntry\x12\x12\n\nquote_hash\x18\x01 \x01(\t\x12\x17\n\x0frewards_address\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\t\"Q\n\x10SignedQuoteEntry\x12\x12\n\nquote_hash\x18\x01 \x01(\t\x12\r\n\x05quote\x18\x02 \x01(\x0c\x12\x1a\n\x12\x63ommitment_sidecar\x18\x03 \x01(\x0c\x42\x44Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3"
|
|
9
9
|
|
|
10
10
|
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
11
11
|
pool.add_serialized_file(descriptor_data)
|
|
@@ -17,5 +17,6 @@ module Antd
|
|
|
17
17
|
PublicKeyProto = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("antd.v1.PublicKeyProto").msgclass
|
|
18
18
|
SecretKeyProto = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("antd.v1.SecretKeyProto").msgclass
|
|
19
19
|
PaymentEntry = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("antd.v1.PaymentEntry").msgclass
|
|
20
|
+
SignedQuoteEntry = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("antd.v1.SignedQuoteEntry").msgclass
|
|
20
21
|
end
|
|
21
22
|
end
|