add local benchmark tools (make benchgen, make benchsign)

This commit is contained in:
creamwhip
2020-12-28 19:31:20 +08:00
parent ac984066ea
commit ccd7db8b44
6 changed files with 537 additions and 6 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
build
vendor
benchdata
.DS_Store
# Binaries for programs and plugins

View File

@@ -1,5 +1,9 @@
MODULE = github.com/binance-chain/tss-lib
PACKAGES = $(shell go list ./... | grep -v '/vendor/')
ENTRYPOINT = ./cmd/...
LD_FLAGS = -s -w
BUILD_FLAGS = -trimpath -ldflags "$(LD_FLAGS)"
BUILD_OUT = ./build/
all: protob test
@@ -13,9 +17,6 @@ protob:
protoc --go_out=module=$(MODULE):. ./protob/$$file.proto ; \
done
build: protob
go fmt ./...
########################################
### Format
@@ -25,6 +26,24 @@ fmt:
lint:
@golangci-lint run
########################################
### Build
build: fmt
@echo "--> Building bench tools"
mkdir -p ./build
go build ${BUILD_FLAGS} -o ${BUILD_OUT} ${ENTRYPOINT}
@echo "\n--> Build complete"
########################################
### Benchmarking
benchgen: fmt
go run ./cmd/tss-benchgen benchdata
benchsign: fmt
go run ./cmd/tss-benchsign benchdata
########################################
### Testing
@@ -51,5 +70,4 @@ pre_commit: build test
# To avoid unintended conflicts with file names, always add to .PHONY
# # unless there is a reason not to.
# # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: protob build test_unit test_unit_race test
.PHONY: protob build test_unit test_unit_race test benchgen benchsign

240
cmd/tss-benchgen/main.go Normal file

File diff suppressed because one or more lines are too long

266
cmd/tss-benchsign/main.go Normal file
View File

@@ -0,0 +1,266 @@
package main
import (
"crypto/ecdsa"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"math/big"
"os"
"runtime"
"sync/atomic"
"time"
"github.com/binance-chain/tss-lib/common"
"github.com/binance-chain/tss-lib/ecdsa/keygen"
"github.com/binance-chain/tss-lib/ecdsa/signing"
"github.com/binance-chain/tss-lib/test"
"github.com/binance-chain/tss-lib/tss"
"github.com/btcsuite/btcd/btcec"
"github.com/ipfs/go-log"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
const libLogLevel = "warn"
type result struct {
quorum int
duration time.Duration
}
func usage() {
if _, err := fmt.Fprintf(os.Stderr, "usage: tss-benchsign [-flag=value, ...] datadir\n"); err != nil {
panic(err)
}
flag.PrintDefaults()
os.Exit(2)
}
func main() {
prt := message.NewPrinter(language.English)
var (
startQuorum = flag.Int("q", 3, "the minimum quorum (t+1) to use (default: 6)")
endQuorum = flag.Int("n", 8, "the maximum quorum (t+1) to benchmark up to (min: 2, default: 10)")
runs = flag.Int("r", 3, "the number of benchmarking runs (default: 3)")
procs = flag.Int("procs", runtime.NumCPU(), "the number of max go procs (threads) to use")
)
flag.Usage = usage
if flag.Parse(); !flag.Parsed() {
usage()
os.Exit(1)
}
if *endQuorum <= 1 || *runs < 1 || *endQuorum < *startQuorum {
fmt.Println("Error: q must be greater than 1, r must be greater than 0, endQuorum must be after startQuorum.")
os.Exit(1)
}
if flag.NArg() < 1 {
usage()
os.Exit(1)
}
dir := flag.Args()[0]
if stat, err := os.Stat(dir); os.IsNotExist(err) || stat == nil || !stat.IsDir() {
fmt.Printf("Error: `%s` does not exist; run tss-benchgen to generate shares first.\n", dir)
os.Exit(1)
}
if _, err := os.Stat(makeKeyGenDataFilePath(dir, *endQuorum-1)); os.IsNotExist(err) {
fmt.Printf("Error: insufficient shares for the specified quorum; run tss-benchgen and generate at least %d shares.\n", *endQuorum)
os.Exit(1)
}
fmt.Println("ECDSA/GG20 Benchmark Tool - Signing")
fmt.Println("-----------------------------------")
fmt.Printf("Will test quorums %d-%d in %d runs\n", *startQuorum, *endQuorum, *runs)
fmt.Printf("Max go procs (threads): %d\n", *procs)
fmt.Println("No network latency.")
fmt.Println("-----------------------------------")
runtime.GOMAXPROCS(*procs)
results := make([][]result, 0, *runs)
for run := 0; run < *runs; run++ {
fmt.Printf("Signing run %d... \n", run+1)
results = append(results, make([]result, 0, *endQuorum))
for q := *startQuorum; q <= *endQuorum; q++ {
fmt.Printf(" Quorum %d... ", q)
start := time.Now()
runSign(dir, q-1)
elapsed := time.Since(start)
results[run] = append(results[run], result{
quorum: q,
duration: elapsed,
})
_, _ = prt.Printf("%d ms.\n", elapsed.Milliseconds())
}
}
fmt.Println("Results summary:")
printSummary(results)
os.Exit(0)
}
func setUp(level string) {
if err := log.SetLogLevel("tss-lib", level); err != nil {
panic(err)
}
}
func runSign(dir string, t int) {
setUp(libLogLevel)
q := t + 1
keys, signPIDs, err := loadKeyGenData(dir, q)
if err != nil {
panic(err)
}
if len(keys) != q || len(signPIDs) != q {
panic(fmt.Errorf("wanted %d keys but got %d keys and %d signPIDs", q, len(keys), len(signPIDs)))
}
msg := common.GetRandomPrimeInt(256)
p2pCtx := tss.NewPeerContext(signPIDs)
parties := make([]*signing.LocalParty, 0, len(signPIDs))
errCh := make(chan *tss.Error, len(signPIDs))
outCh := make(chan tss.Message, len(signPIDs))
endCh := make(chan *signing.SignatureData, len(signPIDs))
updater := test.SharedPartyUpdater
// init the parties
for i := 0; i < len(signPIDs); i++ {
params := tss.NewParameters(p2pCtx, signPIDs[i], len(signPIDs), t)
P := signing.NewLocalParty(msg, params, keys[i], outCh, endCh).(*signing.LocalParty)
parties = append(parties, P)
go func(P *signing.LocalParty) {
if err := P.Start(); err != nil {
errCh <- err
}
}(P)
}
var ended int32
outer:
for {
select {
case err := <-errCh:
common.Logger.Errorf("Error: %s", err)
panic(err)
case msg := <-outCh:
dest := msg.GetTo()
if dest == nil {
for _, P := range parties {
if P.PartyID().Index == msg.GetFrom().Index {
continue
}
go updater(P, msg, errCh)
}
} else {
if dest[0].Index == msg.GetFrom().Index {
panic(fmt.Errorf("party %d tried to send a message to itself (%d)", dest[0].Index, msg.GetFrom().Index))
}
go updater(parties[dest[0].Index], msg, errCh)
}
case data := <-endCh:
atomic.AddInt32(&ended, 1)
if atomic.LoadInt32(&ended) == int32(len(signPIDs)) {
// BEGIN ECDSA verify
pkX, pkY := keys[0].ECDSAPub.X(), keys[0].ECDSAPub.Y()
pk := ecdsa.PublicKey{
Curve: tss.EC(),
X: pkX,
Y: pkY,
}
r := new(big.Int).SetBytes(data.Signature.GetR())
s := new(big.Int).SetBytes(data.Signature.GetS())
var ok bool
if ok = ecdsa.Verify(
&pk,
msg.Bytes(),
r, s,
); !ok {
panic("ECDSA signature verification did not pass")
}
btcecSig := &btcec.Signature{R: r, S: s}
if ok = btcecSig.Verify(msg.Bytes(), (*btcec.PublicKey)(&pk)); !ok {
panic("ECDSA signature verification 2 did not pass")
}
break outer
}
}
}
}
func printSummary(results [][]result) {
prt := message.NewPrinter(language.English)
table := tablewriter.NewWriter(os.Stdout)
header := []string{"Quorum"}
for run := range results {
header = append(header, fmt.Sprintf("Run %d", run+1))
}
header = append(header, "Mean")
table.SetHeader(header)
rows := make([][]string, 0, len(results[0]))
for q, result := range results[0] {
row := []string{
prt.Sprintf("%d", result.quorum),
}
var avgDurationMS int64
for run := range results {
durationMS := results[run][q].duration.Milliseconds()
str := prt.Sprintf("%d ms", durationMS)
row = append(row, str)
avgDurationMS += durationMS
}
avgDurationMS /= int64(len(results))
row = append(row, prt.Sprintf("%d ms", avgDurationMS))
rows = append(rows, row)
}
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("|")
table.AppendBulk(rows)
table.Render()
}
// ----- //
func loadKeyGenData(dir string, qty int, optionalStart ...int) ([]keygen.LocalPartySaveData, tss.SortedPartyIDs, error) {
keys := make([]keygen.LocalPartySaveData, 0, qty)
start := 0
if 0 < len(optionalStart) {
start = optionalStart[0]
}
for i := start; i < qty; i++ {
fixtureFilePath := makeKeyGenDataFilePath(dir, i)
bz, err := ioutil.ReadFile(fixtureFilePath)
if err != nil {
return nil, nil, errors.Wrapf(err,
"could not open the test fixture for party %d in the expected location: %s. run keygen tests first.",
i, fixtureFilePath)
}
var key keygen.LocalPartySaveData
if err = json.Unmarshal(bz, &key); err != nil {
return nil, nil, errors.Wrapf(err,
"could not unmarshal fixture data for party %d located at: %s",
i, fixtureFilePath)
}
keys = append(keys, key)
}
partyIDs := make(tss.UnSortedPartyIDs, len(keys))
for i, key := range keys {
pMoniker := fmt.Sprintf("%d", i+start+1)
partyIDs[i] = tss.NewPartyID(pMoniker, pMoniker, key.ShareID)
}
sortedPIDs := tss.SortPartyIDs(partyIDs)
return keys, sortedPIDs, nil
}
func makeKeyGenDataFilePath(dir string, partyIndex int) string {
return fmt.Sprintf("%s/keygen_data_%d.json", dir, partyIndex)
}

3
go.mod
View File

@@ -12,6 +12,7 @@ require (
github.com/ipfs/go-log/v2 v2.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/otiai10/mint v1.3.1 // indirect
github.com/otiai10/primes v0.0.0-20180210170552-f6d2a1ba97c4
github.com/pkg/errors v0.9.1
@@ -19,6 +20,7 @@ require (
go.uber.org/zap v1.15.0 // indirect
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/text v0.3.0
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 // indirect
google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
@@ -27,4 +29,3 @@ require (
)
replace github.com/agl/ed25519 => github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43

5
go.sum
View File

@@ -73,8 +73,12 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -149,6 +153,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=