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

deposit: marshal array of deposits #557

Merged
merged 17 commits into from
May 19, 2022
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
23 changes: 13 additions & 10 deletions cmd/createcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"strings"
"text/template"

eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/enode"
Expand Down Expand Up @@ -142,7 +143,7 @@ func bindClusterFlags(flags *pflag.FlagSet, config *clusterConfig) {
flags.IntVar(&config.ConfigPortStart, "config-port-start", 16000, "Starting port number used in config files. Requires --config.")
}

func runCreateCluster(w io.Writer, conf clusterConfig) error {
func runCreateCluster(w io.Writer, conf clusterConfig) error { //nolint:gocognit
if conf.Clean {
// Remove previous directories
if err := os.RemoveAll(conf.ClusterDir); err != nil {
Expand Down Expand Up @@ -210,7 +211,11 @@ func runCreateCluster(w io.Writer, conf clusterConfig) error {
}
}

var depositDatas [][]byte
// TODO(xenowits): add flag to specify the number of distributed validators in a cluster
// Currently, we assume that we create a cluster of ONLY 1 Distributed Validator
var pubkeys []eth2p0.BLSPubKey
var msgSigs []eth2p0.BLSSignature

for i := 0; i < numDVs; i++ {
sk := secrets[i] // Secret key for this DV
pk, err := sk.GetPublicKey()
Expand Down Expand Up @@ -244,15 +249,12 @@ func runCreateCluster(w io.Writer, conf clusterConfig) error {
}

sigEth2 := tblsconv.SigToETH2(sig)
bytes, err := deposit.MarshalDepositData(pubkey, withdrawalAddr, conf.Network, sigEth2)
if err != nil {
return err
}

depositDatas = append(depositDatas, bytes)
pubkeys = append(pubkeys, pubkey)
msgSigs = append(msgSigs, sigEth2)
}

if err := writeDepositData(conf, depositDatas); err != nil {
if err := writeDepositData(conf, pubkeys, msgSigs, conf.WithdrawalAddr, conf.Network); err != nil {
return err
}

Expand Down Expand Up @@ -344,10 +346,11 @@ func getKeys(conf clusterConfig, numDVs int) ([]*bls_sig.SecretKey, error) {
}

// writeDepositData writes deposit data to disk for the DVs in a cluster.
func writeDepositData(config clusterConfig, b [][]byte) error {
func writeDepositData(config clusterConfig, pubkeys []eth2p0.BLSPubKey, msgSigs []eth2p0.BLSSignature, withdrawalAddr, network string) error {
depositPath := path.Join(config.ClusterDir, "deposit-data.json")

bytes, err := deposit.MarshalDepositDatas(b)
// serialize the deposit data into bytes
bytes, err := deposit.MarshalDepositData(pubkeys, msgSigs, withdrawalAddr, network)
if err != nil {
return err
}
Expand Down
80 changes: 34 additions & 46 deletions eth2util/deposit/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ var (

// DOMAIN_DEPOSIT. See spec: https://benjaminion.xyz/eth2-annotated-spec/phase0/beacon-chain/#domain-types
depositDomainType = eth2p0.DomainType([4]byte{0x03, 0x00, 0x00, 0x00})

depositCliVersion = "2.1.0"
)

// getMessageRoot returns a deposit message hash root created by the parameters.
Expand All @@ -60,62 +62,47 @@ func getMessageRoot(pubkey eth2p0.BLSPubKey, withdrawalAddr string) (eth2p0.Root
return hashRoot, nil
}

// MarshalDepositData returns a json serialized deposit data.
func MarshalDepositData(pubkey eth2p0.BLSPubKey, withdrawalAddr string, network string, msgSig eth2p0.BLSSignature) ([]byte, error) {
forkVersion := networkToForkVersion(network)

// MarshalDepositData serializes a list of deposit data into a single file.
func MarshalDepositData(pubkeys []eth2p0.BLSPubKey, msgSigs []eth2p0.BLSSignature, withdrawalAddr, network string) ([]byte, error) {
creds, err := withdrawalCredsFromAddr(withdrawalAddr)
if err != nil {
return nil, err
}

// calculate depositMessage root
msgRoot, err := getMessageRoot(pubkey, withdrawalAddr)
if err != nil {
return nil, err
}

// TODO(corver): Verify signature matches msgRoot.

dd := eth2p0.DepositData{
PublicKey: pubkey,
WithdrawalCredentials: creds[:],
Amount: validatorAmt,
Signature: msgSig,
}
dataRoot, err := dd.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "deposit data hash root")
}
forkVersion := networkToForkVersion(network)

bytes, err := json.MarshalIndent(&depositDataJSON{
PubKey: fmt.Sprintf("%x", pubkey),
WithdrawalCredentials: fmt.Sprintf("%x", creds),
Amount: uint64(validatorAmt),
Signature: fmt.Sprintf("%x", msgSig),
DepositMessageRoot: fmt.Sprintf("%x", msgRoot),
DepositDataRoot: fmt.Sprintf("%x", dataRoot),
ForkVersion: fmt.Sprintf("%x", forkVersion),
NetworkName: network,
}, "", " ")
if err != nil {
return nil, errors.Wrap(err, "marshal deposit data")
}
var ddList []depositDataJSON
for i := 0; i < len(pubkeys); i++ {
// calculate depositMessage root
msgRoot, err := getMessageRoot(pubkeys[i], withdrawalAddr)
if err != nil {
return nil, err
}

return bytes, nil
}
// TODO(corver): Verify signature matches msgRoot.

// MarshalDepositDatas serializes a list of deposit data into a single file.
func MarshalDepositDatas(depositDatas [][]byte) ([]byte, error) {
var ddList []depositDataJSON
ddLen := len(depositDatas)
for i := 0; i < ddLen; i++ {
var dd depositDataJSON
err := json.Unmarshal(depositDatas[i], &dd)
dd := eth2p0.DepositData{
PublicKey: pubkeys[i],
WithdrawalCredentials: creds[:],
Amount: validatorAmt,
Signature: msgSigs[i],
}
dataRoot, err := dd.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "unmarshal deposit data")
return nil, errors.Wrap(err, "deposit data hash root")
}
ddList = append(ddList, dd)

ddList = append(ddList, depositDataJSON{
PubKey: fmt.Sprintf("%x", pubkeys[i]),
WithdrawalCredentials: fmt.Sprintf("%x", creds),
Amount: uint64(validatorAmt),
Signature: fmt.Sprintf("%x", msgSigs[i]),
DepositMessageRoot: fmt.Sprintf("%x", msgRoot),
DepositDataRoot: fmt.Sprintf("%x", dataRoot),
ForkVersion: fmt.Sprintf("%x", forkVersion),
NetworkName: network,
DepositCliVersion: depositCliVersion,
})
}

bytes, err := json.MarshalIndent(ddList, "", " ")
Expand Down Expand Up @@ -212,4 +199,5 @@ type depositDataJSON struct {
DepositDataRoot string `json:"deposit_data_root"`
ForkVersion string `json:"fork_version"`
NetworkName string `json:"network_name"`
DepositCliVersion string `json:"deposit_cli_version"`
}
65 changes: 45 additions & 20 deletions eth2util/deposit/deposit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"os"
"testing"

eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
"github.com/stretchr/testify/require"

"github.com/obolnetwork/charon/eth2util/deposit"
Expand All @@ -29,38 +31,61 @@ import (

const (
// Test output file and it's input values.
testfile = "testdata/deposit_data.json"
privKey = "07e0355752a16fdc473d01676f7f82594991ea830eea928d8e2254dfd98d4beb"
withdrawalAddr = "0xc0404ed740a69d11201f5ed297c5732f562c6e4e"
network = "prater"
)

func TestDepositData(t *testing.T) {
// Get the private and public keys
privKeyBytes, err := hex.DecodeString(privKey)
require.NoError(t, err)
sk, err := tblsconv.SecretFromBytes(privKeyBytes)
require.NoError(t, err)
pk, err := sk.GetPublicKey()
func TestMarshalDepositData(t *testing.T) {
file := "testdata/deposit-data.json"
privKeys := []string{
"01477d4bfbbcebe1fef8d4d6f624ecbb6e3178558bb1b0d6286c816c66842a6d",
"5b77c0f0ef7c4ddc123d55b8bd93daeefbd7116764a941c0061a496649e145b5",
"1dabcbfc9258f0f28606bf9e3b1c9f06d15a6e4eb0fbc28a43835eaaed7623fc",
"002ff4fd29d3deb6de9f5d115182a49c618c97acaa365ad66a0b240bd825c4ff",
}

var pubkeys []eth2p0.BLSPubKey
var msgsigs []eth2p0.BLSSignature

for i := 0; i < len(privKeys); i++ {
sk, pk := GetKeys(t, privKeys[i])

msgRoot, err := deposit.GetMessageSigningRoot(pk, withdrawalAddr, network)
require.NoError(t, err)

sig, err := tbls.Sign(sk, msgRoot[:])
require.NoError(t, err)

sigEth2 := tblsconv.SigToETH2(sig)

pubkeys = append(pubkeys, pk)
msgsigs = append(msgsigs, sigEth2)
}

actual, err := deposit.MarshalDepositData(pubkeys, msgsigs, withdrawalAddr, network)
require.NoError(t, err)
pubkey, err := tblsconv.KeyToETH2(pk)

// Not using golden file since output MUST never change.
expected, err := os.ReadFile(file)
require.NoError(t, err)
require.Equal(t, expected, actual)
}

// Get the private and public keys in appropriate format for the test.
func GetKeys(t *testing.T, privKey string) (*bls_sig.SecretKey, eth2p0.BLSPubKey) {
t.Helper()

// Get deposit message signing root
msgSigningRoot, err := deposit.GetMessageSigningRoot(pubkey, withdrawalAddr, network)
privKeyBytes, err := hex.DecodeString(privKey)
require.NoError(t, err)

// Sign it
s, err := tbls.Sign(sk, msgSigningRoot[:])
sk, err := tblsconv.SecretFromBytes(privKeyBytes)
require.NoError(t, err)
sigEth2 := tblsconv.SigToETH2(s)

// Check if serialized versions match.
actual, err := deposit.MarshalDepositData(pubkey, withdrawalAddr, network, sigEth2)
pk, err := sk.GetPublicKey()
require.NoError(t, err)

// Not using golden file since output MUST never change.
expected, err := os.ReadFile(testfile)
pubkey, err := tblsconv.KeyToETH2(pk)
require.NoError(t, err)
require.Equal(t, expected, actual)

return sk, pubkey
}
46 changes: 46 additions & 0 deletions eth2util/deposit/testdata/deposit-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[
{
"pubkey": "813f5d2697f76841a752ef8c1ac11d1bb76e07003799c5745f8a569214653810def3b60920b54fb0ab3cb6deb08c3972",
"withdrawal_credentials": "010000000000000000000000c0404ed740a69d11201f5ed297c5732f562c6e4e",
"amount": 32000000000,
"signature": "b5f95360b98cb6db3c06998b43b1dce29aea4f085950018d9dbd3c9894941f4804c449c771392ed3dde1091d346d71c316273c7213237dfa6b9ea79fbcfe79b055a5880d309f5db2dcf3e9a0fee6a4c843f3adb5835958b0561996218cd63acf",
"deposit_message_root": "33109737473d6ccb5f3c4f7eccaa4a7cd3dd63de3d9878a978243053904d39bf",
"deposit_data_root": "4d97bfb4ad862d7885586186d0c8c482d1b084e3d478d9b7e55bb4729d058dfc",
"fork_version": "00001020",
"network_name": "prater",
"deposit_cli_version": "2.1.0"
},
{
"pubkey": "940a838cd88c10daa9c26fcdf8472dfe09657c4aa4030380c6cca5ddb573a8036dfb03e61137c4baacdc9d69061f1eb2",
"withdrawal_credentials": "010000000000000000000000c0404ed740a69d11201f5ed297c5732f562c6e4e",
"amount": 32000000000,
"signature": "b8e638af308c9e1a1dfac291c7b0218dc3c1dc9baa000dbee0539ed6805cd76afd06fd50797e8192cf917d51c712c31212670e521e0cf326fadbbd8f361fa7a1e0fc80bc77279c71ddd2505835ec3da74a742a5ca19a635c6889915ef3a2dd89",
"deposit_message_root": "8cec1018fcfcb6ae295546901f1c55e2f2643834b89c9a00e4806d8c6b197169",
"deposit_data_root": "0c8b9be09c9e36bef37e9248ce262485d1ee21e005ecf3ff347a820ba0b3c69a",
"fork_version": "00001020",
"network_name": "prater",
"deposit_cli_version": "2.1.0"
},
{
"pubkey": "80d0436ccacd2b263f5e9e7ebaa14015fe5c80d3e57dc7c37bcbda783895e3491019d3ed694ecbb49c8c80a0480c0392",
"withdrawal_credentials": "010000000000000000000000c0404ed740a69d11201f5ed297c5732f562c6e4e",
"amount": 32000000000,
"signature": "8e383948b4909a20e16c17883148adf56234c19540995830a1908720cbdf4bed309568fefbb40bec4b7f8b4b3df9e19016afb25d76899ec6e1cc7259abe5de6e7b9b04723014ebef47852925c4dd3ff343caeeeb5b4ed499c9d5be2cda1f8b84",
"deposit_message_root": "4f5f7044a71d625c59974e32e8d86aed0a2211d423675303124ce5ac70969a46",
"deposit_data_root": "892766707bb5d2bb9602d9620f942af9e0c2c915c4340ad22ef935dd999db85b",
"fork_version": "00001020",
"network_name": "prater",
"deposit_cli_version": "2.1.0"
},
{
"pubkey": "b3d95c8790d63114ab1e813e8943f1bd59683927942df076ad724de8cc00974306c95d6614ef93505b5acd9719a6de78",
"withdrawal_credentials": "010000000000000000000000c0404ed740a69d11201f5ed297c5732f562c6e4e",
"amount": 32000000000,
"signature": "a4f5fac20a37a109200594ab9ee3bf983aab4aea6202fab637e7e79b9bbae37cef5815c746bdf01a57e659605346a9b80d4efe7e0884de144dcff28d07838a58819f03dd8fc2b48ea14e62222390fe3cb0284010f6acdcb1c4b5eb4c46a02560",
"deposit_message_root": "6b6860824055330be4cb0a378a1ffd342e4de77fef1d51621d44419c4b313ca9",
"deposit_data_root": "a7de8c4c4e26b9751afcc1bc7ecc558c3d40ee3985d62c6ba07dc08c0aa64581",
"fork_version": "00001020",
"network_name": "prater",
"deposit_cli_version": "2.1.0"
}
]
10 changes: 0 additions & 10 deletions eth2util/deposit/testdata/deposit_data.json

This file was deleted.