Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add basic common code used in sdk and cometbft repos #2

Merged
merged 13 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Build and Test
on:
pull_request:
merge_group:
push:
branches:
- main
- release/**
permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}-build
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22.2"
- name: Build
run: make build

test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22.2"
- name: Test
run: make test
21 changes: 21 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Lint
on:
pull_request:
push:
branches:
- main
- release/**

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22.2"
- name: Run Linter
run: make lint
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
35 changes: 35 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Variables
PKG := ./...
GOFILES := $(shell find . -name '*.go' | grep -v _test.go)
TESTFILES := $(shell find . -name '*_test.go')
GOLANGCI_VERSION := v1.59.0

all: build

build:
@echo "Building..."
@go build $(PKG)

# Run tests
test:
@echo "Running tests..."
@go test -v $(PKG)

# Install golangci-lint
lint-install:
@echo "--> Installing golangci-lint $(GOLANGCI_VERSION)"
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_VERSION)

# Run golangci-lint
lint:
@echo "--> Running linter"
$(MAKE) lint-install
@golangci-lint run --timeout=15m

# Run golangci-lint and fix
lint-fix:
@echo "--> Running linter with fix"
$(MAKE) lint-install
@golangci-lint run --fix

.PHONY: build test lint-install lint lint-fix
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# crypto

cosmos-crypto is the cryptographic package adapted for the interchain stack

## Importing it

To get the interfaces,
`import "github.com/cosmos/crypto/types"`

52 changes: 52 additions & 0 deletions armor/armor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package armor

import (
"bytes"
"fmt"
"io"

"golang.org/x/crypto/openpgp/armor" //nolint: staticcheck
)

// ErrEncode represents an error from calling [EncodeArmor].
type ErrEncode struct {
Err error
}

func (e ErrEncode) Error() string {
return fmt.Sprintf("armor: could not encode ASCII armor: %v", e.Err)
}

func (e ErrEncode) Unwrap() error {
return e.Err
}

func EncodeArmor(blockType string, headers map[string]string, data []byte) (string, error) {
buf := new(bytes.Buffer)
w, err := armor.Encode(buf, blockType, headers)
if err != nil {
return "", ErrEncode{Err: err}
}
_, err = w.Write(data)
if err != nil {
return "", ErrEncode{Err: err}
}
err = w.Close()
if err != nil {
return "", ErrEncode{Err: err}
}
return buf.String(), nil
}

func DecodeArmor(armorStr string) (blockType string, headers map[string]string, data []byte, err error) {
buf := bytes.NewBufferString(armorStr)
block, err := armor.Decode(buf)
if err != nil {
return "", nil, nil, err
}
data, err = io.ReadAll(block.Body)
if err != nil {
return "", nil, nil, err
}
return block.Type, block.Header, data, nil
}
21 changes: 21 additions & 0 deletions armor/armor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package armor

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestArmor(t *testing.T) {
blockType := "MINT TEST"
data := []byte("somedata")
armorStr, err := EncodeArmor(blockType, nil, data)
require.NoError(t, err, "%+v", err)

// Decode armorStr and test for equivalence.
blockType2, _, data2, err := DecodeArmor(armorStr)
require.NoError(t, err, "%+v", err)
assert.Equal(t, blockType, blockType2)
assert.Equal(t, data, data2)
}
9 changes: 9 additions & 0 deletions curves/bls12381/alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && bls12381

package blst

import blst "github.com/supranational/blst/bindings/go"

// Internal types for blst.
type blstPublicKey = blst.P1Affine
type blstSignature = blst.P2Affine
6 changes: 6 additions & 0 deletions curves/bls12381/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Package blst implements a go-wrapper around a library implementing the
// BLS12-381 curve and signature scheme. This package exposes a public API for
// verifying and aggregating BLS signatures used by Ethereum.
//
// This implementation uses the library written by Supranational, blst.
package blst
15 changes: 15 additions & 0 deletions curves/bls12381/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && bls12381

package blst

// Note: These functions are for tests to access private globals, such as pubkeyCache.

// DisableCaches sets the cache sizes to 0.
func DisableCaches() {
pubkeyCache.Resize(0)
}

// EnableCaches sets the cache sizes to the default values.
func EnableCaches() {
pubkeyCache.Resize(maxKeys)
}
27 changes: 27 additions & 0 deletions curves/bls12381/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && bls12381

package blst

import (
"fmt"
"runtime"

blst "github.com/supranational/blst/bindings/go"

"github.com/cosmos/crypto/internal/cache"
)

func init() {
// Reserve 1 core for general application work
maxProcs := runtime.GOMAXPROCS(0) - 1
if maxProcs <= 0 {
maxProcs = 1
}
blst.SetMaxProcs(maxProcs)
onEvict := func(_ [48]byte, _ PubKey) {}
keysCache, err := cache.NewLRU(maxKeys, onEvict)
if err != nil {
panic(fmt.Sprintf("Could not initiate public keys cache: %v", err))
}
pubkeyCache = keysCache
}
21 changes: 21 additions & 0 deletions curves/bls12381/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package blst

type PubKey interface {
Marshal() []byte
Copy() PubKey
Equals(p2 PubKey) bool
}

// SignatureI represents a BLS signature.
type SignatureI interface {
Verify(pubKey PubKey, msg []byte) bool
Marshal() []byte
Copy() SignatureI
}

// SecretKey represents a BLS secret or private key.
type SecretKey interface {
PublicKey() PubKey
Sign(msg []byte) SignatureI
Marshal() []byte
}
76 changes: 76 additions & 0 deletions curves/bls12381/pubkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && bls12381

package blst

import (
"errors"
"fmt"

"github.com/cosmos/crypto/internal/cache"
)

const (
SignatureLength = 96
PubkeyLength = 48 // PubkeyLength defines the byte length of a BLSSignature.
)

var maxKeys = 2_000_000
var pubkeyCache *cache.LRU[[48]byte, PubKey]

// PublicKey used in the BLS signature scheme.
type PublicKey struct {
p *blstPublicKey
}

// Marshal a public key into a LittleEndian byte slice.
func (p *PublicKey) Marshal() []byte {
return p.p.Compress()
}

// Copy the public key to a new pointer reference.
func (p *PublicKey) Copy() PubKey {
np := *p.p
return &PublicKey{p: &np}
}

// Equals checks if the provided public key is equal to
// the current one.
func (p *PublicKey) Equals(p2 PubKey) bool {
return p.p.Equals(p2.(*PublicKey).p)
}

// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
func PublicKeyFromBytes(pubKey []byte) (PubKey, error) {
return publicKeyFromBytes(pubKey, true)
}

func publicKeyFromBytes(pubKey []byte, cacheCopy bool) (PubKey, error) {
if len(pubKey) != PubkeyLength { //TODO: make this a parameter
return nil, fmt.Errorf("public key must be %d bytes", PubkeyLength)
}

newKey := (*[PubkeyLength]byte)(pubKey)
if cv, ok := pubkeyCache.Get(*newKey); ok {
if cacheCopy {
return cv.Copy(), nil
}
return cv, nil
}

// Subgroup check NOT done when decompressing pubkey.
p := new(blstPublicKey).Uncompress(pubKey)
if p == nil {
return nil, errors.New("could not unmarshal bytes into public key")
}
// Subgroup and infinity check
if !p.KeyValidate() {
// NOTE: the error is not quite accurate since it includes group check
return nil, errors.New("publickey is infinite")
}

pubKeyObj := &PublicKey{p: p}
copiedKey := pubKeyObj.Copy()
cacheKey := *newKey
pubkeyCache.Add(cacheKey, copiedKey)
return pubKeyObj, nil
}
Loading