Build the list of validators and required signatures at the moment of the transaction (#367)

This commit is contained in:
Gerardo Nardelli 2020-06-24 14:47:12 -03:00 committed by GitHub
parent 9e9e891db8
commit 0eb7c41278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 90 deletions

@ -10,6 +10,7 @@ import { ValidatorsConfirmations } from './ValidatorsConfirmations'
import { getConfirmationsStatusDescription } from '../utils/networks'
import { useStateProvider } from '../state/StateProvider'
import { ExecutionConfirmation } from './ExecutionConfirmation'
import { useValidatorContract } from '../hooks/useValidatorContract'
const StatusLabel = styled.label`
font-weight: bold;
@ -43,11 +44,14 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
home: { name: homeName },
foreign: { name: foreignName }
} = useStateProvider()
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
const { confirmations, status, executionData, signatureCollected } = useMessageConfirmations({
message,
receipt,
fromHome,
timestamp
timestamp,
requiredSignatures,
validatorList
})
return (
@ -66,7 +70,11 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
: ''}
</p>
</StatusDescription>
<ValidatorsConfirmations confirmations={confirmations} />
<ValidatorsConfirmations
confirmations={confirmations}
requiredSignatures={requiredSignatures}
validatorList={validatorList}
/>
{signatureCollected && <ExecutionConfirmation executionData={executionData} isHome={!fromHome} />}
</StyledConfirmationContainer>
</div>

@ -1,6 +1,5 @@
import React from 'react'
import { formatTxHashExtended } from '../utils/networks'
import { useStateProvider } from '../state/StateProvider'
import { useWindowWidth } from '@react-hook/window-size'
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { SimpleLoading } from './commons/Loading'
@ -18,12 +17,15 @@ const RequiredConfirmations = styled.label`
export interface ValidatorsConfirmationsParams {
confirmations: Array<ConfirmationParam>
requiredSignatures: number
validatorList: string[]
}
export const ValidatorsConfirmations = ({ confirmations }: ValidatorsConfirmationsParams) => {
const {
home: { requiredSignatures, validatorList }
} = useStateProvider()
export const ValidatorsConfirmations = ({
confirmations,
requiredSignatures,
validatorList
}: ValidatorsConfirmationsParams) => {
const windowWidth = useWindowWidth()
const getValidatorStatusElement = (validatorStatus = '') => {

@ -1,14 +1,9 @@
import { useEffect, useState } from 'react'
import { HOME_AMB_ABI, FOREIGN_AMB_ABI, BRIDGE_VALIDATORS_ABI } from '../../../commons'
import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../../../commons'
import { FOREIGN_BRIDGE_ADDRESS, HOME_BRIDGE_ADDRESS } from '../config/constants'
import { Contract } from 'web3-eth-contract'
import Web3 from 'web3'
import {
getRequiredBlockConfirmations,
getRequiredSignatures,
getValidatorAddress,
getValidatorList
} from '../utils/contract'
import { getRequiredBlockConfirmations } from '../utils/contract'
export interface useBridgeContractsParams {
homeWeb3: Web3
@ -20,9 +15,6 @@ export const useBridgeContracts = ({ homeWeb3, foreignWeb3 }: useBridgeContracts
const [foreignBridge, setForeignBridge] = useState<Maybe<Contract>>(null)
const [homeBlockConfirmations, setHomeBlockConfirmations] = useState(0)
const [foreignBlockConfirmations, setForeignBlockConfirmations] = useState(0)
const [homeValidatorContract, setHomeValidatorContract] = useState<Maybe<Contract>>(null)
const [homeRequiredSignatures, setHomeRequiredSignatures] = useState(0)
const [homeValidatorList, setHomeValidatorList] = useState([])
const callRequireBlockConfirmations = async (contract: Maybe<Contract>, setResult: Function) => {
if (!contract) return
@ -30,31 +22,11 @@ export const useBridgeContracts = ({ homeWeb3, foreignWeb3 }: useBridgeContracts
setResult(result)
}
const callValidatorContract = async (bridgeContract: Maybe<Contract>, web3: Web3, setValidatorContract: Function) => {
if (!web3 || !bridgeContract) return
const address = await getValidatorAddress(bridgeContract)
const contract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, address)
setValidatorContract(contract)
}
const callRequiredSignatures = async (contract: Maybe<Contract>, setResult: Function) => {
if (!contract) return
const result = await getRequiredSignatures(contract)
setResult(result)
}
const callValidatorList = async (contract: Maybe<Contract>, setResult: Function) => {
if (!contract) return
const result = await getValidatorList(contract)
setResult(result)
}
useEffect(
() => {
if (!homeWeb3) return
const homeContract = new homeWeb3.eth.Contract(HOME_AMB_ABI, HOME_BRIDGE_ADDRESS)
callRequireBlockConfirmations(homeContract, setHomeBlockConfirmations)
callValidatorContract(homeContract, homeWeb3, setHomeValidatorContract)
setHomeBridge(homeContract)
},
[homeWeb3]
@ -70,21 +42,10 @@ export const useBridgeContracts = ({ homeWeb3, foreignWeb3 }: useBridgeContracts
[foreignWeb3]
)
useEffect(
() => {
callRequiredSignatures(homeValidatorContract, setHomeRequiredSignatures)
callValidatorList(homeValidatorContract, setHomeValidatorList)
},
[homeValidatorContract]
)
return {
homeBridge,
foreignBridge,
homeBlockConfirmations,
foreignBlockConfirmations,
homeValidatorContract,
homeRequiredSignatures,
homeValidatorList
foreignBlockConfirmations
}
}

@ -29,6 +29,8 @@ export interface useMessageConfirmationsParams {
receipt: Maybe<TransactionReceipt>
fromHome: boolean
timestamp: number
requiredSignatures: number
validatorList: string[]
}
export interface ConfirmationParam {
@ -44,7 +46,14 @@ export interface ExecutionData {
executionResult: boolean
}
export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp }: useMessageConfirmationsParams) => {
export const useMessageConfirmations = ({
message,
receipt,
fromHome,
timestamp,
requiredSignatures,
validatorList
}: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider()
const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
@ -91,7 +100,7 @@ export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp
targetBlock,
setWaitingBlocks,
setWaitingBlocksResolved,
home.validatorList,
validatorList,
setConfirmations,
blockProvider,
interval,
@ -103,15 +112,7 @@ export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp
blockProvider.stop()
}
},
[
foreign.blockConfirmations,
foreign.web3,
fromHome,
home.blockConfirmations,
home.validatorList,
home.web3,
receipt
]
[foreign.blockConfirmations, foreign.web3, fromHome, home.blockConfirmations, validatorList, home.web3, receipt]
)
// The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if
@ -208,11 +209,11 @@ export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp
getConfirmationsForTx(
message.data,
home.web3,
home.validatorList,
validatorList,
home.bridgeContract,
confirmationContractMethod,
setConfirmations,
home.requiredSignatures,
requiredSignatures,
setSignatureCollected,
waitingBlocksResolved,
subscriptions,
@ -231,9 +232,9 @@ export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp
fromHome,
message.data,
home.web3,
home.validatorList,
validatorList,
home.bridgeContract,
home.requiredSignatures,
requiredSignatures,
waitingBlocksResolved,
timestamp
]

@ -0,0 +1,68 @@
import { useEffect, useState } from 'react'
import { Contract } from 'web3-eth-contract'
import Web3 from 'web3'
import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract'
import { BRIDGE_VALIDATORS_ABI } from '../../../commons'
import { useStateProvider } from '../state/StateProvider'
import { TransactionReceipt } from 'web3-eth'
export interface useValidatorContractParams {
fromHome: boolean
receipt: Maybe<TransactionReceipt>
}
export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => {
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
const [requiredSignatures, setRequiredSignatures] = useState(0)
const [validatorList, setValidatorList] = useState([])
const { home, foreign } = useStateProvider()
const callValidatorContract = async (bridgeContract: Maybe<Contract>, web3: Web3, setValidatorContract: Function) => {
if (!web3 || !bridgeContract) return
const address = await getValidatorAddress(bridgeContract)
const contract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, address)
setValidatorContract(contract)
}
const callRequiredSignatures = async (
contract: Maybe<Contract>,
receipt: TransactionReceipt,
setResult: Function
) => {
if (!contract) return
const result = await getRequiredSignatures(contract, receipt.blockNumber)
setResult(result)
}
const callValidatorList = async (contract: Maybe<Contract>, receipt: TransactionReceipt, setResult: Function) => {
if (!contract) return
const result = await getValidatorList(contract, receipt.blockNumber)
setResult(result)
}
useEffect(
() => {
const web3 = fromHome ? home.web3 : foreign.web3
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
if (!web3 || !bridgeContract) return
callValidatorContract(bridgeContract, web3, setValidatorContract)
},
[home.web3, foreign.web3, home.bridgeContract, foreign.bridgeContract, fromHome]
)
useEffect(
() => {
if (!receipt) return
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures)
callValidatorList(validatorContract, receipt, setValidatorList)
},
[validatorContract, receipt]
)
return {
requiredSignatures,
validatorList
}
}

@ -21,14 +21,8 @@ export interface BaseNetworkParams {
blockConfirmations: number
}
export interface HomeNetworkParams extends BaseNetworkParams {
validatorContract: Maybe<Contract>
requiredSignatures: number
validatorList: Array<string>
}
export interface StateContext {
home: HomeNetworkParams
home: BaseNetworkParams
foreign: BaseNetworkParams
loading: boolean
}
@ -40,10 +34,7 @@ const initialState = {
web3: null,
bridgeAddress: HOME_BRIDGE_ADDRESS,
bridgeContract: null,
blockConfirmations: 0,
validatorContract: null,
requiredSignatures: 0,
validatorList: []
blockConfirmations: 0
},
foreign: {
chainId: 0,
@ -61,15 +52,7 @@ const StateContext = createContext<StateContext>(initialState)
export const StateProvider = ({ children }: { children: ReactNode }) => {
const homeNetwork = useNetwork(HOME_RPC_URL)
const foreignNetwork = useNetwork(FOREIGN_RPC_URL)
const {
homeBridge,
foreignBridge,
homeBlockConfirmations,
foreignBlockConfirmations,
homeValidatorContract,
homeRequiredSignatures,
homeValidatorList
} = useBridgeContracts({
const { homeBridge, foreignBridge, homeBlockConfirmations, foreignBlockConfirmations } = useBridgeContracts({
homeWeb3: homeNetwork.web3,
foreignWeb3: foreignNetwork.web3
})
@ -80,9 +63,6 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
name: HOME_NETWORK_NAME,
bridgeContract: homeBridge,
blockConfirmations: homeBlockConfirmations,
validatorContract: homeValidatorContract,
requiredSignatures: homeRequiredSignatures,
validatorList: homeValidatorList,
...homeNetwork
},
foreign: {

@ -7,12 +7,46 @@ export const getRequiredBlockConfirmations = async (contract: Contract) => {
export const getValidatorAddress = (contract: Contract) => contract.methods.validatorContract().call()
export const getRequiredSignatures = async (contract: Contract) => {
const requiredSignatures = await contract.methods.requiredSignatures().call()
export const getRequiredSignatures = async (contract: Contract, blockNumber: number) => {
const events = await contract.getPastEvents('RequiredSignaturesChanged', {
fromBlock: 0,
toBlock: blockNumber
})
// Use the value form last event before the transaction
const event = events[events.length - 1]
const { requiredSignatures } = event.returnValues
return parseInt(requiredSignatures)
}
export const getValidatorList = (contract: Contract) => contract.methods.validatorList().call()
export const getValidatorList = async (contract: Contract, blockNumber: number) => {
let currentList: string[] = await contract.methods.validatorList().call()
const [added, removed] = await Promise.all([
contract.getPastEvents('ValidatorAdded', {
fromBlock: blockNumber
}),
contract.getPastEvents('ValidatorRemoved', {
fromBlock: blockNumber
})
])
// Ordered desc
const orderedEvents = [...added, ...removed].sort(({ blockNumber: prev }, { blockNumber: next }) => next - prev)
// Stored as a Set to avoid duplicates
const validatorList = new Set(currentList)
orderedEvents.forEach(e => {
const { validator } = e.returnValues
if (e.event === 'ValidatorRemoved') {
validatorList.add(validator)
} else if (e.event === 'ValidatorAdded') {
validatorList.delete(validator)
}
})
return Array.from(validatorList)
}
export const getMessagesSigned = (contract: Contract, hash: string) => contract.methods.messagesSigned(hash).call()