Skip to content
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
237 changes: 237 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,243 @@ jobs:
chia stop all -d || true
echo "Chia services stopped"

test-readonly-live-smoke:
name: READ_ONLY live smoke
runs-on: ubuntu-latest
container:
image: node:24

steps:
- uses: Chia-Network/actions/clean-workspace@main

- name: Checkout Code
uses: actions/checkout@v6

- name: Ignore Husky
run: npm pkg delete scripts.prepare

- name: Install Mocha
run: npm install --save-dev mocha

- name: npm install
run: npm install

- name: install global packages
run: npm i -g @babel/cli sequelize-cli cross-env pm2@latest

- name: Show home directory
run: ls -lah ~

- name: Install yq and jq
run: |
apt-get update
apt-get install -y yq jq bc iproute2

- name: Install Chia and chia-tools
shell: bash
run: |
curl -sL https://repo.chia.net/FD39E6D3.pubkey.asc | gpg --dearmor -o /usr/share/keyrings/chia.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/chia.gpg] https://repo.chia.net/debian/ stable main" | tee /etc/apt/sources.list.d/chia.list > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/chia.gpg] https://repo.chia.net/chia-tools/debian/ stable main" | tee /etc/apt/sources.list.d/chia-tools.list > /dev/null
apt-get update

apt-get install -y chia-blockchain-cli chia-tools

- name: Configure Chia
shell: bash
run: |
chia init
chia keys generate -l "readonly_live_smoke"
chia-tools network switch testneta
chia-tools config add-trusted-peer -y testneta-node-msp.chia.net 58444
chia-tools config edit --set wallet.connect_to_unknown_peers=false
chia configure -log-level INFO

- name: Configure CADT
shell: bash
run: |
# Run cadt momentarily to create config file
pm2 start npm --no-autorestart --name "cadt" -- start
sleep 10
pm2 logs cadt --nostream
pm2 stop cadt

# Remove cadt databases to reset system
echo "Removing cadt databases to reset system..."
rm -f ~/.chia/mainnet/cadt/v1/data.sqlite3*
rm -f ~/.chia/mainnet/cadt/v2/data.sqlite3*

# Configure CADT for READ_ONLY smoke mode
echo "Configuring CADT config.yaml..."
yq -yi '.APP.LOG_LEVEL = "debug"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.CHIA_NETWORK = "testnet"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.BIND_ADDRESS = "127.0.0.1"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.DATALAYER_HOST = "https://127.0.0.1:8562"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.WALLET_HOST = "https://127.0.0.1:9256"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.DATALAYER_FILE_SERVER_URL = "https://127.0.0.1:8575"' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.APP.AUTO_MIRROR_EXTERNAL_STORES = false' ~/.chia/mainnet/cadt/config.yaml

# Enable both APIs and force READ_ONLY mode for this smoke
yq -yi '.V1.ENABLE = true' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.V2.ENABLE = true' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.V1.READ_ONLY = true' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.V2.READ_ONLY = true' ~/.chia/mainnet/cadt/config.yaml

# Avoid governance bootstrap dependencies for this suite
yq -yi '.V1.GOVERNANCE.GOVERNANCE_BODY_ID = ""' ~/.chia/mainnet/cadt/config.yaml
yq -yi '.V2.GOVERNANCE.GOVERNANCE_BODY_ID = ""' ~/.chia/mainnet/cadt/config.yaml

# Show the config file
echo "Showing the config file after configuration..."
cat ~/.chia/mainnet/cadt/config.yaml

# Clean up any existing PM2 processes with the same name
pm2 delete cadt 2>/dev/null || true

- name: Start Chia
shell: bash
run: |
chia start wallet data data_layer_http
sleep 10
tail ~/.chia/mainnet/log/debug.log
chia wallet show

- name: Start CADT
shell: bash
run: |
pm2 start npm --no-autorestart --name "cadt" -- start
sleep 30

- name: Wait for wallet sync and datalayer readiness
shell: bash
run: |
echo "Waiting for wallet to finish syncing..."
MAX_WAIT_MINUTES=10
WAIT_SECONDS=$((MAX_WAIT_MINUTES * 60))
START_TIME=$(date +%s)

while true; do
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
REMAINING=$((WAIT_SECONDS - ELAPSED))

if [ "$REMAINING" -le 0 ]; then
echo "Reached maximum wait time of $MAX_WAIT_MINUTES minutes"
break
fi

WALLET_STATUS=$(chia rpc wallet get_sync_status 2>/dev/null || echo '{"synced": false}')
WALLET_SYNCED=$(echo "$WALLET_STATUS" | jq -r '.synced // false')
WALLET_SYNCING=$(echo "$WALLET_STATUS" | jq -r '.syncing // true')

echo "Wallet status: synced=$WALLET_SYNCED, syncing=$WALLET_SYNCING (elapsed: ${ELAPSED}s)"

if [ "$WALLET_SYNCED" = "true" ]; then
echo "Wallet is synced!"
echo "Waiting additional 60 seconds for datalayer to sync..."
sleep 60
break
fi

echo "Wallet still syncing, waiting 30 seconds..."
sleep 30
done

- name: Show CADT logs before tests
shell: bash
run: |
echo "Last 100 lines of CADT logs:"
pm2 logs cadt --lines 100 --nostream

- name: READ_ONLY live smoke tests
env:
TEST_API_HOST: 127.0.0.1
run: |
echo "########################################################"
echo "Running READ_ONLY live smoke tests"
echo "########################################################"
npm run test:live:readonly:smoke

- name: Show CADT logs after tests
if: always()
shell: bash
run: |
echo "=========================================="
echo "CADT stdout logs (full):"
echo "=========================================="
cat ~/.pm2/logs/cadt-out.log || echo "No stdout log found"
echo ""
echo "=========================================="
echo "CADT stderr logs (full):"
echo "=========================================="
cat ~/.pm2/logs/cadt-error.log || echo "No stderr log found"

- name: Show Chia debug log after tests (filtered)
if: always()
shell: bash
run: |
echo "=========================================="
echo "Chia debug log (filtered, last 5000 lines):"
echo "=========================================="
echo "Filtering out noisy messages..."
echo ""
tail -n 5000 ~/.chia/mainnet/log/debug.log 2>/dev/null | grep -v \
-e "Connecting: wss://127.0.0.1" \
-e "respond_block_headers from" \
-e "Time for request request_block_headers" \
-e "get_timestamp_for_height_from_peer add to cache" \
-e "request_puzzle_solution to peer" \
-e "acquired on.*keyring.yaml.lock" \
-e "released on.*keyring.yaml.lock" \
-e "request_header_blocks" \
-e "coin_state_update" \
-e "new_signage_point_or_end_of_sub_slot" \
-e "request_removal_proof" \
-e "RespondToCoinUpdates" \
-e "register_interest_in_coin" \
-e "request_children to peer" \
-e "respond_to_coin_update" \
-e "_coin_subscriptions" \
-e "Getting generator for" \
-e "SubscriptionConfig" \
-e "sub epoch.*start weight is" \
-e "Puzzle at index" \
-e "Attempting to acquire lock" \
-e "watchdog.observers.inotify_buffer" \
-e "filelock" \
-e "Adding derivation record" \
-e "Received message: WSMessage" \
-e "DataLayer subscription update pool" \
-e "register_service" \
-e "Cannot connect to host 127.0.0.1" \
-e "Failed to connect to PeerInfo" \
|| echo "No Chia debug log found or all lines filtered"

- name: Stop CADT (readonly-live-smoke)
if: always()
shell: bash
run: |
echo "Stopping CADT pm2 process..."
pm2 stop cadt || true
pm2 delete cadt || true
echo "CADT stopped"

- name: Show wallet balance (readonly-live-smoke)
if: always()
shell: bash
run: |
echo "Wallet balance at end of job:"
chia wallet show || echo "Failed to show wallet"

- name: Stop Chia services (readonly-live-smoke)
if: always()
shell: bash
run: |
echo "Stopping all Chia services..."
chia stop all -d || true
echo "Chia services stopped"

test-v1-to-v2-upgrade:
name: v1 to v2 upgrade test
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"test": "bash tests/run-tests.sh npx cross-env NODE_ENV=test USE_SIMULATOR=true mocha --loader node_modules/extensionless/src/register.js tests/**/*.spec.js --reporter spec --exit --timeout 300000",
"test:v1": "bash tests/run-tests.sh npx cross-env NODE_ENV=test USE_SIMULATOR=true CW_PORT=31310 mocha --loader node_modules/extensionless/src/register.js 'tests/integration/**/*.spec.js' 'tests/resources/**/*.spec.js' --reporter spec --exit --timeout 300000",
"test:v2": "bash tests/run-tests.sh npx cross-env NODE_ENV=test USE_SIMULATOR=true CW_PORT=31311 mocha --loader node_modules/extensionless/src/register.js 'tests/v2/integration/**/*.spec.js' --reporter spec --exit --timeout 300000",
"test:v2:readonly": "bash tests/run-tests.sh npx cross-env NODE_ENV=test USE_SIMULATOR=true CW_PORT=31311 mocha --loader node_modules/extensionless/src/register.js 'tests/v2/integration/read-only-enforcement.spec.js' --reporter spec --exit --timeout 300000",
"preflight-check": "node --import=extensionless/register scripts/preflight-check.js",
"preflight-check:v1": "node --import=extensionless/register scripts/preflight-check.js --require-v1",
"preflight-check:v2": "node --import=extensionless/register scripts/preflight-check.js --require-v2",
Expand All @@ -22,6 +23,7 @@
"test:v1:live:organization:upgrade": "npx cross-env NODE_ENV=production mocha --loader node_modules/extensionless/src/register.js 'tests/v2/live-api/organization/organization-upgrade-v1.live.spec.js' --reporter spec --exit --timeout 3600000",
"test:v2:live:data:extended": "node --import=extensionless/register tests/v2/live-api/data-extended.js",
"test:v2:live:data:short": "node --import=extensionless/register tests/v2/live-api/data-short.js",
"test:live:readonly:smoke": "npx cross-env NODE_ENV=production mocha --loader node_modules/extensionless/src/register.js 'tests/v2/live-api/read-only-smoke.live.spec.js' --reporter spec --exit --timeout 600000",
"test:v2:live:organization:delete": "npx cross-env NODE_ENV=production mocha --loader node_modules/extensionless/src/register.js 'tests/v2/live-api/organization/organization-delete.live.spec.js' --reporter spec --exit --timeout 600000",
"test:locks": "node --import=extensionless/register tests/helpers/show-test-locks.js",
"test:v1:live:data:short": "node --import=extensionless/register tests/v1/live-api/data-short.js",
Expand Down
31 changes: 26 additions & 5 deletions src/controllers/fileStore.controller.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import crypto from 'crypto';
import { FileStore } from '../models';
import { logger } from '../config/logger';
import { assertIfReadOnlyMode } from '../utils/data-assertions';
import {
isReadOnlyError,
sendReadOnlyError,
} from '../utils/read-only-response.js';

export const subscribeToFileStore = (req, res) => {
export const subscribeToFileStore = async (req, res) => {
try {
await assertIfReadOnlyMode();
const { orgUid } = req.body;

FileStore.subscribeToFileStore(orgUid);
await FileStore.subscribeToFileStore(orgUid);

res.status(200).json({
message: `${orgUid} subscribed to file store.`,
});
} catch (error) {
if (isReadOnlyError(error)) {
return sendReadOnlyError(res);
}
res.status(400).json({
message: `Can not subscribe to file store.`,
error: error.message,
Expand All @@ -20,16 +29,20 @@ export const subscribeToFileStore = (req, res) => {
}
};

export const unsubscribeFromFileStore = (req, res) => {
export const unsubscribeFromFileStore = async (req, res) => {
try {
await assertIfReadOnlyMode();
const { orgUid } = req.body;

FileStore.unsubscribeFromFileStore(orgUid);
await FileStore.unsubscribeFromFileStore(orgUid);

res.status(200).json({
message: `Can not unsubscribe the fileStore from ${orgUid}`,
message: `${orgUid} unsubscribed from file store.`,
});
} catch (error) {
if (isReadOnlyError(error)) {
return sendReadOnlyError(res);
}
res.status(400).json({
message: 'Can not retrieve file list from filestore',
error: error.message,
Expand All @@ -53,12 +66,16 @@ export const getFileList = async (req, res) => {

export const deleteFile = async (req, res) => {
try {
await assertIfReadOnlyMode();
await FileStore.deleteFileStoreItem(req.body.fileId);
res.status(200).json({
message:
'File will be deleted from the filestore, but it will take a few mins to confirm.',
});
} catch (error) {
if (isReadOnlyError(error)) {
return sendReadOnlyError(res);
}
res.status(400).json({
message: 'Can not delete file from filestore',
error: error.message,
Expand Down Expand Up @@ -91,6 +108,7 @@ export const getFile = async (req, res) => {

export const addFileToFileStore = async (req, res) => {
try {
await assertIfReadOnlyMode();
if (!req.file) {
throw new Error('Missing file data, can not upload file.');
}
Expand All @@ -115,6 +133,9 @@ export const addFileToFileStore = async (req, res) => {
fileId: SHA256,
});
} catch (error) {
if (isReadOnlyError(error)) {
return sendReadOnlyError(res);
}
logger.error('[v1]: Error adding file to file store:', error);
res.status(400).json({
message: 'Can not add file to file store',
Expand Down
Loading
Loading