Skip to content

qkc/types: add transactions and signing - #35

Open
blockchaindevsh wants to merge 14 commits into
goshard/basefrom
qkc-3-types-04-transactions
Open

qkc/types: add transactions and signing#35
blockchaindevsh wants to merge 14 commits into
goshard/basefrom
qkc-3-types-04-transactions

Conversation

@blockchaindevsh

Copy link
Copy Markdown

4th PR of QuarkChain/pm#155.

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

func bigIntToHex(data *big.Int) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename solidity_abi_utils.go seems a bit unrelated to the transaction-related changes. Maybe we should choose a more descriptive name.

Also, are all the functions in this file used by the transaction module?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I renamed this to transaction_typed_hash.go and renamed the test accordingly in a53fef1.

I also checked the helpers here: they are all used by the transaction signing path via EvmTransaction.typedHash() -> typedSignatureHash(evmTxToTypedData(tx)). The tests cover both the typed-data conversion and intermediate packed/hash outputs.

Comment thread qkc/types/cross_shard_transaction.go Outdated
TXList []*CrossShardTransactionDeposit `bytesizeofslicelen:"4"`
}

type CrossShardTransactionDepositList struct {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot find CrossShardTransactionDepositList in pyquarkchain. The corresponding Python type seems to be CrossShardTransactionList.

Could you confirm this type is intended to match Python CrossShardTransactionList(version=1)? The field order looks different: Python serializes tx_list followed by version(uint32), while this implementation appears to encode version into the size prefix (bytes[0] = 1).

@blockchaindevsh blockchaindevsh Jul 23, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right. The existing CrossShardTransactionDepositList was based on goquarkchain’s packed-prefix format and does not match pyquarkchain.

I replaced it with CrossShardTransactionList in 1e3f38e), matching Python v1 exactly: tx_list with its normal 4-byte count prefix, followed by version as a trailing uint32(1). I also removed the unused legacy-v0 list path and added Python-generated golden vectors for empty and one-entry v1 lists, plus an unsupported-version test.

@blockchaindevsh
blockchaindevsh changed the base branch from qkc-3-types-03-logs-receipts to goshard/base July 23, 2026 11:35
@blockchaindevsh
blockchaindevsh force-pushed the qkc-3-types-04-transactions branch from bd79a6f to 1e3f38e Compare July 23, 2026 11:35
Comment thread qkc/types/transaction_signing.go Outdated

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *EvmTransaction) common.Hash {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Hash function may also need to branch by tx version, as in Sender.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 543f466.

Comment thread qkc/types/transaction_typed_hash.go Outdated
func data(tx []map[string]string) []string {
t := make([]string, 0)
for _, v := range tx {
if v["types"] == "bytes" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key in the map is "type" instead of "types".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6245898 .

Comment thread qkc/types/cross_shard_transaction.go Outdated
}

type CrossShardTransactionDeposit struct {
CrossShardTransactionDepositV0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the CrossShardTransactionDepositV0 type and merge it into CrossShardTransactionDeposit

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0734b84.

Comment thread qkc/types/transaction.go Outdated
Comment on lines +39 to +40
updated bool
hash atomic.Value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there three copies of hashes in Transaction, EvmTransaction, and txdata?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the one in txdata in 77ed4e4, now it aligns with pyquarkchain:

EvmTransaction.hash is the RLP hash of the inner EVM transaction, while Transaction.hash is the hash of the outer typed QKC envelope.

Comment thread qkc/types/transaction.go

func (e *EvmTransaction) SetFromFullShardKey(data uint32) {
t := Uint32(data)
e.data.FromFullShardKey = &t

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do some setters set updated to true while others don't? What does updated indicate?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced the vague updated flag with hashDirty in 754a0de. It now tracks changes to fields encoded in the inner EVM RLP hash, and all relevant setters (SetGas, SetFromFullShardKey, SetNonce, and SetVRS) mark it dirty. Hash() recomputes once and then caches the new value.
SetSender does not invalidate it because it only updates the derived sender cache, which is not part of the RLP payload. Added regression coverage for each hash-affecting setter.

Comment thread qkc/types/transaction.go

func (e *EvmTransaction) SetFromFullShardKey(data uint32) {
t := Uint32(data)
e.data.FromFullShardKey = &t

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do some setters set updated to true while others don't? What does updated indicate?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above.

Comment thread qkc/types/transaction.go Outdated
func (tx *EvmTransaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *EvmTransaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
func (tx *EvmTransaction) Nonce() uint64 { return tx.data.AccountNonce }
func (tx *EvmTransaction) CheckNonce() bool { return true }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this function?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in a784c9d.

Comment thread qkc/types/transaction.go Outdated
//
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
func NewTransactionsByPriceAndNonce(signer Signer, txs map[account.Recipient]Transactions) (*TransactionsByPriceAndNonce, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Geth includes this function in the miner module, not in this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 3221b8b, it can be introduced in the future QKC mining or transaction-pool integration.

Comment thread qkc/types/transaction.go Outdated
// Message is a fully derived transaction and implements core.Message
//
// NOTE: In a future PR this will be removed.
type Message struct {

@qzhodl qzhodl Jul 29, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Geth includes this type in the state_transition.go, not in this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 432ed4c, it can be introduced later with the state-transition integration.

Comment thread qkc/types/transaction_signing.go Outdated
}

// EIP155Transaction implements Signer using the EIP155 rules.
type EIP155Signer struct {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EIP155Signer is misleading because this signer handles all QKC transaction versions, including the non-EIP-155 versions 0 and 1. The networkId field also has inconsistent semantics: it is the expected QKC network ID for v0/v1, but is ignored for v2, where tx.NetworkId() is interpreted as the Ethereum chain ID.

Could we rename this type to QKCSigner (or VersionedSigner) and explicitly carry both IDs?

type QKCSigner struct {
    qkcNetworkID uint32
    ethChainID   uint32
}

Sender could then validate qkcNetworkID for v0/v1 and ethChainID for v2. This would make signer construction unambiguous and ensure v2 chain-ID validation does not depend solely on a separate validation layer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f84ee04.

}
R = new(big.Int).SetBytes(sig[:32])
S = new(big.Int).SetBytes(sig[32:64])
V = new(big.Int).SetBytes([]byte{sig[64] + 27})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For version 2, this currently stores V as only 27/28. However, Sender() treats version 2 as EIP-155 and subtracts 2*chainID + 8, so a v2 transaction produced by SignTx cannot be recovered correctly. It also differs from pyquarkchain, which stores V = 35/36 + 2*chainID.

Please add the EIP-155 offset for v2, for example:

V = new(big.Int).SetUint64(uint64(sig[64]) + 27)
if tx.Version() == 2 {
    V.Add(V, new(big.Int).SetUint64(8+2*uint64(tx.NetworkId())))
}

Please also add a regression test that signs a v2 transaction with SignTx, checks that V is 35/36 + 2*chainID, and verifies that Sender recovers the original address.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 967072b.

Comment thread qkc/types/transaction_typed_hash.go Outdated
return hexutil.Encode(new(big.Int).SetUint64(uint64(data)).Bytes())
}

func receiptToHex(data *account.Recipient) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be recipientToHex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 346858b.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants