Skip to content

Commit

Permalink
TSDK-789 BTC Monitoring (#10)
Browse files Browse the repository at this point in the history
* TSDK-789 Reorg code

* TSDK-789 Refactor with new architecture

* TSDK-789 First working implementation.

* TSDK-789 Fix integration tests

* TSDK-789 Support env variables to parametrize queue

* TSDK-789 Add docker file

* TSDK-789 Update changelog.

* TSDK-789 Minor formatting

* TSDK-789 Update CHANGELOG

* TSDK-789 Remove commented code

---------

Co-authored-by: Diadem <d.shoukralla@topl.me>
  • Loading branch information
mundacho and DiademShoukralla committed May 7, 2024
1 parent 30c95b7 commit cbc2dc2
Show file tree
Hide file tree
Showing 36 changed files with 889 additions and 729 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/sbt_checkPR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ jobs:
- 9084:9084
bitcoin:
# Docker Hub image
image: docker.io/1maa/bitcoin:v25-regtest
image: toplprotocol/bitcoin-zmq:v25-regtest
#
ports:
- 18444:18444
- 18444:18444
- 18443:18443
- 28332:28332
options: --name bitcoin
topl-btc-bridge:
# Docker Hub image
Expand All @@ -77,6 +79,9 @@ jobs:
env:
TOPL_HOST: "bifrost"
TOPL_WALLET_DB: "/data/topl-wallet.db"
ZMQ_HOST: "bitcoin"
ZMQ_PORT: "28332"
BTC_URL: "http://bitcoin"
steps:
- name: Checkout code
uses: actions/checkout@v3.0.2
Expand Down Expand Up @@ -127,7 +132,8 @@ jobs:
with:
args: docker restart topl_bridge
- name: Run integration tests
run: sbt "integration / test"
run: |
sbt "integration / test"
- uses: actions/download-artifact@v3
with:
path: topl-btc-bridge/target/test-reports/
Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ wallet.json
project/project/
metals.sbt
logs/
static/
static/
secret.yaml
topl-btc-bridge/topl-wallet.json
topl-btc-bridge/topl-wallet.db
topl-btc-bridge/topl-mnemonic.txt
topl-btc-bridge/genesisTxProved.pbuf
topl-btc-bridge/genesisTx.pbuf
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Minting status is now displayed in the UI.
- Dependency on `akka-slf4j`.
- New parameters for the launcher: `--btc-url`, `--btc-port`, `--btc-user`, `--btc-password`, `--zmq-url`, `--zmq-port`. These allow to connect to the Bitcoin node and the ZMQ server.
- Support for monitoring the Bitcoin network.


### Changed

- Default port is now 4000.
- `brambl-cli` version (for IT) is now 2.0.0-beta3.
- `brambl-cli` version (for IT) is now 2.0.0-beta3.
- Updated BramblSc to 2.0.0-beta4.
- Integration tests to use our own Bitcoin regtest node Docker image.

### Removed

- `/api/confirm-deposit-btc` WS was removed.
105 changes: 45 additions & 60 deletions bridge-ui/src/Frame.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,70 @@
import { useEffect, useState } from 'react';
import { Link, Outlet } from 'react-router-dom';
import { PeginUIState, mintedBTC, setupSession } from './controllers/PeginController';
import { PeginUIState, mintedBTC, mintingBTC, setupSession } from './controllers/PeginController';
import { deleteCookie } from './cookie-typescript-utils';
import { ErrorResponse, SessionInformation } from './views/StartSession';
import { SessionInformation } from './views/StartSession';

export type SessionCtx = {
session: SessionInformation;
setSession: React.Dispatch<React.SetStateAction<SessionInformation>>;
}


interface MintingStatusRequest {
sessionID: string;
}

interface MintingStatusResponse {
mintingStatus: string;
address: string;
bridgePKey: string;
redeemScript: string;
}

async function checkMintingStatus(mintingStatusRequest: MintingStatusRequest): Promise<MintingStatusResponse | ErrorResponse> {
const response = await fetch('/api/topl-minting-status',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(mintingStatusRequest)
});
if (response.status == 200) {
const data = await response.json();
return data;
} else {
return { error: "Error" };
}
}


function Frame() {


const [session, setSession] = useState<SessionInformation>({ isSet: false, sessionID: "", escrowAddress: "", currentState: PeginUIState.InitialState, redeemAddress: "", toplBridgePKey: "", redeemTemplate: "" });
useEffect(() => setupSession(session, setSession), []);
const updateStatus = async (sessionId: string) => {
if ((session.currentState === PeginUIState.MintingTBTC) ||
(session.currentState === PeginUIState.WaitingForMint)) {
const currentStatus = await checkMintingStatus({ sessionID: sessionId });
if (typeof currentStatus === 'object' && ("mintingStatus" in currentStatus)) {
console.log(currentStatus.mintingStatus);
if (currentStatus.mintingStatus !== "MintingBTCStateMinted") {
// if (currentStatus.mintingStatus === "MintingBTCStateMinting") {
// mintingBTC(setSession, session);
// }
// if (currentStatus.mintingStatus === "MintingBTCStateWaiting") {
// waitingForTBTC(setSession, session);
// }
setTimeout(() => {
updateStatus(sessionId);
}, 5000);
useEffect(() => {
const sessionPoll = setInterval(async () => {
if ((session.currentState == PeginUIState.SessionStarted)) {
const response = await fetch('/api/topl-minting-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sessionID: session.sessionID })
})
if (response.status == 200) {
const data = await response.json();
const mintStatus = (data?.mintingStatus || "") as string
if (mintStatus !== "PeginSessionStateWaitingForBTC") {
mintingBTC(setSession, session)
}
console.log("mintStatus: " + mintStatus)
} else {
mintedBTC(setSession, session, currentStatus.address, currentStatus.bridgePKey, currentStatus.redeemScript);
console.log(response)
}
} else if (session.currentState == PeginUIState.MintingTBTC) {
const response = await fetch('/api/topl-minting-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sessionID: session.sessionID })
})
if (response.status == 200) {
const data = await response.json();
const mintStatus = (data?.mintingStatus || "") as string
if (mintStatus === "PeginSessionWaitingForRedemption") {
mintedBTC(setSession, session, data.address, data.bridgePKey, data.redeemScript);
clearInterval(sessionPoll)
}
}
else {
console.log(response)
}
} else if (session.currentState == PeginUIState.MintedTBTC) {
clearInterval(sessionPoll)
}
} else {
setTimeout(() => {
updateStatus(sessionId);
}, 5000);
}, 5000)
return () => {
clearInterval(sessionPoll);
}
}

useEffect(() => {
updateStatus(session.sessionID);
}, [session.currentState]);
})


function handleLogout() {
deleteCookie("sessionID");
deleteCookie("escrowAddress");
// reload the page
window.location.reload();
}

Expand Down
3 changes: 0 additions & 3 deletions bridge-ui/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import {
createBrowserRouter,
Expand Down Expand Up @@ -28,7 +27,5 @@ const router = createBrowserRouter([


ReactDOM.createRoot(document.getElementById('wrapper')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
)
50 changes: 20 additions & 30 deletions bridge-ui/src/views/StartSession.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { PeginUIState, sessionStarted, mintingBTC } from '../controllers/PeginController';
import { useState } from 'react';
import { PeginUIState, sessionStarted } from '../controllers/PeginController';

export interface SessionInformation {
isSet: boolean;
Expand Down Expand Up @@ -70,35 +70,28 @@ function errorValidation(error: string) {
}
}

function waitingForDeposit(isWaiting: boolean) {
if (isWaiting) {
return (
<div className="mb-3">
<strong role="status">Waiting for funds to arrive...</strong>
<div className="d-flex float-end">
<div className="spinner-border ms-auto" aria-hidden="true"></div>
</div>
</div>
)
} else {
return (
<div></div>
)
}
}

function StartSession(session: SessionInformation, setSession: React.Dispatch<React.SetStateAction<SessionInformation>>) {

const [hash, setHash] = useState<string>("")
const [error, setError] = useState<string>("")

useEffect(() => {
const sessionPoll = setInterval(async () => {
if((session.currentState == PeginUIState.SessionStarted)){
const response = await fetch('/api/topl-minting-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({sessionID: session.sessionID})
})
if (response.status == 200) {
const data = await response.json();
const mintStatus = (data?.mintingStatus || "") as string
if(mintStatus !== "MintingBTCStateReady") {
mintingBTC(setSession, session)
clearInterval(sessionPoll)
}
} else {
console.error(response)
}
}
}, 1000)
return () => clearInterval(sessionPoll);
})

async function handleSubmitSha(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
Expand Down Expand Up @@ -141,17 +134,14 @@ function StartSession(session: SessionInformation, setSession: React.Dispatch<Re
</form>
<div className='row g-3'>
<div className="row">
<div className="mb-3">
<label htmlFor="sessionId" className="form-label">Session</label>
<input type="text" value={session.sessionID} className="form-control" id="sessionId" disabled />
</div>
<div className="mb-3">
<label htmlFor="escrowAddress" className="form-label">Escrow Address</label>
<input type="text" value={session.escrowAddress} className="form-control" id="escrowAddress" disabled />
</div>
</div>
</div>
</div>
{waitingForDeposit(session.isSet)}
{alertInstructions(session.isSet)}
</div>
</>
Expand Down

0 comments on commit cbc2dc2

Please sign in to comment.