Skip to content

Commit

Permalink
Add chain ID
Browse files Browse the repository at this point in the history
  • Loading branch information
firelizzard18 committed Jul 12, 2024
1 parent 406ce07 commit b9c0b48
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 70 deletions.
17 changes: 17 additions & 0 deletions internal/core/execute/v2/block/sig_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ func (x UserSignature) check(batch *database.Batch, ctx *userSigContext) error {
return errors.Unauthenticated.WithFormat("invalid signature")
}

// Check the chain ID, if this is an EIP-712 signature
err = x.checkChainID(ctx)
if err != nil {
return errors.UnknownError.Wrap(err)
}

// Check if the signature initiates the transaction
err = x.checkInit(ctx)
if err != nil {
Expand Down Expand Up @@ -177,6 +183,17 @@ func (UserSignature) unwrapDelegated(ctx *userSigContext) error {
return nil
}

func (UserSignature) checkChainID(ctx *userSigContext) error {
sig, ok := ctx.keySig.(*protocol.Eip712TypedDataSignature)
if !ok {
return nil
}
if ctx.GetActiveGlobals().ChainID().Cmp(sig.ChainID) != 0 {
return errors.BadRequest.WithFormat("invalid chain ID: want %d, got %d", ctx.GetActiveGlobals().ChainID(), sig.ChainID)
}
return nil
}

func (UserSignature) checkInit(ctx *userSigContext) error {
var initMerkle bool
ctx.isInitiator, initMerkle = protocol.SignatureDidInitiate(ctx.signature, ctx.transaction.Header.Initiator[:], nil)
Expand Down
43 changes: 43 additions & 0 deletions pkg/build/adi_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
package build

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"encoding/binary"
"fmt"
"io"
"math/big"
badrand "math/rand"

btc "github.com/btcsuite/btcd/btcec"
"gitlab.com/accumulatenetwork/accumulate/internal/database"
"gitlab.com/accumulatenetwork/accumulate/internal/database/record"
"gitlab.com/accumulatenetwork/accumulate/pkg/errors"
Expand Down Expand Up @@ -134,12 +141,48 @@ func (b *KeyPageBuilder) GenerateKey(typ protocol.SignatureType, seedParts ...an
addr.Type = typ
b.AddKey(addr)
return addr

case protocol.SignatureTypeETH,
protocol.SignatureTypeBTC,
protocol.SignatureTypeBTCLegacy:
sk := ecdsaFromSeed(btc.S256(), seed)
addr := address.FromETHPrivateKey(sk)
addr.Type = typ
b.AddKey(addr)
return addr

default:
b.errorf(errors.BadRequest, "generating %v keys is not supported", typ)
return nil
}
}

func ecdsaFromSeed(c elliptic.Curve, seed [32]byte) *ecdsa.PrivateKey {
rand := badrand.New(badrand.NewSource(int64(binary.BigEndian.Uint64(seed[:]))))

var k *big.Int
for {
N := c.Params().N
b := make([]byte, (N.BitLen()+7)/8)
if _, err := io.ReadFull(rand, b); err != nil {
panic(err)
}
if excess := len(b)*8 - N.BitLen(); excess > 0 {
b[0] >>= excess
}
k = new(big.Int).SetBytes(b)
if k.Sign() != 0 && k.Cmp(N) < 0 {
break
}
}

priv := new(ecdsa.PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv
}

func (b *KeyPageBuilder) SetAcceptThreshold(v uint64) *KeyPageBuilder {
return b.Update(func(page *protocol.KeyPage) {
page.AcceptThreshold = v
Expand Down
10 changes: 10 additions & 0 deletions pkg/types/address/from.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"fmt"

btc "github.com/btcsuite/btcd/btcec"
eth "github.com/ethereum/go-ethereum/crypto"
"gitlab.com/accumulatenetwork/accumulate/protocol"
)

Expand Down Expand Up @@ -92,6 +93,15 @@ func FromEcdsaPrivateKey(key *ecdsa.PrivateKey) *PrivateKey {
return priv
}

func FromETHPrivateKey(key *ecdsa.PrivateKey) *PrivateKey {
priv := new(PrivateKey)
priv.Type = protocol.SignatureTypeEcdsaSha256
priv.Key = eth.FromECDSA(key)
priv.PublicKey.Type = protocol.SignatureTypeEcdsaSha256
priv.PublicKey.Key = eth.FromECDSAPub(&key.PublicKey)
return priv
}

func FromPrivateKeyBytes(priv []byte, typ protocol.SignatureType) (*PrivateKey, error) {
var pub []byte
switch typ {
Expand Down
80 changes: 46 additions & 34 deletions pkg/types/encoding/eip712.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
Expand Down Expand Up @@ -59,14 +60,6 @@ type EIP712Domain struct {
ChainId *big.Int `json:"chainId,omitempty" form:"chainId" query:"chainId" validate:"required"`
}

var EIP712DomainValue eipResolvedValue
var EIP712DomainHash []byte
var Eip712Domain = EIP712Domain{
Name: "Accumulate",
Version: "1.0.0",
ChainId: big.NewInt(281),
}

type EIP712Resolver interface {
Resolve(any) (eipResolvedValue, error)
}
Expand All @@ -80,6 +73,15 @@ type eipResolvedValue interface {
var eip712EncoderMap = map[string]EIP712Resolver{}
var schemaDictionary = map[string]*TypeDefinition{}

var eip712DomainTypeDef = &TypeDefinition{
Name: "EIP712Domain",
Fields: &[]*TypeField{
NewTypeField("name", "string"),
NewTypeField("version", "string"),
NewTypeField("chainId", "uint256"),
},
}

func init() {
eip712EncoderMap["bool"] = newAtomicEncoder("bool", FromboolToBytes)
eip712EncoderMap["bytes"] = newAtomicEncoder("bytes", FrombytesToBytes)
Expand All @@ -91,24 +93,6 @@ func init() {
eip712EncoderMap["uint256"] = newAtomicEncoder("uint256", Fromuint256ToBytes)
eip712EncoderMap["float64"] = newAtomicEncoder("float64", FromfloatToBytes)
eip712EncoderMap["float"] = newAtomicEncoder("float", FromfloatToBytes) //Note = Float is not a valid type in EIP-712, so it is converted to a string

// Handle EIP712 domain initialization
var jdomain map[string]interface{}
j := must2(Eip712Domain.MarshalJSON())
must(json.Unmarshal(j, &jdomain))

const eipDomainKey = "EIP712Domain"
RegisterTypeDefinition(&[]*TypeField{
NewTypeField("name", "string"),
NewTypeField("version", "string"),
NewTypeField("chainId", "uint256"),
}, eipDomainKey)

td := schemaDictionary[eipDomainKey]
EIP712DomainValue = must2(td.Resolve(jdomain))
EIP712DomainHash = must2(EIP712DomainValue.Hash(map[string][]*TypeField{
eipDomainKey: *td.Fields,
}))
}

func must(err error) {
Expand Down Expand Up @@ -582,34 +566,62 @@ type EIP712Call struct {
Message eipResolvedValue `json:"message"`
}

func NewEIP712Call(value any, typ EIP712Resolver) (*EIP712Call, error) {
func NewEIP712Call(value any, chainID *big.Int, typ EIP712Resolver) (*EIP712Call, error) {
r, err := typ.Resolve(value)
if err != nil {
return nil, err
}

e := EIP712Call{}
e.PrimaryType = "Transaction"
e.Domain = Eip712Domain
e.Message = r
e := EIP712Call{
PrimaryType: "Transaction",
Message: r,
Domain: EIP712Domain{
Name: "Accumulate",
Version: "1.0.0",
ChainId: chainID,
},
Types: map[string][]*TypeField{
eip712DomainTypeDef.Name: *eip712DomainTypeDef.Fields,
},
}

e.Types = map[string][]*TypeField{}
r.Types(e.Types)
EIP712DomainValue.Types(e.Types)

return &e, nil
}

func (c *EIP712Call) Hash() ([]byte, error) {
if c.Domain.ChainId == nil {
return nil, errors.New("missing chain ID")
}

messageHash, err := c.Message.Hash(c.Types)
if err != nil {
return nil, err
}

var jdomain map[string]any
b, err := c.Domain.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(b, &jdomain)
if err != nil {
return nil, err
}
domain, err := eip712DomainTypeDef.Resolve(jdomain)
if err != nil {
return nil, err
}
domainHash, err := domain.Hash(c.Types)
if err != nil {
return nil, err
}

var buf bytes.Buffer
buf.WriteByte(0x19)
buf.WriteByte(0x01)
buf.Write(EIP712DomainHash)
buf.Write(domainHash)
buf.Write(messageHash)
return keccak256(buf.Bytes()), nil
}
Expand Down
1 change: 1 addition & 0 deletions pkg/types/encoding/eip712_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func TestEIP712Arrays(t *testing.T) {
t.Run(hex.EncodeToString(txn.GetHash()), func(t *testing.T) {
priv := acctesting.NewSECP256K1(t.Name())
sig := &protocol.Eip712TypedDataSignature{
ChainID: protocol.EthChainID("Kermit"),
PublicKey: eth.FromECDSAPub(&priv.PublicKey),
Signer: url.MustParse("acc://adi.acme/book/1"),
SignerVersion: 1,
Expand Down
5 changes: 5 additions & 0 deletions pkg/types/network/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package network
import (
"fmt"
"math"
"math/big"
"strings"

"github.com/robfig/cron/v3"
Expand All @@ -17,6 +18,10 @@ import (

var CronFormat = cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)

func (g *GlobalValues) ChainID() *big.Int {
return protocol.EthChainID(g.Network.NetworkName)
}

type globalValueMemos struct {
bvns []string
threshold map[string]uint64
Expand Down
13 changes: 6 additions & 7 deletions protocol/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,6 @@ func (e *EcdsaSha256Signature) Verify(sig Signature, msg Signable) bool {
* privateKey must be ecdsa
*/
func SignEip712TypedData(sig *Eip712TypedDataSignature, privateKey []byte, outer Signature, txn *Transaction) error {
priv, err := eth.ToECDSA(privateKey)
if err != nil {
return err
}
sig.PublicKey = eth.FromECDSAPub(&priv.PublicKey)

if outer == nil {
outer = sig
}
Expand All @@ -1208,6 +1202,11 @@ func SignEip712TypedData(sig *Eip712TypedDataSignature, privateKey []byte, outer
return err
}

priv, err := eth.ToECDSA(privateKey)
if err != nil {
return err
}

sig.TransactionHash = txn.Hash()
sig.Signature, err = eth.Sign(hash, priv)
return err
Expand All @@ -1226,7 +1225,7 @@ func (s *Eip712TypedDataSignature) GetSignerVersion() uint64 { return s.SignerVe
func (s *Eip712TypedDataSignature) GetTimestamp() uint64 { return s.Timestamp }

// GetPublicKeyHash returns the hash of PublicKey.
func (s *Eip712TypedDataSignature) GetPublicKeyHash() []byte { return doSha256(s.PublicKey) }
func (s *Eip712TypedDataSignature) GetPublicKeyHash() []byte { return ETHhash(s.PublicKey) }

// GetPublicKey returns PublicKey.
func (s *Eip712TypedDataSignature) GetPublicKey() []byte { return s.PublicKey }
Expand Down
24 changes: 23 additions & 1 deletion protocol/signature_eip712.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,38 @@
package protocol

import (
"crypto/sha256"
_ "embed"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"reflect"
"strings"

"gitlab.com/accumulatenetwork/accumulate/pkg/types/encoding"
)

var eip712Transaction *encoding.TypeDefinition

// EthChainID returns the Ethereum chain ID for an Accumulate network name.
func EthChainID(name string) *big.Int {
// This exists purely for documentation purposes
const MetaMask_MaxSafeChainID = 0xFFFFFFFFFFFEC

if strings.EqualFold(name, "mainnet") {
return big.NewInt(281) // 0x119
}

// Use the last 2 bytes of the hash
name = strings.ToLower(name)
hash := sha256.Sum256([]byte(name))
id := binary.BigEndian.Uint16(hash[len(hash)-2:])

// This will generate something like 0xBEEF0119
return big.NewInt(281 | int64(id)<<16)
}

func init() {
//need to handle the edge cases for Key page operations and data entries
encoding.RegisterUnion(NewKeyPageOperation)
Expand Down Expand Up @@ -118,5 +140,5 @@ func newEIP712Call(txn *Transaction, sig Signature) (*encoding.EIP712Call, error
delete(body.(map[string]any), "type")
jtx[txn.Body.Type().String()] = body

return encoding.NewEIP712Call(jtx, eip712Transaction)
return encoding.NewEIP712Call(jtx, inner.ChainID, eip712Transaction)
}
Loading

0 comments on commit b9c0b48

Please sign in to comment.