GCULpy 언어

GCULpy는 안전하고, 읽기 쉽고, 감사 가능하도록 설계된 Python의 엄격한 정적 유형 하위 집합입니다. Vyper는 일반적인 스마트 계약 취약점을 방지하고 계약 동작을 항상 예측할 수 있도록 Python의 특정 동적 기능을 의도적으로 제한하도록 설계되었습니다.

이 페이지에서는 GCULpy 언어 사양에 대한 참조를 제공하며, Universal Ledger 네트워크에서 계약의 핵심 개념과 수명 주기를 다룹니다.

핵심 개념

다음 계약은 GCULpy에서 샘플 ERC20 토큰을 정의합니다.

import gcul

class ERC20Token(gcul.Contract):
    """Sample ERC20 implementation for the Universal Ledger."""

    symbol: str
    total_supply: int
    balance: gcul.AccountField[int]

    def __init__(self, symbol: str):
        self.symbol = symbol
        self.total_supply = 0

    def mint(self, beneficiary: gcul.Account, value: int) -> int:
        """Mints tokens to the given beneficiary."""
        assert self.is_owner(gcul.sender), "Only the owner can mint"
        assert value >= 0, "Mint amount must be non-negative"
        self.total_supply += value
        self.balance[beneficiary] += value
        return value

    def transfer(self, beneficiary: gcul.Account, value: int) -> int:
        """Transfers tokens from the sender to the given beneficiary."""
        assert value >= 0, "Transfer amount must be non-negative"
        assert (
            value <= self.balance[gcul.sender]
        ), "Sender does not have enough balance"
        self.balance[gcul.sender] -= value
        self.balance[beneficiary] += value
        return value

GCULpy 계약은 gcul.Contract에서 상속되는 클래스입니다. 여기에는 필드(상태 저장)와 메서드(필드에서 작동하는 처리 로직)가 포함됩니다.

필드

GCULpy 계약의 상태는 필드에 저장됩니다. 모든 필드는 클래스 수준에서 정적 유형으로 선언되어야 합니다. 필드에는 두 가지 유형이 있습니다.

  • 계약 필드에는 계약 자체와 함께 저장된 단일 값이 포함됩니다. ERC20Token 예시에서 symbol: str 및 total_supply: int는 계약 필드입니다.

  • 계정 필드는 계약과 상호작용하는 각 계정에 대해 별도의 값을 보유합니다. gcul.AccountField[T]로 선언되며, 여기서 T는 계정별 값의 유형입니다. ERC20Token 예시에서 balance: gcul.AccountField[int]는 계정 필드입니다. 계약이 해당 계정에 예약된 저장공간에 쓰려면 저장용량 권한이 명시적으로 계약에 부여되어야 합니다. 데이터가 저장되면 계약 인스턴스만 수정할 수 있으며 계정은 수정할 수 없습니다.

제한사항 필드에는 다음과 같은 저장용량 한도가 있습니다. 한도를 초과하는 트랜잭션은 실패합니다.

  • 계약의 모든 계약 필드는 단일 스토리지 항목을 공유하며, 이 항목은 총 약 5MiB의 직렬화된 데이터를 보유할 수 있습니다.
  • 계약이 각 개별 계정에 저장하는 모든 계정 필드는 직렬화된 데이터를 총 최대 1MiB까지 보유할 수 있습니다.

메서드

메서드는 계약의 실행 가능한 로직을 정의합니다. Python 메서드처럼 작동하며 계약의 필드를 읽거나 수정할 수 있습니다.

  • __init__: 생성자는 계약이 처음 배포될 때 한 번만 호출됩니다. 모든 계약 필드는 생성자에서 값이 할당되어야 합니다. 생성자는 계정 필드를 초기화하지 않습니다. 각 계정의 값은 해당 계정이 저장소 권한을 부여할 때 생성됩니다.

  • 비공개 메서드: 밑줄로 시작하는 메서드(예: _internal_logic)는 비공개이며 동일한 계약 내의 다른 메서드에서만 호출할 수 있습니다. Universal Ledger 인터프리터는 이 제약조건을 적용합니다.

  • 공개 메서드: 밑줄(_)로 시작하지 않는 모든 메서드는 공개입니다. 공개 메서드는 InvokeContractMethod 트랜잭션을 제출하여 ROLE_CONTRACT_PARTICIPANT가 있는 모든 사용자가 호출할 수 있습니다.

제한사항 공개 메서드와 모든 @gcul.readonly 메서드는 서명에 변경 가능한 유형(예: list 또는 dict)을 사용할 수 없습니다. 대신 Sequence 또는 Mapping와 같은 읽기 전용 유형 선언을 사용하세요.

제한사항 실행 중에 메서드는 네트워크 전체의 컴퓨팅 사용량 상한으로 제한됩니다. 이 한도를 초과하면 트랜잭션이 실패합니다.

계약 수명 주기

다음 섹션에서는 GCULpy 계약의 수명 주기에 포함된 일반적인 작업을 설명합니다.

계약 배포

먼저 gculpyc 컴파일러를 사용하여 GCULpy 소스 코드를 컴파일합니다. 그런 다음 ROLE_CONTRACT_CREATOR가 있는 사용자는 CreateContract 트랜잭션을 제출하여 컴파일된 바이트코드를 Universal Ledger 네트워크에 배포할 수 있습니다. 자세한 내용은 프로그래밍 가능한 계약 배포 튜토리얼을 참조하세요.

이러한 트랜잭션은 다음과 같습니다.

client_transaction {
  sender_id: "OWNER_ACCOUNT_ID"
  create_contract_transaction {
    contract_bytes: "COMPILED_BYTECODE"
    init_arguments {
      key: "symbol"
      value: { string_value: "US02079K1079" }
    }
  }
}

네트워크에서 이 거래를 처리할 때 다음이 적용됩니다.

  • 생성자, 즉 __init__ 메서드는 제공된 인수로 실행되어 새 계약 인스턴스를 만듭니다.
  • 트랜잭션의 발신자가 계약 소유자가 됩니다.
  • 계약 인스턴스는 원장에 영구적으로 저장되며 트랜잭션 출력의 일부로 반환되는 고유한 계약 ID가 할당됩니다.

권한 부여

계약이 사용자 계정 또는 다른 계약을 대신하여 계정 필드에 데이터를 저장하려면 데이터를 저장하는 계약에 저장소 권한을 부여해야 합니다. 이는 보안의 핵심 단계입니다.

사용자 계정에 권한 부여

ROLE_CONTRACT_PARTICIPANT가 있는 사용자는 특정 계약 ID에 대해 GrantContractPermissions 트랜잭션을 제출할 수 있습니다.

client_transaction {
  sender_id: "PARTICIPANT_ACCOUNT_ID"
  grant_contract_permissions_transaction {
    contract_id: "CONTRACT_ID"
    permissions: CONTRACT_PERMISSION_STORAGE
  }
}

네트워크에서 이 거래를 처리할 때 다음이 적용됩니다.

  • 계약에서 계정 필드를 정의하지 않으면 트랜잭션이 거부됩니다.
  • 계약에서 계정 필드를 정의하는 경우 모두 기본값(예: contract.balance[gcul.sender] = 0)으로 채워집니다. 그런 다음 이러한 값은 계정 데이터의 일부로 월드 상태에 저장됩니다.

위임 계약에 대한 권한 부여

ROLE_CONTRACT_PARTICIPANT가 있는 사용자는 GrantContractPermissions 트랜잭션을 제출하여 하나의 계약이 소유한 다른 계약을 대신하여 데이터를 저장하도록 허용할 수 있습니다.

client_transaction {
  sender_id: "OWNER_ACCOUNT_ID"
  grant_contract_permissions_transaction {
    contract_id: "TARGET_CONTRACT_ID"
    delegate_contract_id: "DELEGATE_CONTRACT_ID"
    permissions: CONTRACT_PERMISSION_STORAGE
  }
}

네트워크에서 이 거래를 처리할 때 다음이 적용됩니다.

  • 트랜잭션 발신자(sender_id)는 delegate_contract_id로 식별되는 계약의 소유자여야 합니다.
  • 계약은 자체에 저장소 권한을 부여할 수 없으므로 delegate_contract_id는 contract_id와 달라야 합니다.
  • 스토리지 권한은 트랜잭션 발신자가 아닌 delegate_contract_id에 지정된 계약에만 부여됩니다.
  • 대상 계약 contract_id에 정의된 계정 필드는 대리자 계약 delegate_contract_id에 대해 초기화되므로 대상 계약의 메서드가 교차 계약 호출 중에 gcul.caller 또는 gcul.sender로 키가 지정된 계정 필드에 안전하게 액세스할 수 있습니다.

계약 메서드 호출

계약이 배포되고 필요한 권한이 부여되면 사용자는 공개 메서드를 호출하여 계약과 상호작용할 수 있습니다. ROLE_CONTRACT_PARTICIPANT가 있는 사용자는 계약 ID, 메서드 이름, 메서드 인수 값을 지정하는 InvokeContractMethod 트랜잭션을 제출할 수 있습니다.

client_transaction {
  sender_id: "PARTICIPANT_ACCOUNT_ID"
  invoke_contract_method_transaction {
    contract_id: "CONTRACT_ID"
    method_name: "mint"
    method_arguments {
      key: "beneficiary"
      value: { account_id: "BENEFICIARY_ID" }
    }
    method_arguments {
      key: "value"
      value: { int64_value: 10 }
    }
  }
}

네트워크에서 이 거래를 처리할 때 다음이 적용됩니다.

  • 제공된 CONTRACT_ID와 연결된 계약 인스턴스가 검색됩니다.
  • mint(beneficiary=Account("BENEFICIARY_ID"), value=10) 메서드가 실행됩니다. 수취인의 Account 객체는 런타임에 의해 빌드되고 검증됩니다. 메서드의 로직은 제공된 ID가 유효하고 원장의 기존 계정을 참조한다고 안전하게 가정할 수 있습니다.
  • 어떤 이유로든 메서드가 실패하면 트랜잭션이 실패하고 계약 상태가 업데이트되지 않습니다.
  • 메서드가 성공하면 계약의 업데이트된 상태가 월드 상태에 기록됩니다.

언어 사양

GCULpy는 안전성과 예측 가능성을 위해 설계되었으므로 여러 Python 기능을 허용하지 않습니다. 이러한 기능은 제한 라벨로 표시됩니다. 이러한 제한사항은 영구적인 언어 기능으로, 계약 로직을 더 쉽게 읽고, 감사하고, 정적으로 분석할 수 있도록 도입되어 놀랍거나 안전하지 않은 동작을 제한합니다.

다른 기능은 로드맵 라벨로 표시되며, 구현 로드맵에 있지만 아직 gculpyc 컴파일러에서 지원되지 않습니다.

유형

GCULpy는 정적 타이핑을 강조하는 다양한 일반 변수 유형을 지원합니다.

핵심 가치 유형:

  • int, bool, str, None는 이미 지원됩니다.
  • 로드맵 Decimal, bytes, Enum는 로드맵에 있습니다.
  • 제한 float 및 complex는 허용되지 않습니다.
  • 제한사항 int 값은 -263~263 - 1의 부호 있는 64비트 정수입니다. 이 범위를 벗어나는 정수 리터럴은 컴파일 시간 오류를 발생시키고 결과가 이 범위를 벗어나는 작업은 런타임 오류를 발생시킵니다.
  • 제한사항 str 값은 최대 1,000,000자(영문 기준)까지 가능합니다. 더 긴 문자열 리터럴은 컴파일 시간 오류를 발생시킵니다. 더 긴 문자열을 메서드 인수로 전달하거나 작업 결과로 생성하면 런타임 오류가 발생합니다.

컨테이너 유형:

  • list 및 dict:
    • list[T]: 변경 가능한 시퀀스입니다. 색인 생성(예: my_list[0]) 및 append() 메서드를 지원합니다.
    • dict[K, V]: 변경 가능한 매핑입니다. 색인 생성(예: my_dict[key]) 및 pop() 메서드를 지원합니다.
  • collections.abc.Sequence 및 collections.abc.Mapping:
    • collections.abc.Sequence[T]: 시퀀스에 대한 읽기 전용 참조입니다. 색인별로 요소 읽기를 지원하지만(예: my_seq[0]) 요소를 수정하거나 삭제할 수 없습니다. list[T]는 Sequence[T]의 하위 유형이므로 list[T] 값 또는 목록 리터럴을 Sequence[T]로 입력된 타겟에 전달, 반환 또는 할당할 수 있지만 Sequence[T]를 list[T]에 할당할 수는 없습니다.
    • collections.abc.Mapping[K, V]: 매핑에 대한 읽기 전용 참조입니다. 키(예: my_map[key])로 항목을 읽을 수 있지만 항목을 수정하거나 삭제할 수는 없습니다. dict[K, V]는 Mapping[K, V]의 하위 유형이므로 dict[K, V] 값 또는 사전 리터럴을 Mapping[K, V]로 입력된 대상에 전달, 반환 또는 할당할 수 있지만 Mapping[K, V]를 dict[K, V]에 할당할 수는 없습니다.
  • 로드맵 tuple, set, dataclass는 로드맵에 있습니다.
  • 제한사항 컨테이너의 값에 구체적인 유형을 지정해야 합니다. 예를 들어 dict[str, int]는 허용되지만 일반 dict 또는 dict[str, Any]는 허용되지 않습니다.
  • 로드맵 목록 슬라이싱(예: my_list[1:3])
  • 로드맵 .get(), .keys(), .values(), .items(), .clear(), .extend()와 같은 다른 메서드

제한사항 계약 및 계정 필드, 함수 매개변수, 반환 유형을 포함한 모든 변수는 정적으로 정의되고 입력되어야 합니다. 런타임 시 유형을 변경할 수 없으며 구체적인 유형만 지원됩니다. 유형은 값으로 사용할 수 없습니다. 예를 들어 변수에 저장하거나 함수에 인수로 전달할 수 없습니다. 선언되지 않은 필드에 값을 할당하려고 하면 컴파일 시간 오류가 발생합니다.

제한사항 지역 변수는 읽기 전에 할당해야 합니다. 로컬 변수에는 기본값이 없으므로 할당되기 전에 읽으면 런타임 오류가 발생합니다.

클래스 및 상속

gcul.Contract 또는 gcul.ContractInterface의 직접 서브클래스인 클래스만 정의할 수 있습니다. 이 엄격한 규칙은 전체 Python 상속의 복잡성을 방지합니다. 이로 인해 찾기 어려운 버그가 발생하고 코드를 추론하기 어려워질 수 있습니다. 보안을 위해 상위 클래스의 속성이나 메서드를 재정의하려고 하면 오류가 발생하여 예기치 않은 동작을 방지하는 명확한 보호 장치가 제공됩니다.

로드맵 GCULpy는 핵심 원칙을 유지하면서 더 많은 유연성을 제공할 예정입니다. 로드맵에는 @override 데코레이터로 명시적으로 관리되는 메서드 재정의를 사용하는 사용자 정의 클래스의 단일 상속 지원이 포함되어 있습니다. 또한 super() 기본 제공은 직접적이고 예측 가능한 작업을 보장하기 위해 인수 없는 형식으로만 지원됩니다.

gcul 모듈

GCULpy는 계약 개발에 필수적인 유형, 데코레이터, 변수가 포함된 기본 제공 gcul 모듈을 제공합니다.

gcul.Contract 클래스

모든 계약의 기본 클래스입니다. 인스턴스를 직접 만들 수 없습니다. 계약은 CreateContract 트랜잭션을 통해서만 인스턴스화됩니다. 기본 gcul.Contract 클래스의 메서드와 속성은 서브클래스에서 재정의할 수 없습니다.

  • Contract.is_owner(account: Account) -> bool

    제공된 계정이 계약 소유자인 경우 True를 반환합니다.

  • Contract.call(target: ContractInterface, payment: Amount | None = None) -> ContractMethods

    target와의 교차 계약 상호작용을 시작합니다. 타겟에서 직접 메서드를 호출하는 대신(예: target.foo()) self.call()를 사용하여 원장 런타임을 통해 호출을 명시적으로 라우팅해야 합니다. 이 메커니즘은 대상 계약에서 해당 공개 메서드를 호출하는 데 사용되는 보안 프록시 객체를 반환하여 실행 경계가 엄격하게 적용되고 모든 payment가 안전하게 전달되도록 합니다. 자세한 내용은 ContractInterface를 참고하세요.

    @gcul.payable로 데코레이팅된 메서드를 호출할 때는 payment를 제공해야 하며, 지불 불가능한 메서드의 경우 생략해야 합니다.

    target은(는) 다른 계약이어야 합니다. self.call()를 통해 현재 계약을 호출하면 런타임 오류가 발생합니다. 동일한 계약에서 다른 메서드를 호출하려면 직접 호출합니다(예: self.foo()).

  • Contract.native_transfer(beneficiary: Account, amount: Amount) -> None

    원장 금액을 계약 잔액에서 beneficiary로 직접 전송합니다.

  • Contract.id: str

    계약의 고유 문자열 ID입니다.

gcul.Account 클래스

원장의 사용자 또는 계약 계정을 나타내는 기본 제공 유형입니다. gcul.Account 객체를 직접 만들 수 없습니다. 런타임 환경에서 객체를 만들고 함수 또는 메서드 인수로 제공합니다. 계정을 트랜잭션 인수로 전달하려면 계정 ID를 보유하는 AccountValue를 제공합니다. 런타임은 ID를 검증하고 전체 계정 객체로 변환합니다. ID가 등록된 계정에 속하지 않으면 트랜잭션이 실패합니다. 이렇게 하면 유효한 계정으로만 작업할 수 있습니다.

클래스 정의는 대략 다음과 같습니다.

@dataclasses.dataclass(frozen=True)
class Account:
  """A valid account on the ledger."""

  id: str  # The ID of the account as a string.
  • Account.id: str

    계정의 고유 문자열 ID입니다.

gcul.Amount 클래스

특정 원장 통화로 표시된 금전적 가치를 나타냅니다. gcul.Amount(value: int, currency: gcul.Currency)을 사용하여 금액을 인스턴스화합니다. 클래스 정의는 대략 다음과 같습니다.

@dataclasses.dataclass(frozen=True)
class Amount:
  """Represents an amount denominated in a ledger currency."""

  value: int
  currency: Currency

gcul.Currency 클래스

원장의 정규화된 통화를 나타냅니다. 클래스 정의는 대략 다음과 같습니다.

@dataclasses.dataclass(frozen=True)
class Currency:
  """Represents a fully qualified currency on the ledger."""

  code: str
  operator_id: str

gcul.ContractInterface 클래스

계약의 공개 인터페이스를 선언하는 기본 클래스입니다. 기본 gcul.ContractInterface 클래스의 메서드와 속성은 서브클래스에서 재정의할 수 없습니다. 또한 인터페이스 선언의 메서드는 코드를 포함할 수 없으며 ...만 허용됩니다.

원장에 배포된 다른 계약에서 메서드를 호출하는 계약의 경우 gcul.ContractInterface에서 상속되는 인터페이스 클래스를 선언합니다. 메서드 스텁은 타겟 계약의 해당 선언과 일치해야 합니다. 여기에는 인수, 반환 유형, 데코레이터 주석(예: @gcul.readonly 또는 @gcul.payable)이 포함됩니다.

계약 코드 내에서 self.call(target, payment)를 사용하여 다른 계약의 메서드를 호출합니다.

다음 예는 TokenVendor 계약이 ERC20Interface를 구현하는 다른 계약과 상호작용하는 방식을 보여줍니다. self.call()를 사용하여 원장 내 통화 결제에 대한 대가로 구매자에게 토큰을 전송합니다.

class ERC20Interface(gcul.ContractInterface):
  """Interface for an external ERC20 token contract."""

  def transfer(self, beneficiary: gcul.Account, value: int) -> int:
    ...

class TokenVendor(gcul.Contract):
  """A vendor contract that distributes ERC20 tokens."""

  price_per_token: int

  def __init__(self, price_per_token: int) -> None:
    self.price_per_token = price_per_token

  @gcul.payable
  def purchase_tokens(self, token: ERC20Interface, quantity: int) -> int:
    """Accepts on-ledger currency for a specific quantity of ERC20 tokens."""

    # Enforce positive quantity.
    assert quantity > 0, "Quantity must be positive"

    # Calculate the required payment.
    required_payment = quantity * self.price_per_token
    assert gcul.payment.value == required_payment, "Incorrect payment amount"

    # Invokes `transfer` on the external ERC20 token contract
    # to move tokens from the vendor's own balance to the caller.
    return self.call(token).transfer(gcul.caller, quantity)

인터페이스 유형은 메서드 매개변수뿐만 아니라 계약 필드로도 사용할 수 있습니다. 인터페이스를 필드에 저장하면 계약이 트랜잭션 전반에서 다른 계약에 대한 참조를 보유하고 나중에 해당 참조를 대체할 수 있습니다. 다음 예시 계약은 이전에 선언된 ERC20Interface을 사용합니다.

class Router(gcul.Contract):
  """Holds a replaceable reference to an ERC20 token contract."""

  target: ERC20Interface

  def __init__(self, target: ERC20Interface) -> None:
    self.target = target

  def set_target(self, new_target: ERC20Interface) -> None:
    assert self.is_owner(gcul.sender), "Only the owner can set the target"
    self.target = new_target

gcul.AccountField 클래스

계정 필드를 선언하기 위한 기본 제공 일반 유형입니다. gcul.AccountField[T]는 계약에 스토리지 권한을 부여한 각 계정에 대해 T 유형의 별도 값을 보유합니다. gcul.Account 키가 있는 매핑으로 작동하며, 이를 사용하여 각 특정 계정의 값을 읽거나 쓸 수 있습니다. 예를 들면 다음과 같습니다.

class Token(gcul.Contract):
  balance: gcul.AccountField[int]

  def deposit(self, account: gcul.Account, value: int) -> None:
    self.balance[account] = value  # Writes the value for one account.

  def get_balance(self, account: gcul.Account) -> int:
    return self.balance[account]  # Reads the value for one account.

T는 기본값이 있는 유형이어야 하므로 gcul.Account, gcul.Amount, gcul.Currency, 인터페이스 유형은 계정 필드 값으로 사용할 수 없습니다. 계정이 계약에 스토리지 권한을 부여하면 모든 계정 필드가 해당 계정의 유형에 대한 기본값으로 채워집니다.

제한사항 계정으로 색인을 생성하는 것은 계정 필드가 지원하는 유일한 작업입니다. 필드 자체를 변수에 할당하거나, 인수로 전달하거나, 함수에서 반환하거나, 반복할 수 없습니다.

제한사항 계정 필드 항목은 삭제할 수 없습니다. 저장된 값을 덮어쓸 수 있지만 항목 자체는 영구적입니다.

제한사항 gcul.sender, gcul.caller의 계정 필드와 메서드 인수로 전달된 모든 gcul.Account(목록 또는 기타 컨테이너 내부 포함)는 메서드 실행 전에 로드됩니다. 다른 계정의 경우 단일 트랜잭션은 최대 15개의 고유한 (contract, account) 쌍에 대해 계정 필드를 지연 로드할 수 있습니다. 트랜잭션에서 동일한 쌍의 계정 필드에 여러 번 액세스하는 것은 한 번만 계산됩니다. 이 한도를 초과하는 트랜잭션은 실패합니다.

@gcul.readonly

계약 메서드에 읽기 전용 주석을 추가합니다. 이 데코레이터가 있는 메서드 선언은 계약 상태를 변경하거나 원장 내 통화 전송을 수행할 수 없습니다. 또한 서명에 변경 가능한 컨테이너 유형(예: list 또는 dict)을 사용할 수 없습니다. 대신 Sequence 또는 Mapping와 같은 읽기 전용 유형 선언을 사용해야 합니다.

@gcul.payable

계약 메서드에 실행을 위해 결제가 필요함을 주석으로 표시합니다. 메서드가 gcul.payment를 사용하여 원장 내 통화 결제를 수신할 것으로 예상하는 경우 이 데코레이터로 표시해야 합니다. 그렇지 않으면 트랜잭션이 실패합니다.

gcul.sender: gcul.Account

현재 InvokeContractMethod 트랜잭션에 서명하고 제출한 계정에 대한 참조를 보유합니다. gcul.sender 값은 교차 계약 호출이 포함되더라도 트랜잭션 실행 중에 변경되지 않습니다. 이는 즉각적인 호출자를 참조하는 gcul.caller와 다릅니다.

gcul.caller: gcul.Account

현재 메서드의 즉각적인 호출자에 대한 참조를 보유합니다. 직접 사용자 트랜잭션의 경우 gcul.caller는 트랜잭션을 제출한 사람을 가리키는 gcul.sender와 일치합니다. 교차 계약 호출의 경우 gcul.caller는 호출 계약의 계정 ID에 해당합니다. 계약 내의 내부 호출은 gcul.caller 값을 변경하지 않습니다.

gcul.payment: gcul.Amount

@gcul.payable로 데코레이션된 메서드로 전송된 결제를 보관합니다. 지불 불가능한 메서드 내에서 gcul.payment에 액세스하면 컴파일 시간 오류가 발생합니다.

연산자

Python에서 사용할 수 있는 대부분의 연산자는 GCULpy에서 지원되며 예상대로 작동합니다.

  • 단항 및 이항 형식을 포함한 덧셈(+) 및 뺄셈(-)
  • 곱하기(*), 내림 나눗셈(//), 나머지(%)
  • 양수 지수의 거듭제곱(**)
  • 비교(<, <=, >, >=, ==, !=)
  • 비트 AND(&), OR(|), XOR(^), 왼쪽 시프트(<<), 오른쪽 시프트(>>), 부정(~)
  • 불리언 연산(and, or, not)
  • 로드맵 객체 ID(is)
  • 제한사항 음수 지수는 허용되지 않으며 런타임 오류가 발생합니다.
  • 제한사항 float 반환 유형이 있는 실제 나눗셈(/)은 허용되지 않으며 컴파일 시간 오류가 발생합니다.

제어 흐름

Python의 대부분의 제어 흐름 문은 동일한 시맨틱스로 GCULpy에서 작동합니다.

  • pass 문
  • 내부 함수 호출(동일한 계약, 비재귀적)
  • assert 문
  • if ... then .. else ... 문
  • for VAR in CONTAINER 문
  • 외부 함수 호출(다른 계약에 대한 호출, 비재귀적)
  • break 및 continue 문
  • raise 문
  • 로드맵 try ... except 문구
  • 로드맵 match 문구
  • 로드맵 generators 및 yield 문
  • 로드맵 컨텍스트 관리자 및 with 문

제한사항 GCULpy는 무한 루프를 방지하고 정적 분석을 용이하게 하기 위해 의도적으로 튜링 불완전입니다. 이를 적용하는 방법은 다음과 같습니다.

  • 무한 루프 없음: 반복은 유한 컨테이너 또는 range(...)가 있는 제한된 시퀀스에 대해 for 루프를 사용하는 경우에만 허용됩니다. while 루프는 허용되지 않습니다. 목록에 요소를 추가 또는 삭제하거나 사전에 키를 추가 또는 삭제하는 등의 일부 컨테이너 업데이트는 반복하는 동안 허용되지 않습니다.
  • 재귀 없음: 함수는 직접 또는 간접적으로 자신을 호출할 수 없습니다. 런타임 환경은 정적 검사와 런타임 검사를 모두 수행하여 재귀 사용을 감지하고 거부합니다.
  • 비동기 제어 흐름 없음: 예측 가능성, 보안, 결정론적 실행이라는 핵심 설계 원칙을 유지하기 위해 async 기본 요소를 사용할 수 없습니다. 비동기 작업은 프로그램의 제어 흐름을 추론하기 어렵게 만들며, 이로 인해 취약점과 경합 상태가 발생하는 경우가 많습니다.

기본 제공 함수

GCULpy는 다음 기본 제공 함수를 지원합니다.

  • abs(x)

    정수 x의 절댓값을 반환합니다. 인수는 int 유형이어야 합니다.

  • len(s)

    문자열 또는 유한 컨테이너의 길이(항목 수)를 반환합니다.

  • max(iterable)
    max(arg1, arg2, *args)

    반복 가능한 항목 중 가장 큰 항목 또는 두 개 이상의 인수 중 가장 큰 인수를 반환합니다. 빈 이터러블에서 max()를 호출하면 런타임 오류가 발생합니다.

  • min(iterable)
    min(arg1, arg2, *args)

    반복 가능한 항목 중 가장 작은 항목 또는 두 개 이상의 인수 중 가장 작은 인수를 반환합니다. 빈 이터러블에서 min()를 호출하면 런타임 오류가 발생합니다.

  • range(stop)
    range(start, stop[, step=1])

    0이 아닌 정수 step를 사용하여 정수의 산술 시퀀스를 생성합니다.

    제한사항 for 루프(예: for i in range(10): ...)와 함께 인라인으로만 사용할 수 있습니다. 범위 객체는 변수에 할당하거나 필드에 저장하거나 함수에 인수로 전달하거나 len()로 쿼리할 수 없습니다.

로드맵 다음 기본 제공 함수는 구현 로드맵에 있습니다.

A
all()
any()

B
bin()
bool()
bytes()

C
chr()

D
dict()
divmod()

E
enumerate()

F
format()
frozenset()

H
hash()
hex()

I
id()
int()

L
list()

O
oct()
ord()

P
pow()
property()

R
repr()
reversed()

S
set()
sorted()
staticmethod()
str()
sum()
super()

T
tuple()

Z
zip()

출시 노트

  • 2026년 11월 3일 교차 계약 호출, 원장 내 통화 전송 및 결제, @gcul.readonly 메서드, list, 추가 제어 흐름 문, 기본 제공 함수를 지원하는 gculpyc 컴파일러(v0.2.0)의 GA 버전을 출시했습니다.

  • 2026년 1월 28일 Universal Ledger 비공개 프리뷰 참여자에게 초기 버전의 gculpyc 컴파일러가 제공됩니다. 컴파일러를 사용하는 튜토리얼은 프로그래밍 가능한 계약 배포를 참조하세요.