PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-lib-mpc/src/tss/ecdsa-dkls

Просмотр файла: dsg.ts

import { Message, SignSessionOTVariant } from '@silencelaboratories/dkls-wasm-ll-node';
import { DeserializedBroadcastMessage, DeserializedDklsSignature, DeserializedMessages, DsgState } from './types';
import { decode } from 'cbor-x';

type NodeWasmer = typeof import('@silencelaboratories/dkls-wasm-ll-node');
type WebWasmer = typeof import('@silencelaboratories/dkls-wasm-ll-web');
type BundlerWasmer = typeof import('@silencelaboratories/dkls-wasm-ll-bundler');

type DklsWasm = NodeWasmer | WebWasmer | BundlerWasmer;

export class Dsg {
  protected dsgSession: SignSessionOTVariant | undefined;
  protected dsgSessionBytes: Uint8Array;
  private _signature: DeserializedDklsSignature | undefined;
  protected keyShareBytes: Buffer;
  protected messageHash: Buffer;
  protected derivationPath: string;
  protected partyIdx: number;
  protected dsgState: DsgState = DsgState.Uninitialized;
  protected dklsWasm: DklsWasm | null;

  constructor(
    keyShare: Buffer,
    partyIdx: number,
    derivationPath: string,
    messageHash: Buffer,
    dklsWasm?: BundlerWasmer
  ) {
    this.partyIdx = partyIdx;
    this.keyShareBytes = keyShare;
    this.derivationPath = derivationPath;
    this.messageHash = messageHash;
    this.dklsWasm = dklsWasm ?? null;
  }

  private _restoreSession() {
    if (!this.dsgSession) {
      this.dsgSession = this.getDklsWasm().SignSessionOTVariant.fromBytes(this.dsgSessionBytes);
    }
  }

  private _deserializeState() {
    if (!this.dsgSession) {
      throw Error('Session not intialized');
    }
    const round = decode(this.dsgSession.toBytes()).round;
    switch (round) {
      case 'WaitMsg1':
        this.dsgState = DsgState.Round1;
        break;
      case 'WaitMsg2':
        this.dsgState = DsgState.Round2;
        break;
      case 'WaitMsg3':
        this.dsgState = DsgState.Round3;
        break;
      case 'Ended':
        this.dsgState = DsgState.Complete;
        break;
      default:
        this.dsgState = DsgState.InvalidState;
        throw Error(`Invalid State: ${round}`);
    }
  }

  private async loadDklsWasm(): Promise<void> {
    if (!this.dklsWasm) {
      this.dklsWasm = await import('@silencelaboratories/dkls-wasm-ll-node');
    }
  }

  private getDklsWasm() {
    if (!this.dklsWasm) {
      throw Error('DKLS wasm not loaded');
    }

    return this.dklsWasm;
  }

  /**
   * Returns the current DSG session as a base64 string.
   * @returns {string} - base64 string of the current DSG session
   */
  getSession(): string {
    return Buffer.from(this.dsgSessionBytes).toString('base64');
  }

  /**
   * Sets the DSG session from a base64 string.
   * @param {string} session - base64 string of the DSG session
   */
  async setSession(session: string): Promise<void> {
    this.dsgSession = undefined;
    if (!this.dklsWasm) {
      await this.loadDklsWasm();
    }
    const sessionBytes = new Uint8Array(Buffer.from(session, 'base64'));
    const round = decode(sessionBytes).round;
    switch (true) {
      case round === 'WaitMsg1':
        this.dsgState = DsgState.Round1;
        break;
      case round === 'WaitMsg2':
        this.dsgState = DsgState.Round2;
        break;
      case round === 'WaitMsg3':
        this.dsgState = DsgState.Round3;
        break;
      case 'WaitMsg4' in round:
        this.dsgState = DsgState.Round4;
        break;
      default:
        throw Error(`Invalid State: ${round}`);
    }
    this.dsgSessionBytes = sessionBytes;
  }

  async init(): Promise<DeserializedBroadcastMessage> {
    if (this.dsgState !== DsgState.Uninitialized) {
      throw Error('DSG session already initialized');
    }
    if (!this.dklsWasm) {
      await this.loadDklsWasm();
    }
    if (
      typeof window !== 'undefined' &&
      /* checks for electron processes */
      !window.process &&
      !window.process?.['type']
    ) {
      /* This is only needed for browsers/web because it uses fetch to resolve the wasm asset for the web */
      const initDkls = await import('@silencelaboratories/dkls-wasm-ll-web');
      await initDkls.default();
    }
    const { Keyshare, SignSessionOTVariant } = this.getDklsWasm();
    const keyShare = Keyshare.fromBytes(this.keyShareBytes);
    if (keyShare.partyId !== this.partyIdx) {
      throw Error(`Party index: ${this.partyIdx} does not match key share partyId: ${keyShare.partyId} `);
    }
    this.dsgSession = new SignSessionOTVariant(keyShare, this.derivationPath);
    try {
      const payload = this.dsgSession.createFirstMessage().payload;
      this._deserializeState();
      this.dsgSessionBytes = this.dsgSession.toBytes();
      this.dsgSession = undefined;
      return {
        payload: payload,
        from: this.partyIdx,
      };
    } catch (e) {
      throw Error(`Error while creating the first message from party ${this.partyIdx}: ${e}`);
    }
  }

  get signature(): DeserializedDklsSignature {
    if (!this._signature) {
      throw Error('Can not request signature. Signature not produced yet.');
    }
    return this._signature;
  }

  /**
   * Ends the DSG session by freeing any heap allocations from wasm. Note that the session is freed if a signature is produced.
   */
  endSession(): void {
    if (this._signature) {
      new Error('Session already ended because combined signature was produced.');
    }
    if (this.dsgSession) {
      this.dsgSession.free();
    }
    this.dsgState = DsgState.Uninitialized;
  }

  /**
   * Proccesses incoming messages to this party in the DKLs DSG protocol and
   * produces messages from this party to other parties for the next round.
   * @param messagesForIthRound - messages to process the current round
   * @returns {DeserializedMessages} - messages to send to other parties for the next round
   */
  handleIncomingMessages(messagesForIthRound: DeserializedMessages): DeserializedMessages {
    let nextRoundMessages: Message[] = [];
    let nextRoundDeserializedMessages: DeserializedMessages = { broadcastMessages: [], p2pMessages: [] };
    this._restoreSession();
    if (!this.dsgSession) {
      throw Error('Session not initialized');
    }
    const { Message } = this.getDklsWasm();
    try {
      if (this.dsgState === DsgState.Round4) {
        this.dsgState = DsgState.Complete;
        const combineResult = this.dsgSession.combine(
          messagesForIthRound.broadcastMessages.map((m) => new Message(m.payload, m.from, undefined))
        );
        this._signature = {
          R: combineResult[0],
          S: combineResult[1],
        };
        return { broadcastMessages: [], p2pMessages: [] };
      } else {
        nextRoundMessages = this.dsgSession.handleMessages(
          messagesForIthRound.broadcastMessages
            .map((m) => new Message(m.payload, m.from, undefined))
            .concat(messagesForIthRound.p2pMessages.map((m) => new Message(m.payload, m.from, m.to)))
        );
      }
      if (this.dsgState === DsgState.Round3) {
        nextRoundMessages = [this.dsgSession.lastMessage(this.messageHash)];
        this.dsgState = DsgState.Round4;
        return {
          broadcastMessages: [
            {
              payload: nextRoundMessages[0].payload,
              from: nextRoundMessages[0].from_id,
              signatureR: decode(this.dsgSession.toBytes()).round.WaitMsg4.r,
            },
          ],
          p2pMessages: [],
        };
      } else {
        // Update round data.
        this._deserializeState();
      }
      nextRoundDeserializedMessages = {
        p2pMessages: nextRoundMessages
          .filter((m) => m.to_id !== undefined)
          .map((m) => {
            if (m.to_id === undefined) {
              throw Error('Invalid P2P message, missing to_id.');
            }
            const p2pReturn = {
              payload: m.payload,
              from: m.from_id,
              to: m.to_id,
            };
            return p2pReturn;
          }),
        broadcastMessages: nextRoundMessages
          .filter((m) => m.to_id === undefined)
          .map((m) => {
            const broadcastReturn = {
              payload: m.payload,
              from: m.from_id,
            };
            return broadcastReturn;
          }),
      };
    } catch (e) {
      if (e.message.startsWith('Abort the protocol and ban')) {
        throw Error(
          'Signing aborted. Please stop all transaction signing from this wallet and contact support@bitgo.com.'
        );
      }
      throw Error(`Error while creating messages from party ${this.partyIdx}, round ${this.dsgState}: ${e}`);
    } finally {
      nextRoundMessages.forEach((m) => m.free());
      // Session is freed when combine is called.
      if (this.dsgState !== DsgState.Complete) {
        this.dsgSessionBytes = this.dsgSession.toBytes();
        this.dsgSession = undefined;
      }
    }
    return nextRoundDeserializedMessages;
  }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!