ALM: warning for auto-relayed messages (#644)

This commit is contained in:
Kirill Fedoseev 2022-03-17 15:11:14 +04:00 committed by GitHub
parent 9c2d2f404c
commit 910c3759c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 14 deletions

@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({
/>
{signatureCollected && (
<ExecutionConfirmation
messageData={message.data}
message={message}
executionData={executionData}
isHome={!fromHome}
signatureCollected={signatureCollected}

@ -10,13 +10,14 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { Thead, AgeTd, StatusTd } from './commons/Table'
import { ManualExecutionButton } from './ManualExecutionButton'
import { useStateProvider } from '../state/StateProvider'
import { matchesRule, MessageObject, WarnRule } from '../utils/web3'
const StyledExecutionConfirmation = styled.div`
margin-top: 30px;
`
export interface ExecutionConfirmationParams {
messageData: string
message: MessageObject
executionData: ExecutionData
setExecutionData: Function
signatureCollected: boolean | string[]
@ -26,7 +27,7 @@ export interface ExecutionConfirmationParams {
}
export const ExecutionConfirmation = ({
messageData,
message,
executionData,
setExecutionData,
signatureCollected,
@ -34,7 +35,7 @@ export const ExecutionConfirmation = ({
executionEventsFetched,
setPendingExecution
}: ExecutionConfirmationParams) => {
const { foreign } = useStateProvider()
const { foreign, setWarning } = useStateProvider()
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
const availableManualExecution =
!isHome &&
@ -67,6 +68,24 @@ export const ExecutionConfirmation = ({
[availableManualExecution, foreign.bridgeContract]
)
useEffect(
() => {
if (!message.data || !executionData || !availableManualExecution) return
try {
const fileName = 'warnRules'
const rules: WarnRule[] = require(`../snapshots/${fileName}.json`)
for (let rule of rules) {
if (matchesRule(rule, message)) {
setWarning(rule.message)
return
}
}
} catch (e) {}
},
[availableManualExecution, executionData, message, message.data, setWarning]
)
const getExecutionStatusElement = (validatorStatus = '') => {
switch (validatorStatus) {
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
@ -125,7 +144,7 @@ export const ExecutionConfirmation = ({
<td>
<ManualExecutionButton
safeExecutionAvailable={safeExecutionAvailable}
messageData={messageData}
messageData={message.data}
setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]}
setPendingExecution={setPendingExecution}

@ -9,6 +9,7 @@ import { InfoAlert } from './commons/InfoAlert'
import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
import { ErrorAlert } from './commons/ErrorAlert'
import { WarningAlert } from './commons/WarningAlert'
const StyledMainPage = styled.div`
text-align: center;
@ -52,7 +53,7 @@ export interface FormSubmitParams {
export const MainPage = () => {
const history = useHistory()
const { home, foreign, error, setError } = useStateProvider()
const { home, foreign, error, setError, warning, setWarning } = useStateProvider()
const [networkName, setNetworkName] = useState('')
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
const [showInfoAlert, setShowInfoAlert] = useState(false)
@ -133,6 +134,7 @@ export const MainPage = () => {
</InfoAlert>
)}
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
<Route
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}

@ -0,0 +1,34 @@
import React from 'react'
import styled from 'styled-components'
import { InfoIcon } from './InfoIcon'
import { CloseIcon } from './CloseIcon'
const StyledErrorAlert = styled.div`
border: 1px solid var(--warning-color);
border-radius: 4px;
margin-bottom: 20px;
padding-top: 10px;
`
const CloseIconContainer = styled.div`
cursor: pointer;
`
const TextContainer = styled.div`
white-space: pre-wrap;
flex-direction: column;
`
export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
return (
<div className="row is-center">
<StyledErrorAlert className="col-10 is-vertical-align row">
<InfoIcon color="var(--warning-color)" />
<TextContainer className="col-10">{error}</TextContainer>
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
<CloseIcon color="var(--warning-color)" />
</CloseIconContainer>
</StyledErrorAlert>
</div>
)
}

@ -27,6 +27,8 @@ export interface StateContext {
loading: boolean
error: string
setError: Function
warning: string
setWarning: Function
}
const initialState = {
@ -46,7 +48,9 @@ const initialState = {
},
loading: true,
error: '',
setError: () => {}
setError: () => {},
warning: '',
setWarning: () => {}
}
const StateContext = createContext<StateContext>(initialState)
@ -59,6 +63,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
foreignWeb3: foreignNetwork.web3
})
const [error, setError] = useState('')
const [warning, setWarning] = useState('')
const value = {
home: {
@ -75,7 +80,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
},
loading: homeNetwork.loading || foreignNetwork.loading,
error,
setError
setError,
warning,
setWarning
}
return <StateContext.Provider value={value}>{children}</StateContext.Provider>

@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
--failed-color: ${props => props.theme.failed.textColor};
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
--warning-color: ${props => props.theme.warning.textColor};
--warning-bg-color: ${props => props.theme.warning.backgroundColor};
}
`

@ -17,6 +17,10 @@ const theme = {
failed: {
textColor: '#de4437',
backgroundColor: 'rgba(222,68,55,.1)'
},
warning: {
textColor: '#ffa758',
backgroundColor: 'rgba(222,68,55,.1)'
}
}
export default theme

@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider'
export interface MessageObject {
id: string
data: string
sender?: string
executor?: string
obToken?: string
obReceiver?: string
}
export interface WarnRule {
message: string
sender?: string
executor?: string
obToken?: string
obReceiver?: string
}
export const matchesRule = (rule: WarnRule, msg: MessageObject) => {
if (!msg.executor || !msg.sender) {
return false
}
if (!!rule.executor && rule.executor.toLowerCase() !== msg.executor.toLowerCase()) {
return false
}
if (!!rule.sender && rule.sender.toLowerCase() !== msg.sender.toLowerCase()) {
return false
}
if (!!rule.obToken && (!msg.obToken || rule.obToken.toLowerCase() !== msg.obToken.toLowerCase())) {
return false
}
if (!!rule.obReceiver && (!msg.obReceiver || rule.obReceiver.toLowerCase() !== msg.obReceiver.toLowerCase())) {
return false
}
return true
}
const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url))
@ -26,15 +57,33 @@ export const filterEventsByAbi = (
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
if (!eventAbi || !eventAbi.inputs || !eventAbi.inputs.length) {
return []
}
const inputs = eventAbi.inputs
return events.map(e => {
let decodedLogs: { [p: string]: string } = {
messageId: '',
encodedData: ''
const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]])
let sender, executor, obToken, obReceiver
if (encodedData.length >= 160) {
sender = `0x${encodedData.slice(66, 106)}`
executor = `0x${encodedData.slice(106, 146)}`
const dataOffset =
160 + (parseInt(encodedData.slice(154, 156), 16) + parseInt(encodedData.slice(156, 158), 16)) * 2 + 8
if (encodedData.length >= dataOffset + 64) {
obToken = `0x${encodedData.slice(dataOffset + 24, dataOffset + 64)}`
}
if (encodedData.length >= dataOffset + 128) {
obReceiver = `0x${encodedData.slice(dataOffset + 88, dataOffset + 128)}`
}
}
if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) {
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
return {
id: messageId || '',
data: encodedData || '',
sender,
executor,
obToken,
obReceiver
}
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
})
}