Skip to content

Commit

Permalink
post multisig run through updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kaladinlight committed Jul 24, 2024
1 parent a9faad7 commit ab54bc5
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 55 deletions.
32 changes: 22 additions & 10 deletions cli/MultiSig.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
## Prerequisites

- Install golang: https://go.dev/doc/install
- Install golang (v1.22): https://go.dev/doc/install
- Create common rfox directory in your home directory. This is where all output files from the script will be stored and where shared files (unsigned transactions, signatures, signed transactions, etc.) should be saved.
```bash
mkdir ~/rfox
```

## Clone and Build

```bash
git clone https://gitlab.com/thorchain/thornode.git
cd thornode/cmd/thornode
cd thornode
git checkout develop
git pull
cd cmd/thornode
go build --tags cgo,ledger
```

Expand All @@ -22,8 +29,12 @@ go build --tags cgo,ledger
```
- Import signer pubkeys:
```bash
./thornode keys add {person2} --pubkey {pubkey}
./thornode keys add {person3} --pubkey {pubkey}
./thornode keys add {person2} --pubkey '{person2_pubkey}'
./thornode keys add {person3} --pubkey '{person3_pubkey}'
```
- View keys:
```bash
./thornode keys list
```
- Add multisig key:
```bash
Expand All @@ -38,29 +49,30 @@ go build --tags cgo,ledger

- Person 1 signs:
```bash
./thornode tx sign --from {person1} --multisig multisig {unsignedTx_epoch-N.json} --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --from ledger --ledger --sign-mode amino-json > signedTx_{person1}.json
./thornode tx sign --from {person1} --multisig multisig ~/rfox/unsignedTx_epoch-{N}.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --ledger --sign-mode amino-json > ~/rfox/signedTx_epoch-{N}_{person1}.json
```
- Person 2 signs:
```bash
./thornode tx sign --from {person2} --multisig multisig {unsignedTx_epoch-N.json} --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --from ledger --ledger --sign-mode amino-json > signedTx_{person2}.json
./thornode tx sign --from {person2} --multisig multisig ~/rfox/unsignedTx_epoch-{N}.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --ledger --sign-mode amino-json > ~/rfox/signedTx_epoch-{N}_{person2}.json
```
- Multisign:
```bash
./thornode tx multisign {unsignedTx_epoch-N.json} multisig signedTx_{person1}.json signedTx_{person2}.json --from multisig --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc > signedTx_multisig.json
./thornode tx multisign ~/rfox/unsignedTx_epoch-{N}.json multisig ~/rfox/signedTx_epoch-{N}_{person1}.json ~/rfox/signedTx_epoch-{N}_{person2}.json --from multisig --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc > ~/rfox/signedTx_epoch-{N}_multisig.json
```

## Send Transaction

- Simulate transaction:

```bash
./thornode tx broadcast signedTx_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto --dry-run > simulatedTx.json
./thornode tx broadcast ~/rfox/signedTx_epoch-{N}_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --dry-run > ~/rfox/simulatedTx_epoch-{N}.json
```

- Validate contents of `simulatedTx.json` for accuracy before broadcasting

- Broadcast transaction:
```bash
./thornode tx broadcast signedTx_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto > tx.json
./thornode tx broadcast ~/rfox/signedTx_epoch-{N}_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto > tx.json
```
- Copy the `txhash` value from `tx.json` to supply to the cli in order to continue

At this point, the cli should pick up the funding transaction and continue running the distribution from the hot wallet.
37 changes: 22 additions & 15 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { isEpochDistributionStarted } from './file'
import { IPFS } from './ipfs'
import { error, info, success, warn } from './logging'
import { create, recoverKeystore } from './mnemonic'
import { EpochWithHash } from './types'
import { Epoch, RFOXMetadata } from './types'
import { Wallet } from './wallet'

const processEpoch = async () => {
Expand Down Expand Up @@ -109,14 +109,15 @@ const processEpoch = async () => {
const run = async () => {
const ipfs = await IPFS.new()

const epoch = await ipfs.getEpochFromMetadata()
const metadata = await ipfs.getMetadata('process')
const epoch = await ipfs.getEpochFromMetadata(metadata)

if (isEpochDistributionStarted(epoch.number)) {
const confirmed = await prompts.confirm({
message: 'It looks like you have already started a distribution for this epoch. Do you want to continue? ',
})

if (confirmed) return recover(epoch)
if (confirmed) return recover(metadata)

info(`Please move or delete all existing files for epoch-${epoch.number} from ${RFOX_DIR} before re-running.`)
warn('This action should never be taken unless you are absolutely sure you know what you are doing!!!')
Expand All @@ -137,22 +138,24 @@ const run = async () => {

const wallet = await Wallet.new(mnemonic)

await processDistribution(epoch, wallet, ipfs)
await processDistribution(metadata, epoch, wallet, ipfs)
}

const recover = async (epoch?: EpochWithHash) => {
const recover = async (metadata?: RFOXMetadata) => {
const ipfs = await IPFS.new()

if (!epoch) {
epoch = await ipfs.getEpochFromMetadata()
if (!metadata) {
metadata = await ipfs.getMetadata('process')
}

const epoch = await ipfs.getEpochFromMetadata(metadata)

const keystoreFile = path.join(RFOX_DIR, `keystore_epoch-${epoch.number}.txt`)
const mnemonic = await recoverKeystore(keystoreFile)

const wallet = await Wallet.new(mnemonic)

await processDistribution(epoch, wallet, ipfs)
await processDistribution(metadata, epoch, wallet, ipfs)
}

const update = async () => {
Expand All @@ -170,18 +173,22 @@ const update = async () => {
)
}

const processDistribution = async (epoch: EpochWithHash, wallet: Wallet, ipfs: IPFS) => {
await wallet.fund(epoch)
const processedEpoch = await wallet.distribute(epoch)
const processDistribution = async (metadata: RFOXMetadata, epoch: Epoch, wallet: Wallet, ipfs: IPFS) => {
const epochHash = metadata.ipfsHashByEpoch[epoch.number]

const processedEpochHash = await ipfs.addEpoch(processedEpoch)
const metadata = await ipfs.getMetadata('process')
await wallet.fund(epoch, epochHash)
const processedEpoch = await wallet.distribute(epoch, epochHash)

const hash = await ipfs.updateMetadata(metadata, {
const processedEpochHash = await ipfs.addEpoch({
...processedEpoch,
distributionStatus: 'complete',
})

const metadataHash = await ipfs.updateMetadata(metadata, {
epoch: { number: processedEpoch.number, hash: processedEpochHash },
})

if (!hash) return
if (!metadataHash) return

success(`rFOX reward distribution for Epoch #${processedEpoch.number} has been completed!`)

Expand Down
22 changes: 4 additions & 18 deletions cli/src/ipfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PinataClient from '@pinata/sdk'
import axios from 'axios'
import BigNumber from 'bignumber.js'
import { error, info } from './logging'
import { Epoch, EpochWithHash, RFOXMetadata, RewardDistribution } from './types'
import { Epoch, RFOXMetadata, RewardDistribution } from './types'
import { MONTHS } from './constants'

const PINATA_API_KEY = process.env['PINATA_API_KEY']
Expand Down Expand Up @@ -99,7 +99,7 @@ export class IPFS {
}
}

async getEpoch(hash?: string): Promise<EpochWithHash> {
async getEpoch(hash?: string): Promise<Epoch> {
if (!hash) {
hash = await prompts.input({
message: 'What is the IPFS hash for the rFOX epoch you want to process? ',
Expand Down Expand Up @@ -127,7 +127,7 @@ export class IPFS {
`Running ${month} rFOX reward distribution for Epoch #${data.number}:\n - Total Rewards: ${totalRewards} RUNE\n - Total Addresses: ${totalAddresses}`,
)

return { ...data, hash }
return data
} else {
error(`The contents of IPFS hash (${hash}) are not valid epoch contents, exiting.`)
process.exit(1)
Expand All @@ -153,18 +153,6 @@ export class IPFS {
metadata.epochEndTimestamp = overrides.metadata.epochEndTimestamp

if (overrides.epoch) {
const hash = metadata.ipfsHashByEpoch[overrides.epoch.number]

if (hash) {
info(`The metadata already contains an IPFS hash for this epoch: ${hash}`)

const confirmed = await prompts.confirm({
message: `Do you want to update the metadata with the new IPFS hash: ${overrides.epoch.hash}?`,
})

if (!confirmed) return
}

metadata.ipfsHashByEpoch[overrides.epoch.number] = overrides.epoch.hash

const { IpfsHash } = await this.client.pinJSONToIPFS(metadata, {
Expand Down Expand Up @@ -308,9 +296,7 @@ export class IPFS {
}
}

async getEpochFromMetadata(): Promise<EpochWithHash> {
const metadata = await this.getMetadata('process')

async getEpochFromMetadata(metadata: RFOXMetadata): Promise<Epoch> {
const hash = metadata.ipfsHashByEpoch[metadata.epoch - 1]

if (!hash) {
Expand Down
2 changes: 0 additions & 2 deletions cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,3 @@ export type Epoch = {
/** A record of staking address to reward distribution for this epoch */
distributionsByStakingAddress: Record<string, RewardDistribution>
}

export type EpochWithHash = Epoch & { hash: string }
20 changes: 10 additions & 10 deletions cli/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ora, { Ora } from 'ora'
import { RFOX_DIR } from './constants'
import { read, write } from './file'
import { error, info, success } from './logging'
import { EpochWithHash } from './types'
import { Epoch } from './types'

const BIP32_PATH = `m/44'/931'/0'/0/0`
const SHAPESHIFT_MULTISIG_ADDRESS = 'thor1xmaggkcln5m5fnha2780xrdrulmplvfrz6wj3l'
Expand Down Expand Up @@ -70,7 +70,7 @@ export class Wallet {
}
}

private async buildFundingTransaction(amount: string, epoch: EpochWithHash) {
private async buildFundingTransaction(amount: string, epoch: Epoch, hash: string) {
const { address } = await this.getAddress()

return {
Expand All @@ -88,7 +88,7 @@ export class Wallet {
],
},
],
memo: `Fund rFOX rewards distribution - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`,
memo: `Fund rFOX rewards distribution - Epoch #${epoch.number} (IPFS Hash: ${hash})`,
timeout_height: '0',
extension_options: [],
non_critical_extension_options: [],
Expand All @@ -106,7 +106,7 @@ export class Wallet {
}
}

async fund(epoch: EpochWithHash) {
async fund(epoch: Epoch, epochHash: string) {
const { address } = await this.getAddress()

const distributions = Object.values(epoch.distributionsByStakingAddress)
Expand Down Expand Up @@ -152,7 +152,7 @@ export class Wallet {

if (await isFunded()) return

const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch)
const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch, epochHash)
const unsignedTxFile = path.join(RFOX_DIR, `unsignedTx_epoch-${epoch.number}.json`)

write(unsignedTxFile, JSON.stringify(unsignedTx, null, 2))
Expand All @@ -173,7 +173,7 @@ export class Wallet {
})()
}

private async signTransactions(epoch: EpochWithHash): Promise<TxsByStakingAddress> {
private async signTransactions(epoch: Epoch, epochHash: string): Promise<TxsByStakingAddress> {
const txsFile = path.join(RFOX_DIR, `txs_epoch-${epoch.number}.json`)
const txs = read(txsFile)

Expand Down Expand Up @@ -230,7 +230,7 @@ export class Wallet {
amount: [],
gas: '0',
},
memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`,
memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number} (IPFS Hash: ${epochHash})`,
signatures: [],
},
}
Expand Down Expand Up @@ -273,7 +273,7 @@ export class Wallet {
return txsByStakingAddress
}

async broadcastTransactions(epoch: EpochWithHash, txsByStakingAddress: TxsByStakingAddress): Promise<EpochWithHash> {
async broadcastTransactions(epoch: Epoch, txsByStakingAddress: TxsByStakingAddress): Promise<Epoch> {
const totalTxs = Object.values(epoch.distributionsByStakingAddress).length
const spinner = ora(`Broadcasting ${totalTxs} transactions...`).start()

Expand Down Expand Up @@ -330,8 +330,8 @@ export class Wallet {
return epoch
}

async distribute(epoch: EpochWithHash): Promise<EpochWithHash> {
const txsByStakingAddress = await this.signTransactions(epoch)
async distribute(epoch: Epoch, epochHash: string): Promise<Epoch> {
const txsByStakingAddress = await this.signTransactions(epoch, epochHash)
return this.broadcastTransactions(epoch, txsByStakingAddress)
}
}

0 comments on commit ab54bc5

Please sign in to comment.