Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0e2628e
Merge pull request #136 from HathorNetwork/release-candidate
andreabadesso Mar 5, 2024
dd9ae3f
Merge pull request #154 from HathorNetwork/release-candidate
andreabadesso Apr 2, 2024
5d20679
Merge pull request #176 from HathorNetwork/release-candidate
andreabadesso Jun 17, 2024
638c9e7
Merge pull request #202 from HathorNetwork/release-candidate
andreabadesso Dec 11, 2024
c643e27
fix: Mainnet release script (#203)
luislhl Dec 12, 2024
f16e1a7
Merge pull request #210 from HathorNetwork/release-candidate
andreabadesso Jan 24, 2025
6412591
Merge pull request #213 from HathorNetwork/release-candidate
andreabadesso Feb 12, 2025
f0d8e7e
Merge pull request #230 from HathorNetwork/release-candidate
r4mmer Apr 2, 2025
b4d7d0d
Merge pull request #236 from HathorNetwork/master
r4mmer Apr 7, 2025
7902bca
feat: version data validation (#241)
r4mmer Apr 8, 2025
ed7992c
chore: remove error handler middleware from authorization lambda (#242)
r4mmer Apr 8, 2025
4d818ed
fix: v1.8.2-rc.3 qa issues (#246)
r4mmer Apr 14, 2025
557403d
Merge pull request #247 from HathorNetwork/master
r4mmer Apr 14, 2025
87f8084
Merge pull request #248 from HathorNetwork/feat/version-camel-case
r4mmer Apr 15, 2025
b709318
fix: we should await mark utxos with proposal id to prevent a race co…
andreabadesso Apr 15, 2025
e32be69
fix: invalid assert in release tx proposal utxos (#251)
andreabadesso Apr 15, 2025
90a1c21
Merge branch 'release-candidate' into sync/release-with-release-candi…
r4mmer Apr 15, 2025
cd8ff8d
Merge pull request #254 from HathorNetwork/sync/release-with-release-…
r4mmer Apr 15, 2025
8457fe8
Merge pull request #252 from HathorNetwork/release-candidate
r4mmer Apr 15, 2025
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
13 changes: 9 additions & 4 deletions .codebuild/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ deploy_hathor_network_account() {
send_slack_message "New version deployed to mainnet-staging: ${GIT_REF_TO_DEPLOY}"
elif expr "${GIT_REF_TO_DEPLOY}" : "v.*" >/dev/null; then
echo $GIT_REF_TO_DEPLOY > /tmp/docker_image_tag
make build-daemon;

# --- Testnet ---
# Gets all env vars with `testnet_` prefix and re-exports them without the prefix
Expand All @@ -75,29 +74,35 @@ deploy_hathor_network_account() {
done

make migrate;
make build-daemon;
make deploy-lambdas-testnet;
# The idea here is that if the lambdas deploy fail, the built image won't be pushed:
make push-daemon;

# Unsets all the testnet env vars so we make sure they don't leak to other deploys
for var in "${!testnet_@}"; do
unset ${var#testnet_}
done

send_slack_message "New version deployed to testnet-production: ${GIT_REF_TO_DEPLOY}"

# --- Mainnet ---
# Gets all env vars with `mainnet_` prefix and re-exports them without the prefix
for var in "${!mainnet_@}"; do
export ${var#mainnet_}="${!var}"
done
make migrate;
make build-daemon;
make deploy-lambdas-mainnet;
# The idea here is that if the lambdas deploy fail, the built image won't be pushed:
make push-daemon;

# Unsets all the mainnet env vars so we make sure they don't leak to other deploys
for var in "${!mainnet_@}"; do
unset ${var#mainnet_}
done

# The idea here is that if the lambdas deploy fail, the built image won't be pushed:
make push-daemon;
send_slack_message "New version deployed to testnet-production and mainnet-production: ${GIT_REF_TO_DEPLOY}"
send_slack_message "New version deployed to mainnet-production: ${GIT_REF_TO_DEPLOY}"
else
# Gets all env vars with `dev_` prefix and re-exports them without the prefix
for var in "${!dev_@}"; do
Expand Down
3 changes: 1 addition & 2 deletions packages/wallet-service/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,5 +235,4 @@ export const bearerAuthorizer: APIGatewayTokenAuthorizerHandler = middy(async (e
}

return _generatePolicy(walletId, 'Deny', event.methodArn, logger);
}).use(cors())
.use(errorHandler());
}).use(cors());
2 changes: 1 addition & 1 deletion packages/wallet-service/src/api/txProposalCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const create = middy(walletIdProxyHandler(async (walletId, event) => {

// mark utxos with tx-proposal id
const txProposalId = uuidv4();
markUtxosWithProposalId(mysql, txProposalId, inputUtxos);
await markUtxosWithProposalId(mysql, txProposalId, inputUtxos);

await createTxProposal(mysql, txProposalId, walletId, now);

Expand Down
10 changes: 7 additions & 3 deletions packages/wallet-service/src/api/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import 'source-map-support/register';
import {
closeDbConnection,
getDbConnection,
getUnixTimestamp,
} from '@src/utils';
import { warmupMiddleware } from '@src/api/utils';
import { getRawFullnodeData } from '@src/nodeConfig'
import { getFullnodeData } from '@src/nodeConfig'
import errorHandler from '@src/api/middlewares/errorHandler';
import middy from '@middy/core';
import cors from '@middy/http-cors';
Expand All @@ -26,15 +27,18 @@ const mysql = getDbConnection();
* This lambda is called by API Gateway on GET /version
*/
export const get: APIGatewayProxyHandler = middy(async () => {
const versionData = await getRawFullnodeData(mysql);
const versionData = await getFullnodeData(mysql);

await closeDbConnection(mysql);

return {
statusCode: 200,
body: JSON.stringify({
success: true,
data: versionData,
data: {
...versionData,
timestamp: getUnixTimestamp(),
},
}),
};
}).use(cors())
Expand Down
8 changes: 1 addition & 7 deletions packages/wallet-service/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1875,19 +1875,13 @@ export const releaseTxProposalUtxos = async (
mysql: ServerlessMysql,
txProposalIds: string[],
): Promise<void> => {
const result: OkPacket = await mysql.query(
await mysql.query(
`UPDATE \`tx_output\`
SET \`tx_proposal\` = NULL,
\`tx_proposal_index\` = NULL
WHERE \`tx_proposal\` IN (?)`,
[txProposalIds],
);

assert.strictEqual(
result.affectedRows,
txProposalIds.length,
'Not all utxos were correctly updated',
);
};

/**
Expand Down
7 changes: 4 additions & 3 deletions packages/wallet-service/src/nodeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ServerlessMysql } from 'serverless-mysql';
import { getVersionData, updateVersionData } from '@src/db';
import { FullNodeVersionData, FullNodeApiVersionResponse } from '@src/types';
import fullnode from '@src/fullnode';
import { constants } from '@hathor/wallet-lib';

const VERSION_CHECK_MAX_DIFF = 60 * 60; // 1 hour

Expand Down Expand Up @@ -48,9 +49,9 @@ export function convertApiVersionData(data: FullNodeApiVersionResponse): FullNod
rewardSpendMinBlocks: data.reward_spend_min_blocks,
maxNumberInputs: data.max_number_inputs,
maxNumberOutputs: data.max_number_outputs,
decimalPlaces: data.decimal_places,
nativeTokenName: data.native_token.name,
nativeTokenSymbol: data.native_token.symbol,
decimalPlaces: data.decimal_places ?? constants.DECIMAL_PLACES,
nativeTokenName: data.native_token?.name ?? constants.DEFAULT_NATIVE_TOKEN_CONFIG.name,
nativeTokenSymbol: data.native_token?.symbol ?? constants.DEFAULT_NATIVE_TOKEN_CONFIG.symbol,
};
}

Expand Down
8 changes: 5 additions & 3 deletions packages/wallet-service/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import Joi from 'joi';
import { FullNodeApiVersionResponse, EnvironmentConfig } from '@src/types';

export const Sha256Schema = Joi.string().hex().length(64);

export const FullnodeVersionSchema = Joi.object<FullNodeApiVersionResponse>({
version: Joi.string().min(1).required(),
network: Joi.string().min(1).required(),
Expand All @@ -20,9 +22,9 @@ export const FullnodeVersionSchema = Joi.object<FullNodeApiVersionResponse>({
max_number_inputs: Joi.number().integer().positive().required(),
max_number_outputs: Joi.number().integer().positive().required(),
decimal_places: Joi.number().integer().positive(),
genesis_block_hash: Joi.string().min(1).required(),
genesis_tx1_hash: Joi.string().hex().length(64).required(),
genesis_tx2_hash: Joi.string().hex().length(64).required(),
genesis_block_hash: Sha256Schema,
genesis_tx1_hash: Sha256Schema,
genesis_tx2_hash: Sha256Schema,
native_token: Joi.object({
name: Joi.string().min(1).max(30).required(),
symbol: Joi.string().min(1).max(5).required(),
Expand Down
10 changes: 5 additions & 5 deletions packages/wallet-service/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ export interface FullNodeApiVersionResponse {
reward_spend_min_blocks: number;
max_number_inputs: number;
max_number_outputs: number;
decimal_places: number;
genesis_block_hash: string,
genesis_tx1_hash: string,
genesis_tx2_hash: string,
native_token: { name: string, symbol: string};
decimal_places?: number;
genesis_block_hash?: string,
genesis_tx1_hash?: string,
genesis_tx2_hash?: string,
native_token?: { name: string, symbol: string};
}

export interface TxProposal {
Expand Down
8 changes: 7 additions & 1 deletion packages/wallet-service/tests/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
import fullnode from '@src/fullnode';
import { getHealthcheck } from '@src/api/healthcheck';
import { Severity } from '@wallet-service/common';
import { convertApiVersionData } from '@src/nodeConfig';

// Monkey patch bitcore-lib

Expand Down Expand Up @@ -1682,17 +1683,22 @@ test('GET /version', async () => {
genesis_tx2_hash: 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
native_token: { name: 'Hathor', symbol: 'HTR'},
};
const returnData = convertApiVersionData(mockData);

const ts = getUnixTimestamp()
await updateVersionData(mysql, ts, mockData);


const event = makeGatewayEvent({});
const result = await getVersionDataGet(event, null, null) as APIGatewayProxyResult;
const returnBody = JSON.parse(result.body as string);

expect(result.statusCode).toBe(200);
expect(returnBody.success).toBe(true);
expect(returnBody.data).toStrictEqual(mockData);
expect(returnBody.data).toEqual(expect.objectContaining({
timestamp: expect.anything(),
...returnData,
}));
});

test('GET /wallet/proxy/transactions/{txId}', async () => {
Expand Down
27 changes: 26 additions & 1 deletion packages/wallet-service/tests/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import {
cleanupVoidedTx,
checkTxWasVoided,
getWalletTxHistory,
getTxOutputs,
} from '@src/db';
import * as Db from '@src/db';
import { cleanUnsentTxProposalsUtxos } from '@src/db/cronRoutines';
Expand Down Expand Up @@ -1580,7 +1581,31 @@ test('createTxProposal, updateTxProposal, getTxProposal, countUnsentTxProposals,
// Release txProposalUtxos should properly release the utxos. This method will throw an error if the
// updated count is different from the sent tx proposals count.
await releaseTxProposalUtxos(mysql, [txProposalId1, txProposalId2, txProposalId3]);
await expect(releaseTxProposalUtxos(mysql, ['invalid-tx-proposal'])).rejects.toMatchInlineSnapshot('[AssertionError: Not all utxos were correctly updated]');

const txOutputs = await getTxOutputs(mysql, [{
txId: 'tx1',
timestamp: 0,
version: 0,
voided: false,
weight: 0
}, {
txId: 'tx2',
timestamp: 0,
version: 0,
voided: false,
weight: 0
}, {
txId: 'tx3',
timestamp: 0,
version: 0,
voided: false,
weight: 0,
}]);

// Check that txProposalId is null after releasing
txOutputs.forEach((txOutput) => {
expect(txOutput.txProposalId).toBeNull();
});
});

test('updateVersionData', async () => {
Expand Down
94 changes: 94 additions & 0 deletions packages/wallet-service/tests/nodeConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
closeDbConnection,
getDbConnection,
getUnixTimestamp,
} from '@src/utils';
import { addToVersionDataTable, cleanDatabase } from '@tests/utils';
import { FullNodeApiVersionResponse } from '@src/types';
import { convertApiVersionData, getRawFullnodeData } from '@src/nodeConfig';

const mysql = getDbConnection();

const VERSION_DATA: FullNodeApiVersionResponse = {
version: '0.63.1',
network: 'mainnet',
min_weight: 14,
min_tx_weight: 14,
min_tx_weight_coefficient: 1.6,
min_tx_weight_k: 100,
token_deposit_percentage: 0.01,
reward_spend_min_blocks: 300,
max_number_inputs: 255,
max_number_outputs: 255,
decimal_places: 2,
genesis_block_hash: '000006cb93385b8b87a545a1cbb6197e6caff600c12cc12fc54250d39c8088fc',
genesis_tx1_hash: '0002d4d2a15def7604688e1878ab681142a7b155cbe52a6b4e031250ae96db0a',
genesis_tx2_hash: '0002ad8d1519daaddc8e1a37b14aac0b045129c01832281fb1c02d873c7abbf9',
native_token: {
name: 'Hathor-new',
symbol: 'nHTR'
}
};

const OLD_VERSION_DATA: FullNodeApiVersionResponse = {
version: '0.63.1',
network: 'mainnet',
min_weight: 14,
min_tx_weight: 14,
min_tx_weight_coefficient: 1.6,
min_tx_weight_k: 100,
token_deposit_percentage: 0.01,
reward_spend_min_blocks: 300,
max_number_inputs: 255,
max_number_outputs: 255,
};

beforeEach(async () => {
jest.resetModules();
await cleanDatabase(mysql);
});

afterAll(async () => {
await closeDbConnection(mysql);
});

test('getRawFullnodeData', async () => {
const now = getUnixTimestamp();
await addToVersionDataTable(mysql, now, VERSION_DATA);

await expect(getRawFullnodeData(mysql)).resolves.toStrictEqual(VERSION_DATA);
});

test('convertApiVersionData', async () => {
expect(convertApiVersionData(OLD_VERSION_DATA)).toStrictEqual({
version: OLD_VERSION_DATA.version,
network: OLD_VERSION_DATA.network,
minWeight: OLD_VERSION_DATA.min_weight,
minTxWeight: OLD_VERSION_DATA.min_tx_weight,
minTxWeightCoefficient: OLD_VERSION_DATA.min_tx_weight_coefficient,
minTxWeightK: OLD_VERSION_DATA.min_tx_weight_k,
tokenDepositPercentage: OLD_VERSION_DATA.token_deposit_percentage,
rewardSpendMinBlocks: OLD_VERSION_DATA.reward_spend_min_blocks,
maxNumberInputs: OLD_VERSION_DATA.max_number_inputs,
maxNumberOutputs: OLD_VERSION_DATA.max_number_outputs,
decimalPlaces: 2,
nativeTokenName: 'Hathor',
nativeTokenSymbol: 'HTR',
});

expect(convertApiVersionData(VERSION_DATA)).toStrictEqual({
version: VERSION_DATA.version,
network: VERSION_DATA.network,
minWeight: VERSION_DATA.min_weight,
minTxWeight: VERSION_DATA.min_tx_weight,
minTxWeightCoefficient: VERSION_DATA.min_tx_weight_coefficient,
minTxWeightK: VERSION_DATA.min_tx_weight_k,
tokenDepositPercentage: VERSION_DATA.token_deposit_percentage,
rewardSpendMinBlocks: VERSION_DATA.reward_spend_min_blocks,
maxNumberInputs: VERSION_DATA.max_number_inputs,
maxNumberOutputs: VERSION_DATA.max_number_outputs,
decimalPlaces: VERSION_DATA.decimal_places,
nativeTokenName: VERSION_DATA.native_token.name,
nativeTokenSymbol: VERSION_DATA.native_token.symbol,
});
});
5 changes: 5 additions & 0 deletions scripts/push-daemon.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
set -e
set -o pipefail

if [ -z "$ACCOUNT_ID" ]; then
echo "Please export a ACCOUNT_ID env var before running this";
exit 1;
fi

DOCKER_IMAGE_TAG=$(cat /tmp/docker_image_tag)

if [ -z "$DOCKER_IMAGE_TAG" ]; then
Expand Down