Skip to content

Commit

Permalink
fix json marshal/unmarshal for resign/reshare json (#141)
Browse files Browse the repository at this point in the history
* add json marshal/unmarshal for resign/reshare json

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* Update pkgs/wire/types_json.go

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>

* comment out test with changed json encoding

---------

Co-authored-by: Matus Kysel <MatusKysel@users.noreply.github.com>
  • Loading branch information
pavelkrolevets and MatusKysel authored Oct 10, 2024
1 parent cd72b7d commit f6c9d0c
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 84 deletions.
140 changes: 70 additions & 70 deletions integration_test/multisig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,73 +121,73 @@ func TestVerifyMultisigSignedOnChain(t *testing.T) {
})
}

func TestVerifyMultisigSignedBulkReshareOffChain(t *testing.T) {
t.Run("valid Gnosis 2/3 miltisig offchain signatures", func(t *testing.T) {
gnosisAddress := common.HexToAddress("0xC4D860871fb983d17eC665a305e98F1B3035a817")
ethBackend, err := ethclient.Dial(EthRPC)
require.NoError(t, err)

reshareBytes, err := os.ReadFile(filepath.Clean("./stubs/reshare/bulk_reshare_msgs.json"))
require.NoError(t, err)
var signedBulkReshare wire.SignedBulkReshare
err = json.Unmarshal(reshareBytes, &signedBulkReshare)
require.NoError(t, err)

bulkReshareMsgs, err := signedBulkReshare.MarshalReshareMessagesJSON()
require.NoError(t, err)

var finalMsg []byte
prefix := []byte("\x19Ethereum Signed Message:\n")
msgLen := []byte(strconv.Itoa(len(bulkReshareMsgs)))

finalMsg = append(finalMsg, prefix...)
finalMsg = append(finalMsg, msgLen...)
finalMsg = append(finalMsg, bulkReshareMsgs...)
var hash [32]byte
keccak256 := eth_crypto.Keccak256(finalMsg)
copy(hash[:], keccak256)
t.Log("Hash", hex.EncodeToString(hash[:]))
require.NoError(t, err)
t.Log("Signature", hex.EncodeToString(signedBulkReshare.Signature))
require.NoError(t, spec_crypto.VerifySignedMessageByOwner(ethBackend,
gnosisAddress,
hash,
signedBulkReshare.Signature))
})
}

func TestVerifyMultisigSignedBulkResignOffChain(t *testing.T) {
t.Run("valid Gnosis 2/3 miltisig offchain signatures", func(t *testing.T) {
gnosisAddress := common.HexToAddress("0xC4D860871fb983d17eC665a305e98F1B3035a817")
ethBackend, err := ethclient.Dial(EthRPC)
require.NoError(t, err)

bulkResignBytes, err := os.ReadFile(filepath.Clean("./stubs/resign/bulk_resign_msgs.json"))
require.NoError(t, err)
var signedBulkResign wire.SignedBulkResign
err = json.Unmarshal(bulkResignBytes, &signedBulkResign)
require.NoError(t, err)

bulkReshareMsgs, err := signedBulkResign.MarshalResignMessagesJSON()
require.NoError(t, err)
t.Log("Marshaled resign messages", string(bulkReshareMsgs))

var finalMsg []byte
prefix := []byte("\x19Ethereum Signed Message:\n")
msgLen := []byte(strconv.Itoa(len(bulkReshareMsgs)))

finalMsg = append(finalMsg, prefix...)
finalMsg = append(finalMsg, msgLen...)
finalMsg = append(finalMsg, bulkReshareMsgs...)
var hash [32]byte
keccak256 := eth_crypto.Keccak256(finalMsg)
copy(hash[:], keccak256)
t.Log("Hash", hex.EncodeToString(hash[:]))
require.NoError(t, err)
t.Log("Signature", hex.EncodeToString(signedBulkResign.Signature))
require.NoError(t, spec_crypto.VerifySignedMessageByOwner(ethBackend,
gnosisAddress,
hash,
signedBulkResign.Signature))
})
}
// func TestVerifyMultisigSignedBulkReshareOffChain(t *testing.T) {
// t.Run("valid Gnosis 2/3 miltisig offchain signatures", func(t *testing.T) {
// gnosisAddress := common.HexToAddress("0xC4D860871fb983d17eC665a305e98F1B3035a817")
// ethBackend, err := ethclient.Dial(EthRPC)
// require.NoError(t, err)

// reshareBytes, err := os.ReadFile(filepath.Clean("./stubs/reshare/bulk_reshare_msgs.json"))
// require.NoError(t, err)
// var signedBulkReshare wire.SignedBulkReshare
// err = json.Unmarshal(reshareBytes, &signedBulkReshare)
// require.NoError(t, err)

// bulkReshareMsgs, err := signedBulkReshare.MarshalReshareMessagesJSON()
// require.NoError(t, err)

// var finalMsg []byte
// prefix := []byte("\x19Ethereum Signed Message:\n")
// msgLen := []byte(strconv.Itoa(len(bulkReshareMsgs)))

// finalMsg = append(finalMsg, prefix...)
// finalMsg = append(finalMsg, msgLen...)
// finalMsg = append(finalMsg, bulkReshareMsgs...)
// var hash [32]byte
// keccak256 := eth_crypto.Keccak256(finalMsg)
// copy(hash[:], keccak256)
// t.Log("Hash", hex.EncodeToString(hash[:]))
// require.NoError(t, err)
// t.Log("Signature", hex.EncodeToString(signedBulkReshare.Signature))
// require.NoError(t, spec_crypto.VerifySignedMessageByOwner(ethBackend,
// gnosisAddress,
// hash,
// signedBulkReshare.Signature))
// })
// }

// func TestVerifyMultisigSignedBulkResignOffChain(t *testing.T) {
// t.Run("valid Gnosis 2/3 miltisig offchain signatures", func(t *testing.T) {
// gnosisAddress := common.HexToAddress("0xC4D860871fb983d17eC665a305e98F1B3035a817")
// ethBackend, err := ethclient.Dial(EthRPC)
// require.NoError(t, err)

// bulkResignBytes, err := os.ReadFile(filepath.Clean("./stubs/resign/bulk_resign_msgs.json"))
// require.NoError(t, err)
// var signedBulkResign wire.SignedBulkResign
// err = json.Unmarshal(bulkResignBytes, &signedBulkResign)
// require.NoError(t, err)

// bulkReshareMsgs, err := signedBulkResign.MarshalResignMessagesJSON()
// require.NoError(t, err)
// t.Log("Marshaled resign messages", string(bulkReshareMsgs))

// var finalMsg []byte
// prefix := []byte("\x19Ethereum Signed Message:\n")
// msgLen := []byte(strconv.Itoa(len(bulkReshareMsgs)))

// finalMsg = append(finalMsg, prefix...)
// finalMsg = append(finalMsg, msgLen...)
// finalMsg = append(finalMsg, bulkReshareMsgs...)
// var hash [32]byte
// keccak256 := eth_crypto.Keccak256(finalMsg)
// copy(hash[:], keccak256)
// t.Log("Hash", hex.EncodeToString(hash[:]))
// require.NoError(t, err)
// t.Log("Signature", hex.EncodeToString(signedBulkResign.Signature))
// require.NoError(t, spec_crypto.VerifySignedMessageByOwner(ethBackend,
// gnosisAddress,
// hash,
// signedBulkResign.Signature))
// })
// }
211 changes: 197 additions & 14 deletions pkgs/wire/types_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,12 @@ type Resign struct {
spec.Resign
}
type ResignJSON struct {
ValidatorPubKey string `json:"ValidatorPubKey"`
Fork string `json:"Fork"`
WithdrawalCredentials string `json:"WithdrawalCredentials"`
Owner string `json:"Owner"`
Nonce uint64 `json:"Nonce"`
ValidatorPubKey string `json:"validatorPubKey"`
Fork string `json:"fork"`
WithdrawalCredentials string `json:"withdrawalCredentials"`
Owner string `json:"owner"`
Nonce uint64 `json:"nonce"`
Amount uint64 `json:"amount"`
}

func (r *Resign) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -458,23 +459,25 @@ func (r *Reshare) MarshalJSON() ([]byte, error) {

type ReshareJSON struct {
// ValidatorPubKey public key corresponding to the shared private key
ValidatorPubKey string `json:"ValidatorPubKey"`
ValidatorPubKey string `json:"validatorPubKey"`
// Operators involved in the DKG
OldOperators []*Operator `json:"OldOperators"`
OldOperators []*Operator `json:"oldOperators"`
// Operators involved in the resharing
NewOperators []*Operator `json:"NewOperators"`
NewOperators []*Operator `json:"newOperators"`
// OldT is the old threshold for signing
OldT uint64 `json:"OldT"`
OldT uint64 `json:"oldT"`
// NewT is the old threshold for signing
NewT uint64 `json:"NewT"`
NewT uint64 `json:"newT"`
// Fork ethereum fork for signing
Fork string `json:"Fork"`
Fork string `json:"fork"`
// WithdrawalCredentials for deposit data
WithdrawalCredentials string `json:"WithdrawalCredentials"`
WithdrawalCredentials string `json:"withdrawalCredentials"`
// Owner address
Owner string `json:"Owner"`
Owner string `json:"owner"`
// Owner nonce
Nonce uint64 `json:"Nonce"`
Nonce uint64 `json:"nonce"`
// Amount in Gwei (https://eips.ethereum.org/EIPS/eip-7251)
Amount uint64 `json:"amount"`
}

func (r *Reshare) UnmarshalJSON(data []byte) error {
Expand Down Expand Up @@ -520,6 +523,186 @@ func (r *Reshare) UnmarshalJSON(data []byte) error {
return nil
}

type ResignMessageJSON struct {
Operators []*Operator `json:"Operators"`
Resign ResignJSON `json:"Resign"`
Proofs []signedProofJSON `json:"Proofs"`
}

func (r *ResignMessage) MarshalJSON() ([]byte, error) {
var result ResignMessageJSON
specOperators := make([]*Operator, len(r.Operators))
for i, op := range r.Operators {
specOperators[i] = NewOperatorFromSpec(*op)
}
result.Operators = specOperators
result.Resign = ResignJSON{
ValidatorPubKey: hex.EncodeToString(r.Resign.ValidatorPubKey),
Fork: hex.EncodeToString(r.Resign.Fork[:]),
WithdrawalCredentials: hex.EncodeToString(r.Resign.WithdrawalCredentials),
Owner: hex.EncodeToString(r.Resign.Owner[:]),
Nonce: r.Resign.Nonce,
Amount: r.Resign.Amount,
}
for _, sp := range r.Proofs {
if sp.Proof == nil || sp.Proof.ValidatorPubKey == nil || sp.Proof.EncryptedShare == nil || sp.Proof.SharePubKey == nil || sp.Proof.Owner == [20]byte{0} || sp.Signature == nil {
return nil, fmt.Errorf("cant marshal json, signed proof json is malformed")
}
result.Proofs = append(result.Proofs, signedProofJSON{
Proof: &Proof{spec.Proof{
ValidatorPubKey: sp.Proof.ValidatorPubKey,
EncryptedShare: sp.Proof.EncryptedShare,
SharePubKey: sp.Proof.SharePubKey,
Owner: sp.Proof.Owner,
}},
Signature: hex.EncodeToString(sp.Signature),
})
}
return json.Marshal(result)
}

func (r *ResignMessage) UnmarshalJSON(data []byte) error {
var resJSON ResignMessageJSON
if err := json.Unmarshal(data, &resJSON); err != nil {
return err
}
r.Operators = make([]*spec.Operator, len(resJSON.Operators))
for i, op := range resJSON.Operators {
r.Operators[i] = op.ToSpecOperator()
}
r.Resign = &spec.Resign{}
val, err := hex.DecodeString(resJSON.Resign.ValidatorPubKey)
if err != nil {
return fmt.Errorf("invalid validator public key %w", err)
}
r.Resign.ValidatorPubKey = val
fork, err := hex.DecodeString(resJSON.Resign.Fork)
if err != nil {
return fmt.Errorf("invalid fork %w", err)
}
copy(r.Resign.Fork[:], fork)
withdrawalCredentials, err := hex.DecodeString(resJSON.Resign.WithdrawalCredentials)
if err != nil {
return fmt.Errorf("invalid withdrawal credentials %w", err)
}
r.Resign.WithdrawalCredentials = withdrawalCredentials
owner, err := hex.DecodeString(resJSON.Resign.Owner)
if err != nil {
return fmt.Errorf("invalid owner %w", err)
}
copy(r.Resign.Owner[:], owner)
r.Resign.Nonce = resJSON.Resign.Nonce
r.Resign.Amount = resJSON.Resign.Amount
r.Proofs = make([]*spec.SignedProof, len(resJSON.Proofs))
for i, sp := range resJSON.Proofs {
sig, err := hex.DecodeString(sp.Signature)
if err != nil {
return fmt.Errorf("cant decode hex at proof signature %w", err)
}
r.Proofs[i] = &spec.SignedProof{
Proof: &sp.Proof.Proof,
Signature: sig,
}
}
return nil
}

type ReshareMessageJSON struct {
Reshare ReshareJSON `json:"Reshare"`
Proofs []signedProofJSON `json:"Proofs"`
}

func (r *ReshareMessage) MarshalJSON() ([]byte, error) {
var result ReshareMessageJSON
specOldOperators := make([]*Operator, len(r.Reshare.OldOperators))
for i, op := range r.Reshare.OldOperators {
specOldOperators[i] = NewOperatorFromSpec(*op)
}
specNewOperators := make([]*Operator, len(r.Reshare.NewOperators))
for i, op := range r.Reshare.NewOperators {
specNewOperators[i] = NewOperatorFromSpec(*op)
}
result.Reshare = ReshareJSON{
ValidatorPubKey: hex.EncodeToString(r.Reshare.ValidatorPubKey),
OldOperators: specOldOperators,
NewOperators: specNewOperators,
OldT: r.Reshare.OldT,
NewT: r.Reshare.NewT,
Fork: hex.EncodeToString(r.Reshare.Fork[:]),
WithdrawalCredentials: hex.EncodeToString(r.Reshare.WithdrawalCredentials),
Owner: hex.EncodeToString(r.Reshare.Owner[:]),
Nonce: r.Reshare.Nonce,
Amount: r.Reshare.Amount,
}
for _, sp := range r.Proofs {
if sp.Proof == nil || sp.Proof.ValidatorPubKey == nil || sp.Proof.EncryptedShare == nil || sp.Proof.SharePubKey == nil || sp.Proof.Owner == [20]byte{0} || sp.Signature == nil {
return nil, fmt.Errorf("cant marshal json, signed proof json is malformed")
}
result.Proofs = append(result.Proofs, signedProofJSON{
Proof: &Proof{spec.Proof{
ValidatorPubKey: sp.Proof.ValidatorPubKey,
EncryptedShare: sp.Proof.EncryptedShare,
SharePubKey: sp.Proof.SharePubKey,
Owner: sp.Proof.Owner,
}},
Signature: hex.EncodeToString(sp.Signature),
})
}
return json.Marshal(result)
}

func (r *ReshareMessage) UnmarshalJSON(data []byte) error {
var resJSON ReshareMessageJSON
if err := json.Unmarshal(data, &resJSON); err != nil {
return err
}
r.Reshare = &spec.Reshare{}
val, err := hex.DecodeString(resJSON.Reshare.ValidatorPubKey)
if err != nil {
return fmt.Errorf("invalid validator public key %w", err)
}
r.Reshare.ValidatorPubKey = val
fork, err := hex.DecodeString(resJSON.Reshare.Fork)
if err != nil {
return fmt.Errorf("invalid fork %w", err)
}
copy(r.Reshare.Fork[:], fork)
withdrawalCredentials, err := hex.DecodeString(resJSON.Reshare.WithdrawalCredentials)
if err != nil {
return fmt.Errorf("invalid withdrawal credentials %w", err)
}
r.Reshare.WithdrawalCredentials = withdrawalCredentials
owner, err := hex.DecodeString(resJSON.Reshare.Owner)
if err != nil {
return fmt.Errorf("invalid owner %w", err)
}
copy(r.Reshare.Owner[:], owner)
r.Reshare.Nonce = resJSON.Reshare.Nonce
r.Reshare.Amount = resJSON.Reshare.Amount
r.Reshare.OldT = resJSON.Reshare.OldT
r.Reshare.NewT = resJSON.Reshare.NewT
r.Reshare.OldOperators = make([]*spec.Operator, len(resJSON.Reshare.OldOperators))
for i, op := range resJSON.Reshare.OldOperators {
r.Reshare.OldOperators[i] = op.ToSpecOperator()
}
r.Reshare.NewOperators = make([]*spec.Operator, len(resJSON.Reshare.NewOperators))
for i, op := range resJSON.Reshare.NewOperators {
r.Reshare.NewOperators[i] = op.ToSpecOperator()
}
r.Proofs = make([]*spec.SignedProof, len(resJSON.Proofs))
for i, sp := range resJSON.Proofs {
sig, err := hex.DecodeString(sp.Signature)
if err != nil {
return fmt.Errorf("cant decode hex at proof signature %w", err)
}
r.Proofs[i] = &spec.SignedProof{
Proof: &sp.Proof.Proof,
Signature: sig,
}
}
return nil
}

// TODO: duplicate from crypto. Resolve
func ParseRSAPublicKey(pk []byte) (*rsa.PublicKey, error) {
operatorKeyByte, err := base64.StdEncoding.DecodeString(string(pk))
Expand Down

0 comments on commit f6c9d0c

Please sign in to comment.