owl’s delusions

Stay hungry, stay foolish.

AI Agent + Web3 调研

AI Agent + Web3 调研 DeFAI & AgentFi: AI 自主交易,收益优化 Agent 通过收集市场信息,预测未来走势,自主进行交易投资,获取收益。Agent 相对于人更可能保持理性,长期来看收益率会更高。 Agent 为用户管理资金,监控市场,发现有高收益的机会,可以提前进入抢占市场,赚早鸟收益。或是发现有负面消息,预测到亏损提前跑路来进行避险。或者可以根据价格自动 rebalance 来保证用户提供的流动性一直能赚收益。 Agent + Web3 MCP 钱包 Agent 扮演的角色主要是理解用户的意思,把自然语言翻译到具体的操作或是意图(Intent 也可以持续分析市场趋势和用户行为来改进功能,提供个性化的投资建议 MCP 端需要提供一些语言模型不适合做的功能,比如查询链上信息,查询合约源码和 ABI,根据 ABI 和参数构造交易,对交易签名,让 Agent 可以只提供相关操作的参数就实现相关功能 半自动 这种适合做一个智能钱包,用户提出需求,Agent 通过 mcp 查询信息和构造交易,每笔交易需用户手动签名确认,安全风险会小很多 效果像是这样 DawnWallet 全自动 这种实现就可以让 Agent 在后台自动进行交易,比如可以做个 Virtuals 里那些自主交易的 Agent 一种方案是让 Agent 直接控制整个私钥,或者通过信任服务器托管私钥,签名都走 mcp,当然这就有一定风险了,比如 Agent 被欺诈之类 另一种是用户授权一次,对交易次数,时间,开销,合约调用等等进行限制,Agent 就可以在授权范围内一直交易而无需用户再次确认。 这种就可以通过 ERC 4337 + 权限控制模块来实现,Agent 发送 4337 的 UserOp,在钱包的 validateUserOp 里实现权限验证,再由 bundler 打包上链。 还可以搭配 7702 让 EOA 用户也能使用。 ...

January 16, 2026 · 1 min · 🦉

MCP OAuth

0115 更新 笑死了,早就有现成的了还自己在这画来画去 😅 https://mcp-auth.dev/docs/tutorials/todo-manager 这两天自己试着开发一些 mcp server 来玩,多用户的时候要考虑授权问题 现在这些 Agent 的实现都是在连接到 mcp server 或者说初始化的时候就要进行授权 梳理了一下两种 MCP OAuth 流程,方便后续开发参考 三方 App 本身支持 OAuth 的情况,比较简单,直接去三方 App 授权,然后 Agent 就可以通过正常调用工具了 三方不支持 OAuth 的,那就要搭一个 OAuth Web/App 来代理一下才行

January 8, 2026 · 1 min · 🦉

Ethernaut 笔记

简介 最近从 Ethernaut 学习到了很多,抛开那些比较基础的,记录一些 ECDSA 相关的 以下公式中小写字母代表一个值,大写字母代表一个点,除了 N 是曲线的 order 我们知道 ECDSA 依赖于椭圆曲线上的离散对数问题,对于私钥 privkey,公钥 PubKey 是椭圆曲线上的一个点并且有 $\text{PubKey} = \text{privkey} \cdot G$,其中 G 是椭圆曲线的生成元,正是因为椭圆曲线上的离散对数问题,已知 PubKey 和 G 求 privkey 是困难的,才让数字签名得以可能 Sign 签名公式 $$ s = k^{-1} \cdot (h + r \cdot \text{privkey})\mod{N} $$k 是随机数,RFC6979 提供了生成方法,不建议真的随机生成,因为重用会导致私钥泄漏,后续会提到 h 是要被签名的 hash 值 r 是 k * G 这个点的 x 轴坐标 计算出 s 后,和 r 共同构成签名 (r, s),但在签名恢复的过程还会产生歧义,有两种结果,所以还要引入一个 v 来确认是哪一个结果,后续会提到 1import hashlib 2from eth_hash.auto import keccak 3from ecdsa import SECP256k1 4from ecdsa.rfc6979 import generate_k 5 6CURVE = SECP256k1 7N = CURVE.order 8G = CURVE.generator 9 10priv_key = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 11 12h = keccak(b'hello') 13print(f'hash: 0x{h.hex()}') 14 15h_int = int.from_bytes(h, 'big') 16 17# random k, important 18k = generate_k( 19 order=N, 20 secexp=priv_key, 21 hash_func=hashlib.sha256, 22 data=h, 23) 24 25R = k * G 26r = R.x() % N 27k_inv = pow(k, -1, N) 28 29# s = k^-1 * (h + r * pk) 30s = (k_inv * (h_int + r * priv_key)) % N 31 32print(f'sig : 0x{r.to_bytes(32, 'big').hex()}{s.to_bytes(32, 'big').hex()}') 33print(f'r : 0x{r.to_bytes(32, 'big').hex()}') 34print(f's : 0x{s.to_bytes(32, 'big').hex()}') Recover 签名恢复公式,即从签名 (r, s) 中恢复出公钥 Pub ...

December 1, 2025 · 6 min · 🦉

proxychains4 在 macOS 上无法正常工作的问题小记

安装了 proxychains-ng 来强制特定程序走代理,发现在 macOS 上有时可以正常工作有时不行 经查询是 macOS SIP 的问题 因为 proxychains4 的工作原理是替换子程序的动态链接库来强制走代理,而 macOS SIP 为了安全考虑会阻止所有内置 executable 被替换动态链接库,以至 proxychains4 对所有内置 executable 无法正常工作,比如 /usr/bin/ 下的所有 解决方法,使用 Homebrew 安装的版本就好,通过 Homebrew 安装的程序就没有 SIP 保护 (实在只能用内置就只好关闭 SIP 了 如果 /usr/bin 和 /opt/homebrew/bin 下都有某个程序,可通过调整 PATH 里 /opt/homebrew/bin 的位置来调整优先级 特例,通过 Homebrew 安装的 curl 在 /opt/homebrew/opt/curl/bin 1export PATH=/opt/homebrew/bin:/usr/bin:$PATH

July 4, 2025 · 1 min · 🦉

博客在手机端的应用模式适配

以下场景仅在 iOS 端完成测试 电脑上打开博客当然很友好啦 但是在手机端打开,保存书签到桌面,就有很神奇的体验了 其他书签比如 Grafana, UptimeKuma 之类的,打开都是全屏模式,体验非常丝滑 唯独自己博客的书签,点击后会跳转去 Safari 打开,令人疑惑 简单搜索了一下还是找到了原因以及解决方案 Safari docs 原来可以通过 meta tag 来调整,还好使用的 hugo 主题简单,可以使用 layouts/partials/extend_head.html 覆盖默认来自定义一些 html head ( 居然有一天会查阅 Safari 的文档

June 23, 2025 · 1 min · 🦉

Crypto Scam

简介 每年都有很多很多人因为各种骗局而损失惨重,这里主要讨论一些最近比较常见的骗局。 各大种类 Crypto Drainers ( the most common link 表现形式 通常是是模仿官方的网页布局和风格,页面内容就是领取空投之类的。 一种是点击按钮就会连接钱包弹出签名。 link 另一种是连接钱包的时候提示连接失败或者繁忙,然后让自己填助记词或者私钥上去,就很直接… 传播方式,会在哪里看到 推特的评论区,是最常见的,通常会取一个和官方很像的名字头像,链接也是和官方网页很像的。 link link 链上发名字里带链接的币,批量转给各个地址。 link link 因为各大浏览器,钱包,追踪 ERC20 代币都是根据 ERC20 的 Transfer 事件来的。所以钓鱼方只要在合约上不断触发 Transfer 事件就可以伪造出很多转账记录,任意地址转到任意地址都可以。 1function airdrop(address[] calldata _to, uint256 _value) public { 2 for (uint256 i = 0; i < _to.length; i++) { 3 emit Transfer(address(0x0), _to[i], _value); 4 } 5} 比较常见的 from 地址就是各大交易所热钱包,各种 Deployer 之类的。上图就是 uniswap v4 pool manager,都是为了骗取信任。 ...

February 28, 2025 · 18 min · 🦉

net/http Transport

$ go version go version go1.20.5 darwin/arm64 DefaultTransport 看一下 DefaultTransport 的 RoundTrip 方法。 1// src/net/http/roundtrip.go#L8 2// RoundTrip implements the RoundTripper interface. 3// 4// For higher-level HTTP client support (such as handling of cookies 5// and redirects), see Get, Post, and the Client type. 6// 7// Like the RoundTripper interface, the error types returned 8// by RoundTrip are unspecified. 9func (t *Transport) RoundTrip(req *Request) (*Response, error) { 10 return t.roundTrip(req) 11} 12 13// src/net/http/transport.go#L510 14// roundTrip implements a RoundTripper over HTTP. 15func (t *Transport) roundTrip(req *Request) (*Response, error) { 16 t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) 17 ctx := req.Context() 18 trace := httptrace.ContextClientTrace(ctx) 19 20 if req.URL == nil { 21 req.closeBody() 22 return nil, errors.New("http: nil Request.URL") 23 } 24 if req.Header == nil { 25 req.closeBody() 26 return nil, errors.New("http: nil Request.Header") 27 } 28 scheme := req.URL.Scheme 29 isHTTP := scheme == "http" || scheme == "https" 30 if isHTTP { 31 for k, vv := range req.Header { 32 if !httpguts.ValidHeaderFieldName(k) { 33 req.closeBody() 34 return nil, fmt.Errorf("net/http: invalid header field name %q", k) 35 } 36 for _, v := range vv { 37 if !httpguts.ValidHeaderFieldValue(v) { 38 req.closeBody() 39 // Don't include the value in the error, because it may be sensitive. 40 return nil, fmt.Errorf("net/http: invalid header field value for %q", k) 41 } 42 } 43 } 44 } 45 46 origReq := req 47 cancelKey := cancelKey{origReq} 48 req = setupRewindBody(req) 49 50 if altRT := t.alternateRoundTripper(req); altRT != nil { 51 if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol { 52 return resp, err 53 } 54 var err error 55 req, err = rewindBody(req) 56 if err != nil { 57 return nil, err 58 } 59 } 60 if !isHTTP { 61 req.closeBody() 62 return nil, badStringError("unsupported protocol scheme", scheme) 63 } 64 if req.Method != "" && !validMethod(req.Method) { 65 req.closeBody() 66 return nil, fmt.Errorf("net/http: invalid method %q", req.Method) 67 } 68 if req.URL.Host == "" { 69 req.closeBody() 70 return nil, errors.New("http: no Host in request URL") 71 } 72 73 for { 74 select { 75 case <-ctx.Done(): 76 req.closeBody() 77 return nil, ctx.Err() 78 default: 79 } 80 81 // treq gets modified by roundTrip, so we need to recreate for each retry. 82 treq := &transportRequest{Request: req, trace: trace, cancelKey: cancelKey} 83 cm, err := t.connectMethodForRequest(treq) 84 if err != nil { 85 req.closeBody() 86 return nil, err 87 } 88 89 // Get the cached or newly-created connection to either the 90 // host (for http or https), the http proxy, or the http proxy 91 // pre-CONNECTed to https server. In any case, we'll be ready 92 // to send it requests. 93 pconn, err := t.getConn(treq, cm) 94 if err != nil { 95 t.setReqCanceler(cancelKey, nil) 96 req.closeBody() 97 return nil, err 98 } 99 100 var resp *Response 101 if pconn.alt != nil { 102 // HTTP/2 path. 103 t.setReqCanceler(cancelKey, nil) // not cancelable with CancelRequest 104 resp, err = pconn.alt.RoundTrip(req) 105 } else { 106 resp, err = pconn.roundTrip(treq) 107 } 108 if err == nil { 109 resp.Request = origReq 110 return resp, nil 111 } 112 113 // Failed. Clean up and determine whether to retry. 114 if http2isNoCachedConnError(err) { 115 if t.removeIdleConn(pconn) { 116 t.decConnsPerHost(pconn.cacheKey) 117 } 118 } else if !pconn.shouldRetryRequest(req, err) { 119 // Issue 16465: return underlying net.Conn.Read error from peek, 120 // as we've historically done. 121 if e, ok := err.(nothingWrittenError); ok { 122 err = e.error 123 } 124 if e, ok := err.(transportReadFromServerError); ok { 125 err = e.err 126 } 127 return nil, err 128 } 129 testHookRoundTripRetried() 130 131 // Rewind the body if we're able to. 132 req, err = rewindBody(req) 133 if err != nil { 134 return nil, err 135 } 136 } 137} 因为 RoundTripper 说明了请求的 URL 和 Header 必须初始化,所以一开始检查报错。再往后,如果 schema 是 http 的话,检查了一下 header 的键值对的非法字符。再往后,注意到有一个 setupRewindBody,是用于恢复请求 body 的 ...

August 29, 2023 · 23 min · 🦉

net/http Client

$ go version go version go1.20.5 darwin/arm64 如果一个文件一个文件挨着看未免也太枯燥了,并且没有和实践结合,感觉看了也体会不深。这里从发起一个请求到最后收到应答的整个流程,跟随一个 http.Request 的生命周期去探索相关的源码。 http.Client 开始发请求之前先看一下 http.Client 结构体 1// src/net/http/client.go#L29 2// A Client is an HTTP client. Its zero value (DefaultClient) is a 3// usable client that uses DefaultTransport. 4// 5// The Client's Transport typically has internal state (cached TCP 6// connections), so Clients should be reused instead of created as 7// needed. Clients are safe for concurrent use by multiple goroutines. 8// 9// A Client is higher-level than a RoundTripper (such as Transport) 10// and additionally handles HTTP details such as cookies and 11// redirects. 12// 13// When following redirects, the Client will forward all headers set on the 14// initial Request except: 15// 16// • when forwarding sensitive headers like "Authorization", 17// "WWW-Authenticate", and "Cookie" to untrusted targets. 18// These headers will be ignored when following a redirect to a domain 19// that is not a subdomain match or exact match of the initial domain. 20// For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" 21// will forward the sensitive headers, but a redirect to "bar.com" will not. 22// 23// • when forwarding the "Cookie" header with a non-nil cookie Jar. 24// Since each redirect may mutate the state of the cookie jar, 25// a redirect may possibly alter a cookie set in the initial request. 26// When forwarding the "Cookie" header, any mutated cookies will be omitted, 27// with the expectation that the Jar will insert those mutated cookies 28// with the updated values (assuming the origin matches). 29// If Jar is nil, the initial cookies are forwarded without change. 30type Client struct { 31 // Transport specifies the mechanism by which individual 32 // HTTP requests are made. 33 // If nil, DefaultTransport is used. 34 Transport RoundTripper 35 36 // CheckRedirect specifies the policy for handling redirects. 37 // If CheckRedirect is not nil, the client calls it before 38 // following an HTTP redirect. The arguments req and via are 39 // the upcoming request and the requests made already, oldest 40 // first. If CheckRedirect returns an error, the Client's Get 41 // method returns both the previous Response (with its Body 42 // closed) and CheckRedirect's error (wrapped in a url.Error) 43 // instead of issuing the Request req. 44 // As a special case, if CheckRedirect returns ErrUseLastResponse, 45 // then the most recent response is returned with its body 46 // unclosed, along with a nil error. 47 // 48 // If CheckRedirect is nil, the Client uses its default policy, 49 // which is to stop after 10 consecutive requests. 50 CheckRedirect func(req *Request, via []*Request) error 51 52 // Jar specifies the cookie jar. 53 // 54 // The Jar is used to insert relevant cookies into every 55 // outbound Request and is updated with the cookie values 56 // of every inbound Response. The Jar is consulted for every 57 // redirect that the Client follows. 58 // 59 // If Jar is nil, cookies are only sent if they are explicitly 60 // set on the Request. 61 Jar CookieJar 62 63 // Timeout specifies a time limit for requests made by this 64 // Client. The timeout includes connection time, any 65 // redirects, and reading the response body. The timer remains 66 // running after Get, Head, Post, or Do return and will 67 // interrupt reading of the Response.Body. 68 // 69 // A Timeout of zero means no timeout. 70 // 71 // The Client cancels requests to the underlying Transport 72 // as if the Request's Context ended. 73 // 74 // For compatibility, the Client will also use the deprecated 75 // CancelRequest method on Transport if found. New 76 // RoundTripper implementations should use the Request's Context 77 // for cancellation instead of implementing CancelRequest. 78 Timeout time.Duration 79} 80 81// DefaultClient is the default Client and is used by Get, Head, and Post. 82var DefaultClient = &Client{} 83 84// RoundTripper is an interface representing the ability to execute a 85// single HTTP transaction, obtaining the Response for a given Request. 86// 87// A RoundTripper must be safe for concurrent use by multiple 88// goroutines. 89type RoundTripper interface { 90 // RoundTrip executes a single HTTP transaction, returning 91 // a Response for the provided Request. 92 // 93 // RoundTrip should not attempt to interpret the response. In 94 // particular, RoundTrip must return err == nil if it obtained 95 // a response, regardless of the response's HTTP status code. 96 // A non-nil err should be reserved for failure to obtain a 97 // response. Similarly, RoundTrip should not attempt to 98 // handle higher-level protocol details such as redirects, 99 // authentication, or cookies. 100 // 101 // RoundTrip should not modify the request, except for 102 // consuming and closing the Request's Body. RoundTrip may 103 // read fields of the request in a separate goroutine. Callers 104 // should not mutate or reuse the request until the Response's 105 // Body has been closed. 106 // 107 // RoundTrip must always close the body, including on errors, 108 // but depending on the implementation may do so in a separate 109 // goroutine even after RoundTrip returns. This means that 110 // callers wanting to reuse the body for subsequent requests 111 // must arrange to wait for the Close call before doing so. 112 // 113 // The Request's URL and Header fields must be initialized. 114 RoundTrip(*Request) (*Response, error) 115} 简单翻译一下注释。 ...

August 29, 2023 · 18 min · 🦉