UNPKG

@river-build/sdk

Version:

For more details, visit the following resources:

218 lines 9.52 kB
/** * @group main */ import _ from 'lodash'; import { unpackEnvelope, makeEvent, publicKeyToAddress } from '../../sign'; import { make_UserPayload_Inception } from '../../types'; import { dlog, bin_fromHexString, bin_toHexString } from '@river-build/dlog'; import { makeUserStreamId, streamIdToBytes } from '../../id'; import { getPublicKey } from 'ethereum-cryptography/secp256k1'; import { ethers } from 'ethers'; import { TEST_ENCRYPTED_MESSAGE_PROPS } from '../testUtils'; import { checkDelegateSig, makeSignerDelegate, makeSignerContext, } from '../../signerContext'; const log = dlog('test:sign'); describe('sign', () => { const keys = [ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', '0123456789012345678901234567890123456789012345678901234567890124', '0123456789012345678901234567890123456789012345678901234567890125', 'aaaa456789012345678901234567890123456789012345678901234567890125', ].map((key) => { const pub = getPublicKey(key); return { privateKey: key, publicKey: pub, address: publicKeyToAddress(pub), }; }); const hash = bin_fromHexString('0x8dc27dbd6fc775e3a05c509c6eb1c63c4ab5bc6e7010bf9a9a80a42ae1ea56b0'); const badHashes = [ bin_fromHexString('0x8dc27dbd6fc775e3a05c509c6eb1c63c4ab5bc6e7010bf9a9a80a42ae1ea56b000'), bin_fromHexString('0x8dc27dbd6fc775e3a05c509c6eb1c63c4ab5bc6e7010bf9a9a80a42ae1ea56'), ]; test('i-need-buffer', () => { const buffer = Buffer.from('hello world', 'ascii'); expect(buffer).toBeInstanceOf(Uint8Array); }); test('delegate-sig', async () => { // one user has two delegates (two devices, A and B) const user = keys[0]; const delegateA = keys[1]; const delegateB = keys[2]; const userWallet = new ethers.Wallet(user.privateKey); const delegateA_wallet = new ethers.Wallet(delegateA.privateKey); const delegateB_wallet = new ethers.Wallet(delegateB.privateKey); const expiryA_none = 0n; const expiryB_valid = BigInt(Date.now() + 10000); const expiry_WRONG = BigInt(Date.now() + 99999); const delegateA_context = await makeSignerContext(userWallet, delegateA_wallet, expiryA_none); const delegateB_context = await makeSignerContext(userWallet, delegateB_wallet, expiryB_valid); const sigA = delegateA_context.delegateSig; log('sigA', bin_toHexString(sigA)); const sigB = delegateB_context.delegateSig; log('sigB', bin_toHexString(sigB)); expect(sigA).not.toEqual(sigB); expect(() => checkDelegateSig({ delegatePubKey: delegateA.publicKey, creatorAddress: user.address, delegateSig: sigA, expiryEpochMs: expiryA_none, })).not.toThrow(); expect(() => checkDelegateSig({ delegatePubKey: delegateB.publicKey, creatorAddress: user.address, delegateSig: sigB, expiryEpochMs: expiryB_valid, })).not.toThrow(); // expect wrong sig (B instead of A) to throw expect(() => checkDelegateSig({ delegatePubKey: delegateB.publicKey, creatorAddress: user.address, delegateSig: sigB, expiryEpochMs: expiryA_none, })).toThrow(); // expect wrong creator address (delegate B instead of user) to throw expect(() => checkDelegateSig({ delegatePubKey: delegateA.publicKey, creatorAddress: delegateB.address, delegateSig: sigA, expiryEpochMs: expiryA_none, })).toThrow(); // expect wrong sig and wrong expiry (B instead of A) to throw expect(() => checkDelegateSig({ delegatePubKey: delegateA.publicKey, creatorAddress: user.address, delegateSig: sigB, expiryEpochMs: expiryB_valid, })).toThrow(); // expict wrong expiry (expiry_WRONG instead of expiryA_none) to throw expect(() => checkDelegateSig({ delegatePubKey: delegateA.publicKey, creatorAddress: user.address, delegateSig: sigA, expiryEpochMs: expiry_WRONG, })).toThrow(); // expict wrong expiry (expiry_WRONG instead of expiryB_valid) to throw expect(() => checkDelegateSig({ delegatePubKey: delegateB.publicKey, creatorAddress: user.address, delegateSig: sigB, expiryEpochMs: expiry_WRONG, })).toThrow(); }); test('delegate-sig-2', async () => { const primary = keys[0]; const primaryWallet = new ethers.Wallet(primary.privateKey); const delegate = keys[1]; const delegateWallet = new ethers.Wallet(delegate.privateKey); log('delegate PublicKey', bin_toHexString(delegate.publicKey)); const context = await makeSignerContext(primaryWallet, delegateWallet, 0n); const delegateSig = context.delegateSig; expect(delegateSig).toBeDefined(); log('OLD delegateSig', bin_toHexString(delegateSig)); expect(() => checkDelegateSig({ delegatePubKey: delegate.publicKey, creatorAddress: primary.address, delegateSig: delegateSig, expiryEpochMs: 0n, })).not.toThrow(); }); test('make-signer-delegate', async () => { const user = keys[0]; const userWallet = new ethers.Wallet(user.privateKey); const { signerContext, delegateWallet } = await makeSignerDelegate(userWallet); expect(signerContext.delegateSig).toBeDefined(); expect(() => checkDelegateSig({ delegatePubKey: delegateWallet.publicKey, creatorAddress: user.address, delegateSig: signerContext.delegateSig, expiryEpochMs: 0n, })).not.toThrow(); }); const makeContext = async (userPrivateKey, delegatePrivateKey) => { const userWallet = new ethers.Wallet(userPrivateKey); if (delegatePrivateKey === undefined) { return { signerPrivateKey: () => userPrivateKey, creatorAddress: bin_fromHexString(userWallet.address), }; } else { const delegateWallet = new ethers.Wallet(delegatePrivateKey); return await makeSignerContext(userWallet, delegateWallet, { days: 1 }); } }; const testParams = [ // direct contexts are not recommended, but they are supported ['direct', () => makeContext(keys[0].privateKey), () => makeContext(keys[1].privateKey)], [ 'delegate', () => makeContext(keys[0].privateKey, keys[1].privateKey), () => makeContext(keys[2].privateKey, keys[3].privateKey), ], ]; test.each(testParams)('sign-and-verify-%s', async (method, c, c2) => { const context = await c(); const context2 = await c2(); const message = { ...TEST_ENCRYPTED_MESSAGE_PROPS, ciphertext: 'Hello, World!', }; const payload = { case: 'channelPayload', value: { content: { case: 'message', value: message, }, }, }; const event = await makeEvent(context, payload, hash); expect(await unpackEnvelope(event, undefined)).toBeDefined(); // Event with same payload from different wallet doesn't match const event2 = await makeEvent(context2, payload, hash); expect(await unpackEnvelope(event2, undefined)).toBeDefined(); expect(event2).not.toEqual(event); await expect(async () => { const e = _.cloneDeep(event); e.hash = event2.hash; await unpackEnvelope(e, undefined); }).rejects.toThrow(); await expect(async () => { const e = _.cloneDeep(event); e.signature = event2.signature; await unpackEnvelope(e, undefined); }).rejects.toThrow(); await expect(async () => { const e = _.cloneDeep(event); e.event = event2.event; await unpackEnvelope(e, undefined); }).rejects.toThrow(); // Event with same payload from the same wallet doesn't match const event3 = await makeEvent(context, payload, hash); expect(await unpackEnvelope(event3, undefined)).toBeDefined(); expect(event3.hash).not.toEqual(event.hash); expect(event3).not.toEqual(event); }); test.each(testParams)('validate-prev-events-%s', async (method, c) => { const userStreamId = makeUserStreamId('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); const context = await c(); expect(await makeEvent(context, make_UserPayload_Inception({ streamId: streamIdToBytes(userStreamId), }))).toBeDefined(); const payload = { case: 'channelPayload', value: { content: { case: 'message', value: { ...TEST_ENCRYPTED_MESSAGE_PROPS, ciphertext: 'Hello, World!' }, }, }, }; expect(await makeEvent(context, payload, hash)).toBeDefined(); for (const h of badHashes) { await expect(makeEvent(context, payload, h)).rejects.toThrow(); } }); }); //# sourceMappingURL=sign.test.js.map