Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#144 - Extract NostrEventId instead Npub #16

Merged
merged 4 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions backend/src/angor/AngorTransactionDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,19 @@ export class AngorTransactionDecoder {
const founderKeyHashInt = this.hashToInt(founderKeyHash);
const projectIdDerivation = this.getProjectIdDerivation(founderKeyHashInt);
const projectId = this.getProjectId(projectIdDerivation);
const nostrPubKey = this.getNostrPubKey();
const nostrEventId = this.getNostrEventId();
const addressOnFeeOutput = this.getAddressOnFeeOutput();
const txid = this.transaction.getId();

// Store Angor project in the DB.
await this.storeProjectInfo(
projectId,
nostrPubKey,
addressOnFeeOutput,
transactionStatus,
founderKeyHex,
txid,
createdOnBlock
createdOnBlock,
nostrEventId
);

// If transaction is confirmed (in the block), update statuses
Expand Down Expand Up @@ -224,23 +224,27 @@ export class AngorTransactionDecoder {
const chunks = bitcoinJS.script.toASM(decompiled).split(' ');

// Throw an error if the chunks amount is incorrect.
if (chunks.length !== 3) {
if (chunks.length !== 4) {
throw new Error(`${errorBase} Wrong chunk amount.`);
}

// Throw an error if the first chunk is not OP_RETURN.
if (chunks[0] !== 'OP_RETURN') {
throw new Error(`${errorBase} Wrong first chunk.`);
throw new Error(`${errorBase} Wrong OP_RETURN chunk.`);
}

// Throw an error if the byte length of the second chunk is not 33.
if (Buffer.from(chunks[1], 'hex').byteLength !== 33) {
throw new Error(`${errorBase} Wrong second chunk.`);
throw new Error(`${errorBase} Wrong founder pubkey chunk.`);
}

// Throw an error if the byte length of the third chunk is not 32.
if (Buffer.from(chunks[2], 'hex').byteLength !== 32) {
throw new Error(`${errorBase} Wrong third chunk.`);
if (Buffer.from(chunks[2], 'hex').byteLength !== 2) {
throw new Error(`${errorBase} Wrong key type chunk.`);
}

if (Buffer.from(chunks[3], 'hex').byteLength !== 32) {
throw new Error(`${errorBase} Wrong nostr event ID chunk.`);
}

// Remove the first chunk (OP_RETURN) as it is not useful anymore.
Expand Down Expand Up @@ -405,13 +409,13 @@ export class AngorTransactionDecoder {
}

/**
* Sets Nostr public key.
* @returns - string representing Nostr public key of Angor project.
* Sets Nostr event id.
* @return - string representing the Nostr event ID associated with the current Angor project.
* @private
*/
private getNostrPubKey(): string {
private getNostrEventId(): string {
const chunks = this.decompileProjectCreationOpReturnScript();

return chunks[1];
return chunks[2];
}

/**
Expand All @@ -434,21 +438,21 @@ export class AngorTransactionDecoder {
*/
private async storeProjectInfo(
projectId: string,
nostrPubKey: string,
addressOnFeeOutput: string,
transactionStatus: AngorTransactionStatus,
founderKey: string,
txid: string,
createdOnBlock?: number
createdOnBlock?: number,
nostrEventId?: string
): Promise<void> {
await AngorProjectRepository.$setProject(
projectId,
nostrPubKey,
addressOnFeeOutput,
transactionStatus,
founderKey,
txid,
createdOnBlock
createdOnBlock,
nostrEventId
);
}

Expand Down
37 changes: 18 additions & 19 deletions backend/src/angor/tests/AngorTransactionDecoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@ import {
import * as bitcoinJS from 'bitcoinjs-lib';
import AngorProjectRepository from '../../repositories/AngorProjectRepository';
import AngorInvestmentRepository from '../../repositories/AngorInvestmentRepository';
import DB from '../../database';

describe('AngorTransactionDecoder', () => {
describe('Decoding transaction for Angor project creation', () => {
const data = {
transactionHex:
'0100000000010138de40ff6a3d27c33d5b84edf8f35911d819c9c547689cc4da6c5603bc3b26990000000000ffffffff0310270000000000001600144282ccfe323dbba535ccdfc8b66aeeb0bd7dd95b0000000000000000446a210352eb18befb145fef4b5c24513608183d7c3d8004d5fcad9e0e6dc2e89a0af6ae20c749d81fc42037e5b9f7b32ae266cbce75019ce6771be6877d3c509e97e39c14669b052a0100000016001431e5c2bdb361b68eb243ec7dbd46bd7cf71a1d86024730440220075dd33e3809a58423257bf23f5632e305387c283f48dfdce7f355787be33d8002201072fa636fb6bad35ac9d7f8e35fb41eea47de8b38346dc4f123260e9c74c3b401210385143acd30d6d05a5bf35ef1028ce4c50eadffa14670716976007fec5ed3e58500000000',
'01000000000101899c7a384fe0e17dfd3d56fcf6bfff796b5d0f40ecb5bc513a92e891bb2018af0200000000ffffffff031127000000000000160014e6285b56cb7cd9a51af2f28cb02762b5298c98db0000000000000000476a2102070d174561688500aac733116dbe70c5ab7480559d25e1c040f480491870c8ba020100200f2d8db8568bd3e12bdab1faa217fffc80459053967eff8bde0a65f14e2b7079d542052a01000000160014ae63769a0b0a5f69b3be5d1e6bb4b00d15eff7d0024830450221008e797faa2ef8c3e91ff03f4a47e76740cbadf4b5061d0508ffd89ab869891cb2022050c624530f5c6afbe6ec0dcba4c81287431595f89370225f7d12d3866cc0499f01210396d79f9c4a836defed971668ea51ed50495d5e2d205da2590e7f6600af03f8c800000000',
founderKeyHex:
'0352eb18befb145fef4b5c24513608183d7c3d8004d5fcad9e0e6dc2e89a0af6ae',
'02070d174561688500aac733116dbe70c5ab7480559d25e1c040f480491870c8ba',
founderKeyHashHex:
'cacedcee9bc28a37b36718ea210fcf7caac182cdb66cc17fafb6027478a221c8',
founderKeyHashInt: 2023891400,
projectIdDeriviation: 1011945700,
projectId: 'angor1qg2pvel3j8ka62dwvmlytv6hwkz7hmk2mms7qll',
nPub: 'c749d81fc42037e5b9f7b32ae266cbce75019ce6771be6877d3c509e97e39c14',
addressOnFeeOutput: 'tb1qg2pvel3j8ka62dwvmlytv6hwkz7hmk2m0ncfee',
txid: '00b78119bb6eff9f64b2d29948ddd830f405b18dfdb802a3ec2df4eacfcd1f40',
'68828edc1c6312c915c8967475be57f42d45764105af8216f2da7170d033240a',
founderKeyHashInt: 3493012490,
projectIdDeriviation: 1746506245,
projectId: 'angor1qzkfpckm2vnhdvfcwr7vdhwt7ns3rd95gr0age0',
nostrEventId: '0f2d8db8568bd3e12bdab1faa217fffc80459053967eff8bde0a65f14e2b7079',
addressOnFeeOutput: 'tb1quc59k4kt0nv62xhj72xtqfmzk55cexxmae8lyc',
txid: '0d28976a42bf7618ad9470cf0202e2eb06d6072e75e139eab012a160b7b480aa',
blockHeight: 40000,
};

Expand Down Expand Up @@ -154,11 +153,11 @@ describe('AngorTransactionDecoder', () => {

const {
projectId,
nPub,
addressOnFeeOutput,
founderKeyHex,
txid,
blockHeight,
nostrEventId
} = data;

await angorDecoder.decodeAndStoreProjectCreationTransaction(
Expand All @@ -168,12 +167,12 @@ describe('AngorTransactionDecoder', () => {

expect(setProjectSpy).toHaveBeenCalledWith(
projectId,
nPub,
addressOnFeeOutput,
transactionStatus,
founderKeyHex,
txid,
blockHeight
blockHeight,
nostrEventId
);

expect(updateInvestmentStatusSpy).toHaveBeenCalledWith(
Expand All @@ -188,10 +187,10 @@ describe('AngorTransactionDecoder', () => {
jest.restoreAllMocks();
});

it('should return 2 chunks', () => {
it('should return 3 chunks', () => {
const chunks = angorDecoder['decompileProjectCreationOpReturnScript']();

expect(chunks.length).toEqual(2);
expect(chunks.length).toEqual(3);

chunks.forEach((chunk) => expect(typeof chunk).toEqual('string'));
});
Expand Down Expand Up @@ -290,9 +289,9 @@ describe('AngorTransactionDecoder', () => {
});
});

describe('getNostrPubKey', () => {
it('should return Nostr public key', () => {
expect(angorDecoder['getNostrPubKey']()).toEqual(data.nPub);
describe('getNostrEventId', () => {
it('should return nostrEventId', () => {
expect(angorDecoder['getNostrEventId']()).toEqual(data.nostrEventId);
});
});
});
Expand Down Expand Up @@ -339,10 +338,10 @@ describe('AngorTransactionDecoder', () => {
.mockImplementation(() =>
Promise.resolve({
founder_key: '',
npub: '',
id: '',
created_on_block: 1,
txid: '',
nostr_event_id: ''
})
);

Expand Down
8 changes: 4 additions & 4 deletions backend/src/api/angor/angor.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { fetchAngorVouts, computeAdvancedStats, computeStatsTally } from './ango

interface ProjectsPayloadItem {
founderKey: string;
nostrPubKey: string;
projectIdentifier: string;
createdOnBlock: number;
trxId: string;
nostrEventId: string;
}
interface ProjectPayloadItem {
founderKey: string;
nostrPubKey: string;
projectIdentifier: string;
createdOnBlock: number;
trxId: string;
nostrEventId: string;
totalInvestmentsCount: number;
}

Expand Down Expand Up @@ -150,10 +150,10 @@ class AngorRoutes {
const payload: ProjectsPayloadItem[] = projects
.map((project) => ({
founderKey: project.founder_key,
nostrPubKey: project.npub,
projectIdentifier: project.id,
createdOnBlock: project.created_on_block,
trxId: project.txid,
nostrEventId: project.nostr_event_id
}))
.sort(
(p1: ProjectsPayloadItem, p2: ProjectsPayloadItem) =>
Expand Down Expand Up @@ -197,10 +197,10 @@ class AngorRoutes {
// Adjust DB data to confirm ProjectsPayloadItem interface.
const payload: ProjectPayloadItem = {
founderKey: project.founder_key,
nostrPubKey: project.npub,
projectIdentifier: project.id,
createdOnBlock: project.created_on_block,
trxId: project.txid,
nostrEventId: project.nostr_event_id,
totalInvestmentsCount: project.investments_count,
};

Expand Down
3 changes: 1 addition & 2 deletions backend/src/api/database-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class DatabaseMigration {
*/
public async $initializeOrMigrateDatabase(): Promise<void> {
logger.debug('MIGRATIONS: Running migrations');

await this.$printDatabaseVersion();

// First of all, if the `state` database does not exist, create it so we can track migration version
Expand Down Expand Up @@ -1285,11 +1284,11 @@ class DatabaseMigration {
private getCreateAngorProjectsTableQuery(): string {
return `CREATE TABLE IF NOT EXISTS angor_projects (
id CHAR(45) NOT NULL,
npub CHAR(64) NOT NULL,
address_on_fee_output CHAR(51) NOT NULL,
creation_transaction_status VARCHAR(10) NOT NULL,
created_on_block INT(10),
txid VARCHAR(64) NOT NULL,
nostr_event_id VARCHAR(64),
founder_key VARCHAR(66) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
Expand Down
19 changes: 10 additions & 9 deletions backend/src/repositories/AngorProjectRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { AngorTransactionStatus } from '../angor/AngorTransactionDecoder';

export interface Project {
founder_key: string;
npub: string;
id: string;
created_on_block: number;
txid: string;
nostr_event_id: string;
}

interface ProjectWithInvestmentsCount extends Project {
Expand Down Expand Up @@ -36,34 +36,35 @@ class AngorProjectRepository {
/**
* Stores Angor project into the DB.
* @param id - project Id.
* @param npub - project Nostr public key.
* @param addressOnFeeOutput - address on fee output.
* @param transactionStatus - transaction status.
* @param founderKey - founder nostr pubkey.
* @param txid - transaction ID.
* @param createdOnBlock - block height (optional).
* @param nostrEventId - nostr event ID associated with the project
*/
public async $setProject(
id: string,
npub: string,
addressOnFeeOutput: string,
transactionStatus: AngorTransactionStatus,
founderKey: string,
txid: string,
createdOnBlock?: number
createdOnBlock?: number,
nostrEventId?: string
): Promise<void> {
try {
logger.debug(`nostrEventId=${nostrEventId}`);
const query = `INSERT INTO angor_projects
(
id,
npub,
address_on_fee_output,
creation_transaction_status,
created_on_block,
txid,
founder_key
founder_key,
nostr_event_id
)
VALUES ('${id}', '${npub}', '${addressOnFeeOutput}', '${transactionStatus}', ${createdOnBlock}, '${txid}', '${founderKey}')
VALUES ('${id}', '${addressOnFeeOutput}', '${transactionStatus}', '${createdOnBlock}', '${txid}', '${founderKey}', '${nostrEventId}')
ON DUPLICATE KEY UPDATE
creation_transaction_status = '${transactionStatus}'
`;
Expand Down Expand Up @@ -117,9 +118,9 @@ class AngorProjectRepository {
const query = `SELECT
angor_projects.id,
angor_projects.founder_key,
angor_projects.npub,
angor_projects.created_on_block,
angor_projects.txid,
angor_projects.nostr_event_id,
COUNT(angor_investments.txid) AS investments_count
FROM angor_projects
LEFT JOIN angor_investments
Expand Down Expand Up @@ -267,7 +268,7 @@ class AngorProjectRepository {
const order = 'DESC';

try {
const query = `SELECT id, founder_key, npub, created_on_block, txid
const query = `SELECT id, founder_key, created_on_block, txid, nostr_event_id
FROM angor_projects
WHERE creation_transaction_status = '${
AngorTransactionStatus.Confirmed
Expand Down
4 changes: 2 additions & 2 deletions docker-angor-api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ services:
MINERENABLED: ${MINERENABLED:-0} # change this to 1 if this node needs to mine (however for simplicity there should only be one mining node per challenge)
NBITS: $NBITS
MINETO: ${MINETO:-tb1qk4pq0rh75qtph47wlufhyss43flhvhvwe4zt8a}
PRIVKEY: ${PRIVKEY:-cRz3Ci2aUNmdP4pViSM8LafwKHZmvn4X6gjeCXzVkBYBLhzA3uFC} # private key path is m/84'/1'/0'/0/0 of test wallet mnemonic "margin radio diamond leg loud street announce guitar video shiver speed eyebrow"
SIGNETCHALLENGE: ${SIGNETCHALLENGE:-512102b57c4413a0354bcc360a37e035f26670deda14bab613c28fbd30fe52b2deccc151ae}
PRIVKEY: ${PRIVKEY:-cNDhpWanXGyCed6U7eBNqRN3EyB7mEUbqm19tA7z5ho6BiNaPfmy} # private key path is m/84'/1'/0'/0/0 of test wallet mnemonic "margin radio diamond leg loud street announce guitar video shiver speed eyebrow"
SIGNETCHALLENGE: ${SIGNETCHALLENGE:-512102a3f8184701a033e5f8faa295647374b0bbc868082240d6e7ad8e9ecb0d86e6d451ae}
EXTERNAL_IP: $EXTERNAL_IP
ADDNODE: ${ADDNODE:-207.180.254.78}
RPCUSER: ${RPCUSER:-rpcuser}
Expand Down
Loading