Skip to content
Merged
3 changes: 3 additions & 0 deletions packages/common/__tests__/utils/nft.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const REAL_NFT_EVENT_DATA = {
}
}
],
'headers': [],
'tokens': [
'000041f860a327969fa03685ed05cf316fc941708c53801cf81f426ac4a55866'
],
Expand Down Expand Up @@ -494,6 +495,7 @@ describe('transaction transformation compatibility', () => {
timelock: null,
}
}],
headers: [],
nonce: 0,
signal_bits: 1,
timestamp: 0,
Expand Down Expand Up @@ -671,6 +673,7 @@ describe('processNftEvent', () => {
}
}
],
headers: [],
tokens: [
'000041f860a327969fa03685ed05cf316fc941708c53801cf81f426ac4a55866'
],
Expand Down
11 changes: 11 additions & 0 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface Transaction {
parents: string[];
inputs: TxInput[];
outputs: TxOutput[];
headers?: TxHeader[];
height?: number;
voided?: boolean | null;
// eslint-disable-next-line camelcase
Expand Down Expand Up @@ -80,6 +81,16 @@ export interface DecodedOutput {
timelock: number | null;
}

export interface TxNanoHeader {
id: string;
nc_seqnum: number;
nc_id: string;
nc_method: string;
nc_address: string;
}

export type TxHeader = TxNanoHeader;

export class Authorities {
/**
* Supporting up to 8 authorities (but we only have mint and melt at the moment)
Expand Down
4 changes: 2 additions & 2 deletions packages/daemon/__tests__/db/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1158,12 +1158,12 @@ describe('getTokenSymbols', () => {
let tokenIdList = tokensToPersist.map((each: TokenInfo) => each.id);
let tokenSymbolMap = await getTokenSymbols(mysql, tokenIdList);

expect(tokenSymbolMap).toBeNull();
expect(tokenSymbolMap).toStrictEqual({});

tokenIdList = [];
tokenSymbolMap = await getTokenSymbols(mysql, tokenIdList);

expect(tokenSymbolMap).toBeNull();
expect(tokenSymbolMap).toStrictEqual({});
});
});

Expand Down
1 change: 0 additions & 1 deletion packages/daemon/src/actors/WebSocketActor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export default (callback: any, receive: any) => {
socket.ping();
}, PING_INTERVAL);

// @ts-ignore: We already check for missing envs in startup
const socket: WebSocket = new WebSocket(getFullnodeWsUrl());
let pingTimeout: NodeJS.Timeout = createPingTimeout();
let pingTimer: NodeJS.Timer;
Expand Down
23 changes: 11 additions & 12 deletions packages/daemon/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Miner,
TokenSymbolsRow,
MaxAddressIndexRow,
AddressesWalletsRow,
} from '../types';
import {
TxInput,
Expand Down Expand Up @@ -776,7 +777,7 @@ export const getAddressWalletInfo = async (mysql: MysqlConnection, addresses: st
}

const addressWalletMap: StringMap<Wallet> = {};
const [results] = await mysql.query(
const [results, _] = await mysql.query<AddressesWalletsRow[]>(
`SELECT DISTINCT a.\`address\`,
a.\`wallet_id\`,
w.\`auth_xpubkey\`,
Expand All @@ -790,15 +791,14 @@ export const getAddressWalletInfo = async (mysql: MysqlConnection, addresses: st
[addresses],
);

// @ts-ignore
for (const entry of results) {
const walletInfo: Wallet = {
walletId: entry.wallet_id as string,
authXpubkey: entry.auth_xpubkey as string,
xpubkey: entry.xpubkey as string,
maxGap: entry.max_gap as number,
walletId: entry.wallet_id,
authXpubkey: entry.auth_xpubkey,
xpubkey: entry.xpubkey,
maxGap: entry.max_gap,
};
addressWalletMap[entry.address as string] = walletInfo;
addressWalletMap[entry.address] = walletInfo;
}
return addressWalletMap;
};
Expand Down Expand Up @@ -1507,25 +1507,24 @@ export const cleanupVoidedTx = async (mysql: MysqlConnection, txId: string): Pro
*
* @param mysql - Database connection
* @param tokenIdList - A list of token ids
* @returns The token information (or null if id is not found)
* @returns The token information (or empty object if id is not found)
*
* @todo This method is duplicated from the wallet-service lambdas,
* we should have common methods for both packages
*/
export const getTokenSymbols = async (
mysql: MysqlConnection,
tokenIdList: string[],
): Promise<StringMap<string> | null> => {
if (tokenIdList.length === 0) return null;
): Promise<StringMap<string>> => {
if (tokenIdList.length === 0) return {};

const [results] = await mysql.query<TokenSymbolsRow[]>(
'SELECT `id`, `symbol` FROM `token` WHERE `id` IN (?)',
[tokenIdList],
);

if (results.length === 0) return null;
if (results.length === 0) return {};
Comment thread
pedroferreira1 marked this conversation as resolved.
return results.reduce((prev: Record<string, string>, token: { id: string, symbol: string }) => {
// eslint-disable-next-line no-param-reassign
prev[token.id] = token.symbol;
return prev;
}, {}) as unknown as StringMap<string>;
Expand Down
30 changes: 26 additions & 4 deletions packages/daemon/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
WalletStatus,
FullNodeEventTypes,
StandardFullNodeEvent,
EventTxHeader,
isNanoHeader,
} from '../types';
import {
TxInput,
Expand Down Expand Up @@ -164,6 +166,15 @@ export const metadataDiff = async (_context: Context, event: Event) => {
export const isBlock = (version: number): boolean => version === hathorLib.constants.BLOCK_VERSION
|| version === hathorLib.constants.MERGED_MINED_BLOCK_VERSION;

export function isNanoContract(headers: EventTxHeader[]) {
for (const header of headers) {
if (isNanoHeader(header)) {
return true;
}
}
return false;
}

export const handleVertexAccepted = async (context: Context, _event: Event) => {
const mysql = await getDbConnection();
await mysql.beginTransaction();
Expand Down Expand Up @@ -197,8 +208,11 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
token_name,
token_symbol,
parents,
headers = [],
} = fullNodeData;

const isNano = isNanoContract(headers);

const dbTx: DbTransaction | null = await getTransactionById(mysql, hash);

if (dbTx) {
Expand Down Expand Up @@ -272,6 +286,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {

// Add the transaction
logger.debug('Will add the tx with height', height);
// TODO: add is_nanocontract to transaction table?
await addOrUpdateTx(
mysql,
hash,
Expand All @@ -288,13 +303,14 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
await updateTxOutputSpentBy(mysql, txInputs, hash);

// Genesis tx has no inputs and outputs, so nothing to be updated, avoid it
if (inputs.length > 0 || outputs.length > 0) {
// Nano contracts are a special case since they can have an address to update even without inputs/outputs
if (inputs.length > 0 || outputs.length > 0 || isNano) {
const tokenList: string[] = getTokenListFromInputsAndOutputs(txInputs, txOutputs);

// Update transaction count with the new tx
await incrementTokensTxCount(mysql, tokenList);

const addressBalanceMap: StringMap<TokenBalanceMap> = getAddressBalanceMap(txInputs, txOutputs);
const addressBalanceMap: StringMap<TokenBalanceMap> = getAddressBalanceMap(txInputs, txOutputs, headers);

// update address tables (address, address_balance, address_tx_history)
await updateAddressTablesWithTx(mysql, hash, timestamp, addressBalanceMap);
Expand Down Expand Up @@ -376,6 +392,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
parents,
inputs: txInputs,
outputs: txOutputs,
headers,
height: metadata.height,
token_name,
token_symbol,
Expand Down Expand Up @@ -444,6 +461,7 @@ export const handleVertexRemoved = async (context: Context, _event: Event) => {
outputs,
inputs,
tokens,
headers = [],
} = fullNodeEvent.event.data;

const dbTx: DbTransaction | null = await getTransactionById(mysql, hash);
Expand All @@ -459,6 +477,7 @@ export const handleVertexRemoved = async (context: Context, _event: Event) => {
inputs,
outputs,
tokens,
headers,
);

logger.info(`[VertexRemoved] Removing tx from database: ${hash}`);
Expand All @@ -481,6 +500,7 @@ export const voidTx = async (
inputs: EventTxInput[],
outputs: EventTxOutput[],
tokens: string[],
headers: EventTxHeader[],
) => {
const dbTxOutputs: DbTxOutput[] = await getTxOutputsFromTx(mysql, hash);
const txOutputs: TxOutputWithIndex[] = prepareOutputs(outputs, tokens);
Expand All @@ -499,7 +519,7 @@ export const voidTx = async (
};
});

const addressBalanceMap: StringMap<TokenBalanceMap> = getAddressBalanceMap(txInputs, txOutputsWithLocked);
const addressBalanceMap: StringMap<TokenBalanceMap> = getAddressBalanceMap(txInputs, txOutputsWithLocked, headers);
await voidTransaction(mysql, hash, addressBalanceMap);
await markUtxosAsVoided(mysql, dbTxOutputs);

Expand All @@ -519,6 +539,7 @@ export const handleVoidedTx = async (context: Context) => {
outputs,
inputs,
tokens,
headers = [],
} = fullNodeEvent.event.data;

logger.debug(`Will handle voided tx for ${hash}`);
Expand All @@ -527,7 +548,8 @@ export const handleVoidedTx = async (context: Context) => {
hash,
inputs,
outputs,
tokens
tokens,
headers,
);
logger.debug(`Voided tx ${hash}`);

Expand Down
8 changes: 8 additions & 0 deletions packages/daemon/src/types/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,11 @@ export interface MaxAddressIndexRow extends RowDataPacket {
max_among_addresses: number,
max_wallet_index: number
}

export interface AddressesWalletsRow extends RowDataPacket {
address: string,
wallet_id: string,
auth_xpubkey: string,
xpubkey: string,
maxGap: number,
}
14 changes: 14 additions & 0 deletions packages/daemon/src/types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export type StandardFullNodeEvent = FullNodeEventBase & {
nonce: number;
inputs: EventTxInput[];
outputs: EventTxOutput[];
headers?: EventTxHeader[];
parents: string[];
tokens: string[];
token_name: null | string;
Expand Down Expand Up @@ -137,3 +138,16 @@ export interface LastSyncedEvent {
updated_at: number;
}

export interface EventTxNanoHeader {
id: string;
nc_seqnum: number;
nc_id: string;
nc_method: string;
nc_address: string;
}

export type EventTxHeader = EventTxNanoHeader;

export function isNanoHeader(header: EventTxHeader): header is EventTxNanoHeader {
return header.id === '10';
}
Loading