@ethersphere/bee-js
Version:
Javascript client for Bee
129 lines • 5.48 kB
JavaScript
import { Binary } from 'cafe-utility';
import * as chunkAPI from "../modules/chunk.js";
import * as socAPI from "../modules/soc.js";
import { Bytes } from "../utils/bytes.js";
import { BeeError } from "../utils/error.js";
import { EthAddress, Identifier, PrivateKey, Reference, Signature, Span } from "../utils/typed-bytes.js";
import { calculateChunkAddress } from "./bmt.js";
import { asContentAddressedChunk, makeContentAddressedChunk } from "./cac.js";
const SOC_SIGNATURE_OFFSET = Identifier.LENGTH;
const SOC_SPAN_OFFSET = SOC_SIGNATURE_OFFSET + Signature.LENGTH;
const SOC_PAYLOAD_OFFSET = SOC_SPAN_OFFSET + Span.LENGTH;
function recoverChunkOwner(data) {
const cacData = data.slice(SOC_SPAN_OFFSET);
const chunkAddress = calculateChunkAddress(cacData);
const signature = Signature.fromSlice(data, SOC_SIGNATURE_OFFSET);
const identifier = Bytes.fromSlice(data, 0, Identifier.LENGTH);
const digest = Binary.concatBytes(identifier.toUint8Array(), chunkAddress.toUint8Array());
const ownerAddress = signature.recoverPublicKey(digest).address();
return ownerAddress;
}
/**
* Verifies if the data is a valid single owner chunk
*
* @param data The chunk data
* @param address The address of the single owner chunk
*
* @returns a single owner chunk or throws error
*/
export function makeSingleOwnerChunkFromData(data, address) {
data = data instanceof Bytes ? data.toUint8Array() : data;
address = new Reference(address);
const ownerAddress = recoverChunkOwner(data);
const identifier = Bytes.fromSlice(data, 0, Identifier.LENGTH);
const socAddress = new Reference(Binary.keccak256(Binary.concatBytes(identifier.toUint8Array(), ownerAddress.toUint8Array())));
if (!Binary.equals(address.toUint8Array(), socAddress.toUint8Array())) {
throw new BeeError('SOC Data does not match given address!');
}
const signature = Signature.fromSlice(data, SOC_SIGNATURE_OFFSET);
const span = Span.fromSlice(data, SOC_SPAN_OFFSET);
const payload = Bytes.fromSlice(data, SOC_PAYLOAD_OFFSET);
return {
data,
identifier,
signature,
span,
payload,
address: socAddress,
owner: ownerAddress
};
}
export function makeSOCAddress(identifier, address) {
return new Reference(Binary.keccak256(Binary.concatBytes(identifier.toUint8Array(), address.toUint8Array())));
}
/**
* Creates a single owner chunk object
*
* @param chunk A chunk object used for the span and payload
* @param identifier The identifier of the chunk
* @param signer The signer interface for signing the chunk
*/
export function makeSingleOwnerChunk(chunk, identifier, signer) {
identifier = new Identifier(identifier);
signer = new PrivateKey(signer);
const address = makeSOCAddress(identifier, signer.publicKey().address());
const signature = signer.sign(Binary.concatBytes(identifier.toUint8Array(), chunk.address.toUint8Array()));
const data = Binary.concatBytes(identifier.toUint8Array(), signature.toUint8Array(), chunk.data);
const span = Span.fromSlice(chunk.data, 0);
const payload = Bytes.fromSlice(chunk.data, Span.LENGTH);
return {
data,
identifier,
signature,
span,
payload,
address,
owner: signer.publicKey().address()
};
}
/**
* Helper function to upload a chunk.
*
* It uses the Chunk API and calculates the address before uploading.
*
* @param requestOptions Options for making requests
* @param chunk A chunk object
* @param stamp Postage BatchId that will be assigned to uploaded data
* @param options Upload options
*/
export async function uploadSingleOwnerChunk(requestOptions, chunk, stamp, options) {
const data = Binary.concatBytes(chunk.span.toUint8Array(), chunk.payload.toUint8Array());
return socAPI.upload(requestOptions, chunk.owner, chunk.identifier, chunk.signature, data, stamp, options);
}
/**
* Helper function to create and upload SOC.
*
* @param requestOptions Options for making requests
* @param signer The signer interface for signing the chunk
* @param postageBatchId
* @param identifier The identifier of the chunk
* @param data The chunk data
* @param options
*/
export async function uploadSingleOwnerChunkData(requestOptions, signer, stamp, identifier, data, options) {
signer = new PrivateKey(signer);
identifier = new Identifier(identifier);
const cac = makeContentAddressedChunk(data);
const soc = makeSingleOwnerChunk(cac, identifier, signer);
return uploadSingleOwnerChunk(requestOptions, soc, stamp, options);
}
export async function uploadSingleOwnerChunkWithWrappedChunk(requestOptions, signer, stamp, identifier, rootChunk, options) {
signer = new PrivateKey(signer);
identifier = new Identifier(identifier);
const soc = makeSingleOwnerChunk(asContentAddressedChunk(rootChunk), identifier, signer);
return uploadSingleOwnerChunk(requestOptions, soc, stamp, options);
}
/**
* Helper function to download SOC.
*
* @param url The url of the Bee service
* @param ownerAddress The signer interface for signing the chunk
* @param identifier The identifier of the chunk
*/
export async function downloadSingleOwnerChunk(requestOptions, ownerAddress, identifier) {
identifier = new Identifier(identifier);
ownerAddress = new EthAddress(ownerAddress);
const address = makeSOCAddress(identifier, ownerAddress);
const cac = await chunkAPI.download(requestOptions, address.toHex());
return makeSingleOwnerChunkFromData(cac, address);
}