Add a command to create and publish a app deployment auction
This commit is contained in:
parent
03dd265e5f
commit
f17874b80f
129
stack_orchestrator/deploy/webapp/publish_deployment_auction.py
Normal file
129
stack_orchestrator/deploy/webapp/publish_deployment_auction.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright ©2023 Vulcanize
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import click
|
||||
import yaml
|
||||
|
||||
from stack_orchestrator.deploy.webapp.util import (
|
||||
LaconicRegistryClient,
|
||||
)
|
||||
|
||||
AUCTION_KIND_PROVIDER = "provider"
|
||||
TOKEN_DENOM = "alnt"
|
||||
|
||||
def fatal(msg: str):
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# TODO: Add defaults for auction params
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option(
|
||||
"--app",
|
||||
help="The LRN of the application to deploy.",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--commits-duration",
|
||||
help="Auction commits duration (in seconds).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--reveals-duration",
|
||||
help="Auction reveals duration (in seconds).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--commit-fee",
|
||||
help="Auction bid commit fee (in alnt).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--reveal-fee",
|
||||
help="Auction bid reveal fee (in alnt).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--max-price",
|
||||
help="Max acceptable bid price (in alnt).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--num-providers",
|
||||
help="Max acceptable bid price (in alnt).",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
help="Don't publish anything, just report what would be done.",
|
||||
is_flag=True,
|
||||
)
|
||||
@click.pass_context
|
||||
def command(
|
||||
ctx,
|
||||
laconic_config,
|
||||
app,
|
||||
commits_duration,
|
||||
reveals_duration,
|
||||
commit_fee,
|
||||
reveal_fee,
|
||||
max_price,
|
||||
num_providers,
|
||||
dry_run,
|
||||
): # noqa: C901
|
||||
tempdir = tempfile.mkdtemp()
|
||||
try:
|
||||
laconic = LaconicRegistryClient(laconic_config)
|
||||
|
||||
app_record = laconic.get_record(app)
|
||||
if not app_record:
|
||||
fatal(f"Unable to locate app: {app}")
|
||||
|
||||
provider_auction_params = {
|
||||
"kind": AUCTION_KIND_PROVIDER,
|
||||
"commits_duration": commits_duration,
|
||||
"reveals_duration": reveals_duration,
|
||||
"denom": TOKEN_DENOM,
|
||||
"commit_fee": commit_fee,
|
||||
"reveal_fee": reveal_fee,
|
||||
"max_price": max_price,
|
||||
"num_providers": num_providers,
|
||||
}
|
||||
auction_id = laconic.create_auction(provider_auction_params)
|
||||
|
||||
if not auction_id:
|
||||
fatal("Unable to create a provider auction")
|
||||
|
||||
deployment_auction = {
|
||||
"record": {
|
||||
"type": "ApplicationDeploymentAuction",
|
||||
"application": app,
|
||||
"auction": auction_id,
|
||||
}
|
||||
}
|
||||
|
||||
if dry_run:
|
||||
print(yaml.dump(deployment_auction))
|
||||
return
|
||||
|
||||
# Publish the deployment auction record
|
||||
laconic.publish(deployment_auction)
|
||||
finally:
|
||||
shutil.rmtree(tempdir, ignore_errors=True)
|
@ -24,9 +24,6 @@ import tempfile
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
AUCTION_KIND_PROVIDER = "provider"
|
||||
TOKEN_DENOM = "alnt"
|
||||
|
||||
class AttrDict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AttrDict, self).__init__(*args, **kwargs)
|
||||
@ -93,75 +90,6 @@ def is_lrn(name_or_id: str):
|
||||
def is_id(name_or_id: str):
|
||||
return not is_lrn(name_or_id)
|
||||
|
||||
|
||||
def confirm_payment(laconic, record, payment_address, min_amount, logger):
|
||||
req_owner = laconic.get_owner(record)
|
||||
if req_owner == payment_address:
|
||||
# No need to confirm payment if the sender and recipient are the same account.
|
||||
return True
|
||||
|
||||
if not record.attributes.payment:
|
||||
logger.log(f"{record.id}: no payment tx info")
|
||||
return False
|
||||
|
||||
tx = laconic.get_tx(record.attributes.payment)
|
||||
if not tx:
|
||||
logger.log(f"{record.id}: cannot locate payment tx")
|
||||
return False
|
||||
|
||||
if tx.code != 0:
|
||||
logger.log(
|
||||
f"{record.id}: payment tx {tx.hash} was not successful - code: {tx.code}, log: {tx.log}"
|
||||
)
|
||||
return False
|
||||
|
||||
if tx.sender != req_owner:
|
||||
logger.log(
|
||||
f"{record.id}: payment sender {tx.sender} in tx {tx.hash} does not match deployment "
|
||||
f"request owner {req_owner}"
|
||||
)
|
||||
return False
|
||||
|
||||
if tx.recipient != payment_address:
|
||||
logger.log(
|
||||
f"{record.id}: payment recipient {tx.recipient} in tx {tx.hash} does not match {payment_address}"
|
||||
)
|
||||
return False
|
||||
|
||||
pay_denom = "".join([i for i in tx.amount if not i.isdigit()])
|
||||
if pay_denom != "alnt":
|
||||
logger.log(
|
||||
f"{record.id}: {pay_denom} in tx {tx.hash} is not an expected payment denomination"
|
||||
)
|
||||
return False
|
||||
|
||||
pay_amount = int("".join([i for i in tx.amount if i.isdigit()]))
|
||||
if pay_amount < min_amount:
|
||||
logger.log(
|
||||
f"{record.id}: payment amount {tx.amount} is less than minimum {min_amount}"
|
||||
)
|
||||
return False
|
||||
|
||||
# Check if the payment was already used on a
|
||||
used = laconic.app_deployments(
|
||||
{"deployer": payment_address, "payment": tx.hash}, all=True
|
||||
)
|
||||
if len(used):
|
||||
logger.log(f"{record.id}: payment {tx.hash} already used on deployment {used}")
|
||||
return False
|
||||
|
||||
used = laconic.app_deployment_removals(
|
||||
{"deployer": payment_address, "payment": tx.hash}, all=True
|
||||
)
|
||||
if len(used):
|
||||
logger.log(
|
||||
f"{record.id}: payment {tx.hash} already used on deployment removal {used}"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class LaconicRegistryClient:
|
||||
def __init__(self, config_file, log_file=None):
|
||||
self.config_file = config_file
|
||||
@ -784,16 +712,69 @@ def skip_by_tag(r, include_tags, exclude_tags):
|
||||
|
||||
return False
|
||||
|
||||
def create_provider_auction(laconic: LaconicRegistryClient, params):
|
||||
auction_params = {
|
||||
"kind": AUCTION_KIND_PROVIDER,
|
||||
"commits_duration": params["commits_duration"],
|
||||
"reveals_duration": params["reveals_duration"],
|
||||
"denom": TOKEN_DENOM,
|
||||
"commit_fee": params["commit_fee"],
|
||||
"reveal_fee": params["reveal_fee"],
|
||||
"max_price": params["max_price"],
|
||||
"num_providers": params["num_providers"],
|
||||
}
|
||||
def confirm_payment(laconic: LaconicRegistryClient, record, payment_address, min_amount, logger):
|
||||
req_owner = laconic.get_owner(record)
|
||||
if req_owner == payment_address:
|
||||
# No need to confirm payment if the sender and recipient are the same account.
|
||||
return True
|
||||
|
||||
return laconic.create_auction(auction_params)
|
||||
if not record.attributes.payment:
|
||||
logger.log(f"{record.id}: no payment tx info")
|
||||
return False
|
||||
|
||||
tx = laconic.get_tx(record.attributes.payment)
|
||||
if not tx:
|
||||
logger.log(f"{record.id}: cannot locate payment tx")
|
||||
return False
|
||||
|
||||
if tx.code != 0:
|
||||
logger.log(
|
||||
f"{record.id}: payment tx {tx.hash} was not successful - code: {tx.code}, log: {tx.log}"
|
||||
)
|
||||
return False
|
||||
|
||||
if tx.sender != req_owner:
|
||||
logger.log(
|
||||
f"{record.id}: payment sender {tx.sender} in tx {tx.hash} does not match deployment "
|
||||
f"request owner {req_owner}"
|
||||
)
|
||||
return False
|
||||
|
||||
if tx.recipient != payment_address:
|
||||
logger.log(
|
||||
f"{record.id}: payment recipient {tx.recipient} in tx {tx.hash} does not match {payment_address}"
|
||||
)
|
||||
return False
|
||||
|
||||
pay_denom = "".join([i for i in tx.amount if not i.isdigit()])
|
||||
if pay_denom != "alnt":
|
||||
logger.log(
|
||||
f"{record.id}: {pay_denom} in tx {tx.hash} is not an expected payment denomination"
|
||||
)
|
||||
return False
|
||||
|
||||
pay_amount = int("".join([i for i in tx.amount if i.isdigit()]))
|
||||
if pay_amount < min_amount:
|
||||
logger.log(
|
||||
f"{record.id}: payment amount {tx.amount} is less than minimum {min_amount}"
|
||||
)
|
||||
return False
|
||||
|
||||
# Check if the payment was already used on a
|
||||
used = laconic.app_deployments(
|
||||
{"deployer": payment_address, "payment": tx.hash}, all=True
|
||||
)
|
||||
if len(used):
|
||||
logger.log(f"{record.id}: payment {tx.hash} already used on deployment {used}")
|
||||
return False
|
||||
|
||||
used = laconic.app_deployment_removals(
|
||||
{"deployer": payment_address, "payment": tx.hash}, all=True
|
||||
)
|
||||
if len(used):
|
||||
logger.log(
|
||||
f"{record.id}: payment {tx.hash} already used on deployment removal {used}"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -26,6 +26,7 @@ from stack_orchestrator.deploy.webapp import (run_webapp,
|
||||
deploy_webapp_from_registry,
|
||||
undeploy_webapp_from_registry,
|
||||
publish_webapp_deployer,
|
||||
publish_deployment_auction,
|
||||
request_webapp_deployment)
|
||||
from stack_orchestrator.deploy import deploy
|
||||
from stack_orchestrator import version
|
||||
@ -64,6 +65,7 @@ cli.add_command(deploy_webapp.command, "deploy-webapp")
|
||||
cli.add_command(deploy_webapp_from_registry.command, "deploy-webapp-from-registry")
|
||||
cli.add_command(undeploy_webapp_from_registry.command, "undeploy-webapp-from-registry")
|
||||
cli.add_command(publish_webapp_deployer.command, "publish-deployer-to-registry")
|
||||
cli.add_command(publish_deployment_auction.command, "publish-deployment-auction")
|
||||
cli.add_command(request_webapp_deployment.command, "request-webapp-deployment")
|
||||
cli.add_command(deploy.command, "deploy") # deploy is an alias for deploy-system
|
||||
cli.add_command(deploy.command, "deploy-system")
|
||||
|
Loading…
Reference in New Issue
Block a user