Bahasa GCULpy

GCULpy adalah subset Python yang diketik secara statis dan ketat, yang dirancang agar aman, mudah dibaca, dan dapat diaudit. Desainnya sengaja membatasi fitur dinamis tertentu dari Python untuk mencegah kerentanan smart contract yang umum dan memastikan bahwa perilaku kontrak selalu dapat diprediksi.

Halaman ini memberikan referensi untuk spesifikasi bahasa GCULpy, yang mencakup konsep inti dan siklus proses kontrak di jaringan Universal Ledger.

Konsep inti

Kontrak berikut menentukan contoh token ERC20 di GCULpy:

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

Kontrak GCULpy adalah class yang diwarisi dari gcul.Contract. Class ini berisi kolom (menyimpan status) dan metode (logika pemrosesan yang beroperasi pada kolom).

Kolom

Status dalam kontrak GCULpy disimpan dalam kolom. Semua kolom harus dideklarasikan dengan jenis statis di tingkat class. Ada dua jenis kolom:

  • Kolom kontrak menyimpan satu nilai yang disimpan bersama kontrak itu sendiri. Dalam contoh ERC20Token, symbol: str dan total_supply: int adalah kolom kontrak.

  • Kolom akun menyimpan nilai terpisah untuk setiap akun yang berinteraksi dengan kontrak. Variabel ini dideklarasikan dengan gcul.AccountField[T], dengan T sebagai jenis nilai per akun. Dalam contoh ERC20Token, balance: gcul.AccountField[int] adalah kolom akun. Sebelum kontrak dapat menulis ke ruang penyimpanan yang dicadangkan untuk akun tersebut, izin penyimpanan harus secara eksplisit diberikan ke kontrak. Setelah data disimpan, hanya instance kontrak yang dapat mengubahnya—akun tidak dapat melakukannya.

Batasan Kolom memiliki batas penyimpanan berikut. Transaksi yang melebihi salah satu batas akan gagal.

  • Semua kolom kontrak dari sebuah kontrak menggunakan satu entri penyimpanan, yang dapat menampung total sekitar 5 MiB data yang diserialisasi.
  • Semua kolom akun yang disimpan kontrak untuk setiap akun individu dapat menampung maksimal 1 MiB data serial secara total.

Metode

Metode menentukan logika kontrak yang dapat dieksekusi. Fungsi ini berperilaku seperti metode Python dan dapat membaca atau mengubah kolom kontrak.

  • __init__: Konstruktor hanya dipanggil sekali saat kontrak pertama kali di-deploy. Setiap kolom kontrak harus diberi nilai di konstruktor. Konstruktor tidak melakukan inisialisasi kolom akun; nilai setiap akun dibuat saat akun tersebut memberikan izin penyimpanan.

  • Metode pribadi: Metode yang diawali dengan garis bawah (misalnya, _internal_logic) bersifat pribadi dan hanya dapat dipanggil oleh metode lain dalam kontrak yang sama. Penafsir Universal Ledger menerapkan batasan ini.

  • Metode publik: Metode apa pun yang tidak diawali dengan garis bawah (_) bersifat publik. Metode publik dapat dipanggil oleh pengguna mana pun dengan ROLE_CONTRACT_PARTICIPANT dengan mengirimkan transaksi InvokeContractMethod.

Batasan Metode publik dan semua metode @gcul.readonly tidak dapat menggunakan jenis yang dapat diubah dalam tanda tangannya, misalnya list atau dict. Sebagai gantinya, gunakan deklarasi jenis hanya baca seperti Sequence atau Mapping.

Batasan Selama eksekusi, metode dibatasi hingga batas atas penggunaan komputasi di seluruh jaringan. Melebihi batas ini akan menyebabkan kegagalan transaksi.

Siklus proses kontrak

Bagian berikut membahas operasi umum yang terlibat dalam siklus proses kontrak GCULpy.

Men-deploy kontrak

Pertama, kompilasi kode sumber GCULpy Anda menggunakan compiler gculpyc. Kemudian, pengguna dengan ROLE_CONTRACT_CREATOR dapat mengirimkan transaksi CreateContract untuk men-deploy bytecode yang telah dikompilasi ke jaringan Universal Ledger. Untuk mengetahui petunjuk mendetail, lihat tutorial Men-deploy kontrak yang dapat diprogram.

Transaksi tersebut akan terlihat seperti:

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

Saat jaringan memproses transaksi ini:

  • Konstruktor, yaitu metode __init__, dijalankan dengan argumen yang disediakan untuk membuat instance kontrak baru.
  • Pengirim transaksi menjadi pemilik kontrak.
  • Instance kontrak disimpan secara permanen di ledger dan diberi ID kontrak unik yang ditampilkan sebagai bagian dari output transaksi.

Memberikan izin

Sebelum kontrak dapat menyimpan data di kolom akun atas nama akun, baik akun untuk pengguna maupun kontrak lain, izin penyimpanan harus diberikan ke kontrak yang menyimpan data. Ini adalah langkah keamanan yang penting.

Memberikan izin untuk akun pengguna

Pengguna dengan ROLE_CONTRACT_PARTICIPANT dapat mengirimkan transaksi GrantContractPermissions untuk ID kontrak tertentu:

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

Saat jaringan memproses transaksi ini:

  • Jika kontrak tidak menentukan kolom akun, transaksi akan ditolak.
  • Jika kontrak menentukan kolom akun, semuanya akan diisi dengan nilai default (misalnya, contract.balance[gcul.sender] = 0). Nilai ini kemudian disimpan dalam status dunia sebagai bagian dari data akun.

Memberikan izin untuk kontrak delegasi

Pengguna dengan ROLE_CONTRACT_PARTICIPANT dapat mengirimkan transaksi GrantContractPermissions untuk mengizinkan satu kontrak menyimpan data atas nama kontrak lain yang mereka miliki:

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
  }
}

Saat jaringan memproses transaksi ini:

  • Pengirim transaksi, yaitu sender_id, harus merupakan pemilik kontrak yang diidentifikasi oleh delegate_contract_id.
  • Kontrak tidak dapat memberikan izin penyimpanan kepada dirinya sendiri, sehingga delegate_contract_id harus berbeda dari contract_id.
  • Izin penyimpanan diberikan secara eksklusif ke kontrak yang ditentukan dalam delegate_contract_id, bukan pengirim transaksi.
  • Kolom akun yang ditentukan pada kontrak target, contract_id, diinisialisasi untuk kontrak delegasi, delegate_contract_id, sehingga memungkinkan metode pada kontrak target mengakses kolom akun dengan aman yang dikunci oleh gcul.caller atau gcul.sender selama pemanggilan lintas kontrak.

Memanggil metode kontrak

Setelah kontrak di-deploy dan izin yang diperlukan diberikan, pengguna dapat berinteraksi dengan kontrak tersebut dengan memanggil metode publiknya. Pengguna dengan ROLE_CONTRACT_PARTICIPANT dapat mengirimkan transaksi InvokeContractMethod yang menentukan ID kontrak, nama metode, dan nilai argumen metode.

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 }
    }
  }
}

Saat jaringan memproses transaksi ini:

  • Instance kontrak yang terkait dengan CONTRACT_ID yang diberikan akan diambil.
  • Metode mint(beneficiary=Account("BENEFICIARY_ID"), value=10) dijalankan. Objek Account untuk penerima manfaat dibuat dan divalidasi oleh runtime. Logika metode dapat mengasumsikan dengan aman bahwa ID yang diberikan valid dan merujuk ke akun yang ada di ledger.
  • Jika metode gagal karena alasan apa pun, transaksi akan gagal dan tidak ada pembaruan yang akan dilakukan pada status kontrak.
  • Jika metode berhasil, status kontrak yang diperbarui akan dicatat dalam status dunia.

Spesifikasi bahasa

GCULpy dirancang untuk keamanan dan prediktabilitas, sehingga tidak mengizinkan beberapa fitur Python; fitur ini akan ditandai dengan label Restriction. Batasan ini dimaksudkan sebagai fitur bahasa permanen, yang diperkenalkan untuk membuat logika kontrak lebih mudah dibaca, diaudit, dan dianalisis secara statis, sehingga membatasi perilaku yang mengejutkan atau tidak aman.

Fitur lainnya ditandai dengan label Roadmap, fitur ini ada dalam roadmap implementasi, tetapi belum didukung oleh compiler gculpyc.

Jenis

GCULpy mendukung berbagai jenis variabel umum dengan penekanan kuat pada pengetikan statis.

Jenis nilai utama:

  • int, bool, str, None sudah didukung.
  • Roadmap Decimal, bytes, Enum ada dalam roadmap.
  • Pembatasan float dan complex tidak diizinkan.
  • Nilai Restriction int adalah bilangan bulat 64-bit bertanda, dari -263 hingga 263 - 1. Literal bilangan bulat di luar rentang ini akan menghasilkan error waktu kompilasi, dan operasi yang hasilnya di luar rentang ini akan menghasilkan error runtime.
  • Nilai Restriction str dapat memiliki panjang maksimal 1.000.000 karakter. Literal string yang lebih panjang akan menyebabkan error waktu kompilasi. Meneruskan string yang lebih panjang sebagai argumen metode, atau menghasilkan string yang lebih panjang sebagai hasil operasi, akan menyebabkan error runtime.

Jenis penampung:

  • list dan dict:
    • list[T]: Urutan yang dapat diubah. Mendukung pengindeksan, misalnya my_list[0], dan metode append().
    • dict[K, V]: Pemetaan yang dapat diubah. Mendukung pengindeksan, misalnya my_dict[key], dan metode pop().
  • collections.abc.Sequence dan collections.abc.Mapping:
    • collections.abc.Sequence[T]: Referensi hanya baca ke urutan. Mendukung pembacaan elemen berdasarkan indeks, misalnya my_seq[0], tetapi elemen tidak dapat diubah atau dihapus. list[T] adalah subjenis dari Sequence[T], sehingga Anda dapat meneruskan, menampilkan, atau menetapkan nilai list[T] atau literal daftar ke target yang diketik sebagai Sequence[T], tetapi Anda tidak dapat menetapkan Sequence[T] ke list[T].
    • collections.abc.Mapping[K, V]: Referensi hanya baca ke pemetaan. Mendukung pembacaan entri berdasarkan kunci, misalnya my_map[key], tetapi entri tidak dapat diubah atau dihapus. dict[K, V] adalah subjenis dari Mapping[K, V], sehingga Anda dapat meneruskan, menampilkan, atau menetapkan nilai dict[K, V] atau literal kamus ke target yang diketik sebagai Mapping[K, V], tetapi Anda tidak dapat menetapkan Mapping[K, V] ke dict[K, V].
  • Roadmap tuple, set, dataclass ada dalam roadmap.
  • Batasan Jenis konkret harus ditentukan untuk nilai dalam penampung, misalnya dict[str, int] diizinkan, tetapi dict atau dict[str, Any] biasa tidak diizinkan.
  • Roadmap Pemotongan daftar, misalnya my_list[1:3].
  • Roadmap Metode lain, seperti .get(), .keys(), .values(), .items(), .clear(), atau .extend().

Batasan Semua variabel—termasuk kolom kontrak dan akun, parameter fungsi, dan jenis nilai yang ditampilkan—harus ditentukan dan diketik secara statis. Jenisnya tidak dapat diubah saat runtime dan hanya jenis konkret yang didukung. Jenis tidak dapat digunakan sebagai nilai, misalnya tidak dapat disimpan dalam variabel atau diteruskan ke fungsi sebagai argumen. Mencoba menetapkan nilai ke kolom yang tidak dideklarasikan akan menghasilkan error waktu kompilasi.

Batasan Variabel lokal harus ditetapkan sebelum dibaca. Variabel lokal tidak memiliki nilai default, sehingga membacanya sebelum ditetapkan akan menghasilkan error runtime.

Class dan pewarisan

Anda hanya dapat menentukan class yang merupakan subclass langsung dari gcul.Contract atau gcul.ContractInterface. Aturan ketat ini mencegah kompleksitas pewarisan Python penuh, yang dapat menimbulkan bug yang sulit ditemukan dan membuat kode sulit dipahami. Demi keamanan, upaya untuk mengganti properti atau metode dari class induk akan memunculkan error, sehingga memberikan perlindungan yang jelas terhadap perilaku yang tidak terduga.

Roadmap GCULpy akan menawarkan lebih banyak fleksibilitas sekaligus mempertahankan prinsip intinya. Roadmap ini mencakup dukungan untuk single inheritance pada class yang ditentukan pengguna dengan penggantian metode yang dikelola secara eksplisit dengan dekorator @override. Selain itu, fungsi bawaan super() hanya akan didukung dalam bentuk tanpa argumen, untuk memastikan operasi yang langsung dan dapat diprediksi.

Modul gcul

GCULpy menyediakan modul gcul bawaan dengan jenis, dekorator, dan variabel penting untuk pengembangan kontrak.

class gcul.Contract

Class dasar untuk semua kontrak. Anda tidak dapat membuat instance-nya secara langsung; kontrak hanya dibuat melalui transaksi CreateContract. Metode dan properti dari class gcul.Contract dasar tidak dapat diganti di subclass.

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

    Menampilkan True jika akun yang diberikan adalah pemilik kontrak.

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

    Memulai interaksi lintas kontrak dengan target. Daripada memanggil metode pada target secara langsung (misalnya, target.foo()), Anda harus menggunakan self.call() untuk merutekan pemanggilan secara eksplisit melalui runtime ledger. Mekanisme ini menampilkan objek proxy aman yang digunakan untuk memanggil metode publik yang sesuai pada kontrak target, sehingga memastikan bahwa batas eksekusi diterapkan secara ketat dan payment apa pun dikirimkan dengan aman. Lihat ContractInterface untuk mengetahui detailnya.

    payment harus diberikan saat memanggil metode yang didekorasi dengan @gcul.payable; dan harus dihilangkan untuk metode yang tidak dapat dibayar.

    target harus berupa kontrak yang berbeda. Memanggil kontrak saat ini melalui self.call() akan memunculkan error runtime. Untuk memanggil metode lain pada kontrak yang sama, panggil secara langsung, misalnya, self.foo().

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

    Mentransfer jumlah on-ledger langsung dari saldo kontrak ke beneficiary.

  • Contract.id: str

    ID string unik kontrak.

class gcul.Account

Jenis bawaan yang mewakili akun pengguna atau kontrak di ledger. Anda tidak dapat membuat objek gcul.Account secara langsung; lingkungan runtime membuatnya untuk Anda dan menyediakannya sebagai argumen fungsi atau metode. Untuk meneruskan akun sebagai argumen transaksi, berikan AccountValue yang menyimpan ID akun. Runtime memvalidasi ID dan mengonversinya menjadi objek akun lengkap. Jika ID bukan milik akun terdaftar, transaksi akan gagal. Hal ini memastikan Anda hanya menggunakan akun yang valid.

Definisi class ini kira-kira setara dengan:

@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 string unik akun.

class gcul.Amount

Mewakili nilai uang yang dinyatakan dalam mata uang buku besar tertentu. Buat instance jumlah menggunakan gcul.Amount(value: int, currency: gcul.Currency). Definisi class ini kira-kira setara dengan:

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

  value: int
  currency: Currency

class gcul.Currency

Mewakili mata uang yang sepenuhnya memenuhi syarat di ledger. Definisi class ini kira-kira setara dengan:

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

  code: str
  operator_id: str

class gcul.ContractInterface

Class dasar untuk mendeklarasikan antarmuka publik kontrak. Metode dan properti dari class gcul.ContractInterface dasar tidak dapat diganti di subclass. Selain itu, metode dalam deklarasi antarmuka tidak boleh menyertakan kode apa pun, hanya ... yang diizinkan.

Agar kontrak dapat memanggil metode pada kontrak lain yang di-deploy di ledger, deklarasikan class antarmuka yang mewarisi dari gcul.ContractInterface. Stub metode harus cocok dengan deklarasi yang sesuai dalam kontrak target. Hal ini mencakup argumen, jenis nilai yang ditampilkan, dan anotasi dekorator seperti @gcul.readonly atau @gcul.payable.

Di dalam kode kontrak Anda, gunakan self.call(target, payment) untuk memanggil metode dari kontrak lain.

Contoh berikut mengilustrasikan cara kontrak TokenVendor berinteraksi dengan kontrak lain yang mengimplementasikan ERC20Interface. Smart contract ini menggunakan self.call() untuk mentransfer token kepada pembeli sebagai imbalan atas pembayaran mata uang on-ledger:

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)

Jenis antarmuka juga dapat digunakan sebagai kolom kontrak, bukan hanya sebagai parameter metode. Menyimpan antarmuka dalam kolom memungkinkan kontrak menyimpan referensi ke kontrak lain di seluruh transaksi, dan mengganti referensi tersebut nanti. Contoh kontrak berikut menggunakan ERC20Interface yang dideklarasikan sebelumnya:

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

class gcul.AccountField

Jenis generik bawaan untuk mendeklarasikan kolom akun. gcul.AccountField[T] menyimpan nilai terpisah dari jenis T untuk setiap akun yang telah memberikan izin penyimpanan kepada kontrak. Fungsi ini berperilaku sebagai pemetaan dengan kunci gcul.Account, yang dapat Anda gunakan untuk membaca atau menulis nilai untuk setiap akun tertentu. Contoh:

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 harus berupa jenis yang memiliki nilai default, sehingga gcul.Account, gcul.Amount, gcul.Currency, dan jenis antarmuka tidak dapat digunakan sebagai nilai kolom akun. Saat akun memberikan izin penyimpanan ke kontrak, setiap kolom akun akan diisi dengan nilai default jenisnya untuk akun tersebut.

Batasan Pengindeksan dengan akun adalah satu-satunya operasi yang didukung kolom akun. Kolom itu sendiri tidak dapat ditetapkan ke variabel, diteruskan sebagai argumen, ditampilkan dari fungsi, atau diiterasi.

Pembatasan Entri kolom akun tidak dapat dihapus. Nilai yang disimpan dapat ditimpa, tetapi entri itu sendiri bersifat permanen.

Kolom akun Pembatasan untuk gcul.sender, gcul.caller, dan gcul.Account apa pun yang diteruskan sebagai argumen metode (termasuk di dalam daftar atau penampung lainnya) dimuat sebelum eksekusi metode. Untuk akun lain, satu transaksi dapat memuat kolom akun secara lambat untuk maksimal 15 pasangan (contract, account) yang berbeda; mengakses kolom akun untuk pasangan yang sama beberapa kali dalam satu transaksi hanya dihitung sekali. Transaksi yang melebihi batas ini akan gagal.

@gcul.readonly

Memberi anotasi pada metode kontrak sebagai hanya baca. Deklarasi metode dengan dekorator ini tidak dapat mengubah status kontrak atau melakukan transfer mata uang di ledger. Fungsi tersebut juga tidak dapat menggunakan jenis penampung yang dapat diubah dalam tanda tangannya, misalnya list atau dict; fungsi tersebut harus menggunakan deklarasi jenis hanya baca seperti Sequence atau Mapping.

@gcul.payable

Menganotasi metode kontrak sebagai memerlukan pembayaran untuk dieksekusi. Jika suatu metode berharap menerima pembayaran mata uang on-ledger menggunakan gcul.payment, metode tersebut harus ditandai dengan dekorator ini, jika tidak, transaksi akan gagal.

gcul.sender: gcul.Account

Menyimpan referensi ke akun yang menandatangani dan mengirimkan transaksi InvokeContractMethod saat ini. Nilai gcul.sender tidak berubah selama eksekusi transaksi, meskipun melibatkan panggilan lintas kontrak, tidak seperti gcul.caller yang merujuk ke pemanggil langsung.

gcul.caller: gcul.Account

Menyimpan referensi ke pemanggil langsung dari metode saat ini. Untuk transaksi pengguna langsung, gcul.caller cocok dengan gcul.sender yang mengarah ke siapa pun yang mengirimkan transaksi. Untuk panggilan lintas kontrak, gcul.caller sesuai dengan ID akun kontrak panggilan. Panggilan internal dalam kontrak tidak mengubah nilai gcul.caller.

gcul.payment: gcul.Amount

Menyimpan pembayaran yang ditransfer ke metode yang didekorasi dengan @gcul.payable. Mengakses gcul.payment di dalam metode yang tidak dapat dibayar akan memunculkan error waktu kompilasi.

Operator

Sebagian besar operator yang tersedia di Python didukung di GCULpy dan berfungsi seperti yang diharapkan.

  • Penambahan (+) dan pengurangan (-), termasuk bentuk unary dan biner.
  • Perkalian (*), pembagian bilangan bulat (//), dan modulo (%).
  • Eksponensial (**) untuk eksponen positif.
  • Perbandingan (<, <=, >, >=, ==, !=).
  • Bitwise and (&), or (|), xor (^), left shift (<<), right shift (>>), negation (~).
  • Operasi Boolean (and, or, not).
  • Roadmap Identitas objek (is).
  • Batasan Eksponen negatif tidak diizinkan dan akan memunculkan error runtime.
  • Batasan Pembagian sebenarnya (/), karena memiliki jenis nilai yang ditampilkan float, tidak diizinkan dan akan menghasilkan error waktu kompilasi.

Alur kontrol

Sebagian besar pernyataan alur kontrol dari Python berfungsi di GCULpy dengan semantik yang sama:

  • Laporan pass.
  • panggilan fungsi internal (kontrak yang sama, non-rekursif).
  • Laporan assert.
  • Laporan if ... then .. else ....
  • Laporan for VAR in CONTAINER.
  • panggilan fungsi eksternal (ke kontrak lain, non-rekursif).
  • Pernyataan break dan continue.
  • Laporan raise.
  • Roadmap try ... except pernyataan.
  • Roadmap match pernyataan.
  • Roadmap pernyataan generators dan yield.
  • Roadmap pengelola konteks dan pernyataan with.

Batasan GCULpy sengaja dibuat tidak lengkap Turing untuk mencegah loop tanpa batas dan memfasilitasi analisis statis. Berikut cara penerapannya:

  • Tidak ada loop tak terbatas: Iterasi hanya diizinkan menggunakan loop for pada container terbatas atau urutan terbatas dengan range(...); loop while tidak diizinkan. Beberapa update container, misalnya menambahkan atau menghapus elemen ke daftar atau kunci ke kamus, tidak diizinkan saat melakukan iterasi.
  • Tidak ada pengulangan: Fungsi tidak dapat memanggil dirinya sendiri, baik secara langsung maupun tidak langsung. Lingkungan runtime melakukan pemeriksaan statis dan runtime untuk mendeteksi dan menolak penggunaan rekursi.
  • Tidak ada alur kontrol asinkron: Penggunaan primitif async tidak diizinkan untuk mempertahankan prinsip desain inti, yaitu prediktabilitas, keamanan, dan eksekusi deterministik. Operasi asinkron menyulitkan penalaran tentang alur kontrol program, yang sering kali menyebabkan kerentanan dan kondisi race.

Fungsi bawaan

GCULpy mendukung fungsi bawaan berikut:

  • abs(x)

    Menampilkan nilai absolut dari bilangan bulat x. Argumen harus berjenis int.

  • len(s)

    Menampilkan panjang, yaitu jumlah item, dari string atau penampung terbatas.

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

    Menampilkan item terbesar dalam iterable atau yang terbesar dari dua argumen atau lebih. Memanggil max() pada iterable kosong akan memunculkan error runtime.

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

    Menampilkan item terkecil dalam iterable atau yang terkecil dari dua argumen atau lebih. Memanggil min() pada iterable kosong akan memunculkan error runtime.

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

    Menghasilkan urutan aritmetika bilangan bulat dengan bilangan bulat bukan nol step.

    Batasan Hanya dapat digunakan secara inline dengan loop for, misalnya for i in range(10): .... Objek rentang tidak dapat ditetapkan ke variabel atau disimpan dalam kolom, diteruskan sebagai argumen ke fungsi, atau dikueri dengan len().

Roadmap Fungsi bawaan berikut ada dalam roadmap implementasi:

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()

Catatan rilis

  • 03 Nov 2026. Merilis versi GA compiler gculpyc (v0.2.0), termasuk dukungan untuk panggilan lintas kontrak, transfer dan pembayaran mata uang on-ledger, metode @gcul.readonly, list, pernyataan alur kontrol tambahan, dan fungsi bawaan.

  • 28 Jan 2026. Versi awal compiler gculpyc tersedia untuk peserta dalam pratinjau pribadi Universal Ledger. Untuk tutorial menggunakan compiler, lihat Men-deploy kontrak yang dapat diprogram.