Skip to content

Commit

Permalink
lazyledger DA client implementation (cosmos#81)
Browse files Browse the repository at this point in the history
* Initial version of lazyledger DA client

Kudos to Evan for unblocking me on gRPC issues.

Co-authored-by: evan-forbes <[email protected]>
  • Loading branch information
tzdybal and evan-forbes authored Jun 22, 2021
1 parent fca8429 commit f436e3f
Show file tree
Hide file tree
Showing 4 changed files with 502 additions and 28 deletions.
168 changes: 168 additions & 0 deletions da/lazyledger/lazyledger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package lazyledger

import (
"context"
"os"
"time"

"github.com/pelletier/go-toml"
"google.golang.org/grpc"

"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
appclient "github.com/lazyledger/lazyledger-app/x/lazyledgerapp/client"
apptypes "github.com/lazyledger/lazyledger-app/x/lazyledgerapp/types"

"github.com/lazyledger/optimint/da"
"github.com/lazyledger/optimint/log"
"github.com/lazyledger/optimint/types"
)

type Config struct {
// PayForMessage related params
NamespaceID []byte
PubKey []byte
BaseRateMax uint64 // currently not used
TipRateMax uint64 // currently not used

// temporary fee fields
GasLimit uint64
FeeAmount uint64

// RPC related params
RPCAddress string
ChainID string
Timeout time.Duration

// keyring related params

// KeyringAccName is the name of the account registered in the keyring
// for the `From` address field
KeyringAccName string
// Backend is the backend of keyring that contains the KeyringAccName
Backend string
RootDir string
}

type LazyLedger struct {
config Config
logger log.Logger

keyring keyring.Keyring

rpcClient *grpc.ClientConn
}

var _ da.DataAvailabilityLayerClient = &LazyLedger{}

// Init is called once to allow DA client to read configuration and initialize resources.
func (ll *LazyLedger) Init(config []byte, logger log.Logger) error {
ll.logger = logger
err := toml.Unmarshal(config, &ll.config)
if err != nil {
return err
}

// TODO(tzdybal): this means interactive reading from stdin - shouldn't we replace this somehow?
userInput := os.Stdin
ll.keyring, err = keyring.New(ll.config.KeyringAccName, ll.config.Backend, ll.config.RootDir, userInput)
return err
}

func (ll *LazyLedger) Start() (err error) {
ll.rpcClient, err = grpc.Dial(ll.config.RPCAddress, grpc.WithInsecure())
return
}

func (ll *LazyLedger) Stop() error {
return ll.rpcClient.Close()
}

// SubmitBlock submits the passed in block to the DA layer.
// This should create a transaction which (potentially)
// triggers a state transition in the DA layer.
func (ll *LazyLedger) SubmitBlock(block *types.Block) da.ResultSubmitBlock {
msg, err := ll.preparePayForMessage(block)
if err != nil {
return da.ResultSubmitBlock{Code: da.StatusError, Message: err.Error()}
}

ctx, cancel := context.WithTimeout(context.TODO(), ll.config.Timeout)
defer cancel()

err = ll.callRPC(ctx, msg)
if err != nil {
return da.ResultSubmitBlock{Code: da.StatusError, Message: err.Error()}
}

return da.ResultSubmitBlock{Code: da.StatusSuccess}
}

func (ll *LazyLedger) callRPC(ctx context.Context, msg *apptypes.MsgWirePayForMessage) error {
signer := appclient.NewKeyringSigner(ll.keyring, ll.config.KeyringAccName, ll.config.ChainID)

err := signer.QueryAccountNumber(ctx, ll.rpcClient)
if err != nil {
return err
}

coin := sdk.Coin{
Denom: "token",
Amount: sdk.NewInt(10), // TODO(tzdybal): un-hardcode
}

builder := signer.NewTxBuilder()
builder.SetFeeAmount(sdk.NewCoins(coin))
builder.SetGasLimit(ll.config.GasLimit)

signedTx, err := signer.BuildSignedTx(builder, msg)
if err != nil {
return err
}

rawTx, err := signer.EncodeTx(signedTx)
if err != nil {
return err
}

// wait for the transaction to be confirmed in a block
_, err = appclient.BroadcastTx(ctx, ll.rpcClient, tx.BroadcastMode_BROADCAST_MODE_BLOCK, rawTx)
if err != nil {
return err
}

return nil
}

func (ll *LazyLedger) preparePayForMessage(block *types.Block) (*apptypes.MsgWirePayForMessage, error) {
// TODO(tzdybal): serialize block
var message []byte
message, err := block.MarshalBinary()
if err != nil {
return nil, err
}

// create PayForMessage message
msg, err := apptypes.NewMsgWirePayForMessage(
ll.config.NamespaceID,
message,
ll.config.PubKey,
&apptypes.TransactionFee{
BaseRateMax: ll.config.BaseRateMax,
TipRateMax: ll.config.TipRateMax,
},
apptypes.SquareSize,
)
if err != nil {
return nil, err
}

// sign the PayForMessage's ShareCommitments
err = msg.SignShareCommitments(ll.config.KeyringAccName, ll.keyring)
if err != nil {
return nil, err
}

return msg, msg.ValidateBasic()
}
114 changes: 114 additions & 0 deletions da/lazyledger/lazyledger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package lazyledger

import (
"errors"
"fmt"
"strconv"
"testing"

"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/lazyledger/optimint/da"
"github.com/lazyledger/optimint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestConfiguration(t *testing.T) {
t.Parallel()
cases := []struct {
name string
input []byte
err error
expected Config
}{
{"empty config", []byte(""), errors.New("unknown keyring backend "), Config{}},
{"with namespace id", []byte("NamespaceID = [3, 2, 1]\nBackend = 'test'"), nil, Config{NamespaceID: []byte{0x03, 0x02, 0x01}, Backend: "test"}},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert := assert.New(t)
ll := &LazyLedger{}
err := ll.Init(c.input, nil)

if c.err != nil {
assert.EqualError(err, c.err.Error())
} else {
assert.NoError(err)
assert.Equal(c.expected, ll.config)
}

})
}
}

func TestSubmission(t *testing.T) {
t.Skip("this test requires configured and running lazyledger-appd")
assert := assert.New(t)
require := require.New(t)
block := &types.Block{Header: types.Header{
Height: 1,
}}

ll := &LazyLedger{}
kr := generateKeyring(t)
key, err := kr.Key("test-account")
require.NoError(err)
conf := testConfig(key)
err = ll.Init([]byte(conf), nil)
require.NoError(err)
ll.keyring = kr

err = ll.Start()
require.NoError(err)

result := ll.SubmitBlock(block)
assert.Equal("", result.Message)
assert.Equal(da.StatusSuccess, result.Code)
}

// nolint: unused
func testConfig(key keyring.Info) string {
keyStr := ""
for _, b := range key.GetPubKey().Bytes() {
keyStr += strconv.Itoa(int(b)) + ", "
}
conf := fmt.Sprintf(`PubKey=[%s]
Backend = 'test'
From = '%s'
KeyringAccName = 'test-account'
RPCAddress = '127.0.0.1:9090'
NamespaceID = [3, 2, 1, 0, 3, 2, 1, 0]
GasLimit = 100000
FeeAmount = 1
ChainID = 'test'
`, keyStr, key.GetAddress().String())
return conf
}

// nolint: unused
func generateKeyring(t *testing.T, accts ...string) keyring.Keyring {
t.Helper()
kb := keyring.NewInMemory()

for _, acc := range accts {
_, _, err := kb.NewMnemonic(acc, keyring.English, "", hd.Secp256k1)
if err != nil {
t.Error(err)
}
}

_, err := kb.NewAccount(testAccName, testMnemo, "1234", "", hd.Secp256k1)
if err != nil {
panic(err)
}

return kb
}

const (
testMnemo = `ramp soldier connect gadget domain mutual staff unusual first midnight iron good deputy wage vehicle mutual spike unlock rocket delay hundred script tumble choose`
testAccName = "test-account"
)
12 changes: 11 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@ module github.com/lazyledger/optimint
go 1.15

require (
github.com/cosmos/cosmos-sdk v0.40.0-rc5
github.com/dgraph-io/badger/v3 v3.2011.1
github.com/go-kit/kit v0.10.0
github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.4.3
github.com/ipfs/go-log v1.0.4
github.com/lazyledger/lazyledger-app v0.0.0-20210615142030-0e60861ff7f5
github.com/lazyledger/lazyledger-core v0.0.0-20210219190522-0eccfb24e2aa
github.com/libp2p/go-libp2p v0.13.0
github.com/libp2p/go-libp2p-core v0.8.5
github.com/libp2p/go-libp2p-discovery v0.5.0
github.com/libp2p/go-libp2p-kad-dht v0.11.1
github.com/libp2p/go-libp2p-pubsub v0.4.1
github.com/minio/sha256-simd v0.1.1
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/multiformats/go-multiaddr v0.3.1
github.com/pelletier/go-toml v1.9.0
github.com/prometheus/client_golang v1.8.0
github.com/stretchr/testify v1.7.0
go.uber.org/multierr v1.6.0
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect
google.golang.org/grpc v1.35.0
)

replace (
github.com/cosmos/cosmos-sdk v0.40.0-rc5 => github.com/lazyledger/cosmos-sdk v0.40.0-rc5.0.20210121152417-3addd7f65d1c
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.4
)
Loading

0 comments on commit f436e3f

Please sign in to comment.