PHP WebShell

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

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

import type { KeygenSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
import { decode, encode } from 'cbor-x';
import { Secp256k1Curve } from '../../curves';
import { bigIntToBufferBE } from '../../util';
import { DeserializedBroadcastMessage, DeserializedMessages, DkgState, ReducedKeyShare, RetrofitData } from './types';

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 Dkg {
  protected dkgSession: KeygenSession | undefined;
  protected dkgSessionBytes: Uint8Array;
  protected dkgKeyShare: Keyshare;
  protected keyShareBuff: Buffer;
  protected n: number;
  protected t: number;
  protected seed: Buffer | undefined;
  protected chainCodeCommitment: Uint8Array | undefined;
  protected partyIdx: number;
  protected dkgState: DkgState = DkgState.Uninitialized;
  protected dklsKeyShareRetrofitObject: Keyshare | undefined;
  protected retrofitData: RetrofitData | undefined;
  protected dklsWasm: DklsWasm | null;

  constructor(
    n: number,
    t: number,
    partyIdx: number,
    seed?: Buffer,
    retrofitData?: RetrofitData,
    dklsWasm?: BundlerWasmer
  ) {
    this.n = n;
    this.t = t;
    this.partyIdx = partyIdx;
    this.chainCodeCommitment = undefined;
    this.retrofitData = retrofitData;
    this.seed = seed;
    this.dklsWasm = dklsWasm ?? null;
  }

  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;
  }

  private _restoreSession() {
    if (!this.dkgSession) {
      this.dkgSession = this.getDklsWasm().KeygenSession.fromBytes(this.dkgSessionBytes);
    }
  }

  private _createDKLsRetrofitKeyShare() {
    if (this.retrofitData) {
      if (!this.retrofitData.xShare.y || !this.retrofitData.xShare.chaincode || !this.retrofitData.xShare.x) {
        throw Error('xShare must have a public key, private share value, and a chaincode.');
      }
      const xiList: Array<Array<number>> = [];
      for (let i = 0; i < this.n; i++) {
        xiList.push(Array.from(bigIntToBufferBE(BigInt(i + 1), 32)));
      }
      const secp256k1 = new Secp256k1Curve();
      const dklsKeyShare = {
        total_parties: this.n,
        threshold: this.t,
        rank_list: new Array(this.n).fill(0),
        party_id: this.partyIdx,
        public_key: Array.from(Buffer.from(this.retrofitData.xShare.y, 'hex')),
        root_chain_code: Array.from(Buffer.from(this.retrofitData.xShare.chaincode, 'hex')),
        final_session_id: Array(32).fill(0),
        seed_ot_receivers: new Array(this.n - 1).fill(Array(32832).fill(0)),
        seed_ot_senders: new Array(this.n - 1).fill(Array(32768).fill(0)),
        sent_seed_list: [Array(32).fill(0)],
        rec_seed_list: [Array(32).fill(0)],
        s_i: Array.from(Buffer.from(this.retrofitData.xShare.x, 'hex')),
        // big_s_list is now created internally during the protocol so isn't needed here, however a valid KeyShare object needs to have it.
        // a dummy public key is used to fill big_s_list.
        big_s_list: new Array(this.n).fill(
          Array.from(bigIntToBufferBE(secp256k1.basePointMult(BigInt('0x' + this.retrofitData.xShare.x))))
        ),
        x_i_list: this.retrofitData.xiList ? this.retrofitData.xiList : xiList,
      };
      this.dklsKeyShareRetrofitObject = this.getDklsWasm().Keyshare.fromBytes(encode(dklsKeyShare));
    }
  }

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

  async initDkg(): Promise<DeserializedBroadcastMessage> {
    if (!this.dklsWasm) {
      await this.loadDklsWasm();
    }
    if (this.t > this.n || this.partyIdx >= this.n) {
      throw Error('Invalid parameters for DKG');
    }
    if (this.dkgState != DkgState.Uninitialized) {
      throw Error('DKG session already initialized');
    }
    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();
    }
    this._createDKLsRetrofitKeyShare();
    if (this.seed && this.seed.length !== 32) {
      throw Error(`Seed should be 32 bytes, got ${this.seed.length}.`);
    }
    const { KeygenSession } = this.getDklsWasm();
    if (this.dklsKeyShareRetrofitObject) {
      this.dkgSession = this.seed
        ? KeygenSession.initKeyRotation(this.dklsKeyShareRetrofitObject, new Uint8Array(this.seed))
        : KeygenSession.initKeyRotation(this.dklsKeyShareRetrofitObject);
    } else {
      this.dkgSession = this.seed
        ? new KeygenSession(this.n, this.t, this.partyIdx, new Uint8Array(this.seed))
        : new KeygenSession(this.n, this.t, this.partyIdx);
    }
    try {
      const payload = this.dkgSession.createFirstMessage().payload;
      this._deserializeState();
      return {
        payload: payload,
        from: this.partyIdx,
      };
    } catch (e) {
      throw Error(`Error while creating the first message from party ${this.partyIdx}: ${e}`);
    }
  }

  getKeyShare(): Buffer {
    if (!this.keyShareBuff) {
      throw Error('Can not get key share, DKG is not complete yet.');
    }
    return this.keyShareBuff;
  }

  getReducedKeyShare(): Buffer {
    if (!this.keyShareBuff) {
      throw Error('Can not get key share, DKG is not complete yet.');
    }
    const decodedKeyshare = decode(this.keyShareBuff);
    const reducedKeyShare: ReducedKeyShare = {
      bigSList: decodedKeyshare.big_s_list,
      xList: decodedKeyshare.x_i_list,
      rootChainCode: decodedKeyshare.root_chain_code,
      prv: decodedKeyshare.s_i,
      pub: decodedKeyshare.public_key,
    };
    const encodedKeyShare = encode(reducedKeyShare);
    return encodedKeyShare;
  }

  handleIncomingMessages(messagesForIthRound: DeserializedMessages): DeserializedMessages {
    let nextRoundMessages: Message[] = [];
    let nextRoundDeserializedMessages: DeserializedMessages = { broadcastMessages: [], p2pMessages: [] };
    this._restoreSession();
    if (!this.dkgSession) {
      throw Error('Session not initialized');
    }
    const { Message } = this.getDklsWasm();
    try {
      if (this.dkgState === DkgState.Round3) {
        const commitmentsUnsorted = messagesForIthRound.p2pMessages
          .map((m) => {
            return { from: m.from, commitment: m.commitment };
          })
          .concat([{ from: this.partyIdx, commitment: this.chainCodeCommitment }]);
        const commitmentsSorted = commitmentsUnsorted
          .sort((a, b) => {
            return a.from - b.from;
          })
          .map((c) => c.commitment);
        nextRoundMessages = this.dkgSession.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))),
          commitmentsSorted
        );
      } else {
        nextRoundMessages = this.dkgSession.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))),
          undefined
        );
      }
      if (this.dkgState === DkgState.Round4) {
        this.dkgKeyShare = this.dkgSession.keyshare();
        this.keyShareBuff = Buffer.from(this.dkgKeyShare.toBytes());
        this.dkgKeyShare.free();
        if (this.dklsKeyShareRetrofitObject) {
          this.dklsKeyShareRetrofitObject.free();
        }
        this.dkgState = DkgState.Complete;
        return { broadcastMessages: [], p2pMessages: [] };
      } else {
        // Update round data.
        this._deserializeState();
      }
      if (this.dkgState === DkgState.Round2) {
        this.chainCodeCommitment = this.dkgSession.calculateChainCodeCommitment();
      }
      nextRoundDeserializedMessages = {
        p2pMessages: nextRoundMessages
          .filter((m) => m.to_id !== undefined)
          .map((m) => {
            const p2pReturn = {
              payload: m.payload,
              from: m.from_id,
              to: m.to_id!,
              commitment: this.chainCodeCommitment,
            };
            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) {
      throw Error(`Error while creating messages from party ${this.partyIdx}, round ${this.dkgState}: ${e}`);
    } finally {
      nextRoundMessages.forEach((m) => m.free());
      // Session is freed when keyshare is called.
      if (this.dkgState !== DkgState.Complete) {
        this.dkgSessionBytes = this.dkgSession.toBytes();
        this.dkgSession = undefined;
      }
    }
    return nextRoundDeserializedMessages;
  }
}

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


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