Skip to content
This repository has been archived by the owner on Jan 15, 2022. It is now read-only.

Heal #2

Merged
merged 2 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions wrangler/contracts/directory.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# txFunctions.js
https://runkit.com/tyvdh/tss-contract

## Fields
```json
Expand All @@ -17,8 +18,8 @@ W3sibmFtZSI6ImRlc3RpbmF0aW9uIiwidHlwZSI6InN0cmluZyIsImRlc2NyaXB0aW9uIjoiU3RlbGxh

## Contract / Signer
```
3ad932d8b521c627ff5cf0a1af5e5b6276d4af69118f3b83a9a3c0d1934ef457
GDM2AIT22QYQUCHRYLK5C2N45ZPZ6SFR7AACQMVD6L4UF566D2ZBEH7F
78565516a844fd4dfc5a7fc7da822028b04ee0aeaf981a4a914d4510906a7a32
GB2BZXNG3JLUX4HYWRHREDMPJ4YINBLEHGZB4US2NJ2IPFNK65MCOOLG
```

## Controlled Account
Expand Down
2 changes: 1 addition & 1 deletion wrangler/contracts/txFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
body: JSON.stringify(body)
})
.then(async (res) => {
.then((res) => {
if (res.ok)
return res.text()
throw res
Expand Down
51 changes: 46 additions & 5 deletions wrangler/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
"deploy": "wrangler publish"
},
"dependencies": {
"@iarna/toml": "^2.2.5",
"bignumber.js": "^9.0.1",
"bluebird": "^3.7.2",
"cfw-easy-utils": "^0.2.3",
"lodash": "^4.17.21",
"sha.js": "^2.4.11",
"stellar-base": "^4.0.3",
"tiny-request-router": "^1.2.2"
Expand Down
173 changes: 173 additions & 0 deletions wrangler/src/ctrlAccounts/heal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { response } from 'cfw-easy-utils'
import { Keypair, TransactionBuilder, Networks, BASE_FEE, Operation, Account } from 'stellar-base'
import { map, find, compact, intersection, chain } from 'lodash'
import Bluebird from 'bluebird'
import { parse } from '@iarna/toml'

import txFunctionsGet from '../txFunctions/get'
import turretToml from '../turret/toml'

export default async ({ request, params }) => {
const {
functionHash: txFunctionHash,
sourceAccount,
removeTurret,
addTurret
} = await request.json()
const { value, metadata } = await TX_FUNCTIONS.getWithMetadata(txFunctionHash, 'arrayBuffer')

if (!value)
throw {status: 404, message: `txFunction could not be found this turret`}

const { ctrlAccount } = params
const horizon = STELLAR_NETWORK === 'PUBLIC' ? 'https://horizon.stellar.org' : 'https://horizon-testnet.stellar.org'

const { requiredThreshold, existingSigners } = await fetch(`${horizon}/accounts/${ctrlAccount}`)
.then((res) => {
if (res.ok)
return res.json()
throw res
})
.then((account) => {
const existingSigners = map(account.data, (value, key) => {
value = Buffer.from(value, 'base64').toString('utf8')

const signer = find(account.signers, {key: value})

return key.indexOf('TSS') === 0 ? {
turret: key.replace('TSS_', ''),
signer: value,
weight: signer?.weight || 0
} : null
})

return {
requiredThreshold: account.thresholds.high_threshold,
existingSigners
}
})

// return response.json({requiredThreshold, existingSigners})

const incomingTurretKeys = await Bluebird.map(
chain([
{turret: addTurret},
...existingSigners
])
.orderBy('weight', 'asc')
.uniqBy('turret')
.value(),
async (signer) => ({
...signer,
...await fetch(`${horizon}/accounts/${signer.turret}`)
.then((res) => {
if (res.ok)
return res.json()
throw res
})
.then((account) => {
const { url } = request
const { hostname } = new URL(url)

return (
hostname === account.home_domain // Workers can't call themselves
? txFunctionsGet({params: {txFunctionHash}})
: fetch(`https://${account.home_domain}/tx-functions/${txFunctionHash}`)
)
.then((res) => {
if (res.ok)
return res.json()
throw res
})
.then(async (data) => ({
signer: data.signer,
toml: await (
hostname === account.home_domain // Workers can't call themselves
? turretToml()
: fetch(`https://${account.home_domain}/.well-known/stellar.toml`)
)
.then(async (res) => {
if (res.ok)
return parse(await res.text())
throw res
})
}))
})
.catch(() => null)
})
)

// return response.json({incomingTurretKeys})

const removeSigner = find(incomingTurretKeys, {turret: removeTurret})

if (!removeSigner || removeSigner.toml)
throw {status: 400, message: 'Signer is not able to be removed'}

const addSigner = find(incomingTurretKeys, {turret: addTurret})

if (!addSigner || !addSigner.toml)
throw {status: 400, message: 'Signer is not able to be added'}

const hasThreshold = chain(incomingTurretKeys)
.filter((signer) => signer.toml && signer.weight)
.sumBy('weight')
.value()

// return response.json({hasThreshold, requiredThreshold})

if (hasThreshold < requiredThreshold)
throw {status: 400, message: 'Insufficient signer threshold'}

const turrets = intersection(...compact(map(incomingTurretKeys, 'toml.TSS.TURRETS')))

if (turrets.indexOf(addSigner.turret) === -1)
throw {status: 400, message: `New turret isn't trusted by existing signer turrets`}

const transaction = await fetch(`${horizon}/accounts/${sourceAccount}`)
.then((res) => {
if (res.ok)
return res.json()
throw res
})
.then((account) => new TransactionBuilder(
new Account(account.id, account.sequence),
{
fee: BASE_FEE,
networkPassphrase: Networks[STELLAR_NETWORK]
}
)
.addOperation(Operation.setOptions({
signer: {
ed25519PublicKey: removeSigner.signer,
weight: 0
}
}))
.addOperation(Operation.setOptions({
signer: {
ed25519PublicKey: addSigner.signer,
weight: removeSigner.weight
}
}))
.addOperation(Operation.manageData({
name: `TSS_${removeSigner.turret}`,
value: null,
}))
.addOperation(Operation.manageData({
name: `TSS_${addSigner.turret}`,
value: addSigner.signer,
}))
.setTimeout(0)
.build())

const { txFunctionSignerPublicKey, txFunctionSignerSecret } = metadata

const txFunctionSignerKeypair = Keypair.fromSecret(txFunctionSignerSecret)
const txFunctionSignature = txFunctionSignerKeypair.sign(transaction.hash()).toString('base64')

return response.json({
xdr: transaction.toXDR(),
signer: txFunctionSignerPublicKey,
signature: txFunctionSignature
})
}
11 changes: 7 additions & 4 deletions wrangler/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ import turretDetails from './turret/details'
import txFunctionsGet from './txFunctions/get'
import txFunctionsUpload from './txFunctions/upload'
import txFunctionsRun from './txFunctions/run'
// import txFunctionsHeal from './txFunctions/heal'

import ctrlAccountsHeal from './ctrlAccounts/heal'

const router = new Router()

router
.get('/.well-known/stellar.toml', turretToml)
.get('/', turretDetails)
.get('/.well-known/stellar.toml', turretToml)

router
.get('/tx-functions/:txFunctionHash', txFunctionsGet)
.post('/tx-functions', txFunctionsUpload)
.get('/tx-functions/:txFunctionHash', txFunctionsGet)
.post('/tx-functions/:txFunctionHash', txFunctionsRun)
// .put('/tx-functions', txFunctionsHeal)

router
.put('/ctrl-accounts/:ctrlAccount', ctrlAccountsHeal)

async function handleRequest(event) {
try {
Expand Down
4 changes: 2 additions & 2 deletions wrangler/src/turret/details.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export default async () => {
})

return response.json({
stellarNetwork: STELLAR_NETWORK,
turretAddress: TURRET_ADDRESS,
network: STELLAR_NETWORK,
turret: TURRET_ADDRESS,
functions: txFunctions
}, {
headers: {
Expand Down
2 changes: 1 addition & 1 deletion wrangler/src/turret/toml.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default async () => {
const stellarToml = await META.get('STELLAR_TOML')

if (!stellarToml)
throw {status: 404}
throw {status: 404, message: `stellar.toml file could not be found on this turret`}

return response.text(stellarToml, {
headers: {
Expand Down
2 changes: 1 addition & 1 deletion wrangler/src/txFunctions/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default async ({ params }) => {
const { value, metadata } = await TX_FUNCTIONS.getWithMetadata(txFunctionHash, 'arrayBuffer')

if (!value)
throw {status: 404}
throw {status: 404, message: `txFunction could not be found this turret`}

const { length, txFunctionSignerPublicKey } = metadata

Expand Down
Empty file removed wrangler/src/txFunctions/heal.js
Empty file.
2 changes: 1 addition & 1 deletion wrangler/src/txFunctions/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default async ({ request, params }) => {
const { value, metadata } = await TX_FUNCTIONS.getWithMetadata(txFunctionHash, 'arrayBuffer')

if (!value)
throw {status: 404}
throw {status: 404, message: `txFunction could not be found this turret`}

const { length, txFunctionSignerPublicKey, txFunctionSignerSecret } = metadata

Expand Down
Loading