Skip to content

Commit dbaf7fe

Browse files
authored
Merge the develop branch to the master branch, preparation to v3.5.0
This merge contains the following set of changes: * [Oracle, Improvement] Helpers for overriding AMB signatures (#640) * [Oracle, Improvement] Support manual signatures in erc to native mode (#646) * [ALM, Improvement] ALM: Do not show unneeded failed confirmations (#641) * [ALM, Improvement] ALM: warning for auto-relayed messages (#644) * [ALM, Fix] ALM: reorder warnings
2 parents 5bc562e + 735aa75 commit dbaf7fe

21 files changed

+445
-138
lines changed

alm/src/components/ConfirmationsContainer.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const ConfirmationsContainer = ({
5454
home: { name: homeName },
5555
foreign: { name: foreignName }
5656
} = useStateProvider()
57-
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
57+
const { requiredSignatures, validatorList } = useValidatorContract(fromHome, receipt ? receipt.blockNumber : 0)
5858
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
5959
const {
6060
confirmations,
@@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({
121121
/>
122122
{signatureCollected && (
123123
<ExecutionConfirmation
124-
messageData={message.data}
124+
message={message}
125125
executionData={executionData}
126126
isHome={!fromHome}
127127
signatureCollected={signatureCollected}

alm/src/components/ExecutionConfirmation.tsx

+30-4
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
1010
import { Thead, AgeTd, StatusTd } from './commons/Table'
1111
import { ManualExecutionButton } from './ManualExecutionButton'
1212
import { useStateProvider } from '../state/StateProvider'
13+
import { matchesRule, MessageObject, WarnRule } from '../utils/web3'
14+
import { WarningAlert } from './commons/WarningAlert'
15+
import { ErrorAlert } from './commons/ErrorAlert'
1316

1417
const StyledExecutionConfirmation = styled.div`
1518
margin-top: 30px;
1619
`
1720

1821
export interface ExecutionConfirmationParams {
19-
messageData: string
22+
message: MessageObject
2023
executionData: ExecutionData
2124
setExecutionData: Function
2225
signatureCollected: boolean | string[]
@@ -26,7 +29,7 @@ export interface ExecutionConfirmationParams {
2629
}
2730

2831
export const ExecutionConfirmation = ({
29-
messageData,
32+
message,
3033
executionData,
3134
setExecutionData,
3235
signatureCollected,
@@ -36,6 +39,8 @@ export const ExecutionConfirmation = ({
3639
}: ExecutionConfirmationParams) => {
3740
const { foreign } = useStateProvider()
3841
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
42+
const [error, setError] = useState('')
43+
const [warning, setWarning] = useState('')
3944
const availableManualExecution =
4045
!isHome &&
4146
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
@@ -67,9 +72,27 @@ export const ExecutionConfirmation = ({
6772
[availableManualExecution, foreign.bridgeContract]
6873
)
6974

75+
useEffect(
76+
() => {
77+
if (!message.data || !executionData || !availableManualExecution) return
78+
79+
try {
80+
const fileName = 'warnRules'
81+
const rules: WarnRule[] = require(`../snapshots/${fileName}.json`)
82+
for (let rule of rules) {
83+
if (matchesRule(rule, message)) {
84+
setWarning(rule.message)
85+
return
86+
}
87+
}
88+
} catch (e) {}
89+
},
90+
[availableManualExecution, executionData, message, message.data, setWarning]
91+
)
92+
7093
const getExecutionStatusElement = (validatorStatus = '') => {
7194
switch (validatorStatus) {
72-
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
95+
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
7396
return <SuccessLabel>{validatorStatus}</SuccessLabel>
7497
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
7598
return <RedLabel>{validatorStatus}</RedLabel>
@@ -87,6 +110,8 @@ export const ExecutionConfirmation = ({
87110

88111
return (
89112
<StyledExecutionConfirmation>
113+
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
114+
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
90115
<table>
91116
<Thead>
92117
<tr>
@@ -125,10 +150,11 @@ export const ExecutionConfirmation = ({
125150
<td>
126151
<ManualExecutionButton
127152
safeExecutionAvailable={safeExecutionAvailable}
128-
messageData={messageData}
153+
messageData={message.data}
129154
setExecutionData={setExecutionData}
130155
signatureCollected={signatureCollected as string[]}
131156
setPendingExecution={setPendingExecution}
157+
setError={setError}
132158
/>
133159
</td>
134160
)}

alm/src/components/MainPage.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { TransactionReceipt } from 'web3-eth'
88
import { InfoAlert } from './commons/InfoAlert'
99
import { ExplorerTxLink } from './commons/ExplorerTxLink'
1010
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
11-
import { ErrorAlert } from './commons/ErrorAlert'
1211

1312
const StyledMainPage = styled.div`
1413
text-align: center;
@@ -52,7 +51,7 @@ export interface FormSubmitParams {
5251

5352
export const MainPage = () => {
5453
const history = useHistory()
55-
const { home, foreign, error, setError } = useStateProvider()
54+
const { home, foreign } = useStateProvider()
5655
const [networkName, setNetworkName] = useState('')
5756
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
5857
const [showInfoAlert, setShowInfoAlert] = useState(false)
@@ -132,7 +131,6 @@ export const MainPage = () => {
132131
</AlertP>
133132
</InfoAlert>
134133
)}
135-
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
136134
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
137135
<Route
138136
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}

alm/src/components/ManualExecutionButton.tsx

+81-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useStateProvider } from '../state/StateProvider'
1414
import { signatureToVRS, packSignatures } from '../utils/signatures'
1515
import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
1616
import { TransactionReceipt } from 'web3-eth'
17+
import { useValidatorContract } from '../hooks/useValidatorContract'
1718

1819
const ActionButton = styled.button`
1920
color: var(--button-color);
@@ -32,20 +33,90 @@ interface ManualExecutionButtonParams {
3233
setExecutionData: Function
3334
signatureCollected: string[]
3435
setPendingExecution: Function
36+
setError: Function
3537
}
3638

3739
export const ManualExecutionButton = ({
3840
safeExecutionAvailable,
3941
messageData,
4042
setExecutionData,
4143
signatureCollected,
42-
setPendingExecution
44+
setPendingExecution,
45+
setError
4346
}: ManualExecutionButtonParams) => {
44-
const { foreign, setError } = useStateProvider()
47+
const { foreign } = useStateProvider()
4548
const { library, activate, account, active } = useWeb3React()
4649
const [manualExecution, setManualExecution] = useState(false)
4750
const [allowFailures, setAllowFailures] = useState(false)
48-
const notReady = !foreign.bridgeContract || !signatureCollected || !signatureCollected.length
51+
const [ready, setReady] = useState(false)
52+
const [title, setTitle] = useState('Loading')
53+
const [validSignatures, setValidSignatures] = useState<string[]>([])
54+
55+
const { requiredSignatures, validatorList } = useValidatorContract(false, 'latest')
56+
57+
useEffect(
58+
() => {
59+
if (
60+
!foreign.bridgeContract ||
61+
!foreign.web3 ||
62+
!signatureCollected ||
63+
!signatureCollected.length ||
64+
!requiredSignatures ||
65+
!validatorList ||
66+
!validatorList.length
67+
)
68+
return
69+
70+
const signatures = []
71+
const remainingValidators = Object.fromEntries(validatorList.map(validator => [validator, true]))
72+
for (let i = 0; i < signatureCollected.length && signatures.length < requiredSignatures; i++) {
73+
const { v, r, s } = signatureToVRS(signatureCollected[i])
74+
const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`)
75+
if (validatorList.includes(signer)) {
76+
delete remainingValidators[signer]
77+
signatures.push(signatureCollected[i])
78+
}
79+
}
80+
81+
if (signatures.length < requiredSignatures) {
82+
console.log('On-chain collected signatures are not enough for message execution')
83+
const manualValidators = Object.keys(remainingValidators)
84+
const msgHash = foreign.web3.utils.sha3(messageData)!
85+
for (let i = 0; i < manualValidators.length && signatures.length < requiredSignatures; i++) {
86+
try {
87+
const overrideSignatures: {
88+
[key: string]: string
89+
} = require(`../snapshots/signatures_${manualValidators[i]}.json`)
90+
if (overrideSignatures[msgHash]) {
91+
console.log(`Adding manual signature from ${manualValidators[i]}`)
92+
signatures.push(overrideSignatures[msgHash])
93+
} else {
94+
console.log(`No manual signature from ${manualValidators[i]} was found`)
95+
}
96+
} catch (e) {
97+
console.log(`Signatures overrides are not present for ${manualValidators[i]}`)
98+
}
99+
}
100+
}
101+
102+
if (signatures.length >= requiredSignatures) {
103+
setValidSignatures(signatures)
104+
setTitle('Execute')
105+
setReady(true)
106+
} else {
107+
setTitle('Unavailable')
108+
}
109+
},
110+
[
111+
foreign.bridgeContract,
112+
foreign.web3,
113+
signatureCollected,
114+
validatorList,
115+
requiredSignatures,
116+
messageData,
117+
setValidSignatures
118+
]
119+
)
49120

50121
useEffect(
51122
() => {
@@ -73,9 +144,9 @@ export const ManualExecutionButton = ({
73144
return
74145
}
75146

76-
if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return
147+
if (!library || !foreign.bridgeContract || !foreign.web3 || !validSignatures || !validSignatures.length) return
77148

78-
const signatures = packSignatures(signatureCollected.map(signatureToVRS))
149+
const signatures = packSignatures(validSignatures.map(signatureToVRS))
79150
const messageId = messageData.slice(0, 66)
80151
const bridge = foreign.bridgeContract
81152
const executeMethod =
@@ -140,19 +211,20 @@ export const ManualExecutionButton = ({
140211
foreign.bridgeContract,
141212
setError,
142213
messageData,
143-
signatureCollected,
144214
setExecutionData,
145215
setPendingExecution,
146216
safeExecutionAvailable,
147-
allowFailures
217+
allowFailures,
218+
foreign.web3,
219+
validSignatures
148220
]
149221
)
150222

151223
return (
152224
<div>
153225
<div className="is-center">
154-
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}>
155-
Execute
226+
<ActionButton disabled={!ready} className="button outline" onClick={() => setManualExecution(true)}>
227+
{title}
156228
</ActionButton>
157229
</div>
158230
{safeExecutionAvailable && (

alm/src/components/ValidatorsConfirmations.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const ValidatorsConfirmations = ({
9494
</tbody>
9595
</table>
9696
<RequiredConfirmations>
97-
{requiredSignatures} of {validatorList.length} confirmations required
97+
At least <strong>{requiredSignatures}</strong> of <strong>{validatorList.length}</strong> confirmations required
9898
</RequiredConfirmations>
9999
</div>
100100
)

alm/src/components/commons/ErrorAlert.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: str
3333
}
3434
return (
3535
<div className="row is-center">
36-
<StyledErrorAlert className="col-10 is-vertical-align row">
36+
<StyledErrorAlert className="col-12 is-vertical-align row">
3737
<InfoIcon color="var(--failed-color)" />
3838
<TextContainer className="col-10">
3939
{text}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react'
2+
import styled from 'styled-components'
3+
import { InfoIcon } from './InfoIcon'
4+
import { CloseIcon } from './CloseIcon'
5+
6+
const StyledErrorAlert = styled.div`
7+
border: 1px solid var(--warning-color);
8+
border-radius: 4px;
9+
margin-bottom: 20px;
10+
padding-top: 10px;
11+
`
12+
13+
const CloseIconContainer = styled.div`
14+
cursor: pointer;
15+
`
16+
17+
const TextContainer = styled.div`
18+
white-space: pre-wrap;
19+
flex-direction: column;
20+
`
21+
22+
export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
23+
return (
24+
<div className="row is-center">
25+
<StyledErrorAlert className="col-12 is-vertical-align row">
26+
<InfoIcon color="var(--warning-color)" />
27+
<TextContainer className="col-10">{error}</TextContainer>
28+
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
29+
<CloseIcon color="var(--warning-color)" />
30+
</CloseIconContainer>
31+
</StyledErrorAlert>
32+
</div>
33+
)
34+
}

alm/src/config/constants.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ export const CONFIRMATIONS_STATUS = {
5454
}
5555

5656
export const VALIDATOR_CONFIRMATION_STATUS = {
57-
SUCCESS: 'Success',
57+
SUCCESS: 'Confirmed',
58+
EXECUTION_SUCCESS: 'Executed',
5859
FAILED: 'Failed',
5960
PENDING: 'Pending',
6061
WAITING: 'Waiting',

alm/src/hooks/useMessageConfirmations.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,10 @@ export const useMessageConfirmations = ({
343343
// Sets the message status based in the collected information
344344
useEffect(
345345
() => {
346-
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS && existsConfirmation(confirmations)) {
346+
if (
347+
executionData.status === VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS &&
348+
existsConfirmation(confirmations)
349+
) {
347350
const newStatus = executionData.executionResult
348351
? CONFIRMATIONS_STATUS.SUCCESS
349352
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED

0 commit comments

Comments
 (0)