Archived
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25dce3f051 | ||
|
|
87251ba65b | ||
|
|
873a6d472c | ||
|
|
39df4683ac | ||
|
|
23ca4c4341 | ||
|
|
f64ef5d128 | ||
|
|
5f8e809b2d | ||
|
|
4a7df2de33 |
@@ -54,6 +54,7 @@ def process_app_deployment_request(
|
||||
deployment_record_namespace,
|
||||
dns_record_namespace,
|
||||
default_dns_suffix,
|
||||
dns_value,
|
||||
deployment_parent_dir,
|
||||
kube_config,
|
||||
image_registry,
|
||||
@@ -251,6 +252,7 @@ def process_app_deployment_request(
|
||||
dns_record,
|
||||
dns_lrn,
|
||||
deployment_dir,
|
||||
dns_value,
|
||||
app_deployment_request,
|
||||
webapp_deployer_record,
|
||||
logger,
|
||||
@@ -304,6 +306,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
help="How to handle requests with an FQDN: prohibit, allow, preexisting",
|
||||
default="prohibit",
|
||||
)
|
||||
@click.option("--ip", help="IP address of the k8s deployment (to be set in DNS record)", default=None)
|
||||
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns", required=True)
|
||||
@click.option(
|
||||
"--record-namespace-deployments",
|
||||
@@ -339,6 +342,17 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
help="Requests must have a minimum payment to be processed (in alnt)",
|
||||
default=0,
|
||||
)
|
||||
@click.option(
|
||||
"--atom-payment-address",
|
||||
help="Cosmos ATOM address to receive payments",
|
||||
default=None,
|
||||
)
|
||||
@click.option(
|
||||
"--min-atom-payment",
|
||||
help="Minimum required ATOM payment amount",
|
||||
default=1,
|
||||
type=float,
|
||||
)
|
||||
@click.option("--lrn", help="The LRN of this deployer.", required=True)
|
||||
@click.option(
|
||||
"--all-requests",
|
||||
@@ -360,6 +374,9 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
@click.option(
|
||||
"--private-key-file", help="The private key for decrypting config.", required=True
|
||||
)
|
||||
@click.option(
|
||||
"--registry-lock-file", help="File path to use for registry mutex lock", default=None
|
||||
)
|
||||
@click.option(
|
||||
"--private-key-passphrase",
|
||||
help="The passphrase for the private key.",
|
||||
@@ -378,6 +395,7 @@ def command( # noqa: C901
|
||||
only_update_state,
|
||||
dns_suffix,
|
||||
fqdn_policy,
|
||||
ip,
|
||||
record_namespace_dns,
|
||||
record_namespace_deployments,
|
||||
dry_run,
|
||||
@@ -387,12 +405,15 @@ def command( # noqa: C901
|
||||
recreate_on_deploy,
|
||||
log_dir,
|
||||
min_required_payment,
|
||||
atom_payment_address,
|
||||
min_atom_payment,
|
||||
lrn,
|
||||
config_upload_dir,
|
||||
private_key_file,
|
||||
private_key_passphrase,
|
||||
all_requests,
|
||||
auction_requests,
|
||||
registry_lock_file,
|
||||
):
|
||||
if request_id and discover:
|
||||
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
||||
@@ -425,6 +446,13 @@ def command( # noqa: C901
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
if fqdn_policy == "allow" and not ip:
|
||||
print(
|
||||
"--ip is required with 'allow' fqdn-policy",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
tempdir = tempfile.mkdtemp()
|
||||
gpg = gnupg.GPG(gnupghome=tempdir)
|
||||
|
||||
@@ -444,7 +472,7 @@ def command( # noqa: C901
|
||||
include_tags = [tag.strip() for tag in include_tags.split(",") if tag]
|
||||
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
||||
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file)
|
||||
webapp_deployer_record = laconic.get_record(lrn, require=True)
|
||||
payment_address = webapp_deployer_record.attributes.paymentAddress
|
||||
main_logger.log(f"Payment address: {payment_address}")
|
||||
@@ -615,6 +643,8 @@ def command( # noqa: C901
|
||||
payment_address,
|
||||
min_required_payment,
|
||||
main_logger,
|
||||
atom_payment_address,
|
||||
min_atom_payment,
|
||||
):
|
||||
main_logger.log(f"{r.id}: Payment confirmed.")
|
||||
requests_to_execute.append(r)
|
||||
@@ -649,7 +679,7 @@ def command( # noqa: C901
|
||||
)
|
||||
run_log_file = open(run_log_file_path, "wt")
|
||||
run_reg_client = LaconicRegistryClient(
|
||||
laconic_config, log_file=run_log_file
|
||||
laconic_config, log_file=run_log_file, mutex_lock_file=registry_lock_file
|
||||
)
|
||||
|
||||
build_logger = TimedLogger(run_id, run_log_file)
|
||||
@@ -661,6 +691,7 @@ def command( # noqa: C901
|
||||
record_namespace_deployments,
|
||||
record_namespace_dns,
|
||||
dns_suffix,
|
||||
ip,
|
||||
os.path.abspath(deployment_parent_dir),
|
||||
kube_config,
|
||||
image_registry,
|
||||
|
||||
@@ -120,6 +120,9 @@ def dump_known_auction_requests(filename, requests, status="SEEN"):
|
||||
help="Bid to place on application deployment auctions (in alnt)",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--registry-lock-file", help="File path to use for registry mutex lock", default=None
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
||||
)
|
||||
@@ -129,6 +132,7 @@ def command(
|
||||
laconic_config,
|
||||
state_file,
|
||||
bid_amount,
|
||||
registry_lock_file,
|
||||
dry_run,
|
||||
):
|
||||
if int(bid_amount) < 0:
|
||||
@@ -138,7 +142,7 @@ def command(
|
||||
logger = TimedLogger(file=sys.stderr)
|
||||
|
||||
try:
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file)
|
||||
auctions_requests = laconic.app_deployment_auctions()
|
||||
|
||||
previous_requests = {}
|
||||
|
||||
@@ -102,7 +102,7 @@ def command(
|
||||
"max_price": max_price,
|
||||
"num_providers": num_providers,
|
||||
}
|
||||
auction_id = laconic.create_auction(provider_auction_params)
|
||||
auction_id = laconic.create_deployment_auction(provider_auction_params)
|
||||
print("Deployment auction created:", auction_id)
|
||||
|
||||
if not auction_id:
|
||||
|
||||
@@ -46,6 +46,17 @@ from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient
|
||||
help="List the minimum required payment (in alnt) to process a deployment request.",
|
||||
default=0,
|
||||
)
|
||||
@click.option(
|
||||
"--atom-payment-address",
|
||||
help="The Cosmos ATOM address to which payments should be made.",
|
||||
default=None,
|
||||
)
|
||||
@click.option(
|
||||
"--min-atom-payment",
|
||||
help="List the minimum required payment (in uatom) to process a deployment request.",
|
||||
default="1000000uatom",
|
||||
type=str,
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
help="Don't publish anything, just report what would be done.",
|
||||
@@ -60,6 +71,8 @@ def command( # noqa: C901
|
||||
lrn,
|
||||
payment_address,
|
||||
min_required_payment,
|
||||
atom_payment_address,
|
||||
min_atom_payment,
|
||||
dry_run,
|
||||
):
|
||||
laconic = LaconicRegistryClient(laconic_config)
|
||||
@@ -83,6 +96,10 @@ def command( # noqa: C901
|
||||
webapp_deployer_record["record"][
|
||||
"minimumPayment"
|
||||
] = f"{min_required_payment}alnt"
|
||||
|
||||
if atom_payment_address:
|
||||
webapp_deployer_record["record"]["atomPaymentAddress"] = atom_payment_address
|
||||
webapp_deployer_record["record"]["minimumAtomPayment"] = min_atom_payment
|
||||
|
||||
if dry_run:
|
||||
yaml.dump(webapp_deployer_record, sys.stdout)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
from functools import wraps
|
||||
import os
|
||||
import time
|
||||
|
||||
# Define default file path for the lock
|
||||
DEFAULT_LOCK_FILE_PATH = "/tmp/registry_mutex_lock_file"
|
||||
LOCK_TIMEOUT = 30
|
||||
LOCK_RETRY_INTERVAL = 3
|
||||
|
||||
|
||||
def acquire_lock(client, lock_file_path, timeout):
|
||||
# Lock alreay acquired by the current client
|
||||
if client.mutex_lock_acquired:
|
||||
return
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Check if lock file exists and is potentially stale
|
||||
if os.path.exists(lock_file_path):
|
||||
with open(lock_file_path, 'r') as lock_file:
|
||||
timestamp = float(lock_file.read().strip())
|
||||
|
||||
# If lock is stale, remove the lock file
|
||||
if time.time() - timestamp > timeout:
|
||||
print(f"Stale lock detected, removing lock file {lock_file_path}")
|
||||
os.remove(lock_file_path)
|
||||
else:
|
||||
print(f"Lock file {lock_file_path} exists and is recent, waiting...")
|
||||
time.sleep(LOCK_RETRY_INTERVAL)
|
||||
continue
|
||||
|
||||
# Try to create a new lock file with the current timestamp
|
||||
fd = os.open(lock_file_path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
||||
with os.fdopen(fd, 'w') as lock_file:
|
||||
lock_file.write(str(time.time()))
|
||||
|
||||
client.mutex_lock_acquired = True
|
||||
print(f"Registry lock acquired, {lock_file_path}")
|
||||
|
||||
# Lock successfully acquired
|
||||
return
|
||||
|
||||
except FileExistsError:
|
||||
print(f"Lock file {lock_file_path} exists, waiting...")
|
||||
time.sleep(LOCK_RETRY_INTERVAL)
|
||||
|
||||
|
||||
def release_lock(client, lock_file_path):
|
||||
try:
|
||||
os.remove(lock_file_path)
|
||||
|
||||
client.mutex_lock_acquired = False
|
||||
print(f"Registry lock released, {lock_file_path}")
|
||||
except FileNotFoundError:
|
||||
# Lock file already removed
|
||||
pass
|
||||
|
||||
|
||||
def registry_mutex():
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
lock_file_path = DEFAULT_LOCK_FILE_PATH
|
||||
if self.mutex_lock_file:
|
||||
lock_file_path = self.mutex_lock_file
|
||||
|
||||
# Acquire the lock before running the function
|
||||
acquire_lock(self, lock_file_path, LOCK_TIMEOUT)
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
finally:
|
||||
# Release the lock after the function completes
|
||||
release_lock(self, lock_file_path)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
@@ -178,6 +178,9 @@ def dump_known_requests(filename, requests):
|
||||
"my payment address are examined).",
|
||||
is_flag=True,
|
||||
)
|
||||
@click.option(
|
||||
"--registry-lock-file", help="File path to use for registry mutex lock", default=None
|
||||
)
|
||||
@click.pass_context
|
||||
def command( # noqa: C901
|
||||
ctx,
|
||||
@@ -195,6 +198,7 @@ def command( # noqa: C901
|
||||
min_required_payment,
|
||||
lrn,
|
||||
all_requests,
|
||||
registry_lock_file,
|
||||
):
|
||||
if request_id and discover:
|
||||
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
||||
@@ -212,7 +216,7 @@ def command( # noqa: C901
|
||||
include_tags = [tag.strip() for tag in include_tags.split(",") if tag]
|
||||
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
||||
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file)
|
||||
deployer_record = laconic.get_record(lrn, require=True)
|
||||
payment_address = deployer_record.attributes.paymentAddress
|
||||
main_logger.log(f"Payment address: {payment_address}")
|
||||
|
||||
@@ -26,6 +26,8 @@ import yaml
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from stack_orchestrator.deploy.webapp.registry_mutex import registry_mutex
|
||||
|
||||
|
||||
class AuctionStatus(str, Enum):
|
||||
COMMIT = "commit"
|
||||
@@ -112,7 +114,7 @@ def is_id(name_or_id: str):
|
||||
|
||||
|
||||
class LaconicRegistryClient:
|
||||
def __init__(self, config_file, log_file=None):
|
||||
def __init__(self, config_file, log_file=None, mutex_lock_file=None):
|
||||
self.config_file = config_file
|
||||
self.log_file = log_file
|
||||
self.cache = AttrDict(
|
||||
@@ -123,6 +125,9 @@ class LaconicRegistryClient:
|
||||
}
|
||||
)
|
||||
|
||||
self.mutex_lock_file = mutex_lock_file
|
||||
self.mutex_lock_acquired = False
|
||||
|
||||
def whoami(self, refresh=False):
|
||||
if not refresh and "whoami" in self.cache:
|
||||
return self.cache["whoami"]
|
||||
@@ -391,6 +396,7 @@ class LaconicRegistryClient:
|
||||
criteria["type"] = "ApplicationDeploymentAuction"
|
||||
return self.list_records(criteria, all)
|
||||
|
||||
@registry_mutex()
|
||||
def publish(self, record, names=None):
|
||||
if names is None:
|
||||
names = []
|
||||
@@ -421,6 +427,7 @@ class LaconicRegistryClient:
|
||||
finally:
|
||||
logged_cmd(self.log_file, "rm", "-rf", tmpdir)
|
||||
|
||||
@registry_mutex()
|
||||
def set_name(self, name, record_id):
|
||||
logged_cmd(
|
||||
self.log_file,
|
||||
@@ -434,6 +441,7 @@ class LaconicRegistryClient:
|
||||
record_id,
|
||||
)
|
||||
|
||||
@registry_mutex()
|
||||
def delete_name(self, name):
|
||||
logged_cmd(
|
||||
self.log_file,
|
||||
@@ -446,6 +454,7 @@ class LaconicRegistryClient:
|
||||
name,
|
||||
)
|
||||
|
||||
@registry_mutex()
|
||||
def send_tokens(self, address, amount, type="alnt"):
|
||||
args = [
|
||||
"laconic",
|
||||
@@ -464,58 +473,36 @@ class LaconicRegistryClient:
|
||||
|
||||
return AttrDict(json.loads(logged_cmd(self.log_file, *args)))
|
||||
|
||||
def create_auction(self, auction):
|
||||
if auction["kind"] == AUCTION_KIND_PROVIDER:
|
||||
args = [
|
||||
"laconic",
|
||||
"-c",
|
||||
self.config_file,
|
||||
"registry",
|
||||
"auction",
|
||||
"create",
|
||||
"--kind",
|
||||
auction["kind"],
|
||||
"--commits-duration",
|
||||
str(auction["commits_duration"]),
|
||||
"--reveals-duration",
|
||||
str(auction["reveals_duration"]),
|
||||
"--denom",
|
||||
auction["denom"],
|
||||
"--commit-fee",
|
||||
str(auction["commit_fee"]),
|
||||
"--reveal-fee",
|
||||
str(auction["reveal_fee"]),
|
||||
"--max-price",
|
||||
str(auction["max_price"]),
|
||||
"--num-providers",
|
||||
str(auction["num_providers"])
|
||||
]
|
||||
else:
|
||||
args = [
|
||||
"laconic",
|
||||
"-c",
|
||||
self.config_file,
|
||||
"registry",
|
||||
"auction",
|
||||
"create",
|
||||
"--kind",
|
||||
auction["kind"],
|
||||
"--commits-duration",
|
||||
str(auction["commits_duration"]),
|
||||
"--reveals-duration",
|
||||
str(auction["reveals_duration"]),
|
||||
"--denom",
|
||||
auction["denom"],
|
||||
"--commit-fee",
|
||||
str(auction["commit_fee"]),
|
||||
"--reveal-fee",
|
||||
str(auction["reveal_fee"]),
|
||||
"--minimum-bid",
|
||||
str(auction["minimum_bid"])
|
||||
]
|
||||
@registry_mutex()
|
||||
def create_deployment_auction(self, auction):
|
||||
args = [
|
||||
"laconic",
|
||||
"-c",
|
||||
self.config_file,
|
||||
"registry",
|
||||
"auction",
|
||||
"create",
|
||||
"--kind",
|
||||
auction["kind"],
|
||||
"--commits-duration",
|
||||
str(auction["commits_duration"]),
|
||||
"--reveals-duration",
|
||||
str(auction["reveals_duration"]),
|
||||
"--denom",
|
||||
auction["denom"],
|
||||
"--commit-fee",
|
||||
str(auction["commit_fee"]),
|
||||
"--reveal-fee",
|
||||
str(auction["reveal_fee"]),
|
||||
"--max-price",
|
||||
str(auction["max_price"]),
|
||||
"--num-providers",
|
||||
str(auction["num_providers"])
|
||||
]
|
||||
|
||||
return json.loads(logged_cmd(self.log_file, *args))["auctionId"]
|
||||
|
||||
@registry_mutex()
|
||||
def commit_bid(self, auction_id, amount, type="alnt"):
|
||||
args = [
|
||||
"laconic",
|
||||
@@ -532,6 +519,7 @@ class LaconicRegistryClient:
|
||||
|
||||
return json.loads(logged_cmd(self.log_file, *args))["reveal_file"]
|
||||
|
||||
@registry_mutex()
|
||||
def reveal_bid(self, auction_id, reveal_file_path):
|
||||
logged_cmd(
|
||||
self.log_file,
|
||||
@@ -701,6 +689,7 @@ def publish_deployment(
|
||||
dns_record,
|
||||
dns_lrn,
|
||||
deployment_dir,
|
||||
dns_value=None,
|
||||
app_deployment_request=None,
|
||||
webapp_deployer_record=None,
|
||||
logger=None,
|
||||
@@ -733,6 +722,8 @@ def publish_deployment(
|
||||
}
|
||||
if app_deployment_request:
|
||||
new_dns_record["record"]["request"] = app_deployment_request.id
|
||||
if dns_value:
|
||||
new_dns_record["record"]["value"] = dns_value
|
||||
|
||||
if logger:
|
||||
logger.log("Publishing DnsRecord.")
|
||||
@@ -810,7 +801,7 @@ def skip_by_tag(r, include_tags, exclude_tags):
|
||||
return False
|
||||
|
||||
|
||||
def confirm_payment(laconic: LaconicRegistryClient, record, payment_address, min_amount, logger):
|
||||
def confirm_payment(laconic: LaconicRegistryClient, record, payment_address, min_amount, logger, atom_payment_address=None, atom_min_amount=None):
|
||||
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.
|
||||
@@ -820,62 +811,114 @@ def confirm_payment(laconic: LaconicRegistryClient, record, payment_address, min
|
||||
logger.log(f"{record.id}: no payment tx info")
|
||||
return False
|
||||
|
||||
# Try to verify as a laconic payment first
|
||||
tx = laconic.get_tx(record.attributes.payment)
|
||||
if not tx:
|
||||
logger.log(f"{record.id}: cannot locate payment tx")
|
||||
return False
|
||||
if tx:
|
||||
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.code != 0:
|
||||
logger.log(
|
||||
f"{record.id}: payment tx {tx.hash} was not successful - code: {tx.code}, log: {tx.log}"
|
||||
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 deployment
|
||||
used = laconic.app_deployments(
|
||||
{"deployer": record.attributes.deployer, "payment": tx.hash}, all=True
|
||||
)
|
||||
return False
|
||||
if len(used):
|
||||
# Fetch the app name from request record
|
||||
used_request = laconic.get_record(used[0].attributes.request, require=True)
|
||||
|
||||
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}"
|
||||
# Check that payment was used for deployment of same application
|
||||
if record.attributes.application != used_request.attributes.application:
|
||||
logger.log(f"{record.id}: payment {tx.hash} already used on a different application deployment {used}")
|
||||
return False
|
||||
|
||||
used = laconic.app_deployment_removals(
|
||||
{"deployer": record.attributes.deployer, "payment": tx.hash}, all=True
|
||||
)
|
||||
return False
|
||||
if len(used):
|
||||
logger.log(
|
||||
f"{record.id}: payment {tx.hash} already used on deployment removal {used}"
|
||||
)
|
||||
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
|
||||
return True
|
||||
|
||||
# If we get here, the transaction hash wasn't found in the laconic testnet
|
||||
# Let's check if it's a valid Cosmos ATOM payment if configuration is available
|
||||
if atom_payment_address:
|
||||
logger.log(f"{record.id}: checking if payment is a valid Cosmos ATOM transaction")
|
||||
|
||||
try:
|
||||
import requests
|
||||
|
||||
# Use the webapp-deployment-status-api to verify the ATOM payment
|
||||
deployer_record = laconic.get_record(record.attributes.deployer)
|
||||
if not deployer_record or not deployer_record.attributes.apiUrl:
|
||||
logger.log(f"{record.id}: cannot find deployer API URL to verify ATOM payment")
|
||||
return False
|
||||
|
||||
api_url = deployer_record.attributes.apiUrl
|
||||
verify_url = f"{api_url}/verify/atom-payment"
|
||||
|
||||
# Make a request to the API to verify the ATOM payment
|
||||
# Pass markAsUsed=true to prevent this transaction from being used again
|
||||
response = requests.post(
|
||||
verify_url,
|
||||
json={
|
||||
"txHash": record.attributes.payment,
|
||||
"minAmount": atom_min_amount,
|
||||
"markAsUsed": True
|
||||
},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.log(f"{record.id}: ATOM payment verification API request failed with status {response.status_code}")
|
||||
return False
|
||||
|
||||
result = response.json()
|
||||
if not result.get("valid", False):
|
||||
logger.log(f"{record.id}: ATOM payment verification failed: {result.get('reason', 'unknown reason')}")
|
||||
return False
|
||||
|
||||
# Payment is valid
|
||||
logger.log(f"{record.id}: ATOM payment verified successfully, amount: {result.get('amount')} ATOM")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.log(f"{record.id}: error verifying ATOM payment: {str(e)}")
|
||||
return False
|
||||
|
||||
logger.log(f"{record.id}: payment tx {record.attributes.payment} not found in laconic testnet and ATOM payment verification not configured")
|
||||
return False
|
||||
|
||||
|
||||
def confirm_auction(laconic: LaconicRegistryClient, record, deployer_lrn, payment_address, logger):
|
||||
|
||||
Reference in New Issue
Block a user