Skip to content

Commit

Permalink
Merge pull request #885 from oasisprotocol/ptrus/feature/runtime-tx-s…
Browse files Browse the repository at this point in the history
…igners

api/runtime/transactions: Add signers field to response
  • Loading branch information
ptrus authored Jan 20, 2025
2 parents ad9ec5d + 6f6e769 commit c5e6650
Show file tree
Hide file tree
Showing 8 changed files with 1,128 additions and 38 deletions.
4 changes: 4 additions & 0 deletions .changelog/885.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
api/runtime/transactions: Add signers field to response

Signers contain information about the transaction signers. Existing sender_0,
sender_0_eth and nonce_0 fields are deprecated and will be removed in future.
38 changes: 34 additions & 4 deletions api/spec/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2914,7 +2914,7 @@ components:
RuntimeTransaction:
type: object
# NOTE: Not guaranteed to be present: eth_hash, to, amount.
required: [round, index, timestamp, hash, sender_0, nonce_0, fee, fee_symbol, charged_fee, gas_limit, gas_used, size]
required: [round, index, timestamp, hash, signers, sender_0, nonce_0, fee, fee_symbol, charged_fee, gas_limit, gas_used, size]
properties:
round:
type: integer
Expand Down Expand Up @@ -2942,24 +2942,34 @@ components:
The Ethereum cryptographic hash of this transaction's encoding.
Absent for non-Ethereum-format transactions.
example: 9e6a5837c6366d4a7e477c71ffe32d40915cdef7ef209792259e5ee70caf2705
signers:
type: array
items:
allOf: [$ref: '#/components/schemas/RuntimeTransactionSigner']
description: The signers of this transaction.
sender_0:
allOf: [$ref: '#/components/schemas/Address']
deprecated: true
description: |
The Oasis address of this transaction's 0th signer.
Unlike Ethereum, Oasis natively supports multiple-signature transactions.
However, the great majority of transactions only have a single signer in practice.
Retrieving the other signers is currently not supported by this API.
# NOTE: To expose the other signers, use the transaction_signers table.
DEPRECATED: This field will be removed in the future in favor of the signers field.
example: oasis1qz670t637yyxshnlxhjj5074wgwl94d0x5x69zqd
sender_0_eth:
type: string
deprecated: true
description: |
The Ethereum address of this transaction's 0th signer.
DEPRECATED: This field will be removed in the future in favor of the signers field.
example: *eth_address_1
nonce_0:
type: integer
format: uint64
description: The nonce used with this transaction's 0th signer, to prevent replay.
deprecated: true
description: |
The nonce used with this transaction's 0th signer, to prevent replay.
DEPRECATED: This field will be removed in the future in favor of the signers field.
example: 114194
fee:
type: string
Expand Down Expand Up @@ -3129,6 +3139,26 @@ components:
format: byte
description: The base64-encoded encrypted result data.

RuntimeTransactionSigner:
type: object
required: [address, nonce]
properties:
address:
allOf: [$ref: '#/components/schemas/Address']
description: |
The Oasis address of the transaction signer.
example: oasis1qz670t637yyxshnlxhjj5074wgwl94d0x5x69zqd
address_eth:
type: string
description: |
The Ethereum address of this transaction signer.
example: *eth_address_1
nonce:
type: integer
format: uint64
description: The transaction signer nonce.
example: 114194

RuntimeAccount:
type: object
required: [address, balances, evm_balances, stats]
Expand Down
44 changes: 30 additions & 14 deletions storage/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1468,15 +1468,18 @@ func (c *StorageClient) RuntimeTransactions(ctx context.Context, p apiTypes.GetR
}
for res.rows.Next() {
t := RuntimeTransaction{
Error: &TxError{},
Error: &TxError{},
Signers: []apiTypes.RuntimeTransactionSigner{},
}
var oasisEncryptionEnvelope RuntimeTransactionEncryptionEnvelope
var oasisEncryptionEnvelopeFormat *common.CallFormat
var evmEncryptionEnvelope RuntimeTransactionEncryptionEnvelope
var evmEncryptionEnvelopeFormat *common.CallFormat
var sender0PreimageContextIdentifier *string
var sender0PreimageContextVersion *int
var sender0PreimageData []byte
var signersAddresses []string
var signersPreimageContextIdentifiers []*string
var signersPreimageContextVersions []*int
var signersPreimageData [][]byte
var signersNonces []uint64
var toPreimageContextIdentifier *string
var toPreimageContextVersion *int
var toPreimageData []byte
Expand All @@ -1487,11 +1490,11 @@ func (c *StorageClient) RuntimeTransactions(ctx context.Context, p apiTypes.GetR
&t.Timestamp,
&t.Hash,
&t.EthHash,
&t.Sender0,
&sender0PreimageContextIdentifier,
&sender0PreimageContextVersion,
&sender0PreimageData,
&t.Nonce0,
&signersAddresses,
&signersPreimageContextIdentifiers,
&signersPreimageContextVersions,
&signersPreimageData,
&signersNonces,
&t.Fee,
&t.FeeSymbol,
&t.FeeProxyModule,
Expand Down Expand Up @@ -1548,12 +1551,25 @@ func (c *StorageClient) RuntimeTransactions(ctx context.Context, p apiTypes.GetR
t.EncryptionEnvelope = &evmEncryptionEnvelope
}

// Render Ethereum-compatible address preimages.
// TODO: That's a little odd to do in the database layer. Move this farther
// out if we have the energy.
if sender0PreimageContextIdentifier != nil && sender0PreimageContextVersion != nil {
t.Sender0Eth = EthChecksumAddrFromPreimage(*sender0PreimageContextIdentifier, *sender0PreimageContextVersion, sender0PreimageData)
for i := range signersAddresses {
t.Signers = append(t.Signers, apiTypes.RuntimeTransactionSigner{
Address: signersAddresses[i],
Nonce: signersNonces[i],
})
// Render Ethereum-compatible address preimage.
if signersPreimageContextIdentifiers[i] != nil && signersPreimageContextVersions[i] != nil {
t.Signers[i].AddressEth = EthChecksumAddrFromPreimage(*signersPreimageContextIdentifiers[i], *signersPreimageContextVersions[i], signersPreimageData[i])
}

// Deprecated sender_0 fields.
if i == 0 {
t.Sender0 = t.Signers[0].Address
t.Nonce0 = t.Signers[0].Nonce
t.Sender0Eth = t.Signers[0].AddressEth
}
}

// Render Ethereum-compatible address preimages.
if toPreimageContextIdentifier != nil && toPreimageContextVersion != nil {
t.ToEth = EthChecksumAddrFromPreimage(*toPreimageContextIdentifier, *toPreimageContextVersion, toPreimageData)
}
Expand Down
79 changes: 59 additions & 20 deletions storage/client/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,20 +467,20 @@ const (
txs.timestamp,
txs.tx_hash,
txs.tx_eth_hash,
signer0.signer_address AS sender0, -- oh god we didn't even use the same word between the db and the api
signer0_preimage.context_identifier AS sender0_preimage_context_identifier,
signer0_preimage.context_version AS sender0_preimage_context_version,
signer0_preimage.address_data AS sender0_preimage_data,
signer0.nonce AS nonce0,
ARRAY_AGG(signers.signer_address),
ARRAY_AGG(signer_preimages.context_identifier),
ARRAY_AGG(signer_preimages.context_version),
ARRAY_AGG(signer_preimages.address_data),
ARRAY_AGG(signers.nonce),
txs.fee,
txs.fee_symbol,
txs.fee_proxy_module,
txs.fee_proxy_id,
txs.gas_limit,
txs.gas_used,
CASE
WHEN txs.tx_eth_hash IS NULL THEN txs.fee -- charged_fee=fee for non-EVM txs
ELSE COALESCE(FLOOR(txs.fee / NULLIF(txs.gas_limit, 0)) * txs.gas_used, 0) -- charged_fee=gas_price * gas_used for EVM txs
WHEN txs.tx_eth_hash IS NULL THEN txs.fee -- charged_fee=fee for non-EVM txs
ELSE COALESCE(FLOOR(txs.fee / NULLIF(txs.gas_limit, 0)) * txs.gas_used, 0) -- charged_fee=gas_price * gas_used for EVM txs
END AS charged_fee,
txs.size,
txs.oasis_encrypted_format,
Expand Down Expand Up @@ -511,23 +511,24 @@ const (
txs.error_message,
txs.error_params
FROM chain.runtime_transactions AS txs
LEFT JOIN chain.runtime_transaction_signers AS signer0 ON
(signer0.runtime = txs.runtime) AND
(signer0.round = txs.round) AND
(signer0.tx_index = txs.tx_index) AND
(signer0.signer_index = 0)
LEFT JOIN chain.address_preimages AS signer0_preimage ON
(signer0.signer_address = signer0_preimage.address) AND
LEFT JOIN chain.runtime_transaction_signers AS signers ON
(signers.runtime = txs.runtime) AND
(signers.round = txs.round) AND
(signers.tx_index = txs.tx_index)
LEFT JOIN chain.address_preimages AS signer_preimages ON
(signers.signer_address = signer_preimages.address) AND
-- For now, the only user is the explorer, where we only care
-- about Ethereum-compatible addresses, so only get those. Can
-- easily enable for other address types though.
(signer0_preimage.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth') AND (signer0_preimage.context_version = 0)
(signer_preimages.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth') AND
(signer_preimages.context_version = 0)
LEFT JOIN chain.address_preimages AS to_preimage ON
(txs.to = to_preimage.address) AND
-- For now, the only user is the explorer, where we only care
-- about Ethereum-compatible addresses, so only get those. Can
-- easily enable for other address types though.
(to_preimage.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth') AND (to_preimage.context_version = 0)
(to_preimage.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth') AND
(to_preimage.context_version = 0)
LEFT JOIN chain.runtime_related_transactions AS rel ON
(txs.round = rel.tx_round) AND
(txs.tx_index = rel.tx_index) AND
Expand All @@ -542,12 +543,50 @@ const (
($4::text IS NULL OR rel.account_address = $4::text) AND
($5::text IS NULL or txs.method = $5::text) AND
($6::timestamptz IS NULL OR txs.timestamp >= $6::timestamptz) AND
($7::timestamptz IS NULL OR txs.timestamp < $7::timestamptz) AND
(signer0.signer_address IS NOT NULL) -- HACK: excludes malformed transactions that do not have the required fields
($7::timestamptz IS NULL OR txs.timestamp < $7::timestamptz)
GROUP BY
txs.round,
txs.tx_index,
txs.timestamp,
txs.tx_hash,
txs.tx_eth_hash,
txs.fee,
txs.fee_symbol,
txs.fee_proxy_module,
txs.fee_proxy_id,
txs.gas_limit,
txs.gas_used,
txs.size,
txs.oasis_encrypted_format,
txs.oasis_encrypted_public_key,
txs.oasis_encrypted_data_nonce,
txs.oasis_encrypted_data_data,
txs.oasis_encrypted_result_nonce,
txs.oasis_encrypted_result_data,
txs.method,
txs.body,
txs.to,
to_preimage.context_identifier,
to_preimage.context_version,
to_preimage.address_data,
txs.amount,
txs.amount_symbol,
txs.evm_encrypted_format,
txs.evm_encrypted_public_key,
txs.evm_encrypted_data_nonce,
txs.evm_encrypted_data_data,
txs.evm_encrypted_result_nonce,
txs.evm_encrypted_result_data,
txs.success,
txs.evm_fn_name,
txs.evm_fn_params,
txs.error_module,
txs.error_code,
txs.error_message,
txs.error_params
ORDER BY txs.round DESC, txs.tx_index DESC
LIMIT $8::bigint
OFFSET $9::bigint
`
OFFSET $9::bigint`

RuntimeEvents = `
SELECT
Expand Down
7 changes: 7 additions & 0 deletions tests/e2e_regression/eden/expected/emerald_failed_tx.body
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
"round": 8059465,
"sender_0": "oasis1qr9ak7ck2fs3pscs635gapk96ncl02yyxggrqgq5",
"sender_0_eth": "0x7274e8a2F8C599a1265651037e0da68C5ECa06bD",
"signers": [
{
"address": "oasis1qr9ak7ck2fs3pscs635gapk96ncl02yyxggrqgq5",
"address_eth": "0x7274e8a2F8C599a1265651037e0da68C5ECa06bD",
"nonce": 31228
}
],
"size": 205,
"success": false,
"timestamp": "2023-12-12T08:40:35Z",
Expand Down
7 changes: 7 additions & 0 deletions tests/e2e_regression/eden/expected/emerald_tx.body
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
"round": 8060338,
"sender_0": "oasis1qzchk9eh0n4p2raym2e6cyvegrjumxl3lqkfhztr",
"sender_0_eth": "0xCB2412a993f406eFf10a74011410a4F35e3549E3",
"signers": [
{
"address": "oasis1qzchk9eh0n4p2raym2e6cyvegrjumxl3lqkfhztr",
"address_eth": "0xCB2412a993f406eFf10a74011410a4F35e3549E3",
"nonce": 3063641
}
],
"size": 136,
"success": true,
"timestamp": "2023-12-12T10:06:38Z",
Expand Down
Loading

0 comments on commit c5e6650

Please sign in to comment.