mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
196 lines
5.1 KiB
Go
196 lines
5.1 KiB
Go
// Package main exports all Lux precompiles as a C shared library.
|
|
//
|
|
// Build:
|
|
//
|
|
// CGO_ENABLED=1 go build -buildmode=c-shared -o libluxprecompile.so ./bindings/cabi/
|
|
// CGO_ENABLED=1 go build -buildmode=c-shared -o libluxprecompile.dylib ./bindings/cabi/ # macOS
|
|
//
|
|
// This produces libluxprecompile.{so,dylib} + libluxprecompile.h
|
|
// Rust (hanzod), Python, and TypeScript can bind to it.
|
|
//
|
|
// The dispatch pattern: one Run function handles all registered precompiles.
|
|
// Precompile lookup is by 20-byte hex address via the module registry.
|
|
package main
|
|
|
|
/*
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
*/
|
|
import "C"
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"strings"
|
|
"unsafe"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/precompile/modules"
|
|
|
|
// Blank imports trigger init() → RegisterModule for every precompile.
|
|
_ "github.com/luxfi/precompile/ai"
|
|
_ "github.com/luxfi/precompile/blake3"
|
|
_ "github.com/luxfi/precompile/cggmp21"
|
|
_ "github.com/luxfi/precompile/dead"
|
|
_ "github.com/luxfi/precompile/dex"
|
|
// REMOVED: ecies — secret keys in calldata are public on-chain
|
|
_ "github.com/luxfi/precompile/corona"
|
|
_ "github.com/luxfi/precompile/fhe"
|
|
_ "github.com/luxfi/precompile/frost"
|
|
_ "github.com/luxfi/precompile/graph"
|
|
_ "github.com/luxfi/precompile/hpke"
|
|
_ "github.com/luxfi/precompile/mldsa"
|
|
_ "github.com/luxfi/precompile/mlkem"
|
|
_ "github.com/luxfi/precompile/ring"
|
|
_ "github.com/luxfi/precompile/slhdsa"
|
|
_ "github.com/luxfi/precompile/sr25519"
|
|
_ "github.com/luxfi/precompile/zk"
|
|
)
|
|
|
|
// gasEstimator is the optional interface for precompiles that expose RequiredGas.
|
|
type gasEstimator interface {
|
|
RequiredGas(input []byte) uint64
|
|
}
|
|
|
|
// parseAddress decodes a hex address string (with or without 0x prefix) into common.Address.
|
|
func parseAddress(addrPtr *C.char, addrLen C.int) (common.Address, bool) {
|
|
raw := C.GoStringN(addrPtr, addrLen)
|
|
raw = strings.TrimPrefix(raw, "0x")
|
|
raw = strings.TrimPrefix(raw, "0X")
|
|
if len(raw) != 40 {
|
|
return common.Address{}, false
|
|
}
|
|
b, err := hex.DecodeString(raw)
|
|
if err != nil {
|
|
return common.Address{}, false
|
|
}
|
|
return common.BytesToAddress(b), true
|
|
}
|
|
|
|
// lux_precompile_run dispatches a precompile call by address.
|
|
//
|
|
// Returns 0 on success, -1 if address not found, -2 on execution error.
|
|
// On success, output is written to the output buffer and outputLen is set.
|
|
// remainingGas is updated with gas left after execution.
|
|
//
|
|
//export lux_precompile_run
|
|
func lux_precompile_run(
|
|
address *C.char, addrLen C.int,
|
|
input *C.char, inputLen C.int,
|
|
gas C.uint64_t,
|
|
output *C.char, outputLen *C.int,
|
|
remainingGas *C.uint64_t,
|
|
) C.int {
|
|
addr, ok := parseAddress(address, addrLen)
|
|
if !ok {
|
|
return -1
|
|
}
|
|
|
|
mod, found := modules.GetPrecompileModuleByAddress(addr)
|
|
if !found {
|
|
return -1
|
|
}
|
|
|
|
inputBytes := C.GoBytes(unsafe.Pointer(input), inputLen)
|
|
suppliedGas := uint64(gas)
|
|
|
|
// Run with nil AccessibleState + zero caller/addr for stateless precompiles.
|
|
// Precompiles that need state (DEX, AI mining, dead) will panic on nil state;
|
|
// recover and return -3 (needs state) to distinguish from execution errors.
|
|
var (
|
|
ret []byte
|
|
gasLeft uint64
|
|
runErr error
|
|
panicked bool
|
|
)
|
|
func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
panicked = true
|
|
}
|
|
}()
|
|
ret, gasLeft, runErr = mod.Contract.Run(nil, common.Address{}, addr, inputBytes, suppliedGas, true)
|
|
}()
|
|
if panicked {
|
|
return -3 // precompile requires EVM state
|
|
}
|
|
if runErr != nil {
|
|
return -2
|
|
}
|
|
|
|
*outputLen = C.int(len(ret))
|
|
*remainingGas = C.uint64_t(gasLeft)
|
|
if len(ret) > 0 {
|
|
C.memcpy(unsafe.Pointer(output), unsafe.Pointer(&ret[0]), C.size_t(len(ret)))
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// precompileEntry is the JSON structure for lux_precompile_list.
|
|
type precompileEntry struct {
|
|
Address string `json:"address"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// lux_precompile_list writes a JSON array of {address, name} for all registered precompiles.
|
|
//
|
|
// Returns 0 on success, -1 if buffer too small (bufLen is set to required size).
|
|
//
|
|
//export lux_precompile_list
|
|
func lux_precompile_list(buf *C.char, bufLen *C.int) C.int {
|
|
mods := modules.RegisteredModules()
|
|
entries := make([]precompileEntry, len(mods))
|
|
for i, m := range mods {
|
|
entries[i] = precompileEntry{
|
|
Address: m.Address.Hex(),
|
|
Name: m.ConfigKey,
|
|
}
|
|
}
|
|
|
|
data, err := json.Marshal(entries)
|
|
if err != nil {
|
|
return -2
|
|
}
|
|
|
|
needed := C.int(len(data))
|
|
if *bufLen < needed {
|
|
*bufLen = needed
|
|
return -1
|
|
}
|
|
|
|
*bufLen = needed
|
|
C.memcpy(unsafe.Pointer(buf), unsafe.Pointer(&data[0]), C.size_t(len(data)))
|
|
return 0
|
|
}
|
|
|
|
// lux_precompile_required_gas returns the gas cost estimate for a precompile call.
|
|
//
|
|
// Returns the gas cost, or 0 if the precompile is not found or does not implement RequiredGas.
|
|
//
|
|
//export lux_precompile_required_gas
|
|
func lux_precompile_required_gas(
|
|
address *C.char, addrLen C.int,
|
|
input *C.char, inputLen C.int,
|
|
) C.uint64_t {
|
|
addr, ok := parseAddress(address, addrLen)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
|
|
mod, found := modules.GetPrecompileModuleByAddress(addr)
|
|
if !found {
|
|
return 0
|
|
}
|
|
|
|
estimator, ok := mod.Contract.(gasEstimator)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
|
|
inputBytes := C.GoBytes(unsafe.Pointer(input), inputLen)
|
|
return C.uint64_t(estimator.RequiredGas(inputBytes))
|
|
}
|
|
|
|
func main() {}
|