2025-07-15 05:42:20
I’ll show you how to serve raw Markdown files in a Next.js blog using middleware and API routes, enabling Agent Experience SEO (AX-SEO).
\ AX-SEO focuses on optimizing content for AI agents, LLM-powered search engines, and automated crawlers that now play a major role in content discovery.
\ By the end of this post, you’ll be able to:
\
✅ Implement Next.js middleware for .md
URL rewriting.
\ ✅ Set up API routes to fetch and serve Markdown content.
\ ✅ Optimize your blog for Agent Experience SEO (AI-driven search).
\ ✅ Provide clean, structured data for AI-powered search engines.
Traditional SEO is no longer enough. AI search engines, LLM-powered assistants (ChatGPT, Perplexity, Google’s AI Overviews, etc.), and autonomous agents are now indexing and ranking content differently.
Why does AX-SEO matter?
\ If you want your blog optimized for AI-driven search, this guide is for you!
Most blogs serve only HTML, making extracting structured information hard for AI tools. Common mistakes include:
\ ❌ Not offering raw text formats – AI agents struggle with unnecessary markup.❌ Ignoring structured data for LLMs – AI search engines prioritize clean content.
\ This guide avoids these mistakes and ensures a future-proof content strategy for AI indexing.
.md
RoutingI used Next.js Middleware to detect .md
URLs and redirect them to an API route:
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
export function middleware(req: NextRequest) {
const url = req.nextUrl.clone();
if (url.pathname.endsWith(".md")) {
url.pathname = `/api${url.pathname}`;
return NextResponse.rewrite(url);
}
return NextResponse.next();
}
export const config = {
matcher: ["/blog/:path*.md", "/projects/:path*.md"],
};
Next, I created API endpoints to fetch Markdown content:
import { readMarkdown } from "@/server/read-markdown-file";
import { NextRequest } from "next/server";
export async function GET(_req: NextRequest, { params: { slug } }: { params: { slug: string } }) {
return readMarkdown(slug);
}
The readMarkdown
the function retrieves the raw Markdown content:
import useBlogSlug from "@/features/posts/hooks/useBlogSlug";
import { NextResponse } from "next/server";
export async function readMarkdown(slug: string) {
try {
const {
post: {
content: { markdown },
},
} = await useBlogSlug({ params: { slug: slug.replace(".md", "") } });
return new NextResponse(markdown, {
headers: {
"Content-Type": "text/plain",
},
});
} catch (error) {
console.error("Error reading markdown file:", error);
return new NextResponse(JSON.stringify({ message: "Markdown file not found" }), { status: 404 });
}
}
To make the Markdown file accessible, I added a link on each blog page:
<Link href={`/api/${params.type}/${params.slug}.md`} passHref>
View Markdown
</Link>
\
Now, every post has a “View Markdown” button that links to the raw .md
version.
With this approach, I made my Next.js blog serve Markdown dynamically, benefiting both humans and AI bots.
\ 📂 Source Code: GitHub Repository🌍 Live Website: sm-y.dev
\ I hope this guide helps you boost your blog’s AX-SEO and AI search visibility! 🚀
2025-07-15 05:39:41
Background
Motivating Example and Challenges
Design of AVVERIFIER and 4.1 Overview
Evaluation
5.1 Experimental Setup & Research Questions
5.2 RQ1: Effectiveness & Efficiency
Discussion
Based upon the F passed by the Grapher, the Simulator is responsible for: 1) maintaining the data structures required by EVM, and 2) updating the taint information. Specifically, S is composed of all user-controllable variables, e.g., ORIGIN, CALLDATA, and BALANCE. For each opcode, the Simulator specifies a set of rules to update both data structures and taint information in a static way. Without loss of generality, EVM opcodes are classified into four categories: stack related, load & store related, call related, and control flow related.
\ 4.4.1 Stack Related Opcodes
\
\ 4.4.2 Load & Store Related Opcodes
\ As we mentioned in §2.1, memory and storage are maintained by EVM. Data in them are organized in different ways. Specifically, storage operates like a key-value dictionary, i.e., the values are retrieved by the given key [3]. As for the memory, it can be seen as a flat and contiguous array of bytes. Accessing to an item in memory is achieved by a calculated offset. However, in Ethereum smart contracts, a memory is always sparse. Thus, we are motivated by the method raised by He et al. [41], i.e., abstracting the memory area as a key-value pairs, where key is the offset and value is the corresponding data. To this end, data in memory and offset can be retrieved and indexed uniformly, like Sto[key] or Mem[o f f set].
\ Load Related Opcodes. This includes MLOAD and SLOAD, i.e., load from memory or storage, respectively. Both of them take a single argument, tar, i.e., the target address, and return ret, indicating the retrieved data. The taint rules are defined as:
\
\ That is to say, if any of the val or dest is tainted, the final stored val will be tainted. When maintaining the taint dependency relation, we only consider if val depends on dest. There does not exist an edge from val to val.
\ 4.4.3 Call Related Opcodes
\
\ 4.4.4 Control Flow Related Opcodes
\ According to the specification, some of them do not take arguments, like RETURN, STOP, REVERT, and INVALID. Therefore, no taint marks are propagated when simulating these instructions. The Simulator only performs the control flow simulation on them. For example, when the Simulator encounters the INVALID instruction, it halts the simulation of the current path and moves to the next one.
\ JUMP and JUMPI opcodes are crucial in handling taint propagation as they take the jump destination to explicitly alter the control flow, where JUMPI takes an argument as the condition. Because JUMP can be seen as a special edition of JUMPI, we demonstrate the handling on JUMPI in the following. Specifically, JUMPI takes two arguments, i.e., dest and cond, referring to the jump destination and the jump condition, respectively. It returns no value and directly performs the control flow jump. For each JUMPI, there are two following branches, fallthrough and jumpdest. The former one corresponds to the successive opcode, which is executed once the condition is not met, while the latter one is executed when the condition meets. According to the specification [66], dest should not rely on any arguments, i.e., determined statically during the compilation. Therefore, according to whether the cond is determined dynamically, the Simulator adopts the following rules:
\
If the cond is a concrete number, either fallthrough or jumpdest is chosen by the Simulator deterministically.
\
Otherwise, two paths are all feasible.
(a) If the ancestor of the cond is CALLDATALOAD, prioritize the jumpdest path, i.e., cond = True, to emulate attackers have successfully bypassed the examination.
(b) Otherwise, it takes two paths into consideration.
\ The branch prioritization in step 2(a) is heuristic. Specifically, once a condition is tainted from a source, it means that it is totally controllable for attackers, like L17 in Listing 3, i.e., attackers can always bypass the assertion by constructing the owner function. On bytecode level, there is a conditional jump at L17, one is to L18 associated with a met condition, another is not shown in Listing 3, indicating the require at L17 fails. Thus, the heuristic in step 2(a) prioritizes the branch to L18, i.e., attackers successfully bypass verifications. Compared to symbolic execution, which collects path conditions along the simulation and queries the back-end SMT solver to obtain a concrete set of solutions, the advantages of this heuristic are twofold. On the one hand, collecting path conditions and asking for solving is time- and resourceconsuming, which is the greatest bottleneck [26]. On the other hand, symbolic execution sometimes even cannot handle complex address verification logic. For example, when handling the L17 in Listing 3, the symbolic executor has to conduct an inter-contract analysis to IVisor to obtain the value of owner(), which cannot be completed within an acceptable time. Though step 2(b) may introduce some false positives, it is a tradeoff between the efficiency and effectiveness. In §5.2, it proves its effectiveness and practicability.
\ As for the taint propagation rules, we should note that no values are returned by these opcodes. Moreover, they have no side effects on both memory and storage. In summary, no taint marks are propagated through these opcodes.
\
:::info Authors:
(1) Tianle Sun, Huazhong University of Science and Technology;
(2) Ningyu He, Peking University;
(3) Jiang Xiao, Huazhong University of Science and Technology;
(4) Yinliang Yue, Zhongguancun Laboratory;
(5) Xiapu Luo, The Hong Kong Polytechnic University;
(6) Haoyu Wang, Huazhong University of Science and Technology.
:::
:::info This paper is available on arxiv under CC BY 4.0 DEED license.
:::
\
2025-07-15 05:39:30
Background
Motivating Example and Challenges
Design of AVVERIFIER and 4.1 Overview
Evaluation
5.1 Experimental Setup & Research Questions
5.2 RQ1: Effectiveness & Efficiency
Discussion
This section elucidates the technical intricacies of AVVERIFIER, which is designed to detect the address verification vulnerability in Ethereum smart contracts. We firstly give a high-level overview in §4.1, and an introduction of adopted notations in §4.2. Then, we delve into the three components, respectively, from §4.3 to §4.5.
Fig. 1 illustrates the architecture and workflow of AVVERIFIER, which is composed of three main components, i.e., code grapher (denoted as Grapher), EVM simulator (denoted as Simulator), and vulnerability detector (denoted as Detector). Specifically, AVVERIFIER only takes the bytecode of a Solidity smart contract as input. The Grapher parses it into the control flow graph (CFG), filters out all suspicious functions as candidates, and delivers them to the Simulator. The Simulator maintains a state, consisting of two parts. One part is the data structures required by EVM, i.e., stack, memory, and storage (see §2.1); the other part is the collected taint information. According to the CFG, the Simulator updates fields in states according to the opcode sequence. It also adopts a heuristicbased path selection method to focus on the most valuable path, i.e., the ones that may lead to the vulnerability. Once the analysis against a path is finished, the corresponding state is sent to the Detector to determine if the current contract is vulnerable to the address verification vulnerability. The cascaded three-phase detection strategy in the Detector rules out false positives and false negatives based on the intrinsic characteristics (P1 to P3).
To better explain the implementation of AVVERIFIER, we define some notations here:
\ • S, the set of sources that can be controlled by users;
\ • T, the set of tainted variables;
\ • CT, a mapping from a tainted variable to its sources;
\
\
\
\
\
\ \ • F, the set of suspicious functions;
\ • Mem, Sto, refer to the memory and storage area in EVM.
\ • V, EC, and SM, refer to the three-stage detection in the Detector, respectively. Each of them takes a function f and a parameter p as inputs.
Generally speaking, the Grapher is responsible for obtaining the sub-tree of CFG of the given function. Given a piece of bytecode, Grapher firstly extracts the runtime code, consisting of implementation of functions [40]. Then, Grapher parses it into basic blocks and constructs the CFG according to their jump relations. However, some jump relations are determined dynamically at runtime instead of statically at the compilation stage. Thus, the Grapher constructs the CFG only on statically determined jump relations considering the soundness. Take the Fig. 2 as an example, where bb1 is the entry of the function foo and its jump relations to bb2 and bb3 can be statically determined. Though bb3 and bb4 can jump to bb5 at runtime, they are determined dynamically. Thus, though the Grapher generates two trees whose roots are bb1 and bb5, respectively, bb5 is actually a subtree of bb1 at runtime.
\ To filter out suspicious functions, i.e., the ones that may be vulnerable to the address verification vulnerability, the Grapher heuristically keeps functions that take addresses as arguments (P1). Specifically, each address parameter undergoes a bitwise AND operation with 0xFF..FF (160-bit long). By identifying such a specific pattern, which is also widely adopted in previous work [7], we can extract functions that meet P1. These functions will be added to the set F .
\ \
:::info Authors:
(1) Tianle Sun, Huazhong University of Science and Technology;
(2) Ningyu He, Peking University;
(3) Jiang Xiao, Huazhong University of Science and Technology;
(4) Yinliang Yue, Zhongguancun Laboratory;
(5) Xiapu Luo, The Hong Kong Polytechnic University;
(6) Haoyu Wang, Huazhong University of Science and Technology.
:::
:::info This paper is available on arxiv under CC BY 4.0 DEED license.
:::
\
2025-07-15 05:39:24
Background
Motivating Example and Challenges
Design of AVVERIFIER and 4.1 Overview
Evaluation
5.1 Experimental Setup & Research Questions
5.2 RQ1: Effectiveness & Efficiency
Discussion
Considering the aim of implementing a lightweight and effective detector for address verification vulnerability, we exclude the analyzers that adopt dynamic analysis. The reasons for this decision are twofold. On the one hand, dynamic analysis requires a runtime environment for execution, which is resource-intensive and time-consuming, contrary to our lightweight goal. On the other hand, dynamic analysis identifies vulnerabilities with generated test cases and oracles. For intricate contracts, especially for inter-contract analysis, this method might not always cover all vulnerable paths, potentially resulting in false negatives. Consequently, static methods are considered, including pattern-based matching, symbolic execution, and taint analysis. To the best of our knowledge, no existing tools can be directly deployed to detect this vulnerability. Although we can extend them with the ability to detect the vulnerability, we observe some intrinsic limitations.
\ Pattern-based Matching. It relies on heuristic rules summarized by developers. Lots of preliminary tools adopt this method, identifying vulnerabilities according to opcode sequences [37], transaction histories [5], and call traces [23]. However, such a manual process is unsound and error-prone, which cannot even handle C1, i.e., identifying semantics of bytecode sequences. Consequently, the constant update of Solidity syntax [75] and compilation toolchain [30] make this kind of analyzers ineffective.
\ Symbolic Execution & Model Checking. Both techniques are widely adopted in identifying vulnerabilities in software analysis. Taking advantages of abstraction on programs, symbolic executors and model checkers can recover some semantic information to overcome C1. However, they are inherently limited by the efficiency issue, mainly brought by the path/state explosion. For example, when dealing with the example shown in Listing 3, both of them should not only traverse all possible functions through the entry to find the depositlike functions, but also get stuck from L16 to L18, where they would try all possible contracts. Because they cannot effectively and efficiently conduct the inter-contract or even inter-procedural analysis, C2 is the main challenge hiders the adoption of them. Moreover, they try to model the memory area in a precise way, i.e., considering every bit in the memory. Although such a precise modeling benefits to the soundness of analyzing results, it comes at the cost of efficiency. In the context of address verification vulnerability, we only need to focus on the propagation of address parameters in the memory area instead of concrete data.
\ Taint Analysis. Existing taint analyzers cannot be directly adopted to identify this vulnerability. Firstly, some of them rely on the source code [31]. In Ethereum, such contracts only account for less than 1%. Secondly, some of them are limited by their adopted methods when collecting the taint propagation information and tracking taint propagation. For instance, Sereum [66] uses dynamic analysis to monitor execution at runtime. Furthermore, Mythril [70] and Osiris [73] both adopt symbolic execution to statically collect information. Mythril tries to traverse all feasible paths, while Osiris is only intraprocedural. Ethainter [14] is a strong contender. However, it exhibits proficiency in addressing C1 and C2, but it suffers due to the intricacies of EVM’s linear memory model. Specifically, it cannot properly handle dynamic memory allocations and deallocations in EVM’s memory, which potentially compromises the accuracy of its taint tracking, especially when contracts execute complex memory operations or utilize memory for storing and manipulating intermediate data (like the three memory verification mechanisms mentioned in §2.2).
\ Our Key Idea. As aforementioned, symbolic execution and taint analysis can effectively recover the semantics and conduct the inter-procedural analysis on control flow and data flow dependencies to some extent. However, considering the soundness of the analysis, symbolic execution always suffers the efficiency issue. To this end, we decided to adopt static EVM simulation based taint analysis, which tracks the taint propagation through a static simulation on the bytecode. To reduce the false positives introduced by static simulation and filter out paths that can lead to exploitation, we design a threestage detector corresponding to the three principles mentioned in §3.1. Moreover, to overcome the memory issue suffered by Ethainter, we decide to design the memory model motivated by He et al. [41] to abstract the sparse linear memory into key-value pairs. Consequently, our design can address both C1 and C2. Furthermore, AVVERIFIER simulates stack and memory through a self-implemented EVM simulator to capture the propagation of addresses, ensuring efficiency for non-dynamic data. As for the dynamic memory parameters, AVVERIFIER conservatively treats them as symbols and only guarantees the stack balance to avoid the negative influence brought by enumerating all possible values.
\
:::info Authors:
(1) Tianle Sun, Huazhong University of Science and Technology;
(2) Ningyu He, Peking University;
(3) Jiang Xiao, Huazhong University of Science and Technology;
(4) Yinliang Yue, Zhongguancun Laboratory;
(5) Xiapu Luo, The Hong Kong Polytechnic University;
(6) Haoyu Wang, Huazhong University of Science and Technology.
:::
:::info This paper is available on arxiv under CC BY 4.0 DEED license.
:::
\
2025-07-15 05:39:19
Background
Motivating Example and Challenges
Design of AVVERIFIER and 4.1 Overview
Evaluation
5.1 Experimental Setup & Research Questions
5.2 RQ1: Effectiveness & Efficiency
Discussion
Listing 3 shows a smart contract owned by Visor Finance that is vulnerable to the address verification vulnerability. It was attacked on Dec. 21st, 2021 [11], causing $8.2 million financial losses. As we can see, the deposit function takes three arguments, namely, the number of tokens to be deposited (visrDeposit), the payer (from), and the beneficiary (to). From L6 to L8, it performs sanity checks, i.e., a valid amount of deposit, and valid addresses for both payer and payee. After that, it translates the deposit into shares according to totalSupply() (L11 to L14), performs the corresponding token transfer from the from address to itself (L16 to L22), and mints some vvisr tokens to the to address (L24). The vulnerability is hidden in the if code block
\
\ at L16. Specifically, it allows the from address as a contract, and examines if its owner function returns the address of the transaction initiator (L17). If the assertion passes, then it invokes the delegatedTransferERC20 function defined in from. Recalling the threat model mentioned in §2.4, attackers are able to deploy contracts and initiate transactions arbitrarily. More specific, if the from is actually provided by some malicious ones, they can control the behaviors of L17 and L18. To this end, the control flow will be successfully directed to L24, where vvisr finally issues tokens to to, which is also controlled by attackers, without receiving any tokens from from that is expected by developers of Visor Finance.
\ Through this example, we can summarize three principles related to the address verification vulnerability:
\ P1 The vulnerable function takes an address as a parameter, and performs insufficient authorization examination on that address. Through the address, attackers can pass self-deployed and unauthorized contracts.
\ P2 The address in P1 is taken as the target of an external call. Through the external call, the control flow is transferred to attackers. Thus, they can totally control the behavior of the external call, including the return value.
\ P3 On-chain states that are control-flow dependent on the return value mentioned in P2 are updated. To this end, through an unauthorized control flow, adversaries can get profits by indirectly manipulating on-chain states, like initiating an external call or updating balance.
In response to the address verification vulnerability, as outlined in the summarized principles and the motivating example in §3.1, we identify the following challenges.
\ C1: Lack of semantics. It is challenging to precisely identify if the address mentioned in P1 is sufficiently verified due to the lack of semantics in bytecode. According to the statistics [59], more than 99% of Ethereum contracts have not released their source code. The bytecode format is quite unreadable and contains little semantic information. Moreover, there is no debug information to assist in recovering the semantics. Traditional bytecode-based analysis tools usually require some methods to overcome this challenge, like symbolic execution [70].
\ C2: Inter-procedural analysis on control flow and data flow. Detecting this vulnerability requires accurately extracting the control flow and data flow dependencies inter-procedurally. Specifically, in P2, there is an external call to an address, which is passed via the argument. Between the external call and the function entry, it will be propagated several times due to the authorization verification. Thus, we have to precisely identify if the callee address is one of the arguments through parsing data flow. Moreover, in P3, the on-chain state update depends on the return value of the external call in P2 in terms of control flow, which requires us to identify control flow dependencies among variables. In addition, from Listing 2 we can conclude that some authorizations are verified in other functions, which requires inter-procedural analysis.
\
:::info Authors:
(1) Tianle Sun, Huazhong University of Science and Technology;
(2) Ningyu He, Peking University;
(3) Jiang Xiao, Huazhong University of Science and Technology;
(4) Yinliang Yue, Zhongguancun Laboratory;
(5) Xiapu Luo, The Hong Kong Polytechnic University;
(6) Haoyu Wang, Huazhong University of Science and Technology.
:::
:::info This paper is available on arxiv under CC BY 4.0 DEED license.
:::
\
2025-07-15 05:39:12
Background
Motivating Example and Challenges
Design of AVVERIFIER and 4.1 Overview
Evaluation
5.1 Experimental Setup & Research Questions
5.2 RQ1: Effectiveness & Efficiency
Discussion
There are two types of accounts in Ethereum: external owned account (EOA) and smart contract. Specifically, an EOA is an ordinary account, which is identified by a unique address and controlled by a private key. Furthermore, smart contracts can be regarded as scripts, which are mainly written in Solidity [28], a well-defined and easy-to-use programming language proposed by the Ethereum official. Interactions among accounts are achieved by initiating transactions, which carry the corresponding data. Smart contracts are executed in Ethereum Virtual Machine (EVM) [43], which is embedded in Ethereum client nodes. EVM is stack-based, and all data is stored either permanently or temporarily. Specifically, all operands of operators and intermediate values are pushed onto and popped from the stack. The memory area [42], the temporary one, only keeps data under the context of the current transaction. Only the data stored in the storage area [56] is permanent, i.e., is stored on-chain. We often denote a set of smart contracts which jointly achieve a specific functionality as a decentralized application (DApp).
\ DApps have demonstrated significant potential since their rise in 2016 [40]. Many genres of DApps have emerged, e.g., gambling [61], token swap [76], and lending [71]. Alongside the hundreds of billions of USD invested in Ethereum, the decentralized versions of traditional financial tools, e.g., exchanges and insurance, have appeared, which are called decentralized finance, i.e., DeFi. Taking advantage of the decentralization, permissionlessness, and transparency in Ethereum, DeFi starts to rise like a rocket. According to statistics, DeFi accounted for $163 billion at the end of 2022 [54].
\ Except for the official token, Ether, Ethereum allows users to issue tokens as they wish, as long as these tokens meet the standard like ERC-20 [63]. The ERC-20 standard consists of six mandatory functions. Any smart contract implements these functions can issue valid tokens that can circulate in Ethereum, like USDT [16] and USDC [9]. Therefore, DeFi can also issue its own ERC-20 tokens, and take other ERC-20 tokens as valid ones. The interoperability between ERC-20 tokens and DeFi pushes the prosperity of Ethereum.
In Ethereum, examining the validity of the given addresses is a common practice, which is called whitelisted address verification. It is widely adopted in DeFi apps such as lending [77] and bank [6]. Address verification is the cornerstone to ensure the safety of smart contracts. Therefore, OpenZeppelin [64], a well-known standard library provider in Ethereum, offers a whitelisted verification method. Moreover, through a comprehensive study of the top 40 DeFi projects ranked by TVL (Total Value Locked) [53], which account for over 95% of the total DeFi market, we summarized the verification techniques they adopted. In short, three whitelisted verification methods are involved, i.e., hard-encoded comparison, mapping validation, and hard-encoded address enumeration. Note that, though we cannot guarantee all adopted address verification techniques are covered, we cover the most prevalent ones. Considering the extensive copy-and-use in Ethereum smart contracts [40], these three mechanisms are representative.
\
\
\
\
\
\ \ Listing 1 illustrates how hard-encoded comparison works. As we can see, the passed token at L2[2] is required to equal the address of usdt, otherwise it raises an exception. Mapping validation adopts a mapping structure that can dynamically maintain the whitelisted status of addresses, e.g., mapping(address => bool) whitelist. As for the hard-encoded addresses enumeration, it is a variant of the first one. As shown in Listing 2, an array named addresses keeps all whitelisted addresses. Therefore, once the deposit function is invoked, the argument token is passed to the contains function, defined at L3, which is basically a hard-encoded comparison wrapped by a loop. At the bytecode level, these three techniques perform similarly. The contract loads the address in arguments by CALLDATALOAD and performs examination via a conditional opcode JUMPI. If an address is whitelisted, the control flow will be directed to the fallthrough branch, and the following logic will be used. Otherwise, the jumpdest branch is responsible for handling failed assertions.
Taint analysis is a fundamental method of program analysis, used for detecting vulnerabilities [34] and tracking sensitive information flow [47]. Before performing the taint analysis, sources and sinks should be specifically defined, where source refers to input fields controlled by adversaries, and sink refers to any part of the system where potentially dangerous data can be used in an unsafe manner. Taint analysis will track data flow from sources to sinks and identify any operations or transformations on the data along the way.
\ In the context of Ethereum smart contracts, the source is often the function of smart contracts that accept transactions from other accounts, while the sink varies, depending on specific goals. For example, to examine if a contract can be destructed, Ethainter [14] takes the SELFDESTRUCT opcode as the sink. Moreover, Michael et al. [34] introduce a tool based on symbolic execution and taint analysis, designating SSTORE as the primary sink for its evaluations.
Adversaries in our study do not require any extra privileges. This is because Ethereum is a permissionless blockchain platform, which allows any non-privileged account, including malicious ones, to initiate transactions with sufficient gas, deploy valid smart contracts, and invoke any already deployed ones. However, certain limitations still exist. For instance, they cannot breach the integrity of the Ethereum network or manipulate the block generation process, and cannot access the private keys of legitimate accounts. In a nutshell, we can barely distinguish adversaries from well-behaved accounts.
:::info Authors:
(1) Tianle Sun, Huazhong University of Science and Technology;
(2) Ningyu He, Peking University;
(3) Jiang Xiao, Huazhong University of Science and Technology;
(4) Yinliang Yue, Zhongguancun Laboratory;
(5) Xiapu Luo, The Hong Kong Polytechnic University;
(6) Haoyu Wang, Huazhong University of Science and Technology.
:::
:::info This paper is available on arxiv under CC BY 4.0 DEED license.
:::
\