@ethersphere/bee-js 3.2.0 → 3.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.
Files changed (183) hide show
  1. package/dist/cjs/bee-debug.js +615 -0
  2. package/dist/cjs/bee.js +922 -0
  3. package/dist/cjs/chunk/bmt.js +50 -0
  4. package/dist/cjs/chunk/cac.js +56 -0
  5. package/dist/cjs/chunk/serialize.js +19 -0
  6. package/dist/cjs/chunk/signer.js +132 -0
  7. package/dist/cjs/chunk/soc.js +172 -0
  8. package/dist/cjs/chunk/span.js +29 -0
  9. package/dist/cjs/feed/index.js +184 -0
  10. package/dist/cjs/feed/json.js +41 -0
  11. package/dist/cjs/feed/topic.js +25 -0
  12. package/dist/cjs/feed/type.js +15 -0
  13. package/dist/cjs/index.js +35 -0
  14. package/dist/cjs/modules/bytes.js +74 -0
  15. package/dist/cjs/modules/bzz.js +131 -0
  16. package/dist/cjs/modules/chunk.js +58 -0
  17. package/dist/cjs/modules/debug/balance.js +77 -0
  18. package/dist/cjs/modules/debug/chequebook.js +167 -0
  19. package/dist/cjs/modules/debug/chunk.js +51 -0
  20. package/dist/cjs/modules/debug/connectivity.js +75 -0
  21. package/dist/cjs/modules/debug/settlements.js +45 -0
  22. package/dist/cjs/modules/debug/stamps.js +89 -0
  23. package/dist/cjs/modules/debug/states.js +47 -0
  24. package/dist/cjs/modules/debug/status.js +153 -0
  25. package/dist/cjs/modules/debug/tag.js +30 -0
  26. package/dist/cjs/modules/debug/transactions.js +81 -0
  27. package/dist/cjs/modules/feed.js +76 -0
  28. package/dist/cjs/modules/pinning.js +84 -0
  29. package/dist/cjs/modules/pss.js +55 -0
  30. package/dist/cjs/modules/soc.js +40 -0
  31. package/dist/cjs/modules/status.js +26 -0
  32. package/dist/cjs/modules/stewardship.js +41 -0
  33. package/dist/cjs/modules/tag.js +96 -0
  34. package/dist/cjs/package.json +7 -0
  35. package/dist/cjs/types/debug.js +9 -0
  36. package/dist/cjs/types/index.js +46 -0
  37. package/dist/cjs/utils/bytes.js +107 -0
  38. package/dist/cjs/utils/collection.browser.js +36 -0
  39. package/dist/cjs/utils/collection.js +70 -0
  40. package/dist/cjs/utils/collection.node.js +115 -0
  41. package/dist/cjs/utils/data.browser.js +78 -0
  42. package/dist/cjs/utils/data.js +60 -0
  43. package/dist/cjs/utils/error.js +50 -0
  44. package/dist/cjs/utils/eth.js +206 -0
  45. package/dist/cjs/utils/expose.js +44 -0
  46. package/dist/cjs/utils/file.js +49 -0
  47. package/dist/cjs/utils/hash.js +16 -0
  48. package/dist/cjs/utils/headers.js +59 -0
  49. package/dist/cjs/utils/hex.js +150 -0
  50. package/dist/cjs/utils/http.js +166 -0
  51. package/dist/cjs/utils/merge.js +34 -0
  52. package/dist/cjs/utils/pss.js +18 -0
  53. package/dist/cjs/utils/stamps.js +17 -0
  54. package/dist/cjs/utils/stream.js +137 -0
  55. package/dist/cjs/utils/tar.js +25 -0
  56. package/dist/cjs/utils/type.js +327 -0
  57. package/dist/cjs/utils/uint64.js +29 -0
  58. package/dist/cjs/utils/url.js +56 -0
  59. package/dist/index.js +28326 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/mjs/bee-debug.js +509 -0
  62. package/dist/mjs/bee.js +845 -0
  63. package/dist/mjs/chunk/bmt.js +46 -0
  64. package/dist/mjs/chunk/cac.js +50 -0
  65. package/dist/mjs/chunk/serialize.js +15 -0
  66. package/dist/mjs/chunk/signer.js +112 -0
  67. package/dist/mjs/chunk/soc.js +127 -0
  68. package/dist/mjs/chunk/span.js +25 -0
  69. package/dist/mjs/feed/index.js +136 -0
  70. package/dist/mjs/feed/json.js +23 -0
  71. package/dist/mjs/feed/topic.js +20 -0
  72. package/dist/mjs/feed/type.js +10 -0
  73. package/dist/mjs/index.js +7 -0
  74. package/dist/mjs/modules/bytes.js +56 -0
  75. package/dist/mjs/modules/bzz.js +119 -0
  76. package/dist/mjs/modules/chunk.js +43 -0
  77. package/dist/mjs/modules/debug/balance.js +53 -0
  78. package/dist/mjs/modules/debug/chequebook.js +131 -0
  79. package/dist/mjs/modules/debug/chunk.js +33 -0
  80. package/dist/mjs/modules/debug/connectivity.js +45 -0
  81. package/dist/mjs/modules/debug/settlements.js +27 -0
  82. package/dist/mjs/modules/debug/stamps.js +59 -0
  83. package/dist/mjs/modules/debug/states.js +29 -0
  84. package/dist/mjs/modules/debug/status.js +114 -0
  85. package/dist/mjs/modules/debug/tag.js +15 -0
  86. package/dist/mjs/modules/debug/transactions.js +57 -0
  87. package/dist/mjs/modules/feed.js +61 -0
  88. package/dist/mjs/modules/pinning.js +60 -0
  89. package/dist/mjs/modules/pss.js +36 -0
  90. package/dist/mjs/modules/soc.js +28 -0
  91. package/dist/mjs/modules/status.js +11 -0
  92. package/dist/mjs/modules/stewardship.js +23 -0
  93. package/dist/mjs/modules/tag.js +69 -0
  94. package/dist/mjs/package.json +7 -0
  95. package/dist/mjs/types/debug.js +6 -0
  96. package/dist/mjs/types/index.js +33 -0
  97. package/dist/mjs/utils/bytes.js +95 -0
  98. package/dist/mjs/utils/collection.browser.js +18 -0
  99. package/dist/mjs/utils/collection.js +52 -0
  100. package/dist/mjs/utils/collection.node.js +63 -0
  101. package/dist/mjs/utils/data.browser.js +57 -0
  102. package/dist/mjs/utils/data.js +39 -0
  103. package/dist/mjs/utils/error.js +48 -0
  104. package/dist/mjs/utils/eth.js +184 -0
  105. package/dist/mjs/utils/expose.js +9 -0
  106. package/dist/mjs/utils/file.js +33 -0
  107. package/dist/mjs/utils/hash.js +12 -0
  108. package/dist/mjs/utils/headers.js +54 -0
  109. package/dist/mjs/utils/hex.js +139 -0
  110. package/dist/mjs/utils/http.js +136 -0
  111. package/dist/mjs/utils/merge.js +30 -0
  112. package/dist/mjs/utils/pss.js +14 -0
  113. package/dist/mjs/utils/stamps.js +13 -0
  114. package/dist/mjs/utils/stream.js +131 -0
  115. package/dist/mjs/utils/tar.js +18 -0
  116. package/dist/mjs/utils/type.js +296 -0
  117. package/dist/mjs/utils/uint64.js +23 -0
  118. package/dist/mjs/utils/url.js +50 -0
  119. package/dist/{src → types}/bee-debug.d.ts +47 -1
  120. package/dist/{src → types}/bee.d.ts +0 -0
  121. package/dist/{src → types}/chunk/bmt.d.ts +0 -0
  122. package/dist/{src → types}/chunk/cac.d.ts +0 -0
  123. package/dist/{src → types}/chunk/serialize.d.ts +0 -0
  124. package/dist/{src → types}/chunk/signer.d.ts +0 -0
  125. package/dist/{src → types}/chunk/soc.d.ts +0 -0
  126. package/dist/{src → types}/chunk/span.d.ts +0 -0
  127. package/dist/{src → types}/feed/index.d.ts +0 -0
  128. package/dist/{src → types}/feed/json.d.ts +0 -0
  129. package/dist/{src → types}/feed/topic.d.ts +0 -0
  130. package/dist/{src → types}/feed/type.d.ts +0 -0
  131. package/dist/{src → types}/index.d.ts +0 -0
  132. package/dist/{src → types}/modules/bytes.d.ts +0 -0
  133. package/dist/{src → types}/modules/bzz.d.ts +0 -0
  134. package/dist/{src → types}/modules/chunk.d.ts +0 -0
  135. package/dist/{src → types}/modules/debug/balance.d.ts +0 -0
  136. package/dist/{src → types}/modules/debug/chequebook.d.ts +0 -0
  137. package/dist/{src → types}/modules/debug/chunk.d.ts +0 -0
  138. package/dist/{src → types}/modules/debug/connectivity.d.ts +0 -0
  139. package/dist/{src → types}/modules/debug/settlements.d.ts +0 -0
  140. package/dist/{src → types}/modules/debug/stamps.d.ts +0 -0
  141. package/dist/{src → types}/modules/debug/states.d.ts +0 -0
  142. package/dist/types/modules/debug/status.d.ts +72 -0
  143. package/dist/{src → types}/modules/debug/tag.d.ts +0 -0
  144. package/dist/{src → types}/modules/debug/transactions.d.ts +0 -0
  145. package/dist/{src → types}/modules/feed.d.ts +0 -0
  146. package/dist/{src → types}/modules/pinning.d.ts +0 -0
  147. package/dist/{src → types}/modules/pss.d.ts +0 -0
  148. package/dist/{src → types}/modules/soc.d.ts +0 -0
  149. package/dist/{src → types}/modules/status.d.ts +0 -0
  150. package/dist/{src → types}/modules/stewardship.d.ts +0 -0
  151. package/dist/{src → types}/modules/tag.d.ts +0 -0
  152. package/dist/{src → types}/types/debug.d.ts +8 -0
  153. package/dist/{src → types}/types/index.d.ts +1 -0
  154. package/dist/{src → types}/utils/bytes.d.ts +0 -0
  155. package/dist/types/utils/collection.browser.d.ts +15 -0
  156. package/dist/{src → types}/utils/collection.d.ts +0 -14
  157. package/dist/types/utils/collection.node.d.ts +15 -0
  158. package/dist/{src → types}/utils/data.browser.d.ts +0 -0
  159. package/dist/{src → types}/utils/data.d.ts +0 -0
  160. package/dist/{src → types}/utils/error.d.ts +0 -0
  161. package/dist/{src → types}/utils/eth.d.ts +0 -0
  162. package/dist/{src → types}/utils/expose.d.ts +2 -1
  163. package/dist/{src → types}/utils/file.d.ts +0 -0
  164. package/dist/{src → types}/utils/hash.d.ts +0 -0
  165. package/dist/{src → types}/utils/headers.d.ts +0 -0
  166. package/dist/{src → types}/utils/hex.d.ts +0 -0
  167. package/dist/{src → types}/utils/http.d.ts +0 -0
  168. package/dist/{src → types}/utils/merge.d.ts +0 -0
  169. package/dist/{src → types}/utils/pss.d.ts +0 -0
  170. package/dist/{src → types}/utils/stamps.d.ts +0 -0
  171. package/dist/{src → types}/utils/stream.d.ts +0 -0
  172. package/dist/{src → types}/utils/tar.d.ts +0 -0
  173. package/dist/{src → types}/utils/type.d.ts +0 -0
  174. package/dist/{src → types}/utils/uint64.d.ts +0 -0
  175. package/dist/{src → types}/utils/url.d.ts +0 -0
  176. package/package.json +39 -23
  177. package/dist/index.browser.min.js +0 -3
  178. package/dist/index.browser.min.js.LICENSE.txt +0 -59
  179. package/dist/index.browser.min.js.map +0 -1
  180. package/dist/index.min.js +0 -3
  181. package/dist/index.min.js.LICENSE.txt +0 -50
  182. package/dist/index.min.js.map +0 -1
  183. package/dist/src/modules/debug/status.d.ts +0 -24
@@ -0,0 +1,46 @@
1
+ import { keccak256 } from 'js-sha3';
2
+ import { BeeArgumentError } from '../utils/error';
3
+ import { keccak256Hash } from '../utils/hash';
4
+ const MAX_CHUNK_PAYLOAD_SIZE = 4096;
5
+ const SEGMENT_SIZE = 32;
6
+ const SEGMENT_PAIR_SIZE = 2 * SEGMENT_SIZE;
7
+ const HASH_SIZE = 32;
8
+ /**
9
+ * Calculate a Binary Merkle Tree hash for a chunk
10
+ *
11
+ * The BMT chunk address is the hash of the 8 byte span and the root
12
+ * hash of a binary Merkle tree (BMT) built on the 32-byte segments
13
+ * of the underlying data.
14
+ *
15
+ * If the chunk content is less than 4k, the hash is calculated as
16
+ * if the chunk was padded with all zeros up to 4096 bytes.
17
+ *
18
+ * @param chunkContent Chunk data including span and payload as well
19
+ *
20
+ * @returns the keccak256 hash in a byte array
21
+ */
22
+ export function bmtHash(chunkContent) {
23
+ const span = chunkContent.slice(0, 8);
24
+ const payload = chunkContent.slice(8);
25
+ const rootHash = bmtRootHash(payload);
26
+ const chunkHashInput = new Uint8Array([...span, ...rootHash]);
27
+ const chunkHash = keccak256Hash(chunkHashInput);
28
+ return chunkHash;
29
+ }
30
+ function bmtRootHash(payload) {
31
+ if (payload.length > MAX_CHUNK_PAYLOAD_SIZE) {
32
+ throw new BeeArgumentError('invalid data length', payload);
33
+ }
34
+ // create an input buffer padded with zeros
35
+ let input = new Uint8Array([...payload, ...new Uint8Array(MAX_CHUNK_PAYLOAD_SIZE - payload.length)]);
36
+ while (input.length !== HASH_SIZE) {
37
+ const output = new Uint8Array(input.length / 2);
38
+ // in each round we hash the segment pairs together
39
+ for (let offset = 0; offset < input.length; offset += SEGMENT_PAIR_SIZE) {
40
+ const hashNumbers = keccak256.array(input.slice(offset, offset + SEGMENT_PAIR_SIZE));
41
+ output.set(hashNumbers, offset / 2);
42
+ }
43
+ input = output;
44
+ }
45
+ return input;
46
+ }
@@ -0,0 +1,50 @@
1
+ import { BeeError } from '../utils/error';
2
+ import { bmtHash } from './bmt';
3
+ import { bytesEqual, flexBytesAtOffset, assertFlexBytes } from '../utils/bytes';
4
+ import { serializeBytes } from './serialize';
5
+ import { makeSpan, SPAN_SIZE } from './span';
6
+ export const MIN_PAYLOAD_SIZE = 1;
7
+ export const MAX_PAYLOAD_SIZE = 4096;
8
+ const CAC_SPAN_OFFSET = 0;
9
+ const CAC_PAYLOAD_OFFSET = CAC_SPAN_OFFSET + SPAN_SIZE;
10
+ /**
11
+ * Creates a content addressed chunk and verifies the payload size.
12
+ *
13
+ * @param payloadBytes the data to be stored in the chunk
14
+ */
15
+ export function makeContentAddressedChunk(payloadBytes) {
16
+ const span = makeSpan(payloadBytes.length);
17
+ assertFlexBytes(payloadBytes, MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE);
18
+ const data = serializeBytes(span, payloadBytes);
19
+ return {
20
+ data,
21
+ span: () => span,
22
+ payload: () => flexBytesAtOffset(data, CAC_PAYLOAD_OFFSET, MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE),
23
+ address: () => bmtHash(data),
24
+ };
25
+ }
26
+ /**
27
+ * Type guard for valid content addressed chunk data
28
+ *
29
+ * @param data The chunk data
30
+ * @param chunkAddress The address of the chunk
31
+ */
32
+ export function isValidChunkData(data, chunkAddress) {
33
+ if (!(data instanceof Uint8Array))
34
+ return false;
35
+ const address = bmtHash(data);
36
+ return bytesEqual(address, chunkAddress);
37
+ }
38
+ /**
39
+ * Asserts if data are representing given address of its chunk.
40
+ *
41
+ * @param data The chunk data
42
+ * @param chunkAddress The address of the chunk
43
+ *
44
+ * @returns a valid content addressed chunk or throws error
45
+ */
46
+ export function assertValidChunkData(data, chunkAddress) {
47
+ if (!isValidChunkData(data, chunkAddress)) {
48
+ throw new BeeError('Address of content address chunk does not match given data!');
49
+ }
50
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Helper function for serialize byte arrays
3
+ *
4
+ * @param arrays Any number of byte array arguments
5
+ */
6
+ export function serializeBytes(...arrays) {
7
+ const length = arrays.reduce((prev, curr) => prev + curr.length, 0);
8
+ const buffer = new Uint8Array(length);
9
+ let offset = 0;
10
+ arrays.forEach(arr => {
11
+ buffer.set(arr, offset);
12
+ offset += arr.length;
13
+ });
14
+ return buffer;
15
+ }
@@ -0,0 +1,112 @@
1
+ import { ec } from 'elliptic';
2
+ import { BeeError } from '../utils/error';
3
+ import { isBytes, assertBytes, wrapBytesWithHelpers } from '../utils/bytes';
4
+ import { keccak256Hash } from '../utils/hash';
5
+ import { hexToBytes, makeHexString } from '../utils/hex';
6
+ import { SIGNATURE_BYTES_LENGTH, SIGNATURE_HEX_LENGTH } from '../types';
7
+ import { isStrictlyObject } from '../utils/type';
8
+ const UNCOMPRESSED_RECOVERY_ID = 27;
9
+ function hashWithEthereumPrefix(data) {
10
+ const ethereumSignedMessagePrefix = `\x19Ethereum Signed Message:\n${data.length}`;
11
+ const prefixBytes = new TextEncoder().encode(ethereumSignedMessagePrefix);
12
+ return keccak256Hash(prefixBytes, data);
13
+ }
14
+ /**
15
+ * The default signer function that can be used for integrating with
16
+ * other applications (e.g. wallets).
17
+ *
18
+ * @param data The data to be signed
19
+ * @param privateKey The private key used for signing the data
20
+ */
21
+ export function defaultSign(data, privateKey) {
22
+ const curve = new ec('secp256k1');
23
+ const keyPair = curve.keyFromPrivate(privateKey);
24
+ const hashedDigest = hashWithEthereumPrefix(data);
25
+ const sigRaw = curve.sign(hashedDigest, keyPair, { canonical: true, pers: undefined });
26
+ if (sigRaw.recoveryParam === null) {
27
+ throw new BeeError('signDigest recovery param was null');
28
+ }
29
+ const signature = new Uint8Array([
30
+ ...sigRaw.r.toArray('be', 32),
31
+ ...sigRaw.s.toArray('be', 32),
32
+ sigRaw.recoveryParam + UNCOMPRESSED_RECOVERY_ID,
33
+ ]);
34
+ return signature;
35
+ }
36
+ function publicKeyToAddress(pubKey) {
37
+ const pubBytes = pubKey.encode('array', false);
38
+ return keccak256Hash(pubBytes.slice(1)).slice(12);
39
+ }
40
+ /**
41
+ * Recovers the ethereum address from a given signature.
42
+ *
43
+ * Can be used for verifying a piece of data when the public key is
44
+ * known.
45
+ *
46
+ * @param signature The signature
47
+ * @param digest The digest of the data
48
+ *
49
+ * @returns the recovered address
50
+ */
51
+ export function recoverAddress(signature, digest) {
52
+ const curve = new ec('secp256k1');
53
+ const sig = {
54
+ r: signature.slice(0, 32),
55
+ s: signature.slice(32, 64),
56
+ };
57
+ const recoveryParam = signature[64] - UNCOMPRESSED_RECOVERY_ID;
58
+ const hash = hashWithEthereumPrefix(digest);
59
+ const recPubKey = curve.recoverPubKey(hash, sig, recoveryParam);
60
+ return publicKeyToAddress(recPubKey);
61
+ }
62
+ /**
63
+ * Creates a singer object that can be used when the private key is known.
64
+ *
65
+ * @param privateKey The private key
66
+ */
67
+ export function makePrivateKeySigner(privateKey) {
68
+ const curve = new ec('secp256k1');
69
+ const keyPair = curve.keyFromPrivate(privateKey);
70
+ const address = publicKeyToAddress(keyPair.getPublic());
71
+ return {
72
+ sign: (digest) => defaultSign(digest, privateKey),
73
+ address,
74
+ };
75
+ }
76
+ export function assertSigner(signer) {
77
+ if (!isStrictlyObject(signer)) {
78
+ throw new TypeError('Signer must be an object!');
79
+ }
80
+ const typedSigner = signer;
81
+ if (!isBytes(typedSigner.address, 20)) {
82
+ throw new TypeError("Signer's address must be Uint8Array with 20 bytes!");
83
+ }
84
+ if (typeof typedSigner.sign !== 'function') {
85
+ throw new TypeError('Signer sign property needs to be function!');
86
+ }
87
+ }
88
+ export function makeSigner(signer) {
89
+ if (typeof signer === 'string') {
90
+ const hexKey = makeHexString(signer, 64);
91
+ const keyBytes = hexToBytes(hexKey); // HexString is verified for 64 length => 32 is guaranteed
92
+ return makePrivateKeySigner(keyBytes);
93
+ }
94
+ else if (signer instanceof Uint8Array) {
95
+ assertBytes(signer, 32);
96
+ return makePrivateKeySigner(signer);
97
+ }
98
+ assertSigner(signer);
99
+ return signer;
100
+ }
101
+ export async function sign(signer, data) {
102
+ const result = await signer.sign(wrapBytesWithHelpers(data));
103
+ if (typeof result === 'string') {
104
+ const hexString = makeHexString(result, SIGNATURE_HEX_LENGTH);
105
+ return hexToBytes(hexString);
106
+ }
107
+ if (result instanceof Uint8Array) {
108
+ assertBytes(result, SIGNATURE_BYTES_LENGTH);
109
+ return result;
110
+ }
111
+ throw new TypeError('Invalid output of sign function!');
112
+ }
@@ -0,0 +1,127 @@
1
+ import { bytesAtOffset, bytesEqual, flexBytesAtOffset } from '../utils/bytes';
2
+ import { bmtHash } from './bmt';
3
+ import { recoverAddress, sign } from './signer';
4
+ import { keccak256Hash } from '../utils/hash';
5
+ import { SPAN_SIZE } from './span';
6
+ import { serializeBytes } from './serialize';
7
+ import { BeeError } from '../utils/error';
8
+ import { makeContentAddressedChunk, MAX_PAYLOAD_SIZE, MIN_PAYLOAD_SIZE, assertValidChunkData, } from './cac';
9
+ import { bytesToHex } from '../utils/hex';
10
+ import * as socAPI from '../modules/soc';
11
+ import * as chunkAPI from '../modules/chunk';
12
+ import { assertAddress } from '../utils/type';
13
+ const IDENTIFIER_SIZE = 32;
14
+ const SIGNATURE_SIZE = 65;
15
+ const SOC_IDENTIFIER_OFFSET = 0;
16
+ const SOC_SIGNATURE_OFFSET = SOC_IDENTIFIER_OFFSET + IDENTIFIER_SIZE;
17
+ const SOC_SPAN_OFFSET = SOC_SIGNATURE_OFFSET + SIGNATURE_SIZE;
18
+ const SOC_PAYLOAD_OFFSET = SOC_SPAN_OFFSET + SPAN_SIZE;
19
+ function recoverChunkOwner(data) {
20
+ const cacData = data.slice(SOC_SPAN_OFFSET);
21
+ const chunkAddress = bmtHash(cacData);
22
+ const signature = bytesAtOffset(data, SOC_SIGNATURE_OFFSET, SIGNATURE_SIZE);
23
+ const identifier = bytesAtOffset(data, SOC_IDENTIFIER_OFFSET, IDENTIFIER_SIZE);
24
+ const digest = keccak256Hash(identifier, chunkAddress);
25
+ const ownerAddress = recoverAddress(signature, digest);
26
+ return ownerAddress;
27
+ }
28
+ /**
29
+ * Verifies if the data is a valid single owner chunk
30
+ *
31
+ * @param data The chunk data
32
+ * @param address The address of the single owner chunk
33
+ *
34
+ * @returns a single owner chunk or throws error
35
+ */
36
+ export function makeSingleOwnerChunkFromData(data, address) {
37
+ const ownerAddress = recoverChunkOwner(data);
38
+ const identifier = bytesAtOffset(data, SOC_IDENTIFIER_OFFSET, IDENTIFIER_SIZE);
39
+ const socAddress = keccak256Hash(identifier, ownerAddress);
40
+ if (!bytesEqual(address, socAddress)) {
41
+ throw new BeeError('SOC Data does not match given address!');
42
+ }
43
+ const signature = () => bytesAtOffset(data, SOC_SIGNATURE_OFFSET, SIGNATURE_SIZE);
44
+ const span = () => bytesAtOffset(data, SOC_SPAN_OFFSET, SPAN_SIZE);
45
+ const payload = () => flexBytesAtOffset(data, SOC_PAYLOAD_OFFSET, MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE);
46
+ return {
47
+ data,
48
+ identifier: () => identifier,
49
+ signature,
50
+ span,
51
+ payload,
52
+ address: () => socAddress,
53
+ owner: () => ownerAddress,
54
+ };
55
+ }
56
+ export function makeSOCAddress(identifier, address) {
57
+ return keccak256Hash(identifier, address);
58
+ }
59
+ /**
60
+ * Creates a single owner chunk object
61
+ *
62
+ * @param chunk A chunk object used for the span and payload
63
+ * @param identifier The identifier of the chunk
64
+ * @param signer The singer interface for signing the chunk
65
+ */
66
+ export async function makeSingleOwnerChunk(chunk, identifier, signer) {
67
+ const chunkAddress = chunk.address();
68
+ assertValidChunkData(chunk.data, chunkAddress);
69
+ const digest = keccak256Hash(identifier, chunkAddress);
70
+ const signature = await sign(signer, digest);
71
+ const data = serializeBytes(identifier, signature, chunk.span(), chunk.payload());
72
+ const address = makeSOCAddress(identifier, signer.address);
73
+ return {
74
+ data,
75
+ identifier: () => identifier,
76
+ signature: () => signature,
77
+ span: () => chunk.span(),
78
+ payload: () => chunk.payload(),
79
+ address: () => address,
80
+ owner: () => signer.address,
81
+ };
82
+ }
83
+ /**
84
+ * Helper function to upload a chunk.
85
+ *
86
+ * It uses the Chunk API and calculates the address before uploading.
87
+ *
88
+ * @param ky Ky instance
89
+ * @param chunk A chunk object
90
+ * @param postageBatchId Postage BatchId that will be assigned to uploaded data
91
+ * @param options Upload options
92
+ */
93
+ export async function uploadSingleOwnerChunk(ky, chunk, postageBatchId, options) {
94
+ const owner = bytesToHex(chunk.owner());
95
+ const identifier = bytesToHex(chunk.identifier());
96
+ const signature = bytesToHex(chunk.signature());
97
+ const data = serializeBytes(chunk.span(), chunk.payload());
98
+ return socAPI.upload(ky, owner, identifier, signature, data, postageBatchId, options);
99
+ }
100
+ /**
101
+ * Helper function to create and upload SOC.
102
+ *
103
+ * @param ky Ky instance
104
+ * @param signer The singer interface for signing the chunk
105
+ * @param postageBatchId
106
+ * @param identifier The identifier of the chunk
107
+ * @param data The chunk data
108
+ * @param options
109
+ */
110
+ export async function uploadSingleOwnerChunkData(ky, signer, postageBatchId, identifier, data, options) {
111
+ assertAddress(postageBatchId);
112
+ const cac = makeContentAddressedChunk(data);
113
+ const soc = await makeSingleOwnerChunk(cac, identifier, signer);
114
+ return uploadSingleOwnerChunk(ky, soc, postageBatchId, options);
115
+ }
116
+ /**
117
+ * Helper function to download SOC.
118
+ *
119
+ * @param url The url of the Bee service
120
+ * @param ownerAddress The singer interface for signing the chunk
121
+ * @param identifier The identifier of the chunk
122
+ */
123
+ export async function downloadSingleOwnerChunk(ky, ownerAddress, identifier) {
124
+ const address = makeSOCAddress(identifier, ownerAddress);
125
+ const data = await chunkAPI.download(ky, bytesToHex(address));
126
+ return makeSingleOwnerChunkFromData(data, address);
127
+ }
@@ -0,0 +1,25 @@
1
+ import { BeeArgumentError } from '../utils/error';
2
+ export const SPAN_SIZE = 8;
3
+ // we limit the maximum span size in 32 bits to avoid BigInt compatibility issues
4
+ const MAX_SPAN_LENGTH = 2 ** 32 - 1;
5
+ /**
6
+ * Create a span for storing the length of the chunk
7
+ *
8
+ * The length is encoded in 64-bit little endian.
9
+ *
10
+ * @param length The length of the span
11
+ */
12
+ export function makeSpan(length) {
13
+ if (length <= 0) {
14
+ throw new BeeArgumentError('invalid length for span', length);
15
+ }
16
+ if (length > MAX_SPAN_LENGTH) {
17
+ throw new BeeArgumentError('invalid length (> MAX_SPAN_LENGTH)', length);
18
+ }
19
+ const span = new Uint8Array(SPAN_SIZE);
20
+ const dataView = new DataView(span.buffer);
21
+ const littleEndian = true;
22
+ const lengthLower32 = length & 0xffffffff;
23
+ dataView.setUint32(0, lengthLower32, littleEndian);
24
+ return span;
25
+ }
@@ -0,0 +1,136 @@
1
+ import { keccak256Hash } from '../utils/hash';
2
+ import { serializeBytes } from '../chunk/serialize';
3
+ import { uploadSingleOwnerChunkData, makeSingleOwnerChunkFromData } from '../chunk/soc';
4
+ import { fetchFeedUpdate } from '../modules/feed';
5
+ import { REFERENCE_HEX_LENGTH, ENCRYPTED_REFERENCE_HEX_LENGTH, } from '../types';
6
+ import { makeBytes, bytesAtOffset } from '../utils/bytes';
7
+ import { BeeResponseError } from '../utils/error';
8
+ import { bytesToHex, hexToBytes, makeHexString } from '../utils/hex';
9
+ import { readUint64BigEndian, writeUint64BigEndian } from '../utils/uint64';
10
+ import * as chunkAPI from '../modules/chunk';
11
+ import { makeHexEthAddress } from '../utils/eth';
12
+ import { assertAddress } from '../utils/type';
13
+ const TIMESTAMP_PAYLOAD_OFFSET = 0;
14
+ const TIMESTAMP_PAYLOAD_SIZE = 8;
15
+ const REFERENCE_PAYLOAD_OFFSET = TIMESTAMP_PAYLOAD_SIZE;
16
+ const REFERENCE_PAYLOAD_MIN_SIZE = 32;
17
+ const REFERENCE_PAYLOAD_MAX_SIZE = 64;
18
+ const INDEX_HEX_LENGTH = 16;
19
+ export function isEpoch(epoch) {
20
+ return typeof epoch === 'object' && epoch !== null && 'time' in epoch && 'level' in epoch;
21
+ }
22
+ function hashFeedIdentifier(topic, index) {
23
+ return keccak256Hash(hexToBytes(topic), index);
24
+ }
25
+ export function makeSequentialFeedIdentifier(topic, index) {
26
+ const indexBytes = writeUint64BigEndian(index);
27
+ return hashFeedIdentifier(topic, indexBytes);
28
+ }
29
+ export function makeFeedIndexBytes(s) {
30
+ const hex = makeHexString(s, INDEX_HEX_LENGTH);
31
+ return hexToBytes(hex);
32
+ }
33
+ export function makeFeedIdentifier(topic, index) {
34
+ if (typeof index === 'number') {
35
+ return makeSequentialFeedIdentifier(topic, index);
36
+ }
37
+ else if (typeof index === 'string') {
38
+ const indexBytes = makeFeedIndexBytes(index);
39
+ return hashFeedIdentifier(topic, indexBytes);
40
+ }
41
+ else if (isEpoch(index)) {
42
+ throw new TypeError('epoch is not yet implemented');
43
+ }
44
+ return hashFeedIdentifier(topic, index);
45
+ }
46
+ export async function uploadFeedUpdate(ky, signer, topic, index, reference, postageBatchId, options) {
47
+ const identifier = makeFeedIdentifier(topic, index);
48
+ const at = options?.at ?? Date.now() / 1000.0;
49
+ const timestamp = writeUint64BigEndian(at);
50
+ const payloadBytes = serializeBytes(timestamp, reference);
51
+ return uploadSingleOwnerChunkData(ky, signer, postageBatchId, identifier, payloadBytes, options);
52
+ }
53
+ export async function findNextIndex(ky, owner, topic, options) {
54
+ try {
55
+ const feedUpdate = await fetchFeedUpdate(ky, owner, topic, options);
56
+ return makeHexString(feedUpdate.feedIndexNext, INDEX_HEX_LENGTH);
57
+ }
58
+ catch (e) {
59
+ if (e instanceof BeeResponseError && e.status === 404) {
60
+ return bytesToHex(makeBytes(8));
61
+ }
62
+ throw e;
63
+ }
64
+ }
65
+ export async function updateFeed(ky, signer, topic, reference, postageBatchId, options) {
66
+ const ownerHex = makeHexEthAddress(signer.address);
67
+ const nextIndex = await findNextIndex(ky, ownerHex, topic, options);
68
+ return uploadFeedUpdate(ky, signer, topic, nextIndex, reference, postageBatchId, options);
69
+ }
70
+ function verifyChunkReferenceAtOffset(offset, data) {
71
+ try {
72
+ return bytesAtOffset(data, offset, REFERENCE_PAYLOAD_MAX_SIZE);
73
+ }
74
+ catch (e) {
75
+ return bytesAtOffset(data, offset, REFERENCE_PAYLOAD_MIN_SIZE);
76
+ }
77
+ }
78
+ export function verifyChunkReference(data) {
79
+ return verifyChunkReferenceAtOffset(0, data);
80
+ }
81
+ export async function downloadFeedUpdate(ky, owner, topic, index) {
82
+ const identifier = makeFeedIdentifier(topic, index);
83
+ const address = keccak256Hash(identifier, owner);
84
+ const addressHex = bytesToHex(address);
85
+ const data = await chunkAPI.download(ky, addressHex);
86
+ const soc = makeSingleOwnerChunkFromData(data, address);
87
+ const payload = soc.payload();
88
+ const timestampBytes = bytesAtOffset(payload, TIMESTAMP_PAYLOAD_OFFSET, TIMESTAMP_PAYLOAD_SIZE);
89
+ const timestamp = readUint64BigEndian(timestampBytes);
90
+ const reference = verifyChunkReferenceAtOffset(REFERENCE_PAYLOAD_OFFSET, payload);
91
+ return {
92
+ timestamp,
93
+ reference,
94
+ };
95
+ }
96
+ export function makeFeedReader(ky, type, topic, owner) {
97
+ const download = async (options) => fetchFeedUpdate(ky, owner, topic, { ...options, type });
98
+ return {
99
+ type,
100
+ owner,
101
+ topic,
102
+ download,
103
+ };
104
+ }
105
+ function makeChunkReference(reference) {
106
+ if (typeof reference === 'string') {
107
+ try {
108
+ // Non-encrypted chunk hex string reference
109
+ const hexReference = makeHexString(reference, REFERENCE_HEX_LENGTH);
110
+ return hexToBytes(hexReference);
111
+ }
112
+ catch (e) {
113
+ if (!(e instanceof TypeError)) {
114
+ throw e;
115
+ }
116
+ // Encrypted chunk hex string reference
117
+ const hexReference = makeHexString(reference, ENCRYPTED_REFERENCE_HEX_LENGTH);
118
+ return hexToBytes(hexReference);
119
+ }
120
+ }
121
+ else if (reference instanceof Uint8Array) {
122
+ return verifyChunkReference(reference);
123
+ }
124
+ throw new TypeError('invalid chunk reference');
125
+ }
126
+ export function makeFeedWriter(ky, type, topic, signer) {
127
+ const upload = async (postageBatchId, reference, options) => {
128
+ assertAddress(postageBatchId);
129
+ const canonicalReference = makeChunkReference(reference);
130
+ return updateFeed(ky, signer, topic, canonicalReference, postageBatchId, { ...options, type });
131
+ };
132
+ return {
133
+ ...makeFeedReader(ky, type, topic, makeHexEthAddress(signer.address)),
134
+ upload,
135
+ };
136
+ }
@@ -0,0 +1,23 @@
1
+ import { isError } from '../utils/type';
2
+ function serializeJson(data) {
3
+ try {
4
+ const jsonString = JSON.stringify(data);
5
+ return new TextEncoder().encode(jsonString);
6
+ }
7
+ catch (e) {
8
+ if (isError(e)) {
9
+ e.message = `JsonFeed: ${e.message}`;
10
+ }
11
+ throw e;
12
+ }
13
+ }
14
+ export async function getJsonData(bee, reader) {
15
+ const feedUpdate = await reader.download();
16
+ const retrievedData = await bee.downloadData(feedUpdate.reference);
17
+ return retrievedData.json();
18
+ }
19
+ export async function setJsonData(bee, writer, postageBatchId, data, options) {
20
+ const serializedData = serializeJson(data);
21
+ const { reference } = await bee.uploadData(postageBatchId, serializedData, options);
22
+ return writer.upload(postageBatchId, reference);
23
+ }
@@ -0,0 +1,20 @@
1
+ import { keccak256Hash } from '../utils/hash';
2
+ import { assertBytes } from '../utils/bytes';
3
+ import { makeHexString, bytesToHex } from '../utils/hex';
4
+ import { TOPIC_BYTES_LENGTH, TOPIC_HEX_LENGTH } from '../types';
5
+ export function makeTopic(topic) {
6
+ if (typeof topic === 'string') {
7
+ return makeHexString(topic, TOPIC_HEX_LENGTH);
8
+ }
9
+ else if (topic instanceof Uint8Array) {
10
+ assertBytes(topic, TOPIC_BYTES_LENGTH);
11
+ return bytesToHex(topic, TOPIC_HEX_LENGTH);
12
+ }
13
+ throw new TypeError('invalid topic');
14
+ }
15
+ export function makeTopicFromString(s) {
16
+ if (typeof s !== 'string') {
17
+ throw new TypeError('topic has to be string!');
18
+ }
19
+ return bytesToHex(keccak256Hash(s), TOPIC_HEX_LENGTH);
20
+ }
@@ -0,0 +1,10 @@
1
+ const feedTypes = ['sequence', 'epoch'];
2
+ export const DEFAULT_FEED_TYPE = 'sequence';
3
+ export function isFeedType(type) {
4
+ return typeof type === 'string' && feedTypes.includes(type);
5
+ }
6
+ export function assertFeedType(type) {
7
+ if (!isFeedType(type)) {
8
+ throw new TypeError('invalid feed type');
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ import { Bee } from './bee';
2
+ import { BeeDebug } from './bee-debug';
3
+ export * as Utils from './utils/expose';
4
+ export * from './types';
5
+ export * from './utils/error';
6
+ export { SUPPORTED_BEE_VERSION, SUPPORTED_BEE_VERSION_EXACT } from './modules/debug/status';
7
+ export { Bee, BeeDebug };
@@ -0,0 +1,56 @@
1
+ import { prepareData } from '../utils/data';
2
+ import { extractUploadHeaders } from '../utils/headers';
3
+ import { http } from '../utils/http';
4
+ import { wrapBytesWithHelpers } from '../utils/bytes';
5
+ import { makeTagUid } from '../utils/type';
6
+ const endpoint = 'bytes';
7
+ /**
8
+ * Upload data to a Bee node
9
+ *
10
+ * @param ky Ky instance
11
+ * @param data Data to be uploaded
12
+ * @param postageBatchId Postage BatchId that will be assigned to uploaded data
13
+ * @param options Additional options like tag, encryption, pinning
14
+ */
15
+ export async function upload(ky, data, postageBatchId, options) {
16
+ const response = await http(ky, {
17
+ path: endpoint,
18
+ method: 'post',
19
+ responseType: 'json',
20
+ body: await prepareData(data),
21
+ headers: {
22
+ 'content-type': 'application/octet-stream',
23
+ ...extractUploadHeaders(postageBatchId, options),
24
+ },
25
+ });
26
+ return {
27
+ reference: response.data.reference,
28
+ tagUid: makeTagUid(response.headers.get('swarm-tag')),
29
+ };
30
+ }
31
+ /**
32
+ * Download data as a byte array
33
+ *
34
+ * @param ky
35
+ * @param hash Bee content reference
36
+ */
37
+ export async function download(ky, hash) {
38
+ const response = await http(ky, {
39
+ responseType: 'arraybuffer',
40
+ path: `${endpoint}/${hash}`,
41
+ });
42
+ return wrapBytesWithHelpers(new Uint8Array(response.data));
43
+ }
44
+ /**
45
+ * Download data as a readable stream
46
+ *
47
+ * @param ky
48
+ * @param hash Bee content reference
49
+ */
50
+ export async function downloadReadable(ky, hash) {
51
+ const response = await http(ky, {
52
+ responseType: 'stream',
53
+ path: `${endpoint}/${hash}`,
54
+ });
55
+ return response.data;
56
+ }