kopia lustrzana https://github.com/bugout-dev/moonstream
Merge pull request #1062 from moonstream-to/verify-call-requests-post
Verify if requests exists before push new listpull/1064/head
commit
e2fb5b10e5
|
@ -3,9 +3,9 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
from sqlalchemy import func, or_, text
|
from sqlalchemy import func, or_, text, tuple_
|
||||||
from sqlalchemy.dialects.postgresql import insert
|
from sqlalchemy.dialects.postgresql import insert
|
||||||
from sqlalchemy.engine import Row
|
from sqlalchemy.engine import Row
|
||||||
from sqlalchemy.exc import IntegrityError, NoResultFound
|
from sqlalchemy.exc import IntegrityError, NoResultFound
|
||||||
|
@ -71,6 +71,12 @@ class CallRequestAlreadyRegistered(Exception):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CallRequestIdDuplicates(Exception):
|
||||||
|
"""
|
||||||
|
Raised when same call request IDs passed in one request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def parse_registered_contract_response(
|
def parse_registered_contract_response(
|
||||||
obj: Tuple[RegisteredContract, Blockchain]
|
obj: Tuple[RegisteredContract, Blockchain]
|
||||||
) -> data.RegisteredContractResponse:
|
) -> data.RegisteredContractResponse:
|
||||||
|
@ -431,6 +437,38 @@ def create_request_calls(
|
||||||
return len(call_specs)
|
return len(call_specs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_call_request_from_tuple(
|
||||||
|
db_session: Session,
|
||||||
|
metatx_requester_id: uuid.UUID,
|
||||||
|
requests: Set[Tuple[str, str]],
|
||||||
|
contract_id: Optional[uuid.UUID] = None,
|
||||||
|
contract_address: Optional[str] = None,
|
||||||
|
) -> List[CallRequest]:
|
||||||
|
if contract_id is None and contract_address is None:
|
||||||
|
raise ValueError(
|
||||||
|
"At least one of contract_id or contract_address must be specified"
|
||||||
|
)
|
||||||
|
query = (
|
||||||
|
db_session.query(CallRequest)
|
||||||
|
.join(
|
||||||
|
RegisteredContract,
|
||||||
|
CallRequest.registered_contract_id == RegisteredContract.id,
|
||||||
|
)
|
||||||
|
.filter(RegisteredContract.metatx_requester_id == metatx_requester_id)
|
||||||
|
.filter(tuple_(CallRequest.caller, CallRequest.request_id).in_(requests))
|
||||||
|
)
|
||||||
|
if contract_id is not None:
|
||||||
|
query = query.filter(RegisteredContract.id == contract_id)
|
||||||
|
if contract_address is not None:
|
||||||
|
query = query.filter(
|
||||||
|
RegisteredContract.address == Web3.toChecksumAddress(contract_address)
|
||||||
|
)
|
||||||
|
|
||||||
|
existing_requests = query.all()
|
||||||
|
|
||||||
|
return existing_requests
|
||||||
|
|
||||||
|
|
||||||
def get_call_request(
|
def get_call_request(
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
request_id: uuid.UUID,
|
request_id: uuid.UUID,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict, List, Optional, Set
|
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from bugout.data import BugoutResource
|
from bugout.data import BugoutResource
|
||||||
|
@ -329,6 +329,10 @@ class CallRequestResponse(BaseModel):
|
||||||
return Web3.toChecksumAddress(v)
|
return Web3.toChecksumAddress(v)
|
||||||
|
|
||||||
|
|
||||||
|
class CallRequestsCheck(BaseModel):
|
||||||
|
existing_requests: Set[Tuple[str, str]] = Field(default_factory=set)
|
||||||
|
|
||||||
|
|
||||||
class CompleteCallRequestsAPIRequest(BaseModel):
|
class CompleteCallRequestsAPIRequest(BaseModel):
|
||||||
tx_hash: str
|
tx_hash: str
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@ Moonstream users can register contracts on Moonstream Engine. This allows them t
|
||||||
as part of their chain-adjacent activities (like performing signature-based token distributions on the
|
as part of their chain-adjacent activities (like performing signature-based token distributions on the
|
||||||
Dropper contract).
|
Dropper contract).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional, Set, Tuple
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from bugout.data import BugoutUser
|
from bugout.data import BugoutUser
|
||||||
from fastapi import Body, Depends, FastAPI, Form, Path, Query, Request
|
from fastapi import Body, Depends, FastAPI, Form, Path, Query, Request
|
||||||
from sqlalchemy.exc import NoResultFound
|
from sqlalchemy.exc import NoResultFound
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from web3 import Web3
|
||||||
|
|
||||||
from .. import contracts_actions, data, db
|
from .. import contracts_actions, data, db
|
||||||
from ..middleware import (
|
from ..middleware import (
|
||||||
|
@ -316,6 +318,57 @@ async def list_requests_route(
|
||||||
return [contracts_actions.parse_call_request_response(r) for r in requests]
|
return [contracts_actions.parse_call_request_response(r) for r in requests]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/requests/check",
|
||||||
|
response_model=data.CallRequestsCheck,
|
||||||
|
)
|
||||||
|
async def check_requests_route(
|
||||||
|
request_data: data.CreateCallRequestsAPIRequest = Body(...),
|
||||||
|
user: BugoutUser = Depends(request_user_auth),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> data.CallRequestsCheck:
|
||||||
|
"""
|
||||||
|
Implemented for pre-check until list of requests to be pushed into database.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
incoming_requests: Set[Tuple[str, str]] = set()
|
||||||
|
incoming_request_ids: List[str] = []
|
||||||
|
for r in request_data.specifications:
|
||||||
|
caller_addr = Web3.toChecksumAddress(r.caller)
|
||||||
|
incoming_requests.add((caller_addr, r.request_id))
|
||||||
|
incoming_request_ids.append(r.request_id)
|
||||||
|
|
||||||
|
if len(incoming_requests) != len(incoming_request_ids):
|
||||||
|
raise contracts_actions.CallRequestIdDuplicates(
|
||||||
|
"There are same call_request_id's in one request"
|
||||||
|
)
|
||||||
|
|
||||||
|
existing_requests = contracts_actions.get_call_request_from_tuple(
|
||||||
|
db_session=db_session,
|
||||||
|
metatx_requester_id=user.id,
|
||||||
|
requests=incoming_requests,
|
||||||
|
contract_id=request_data.contract_id,
|
||||||
|
contract_address=request_data.contract_address,
|
||||||
|
)
|
||||||
|
except contracts_actions.CallRequestIdDuplicates:
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=400, detail="There are same call_request_id's in one request"
|
||||||
|
)
|
||||||
|
except Exception as err:
|
||||||
|
logger.error(repr(err))
|
||||||
|
raise EngineHTTPException(status_code=500)
|
||||||
|
|
||||||
|
existing_requests_set: Set[Tuple[str, str]] = set()
|
||||||
|
if len(existing_requests) != 0:
|
||||||
|
existing_requests_set = {
|
||||||
|
(er.caller, str(er.request_id)) for er in existing_requests
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.CallRequestsCheck(
|
||||||
|
existing_requests=existing_requests_set,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get(
|
@app.get(
|
||||||
"/requests/{request_id}", tags=["requests"], response_model=data.CallRequestResponse
|
"/requests/{request_id}", tags=["requests"], response_model=data.CallRequestResponse
|
||||||
)
|
)
|
||||||
|
@ -348,7 +401,7 @@ async def get_request(
|
||||||
|
|
||||||
@app.post("/requests", tags=["requests"], response_model=int)
|
@app.post("/requests", tags=["requests"], response_model=int)
|
||||||
async def create_requests(
|
async def create_requests(
|
||||||
data: data.CreateCallRequestsAPIRequest = Body(...),
|
request_data: data.CreateCallRequestsAPIRequest = Body(...),
|
||||||
user: BugoutUser = Depends(request_user_auth),
|
user: BugoutUser = Depends(request_user_auth),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> int:
|
) -> int:
|
||||||
|
@ -361,11 +414,11 @@ async def create_requests(
|
||||||
num_requests = contracts_actions.create_request_calls(
|
num_requests = contracts_actions.create_request_calls(
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
metatx_requester_id=user.id,
|
metatx_requester_id=user.id,
|
||||||
registered_contract_id=data.contract_id,
|
registered_contract_id=request_data.contract_id,
|
||||||
contract_address=data.contract_address,
|
contract_address=request_data.contract_address,
|
||||||
call_specs=data.specifications,
|
call_specs=request_data.specifications,
|
||||||
ttl_days=data.ttl_days,
|
ttl_days=request_data.ttl_days,
|
||||||
live_at=data.live_at,
|
live_at=request_data.live_at,
|
||||||
)
|
)
|
||||||
except contracts_actions.InvalidAddressFormat as err:
|
except contracts_actions.InvalidAddressFormat as err:
|
||||||
raise EngineHTTPException(
|
raise EngineHTTPException(
|
||||||
|
|
Ładowanie…
Reference in New Issue