@logsn/arweave
Version:
Arweave JS client library
194 lines (193 loc) • 7.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tag = void 0;
const ArweaveUtils = require("./utils");
const deepHash_1 = require("./deepHash");
const merkle_1 = require("./merkle");
class BaseObject {
get(field, options) {
if (!Object.getOwnPropertyNames(this).includes(field)) {
throw new Error(`Field "${field}" is not a property of the Arweave Transaction class.`);
}
// Handle fields that are Uint8Arrays.
// To maintain compat we encode them to b64url
// if decode option is not specificed.
if (this[field] instanceof Uint8Array) {
if (options && options.decode && options.string) {
return ArweaveUtils.bufferToString(this[field]);
}
if (options && options.decode && !options.string) {
return this[field];
}
return ArweaveUtils.bufferTob64Url(this[field]);
}
if (this[field] instanceof Array) {
if (options?.decode !== undefined || options?.string !== undefined) {
if (field === "tags") {
console.warn(`Did you mean to use 'transaction["tags"]' ?`);
}
throw new Error(`Cannot decode or stringify an array.`);
}
return this[field];
}
if (options && options.decode == true) {
if (options && options.string) {
return ArweaveUtils.b64UrlToString(this[field]);
}
return ArweaveUtils.b64UrlToBuffer(this[field]);
}
return this[field];
}
}
class Tag extends BaseObject {
name;
value;
constructor(name, value, decode = false) {
super();
this.name = name;
this.value = value;
}
}
exports.Tag = Tag;
class Transaction extends BaseObject {
format = 2;
id = "";
last_tx = "";
owner = "";
tags = [];
target = "";
quantity = "0";
data_size = "0";
data = new Uint8Array();
data_root = "";
reward = "0";
signature = "";
// Computed when needed.
chunks;
constructor(attributes = {}) {
super();
Object.assign(this, attributes);
// If something passes in a Tx that has been toJSON'ed and back,
// or where the data was filled in from /tx/data endpoint.
// data will be b64url encoded, so decode it.
if (typeof this.data === "string") {
this.data = ArweaveUtils.b64UrlToBuffer(this.data);
}
if (attributes.tags) {
this.tags = attributes.tags.map((tag) => {
return new Tag(tag.name, tag.value);
});
}
}
addTag(name, value) {
this.tags.push(new Tag(ArweaveUtils.stringToB64Url(name), ArweaveUtils.stringToB64Url(value)));
}
toJSON() {
return {
format: this.format,
id: this.id,
last_tx: this.last_tx,
owner: this.owner,
tags: this.tags,
target: this.target,
quantity: this.quantity,
data: ArweaveUtils.bufferTob64Url(this.data),
data_size: this.data_size,
data_root: this.data_root,
data_tree: this.data_tree,
reward: this.reward,
signature: this.signature,
};
}
setOwner(owner) {
this.owner = owner;
}
setSignature({ id, owner, reward, tags, signature, }) {
this.id = id;
this.owner = owner;
if (reward)
this.reward = reward;
if (tags)
this.tags = tags;
this.signature = signature;
}
async prepareChunks(data) {
// Note: we *do not* use `this.data`, the caller may be
// operating on a transaction with an zero length data field.
// This function computes the chunks for the data passed in and
// assigns the result to this transaction. It should not read the
// data *from* this transaction.
if (!this.chunks && data.byteLength > 0) {
this.chunks = await (0, merkle_1.generateTransactionChunks)(data);
this.data_root = ArweaveUtils.bufferTob64Url(this.chunks.data_root);
}
if (!this.chunks && data.byteLength === 0) {
this.chunks = {
chunks: [],
data_root: new Uint8Array(),
proofs: [],
};
this.data_root = "";
}
}
// Returns a chunk in a format suitable for posting to /chunk.
// Similar to `prepareChunks()` this does not operate `this.data`,
// instead using the data passed in.
getChunk(idx, data) {
if (!this.chunks) {
throw new Error(`Chunks have not been prepared`);
}
const proof = this.chunks.proofs[idx];
const chunk = this.chunks.chunks[idx];
return {
data_root: this.data_root,
data_size: this.data_size,
data_path: ArweaveUtils.bufferTob64Url(proof.proof),
offset: proof.offset.toString(),
chunk: ArweaveUtils.bufferTob64Url(data.slice(chunk.minByteRange, chunk.maxByteRange)),
};
}
async getSignatureData() {
switch (this.format) {
case 1:
let tags = this.tags.reduce((accumulator, tag) => {
return ArweaveUtils.concatBuffers([
accumulator,
tag.get("name", { decode: true, string: false }),
tag.get("value", { decode: true, string: false }),
]);
}, new Uint8Array());
return ArweaveUtils.concatBuffers([
this.get("owner", { decode: true, string: false }),
this.get("target", { decode: true, string: false }),
this.get("data", { decode: true, string: false }),
ArweaveUtils.stringToBuffer(this.quantity),
ArweaveUtils.stringToBuffer(this.reward),
this.get("last_tx", { decode: true, string: false }),
tags,
]);
case 2:
if (!this.data_root) {
await this.prepareChunks(this.data);
}
const tagList = this.tags.map((tag) => [
tag.get("name", { decode: true, string: false }),
tag.get("value", { decode: true, string: false }),
]);
return await (0, deepHash_1.default)([
ArweaveUtils.stringToBuffer(this.format.toString()),
this.get("owner", { decode: true, string: false }),
this.get("target", { decode: true, string: false }),
ArweaveUtils.stringToBuffer(this.quantity),
ArweaveUtils.stringToBuffer(this.reward),
this.get("last_tx", { decode: true, string: false }),
tagList,
ArweaveUtils.stringToBuffer(this.data_size),
this.get("data_root", { decode: true, string: false }),
]);
default:
throw new Error(`Unexpected transaction format: ${this.format}`);
}
}
}
exports.default = Transaction;