@ethersphere/bee-js 10.2.0 → 10.4.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.
package/dist/mjs/bee.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Binary, Objects, System, Types } from 'cafe-utility';
2
- import { makeContentAddressedChunk } from "./chunk/cac.js";
3
- import { downloadSingleOwnerChunk, makeSOCAddress, makeSingleOwnerChunk, uploadSingleOwnerChunkData } from "./chunk/soc.js";
2
+ import { makeContentAddressedChunk, unmarshalContentAddressedChunk } from "./chunk/cac.js";
3
+ import { downloadSingleOwnerChunk, makeSOCAddress, makeSingleOwnerChunk, unmarshalSingleOwnerChunk, uploadSingleOwnerChunkData } from "./chunk/soc.js";
4
4
  import { makeFeedReader, makeFeedWriter } from "./feed/index.js";
5
5
  import { areAllSequentialFeedsUpdateRetrievable } from "./feed/retrievable.js";
6
6
  import * as bytes from "./modules/bytes.js";
@@ -37,7 +37,7 @@ import { ResourceLocator } from "./utils/resource-locator.js";
37
37
  import { getAmountForDuration, getDepthForSize, getStampCost } from "./utils/stamps.js";
38
38
  import { BZZ, DAI } from "./utils/tokens.js";
39
39
  import { asNumberString, assertData, assertFileData, makeTagUid, prepareAllTagsOptions, prepareBeeRequestOptions, prepareCollectionUploadOptions, prepareDownloadOptions, prepareFileUploadOptions, prepareGsocMessageHandler, preparePostageBatchOptions, preparePssMessageHandler, prepareRedundantUploadOptions, prepareTransactionOptions, prepareUploadOptions } from "./utils/type.js";
40
- import { BatchId, EthAddress, Identifier, PeerAddress, PrivateKey, PublicKey, Reference, Span, Topic, TransactionId } from "./utils/typed-bytes.js";
40
+ import { BatchId, EthAddress, Identifier, PeerAddress, PrivateKey, PublicKey, Reference, Signature, Span, Topic, TransactionId } from "./utils/typed-bytes.js";
41
41
  import { assertBeeUrl, stripLastSlash } from "./utils/url.js";
42
42
  /**
43
43
  * The main component that abstracts operations available on the Bee API.
@@ -158,7 +158,7 @@ export class Bee {
158
158
  * Chunks uploaded with this method should be retrieved with the {@link downloadChunk} method.
159
159
  *
160
160
  * @param stamp Postage Batch ID or an Envelope created with the {@link createEnvelope} method.
161
- * @param data Raw chunk to be uploaded
161
+ * @param data Raw chunk to be uploaded (Content Addressed Chunk or Single Owner Chunk)
162
162
  * @param options Additional options like tag, encryption, pinning, content-type and request options
163
163
  * @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
164
164
  *
@@ -167,6 +167,7 @@ export class Bee {
167
167
  * @see [Bee API reference - `POST /chunks`](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks/post)
168
168
  */
169
169
  async uploadChunk(stamp, data, options, requestOptions) {
170
+ const isSOC = 'identifier' in data && 'signature' in data && 'owner' in data;
170
171
  data = data instanceof Uint8Array ? data : data.data;
171
172
  if (options) {
172
173
  options = prepareUploadOptions(options);
@@ -174,8 +175,11 @@ export class Bee {
174
175
  if (data.length < Span.LENGTH) {
175
176
  throw new BeeArgumentError(`Chunk has to have size of at least ${Span.LENGTH}.`, data);
176
177
  }
177
- if (data.length > CHUNK_SIZE + Span.LENGTH) {
178
- throw new BeeArgumentError(`Chunk has to have size of at most ${CHUNK_SIZE + Span.LENGTH}.`, data);
178
+ if (!isSOC && data.length > CHUNK_SIZE + Span.LENGTH) {
179
+ throw new BeeArgumentError(`Content Addressed Chunk must not exceed ${CHUNK_SIZE + Span.LENGTH} bytes.`, data);
180
+ }
181
+ if (isSOC && data.length > CHUNK_SIZE + Span.LENGTH + Signature.LENGTH + Identifier.LENGTH) {
182
+ throw new BeeArgumentError(`Single Owner Chunk must not exceed ${CHUNK_SIZE + Span.LENGTH + Signature.LENGTH + Identifier.LENGTH} bytes.`, data);
179
183
  }
180
184
  return chunk.upload(this.getRequestOptionsForCall(requestOptions), data, stamp, options);
181
185
  }
@@ -810,7 +814,7 @@ export class Bee {
810
814
  signer = new PrivateKey(signer);
811
815
  identifier = new Identifier(identifier);
812
816
  const cac = makeContentAddressedChunk(data);
813
- const soc = makeSingleOwnerChunk(cac, identifier, signer);
817
+ const soc = cac.toSingleOwnerChunk(identifier, signer);
814
818
  return gsoc.send(this.getRequestOptionsForCall(requestOptions), soc, postageBatchId, options);
815
819
  }
816
820
  /**
@@ -940,6 +944,70 @@ export class Bee {
940
944
  owner = new EthAddress(owner);
941
945
  return fetchLatestFeedUpdate(this.getRequestOptionsForCall(requestOptions), owner, topic);
942
946
  }
947
+ /**
948
+ * Creates a Content Addressed Chunk.
949
+ *
950
+ * To be uploaded with the {@link uploadChunk} method.
951
+ *
952
+ * Payload size must be between 1 and 4096 bytes.
953
+ *
954
+ * @param rawPayload Data to be stored in the chunk. If the data is a string, it will be converted to UTF-8 bytes.
955
+ * @param span Optional span for the chunk. If not provided, it will be set to the length of the payload.
956
+ *
957
+ * @example
958
+ *
959
+ */
960
+ makeContentAddressedChunk(rawPayload, span) {
961
+ return makeContentAddressedChunk(rawPayload, span);
962
+ }
963
+ /**
964
+ * Attempts to unmarshal arbitrary data into a Content Addressed Chunk.
965
+ * Throws an error if the data is not a valid CAC.
966
+ *
967
+ * @param data The chunk data (`span` and `payload`)
968
+ */
969
+ unmarshalContentAddressedChunk(data) {
970
+ return unmarshalContentAddressedChunk(data);
971
+ }
972
+ /**
973
+ * Creates a Single Owner Chunk.
974
+ *
975
+ * To be uploaded with the {@link uploadChunk} method.
976
+ *
977
+ * Identical to chaining `makeContentAddressedChunk` and `toSingleOwnerChunk`.
978
+ *
979
+ * Payload size must be between 1 and 4096 bytes.
980
+ *
981
+ * @param address Address of the Content Addressed Chunk
982
+ * @param span Span of the Content Addressed Chunk
983
+ * @param payload Payload of the Content Addressed Chunk
984
+ * @param identifier The identifier of the chunk
985
+ * @param signer The signer interface for signing the chunk
986
+ */
987
+ makeSingleOwnerChunk(address, span, payload, identifier, signer) {
988
+ return makeSingleOwnerChunk(address, span, payload, identifier, signer);
989
+ }
990
+ /**
991
+ * Calculates the address of a Single Owner Chunk based on its identifier and owner address.
992
+ *
993
+ * @param identifier
994
+ * @param address
995
+ */
996
+ calculateSingleOwnerChunkAddress(identifier, address) {
997
+ return makeSOCAddress(identifier, address);
998
+ }
999
+ /**
1000
+ * Attempts to unmarshal arbitrary data into a Single Owner Chunk.
1001
+ * Throws an error if the data is not a valid SOC.
1002
+ *
1003
+ * @param data The chunk data
1004
+ * @param address The address of the single owner chunk
1005
+ *
1006
+ * @returns a single owner chunk or throws error
1007
+ */
1008
+ unmarshalSingleOwnerChunk(data, address) {
1009
+ return unmarshalSingleOwnerChunk(data, address);
1010
+ }
943
1011
  /**
944
1012
  * Returns an object for reading single owner chunks
945
1013
  *
@@ -1535,7 +1603,7 @@ export class Bee {
1535
1603
  const blockTime = this.network === 'gnosis' ? 5 : 15;
1536
1604
  const additionalAmount = getAmountForDuration(duration, chainState.currentPrice, blockTime);
1537
1605
  const currentAmount = getAmountForDuration(batch.duration, chainState.currentPrice, blockTime);
1538
- const targetAmount = duration.isZero() ? currentAmount * multiplier : currentAmount + additionalAmount * multiplier;
1606
+ const targetAmount = duration.isZero() ? currentAmount * multiplier : (currentAmount + additionalAmount) * multiplier;
1539
1607
  const amountDelta = targetAmount - currentAmount;
1540
1608
  const transactionId = await this.topUpBatch(batch.batchID, amountDelta, requestOptions);
1541
1609
  if (depthDelta > 0) {
@@ -1,41 +1,32 @@
1
- import { Binary } from 'cafe-utility';
1
+ import { Binary, Types } from 'cafe-utility';
2
2
  import { Bytes } from "../utils/bytes.js";
3
3
  import { Span } from "../utils/typed-bytes.js";
4
4
  import { calculateChunkAddress } from "./bmt.js";
5
+ import { makeSingleOwnerChunk } from "./soc.js";
5
6
  export const MIN_PAYLOAD_SIZE = 1;
6
7
  export const MAX_PAYLOAD_SIZE = 4096;
7
- const ENCODER = new TextEncoder();
8
- /**
9
- * Creates a content addressed chunk and verifies the payload size.
10
- *
11
- * @param payloadBytes the data to be stored in the chunk
12
- */
13
- export function makeContentAddressedChunk(payloadBytes) {
14
- if (!(payloadBytes instanceof Uint8Array)) {
15
- payloadBytes = ENCODER.encode(payloadBytes);
16
- }
17
- if (payloadBytes.length < MIN_PAYLOAD_SIZE || payloadBytes.length > MAX_PAYLOAD_SIZE) {
18
- throw new RangeError(`payload size ${payloadBytes.length} exceeds limits [${MIN_PAYLOAD_SIZE}, ${MAX_PAYLOAD_SIZE}]`);
19
- }
20
- const span = Span.fromBigInt(BigInt(payloadBytes.length));
21
- const data = Binary.concatBytes(span.toUint8Array(), payloadBytes);
22
- return {
23
- data,
24
- span,
25
- payload: Bytes.fromSlice(data, Span.LENGTH),
26
- address: calculateChunkAddress(data)
27
- };
8
+ export function unmarshalContentAddressedChunk(data) {
9
+ data = new Bytes(data);
10
+ return makeContentAddressedChunk(data.toUint8Array().slice(Span.LENGTH), Span.fromSlice(data.toUint8Array(), 0));
28
11
  }
29
- export function asContentAddressedChunk(chunkBytes) {
30
- if (chunkBytes.length < MIN_PAYLOAD_SIZE + Span.LENGTH || chunkBytes.length > MAX_PAYLOAD_SIZE + Span.LENGTH) {
31
- throw new RangeError(`chunk size ${chunkBytes.length} exceeds limits [${MIN_PAYLOAD_SIZE + Span.LENGTH}, ${Span.LENGTH}]`);
12
+ export function makeContentAddressedChunk(rawPayload, span) {
13
+ if (Types.isString(rawPayload)) {
14
+ rawPayload = Bytes.fromUtf8(rawPayload);
15
+ }
16
+ if (rawPayload.length < MIN_PAYLOAD_SIZE || rawPayload.length > MAX_PAYLOAD_SIZE) {
17
+ throw new RangeError(`payload size ${rawPayload.length} exceeds limits [${MIN_PAYLOAD_SIZE}, ${MAX_PAYLOAD_SIZE}]`);
32
18
  }
33
- const span = Span.fromSlice(chunkBytes, 0);
34
- const data = Binary.concatBytes(span.toUint8Array(), chunkBytes.slice(Span.LENGTH));
19
+ const typedSpan = span ? typeof span === 'bigint' ? Span.fromBigInt(span) : span : Span.fromBigInt(BigInt(rawPayload.length));
20
+ const payload = new Bytes(rawPayload);
21
+ const data = Binary.concatBytes(typedSpan.toUint8Array(), payload.toUint8Array());
22
+ const address = calculateChunkAddress(data);
35
23
  return {
36
24
  data,
37
- span,
38
- payload: Bytes.fromSlice(data, Span.LENGTH),
39
- address: calculateChunkAddress(data)
25
+ span: typedSpan,
26
+ payload,
27
+ address,
28
+ toSingleOwnerChunk: (identifier, signer) => {
29
+ return makeSingleOwnerChunk(address, typedSpan, payload, identifier, signer);
30
+ }
40
31
  };
41
32
  }
@@ -5,10 +5,10 @@ import { Bytes } from "../utils/bytes.js";
5
5
  import { BeeError } from "../utils/error.js";
6
6
  import { EthAddress, Identifier, PrivateKey, Reference, Signature, Span } from "../utils/typed-bytes.js";
7
7
  import { calculateChunkAddress } from "./bmt.js";
8
- import { asContentAddressedChunk, makeContentAddressedChunk } from "./cac.js";
8
+ import { makeContentAddressedChunk } from "./cac.js";
9
9
  const SOC_SIGNATURE_OFFSET = Identifier.LENGTH;
10
- const SOC_SPAN_OFFSET = SOC_SIGNATURE_OFFSET + Signature.LENGTH;
11
- const SOC_PAYLOAD_OFFSET = SOC_SPAN_OFFSET + Span.LENGTH;
10
+ const SOC_SPAN_OFFSET = Identifier.LENGTH + Signature.LENGTH;
11
+ const SOC_PAYLOAD_OFFSET = Identifier.LENGTH + Signature.LENGTH + Span.LENGTH;
12
12
  function recoverChunkOwner(data) {
13
13
  const cacData = data.slice(SOC_SPAN_OFFSET);
14
14
  const chunkAddress = calculateChunkAddress(cacData);
@@ -19,21 +19,22 @@ function recoverChunkOwner(data) {
19
19
  return ownerAddress;
20
20
  }
21
21
  /**
22
- * Verifies if the data is a valid single owner chunk
22
+ * Unmarshals arbitrary data into a Single Owner Chunk.
23
+ * Throws an error if the data is not a valid SOC.
23
24
  *
24
25
  * @param data The chunk data
25
26
  * @param address The address of the single owner chunk
26
27
  *
27
28
  * @returns a single owner chunk or throws error
28
29
  */
29
- export function makeSingleOwnerChunkFromData(data, address) {
30
+ export function unmarshalSingleOwnerChunk(data, address) {
30
31
  data = data instanceof Bytes ? data.toUint8Array() : data;
31
32
  address = new Reference(address);
32
33
  const ownerAddress = recoverChunkOwner(data);
33
34
  const identifier = Bytes.fromSlice(data, 0, Identifier.LENGTH);
34
35
  const socAddress = new Reference(Binary.keccak256(Binary.concatBytes(identifier.toUint8Array(), ownerAddress.toUint8Array())));
35
36
  if (!Binary.equals(address.toUint8Array(), socAddress.toUint8Array())) {
36
- throw new BeeError('SOC Data does not match given address!');
37
+ throw new BeeError('SOC data does not match given address!');
37
38
  }
38
39
  const signature = Signature.fromSlice(data, SOC_SIGNATURE_OFFSET);
39
40
  const span = Span.fromSlice(data, SOC_SPAN_OFFSET);
@@ -58,21 +59,19 @@ export function makeSOCAddress(identifier, address) {
58
59
  * @param identifier The identifier of the chunk
59
60
  * @param signer The signer interface for signing the chunk
60
61
  */
61
- export function makeSingleOwnerChunk(chunk, identifier, signer) {
62
+ export function makeSingleOwnerChunk(address, span, payload, identifier, signer) {
62
63
  identifier = new Identifier(identifier);
63
64
  signer = new PrivateKey(signer);
64
- const address = makeSOCAddress(identifier, signer.publicKey().address());
65
- const signature = signer.sign(Binary.concatBytes(identifier.toUint8Array(), chunk.address.toUint8Array()));
66
- const data = Binary.concatBytes(identifier.toUint8Array(), signature.toUint8Array(), chunk.data);
67
- const span = Span.fromSlice(chunk.data, 0);
68
- const payload = Bytes.fromSlice(chunk.data, Span.LENGTH);
65
+ const socAddress = makeSOCAddress(identifier, signer.publicKey().address());
66
+ const signature = signer.sign(Binary.concatBytes(identifier.toUint8Array(), address.toUint8Array()));
67
+ const data = Binary.concatBytes(identifier.toUint8Array(), signature.toUint8Array(), span.toUint8Array(), payload.toUint8Array());
69
68
  return {
70
69
  data,
71
70
  identifier,
72
71
  signature,
73
72
  span,
74
73
  payload,
75
- address,
74
+ address: socAddress,
76
75
  owner: signer.publicKey().address()
77
76
  };
78
77
  }
@@ -104,13 +103,13 @@ export async function uploadSingleOwnerChunkData(requestOptions, signer, stamp,
104
103
  signer = new PrivateKey(signer);
105
104
  identifier = new Identifier(identifier);
106
105
  const cac = makeContentAddressedChunk(data);
107
- const soc = makeSingleOwnerChunk(cac, identifier, signer);
106
+ const soc = cac.toSingleOwnerChunk(identifier, signer);
108
107
  return uploadSingleOwnerChunk(requestOptions, soc, stamp, options);
109
108
  }
110
- export async function uploadSingleOwnerChunkWithWrappedChunk(requestOptions, signer, stamp, identifier, rootChunk, options) {
109
+ export async function uploadSingleOwnerChunkWithWrappedChunk(requestOptions, signer, stamp, identifier, wrappedChunk, options) {
111
110
  signer = new PrivateKey(signer);
112
111
  identifier = new Identifier(identifier);
113
- const soc = makeSingleOwnerChunk(asContentAddressedChunk(rootChunk), identifier, signer);
112
+ const soc = wrappedChunk.toSingleOwnerChunk(identifier, signer);
114
113
  return uploadSingleOwnerChunk(requestOptions, soc, stamp, options);
115
114
  }
116
115
  /**
@@ -125,5 +124,5 @@ export async function downloadSingleOwnerChunk(requestOptions, ownerAddress, ide
125
124
  ownerAddress = new EthAddress(ownerAddress);
126
125
  const address = makeSOCAddress(identifier, ownerAddress);
127
126
  const cac = await chunkAPI.download(requestOptions, address.toHex());
128
- return makeSingleOwnerChunkFromData(cac, address);
127
+ return unmarshalSingleOwnerChunk(cac, address);
129
128
  }
@@ -1,13 +1,13 @@
1
1
  import { Binary, Optional, Types } from 'cafe-utility';
2
- import { asContentAddressedChunk } from "../chunk/cac.js";
3
- import { makeSingleOwnerChunkFromData, uploadSingleOwnerChunkData, uploadSingleOwnerChunkWithWrappedChunk } from "../chunk/soc.js";
2
+ import { makeContentAddressedChunk, unmarshalContentAddressedChunk } from "../chunk/cac.js";
3
+ import { unmarshalSingleOwnerChunk, uploadSingleOwnerChunkData, uploadSingleOwnerChunkWithWrappedChunk } from "../chunk/soc.js";
4
4
  import * as bytes from "../modules/bytes.js";
5
5
  import * as chunkAPI from "../modules/chunk.js";
6
6
  import { fetchLatestFeedUpdate, probeFeed } from "../modules/feed.js";
7
7
  import { Bytes } from "../utils/bytes.js";
8
8
  import { BeeResponseError } from "../utils/error.js";
9
9
  import { ResourceLocator } from "../utils/resource-locator.js";
10
- import { FeedIndex, Identifier, Reference, Signature } from "../utils/typed-bytes.js";
10
+ import { FeedIndex, Reference } from "../utils/typed-bytes.js";
11
11
  import { makeFeedIdentifier } from "./identifier.js";
12
12
  const TIMESTAMP_PAYLOAD_OFFSET = 0;
13
13
  const TIMESTAMP_PAYLOAD_SIZE = 8;
@@ -40,7 +40,7 @@ export async function updateFeedWithPayload(requestOptions, signer, topic, data,
40
40
  const identifier = makeFeedIdentifier(topic, nextIndex);
41
41
  if (data.length > 4096) {
42
42
  const uploadResult = await bytes.upload(requestOptions, data, postageBatchId, options);
43
- const rootChunk = await chunkAPI.download(requestOptions, uploadResult.reference);
43
+ const rootChunk = unmarshalContentAddressedChunk(await chunkAPI.download(requestOptions, uploadResult.reference));
44
44
  return uploadSingleOwnerChunkWithWrappedChunk(requestOptions, signer, postageBatchId, identifier, rootChunk, options);
45
45
  }
46
46
  return uploadSingleOwnerChunkData(requestOptions, signer, postageBatchId, identifier, Types.isString(data) ? Bytes.fromUtf8(data).toUint8Array() : data, options);
@@ -53,7 +53,7 @@ export async function downloadFeedUpdate(requestOptions, owner, topic, index, ha
53
53
  index = typeof index === 'number' ? FeedIndex.fromBigInt(BigInt(index)) : index;
54
54
  const address = getFeedUpdateChunkReference(owner, topic, index);
55
55
  const data = await chunkAPI.download(requestOptions, address.toHex());
56
- const soc = makeSingleOwnerChunkFromData(data, address);
56
+ const soc = unmarshalSingleOwnerChunk(data, address);
57
57
  let timestamp = Optional.empty();
58
58
  if (hasTimestamp) {
59
59
  const timestampBytes = Bytes.fromSlice(soc.payload.toUint8Array(), TIMESTAMP_PAYLOAD_OFFSET, TIMESTAMP_PAYLOAD_SIZE);
@@ -68,7 +68,8 @@ export async function downloadFeedUpdateAsCAC(requestOptions, owner, topic, inde
68
68
  index = typeof index === 'number' ? FeedIndex.fromBigInt(BigInt(index)) : index;
69
69
  const address = getFeedUpdateChunkReference(owner, topic, index);
70
70
  const data = await chunkAPI.download(requestOptions, address);
71
- return asContentAddressedChunk(data.slice(Identifier.LENGTH + Signature.LENGTH));
71
+ const soc = unmarshalSingleOwnerChunk(data, address);
72
+ return makeContentAddressedChunk(soc.payload, soc.span);
72
73
  }
73
74
  export function makeFeedReader(requestOptions, topic, owner) {
74
75
  // TODO: remove after enough time has passed in deprecated version
@@ -1,11 +1,8 @@
1
1
  import { Types } from 'cafe-utility';
2
- import { Duration } from "../../utils/duration.js";
3
2
  import { http } from "../../utils/http.js";
4
- import { Size } from "../../utils/size.js";
5
- import { getStampEffectiveBytes, getStampTheoreticalBytes, getStampUsage } from "../../utils/stamps.js";
3
+ import { mapPostageBatch } from "../../utils/stamps.js";
6
4
  import { asNumberString } from "../../utils/type.js";
7
5
  import { BatchId, EthAddress } from "../../utils/typed-bytes.js";
8
- import { normalizeBatchTTL } from "../../utils/workaround.js";
9
6
  const STAMPS_ENDPOINT = 'stamps';
10
7
  const BATCHES_ENDPOINT = 'batches';
11
8
  export async function getGlobalPostageBatches(requestOptions) {
@@ -63,60 +60,7 @@ export async function getAllPostageBatches(requestOptions) {
63
60
  }).map(x => Types.asObject(x, {
64
61
  name: 'stamp'
65
62
  }));
66
- return stamps.map(x => {
67
- const utilization = Types.asNumber(x.utilization, {
68
- name: 'utilization'
69
- });
70
- const depth = Types.asNumber(x.depth, {
71
- name: 'depth'
72
- });
73
- const bucketDepth = Types.asNumber(x.bucketDepth, {
74
- name: 'bucketDepth'
75
- });
76
- const usage = getStampUsage(utilization, depth, bucketDepth);
77
- const batchTTL = normalizeBatchTTL(Types.asNumber(x.batchTTL, {
78
- name: 'batchTTL'
79
- }));
80
- const duration = Duration.fromSeconds(batchTTL);
81
- const effectiveBytes = getStampEffectiveBytes(depth);
82
- return {
83
- batchID: new BatchId(Types.asString(x.batchID, {
84
- name: 'batchID'
85
- })),
86
- utilization,
87
- usable: Types.asBoolean(x.usable, {
88
- name: 'usable'
89
- }),
90
- label: Types.asEmptiableString(x.label, {
91
- name: 'label'
92
- }),
93
- depth,
94
- amount: asNumberString(x.amount, {
95
- name: 'amount'
96
- }),
97
- bucketDepth,
98
- blockNumber: Types.asNumber(x.blockNumber, {
99
- name: 'blockNumber'
100
- }),
101
- immutableFlag: Types.asBoolean(x.immutableFlag, {
102
- name: 'immutableFlag'
103
- }),
104
- usage,
105
- usageText: `${Math.round(usage * 100)}%`,
106
- size: Size.fromBytes(effectiveBytes),
107
- remainingSize: Size.fromBytes(Math.ceil(effectiveBytes * (1 - usage))),
108
- theoreticalSize: Size.fromBytes(getStampTheoreticalBytes(depth)),
109
- duration,
110
- calculateSize(encryption, redundancyLevel) {
111
- const effectiveBytes = getStampEffectiveBytes(this.depth, encryption, redundancyLevel);
112
- return Size.fromBytes(effectiveBytes);
113
- },
114
- calculateRemainingSize(encryption, redundancyLevel) {
115
- const effectiveBytes = getStampEffectiveBytes(this.depth, encryption, redundancyLevel);
116
- return Size.fromBytes(Math.ceil(effectiveBytes * (1 - this.usage)));
117
- }
118
- };
119
- });
63
+ return stamps.map(x => mapPostageBatch(validateRawPostageBatch(x)));
120
64
  }
121
65
  export async function getPostageBatch(requestOptions, postageBatchId, encryption, erasureCodeLevel) {
122
66
  const response = await http(requestOptions, {
@@ -127,58 +71,7 @@ export async function getPostageBatch(requestOptions, postageBatchId, encryption
127
71
  const body = Types.asObject(response.data, {
128
72
  name: 'response.data'
129
73
  });
130
- const utilization = Types.asNumber(body.utilization, {
131
- name: 'utilization'
132
- });
133
- const depth = Types.asNumber(body.depth, {
134
- name: 'depth'
135
- });
136
- const bucketDepth = Types.asNumber(body.bucketDepth, {
137
- name: 'bucketDepth'
138
- });
139
- const usage = getStampUsage(utilization, depth, bucketDepth);
140
- const batchTTL = normalizeBatchTTL(Types.asNumber(body.batchTTL, {
141
- name: 'batchTTL'
142
- }));
143
- const duration = Duration.fromSeconds(batchTTL);
144
- const effectiveBytes = getStampEffectiveBytes(depth, encryption, erasureCodeLevel);
145
- return {
146
- batchID: new BatchId(Types.asString(body.batchID, {
147
- name: 'batchID'
148
- })),
149
- utilization,
150
- usable: Types.asBoolean(body.usable, {
151
- name: 'usable'
152
- }),
153
- label: Types.asEmptiableString(body.label, {
154
- name: 'label'
155
- }),
156
- depth,
157
- amount: asNumberString(body.amount, {
158
- name: 'amount'
159
- }),
160
- bucketDepth,
161
- blockNumber: Types.asNumber(body.blockNumber, {
162
- name: 'blockNumber'
163
- }),
164
- immutableFlag: Types.asBoolean(body.immutableFlag, {
165
- name: 'immutableFlag'
166
- }),
167
- usage,
168
- usageText: `${Math.round(usage * 100)}%`,
169
- size: Size.fromBytes(effectiveBytes),
170
- remainingSize: Size.fromBytes(Math.ceil(effectiveBytes * (1 - usage))),
171
- theoreticalSize: Size.fromBytes(getStampTheoreticalBytes(depth)),
172
- duration,
173
- calculateSize(encryption, redundancyLevel) {
174
- const effectiveBytes = getStampEffectiveBytes(depth, encryption, redundancyLevel);
175
- return Size.fromBytes(effectiveBytes);
176
- },
177
- calculateRemainingSize(encryption, redundancyLevel) {
178
- const effectiveBytes = getStampEffectiveBytes(depth, encryption, redundancyLevel);
179
- return Size.fromBytes(Math.ceil(effectiveBytes * (1 - usage)));
180
- }
181
- };
74
+ return mapPostageBatch(validateRawPostageBatch(body), encryption, erasureCodeLevel);
182
75
  }
183
76
  export async function getPostageBatchBuckets(requestOptions, postageBatchId) {
184
77
  const response = await http(requestOptions, {
@@ -262,4 +155,38 @@ export async function diluteBatch(requestOptions, id, depth) {
262
155
  return new BatchId(Types.asString(body.batchID, {
263
156
  name: 'batchID'
264
157
  }));
158
+ }
159
+ function validateRawPostageBatch(raw) {
160
+ return {
161
+ amount: asNumberString(raw.amount, {
162
+ name: 'amount'
163
+ }),
164
+ batchID: Types.asString(raw.batchID, {
165
+ name: 'batchID'
166
+ }),
167
+ batchTTL: Types.asNumber(raw.batchTTL, {
168
+ name: 'batchTTL'
169
+ }),
170
+ bucketDepth: Types.asNumber(raw.bucketDepth, {
171
+ name: 'bucketDepth'
172
+ }),
173
+ blockNumber: Types.asNumber(raw.blockNumber, {
174
+ name: 'blockNumber'
175
+ }),
176
+ depth: Types.asNumber(raw.depth, {
177
+ name: 'depth'
178
+ }),
179
+ immutableFlag: Types.asBoolean(raw.immutableFlag, {
180
+ name: 'immutableFlag'
181
+ }),
182
+ label: Types.asEmptiableString(raw.label, {
183
+ name: 'label'
184
+ }),
185
+ usable: Types.asBoolean(raw.usable, {
186
+ name: 'usable'
187
+ }),
188
+ utilization: Types.asNumber(raw.utilization, {
189
+ name: 'utilization'
190
+ })
191
+ };
265
192
  }
@@ -2,4 +2,4 @@ export { getCollectionSize, makeCollectionFromFileList } from "./collection.js";
2
2
  export { getFolderSize } from "./collection.node.js";
3
3
  export { makeMaxTarget } from "./pss.js";
4
4
  export { approximateOverheadForRedundancyLevel, getRedundancyStat, getRedundancyStats } from "./redundancy.js";
5
- export { getAmountForDuration, getDepthForSize, getStampCost, getStampDuration, getStampEffectiveBytes, getStampEffectiveBytesBreakpoints, getStampTheoreticalBytes, getStampUsage } from "./stamps.js";
5
+ export { getAmountForDuration, getDepthForSize, getStampCost, getStampDuration, getStampEffectiveBytes, getStampEffectiveBytesBreakpoints, getStampTheoreticalBytes, getStampUsage, mapPostageBatch, unmapPostageBatch } from "./stamps.js";
@@ -2,8 +2,11 @@ import { Binary } from 'cafe-utility';
2
2
  import { capacityBreakpoints } from "../types/index.js";
3
3
  import { Bytes, parseSizeToBytes } from "./bytes.js";
4
4
  import { Duration } from "./duration.js";
5
+ import { Size } from "./size.js";
5
6
  import { BZZ } from "./tokens.js";
6
7
  import { asNumberString } from "./type.js";
8
+ import { BatchId } from "./typed-bytes.js";
9
+ import { normalizeBatchTTL } from "./workaround.js";
7
10
  const MAX_UTILIZATION = 0.9;
8
11
  /**
9
12
  * Utility function that calculates usage of postage batch based on its utilization, depth and bucket depth.
@@ -137,4 +140,49 @@ export function marshalStamp(signature, batchId, timestamp, index) {
137
140
  throw Error('invalid index length');
138
141
  }
139
142
  return new Bytes(Binary.concatBytes(batchId, index, timestamp, signature));
143
+ }
144
+ export function mapPostageBatch(raw, encryption, erasureCodeLevel) {
145
+ const usage = getStampUsage(raw.utilization, raw.depth, raw.bucketDepth);
146
+ const batchTTL = normalizeBatchTTL(raw.batchTTL);
147
+ const duration = Duration.fromSeconds(batchTTL);
148
+ const effectiveBytes = getStampEffectiveBytes(raw.depth, encryption, erasureCodeLevel);
149
+ return {
150
+ batchID: new BatchId(raw.batchID),
151
+ utilization: raw.utilization,
152
+ usable: raw.usable,
153
+ label: raw.label,
154
+ depth: raw.depth,
155
+ amount: asNumberString(raw.amount),
156
+ bucketDepth: raw.bucketDepth,
157
+ blockNumber: raw.blockNumber,
158
+ immutableFlag: raw.immutableFlag,
159
+ usage,
160
+ usageText: `${Math.round(usage * 100)}%`,
161
+ size: Size.fromBytes(effectiveBytes),
162
+ remainingSize: Size.fromBytes(Math.ceil(effectiveBytes * (1 - usage))),
163
+ theoreticalSize: Size.fromBytes(getStampTheoreticalBytes(raw.depth)),
164
+ duration,
165
+ calculateSize(encryption, redundancyLevel) {
166
+ const effectiveBytes = getStampEffectiveBytes(raw.depth, encryption, redundancyLevel);
167
+ return Size.fromBytes(effectiveBytes);
168
+ },
169
+ calculateRemainingSize(encryption, redundancyLevel) {
170
+ const effectiveBytes = getStampEffectiveBytes(raw.depth, encryption, redundancyLevel);
171
+ return Size.fromBytes(Math.ceil(effectiveBytes * (1 - this.usage)));
172
+ }
173
+ };
174
+ }
175
+ export function unmapPostageBatch(batch) {
176
+ return {
177
+ batchID: batch.batchID.toHex(),
178
+ utilization: batch.utilization,
179
+ usable: batch.usable,
180
+ label: batch.label,
181
+ depth: batch.depth,
182
+ amount: batch.amount,
183
+ bucketDepth: batch.bucketDepth,
184
+ blockNumber: batch.blockNumber,
185
+ immutableFlag: batch.immutableFlag,
186
+ batchTTL: batch.duration.toSeconds()
187
+ };
140
188
  }
@@ -6,8 +6,11 @@ export function normalizeBatchTTL(batchTTL) {
6
6
  if (batchTTL < 1) {
7
7
  return 1;
8
8
  }
9
- if (batchTTL > 315569260) {
10
- return 315569260;
9
+ // Cap `batchTTL` (represents seconds) to 100 years.
10
+ // We can assume `storagePrice` is invalid (e.g. 1).
11
+ // This is needed to prevent Date objects breaking.
12
+ if (batchTTL > 3155695200) {
13
+ return 3155695200;
11
14
  }
12
15
  return batchTTL;
13
16
  }