Files
grin-web-wallet/scripts/slate_output.js
2024-12-20 18:08:44 -08:00

587 lines
14 KiB
JavaScript
Executable File

// Use strict
"use strict";
// Classes
// Slate output class
class SlateOutput {
// Public
// Constructor
constructor(serializedSlateOutputOrFeatures, slateOrCommit, proof) {
// Reset
this.reset();
// Check if a binary serialized slate output is provided
if(serializedSlateOutputOrFeatures instanceof BitReader === true) {
// Get serialized slate output
var serializedSlateOutput = serializedSlateOutputOrFeatures;
// Get slate
var slate = slateOrCommit;
// Unserialize the serialized slate output
this.unserialize(serializedSlateOutput, slate);
}
// Otherwise check if a serialized slate output is provided
else if(Object.isObject(serializedSlateOutputOrFeatures) === true) {
// Get serialized slate output
var serializedSlateOutput = serializedSlateOutputOrFeatures;
// Get slate
var slate = slateOrCommit;
// Unserialize the serialized slate output
this.unserialize(serializedSlateOutput, slate);
}
// Otherwise check if arguments are provided
else if(typeof serializedSlateOutputOrFeatures === "number") {
// Get features
var features = serializedSlateOutputOrFeatures;
// Get Commit
var commit = slateOrCommit;
// Set features to provided features
this.features = features;
// Set commit to provided commit
this.commit = commit;
// Set proof to provided proof
this.proof = proof;
// Check if proof failed to be verified
if(this.verifyProof() === false) {
// Throw error
throw "Unsupported output.";
}
}
// Otherwise
else {
// Throw error
throw "Unsupported output.";
}
}
// Serialize
serialize(slateOrVersion, bitWriter) {
// Get slate version
var slateVersion = (slateOrVersion instanceof Slate === true) ? slateOrVersion.getVersion() : slateOrVersion;
// Check slate's version
switch((slateVersion instanceof BigNumber === true) ? slateVersion.toFixed() : slateVersion) {
// Version two and three
case Slate.VERSION_TWO.toFixed():
case Slate.VERSION_THREE.toFixed():
// Return serialized slate output
return {
// Features
"features": SlateOutput.featuresToText(this.getFeatures()),
// Commit
"commit": Common.toHexString(this.getCommit()),
// Proof
"proof": Common.toHexString(this.getProof())
};
// Version Slatepack
case Slate.VERSION_SLATEPACK:
// Try
try {
// Write commit
bitWriter.setBytes(this.getCommit());
// Check if proof is too long
if(this.getProof()["length"] >= Math.pow(2, SlateOutput.COMPACT_PROOF_LENGTH_LENGTH)) {
// Throw error
throw "Proof is too long.";
}
// Write proof length
bitWriter.setBits(this.getProof()["length"], SlateOutput.COMPACT_PROOF_LENGTH_LENGTH);
// Write proof
bitWriter.setBytes(this.getProof());
}
// Catch errors
catch(error) {
// Throw error
throw "Unsupported output.";
}
// Break
break;
// Version four
case Slate.VERSION_FOUR.toFixed():
// Check if serializing slate output as binary
if(typeof bitWriter !== "undefined") {
// Try
try {
// Write features
bitWriter.setBytes([this.getFeatures()]);
// Write commit
bitWriter.setBytes(this.getCommit());
// Write proof length
bitWriter.setBytes((new BigNumber(this.getProof()["length"])).toBytes(BigNumber.BIG_ENDIAN, Common.BYTES_IN_A_UINT64));
// Write proof
bitWriter.setBytes(this.getProof());
}
// Catch errors
catch(error) {
// Throw error
throw "Unsupported output.";
}
}
// Otherwise
else {
// Create serialized slate output
var serializedSlateOutput = {
// Commit
"c": Common.toHexString(this.getCommit()),
// Proof
"p": Common.toHexString(this.getProof())
};
// Check if not plain
if(this.isPlain() === false) {
// Set serialized slate output's features
serializedSlateOutput["f"] = this.getFeatures();
}
// Return serialized slate output
return serializedSlateOutput;
}
// Break
break;
// Default
default:
// Throw error
throw "Unsupported slate version.";
}
}
// Get transaction
getTransaction() {
// Return transaction
return {
// Features
"features": SlateOutput.featuresToText(this.getFeatures()),
// Commit
"commit": Common.toHexString(this.getCommit()),
// Proof
"proof": Common.toHexString(this.getProof())
};
}
// Get features
getFeatures() {
// Return features
return this.features;
}
// Get commit
getCommit() {
// Return commit
return this.commit;
}
// Get proof
getProof() {
// Return proof
return this.proof;
}
// Is plain
isPlain() {
// Return if features is plain
return this.getFeatures() === SlateOutput.PLAIN_FEATURES;
}
// Is coinbase
isCoinbase() {
// Return if features is coinbase
return this.getFeatures() === SlateOutput.COINBASE_FEATURES;
}
// Get hash
getHash() {
// Return hash
return new Hash([
// Features
new Uint8Array([this.getFeatures()]),
// Commit
this.getCommit()
]);
}
// Is equal to
isEqualTo(slateOutput) {
// Check if features aren't equal
if(this.getFeatures() !== slateOutput.getFeatures())
// Return false
return false;
// Check if commits aren't equal
if(Common.arraysAreEqual(this.getCommit(), slateOutput.getCommit()) === false)
// Return false
return false;
// Check if proofs aren't equal
if(Common.arraysAreEqual(this.getProof(), slateOutput.getProof()) === false)
// Return false
return false;
// Return true
return true;
}
// Private
// Reset
reset() {
// Set features to plain features
this.features = SlateOutput.PLAIN_FEATURES;
}
// Unserialize
unserialize(serializedSlateOutput, slate) {
// Check slate's version
switch((slate.getVersion() instanceof BigNumber === true) ? slate.getVersion().toFixed() : slate.getVersion()) {
// Version two and three
case Slate.VERSION_TWO.toFixed():
case Slate.VERSION_THREE.toFixed():
// Check if serialized slate output's features isn't supported
if("features" in serializedSlateOutput === false || SlateOutput.textToFeatures(serializedSlateOutput["features"]) === SlateOutput.UNSUPPORTED_FEATURES || SlateOutput.textToFeatures(serializedSlateOutput["features"]) === SlateOutput.COINBASE_FEATURES) {
// Throw error
throw "Unsupported output.";
}
// Set features to serialized slate output's features
this.features = SlateOutput.textToFeatures(serializedSlateOutput["features"]);
// Check if serialized slate output's commit isn't supported
if("commit" in serializedSlateOutput === false || Common.isHexString(serializedSlateOutput["commit"]) === false || Common.hexStringLength(serializedSlateOutput["commit"]) !== Crypto.COMMIT_LENGTH || Secp256k1Zkp.isValidCommit(Common.fromHexString(serializedSlateOutput["commit"])) !== true) {
// Throw error
throw "Unsupported output.";
}
// Set commit to serialized slate output's commit
this.commit = Common.fromHexString(serializedSlateOutput["commit"]);
// Check if serialized slate output's proof isn't supported
if("proof" in serializedSlateOutput === false || Common.isHexString(serializedSlateOutput["proof"]) === false || Common.hexStringLength(serializedSlateOutput["proof"]) !== Crypto.PROOF_LENGTH) {
// Throw error
throw "Unsupported output.";
}
// Set proof to serialized slate output's proof
this.proof = Common.fromHexString(serializedSlateOutput["proof"]);
// Break
break;
// Version Slatepack
case Slate.VERSION_SLATEPACK:
// Get bit reader
var bitReader = serializedSlateOutput;
// Try
try {
// Set commit to serialized slate output's commit
this.commit = bitReader.getBytes(Crypto.COMMIT_LENGTH);
// Check if commit is invalid
if(Secp256k1Zkp.isValidCommit(this.getCommit()) !== true) {
// Throw error
throw "Unsupported output.";
}
// Get serialized slate output's proof length
var proofLength = bitReader.getBits(SlateOutput.COMPACT_PROOF_LENGTH_LENGTH);
// Check if serialized slate output's proof length isn't supported
if(proofLength !== Crypto.PROOF_LENGTH) {
// Throw error
throw "Unsupported output.";
}
// Set proof to serialized slate output's proof
this.proof = bitReader.getBytes(proofLength);
}
// Catch errors
catch(error) {
// Throw error
throw "Unsupported output.";
}
// Break
break;
// Version four
case Slate.VERSION_FOUR.toFixed():
// Check if serialized slate output is binary
if(serializedSlateOutput instanceof BitReader === true) {
// Get bit reader
var bitReader = serializedSlateOutput;
// Try
try {
// Set features to serialized slate output's features
this.features = bitReader.getBytes(1)[0];
// Check if serialized slate output's features isn't supported
if(this.getFeatures() !== SlateOutput.PLAIN_FEATURES) {
// Throw error
throw "Unsupported output.";
}
// Set commit to serialized slate output's commit
this.commit = bitReader.getBytes(Crypto.COMMIT_LENGTH);
// Check if serialized slate output's commit isn't supported
if(Secp256k1Zkp.isValidCommit(this.getCommit()) !== true) {
// Throw error
throw "Unsupported output.";
}
// Get serialized slate output's proof length
var proofLength = new BigNumber(Common.HEX_PREFIX + Common.toHexString(bitReader.getBytes(Common.BYTES_IN_A_UINT64)));
// Check if serialized slate output's proof length is invalid
if(proofLength.isEqualTo(Crypto.PROOF_LENGTH) === false) {
// Throw error
throw "Unsupported output.";
}
// Set proof to serialized slate output's proof
this.proof = bitReader.getBytes(proofLength.toNumber());
}
// Catch errors
catch(error) {
// Throw error
throw "Unsupported output.";
}
}
// Otherwise
else {
// Check if serialized slate output's features isn't supported
if("f" in serializedSlateOutput === true && ((Common.isNumberString(serializedSlateOutput["f"]) === false && serializedSlateOutput["f"] instanceof BigNumber === false) || (new BigNumber(serializedSlateOutput["f"])).isEqualTo(SlateOutput.PLAIN_FEATURES) === false)) {
// Throw error
throw "Unsupported output.";
}
// Set features to serialized slate output's features
this.features = ("f" in serializedSlateOutput === true) ? (new BigNumber(serializedSlateOutput["f"])).toNumber() : SlateOutput.PLAIN_FEATURES;
// Check if serialized slate output's commit isn't supported
if("c" in serializedSlateOutput === false || Common.isHexString(serializedSlateOutput["c"]) === false || Common.hexStringLength(serializedSlateOutput["c"]) !== Crypto.COMMIT_LENGTH || Secp256k1Zkp.isValidCommit(Common.fromHexString(serializedSlateOutput["c"])) !== true) {
// Throw error
throw "Unsupported output.";
}
// Set commit to serialized slate output's commit
this.commit = Common.fromHexString(serializedSlateOutput["c"]);
// Check if serialized slate output's proof isn't supported
if("p" in serializedSlateOutput === false || Common.isHexString(serializedSlateOutput["p"]) === false || Common.hexStringLength(serializedSlateOutput["p"]) !== Crypto.PROOF_LENGTH) {
// Throw error
throw "Unsupported output.";
}
// Set proof to serialized slate output's proof
this.proof = Common.fromHexString(serializedSlateOutput["p"]);
}
// Break
break;
// Default
default:
// Throw error
throw "Unsupported output.";
}
// Check if proof failed to be verified
if(this.verifyProof() === false) {
// Throw error
throw "Unsupported output.";
}
}
// Verify proof
verifyProof() {
// Check if proof verifies the commit
return Secp256k1Zkp.verifyBulletproof(this.getProof(), this.getCommit(), new Uint8Array([])) === true;
}
// Features to text
static featuresToText(features) {
// Check features
switch(features) {
// Plain features
case SlateOutput.PLAIN_FEATURES:
// Return plain text
return "Plain";
// Coinbase features
case SlateOutput.COINBASE_FEATURES:
// Return coinbase text
return "Coinbase";
// Default
default:
// Return unsupported features
return SlateOutput.UNSUPPORTED_FEATURES;
}
}
// Text to feature
static textToFeatures(text) {
// Check text
switch(text) {
// Plain text
case "Plain":
// Return plain features
return SlateOutput.PLAIN_FEATURES;
// Coinbase text
case "Coinbase":
// Return coinbase features
return SlateOutput.COINBASE_FEATURES;
// Default
default:
// Return unsupported features
return SlateOutput.UNSUPPORTED_FEATURES;
}
}
// Plain features
static get PLAIN_FEATURES() {
// Return plain features
return 0;
}
// Coinbase features
static get COINBASE_FEATURES() {
// Return coinbase features
return SlateOutput.PLAIN_FEATURES + 1;
}
// Unsupported features
static get UNSUPPORTED_FEATURES() {
// Return unsupported features
return SlateOutput.COINBASE_FEATURES + 1;
}
// Compact proof length length
static get COMPACT_PROOF_LENGTH_LENGTH() {
// Return compact proof length length
return 10;
}
}
// Main function
// Set global object's slate output
globalThis["SlateOutput"] = SlateOutput;