Skip to content

Commit

Permalink
Merge pull request ethereum#260 from nextyio/vdf
Browse files Browse the repository at this point in the history
VDF engine for both cli and golib support
  • Loading branch information
Zergity authored Aug 6, 2019
2 parents 96c426f + 0f4c2e2 commit 6b95ee6
Show file tree
Hide file tree
Showing 18 changed files with 1,378 additions and 3 deletions.
133 changes: 133 additions & 0 deletions core/vdf/engine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2019 The gonex Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package vdf

import (
"fmt"
"os/exec"
"strconv"
"strings"
"sync"

"github.com/ethereum/go-ethereum/common"

"github.com/ethereum/go-ethereum/log"
"github.com/harmony-one/vdf/src/vdf_go"
)

var (
engine *Engine
engineOnce sync.Once
)

// Engine defines the structure of the engine
type Engine struct {
cli string
}

// Instance returns the singleton instance of the VDF Engine
func Instance() *Engine {
engineOnce.Do(func() {
engine = newEngine()
})
return engine
}

func newEngine() *Engine {
cli, err := exec.LookPath("vdf-cli")
if err != nil {
log.Warn("vdf.newEngine", "vdf-cli", "not found")
}
return &Engine{cli}
}

// Verify verifies the generated output against the seed
func (e *Engine) Verify(seed, output []byte, iteration uint64, bitSize uint64) (valid bool) {
defer func() {
if x := recover(); x != nil {
log.Error("vdf.Verify: verification process panic", "reason", x)
valid = false
}
}()
return vdf_go.VerifyVDF(seed, output, int(iteration), int(bitSize))
}

// Generate generates the vdf output = (y, proof)
func (e *Engine) Generate(seed []byte, iteration uint64, bitSize uint64, stop <-chan struct{}) (output []byte, err error) {
if len(e.cli) == 0 {
defer func() {
if x := recover(); x != nil {
log.Error("vdf.Generate: generation process panic", "reason", x)
err = fmt.Errorf("%v", x)
}
}()
y, proof := vdf_go.GenerateVDFWithStopChan(seed, int(iteration), int(bitSize), stop)
if y == nil || proof == nil {
return nil, nil
}
return append(y, proof...), nil
}

cmd := exec.Command(e.cli,
"-l"+strconv.Itoa(int(bitSize)),
common.Bytes2Hex(seed),
strconv.Itoa(int(iteration)))

log.Trace(e.cli + " -l" + strconv.Itoa(int(bitSize)) + " " + common.Bytes2Hex(seed) + " " + strconv.Itoa(int(iteration)))

var done chan struct{}
if stop != nil {
go func() {
select {
case <-stop:
if cmd == nil || cmd.Process == nil {
return
}
log.Trace("vdf.Generate: vdf-cli interrupted")
if err := cmd.Process.Kill(); err != nil {
log.Error("vdf.Generate: failed to kill vdf-cli process", "err", err)
}
return
case <-done:
return
}
}()
}

log.Trace("vdf.Generate", "seed", common.Bytes2Hex(seed), "iteration", iteration, "output", common.Bytes2Hex(output))
output, err = cmd.Output()
if err, ok := err.(*exec.ExitError); ok {
// verification failed
log.Trace("vdf.Generate", "error code", err.Error())
return nil, err
}
if err != nil {
// sum ting wong
log.Error("vdf.Generate", "error", err)
return nil, err
}

if stop != nil && done != nil {
// prevent goroutine leakage
done <- struct{}{}
}

strOutput := strings.TrimSpace(string(output))

log.Trace("vdf.Generate", "output", strOutput)
return common.Hex2Bytes(strOutput), nil
}
63 changes: 63 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vdf"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)
Expand Down Expand Up @@ -59,6 +61,20 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{8}): &bn256Pairing{},
}

// PrecompiledContractsCoLoa contains the default set of pre-compiled Ethereum
// contracts used in the CoLoa release.
var PrecompiledContractsCoLoa = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x01}): &ecrecover{},
common.BytesToAddress([]byte{0x02}): &sha256hash{},
common.BytesToAddress([]byte{0x03}): &ripemd160hash{},
common.BytesToAddress([]byte{0x04}): &dataCopy{},
common.BytesToAddress([]byte{0x05}): &bigModExp{},
common.BytesToAddress([]byte{0x06}): &bn256Add{},
common.BytesToAddress([]byte{0x07}): &bn256ScalarMul{},
common.BytesToAddress([]byte{0x08}): &bn256Pairing{},
common.BytesToAddress([]byte{0xFF}): &vdfVerify{},
}

// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input)
Expand Down Expand Up @@ -320,6 +336,15 @@ var (

// errBadPairingInput is returned if the bn256 pairing input is invalid.
errBadPairingInput = errors.New("bad elliptic curve pairing size")

// errBadVDFInputLen is returned if the vdf verification input length is invalid.
errBadVDFInputLen = errors.New("bad VDF verification input length")

// errBadVDFInput is returned if the vdf verification input is invalid.
errBadVDFInput = errors.New("bad VDF verification input")

// errVDFVerificationFailed is returned if there is something wrong with vdf verification process.
errVDFVerificationFailed = errors.New("VDF verification internal error")
)

// bn256Pairing implements a pairing pre-compile for the bn256 curve
Expand Down Expand Up @@ -358,3 +383,41 @@ func (c *bn256Pairing) Run(input []byte) ([]byte, error) {
}
return false32Byte, nil
}

// vdfVerify implements a VDF verification using either wesolowski or pietrzak construction
type vdfVerify struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *vdfVerify) RequiredGas(input []byte) uint64 {
return params.VDFVerifyBaseGas // TODO +++
}

func (c *vdfVerify) Run(input []byte) (valid []byte, err error) {
log.Trace("VDFVerify", "input", common.ToHex(input))

// Convert the input into a vdf params
bitSize := new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
outputLen := (bitSize + 16) >> 2
// Handle some corner cases cheaply
if uint64(len(input)) != 32*3+outputLen {
log.Error("VDFVerify", "error", errBadVDFInputLen, "input len", len(input), "expected", 32*3+outputLen)
return nil, errBadVDFInputLen
}

iteration := new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
seed := getData(input, 64, 32)
output := getData(input, 96, outputLen)

log.Trace("VDFVerify",
"bitSize", bitSize,
"iteration", iteration,
"seed", common.ToHex(seed),
"output", common.ToHex(output))

ok := vdf.Instance().Verify(seed, output, iteration, bitSize)
log.Trace("VDFVerify", "valid", ok)
if ok {
return true32Byte, nil
}
return false32Byte, nil
}
8 changes: 6 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ type (
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
if evm.ChainConfig().IsCoLoa(evm.BlockNumber) {
precompiles = PrecompiledContractsCoLoa
} else if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
precompiles = PrecompiledContractsByzantium
}
if p := precompiles[*contract.CodeAddr]; p != nil {
Expand Down Expand Up @@ -203,7 +205,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
)
if !evm.StateDB.Exist(addr) {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
if evm.ChainConfig().IsCoLoa(evm.BlockNumber) {
precompiles = PrecompiledContractsCoLoa
} else if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
precompiles = PrecompiledContractsByzantium
}
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
Expand Down
2 changes: 1 addition & 1 deletion eth/tracers/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func New(code string) (*Tracer, error) {
return 1
})
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))]
_, ok := vm.PrecompiledContractsCoLoa[common.BytesToAddress(popSlice(ctx))]
ctx.PushBoolean(ok)
return 1
})
Expand Down
2 changes: 2 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const (
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check
VDFVerifyBaseGas uint64 = 130000 // Base price for a VDF verification
VDFVerifyPerBitGas uint64 = 15000 // Per-bit integer size for a VDF verification
)

var (
Expand Down
16 changes: 16 additions & 0 deletions vendor/github.com/harmony-one/vdf/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions vendor/github.com/harmony-one/vdf/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions vendor/github.com/harmony-one/vdf/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions vendor/github.com/harmony-one/vdf/go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions vendor/github.com/harmony-one/vdf/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6b95ee6

Please sign in to comment.