mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
* v2.1.0 [omit consensus and adjacent] - this commit will be amended with the full release after the file copy is complete * 2.1.0 main node rollup
335 lines
7.4 KiB
Go
335 lines
7.4 KiB
Go
//
|
|
// Copyright (c) 2019-2023 Markku Rossi
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
|
|
package compiler
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/circuit"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/ast"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/utils"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/ot"
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/p2p"
|
|
)
|
|
|
|
// Compiler implements QCL compiler.
|
|
type Compiler struct {
|
|
params *utils.Params
|
|
packages map[string]*ast.Package
|
|
pkgPath string
|
|
}
|
|
|
|
type pkgPath struct {
|
|
precond string
|
|
env string
|
|
prefix string
|
|
}
|
|
|
|
// New creates a new compiler instance.
|
|
func New(params *utils.Params) *Compiler {
|
|
return &Compiler{
|
|
params: params,
|
|
packages: make(map[string]*ast.Package),
|
|
}
|
|
}
|
|
|
|
// Compile compiles the input program.
|
|
func (c *Compiler) Compile(data string, inputSizes [][]int) (
|
|
*circuit.Circuit, ast.Annotations, error) {
|
|
return c.compile("{data}", strings.NewReader(data), inputSizes)
|
|
}
|
|
|
|
// CompileFile compiles the input file.
|
|
func (c *Compiler) CompileFile(file string, inputSizes [][]int) (
|
|
*circuit.Circuit, ast.Annotations, error) {
|
|
|
|
f, err := os.Open(file)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer f.Close()
|
|
return c.compile(file, f, inputSizes)
|
|
}
|
|
|
|
// ParseFile parses the input file.
|
|
func (c *Compiler) ParseFile(file string) (*ast.Package, error) {
|
|
f, err := os.Open(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
logger := utils.NewLogger(os.Stdout)
|
|
return c.parse(file, f, logger, nil)
|
|
}
|
|
|
|
func (c *Compiler) Parse(data string) (*ast.Package, error) {
|
|
logger := utils.NewLogger(io.Discard)
|
|
return c.parse("{data}", strings.NewReader(data), logger, ast.NewPackage("main", "{data}", nil))
|
|
}
|
|
|
|
func (c *Compiler) compile(source string, in io.Reader, inputSizes [][]int) (
|
|
*circuit.Circuit, ast.Annotations, error) {
|
|
|
|
logger := utils.NewLogger(os.Stdout)
|
|
pkg, err := c.parse(source, in, logger, ast.NewPackage("main", source, nil))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
ctx := ast.NewCodegen(logger, pkg, c.packages, c.params, inputSizes)
|
|
|
|
program, annotation, err := pkg.Compile(ctx)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if c.params.NoCircCompile {
|
|
return nil, annotation, nil
|
|
}
|
|
circ, err := program.CompileCircuit(c.params)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return circ, annotation, nil
|
|
}
|
|
|
|
// StreamFile compiles the input program and uses the streaming mode
|
|
// to garble and stream the circuit to the evaluator node.
|
|
func (c *Compiler) StreamFile(conn *p2p.Conn, oti ot.OT, file string,
|
|
input []string, inputSizes [][]int) (circuit.IO, []*big.Int, error) {
|
|
|
|
f, err := os.Open(file)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer f.Close()
|
|
return c.stream(conn, oti, file, f, input, inputSizes)
|
|
}
|
|
|
|
func (c *Compiler) stream(conn *p2p.Conn, oti ot.OT, source string,
|
|
in io.Reader, inputFlag []string, inputSizes [][]int) (
|
|
circuit.IO, []*big.Int, error) {
|
|
|
|
timing := circuit.NewTiming()
|
|
|
|
logger := utils.NewLogger(os.Stdout)
|
|
pkg, err := c.parse(source, in, logger, ast.NewPackage("main", source, nil))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
ctx := ast.NewCodegen(logger, pkg, c.packages, c.params, inputSizes)
|
|
|
|
program, _, err := pkg.Compile(ctx)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
timing.Sample("Compile", nil)
|
|
|
|
if c.params.BenchmarkCompile {
|
|
fmt.Println("BenchmarkCompile")
|
|
return nil, nil, nil
|
|
}
|
|
|
|
if len(program.Inputs) != 2 {
|
|
return nil, nil,
|
|
fmt.Errorf("invalid program for 2-party computation: %d parties",
|
|
len(program.Inputs))
|
|
}
|
|
input, err := program.Inputs[0].Parse(inputFlag)
|
|
if err != nil {
|
|
main, err2 := pkg.Main()
|
|
if err2 != nil {
|
|
return nil, nil, err
|
|
}
|
|
return nil, nil, ctx.Errorf(main.Location(), "%s: %v", main.Name, err)
|
|
}
|
|
|
|
fmt.Printf(" + In1: %s\n", program.Inputs[0])
|
|
fmt.Printf(" - In2: %s\n", program.Inputs[1])
|
|
fmt.Printf(" - Out: %s\n", program.Outputs)
|
|
fmt.Printf(" - In: %s\n", inputFlag)
|
|
|
|
out, bits, err := program.Stream(conn, oti, c.params, input, timing)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return nil, nil, err
|
|
}
|
|
// if false {
|
|
program.StreamDebug()
|
|
// }
|
|
return out, bits, err
|
|
}
|
|
|
|
func (c *Compiler) parse(source string, in io.Reader, logger *utils.Logger,
|
|
pkg *ast.Package) (*ast.Package, error) {
|
|
|
|
parser := NewParser(source, c, logger, in)
|
|
pkg, err := parser.Parse(pkg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.packages[pkg.Name] = pkg
|
|
|
|
for alias, name := range pkg.Imports {
|
|
_, err := c.parsePkg(alias, name, source)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return pkg, nil
|
|
}
|
|
|
|
func (c *Compiler) resolvePkgPath() error {
|
|
if len(c.pkgPath) != 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, pkgPath := range pkgPaths {
|
|
// Check path precondition.
|
|
if len(pkgPath.precond) > 0 {
|
|
_, ok := os.LookupEnv(pkgPath.precond)
|
|
if !ok {
|
|
continue
|
|
}
|
|
}
|
|
dir := path.Join(os.Getenv(pkgPath.env), pkgPath.prefix)
|
|
df, err := os.Open(dir)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
defer df.Close()
|
|
fi, err := df.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !fi.IsDir() {
|
|
return fmt.Errorf("pkg root is not directory: %s", dir)
|
|
}
|
|
c.pkgPath = dir
|
|
break
|
|
}
|
|
if len(c.pkgPath) == 0 {
|
|
fmt.Printf("could not resolve pkg root directory, tried:\n")
|
|
for _, pkgPath := range pkgPaths {
|
|
if len(pkgPath.precond) > 0 {
|
|
fmt.Printf(" - $(%s):\n", pkgPath.precond)
|
|
} else {
|
|
fmt.Printf(" - *:\n")
|
|
}
|
|
fmt.Printf(" - $(%s)/%s\n", pkgPath.env, pkgPath.prefix)
|
|
}
|
|
return fmt.Errorf("could not find pkg root directory")
|
|
}
|
|
if c.params.Verbose {
|
|
fmt.Printf("found PkgRoot from '%s'\n", c.pkgPath)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var pkgPaths = []*pkgPath{
|
|
{
|
|
precond: "QCLDIR",
|
|
env: "QCLDIR",
|
|
prefix: "pkg",
|
|
},
|
|
{
|
|
precond: "GITHUB_WORKFLOW",
|
|
env: "GITHUB_WORKSPACE",
|
|
prefix: "pkg",
|
|
},
|
|
{
|
|
env: "HOME",
|
|
prefix: "go/src/source.quilibrium.com/quilibrium/monorepo/bedlam/pkg",
|
|
},
|
|
}
|
|
|
|
func (c *Compiler) parsePkg(alias, name, source string) (*ast.Package, error) {
|
|
pkg, ok := c.packages[alias]
|
|
if ok {
|
|
return pkg, nil
|
|
}
|
|
pkg = ast.NewPackage(alias, source, nil)
|
|
|
|
err := c.resolvePkgPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if c.params.Verbose {
|
|
fmt.Printf("looking for package %s (%s)\n", alias, name)
|
|
}
|
|
|
|
var dirs []string
|
|
dirs = append(dirs, c.pkgPath)
|
|
dirs = append(dirs, c.params.PkgPath...)
|
|
|
|
for _, dir := range dirs {
|
|
pkg, ok, err := c.tryParsePkg(pkg, dir, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return pkg, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("package %s not found", name)
|
|
}
|
|
|
|
func (c *Compiler) tryParsePkg(pkg *ast.Package, prefix, name string) (
|
|
*ast.Package, bool, error) {
|
|
|
|
dir := path.Join(prefix, name)
|
|
df, err := os.Open(dir)
|
|
if err != nil {
|
|
return nil, false, nil
|
|
}
|
|
defer df.Close()
|
|
|
|
files, err := df.Readdirnames(-1)
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("package %s not found: %s", name, err)
|
|
}
|
|
var qcls []string
|
|
for _, f := range files {
|
|
if strings.HasSuffix(f, ".qcl") {
|
|
qcls = append(qcls, f)
|
|
}
|
|
}
|
|
if len(qcls) == 0 {
|
|
return nil, false, fmt.Errorf("package %s is empty", name)
|
|
}
|
|
|
|
for _, qcl := range qcls {
|
|
fp := path.Join(dir, qcl)
|
|
|
|
if c.params.Verbose {
|
|
fmt.Printf(" - parsing @%v\n", fp[len(c.pkgPath):])
|
|
}
|
|
|
|
f, err := os.Open(fp)
|
|
if err != nil {
|
|
fmt.Printf("pkg not found: %s\n", err)
|
|
return nil, false, fmt.Errorf("error reading package %s: %s",
|
|
name, err)
|
|
}
|
|
defer f.Close()
|
|
|
|
pkg, err = c.parse(fp, f, utils.NewLogger(os.Stdout), pkg)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
}
|
|
return pkg, true, nil
|
|
}
|