Skip to content

Commit

Permalink
feat: add basic common code used in sdk and cometbft repos (#2)
Browse files Browse the repository at this point in the history
* Add files from cometBFT repo

* Rename hash package

* wip

* Clean up curves (for now)

* Add bls curve and cache util

* Add github action

* Fix action

* Fix tests

* Fix ci

* Simpler ci

* Fix lint

* Add lint jobs

* Add lint ci
  • Loading branch information
raynaudoe authored Jun 26, 2024
1 parent 9141911 commit 149f443
Show file tree
Hide file tree
Showing 49 changed files with 3,412 additions and 0 deletions.
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

0 comments on commit 149f443

Please sign in to comment.