PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-coin-algo/test/unit/lib/transactionBuilder

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

import { coins } from '@bitgo/statics';
import algosdk from 'algosdk';
import assert from 'assert';
import should from 'should';
import sinon, { assert as SinonAssert } from 'sinon';
import { Transaction } from '../../../../src/lib';
import { KeyRegistrationBuilder } from '../../../../src/lib/keyRegistrationBuilder';

import * as AlgoResources from '../../../fixtures/resources';

class StubTransactionBuilder extends KeyRegistrationBuilder {
  getTransaction(): Transaction {
    return this._transaction;
  }
}

describe('Algo KeyRegistration Builder', () => {
  const {
    networks: { testnet },
  } = AlgoResources;
  const { genesisHash, genesisID } = testnet;
  let builder: StubTransactionBuilder;

  const sender = AlgoResources.accounts.account1;
  const { rawTx } = AlgoResources;

  beforeEach(() => {
    const config = coins.get('algo');
    builder = new StubTransactionBuilder(config);
  });

  describe('setter validation', () => {
    it('should validate voteKey, is set and is a valid string', () => {
      should.doesNotThrow(() => builder.voteKey(sender.voteKey));
    });

    it('should validate selection key, is set and is a valid string', () => {
      should.doesNotThrow(() => builder.selectionKey(sender.selectionKey));
    });

    it('should validate voteFirst is gt than 0', () => {
      const spy = sinon.spy(builder, 'validateValue');
      assert.throws(
        () => builder.voteFirst(-1),
        (e: Error) => e.message === 'Value cannot be less than zero'
      );
      should.doesNotThrow(() => builder.voteFirst(15));
      SinonAssert.calledTwice(spy);
    });

    it('should validate voteLast is gt than 0', () => {
      const validateValueSpy = sinon.spy(builder, 'validateValue');
      builder.voteFirst(1);
      assert.throws(
        () => builder.voteLast(-1),
        (e: Error) => e.message === 'Value cannot be less than zero'
      );
      should.doesNotThrow(() => builder.voteLast(15));
      SinonAssert.calledThrice(validateValueSpy);
    });

    it('should validate vote Key Dilution', () => {
      const validateValueSpy = sinon.spy(builder, 'validateValue');
      builder.voteFirst(5).voteLast(18);
      should.doesNotThrow(() => builder.voteKeyDilution(2));
      SinonAssert.calledThrice(validateValueSpy);
    });
  });

  describe('transaction validation', () => {
    beforeEach(() => {
      builder.sender({ address: sender.address }).fee({ fee: '1000' }).firstRound(1).lastRound(100).testnet();
    });
    it('should validate an online transaction', () => {
      builder.voteKey(sender.voteKey).selectionKey(sender.selectionKey).voteFirst(1).voteLast(100).voteKeyDilution(9);
      should.doesNotThrow(() => builder.validateTransaction(builder.getTransaction()));
    });
  });

  describe('build key registration transaction', () => {
    const requiredFieldErrorRegEx =
      /Transaction validation failed: "(voteLast|voteKey|voteFirst|voteKeyDilution|selectionKey)" is required/;

    it('should build an online key registration transaction without stateProof param', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .selectionKey(sender.selectionKey)
        .voteFirst(1)
        .voteLast(100)
        .voteKeyDilution(9)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });

    it('should build an online key registration transaction with stateProof Param', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .selectionKey(sender.selectionKey)
        .voteFirst(1)
        .voteLast(100)
        .voteKeyDilution(9)
        .stateProofKey(sender.stateProofKey)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
      should.deepEqual(txJson.stateProofKey.toString('base64'), sender.stateProofKey);
    });

    it('should build an online keyreg transaction with malformated stateProof Param', async () => {
      const malformatedStateProofKey = '0x' + '0'.repeat(64);

      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .selectionKey(sender.selectionKey)
        .voteFirst(1)
        .voteLast(100)
        .voteKeyDilution(9);
      assert.throws(() => builder.stateProofKey(malformatedStateProofKey), 'Error: Invalid base64 string');
    });

    it('should build an offline key registration transaction', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(false);
      builder.sign({ key: sender.prvKey });
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.deepEqual(txJson.voteKey, undefined);
      should.deepEqual(txJson.selectionKey, undefined);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, undefined);
      should.deepEqual(txJson.voteLast, undefined);
      should.deepEqual(txJson.voteKeyDilution, undefined);
      should.deepEqual(txJson.nonParticipation, undefined);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });

    it('should build a key registration transaction with non participation', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(true);
      builder.sign({ key: sender.prvKey });
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.deepEqual(txJson.voteKey, undefined);
      should.deepEqual(txJson.selectionKey, undefined);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, undefined);
      should.deepEqual(txJson.voteLast, undefined);
      should.deepEqual(txJson.voteKeyDilution, undefined);
      should.deepEqual(txJson.nonParticipation, true);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });

    it('build an offline key registration transaction should thrown an error when it hasfee', async () => {
      builder.sender({ address: sender.address }).firstRound(1).lastRound(100).testnet().numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "fee" is required');
    });

    it('build an offline key registration transaction should thrown an error when it has voteLast', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteLast(100)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith(requiredFieldErrorRegEx);
    });

    it('build an offline key registration transaction should thrown an error when it has selectionKey', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .selectionKey(sender.selectionKey)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith(requiredFieldErrorRegEx);
    });

    it('build an offline key registration transaction should thrown an error when it has voteKeyDilution', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKeyDilution(9)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith(requiredFieldErrorRegEx);
    });

    it('build an offline key registration transaction should thrown an error when it has voteFirst', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteFirst(1)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith(requiredFieldErrorRegEx);
    });

    it('build an offline key registration transaction should thrown an error when it has voteKey', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .testnet()
        .numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith(requiredFieldErrorRegEx);
    });

    it('build an offline key registration transaction without non participation should thrown an error when it has not first round', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .lastRound(100)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(false);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "firstRound" is required');
    });

    it('build an offline key registration transaction without non participation should thrown an error when it has not last round', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(false);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "lastRound" is required');
    });

    it('build an offline key registration transaction without non participation should thrown an error when it has not testnet set', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .numberOfSigners(1)
        .nonParticipation(false);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "genesisHash" is required');
    });

    it('should build an unsigned offline key registration transaction', async () => {
      builder.sender({ address: sender.address }).fee({ fee: '1000' }).firstRound(1).lastRound(100).testnet();
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });

    it('build a key registration transaction with non participation should thrown an error when it has not fee', async () => {
      builder
        .sender({ address: sender.address })
        .firstRound(1)
        .lastRound(100)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(true);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "fee" is required');
    });

    it('build a key registration transaction with non participation should thrown an error when it has not first round', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .lastRound(100)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(true);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "firstRound" is required');
    });

    it('build a key registration transaction with non participation should thrown an error when it has not last round', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .testnet()
        .numberOfSigners(1)
        .nonParticipation(true);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "lastRound" is required');
    });

    it('build a key registration transaction with non participation should thrown an error when it has not testnet set', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .numberOfSigners(1)
        .nonParticipation(true);
      builder.sign({ key: sender.prvKey });
      await builder.build().should.be.rejectedWith('Transaction validation failed: "genesisHash" is required');
    });

    it('should build an unsigned key registration transaction', async () => {
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .selectionKey(sender.selectionKey)
        .voteFirst(1)
        .voteLast(100)
        .voteKeyDilution(9)
        .testnet();
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });

    it('should build a trx from an unsigned raw transaction', async () => {
      builder.from(rawTx.keyReg.unsigned);
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
    });

    it('should sign from raw unsigned tx', async () => {
      builder.from(rawTx.keyReg.unsigned);
      builder.numberOfSigners(1);
      builder.sign({ key: sender.prvKey });
      const tx = await builder.build();
      should.deepEqual(Buffer.from(tx.toBroadcastFormat()).toString('hex'), AlgoResources.rawTx.keyReg.signed);
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, sender.address);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
    });
  });

  describe('build multi-sig key registration transaction', () => {
    it('should build a msig registration transaction', async () => {
      const msigAddress = algosdk.multisigAddress({
        version: 1,
        threshold: 2,
        addrs: [AlgoResources.accounts.account1.address, AlgoResources.accounts.account3.address],
      });
      builder
        .sender({ address: sender.address })
        .fee({ fee: '1000' })
        .firstRound(1)
        .lastRound(100)
        .voteKey(sender.voteKey)
        .selectionKey(sender.selectionKey)
        .voteFirst(1)
        .voteLast(100)
        .voteKeyDilution(9)
        .testnet()
        .numberOfSigners(2)
        .setSigners([AlgoResources.accounts.account1.address, AlgoResources.accounts.account3.address])
        .sign({ key: AlgoResources.accounts.account1.prvKey });
      builder.sign({ key: AlgoResources.accounts.account3.prvKey });
      const tx = await builder.build();
      const txJson = tx.toJson();
      should.doesNotThrow(() => builder.validateKey({ key: txJson.voteKey }));
      should.deepEqual(txJson.voteKey.toString('base64'), sender.voteKey);
      should.doesNotThrow(() => builder.validateKey({ key: txJson.selectionKey }));
      should.deepEqual(txJson.selectionKey.toString('base64'), sender.selectionKey);
      should.deepEqual(txJson.from, msigAddress);
      should.deepEqual(txJson.firstRound, 1);
      should.deepEqual(txJson.lastRound, 100);
      should.deepEqual(txJson.voteFirst, 1);
      should.deepEqual(txJson.voteLast, 100);
      should.deepEqual(txJson.voteKeyDilution, 9);
      should.deepEqual(txJson.genesisID, genesisID.toString());
      should.deepEqual(txJson.genesisHash.toString('base64'), genesisHash);
    });
  });
});

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


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