Skip to content

Commit c9f8849

Browse files
EgeCanerwojciechos
andauthored
feat: 0.14.0 preconfirmed block (#2954)
* starknet preconfirmed block type * core preconfirmed * test feeder gw supports get_preconfirmed_block * feeder endpoints and preconfirmed block adapter with tests * extract statediff into its own type * fix message * remove blocknumber assignment since it is already done in adapter * remove unnecessary functions * []*Transaction to []Transaction * move preconfirmed block status check to adapter * return PreConfirmed as value * address requested changes * bump SupportedStarknetVersion * extract feeder test client into its own file * update starknet-js version * ci: upgrade starknet.js tests --------- Co-authored-by: wojo <wojciech.zieba@gmail.com>
1 parent d9a5b32 commit c9f8849

File tree

20 files changed

+1763
-223
lines changed

20 files changed

+1763
-223
lines changed

.github/workflows/deploy-and-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ jobs:
105105
with:
106106
test_mode: ${{ inputs.test_mode }}
107107
secrets:
108-
TEST_RPC_URL: ${{ secrets.RPC_URL }}/v0_7?apikey=${{ secrets.AUTH_TOKEN }}
108+
TEST_RPC_URL: ${{ secrets.RPC_URL }}/v0_8?apikey=${{ secrets.AUTH_TOKEN }}
109109
TEST_ACCOUNT_ADDRESS: ${{ secrets.TEST_ACCOUNT_ADDRESS }}
110110
TEST_ACCOUNT_PRIVATE_KEY: ${{ secrets.TEST_ACCOUNT_PRIVATE_KEY }}
111111

.github/workflows/starknet-js-tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
uses: actions/checkout@v4
2424
with:
2525
repository: starknet-io/starknet.js
26-
ref: v6.14.1
26+
ref: v7.6.2
2727

2828
- name: Setup Node.js
2929
uses: actions/setup-node@v4
@@ -48,3 +48,4 @@ jobs:
4848
TEST_RPC_URL: ${{ secrets.TEST_RPC_URL }}
4949
TEST_ACCOUNT_ADDRESS: ${{ secrets.TEST_ACCOUNT_ADDRESS }}
5050
TEST_ACCOUNT_PRIVATE_KEY: ${{ secrets.TEST_ACCOUNT_PRIVATE_KEY }}
51+
TX_VERSION: v3

adapters/sn2core/sn2core.go

Lines changed: 121 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -357,35 +357,49 @@ func AdaptCairo0Class(response *starknet.Cairo0Definition) (core.Class, error) {
357357
}
358358

359359
func AdaptStateUpdate(response *starknet.StateUpdate) (*core.StateUpdate, error) {
360+
stateDiff, err := AdaptStateDiff(&response.StateDiff)
361+
if err != nil {
362+
return nil, err
363+
}
364+
365+
return &core.StateUpdate{
366+
BlockHash: response.BlockHash,
367+
NewRoot: response.NewRoot,
368+
OldRoot: response.OldRoot,
369+
StateDiff: stateDiff,
370+
}, nil
371+
}
372+
373+
func AdaptStateDiff(response *starknet.StateDiff) (*core.StateDiff, error) {
360374
stateDiff := new(core.StateDiff)
361-
stateDiff.DeclaredV0Classes = response.StateDiff.OldDeclaredContracts
375+
stateDiff.DeclaredV0Classes = response.OldDeclaredContracts
362376

363-
stateDiff.DeclaredV1Classes = make(map[felt.Felt]*felt.Felt, len(response.StateDiff.DeclaredClasses))
364-
for _, declaredV1Class := range response.StateDiff.DeclaredClasses {
377+
stateDiff.DeclaredV1Classes = make(map[felt.Felt]*felt.Felt, len(response.DeclaredClasses))
378+
for _, declaredV1Class := range response.DeclaredClasses {
365379
stateDiff.DeclaredV1Classes[*declaredV1Class.ClassHash] = declaredV1Class.CompiledClassHash
366380
}
367381

368-
stateDiff.ReplacedClasses = make(map[felt.Felt]*felt.Felt, len(response.StateDiff.ReplacedClasses))
369-
for _, replacedClass := range response.StateDiff.ReplacedClasses {
382+
stateDiff.ReplacedClasses = make(map[felt.Felt]*felt.Felt, len(response.ReplacedClasses))
383+
for _, replacedClass := range response.ReplacedClasses {
370384
stateDiff.ReplacedClasses[*replacedClass.Address] = replacedClass.ClassHash
371385
}
372386

373-
stateDiff.DeployedContracts = make(map[felt.Felt]*felt.Felt, len(response.StateDiff.DeployedContracts))
374-
for _, deployedContract := range response.StateDiff.DeployedContracts {
387+
stateDiff.DeployedContracts = make(map[felt.Felt]*felt.Felt, len(response.DeployedContracts))
388+
for _, deployedContract := range response.DeployedContracts {
375389
stateDiff.DeployedContracts[*deployedContract.Address] = deployedContract.ClassHash
376390
}
377391

378-
stateDiff.Nonces = make(map[felt.Felt]*felt.Felt, len(response.StateDiff.Nonces))
379-
for addrStr, nonce := range response.StateDiff.Nonces {
392+
stateDiff.Nonces = make(map[felt.Felt]*felt.Felt, len(response.Nonces))
393+
for addrStr, nonce := range response.Nonces {
380394
addr, err := new(felt.Felt).SetString(addrStr)
381395
if err != nil {
382396
return nil, err
383397
}
384398
stateDiff.Nonces[*addr] = nonce
385399
}
386400

387-
stateDiff.StorageDiffs = make(map[felt.Felt]map[felt.Felt]*felt.Felt, len(response.StateDiff.StorageDiffs))
388-
for addrStr, diffs := range response.StateDiff.StorageDiffs {
401+
stateDiff.StorageDiffs = make(map[felt.Felt]map[felt.Felt]*felt.Felt, len(response.StorageDiffs))
402+
for addrStr, diffs := range response.StorageDiffs {
389403
addr, err := new(felt.Felt).SetString(addrStr)
390404
if err != nil {
391405
return nil, err
@@ -397,12 +411,102 @@ func AdaptStateUpdate(response *starknet.StateUpdate) (*core.StateUpdate, error)
397411
}
398412
}
399413

400-
return &core.StateUpdate{
401-
BlockHash: response.BlockHash,
402-
NewRoot: response.NewRoot,
403-
OldRoot: response.OldRoot,
404-
StateDiff: stateDiff,
405-
}, nil
414+
return stateDiff, nil
415+
}
416+
417+
func AdaptPreConfirmedBlock(response *starknet.PreConfirmedBlock, number uint64) (core.PreConfirmed, error) {
418+
if response == nil {
419+
return core.PreConfirmed{}, errors.New("nil preconfirmed block")
420+
}
421+
422+
if response.Status != "PRE_CONFIRMED" {
423+
return core.PreConfirmed{}, errors.New("invalid status for pre_confirmed block")
424+
}
425+
426+
var adaptedStateDiff *core.StateDiff
427+
var err error
428+
429+
txStateDiffs := make([]*core.StateDiff, 0, len(response.TransactionStateDiffs))
430+
for _, stateDiff := range response.TransactionStateDiffs {
431+
if stateDiff == nil {
432+
break
433+
}
434+
435+
if adaptedStateDiff, err = AdaptStateDiff(stateDiff); err != nil {
436+
return core.PreConfirmed{}, err
437+
}
438+
txStateDiffs = append(txStateDiffs, adaptedStateDiff)
439+
}
440+
441+
preConfirmedTxCount := len(txStateDiffs)
442+
443+
txns := make([]core.Transaction, preConfirmedTxCount)
444+
for i := range preConfirmedTxCount {
445+
txns[i], err = AdaptTransaction(&response.Transactions[i])
446+
if err != nil {
447+
return core.PreConfirmed{}, err
448+
}
449+
}
450+
451+
rawTxCount := len(response.Transactions)
452+
candidateTxs := make([]core.Transaction, rawTxCount-preConfirmedTxCount)
453+
454+
for i := range rawTxCount - preConfirmedTxCount {
455+
candidateTxs[i], err = AdaptTransaction(&response.Transactions[preConfirmedTxCount+i])
456+
if err != nil {
457+
return core.PreConfirmed{}, err
458+
}
459+
}
460+
461+
receipts := make([]*core.TransactionReceipt, preConfirmedTxCount)
462+
eventCount := uint64(0)
463+
for i, receipt := range response.Receipts[:preConfirmedTxCount] {
464+
receipts[i] = AdaptTransactionReceipt(receipt)
465+
eventCount += uint64(len(receipt.Events))
466+
}
467+
468+
// Squash per-tx state updates
469+
stateDiff := core.EmptyStateDiff()
470+
for _, txStateDiff := range txStateDiffs {
471+
stateDiff.Merge(txStateDiff)
472+
}
473+
474+
stateUpdate := core.StateUpdate{
475+
BlockHash: nil,
476+
NewRoot: nil,
477+
// Must be set to previous global state root, when have access to latest header
478+
OldRoot: nil,
479+
StateDiff: &stateDiff,
480+
}
481+
482+
adaptedBlock := &core.Block{
483+
// https://github.com/starkware-libs/starknet-specs/blob/9377851884da5c81f757b6ae0ed47e84f9e7c058/api/starknet_api_openrpc.json#L1636
484+
Header: &core.Header{
485+
Number: number,
486+
SequencerAddress: response.SequencerAddress,
487+
// Not required in spec but useful
488+
TransactionCount: uint64(len(txns)),
489+
// Not required in spec but useful
490+
EventCount: eventCount,
491+
Timestamp: response.Timestamp,
492+
ProtocolVersion: response.Version,
493+
// Not required in spec but useful
494+
EventsBloom: core.EventsBloom(receipts),
495+
L1GasPriceETH: response.L1GasPrice.PriceInWei,
496+
L1GasPriceSTRK: response.L1GasPrice.PriceInFri,
497+
L1DAMode: core.L1DAMode(response.L1DAMode),
498+
L1DataGasPrice: (*core.GasPrice)(response.L1DataGasPrice),
499+
L2GasPrice: (*core.GasPrice)(response.L2GasPrice),
500+
// Following fields are nil for pre_confirmed block
501+
Hash: nil,
502+
ParentHash: nil,
503+
GlobalStateRoot: nil,
504+
Signatures: nil,
505+
},
506+
Transactions: txns,
507+
Receipts: receipts,
508+
}
509+
return core.NewPreConfirmed(adaptedBlock, &stateUpdate, txStateDiffs, candidateTxs), nil
406510
}
407511

408512
func safeFeltToUint64(f *felt.Felt) uint64 {

adapters/sn2core/sn2core_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,139 @@ func TestAdaptCompiledClass(t *testing.T) {
623623
require.NoError(t, err)
624624
assert.Nil(t, result)
625625
}
626+
627+
func TestAdaptPreConfirmed(t *testing.T) {
628+
n := &utils.SepoliaIntegration
629+
client := feeder.NewTestClient(t, n)
630+
emptyPreConfirmedBlock := uint64(1201960)
631+
blockWithCandidates := uint64(1204672)
632+
blockWithNoCandidates := uint64(1204673)
633+
blockFullOfCandidates := uint64(1204674)
634+
635+
type preConfirmedTest struct {
636+
description string
637+
blockNumber uint64
638+
}
639+
640+
tests := []preConfirmedTest{
641+
{
642+
description: "PreConfirmedBlock when there is no candidates",
643+
blockNumber: blockWithNoCandidates,
644+
},
645+
{
646+
description: "PreConfirmedBlock with candidates",
647+
blockNumber: blockWithCandidates,
648+
},
649+
{
650+
description: "PreConfirmedBlock when full of candidates",
651+
blockNumber: blockFullOfCandidates,
652+
},
653+
{
654+
description: "PreConfirmedBlock when empty",
655+
blockNumber: emptyPreConfirmedBlock,
656+
},
657+
}
658+
659+
for _, test := range tests {
660+
response, err := client.PreConfirmedBlock(t.Context(), strconv.FormatUint(test.blockNumber, 10))
661+
require.NoError(t, err)
662+
663+
expectedEventCount, expectedPreConfirmedTxCount := countEventsAndTxs(response.Receipts)
664+
expectedCandidateCount := len(response.Transactions) - expectedPreConfirmedTxCount
665+
666+
adapted, err := sn2core.AdaptPreConfirmedBlock(response, test.blockNumber)
667+
require.NoError(t, err)
668+
669+
assertPreConfirmedBlockBasics(t, &adapted, test.blockNumber, response, expectedPreConfirmedTxCount, expectedCandidateCount, expectedEventCount)
670+
assertPreConfirmedBlockReceipts(t, response.Receipts, adapted.Block.Receipts, expectedPreConfirmedTxCount)
671+
assertPreConfirmedBlockGasPrices(t, response, adapted.Block)
672+
assertCandidateTxs(t, response.Transactions, adapted.CandidateTxs, expectedPreConfirmedTxCount)
673+
assert.Equal(t, len(adapted.TransactionStateDiffs), expectedPreConfirmedTxCount)
674+
}
675+
}
676+
677+
func assertPreConfirmedBlockBasics(
678+
t *testing.T,
679+
preConfirmed *core.PreConfirmed,
680+
blockNum uint64,
681+
response *starknet.PreConfirmedBlock,
682+
expectedTxCount int,
683+
expectedCandidateCount int,
684+
expectedEventCount uint64,
685+
) {
686+
assert.Equal(t, blockNum, preConfirmed.Block.Number)
687+
assert.NotNil(t, preConfirmed.Block.EventsBloom)
688+
assert.Empty(t, preConfirmed.Block.Hash)
689+
assert.Empty(t, preConfirmed.Block.ParentHash)
690+
assert.Empty(t, preConfirmed.Block.GlobalStateRoot)
691+
assert.Empty(t, preConfirmed.Block.Signatures)
692+
assert.Empty(t, preConfirmed.StateUpdate.NewRoot)
693+
assert.Empty(t, preConfirmed.StateUpdate.BlockHash)
694+
assert.Equal(t, response.Timestamp, preConfirmed.Block.Timestamp)
695+
assert.Equal(t, expectedTxCount, len(preConfirmed.Block.Transactions))
696+
assert.Equal(t, expectedCandidateCount, len(preConfirmed.CandidateTxs))
697+
assert.Equal(t, uint64(expectedTxCount), preConfirmed.Block.TransactionCount)
698+
assert.Equal(t, expectedEventCount, preConfirmed.Block.EventCount)
699+
assert.Equal(t, response.Version, preConfirmed.Block.ProtocolVersion)
700+
assert.Equal(t, response.L1GasPrice.PriceInFri, preConfirmed.Block.L1GasPriceSTRK)
701+
assert.Equal(t, response.L1GasPrice.PriceInWei, preConfirmed.Block.L1GasPriceETH)
702+
}
703+
704+
func assertCandidateTxs(
705+
t *testing.T,
706+
transactions []starknet.Transaction,
707+
candidateTxs []core.Transaction,
708+
offset int,
709+
) {
710+
t.Helper()
711+
712+
for i, txn := range candidateTxs {
713+
adaptedTx, err := sn2core.AdaptTransaction(&transactions[offset+i])
714+
require.NoError(t, err)
715+
assert.Equal(t, adaptedTx, txn)
716+
}
717+
}
718+
719+
func assertPreConfirmedBlockGasPrices(t *testing.T, response *starknet.PreConfirmedBlock, block *core.Block) {
720+
t.Helper()
721+
assert.Equal(t, response.L1DataGasPrice.PriceInFri, block.L1DataGasPrice.PriceInFri)
722+
assert.Equal(t, response.L1DataGasPrice.PriceInWei, block.L1DataGasPrice.PriceInWei)
723+
assert.Equal(t, response.L2GasPrice.PriceInFri, block.L2GasPrice.PriceInFri)
724+
assert.Equal(t, response.L2GasPrice.PriceInWei, block.L2GasPrice.PriceInWei)
725+
}
726+
727+
func countEventsAndTxs(receipts []*starknet.TransactionReceipt) (uint64, int) {
728+
var evCount uint64
729+
txCount := 0
730+
for _, r := range receipts {
731+
if r == nil {
732+
break
733+
}
734+
evCount += uint64(len(r.Events))
735+
txCount++
736+
}
737+
return evCount, txCount
738+
}
739+
740+
func assertPreConfirmedBlockReceipts(
741+
t *testing.T,
742+
expectedReceipts []*starknet.TransactionReceipt,
743+
blockReceipts []*core.TransactionReceipt,
744+
expectedPreConfirmedTxCount int,
745+
) {
746+
t.Helper()
747+
if assert.Equal(t, expectedPreConfirmedTxCount, len(blockReceipts)) {
748+
for i, actual := range blockReceipts {
749+
expected := expectedReceipts[i]
750+
assert.Equal(t, expected.ExecutionStatus == starknet.Reverted, actual.Reverted)
751+
assert.Equal(t, expected.RevertError, actual.RevertReason)
752+
if expected.ExecutionResources != nil {
753+
assert.Equal(
754+
t,
755+
(*core.DataAvailability)(expected.ExecutionResources.DataAvailability),
756+
actual.ExecutionResources.DataAvailability,
757+
)
758+
}
759+
}
760+
}
761+
}

blockchain/blockchain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ type Reader interface {
5252

5353
var (
5454
ErrParentDoesNotMatchHead = errors.New("block's parent hash does not match head block hash")
55-
SupportedStarknetVersion = semver.MustParse("0.13.3")
55+
SupportedStarknetVersion = semver.MustParse("0.14.0")
5656
)
5757

5858
func CheckBlockVersion(protocolVersion string) error {

0 commit comments

Comments
 (0)