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
114 changes: 114 additions & 0 deletions GSM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Architectural Framework for Proactive Transaction Security in BSC Geth Nodes via Native GSM Integration

## Summary

This document outlines a strategic initiative to enhance the security posture of the Binance Smart Chain (BSC) by natively integrating the GoPlus Security Module (GSM), a centralized transaction assessment engine, directly into the BSC Geth validator nodes. The proposed architecture introduces a robust, two-stage security scanning framework designed to proactively identify and mitigate malicious transactions before they are committed to a block. The first stage intercepts individual transactions upon submission for a rapid preliminary check, while the second stage performs a sophisticated, contextual batch analysis on transactions prior to their promotion to the `pending` state in the mempool.

Performance benchmarks demonstrate that this integration is highly efficient, with negligible impact on node latency and block production throughput, thanks to optimized gRPC interfaces and caching mechanisms. While the design significantly elevates on-chain security, we also analyze current limitations, such as redundant cross-validator checks and reliance on a remote service. To address these, we propose a forward-looking roadmap that includes decentralizing the security service, optimizing data payloads via hash-based pre-flight checks, and developing a hybrid model with embedded core logic for critical, non-negotiable security rules. This initiative marks a pivotal shift from optional, client-side security to a mandatory, protocol-level defense mechanism, fundamentally strengthening the integrity and trustworthiness of the BSC network.

---

## 1. Introduction and Motivation

The Binance Smart Chain (BSC) stands as a prominent blockchain platform, distinguished by its high throughput and low transaction fees. Its Proof of Staked Authority (PoSA) consensus mechanism, driven by a limited set of active validators, provides a blend of performance and decentralization that has fostered a vibrant ecosystem of decentralized applications.

However, like all public ledgers, BSC faces a persistent landscape of threats from malicious actors. These threats range from attempts to exploit smart contract vulnerabilities to the submission of fraudulent transactions designed to compromise network integrity. As the ecosystem's complexity and value grow, the need for a robust, on-chain security apparatus has become paramount.

To date, services like the GoPlus Security Module (GSM)—a high-speed transaction security assessment engine—have primarily operated at the client-side, offering users an optional layer of protection within Web3 wallets. While beneficial, this approach places the onus of security on the end-user and cannot guarantee network-wide protection.

This proposal details a paradigm shift: the direct, native integration of GSM into the BSC Geth validator nodes. By embedding security validation at the consensus layer, we empower validators to proactively vet all transactions before they are considered for block inclusion. This moves security from an optional feature to an intrinsic property of the network, creating a formidable defense against on-chain attacks and significantly enhancing the overall security, reliability, and trust of the BSC ecosystem.

## 2. Architectural Design: A Two-Stage Scanning Framework

The core of our design is a layered, two-stage scanning process that intercepts transactions at critical points within the Geth transaction lifecycle.

#### **Stage 1: Pre-Mempool Transaction Interception**

This stage functions as the system's frontline defense. When a new transaction is submitted to the node (e.g., via the `eth_sendRawTransaction` RPC call), it is immediately scanned by GSM _before_ being admitted into the transaction pool (mempool).

- **Objective:** To instantly reject overtly malicious transactions, such as those originating from addresses on established blacklists or interacting with known malicious contracts.
- **Benefit:** This preemptive filtering conserves significant node resources and network bandwidth by preventing toxic transactions from propagating to peers or consuming mempool memory.

#### **Stage 2: Contextual Batch Analysis Before Pending Promotion**

The second, more sophisticated scan occurs just before a batch of transactions is promoted from the `queued` state to the `pending` state within the mempool. The `pending` pool contains executable transactions that are candidates for inclusion in the next block.

- **Objective:** To conduct deep, contextual analysis on sequences of transactions. At this stage, transactions from the same sender are ordered by nonce, enabling GSM to detect complex, multi-step attack patterns.
- **Capabilities:**
- **Exploit Detection:** Identifying sequenced calls designed to exploit contract vulnerabilities (e.g., re-entrancy attacks).
- **Behavioral Analysis:** Detecting manipulative patterns like wash trading that are only visible across multiple transactions.
- **Cumulative Risk Assessment:** Evaluating the aggregate risk of a transaction sequence, providing insights that isolated checks cannot.

A crucial component of this architecture is the **GSM caching mechanism**, which stores recent scan results. This ensures that high-frequency, benign transactions are not repeatedly re-evaluated, thereby maintaining high throughput and low latency.

## 3. Performance Analysis and Integration Strategy

GSM provides two high-performance gRPC endpoints: `EVMRiskScore` for single transactions and `EVMBatchRiskScore` for batch analysis. All benchmarks were conducted on a BSC validator with server-side caching disabled, representing a worst-case performance scenario.

- **`EVMRiskScore`:** This interface evaluates the security of a single transaction.

- On a BSC validator, a single call to `EVMRiskScore` exhibits the following latencies:
- **P50:** 52 ms
- **P90:** 77 ms
- **P99:** 130 ms
- When called asynchronously in bulk (with a default concurrency of 10 tasks), 1,000 invocations complete in:
- **P50:** 6.21 seconds
- **P90:** 6.50 seconds
- **P99:** 6.64 seconds
- This translates to an average per-call latency of approximately 6 ms, demonstrating strong scalability under load.

- **`EVMBatchRiskScore`:** This interface is optimized for analyzing large batches of transactions, particularly those from multiple `from` addresses with consecutively increasing nonces. For a batch of 1,000 transactions:
- Request construction time:
- **P50:** 4.1 ms
- **P90:** 5.1 ms
- **P99**: 5.3 ms
- Request processing time:
- **P50:** 306 ms
- **P90:** 349 ms
- **P99**: 390 ms

### Integration Strategy

The integration is carefully designed to be non-blocking and to minimize any impact on validator performance.

1. **Single Transaction Check (`SubmitTransaction`):** The `EVMRiskScore` call is integrated into the `SubmitTransaction` function, which is triggered by `eth_sendRawTransaction`.

- **Early Mitigation:** Screens transactions before they enter the mempool and propagate to peers.
- **Efficiency:** Geth's native validation (e.g., signature, nonce) runs first, preventing unnecessary calls to GSM. The asynchronous and sporadic nature of user submissions means the ~52 ms P50 latency is imperceptible.
- **Cache Priming:** This initial scan effectively "warms" the GSM cache, accelerating the subsequent batch scan.

2. **Batch Transaction Check (Promotion to `pending`):** The `EVMBatchRiskScore` call is invoked during the mempool's internal process of promoting `queued` transactions to the `pending` state.

- **Zero Impact on Block Production:** This promotion process runs asynchronously to block creation. With a sufficiently populated `pending` pool of already-vetted transactions, the block producer is never delayed waiting for GSM analysis.
- **Pre-Processed Data:** The validator node has already sorted transactions by sender and nonce, providing perfectly structured input for GSM's contextual analysis and eliminating redundant data preparation.
- **Comprehensive Screening:** This check acts as a catch-all, analyzing transactions received from peers in addition to those submitted directly.

## 4. Analysis of Current Limitations and Future Work

While this architecture provides a transformative security uplift, we identify several areas for future optimization to ensure long-term scalability and robustness.

#### **Limitation 1: Redundant Cross-Validator Scanning**

Currently, each of the active validators independently fetches transactions from the network and performs its own batch security analysis. This creates a scenario where the active validator set performs redundant, near-identical `EVMBatchRiskScore` analyses on the same transaction sets, placing a significant and repetitive load on the centralized GSM service. This challenge would be amplified in networks with larger validator sets, such as Ethereum.

- **Proposed Solution: Decentralized AVS Model:** A long-term solution is to decentralize the security analysis infrastructure itself. By adopting a model akin to EigenLayer's Actively Validated Services (AVS), multiple independent parties could operate GSM nodes. This would distribute the computational load, enhance censorship resistance, and provide the scalability required for larger networks.

#### **Limitation 2: Large Request Payloads in Batch Mode**

The `EVMBatchRiskScore` API requires the full raw transaction data for analysis. In periods of high network activity, these batch requests can generate large data payloads, consuming considerable bandwidth between the validator and the GSM server.

- **Proposed Solution: Hash-Based Pre-Flight Check:** We propose optimizing the API with a two-phase commit process.
1. The validator sends a list of transaction hashes to GSM.
2. Leveraging its cache, GSM identifies any hashes it has not seen recently and requests the full transaction data for only that unknown subset. This would dramatically reduce bandwidth consumption, as the vast majority of transactions in a batch will likely already be in GSM's cache.

#### **Limitation 3: Optionality of Security Checks**

To guarantee validator uptime and performance, the current design is fault-tolerant: if a GSM request times out (>500 ms) or the service is unavailable, the check is bypassed, and the transaction is deemed safe. This "fail-open" approach, while pragmatic, creates a potential security gap.

- **Proposed Solution: Hybrid Model with Embedded Core Logic:** The future architecture should evolve towards a hybrid model. While complex, computationally intensive analysis can remain with a remote service, a core set of critical, non-negotiable security rules (e.g., blocking known malicious signatures, preventing re-entrancy patterns) should be embedded directly into the Geth client as a lightweight, local module. This would ensure a baseline level of security is always enforced, eliminating reliance on a remote call for the most essential checks.

## 5. Conclusion

The native integration of the GSM service into BSC Geth validators represents a significant evolution in blockchain security architecture. By implementing a performant, two-stage scanning framework, we shift from a reactive, user-dependent security model to a proactive, protocol-enforced defense mechanism. The design is engineered to be minimally invasive to validator performance while providing maximum protection against a wide range of on-chain threats.

The identified limitations—redundant scanning, payload inefficiency, and fault-tolerant bypasses—are not weaknesses in the core concept but rather clear signposts for future development. By pursuing a roadmap towards decentralization, data efficiency, and embedded core logic, this framework can serve as a robust and scalable security blueprint not only for BSC but for the broader ecosystem of EVM-compatible blockchains. This work lays the foundation for a more secure and resilient decentralized future.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## BNB Smart Chain with GSM Integration (Proof of Concept)

This fork implements a proof-of-concept integration of the GoPlus Security Module (GSM) into BSC Geth nodes for proactive transaction security. The integration is based on [BSC v1.5.13](https://github.com/bnb-chain/bsc/releases/tag/v1.5.13) and provides a two-stage security scanning framework: first intercepting transactions at submission to perform quick risk assessment, then conducting batch analysis before transactions enter the pending state in the mempool. This approach helps identify and mitigate malicious transactions before they are committed to blocks, significantly enhancing on-chain security with minimal performance impact.

For implementation details, please refer to [GSM.md](./GSM.md) in this repository. Note that this is a proof-of-concept implementation with hardcoded configuration in `gsm/risk.go`. If you wish to test the GSM functionality, please contact GoPlus at `service@gopluslabs.io` to obtain the necessary API key and endpoint information.

## BNB Smart Chain

The goal of BNB Smart Chain is to bring programmability and interoperability to BNB Beacon Chain. In order to embrace the existing popular community and advanced technology, it will bring huge benefits by staying compatible with all the existing smart contracts on Ethereum and Ethereum tooling. And to achieve that, the easiest solution is to develop based on go-ethereum fork, as we respect the great work of Ethereum very much.
Expand Down
135 changes: 133 additions & 2 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/gsm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
Expand Down Expand Up @@ -103,6 +104,7 @@
queuedRateLimitMeter = metrics.NewRegisteredMeter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
queuedEvictionMeter = metrics.NewRegisteredMeter("txpool/queued/eviction", nil) // Dropped due to lifetime
queuedGSMRiskyMeter = metrics.NewRegisteredMeter("txpool/queued/gsmrisky", nil) // Dropped due to GSM risk assessment

// General tx metrics
knownTxMeter = metrics.NewRegisteredMeter("txpool/known", nil)
Expand Down Expand Up @@ -1488,10 +1490,139 @@
pool.addTxsLocked(reinject)
}

// promoteExecutables moves transactions that have become processable from the
// Reorganized implementation of promoteExecutables
// The approach splits the accounts processing into three phases, allowing the second phase
// to batch-promote transactions across all accounts rather than processing one account at a time.
// This leverages GSM's batch evaluation capabilities for more effective risk detection.
func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.Transaction {
// Early return if no accounts to process
if len(accounts) == 0 {
return make([]*types.Transaction, 0)
}

// Track statistics for all transaction classifications
txCounter := struct {
forwards int // Transactions with nonce lower than current state
drops int // Transactions that are unexecutable (insufficient balance/gas)
caps int // Transactions removed due to per-account queue limits
readies int // Transactions ready for execution
riskies int // Transactions flagged as risky by GSM
}{}

// Temporary map to hold ready transactions for each account
readyMap := make(map[common.Address]types.Transactions, len(accounts))

gasLimit := pool.currentHead.Load().GasLimit

// Phase 1: Process forwards, drops, and identify ready transactions
for _, addr := range accounts {
list := pool.queue[addr]
if list == nil {
continue
}

// 1. Remove transactions with nonces lower than current state
forwards := list.Forward(pool.currentState.GetNonce(addr))
for _, tx := range forwards {
pool.all.Remove(tx.Hash())
}
txCounter.forwards += len(forwards)

// 2. Remove transactions with insufficient balance or exceeding gas limit
drops, _ := list.Filter(pool.currentState.GetBalance(addr), gasLimit)
for _, tx := range drops {
pool.all.Remove(tx.Hash())
}
txCounter.drops += len(drops)

// 3. Identify ready transactions but don't promote them yet
readies := list.Ready(pool.pendingNonces.get(addr))
if len(readies) > 0 {
// Note: At this point, all Ready transactions have been moved from list to readies
// readyMap excludes accounts with no ready transactions
readyMap[addr] = readies
txCounter.readies += len(readies)
}
}
log.Trace("Removed old queued transactions", "count", int64(txCounter.forwards))
log.Trace("Removed unpayable queued transactions", "count", int64(txCounter.drops))
queuedNofundsMeter.Mark(int64(txCounter.drops))

// Phase 1.5: Batch evaluate transaction safety with GSM
// This step identifies risky transactions across all accounts in a single batch call,
// which can detect complex risk patterns and relationships between transactions
riskyMap := gsm.GetRiskServiceClient().BatchIsRiskyTx(&readyMap)
for addr, riskyArr := range riskyMap {
readies := readyMap[addr]
for idx, risky := range riskyArr {
if risky {
// Remove transactions flagged as risky
// This implementation assumes risky transactions are rare, so individual removal is acceptable
tx := readies[idx]
txCounter.riskies += 1
pool.all.Remove(tx.Hash())
log.Warn("legacypool.promoteExecutables removed risky tx from pool", "FromAddr", addr.Hex(), "tx", tx.Hash())
}
}
}
queuedGSMRiskyMeter.Mark(int64(txCounter.riskies))

// Phase 2: Promote all safe transactions
// Only transactions that passed GSM's risk assessment will be promoted to the pending pool
var promoted []*types.Transaction
for addr, riskyArr := range riskyMap {
for idx, risky := range riskyArr {
if !risky {
tx := readyMap[addr][idx]
hash := tx.Hash()
if pool.promoteTx(addr, hash, tx) {
promoted = append(promoted, tx)
}
}
}
}
log.Trace("Promoted queued transactions", "count", len(promoted))
// Note: All ready transactions are considered consumed regardless of whether
// they were successfully promoted by pool.promoteTx
queuedGauge.Dec(int64(txCounter.readies))

// Phase 3: Apply caps and clean up the pool
for _, addr := range accounts {
list := pool.queue[addr]
if list == nil {
continue
}
// Drop all transactions over the allowed limit
caps := list.Cap(int(pool.config.AccountQueue))
for _, tx := range caps {
hash := tx.Hash()
pool.all.Remove(hash)
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
}
txCounter.caps += len(caps)

// Remove empty queues entirely
if list.Empty() {
delete(pool.queue, addr)
delete(pool.beats, addr)
// Release the account's reserved capacity if it has no pending transactions either
if _, ok := pool.pending[addr]; !ok {
pool.reserve(addr, false)
}
}
}

queuedRateLimitMeter.Mark(int64(txCounter.caps))
pool.priced.Removed(txCounter.forwards + txCounter.drops + txCounter.caps)
queuedGauge.Dec(int64(txCounter.forwards + txCounter.drops + txCounter.caps))

return promoted
}

// promoteExecutablesOld (For comparison) moves transactions that have become processable from the
// future queue to the set of pending transactions. During this process, all
// invalidated transactions (low nonce, low balance) are deleted.
func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.Transaction {
func (pool *LegacyPool) promoteExecutablesOld(accounts []common.Address) []*types.Transaction {

Check failure on line 1625 in core/txpool/legacypool/legacypool.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.23.x, ubuntu-latest)

func `(*LegacyPool).promoteExecutablesOld` is unused (unused)
// Track the promoted transactions to broadcast them at once
var promoted []*types.Transaction

Expand Down
Loading
Loading