Skip to content

Commit

Permalink
Merge pull request #1926 from cardanoapi/develop
Browse files Browse the repository at this point in the history
Add Guardrail Script Field and Multiple Testnet Support
  • Loading branch information
mesudip authored Sep 9, 2024
2 parents c7b24c6 + 1c90e28 commit c438146
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 314 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-and-deploy-test-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ jobs:
USERSNAP_SPACE_API_KEY: ${{ secrets.USERSNAP_SPACE_API_KEY }}
APP_ENV: test
PDF_API_URL: ${{ secrets.PDF_API_URL }}
KUBER_API_KEY: ${{secrets.KUBER_API_KEY}}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ node_storage/

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

.venv
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Expand Down
4 changes: 3 additions & 1 deletion gov-action-loader/backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
KUBER_API_URL=https://sanchonet.kuber.cardanoapi.io
KUBER_API_URL_SANCHO=https://sanchonet.kuber.cardanoapi.io
KUBER_API_URL_PREVIEW=https://preview.kuber.cardanoapi.io
KUBER_API_URL_PREPROD=https://preprod.kuber.cardanoapi.io
KUBER_API_KEY=xxxxxxxxxxxxx
5 changes: 5 additions & 0 deletions gov-action-loader/backend/app/data/gov-script.plutus
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "PlutusScriptV3",
"description": "",
"cborHex": "5908545908510101003232323232323232323232323232323232323232323232323232323232323232323232323232323232259323255333573466e1d20000011180098111bab357426ae88d55cf00104554ccd5cd19b87480100044600422c6aae74004dd51aba1357446ae88d55cf1baa3255333573466e1d200a35573a002226ae84d5d11aab9e00111637546ae84d5d11aba235573c6ea800642b26006003149a2c8a4c301f801c0052000c00e0070018016006901e4070c00e003000c00d20d00fc000c0003003800a4005801c00e003002c00d20c09a0c80e1801c006001801a4101b5881380018000600700148013003801c006005801a410100078001801c006001801a4101001f8001800060070014801b0038018096007001800600690404002600060001801c0052008c00e006025801c006001801a41209d8001800060070014802b003801c006005801a410112f501c3003800c00300348202b7881300030000c00e00290066007003800c00b003482032ad7b806038403060070014803b00380180960003003800a4021801c00e003002c00d20f40380e1801c006001801a41403f800100a0c00e0029009600f0030078040c00e002900a600f003800c00b003301a483403e01a600700180060066034904801e00060001801c0052016c01e00600f801c006001801980c2402900e30000c00e002901060070030128060c00e00290116007003800c00b003483c0ba03860070018006006906432e00040283003800a40498003003800a404d802c00e00f003800c00b003301a480cb0003003800c003003301a4802b00030001801c01e0070018016006603490605c0160006007001800600660349048276000600030000c00e0029014600b003801c00c04b003800c00300348203a2489b00030001801c00e006025801c006001801a4101b11dc2df80018000c0003003800a4055802c00e007003012c00e003000c00d2080b8b872c000c0006007003801809600700180060069040607e4155016000600030000c00e00290166007003012c00e003000c00d2080c001c000c0003003800a405d801c00e003002c00d20c80180e1801c006001801a412007800100a0c00e00290186007003013c0006007001480cb005801801e006003801800e00600500403003800a4069802c00c00f003001c00c007003803c00e003002c00c05300333023480692028c0004014c00c00b003003c00c00f003003c00e00f003800c00b00301480590052008003003800a406d801c00e003002c00d2000c00d2006c00060070018006006900a600060001801c0052038c00e007001801600690006006901260003003800c003003483281300020141801c005203ac00e006027801c006001801a403d800180006007001480f3003801804e00700180060069040404af3c4e302600060001801c005203ec00e006013801c006001801a4101416f0fd20b80018000600700148103003801c006005801a403501c3003800c0030034812b00030000c00e0029021600f003800c00a01ac00e003000c00ccc08d20d00f4800b00030000c0000000000803c00c016008401e006009801c006001801807e0060298000c000401e006007801c0060018018074020c000400e00f003800c00b003010c000802180020070018006006019801805e0003000400600580180760060138000800c00b00330134805200c400e00300080330004006005801a4001801a410112f58000801c00600901260008019806a40118002007001800600690404a75ee01e00060008018046000801801e000300c4832004c025201430094800a0030028052003002c00d2002c000300648010c0092002300748028c0312000300b48018c0292012300948008c0212066801a40018000c0192008300a2233335573e00250002801994004d55ce800cd55cf0008d5d08014c00cd5d10011263009222532900389800a4d2219002912c80344c01526910c80148964cc04cdd68010034564cc03801400626601800e0071801226601800e01518010096400a3000910c008600444002600244004a664600200244246466004460044460040064600444600200646a660080080066a00600224446600644b20051800484ccc02600244666ae68cdc3801000c00200500a91199ab9a33710004003000801488ccd5cd19b89002001800400a44666ae68cdc4801000c00a00122333573466e20008006005000912a999ab9a3371200400222002220052255333573466e2400800444008440040026eb400a42660080026eb000a4264666015001229002914801c8954ccd5cd19b8700400211333573466e1c00c006001002118011229002914801c88cc044cdc100200099b82002003245200522900391199ab9a3371066e08010004cdc1001001c002004403245200522900391199ab9a3371266e08010004cdc1001001c00a00048a400a45200722333573466e20cdc100200099b820020038014000912c99807001000c40062004912c99807001000c400a2002001199919ab9a357466ae880048cc028dd69aba1003375a6ae84008d5d1000934000dd60010a40064666ae68d5d1800c0020052225933006003357420031330050023574400318010600a444aa666ae68cdc3a400000222c22aa666ae68cdc4000a4000226600666e05200000233702900000088994004cdc2001800ccdc20010008cc010008004c01088954ccd5cd19b87480000044400844cc00c004cdc300100091119803112c800c60012219002911919806912c800c4c02401a442b26600a004019130040018c008002590028c804c8888888800d1900991111111002a244b267201722222222008001000c600518000001112a999ab9a3370e004002230001155333573466e240080044600823002229002914801c88ccd5cd19b893370400800266e0800800e00100208c8c0040048c0088cc008008005"
}
14 changes: 8 additions & 6 deletions gov-action-loader/backend/app/funds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

from fastapi import HTTPException

from app.network import get_api_url
from app.settings import settings
from app.transaction import (default_proposal_deposit_ada, main_wallet,
submit_tx)


async def get_ada_balance(address, client):
utxo_url = settings.kuber_api_url + "/api/v3/utxo"
async def get_ada_balance(address, client, network):

utxo_url = get_api_url(network) + "/api/v3/utxo"
kuber_response = await client.get(
utxo_url,
params={"address": address},
Expand All @@ -25,8 +27,8 @@ async def get_ada_balance(address, client):
)


async def get_protocol_params(client):
pParamsQuery = settings.kuber_api_url + "/api/v3/protocol-params"
async def get_protocol_params(client, network):
pParamsQuery = get_api_url(network) + "/api/v3/protocol-params"
kuber_response = await client.get(
pParamsQuery,
headers={"api-key": settings.kuber_api_key},
Expand All @@ -41,7 +43,7 @@ async def get_protocol_params(client):


async def check_balance_and_fund_wallets(
wallets, supported_proposals_in_single_tx, per_proposal_deposit, client
wallets, supported_proposals_in_single_tx, per_proposal_deposit, client, network
):
wallets_with_balance = await asyncio.gather(
*[get_ada_balance(wallet["address"], client) for wallet in wallets]
Expand Down Expand Up @@ -82,7 +84,7 @@ async def check_balance_and_fund_wallets(
for wallet in low_balance_wallets
],
}
tx = await submit_tx(fund_from_main_tx, client)
tx = await submit_tx(fund_from_main_tx, client, network)
raise HTTPException(
status_code=412,
detail="This action required multiple transaction and wallet setup TX:"
Expand Down
45 changes: 19 additions & 26 deletions gov-action-loader/backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import asyncio
import json
import math
from typing import Any, Dict
from typing import Any, Dict, Literal

import httpx
from fastapi import Depends, FastAPI, HTTPException
from fastapi import Depends, FastAPI, HTTPException, Header

from app.cors import add_cors
from app.funds import (check_balance_and_fund_wallets, get_ada_balance,
get_protocol_params)
from app.http_utils import get_client
from app.models import MultipleProposal
from app.network import get_api_url
from app.settings import settings
from app.transaction import (get_base_proposal_for_multiple,
get_default_transaction,
get_proposal_data_from_type,
main_wallet, submit_proposal_tx)
main_wallet, submit_proposal_tx,
get_gov_script
)

app = FastAPI()
add_cors(app)
Expand All @@ -25,13 +28,14 @@
async def submit_multiple_proposals(
multi_proposal: MultipleProposal,
client: httpx.AsyncClient = Depends(get_client),
network: str = Header(...)
):
required_proposals = multi_proposal.no_of_proposals
base_proposal = get_base_proposal_for_multiple()

supported_proposals_in_single_tx = 50
maximum_supported_proposals = 10000
pparams = await get_protocol_params(client)
pparams = await get_protocol_params(client, network)

if required_proposals <= supported_proposals_in_single_tx:
tx = await submit_proposal_tx(
Expand All @@ -40,6 +44,7 @@ async def submit_multiple_proposals(
| get_proposal_data_from_type(multi_proposal.proposal_type, pparams),
required_proposals,
client,
network
)
return [{"proposal_count": required_proposals, "tx_hash": tx}]
elif required_proposals <= maximum_supported_proposals:
Expand Down Expand Up @@ -73,6 +78,7 @@ async def submit_multiple_proposals(
supported_proposals_in_single_tx,
per_proposal_deposit,
client,
network
)

proposals_numbers_in_last_tx = (
Expand All @@ -93,6 +99,7 @@ async def submit_multiple_proposals(
if wallet != required_wallets[-1]
else proposals_numbers_in_last_tx,
client,
network
)
for wallet in required_wallets
]
Expand All @@ -111,21 +118,26 @@ async def submit_multiple_proposals(


@app.get("/api/balance")
async def getWalletBalance(client: httpx.AsyncClient = Depends(get_client)):
return await get_ada_balance(main_wallet["address"], client)
async def getWalletBalance(client: httpx.AsyncClient = Depends(get_client),
network: str = Header(...)):
return await get_ada_balance(main_wallet["address"], client, network)


@app.post("/api/load/single")
async def submit_single_proposal(
proposal: Dict[str, Any],
client: httpx.AsyncClient = Depends(get_client),
network: str = Header(...)
):
default_transaction = get_default_transaction()
default_proposal_data = default_transaction["proposals"][0]
combined_proposal = default_proposal_data | proposal
default_transaction["proposals"][0] = combined_proposal

tx_url = settings.kuber_api_url + "/api/v1/tx?submit=true"
if "withdraw" in combined_proposal or "parameterupdate" in combined_proposal:
if combined_proposal["script"]["cborHex"] == "":
combined_proposal["script"] = get_gov_script()
tx_url = get_api_url(network) + "/api/v1/tx?submit=true"
kuber_response = await client.post(
tx_url,
json=default_transaction,
Expand All @@ -141,22 +153,3 @@ async def submit_single_proposal(
raise HTTPException(
status_code=kuber_response.status_code, detail=kuber_response.text
)


@app.get("/api/blockfrost/transaction/{tx_hash}")
async def get_transaction_from_blockfrost(
tx_hash: str,
client: httpx.AsyncClient = Depends(get_client),
):
tx_url = settings.blockfrost_api_url + "/txs/" + tx_hash
tx_response = await client.get(
tx_url,
headers={"project_id": settings.blockfrost_project_id},
)
if tx_response.status_code == 200:
return tx_response.json()
else:
print(tx_response.text)
raise HTTPException(
status_code=tx_response.status_code, detail=tx_response.json()
)
1 change: 1 addition & 0 deletions gov-action-loader/backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
class MultipleProposal(BaseModel):
proposal_type: str
no_of_proposals: int

15 changes: 15 additions & 0 deletions gov-action-loader/backend/app/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import HTTPException
from app.settings import settings

NETWORKS = {"preview", "preprod", "sanchonet"}

def get_api_url (network):
network_l_case = network.lower()
if network_l_case == "sancho" or network_l_case == "sanchonet":
return settings.kuber_api_url_sancho
elif network_l_case == "preview":
return settings.kuber_api_url_preview
elif network_l_case == "preprod":
return settings.kuber_api_url_preprod
else:
raise HTTPException(status_code=400, detail=f"Invalid network: {network}. Expected: {NETWORKS}")
4 changes: 3 additions & 1 deletion gov-action-loader/backend/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@


class Settings(BaseSettings):
kuber_api_url: str
kuber_api_url_sancho: str
kuber_api_url_preview: str
kuber_api_url_preprod: str
kuber_api_key: str = ''

settings = Settings()
75 changes: 59 additions & 16 deletions gov-action-loader/backend/app/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from fastapi import HTTPException

from app.network import get_api_url
from app.settings import settings

main_wallet = {
Expand Down Expand Up @@ -90,12 +91,16 @@ def generate_quorom():
return {"numerator": numerator, "denominator": denomintor}


def generate_hardfork():
majorProtocolNum = random.randint(1, 9)
minorProtocolNum = random.randint(1, 9)
def generate_hardfork(current_pParams):
protocol_version = [current_pParams["protocolVersion"]["major"],
current_pParams["protocolVersion"]["minor"]]

# Randomly select an index (0 for major, 1 for minor) and increment it
protocol_version[random.randint(0, 1)] += 1

return {
"hardfork": {
"protocolVersion": {"major": majorProtocolNum, "minor": minorProtocolNum}
"protocolVersion": {"major": protocol_version[0], "minor": protocol_version[1]}
}
}

Expand All @@ -115,6 +120,13 @@ def change_pp_value(random_parameter):
return random_parameter


def get_gov_script():
with open("app/data/gov-script.plutus", "r") as script_file:
script_str = script_file.read()
script_json = json.loads(script_str)
return script_json


def get_proposal_data_from_type(proposal_type, current_pParams):
match proposal_type:
case "constitution":
Expand All @@ -126,7 +138,10 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
}
case "withdrawal":
number_of_addresses = random.randint(1, 5)
return {"withdraw": generate_withdraw(number_of_addresses)}
return {
"script": get_gov_script(),
"withdraw": generate_withdraw(number_of_addresses),
}
case "no-confidence":
return {"noconfidence": True}
case "update-committee":
Expand All @@ -138,15 +153,18 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
}
}
case "hardfork":
return generate_hardfork()
return generate_hardfork(current_pParams)
case "update-parameters":
# read current protocol parameters from json
# get one of the keys of the pp
pParams = json.loads(json.dumps(current_pParams))
keys = pParams.keys()
rand_key = random.choice(filter_updatable_paramKeys(list(keys)))
# recurse into the innermost element of that key and change it by +-1, all protocol parameter value is either float or int
return {"parameterupdate": {rand_key: change_pp_value(pParams[rand_key])}}
return {
"script": get_gov_script(),
"parameterupdate": {rand_key: change_pp_value(pParams[rand_key])},
}
# added_proposal =
case "info":
return {}
Expand All @@ -160,17 +178,42 @@ def get_proposal_data_from_type(proposal_type, current_pParams):


def filter_updatable_paramKeys(keys):
updatable_keys = {"maxBlockSize", "maxBBSize", "maxTxSize", "maxBHSize", "keyDeposit", "poolDeposit", "eMax",
"nOpt", "a0", "rho", "tau", "minPoolCost", "coinsPerUTxOByte", "costModels", "prices",
"maxTxExUnits", "maxBlockExUnits", "maxValSize", "collateralPercentage", "maxCollateralInputs",
"poolVotingThresholds", "dRepVotingThresholds", "committeeMinSize", "committeeMaxTermLength",
"govActionLifetime", "govActionDeposit", "dRepDeposit", "dRepActivity"}
updatable_keys = {
"maxBlockSize",
"maxBBSize",
"maxTxSize",
"maxBHSize",
"keyDeposit",
"poolDeposit",
"eMax",
"nOpt",
"a0",
"rho",
"tau",
"minPoolCost",
"coinsPerUTxOByte",
"costModels",
"prices",
"maxTxExUnits",
"maxBlockExUnits",
"maxValSize",
"collateralPercentage",
"maxCollateralInputs",
"poolVotingThresholds",
"dRepVotingThresholds",
"committeeMinSize",
"committeeMaxTermLength",
"govActionLifetime",
"govActionDeposit",
"dRepDeposit",
"dRepActivity",
}
return [x for x in keys if x in updatable_keys]


async def submit_tx(tx, client, submit=True):
async def submit_tx(tx, client, network, submit=True):
submit_query = "?submit=true" if submit else ""
tx_url = settings.kuber_api_url + "/api/v1/tx" + submit_query
tx_url = get_api_url(network) + "/api/v1/tx" + submit_query
response = await client.post(
tx_url,
json=tx,
Expand All @@ -187,7 +230,7 @@ async def submit_tx(tx, client, submit=True):
raise HTTPException(status_code=response.status_code, detail=response.text)


async def submit_proposal_tx(wallet, proposal, proposal_numbers, client):
async def submit_proposal_tx(wallet, proposal, proposal_numbers, client, network):
proposals = [
{
**proposal,
Expand All @@ -202,4 +245,4 @@ async def submit_proposal_tx(wallet, proposal, proposal_numbers, client):
"selections": [wallet["address"], wallet["skey"]],
"proposals": proposals,
}
return await submit_tx(tx, client)
return await submit_tx(tx, client, network)
Loading

0 comments on commit c438146

Please sign in to comment.