Skip to content

Commit 771609b

Browse files
authored
Feature l2to l1 (#75)
* [WIP]L2->L1 call updateState pass * [WIP]L2->L1 Adapt messages * [WIP]L2->L1 import fix * [WIP]L2->L1 debug for L1-L2 messages * [WIP]L2->L1 debug for L1-L2 messages * [WIP]L2->L1 debug for L1-L2 messages
1 parent a9726ba commit 771609b

13 files changed

+3976
-1227
lines changed

cairo/adapters/messages.go

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package adapters
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/NethermindEth/juno/core/felt"
7+
"github.com/ethereum/go-ethereum/common"
8+
9+
"math/big"
10+
)
11+
12+
type MessageL1ToL2 struct {
13+
// The address of the L1 contract sending the message.
14+
From common.Address `json:"from_address" validate:"required"`
15+
// The address of the L1 contract sending the message.
16+
To felt.Felt `json:"to_address" validate:"required"`
17+
Nonce felt.Felt `json:"nonce" validate:"required"`
18+
Selector felt.Felt `json:"entry_point_selector" validate:"required"`
19+
// The payload of the message.
20+
Payload []felt.Felt `json:"payload" validate:"required"`
21+
}
22+
23+
func (m *MessageL1ToL2) EncodeTo() ([]*big.Int, error) {
24+
var result []*big.Int
25+
26+
// From
27+
result = append(result, new(big.Int).SetBytes(m.From.Bytes()))
28+
29+
// To
30+
if m.To.IsZero() {
31+
return nil, errors.New("To field is zero (invalid)")
32+
}
33+
result = append(result, m.To.BigInt(new(big.Int)))
34+
35+
// Nonce
36+
result = append(result, m.Nonce.BigInt(new(big.Int)))
37+
38+
// Selector
39+
if m.Selector.IsZero() {
40+
return nil, errors.New("Selector field is zero (invalid)")
41+
}
42+
result = append(result, m.Selector.BigInt(new(big.Int)))
43+
44+
payloadSize := big.NewInt(int64(len(m.Payload)))
45+
result = append(result, payloadSize)
46+
// Payload
47+
for _, p := range m.Payload {
48+
if !p.IsZero() {
49+
result = append(result, p.BigInt(new(big.Int)))
50+
}
51+
}
52+
53+
return result, nil
54+
}
55+
56+
func (m *MessageL1ToL2) SizeInFelts() int {
57+
size := 0
58+
size += sizeOfCommonAddress(m.From)
59+
size += sizeOfFelt(m.To)
60+
size += sizeOfFelt(m.Selector)
61+
// for payload length field
62+
size += 1
63+
for _, p := range m.Payload {
64+
size += sizeOfFelt(p)
65+
}
66+
return size
67+
}
68+
69+
func sizeOfCommonAddress(addr common.Address) int {
70+
return 1
71+
}
72+
73+
func sizeOfFelt(f felt.Felt) int {
74+
return 1
75+
}
76+
77+
// MessageL2ToL1 L2ToL1Message
78+
type MessageL2ToL1 struct {
79+
From *felt.Felt `json:"from_address,omitempty"`
80+
To common.Address `json:"to_address"`
81+
82+
Payload []*felt.Felt `json:"payload"`
83+
}
84+
85+
func (m *MessageL2ToL1) EncodeTo() ([]*big.Int, error) {
86+
var result []*big.Int
87+
88+
// From
89+
if m.From != nil {
90+
result = append(result, m.From.BigInt(new(big.Int)))
91+
} else {
92+
return nil, errors.New("From field is nil")
93+
}
94+
95+
// To
96+
result = append(result, new(big.Int).SetBytes(m.To.Bytes()))
97+
fmt.Println("To:", new(big.Int).SetBytes(m.To.Bytes()))
98+
99+
payloadSize := big.NewInt(int64(len(m.Payload)))
100+
result = append(result, payloadSize)
101+
// Payload
102+
for _, p := range m.Payload {
103+
if p != nil {
104+
result = append(result, p.BigInt(new(big.Int)))
105+
}
106+
}
107+
108+
return result, nil
109+
}
110+
111+
func (m *MessageL2ToL1) SizeInFelts() int {
112+
size := 0
113+
114+
if m.From != nil {
115+
size += sizeOfFelt(*m.From)
116+
}
117+
118+
size += sizeOfCommonAddress(m.To)
119+
120+
// for payload length field
121+
size += 1
122+
123+
for _, p := range m.Payload {
124+
size += sizeOfFelt(*p)
125+
}
126+
127+
return size
128+
}

cairo/cairo.go

+148-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package cairo
22

33
import (
44
"encoding/hex"
5-
"itachi/cairo/config"
6-
"net/http"
7-
5+
"fmt"
86
junostate "github.com/NethermindEth/juno/blockchain"
97
"github.com/NethermindEth/juno/core"
108
"github.com/NethermindEth/juno/core/felt"
@@ -15,10 +13,21 @@ import (
1513
"github.com/NethermindEth/juno/utils"
1614
"github.com/NethermindEth/juno/vm"
1715
"github.com/ethereum/go-ethereum/common"
16+
"github.com/ethereum/go-ethereum/crypto"
17+
"github.com/ethereum/go-ethereum/ethclient"
1818
"github.com/sirupsen/logrus"
1919
"github.com/yu-org/yu/core/context"
2020
"github.com/yu-org/yu/core/tripod"
2121
"github.com/yu-org/yu/core/types"
22+
. "github.com/yu-org/yu/core/types"
23+
"github.com/yu-org/yu/utils/log"
24+
"itachi/cairo/adapters"
25+
"itachi/cairo/config"
26+
"itachi/cairo/l1/contract"
27+
snos_ouput "itachi/cairo/snos-ouput"
28+
"math/big"
29+
"net/http"
30+
"os"
2231
)
2332

2433
type Cairo struct {
@@ -30,6 +39,142 @@ type Cairo struct {
3039
network utils.Network
3140
}
3241

42+
func (c *Cairo) FinalizeBlock(block *Block) {
43+
logrus.SetOutput(os.Stdout)
44+
45+
if !c.cfg.EnableL2UpdateState {
46+
47+
return
48+
}
49+
50+
// for PrevStateRoot, get last finalized block
51+
compactBlock, err := c.Chain.LastFinalized()
52+
if err != nil {
53+
logrus.Fatal("get compactBlock for finalize block failed: ", err)
54+
}
55+
56+
var starkReceipt *rpc.TransactionReceipt
57+
txns := block.Txns.ToArray()
58+
messagesToL1 := make([]*rpc.MsgToL1, 0)
59+
for t := 0; t < len(txns); t++ {
60+
txn := txns[t]
61+
receipt, _ := c.TxDB.GetReceipt(txn.TxnHash)
62+
receiptExtraByt := receipt.Extra
63+
err := encoder.Unmarshal(receiptExtraByt, &starkReceipt)
64+
if err != nil {
65+
// handle error
66+
logrus.Fatal("unmarshal starkReceipt failed: ", err)
67+
} else {
68+
messagesToL1 = append(messagesToL1, starkReceipt.MessagesSent...)
69+
}
70+
}
71+
// Adapt
72+
messageL2ToL1 := make([]*adapters.MessageL2ToL1, len(messagesToL1))
73+
for idx, msg := range messagesToL1 {
74+
messageL2ToL1[idx] = &adapters.MessageL2ToL1{
75+
From: msg.From,
76+
To: msg.To,
77+
Payload: msg.Payload,
78+
}
79+
}
80+
81+
// todo messagesToL2 := make([]*rpc.MsgFromL1, 0)
82+
messagesToL2 := make([]*adapters.MessageL1ToL2, 0)
83+
//for t := 0; t < len(txns); t++ {
84+
// txn := txns[t]
85+
//
86+
//}
87+
88+
num := uint64(block.Height)
89+
// init StarknetOsOutput by block
90+
snOsOutput := &snos_ouput.StarknetOsOutput{
91+
PrevStateRoot: new(felt.Felt).SetBytes(compactBlock.StateRoot.Bytes()),
92+
NewStateRoot: new(felt.Felt).SetBytes(block.StateRoot.Bytes()),
93+
BlockNumber: new(felt.Felt).SetUint64(num),
94+
BlockHash: new(felt.Felt).SetBytes(block.Hash.Bytes()),
95+
ConfigHash: new(felt.Felt).SetUint64(0),
96+
KzgDA: new(felt.Felt).SetUint64(0),
97+
MessagesToL1: messageL2ToL1,
98+
MessagesToL2: messagesToL2,
99+
}
100+
// cairoState.UpdateStarknetOsOutput(snOsOutput)
101+
fmt.Printf("snOsOutput:\n%+v\n", snOsOutput)
102+
103+
// 新旧状态根对比
104+
if snOsOutput.PrevStateRoot.String() != snOsOutput.NewStateRoot.String() {
105+
// send snOsOutput to L1 chain
106+
c.ethCallUpdateState(c.cairoState, snOsOutput)
107+
}
108+
109+
log.DoubleLineConsole.Info(fmt.Sprintf("Cairo Tripod finalize block, height=%d, hash=%s", block.Height, block.Hash.String()))
110+
111+
}
112+
113+
func (c *Cairo) ethCallUpdateState(cairoState *CairoState, snOsOutput *snos_ouput.StarknetOsOutput) {
114+
115+
client, err := ethclient.Dial(c.cfg.EthRpcUrl)
116+
if err != nil {
117+
fmt.Println("init client failed: ", err)
118+
}
119+
120+
starknetCore, err := contract.NewStarknetCore(common.HexToAddress(c.cfg.EthCoreContractAddress), client)
121+
if err != nil {
122+
fmt.Println("init starknetCore failed: ", err)
123+
return
124+
}
125+
126+
// encode snOsOutput to []*big.Int
127+
programOutput, err := snOsOutput.EncodeTo()
128+
if err != nil {
129+
fmt.Println("encode snOsOutput failed: ", err)
130+
return
131+
}
132+
133+
// compute onchainDataHash and onchainDataSize
134+
onchainDataHash, onchainDataSize, err := calculateOnchainData(programOutput)
135+
if err != nil {
136+
fmt.Println("calculate onchain data failed: ", err)
137+
return
138+
}
139+
140+
chainID := big.NewInt(c.cfg.ChainID)
141+
privateKeyHex := c.cfg.EthPrivateKey
142+
address := c.cfg.EthWalletAddress
143+
gasLimit := c.cfg.GasLimit
144+
auth, err := CreateAuth(client, privateKeyHex, address, gasLimit, chainID)
145+
if err != nil {
146+
fmt.Println("create auth failed: ", err)
147+
return
148+
}
149+
150+
// call updateState
151+
tx, err := starknetCore.UpdateState(auth, programOutput, onchainDataHash, onchainDataSize)
152+
if err != nil {
153+
fmt.Println("call updateState failed: %s", err)
154+
return
155+
}
156+
157+
// retrieve transaction hash and print.
158+
txHash := tx.Hash()
159+
log.DoubleLineConsole.Info("update state, tx hash: %s", txHash.Hex())
160+
fmt.Println("https://sepolia.etherscan.io/tx/" + tx.Hash().Hex())
161+
162+
}
163+
164+
func calculateOnchainData(programOutput []*big.Int) (*big.Int, *big.Int, error) {
165+
var data []byte
166+
for _, output := range programOutput {
167+
data = append(data, output.Bytes()...)
168+
}
169+
onchainDataHash := crypto.Keccak256Hash(data)
170+
onchainDataHashBig := new(big.Int).SetBytes(onchainDataHash.Bytes())
171+
172+
// compute onchainDataSize
173+
onchainDataSize := new(big.Int).SetInt64(int64(len(programOutput)))
174+
175+
return onchainDataHashBig, onchainDataSize, nil
176+
}
177+
33178
func NewCairo(cfg *config.Config) *Cairo {
34179
state, err := NewCairoState(cfg)
35180
if err != nil {

cairo/config/config.go

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ type Config struct {
5050
EthClientAddress string `toml:"eth_client_address"`
5151
EthContractAddress string `toml:"eth_contract_address"`
5252

53+
// L2 eth client configs
54+
EnableL2UpdateState bool `toml:"enable_l2_update_state"`
55+
EthRpcUrl string `toml:"eth_rpc_url"`
56+
ChainID int64 `toml:"chain_id"`
57+
GasLimit uint64 `toml:"gas_limit"`
58+
EthCoreContractAddress string `toml:"eth_core_contract_address"`
59+
EthWalletAddress string `toml:"eth_wallet_address"`
60+
EthPrivateKey string `toml:"eth_private_key"`
61+
5362
EnablePprof bool `toml:"enable_pprof"`
5463
PprofAddr string `toml:"pprof_addr"`
5564
}

cairo/eth_send.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package cairo
2+
3+
import (
4+
"context"
5+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/ethereum/go-ethereum/common/hexutil"
8+
"github.com/ethereum/go-ethereum/crypto"
9+
"github.com/ethereum/go-ethereum/ethclient"
10+
"math/big"
11+
)
12+
13+
func CreateAuth(client *ethclient.Client, privateKeyHex string, address string, gasLimit uint64, chainID *big.Int) (*bind.TransactOpts, error) {
14+
// Get nonce
15+
nonce, err := client.PendingNonceAt(context.Background(), common.HexToAddress(address))
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
// Get gas price
21+
gasPrice, err := client.SuggestGasPrice(context.Background())
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
// Ensure private key string has 0x prefix
27+
if !has0xPrefix(privateKeyHex) {
28+
privateKeyHex = "0x" + privateKeyHex
29+
}
30+
31+
// Decode private key
32+
rawPrivateKey, err := hexutil.Decode(privateKeyHex)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
// Convert to ECDSA private key
38+
privateKey, err := crypto.ToECDSA(rawPrivateKey)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
// Create auth object
44+
auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
45+
auth.Nonce = big.NewInt(int64(nonce))
46+
auth.Value = big.NewInt(0) // 0 value for no ether transfer
47+
auth.GasLimit = gasLimit
48+
auth.GasPrice = gasPrice
49+
50+
return auth, nil
51+
}
52+
53+
// Helper function to check if string has 0x prefix
54+
func has0xPrefix(s string) bool {
55+
return len(s) >= 2 && s[0:2] == "0x"
56+
}

0 commit comments

Comments
 (0)