Skip to content

Commit

Permalink
WIP: ChangePubKey tx
Browse files Browse the repository at this point in the history
  • Loading branch information
VickMellon committed Jul 6, 2021
1 parent b12eac1 commit 9510ee6
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 65 deletions.
20 changes: 20 additions & 0 deletions Bits.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ func (b *Bits) Len() uint {
}

func (b *Bits) SetBit(i uint, v bool) {
// len is not checked, can cause panic
b.bits[i] = v
}

func (b *Bits) GetBit(i uint) bool {
// len is not checked, can cause panic
return b.bits[i]
}

func (b *Bits) Clone() *Bits {
clone := NewBits(b.Len())
copy(clone.bits, b.bits)
Expand Down Expand Up @@ -63,3 +69,17 @@ func (b *Bits) ToBytesBE() ([]byte, error) {
}
return res, nil
}

func (b *Bits) FromBytesBE(bytes []byte) *Bits {
for i, v := range bytes {
b.SetBit(uint(i*8+0), v&0x80 > 0)
b.SetBit(uint(i*8+1), v&0x40 > 0)
b.SetBit(uint(i*8+2), v&0x20 > 0)
b.SetBit(uint(i*8+3), v&0x10 > 0)
b.SetBit(uint(i*8+4), v&0x08 > 0)
b.SetBit(uint(i*8+5), v&0x04 > 0)
b.SetBit(uint(i*8+6), v&0x02 > 0)
b.SetBit(uint(i*8+7), v&0x01 > 0)
}
return b
}
83 changes: 83 additions & 0 deletions ChangePubKey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package zksync

import (
"encoding/hex"
"github.com/ethereum/go-ethereum/common"
)

type ChangePubKey struct {
Type string `json:"type"`
AccountId uint32 `json:"accountId"`
Account common.Address `json:"account"`
NewPkHash string `json:"newPkHash"`
FeeToken uint32 `json:"feeToken"`
Fee string `json:"fee"`
Nonce uint32 `json:"nonce"`
Signature *Signature `json:"signature"`
EthAuthData ChangePubKeyVariant `json:"ethAuthData"`
*TimeRange
}

func (t *ChangePubKey) getType() string {
return "ChangePubKey"
}

type ChangePubKeyAuthType string

const (
ChangePubKeyAuthTypeOnchain ChangePubKeyAuthType = `Onchain`
ChangePubKeyAuthTypeECDSA ChangePubKeyAuthType = `ECDSA`
ChangePubKeyAuthTypeCREATE2 ChangePubKeyAuthType = `CREATE2`
)

type ChangePubKeyVariant interface {
getType() ChangePubKeyAuthType
getBytes() []byte
}

type ChangePubKeyOnchain struct {
Type ChangePubKeyAuthType `json:"type"`
}

func (t *ChangePubKeyOnchain) getType() ChangePubKeyAuthType {
return ChangePubKeyAuthTypeOnchain
}

func (t *ChangePubKeyOnchain) getBytes() []byte {
return make([]byte, 32)
}

type ChangePubKeyECDSA struct {
Type ChangePubKeyAuthType `json:"type"`
EthSignature string `json:"ethSignature"`
BatchHash string `json:"batchHash"`
}

func (t *ChangePubKeyECDSA) getType() ChangePubKeyAuthType {
return ChangePubKeyAuthTypeECDSA
}

func (t *ChangePubKeyECDSA) getBytes() []byte {
res, _ := hex.DecodeString(t.BatchHash)
return res
}

type ChangePubKeyCREATE2 struct {
Type ChangePubKeyAuthType `json:"type"`
CreatorAddress string `json:"creatorAddress"`
SaltArg string `json:"saltArg"`
CodeHash string `json:"codeHash"`
}

func (t *ChangePubKeyCREATE2) getType() ChangePubKeyAuthType {
return ChangePubKeyAuthTypeCREATE2
}

//func (t *ChangePubKeyCREATE2) getBytes() []byte {
// return make([]byte, 32)
//}

type Signature struct {
PubKey string `json:"pubKey"`
Signature string `json:"signature"`
}
60 changes: 58 additions & 2 deletions EthSigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ package zksync

import (
"crypto/ecdsa"
"encoding/hex"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/miguelmota/go-ethereum-hdwallet"
"github.com/pkg/errors"
"math/big"
)

type EthSigner interface {
GetAddress() common.Address
GetPk() (*ecdsa.PrivateKey, error)
SignMessage([]byte) ([]byte, error)
SignAuth(txData *ChangePubKey) (*ChangePubKeyECDSA, error)
SignTransaction(tx ZksTransaction, nonce uint32, token *Token, fee *big.Int) (*EthSignature, error)
}

type DefaultEthSigner struct {
Expand All @@ -28,7 +32,7 @@ func NewEthSignerFromMnemonic(mnemonic string) (*DefaultEthSigner, error) {
if err != nil {
return nil, errors.Wrap(err, "failed to parse derivation path")
}
account, err := wallet.Derive(path, false)
account, err := wallet.Derive(path, true)
if err != nil {
return nil, errors.Wrap(err, "failed to derive account from HD wallet")
}
Expand All @@ -43,9 +47,61 @@ func (s *DefaultEthSigner) GetAddress() common.Address {
}

func (s *DefaultEthSigner) SignMessage(msg []byte) ([]byte, error) {
return s.wallet.SignText(s.account, msg)
return s.wallet.SignText(s.account, msg) // prefixed
}

func (s *DefaultEthSigner) SignAuth(txData *ChangePubKey) (*ChangePubKeyECDSA, error) {
auth := &ChangePubKeyECDSA{
Type: ChangePubKeyAuthTypeECDSA,
EthSignature: "",
BatchHash: hex.EncodeToString(make([]byte, 32)),
}
txData.EthAuthData = auth
msg, err := getChangePubKeyData(txData)
if err != nil {
return nil, errors.Wrap(err, "failed to get ChangePubKey data for sign")
}
sig, err := s.SignMessage(msg)
if err != nil {
return nil, errors.Wrap(err, "failed to sign ChangePubKeyECDSA msg")
}
auth.EthSignature = hex.EncodeToString(sig)
return auth, nil
}

func (s *DefaultEthSigner) SignTransaction(tx ZksTransaction, nonce uint32, token *Token, fee *big.Int) (*EthSignature, error) {
switch tx.getType() {
case "ChangePubKey":
if txData, ok := tx.(*ChangePubKey); ok {
msg, err := getChangePubKeyData(txData)
if err != nil {
return nil, errors.Wrap(err, "failed to get ChangePubKey data for sign")
}
sig, err := s.SignMessage(msg)
if err != nil {
return nil, errors.Wrap(err, "failed to sign ChangePubKey tx")
}
return &EthSignature{
sigType: EthSignatureTypeEth,
signature: hex.EncodeToString(sig),
}, nil
}
}
return nil, errors.New("unknown tx type")
}

func (s *DefaultEthSigner) GetPk() (*ecdsa.PrivateKey, error) {
return s.wallet.PrivateKey(s.account)
}

type EthSignatureType string

const (
EthSignatureTypeEth EthSignatureType = "EthereumSignature"
EthSignatureTypeEIP1271 EthSignatureType = "EIP1271Signature"
)

type EthSignature struct {
sigType EthSignatureType
signature string
}
22 changes: 22 additions & 0 deletions Fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@ package zksync

import "math/big"

type TransactionType string

func (t TransactionType) getType() interface{} {
switch t {
case TransactionTypeChangePubKeyOnchain, TransactionTypeChangePubKeyECDSA, TransactionTypeChangePubKeyCREATE2:
// custom object instead of string
return TransactionTypeChangePubKey{ChangePubKey: string(t)}
default:
return string(t)
}
}

type TransactionTypeChangePubKey struct {
ChangePubKey string `json:"ChangePubKey"`
}

const (
TransactionTypeChangePubKeyOnchain TransactionType = "Onchain"
TransactionTypeChangePubKeyECDSA TransactionType = "ECDSA"
TransactionTypeChangePubKeyCREATE2 TransactionType = "CREATE2"
)

type TransactionFeeDetails struct {
GasTxAmount string `json:"gasTxAmount"`
GasPriceWei string `json:"gasPriceWei"`
Expand Down
14 changes: 8 additions & 6 deletions Provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type Provider interface {
GetTokens() (*Tokens, error)
ContractAddress() (*ContractAddress, error)
GetState(address common.Address) (*AccountState, error)
GetTransactionFee(txType TransactionType, address common.Address, token *Token) (*TransactionFeeDetails, error)
SubmitTx(signedTx ZksTransaction, ethSignature *EthSignature, fastProcessing bool) (string, error)
}

func NewDefaultProvider(rawUrl string) (*DefaultProvider, error) {
Expand Down Expand Up @@ -70,20 +72,20 @@ func (p *DefaultProvider) GetState(address common.Address) (*AccountState, error
return res, nil
}

func (p *DefaultProvider) GetTransactionFee(txType string, address common.Address, token *Token) (*TransactionFeeDetails, error) {
func (p *DefaultProvider) GetTransactionFee(txType TransactionType, address common.Address, token *Token) (*TransactionFeeDetails, error) {
res := new(TransactionFeeDetails)
err := p.client.Call(&res, "get_tx_fee", txType, address.String(), token.Symbol)
err := p.client.Call(&res, "get_tx_fee", txType.getType(), address.String(), token.Symbol)
if err != nil {
return nil, errors.Wrap(err, "failed to call `get_tx_fee` method")
}
return res, nil
}

func (p *DefaultProvider) SubmitTx(txType string, address common.Address, token *Token) (*TransactionFeeDetails, error) {
res := new(TransactionFeeDetails)
err := p.client.Call(&res, "tx_submit", txType, address.String(), token.Symbol)
func (p *DefaultProvider) SubmitTx(signedTx ZksTransaction, ethSignature *EthSignature, fastProcessing bool) (string, error) {
var res string
err := p.client.Call(&res, "tx_submit", signedTx, ethSignature, fastProcessing)
if err != nil {
return nil, errors.Wrap(err, "failed to call `get_tx_fee` method")
return "", errors.Wrap(err, "failed to call `tx_submit` method")
}
return res, nil
}
74 changes: 63 additions & 11 deletions SigningUtils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package zksync

import (
"bytes"
"encoding/binary"
"encoding/hex"
"github.com/pkg/errors"
Expand All @@ -12,9 +13,15 @@ const (
FeeMantissaBitWidth int64 = 11
)

func Uint32ToBytes(accountId uint32) []byte {
func Uint32ToBytes(v uint32) []byte {
res := make([]byte, 4)
binary.BigEndian.PutUint32(res, accountId)
binary.BigEndian.PutUint32(res, v)
return res
}

func Uint64ToBytes(v uint64) []byte {
res := make([]byte, 8)
binary.BigEndian.PutUint64(res, v)
return res
}

Expand All @@ -33,8 +40,17 @@ func pkhToBytes(pkh string) ([]byte, error) {
}

func packFee(fee *big.Int) ([]byte, error) {
// WIP
return integerToDecimalByteArray(fee, FeeExponentBitWidth, FeeMantissaBitWidth, 10)
packedFee, err := integerToDecimalByteArray(fee, FeeExponentBitWidth, FeeMantissaBitWidth, 10)
if err != nil {
return nil, errors.Wrap(err, "failed to pack fee")
}
// check that unpacked fee still has same value
if unpackedFee, err := decimalByteArrayToInteger(packedFee, FeeExponentBitWidth, FeeMantissaBitWidth, 10); err != nil {
return nil, errors.Wrap(err, "failed to unpack fee")
} else if unpackedFee.Cmp(fee) != 0 {
return nil, errors.New("fee Amount is not packable")
}
return packedFee, nil
}

func integerToDecimalByteArray(value *big.Int, expBits, mantissaBits, expBase int64) ([]byte, error) {
Expand All @@ -49,7 +65,7 @@ func integerToDecimalByteArray(value *big.Int, expBits, mantissaBits, expBase in
return nil, errors.New("Integer is too big")
}
exponent := uint64(0)
mantissa := value
mantissa := big.NewInt(0).Set(value)
for mantissa.Cmp(maxMantissa) > 0 {
mantissa.Div(mantissa, bigExpBase)
exponent++
Expand All @@ -59,8 +75,38 @@ func integerToDecimalByteArray(value *big.Int, expBits, mantissaBits, expBase in
mantissaData := uint64ToBitsLE(mantissa.Uint64(), uint(mantissaBits))
combined := exponentData.Clone().Append(mantissaData)
reversed := combined.Reverse()
bytes, _ := reversed.ToBytesBE()
return reverseByte(bytes), nil
bytes, err := reversed.ToBytesBE()
if err != nil {
return nil, errors.Wrap(err, "failed to convert bits to bytes BE")
}
return bytes, nil
}

func decimalByteArrayToInteger(value []byte, expBits, mantissaBits, expBase int64) (*big.Int, error) {
if int64(len(value)*8) != expBits+mantissaBits {
return nil, errors.New("Decimal unpacking, incorrect input length")
}
bits := NewBits(uint(expBits + mantissaBits))
bits.FromBytesBE(value).Reverse()
exponent := big.NewInt(0)
expPow2 := big.NewInt(1)
for i := uint(0); i < uint(expBits); i++ {
if bits.GetBit(i) {
exponent.Add(exponent, expPow2)
}
expPow2.Mul(expPow2, big.NewInt(2))
}
exponent.Exp(big.NewInt(expBase), exponent, nil)

mantissa := big.NewInt(0)
mantissaPow2 := big.NewInt(1)
for i := uint(expBits); i < uint(expBits+mantissaBits); i++ {
if bits.GetBit(i) {
mantissa.Add(mantissa, mantissaPow2)
}
mantissaPow2.Mul(mantissaPow2, big.NewInt(2))
}
return exponent.Mul(exponent, mantissa), nil
}

func uint64ToBitsLE(v uint64, size uint) *Bits {
Expand All @@ -72,9 +118,15 @@ func uint64ToBitsLE(v uint64, size uint) *Bits {
return res
}

func reverseByte(v []byte) []byte {
for i, j := 0, len(v)-1; i < j; i, j = i+1, j-1 {
v[i], v[j] = v[j], v[i]
func getChangePubKeyData(txData *ChangePubKey) ([]byte, error) {
buf := bytes.Buffer{}
pkhBytes, err := pkhToBytes(txData.NewPkHash)
if err != nil {
return nil, errors.Wrap(err, "failed to get pkh bytes")
}
return v
buf.Write(pkhBytes)
buf.Write(Uint32ToBytes(txData.Nonce))
buf.Write(Uint32ToBytes(txData.AccountId))
buf.Write(txData.EthAuthData.getBytes())
return buf.Bytes(), nil
}
Loading

0 comments on commit 9510ee6

Please sign in to comment.