Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions qkc/types/cross_shard_transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2026-2027, QuarkChain.

// Cross-shard transactions follow pyquarkchain-compatible QKC wire encoding.

package types

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/qkc/account"
"github.com/ethereum/go-ethereum/qkc/serialize"
)

const crossShardTransactionListVersion = uint32(1)

// CrossShardTransactionDeposit matches pyquarkchain's current
// CrossShardTransactionDeposit.FIELDS order.
type CrossShardTransactionDeposit struct {
TxHash common.Hash
From account.Address
To account.Address
Value *serialize.Uint256
GasPrice *serialize.Uint256
GasTokenID uint64
TransferTokenID uint64
GasRemained *serialize.Uint256
MessageData []byte `bytesizeofslicelen:"4"`
CreateContract bool
IsFromRootChain bool
RefundRate uint8
}

// CrossShardTransactionList is pyquarkchain's CrossShardTransactionList
// version 1 wire type.
type CrossShardTransactionList struct {
TXList []*CrossShardTransactionDeposit
}

func NewCrossShardTransactionList(txList []*CrossShardTransactionDeposit) *CrossShardTransactionList {
if txList == nil {
txList = make([]*CrossShardTransactionDeposit, 0)
}
return &CrossShardTransactionList{
TXList: txList,
}
}

// Serialize writes pyquarkchain CrossShardTransactionList.FIELDS order:
// tx_list followed by version(uint32).
func (c *CrossShardTransactionList) Serialize(w *[]byte) error {
if c == nil {
return fmt.Errorf("nil cross-shard transaction list")
}
if err := serialize.SerializeWithTags(w, c.TXList, serialize.Tags{ByteSizeOfSliceLen: 4}); err != nil {
return err
}
return serialize.Serialize(w, crossShardTransactionListVersion)
}

// Deserialize reads the current pyquarkchain CrossShardTransactionList version.
func (c *CrossShardTransactionList) Deserialize(bb *serialize.ByteBuffer) error {
if c == nil {
return fmt.Errorf("nil cross-shard transaction list")
}
var txList []*CrossShardTransactionDeposit
if err := serialize.DeserializeWithTags(bb, &txList, serialize.Tags{ByteSizeOfSliceLen: 4}); err != nil {
Comment thread
ping-ke marked this conversation as resolved.
return err
}
var version uint32
Comment thread
ping-ke marked this conversation as resolved.
if err := serialize.Deserialize(bb, &version); err != nil {
return err
}
if version != crossShardTransactionListVersion {
return fmt.Errorf("unsupported cross-shard transaction list version %d", version)
}
c.TXList = txList
return nil
}
106 changes: 106 additions & 0 deletions qkc/types/cross_shard_transaction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2026-2027, QuarkChain.

// Cross-shard transaction tests exercise pyquarkchain-compatible QKC wire bytes.

package types

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/qkc/account"
"github.com/ethereum/go-ethereum/qkc/serialize"
"github.com/stretchr/testify/assert"
)

func TestCrossShardTransactionList(t *testing.T) {
c1 := NewCrossShardTransactionList(nil)
for index := uint64(0); index < 100; index++ {
u256 := new(serialize.Uint256)
u256.Value = new(big.Int).SetUint64(index)
c1.TXList = append(c1.TXList, &CrossShardTransactionDeposit{
TxHash: common.BigToHash(new(big.Int).SetUint64(index)),
From: account.Address{
Recipient: common.BigToAddress(new(big.Int).SetUint64(2)),
FullShardKey: 2,
},
To: account.Address{
Recipient: common.BigToAddress(new(big.Int).SetUint64(3)),
FullShardKey: 3,
},
Value: u256,
GasPrice: u256,
GasTokenID: 123,
TransferTokenID: 456,
IsFromRootChain: false,
GasRemained: u256,
MessageData: []byte{},
CreateContract: true,
RefundRate: uint8(index),
})
}

data, err := serialize.SerializeToBytes(c1)
assert.NoError(t, err)

d1 := NewCrossShardTransactionList(nil)
err = serialize.DeserializeFromBytes(data, d1)
assert.NoError(t, err)
for k, v := range c1.TXList {
assert.Equal(t, v.TxHash, (*d1).TXList[k].TxHash)
assert.Equal(t, v.From, (*d1).TXList[k].From)
assert.Equal(t, v.To, (*d1).TXList[k].To)
assert.Equal(t, v.Value.Value.Uint64(), (*d1).TXList[k].Value.Value.Uint64())
assert.Equal(t, v.GasPrice.Value.Uint64(), (*d1).TXList[k].GasPrice.Value.Uint64())
assert.Equal(t, v.GasTokenID, (*d1).TXList[k].GasTokenID)
assert.Equal(t, v.TransferTokenID, (*d1).TXList[k].TransferTokenID)
assert.Equal(t, v.IsFromRootChain, (*d1).TXList[k].IsFromRootChain)
assert.Equal(t, v.GasRemained.Value.Uint64(), (*d1).TXList[k].GasRemained.Value.Uint64())
assert.Equal(t, v.MessageData, (*d1).TXList[k].MessageData)
assert.Equal(t, v.CreateContract, (*d1).TXList[k].CreateContract)
assert.Equal(t, uint8(k), (*d1).TXList[k].RefundRate)
}

}

func TestCrossShardTransactionListPyquarkchainGolden(t *testing.T) {
assertSerialized := func(t *testing.T, list *CrossShardTransactionList, expected string) {
t.Helper()
encoded, err := serialize.SerializeToBytes(list)
assert.NoError(t, err)
assert.Equal(t, expected, common.Bytes2Hex(encoded))

var decoded CrossShardTransactionList
err = serialize.DeserializeFromBytes(common.FromHex(expected), &decoded)
assert.NoError(t, err)
reencoded, err := serialize.SerializeToBytes(&decoded)
assert.NoError(t, err)
assert.Equal(t, expected, common.Bytes2Hex(reencoded))
}

// Generated by pyquarkchain's CrossShardTransactionList.serialize().
assertSerialized(t, NewCrossShardTransactionList(nil), "0000000000000001")

u256 := func(value int64) *serialize.Uint256 { return &serialize.Uint256{Value: big.NewInt(value)} }
assertSerialized(t, NewCrossShardTransactionList([]*CrossShardTransactionDeposit{{
TxHash: common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111"),
From: account.Address{Recipient: common.HexToAddress("0x2222222222222222222222222222222222222222"), FullShardKey: 3},
To: account.Address{Recipient: common.HexToAddress("0x3333333333333333333333333333333333333333"), FullShardKey: 4},
Value: u256(5),
GasPrice: u256(6),
GasTokenID: 7,
TransferTokenID: 8,
GasRemained: u256(9),
MessageData: []byte{0xaa, 0xbb},
CreateContract: true,
IsFromRootChain: true,
RefundRate: 10,
}}), "0000000111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222000000033333333333333333333333333333333333333333000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000070000000000000008000000000000000000000000000000000000000000000000000000000000000900000002aabb01010a00000001")
}

func TestCrossShardTransactionListRejectsUnsupportedVersion(t *testing.T) {
var list CrossShardTransactionList
err := serialize.DeserializeFromBytes(common.FromHex("0000000000000000"), &list)
assert.Error(t, err)
}
Loading