用了俩U卡

因为要把手里的加密货币用于日常消费,所以申请了U卡。在这里稍微记述一下:

SafePal Fiat24

SafePal是个中华系硬件钱包厂家,自称被币安投资(另外还有OneKey厂也是被币安投资的)因为和Coinbase合作低价卖硬件钱包、和Fiat24合作开设MasterCard卡,导致其生态强绑定于Arbitrum One链USDC币;SafePal自己的币SFP好像其实是BEP20链的。

Fiat24是一个欧洲持牌的“金融科技”企业,并非神圣的银行,我觉得其牌照性质可以类比支付宝之类的吧,但SafePal的UI里把Fiat24业务显示在Bank tab内。另外Bitget wallet(注意不是Bitget而是Wallet)、imToken等的金融卡业务也是这个fiat24。

Fiat24是典型的欧洲涉币圈金融企业,提供欧洲双向SEPA通道(受白名单限制)、自己名字的IBAN瑞士区域号码、加密货币充备付金(经分析智能合约的程序,可能还有更多充值渠道)、备付金买加密货币、内部4个币种兑换、对外支付的接口是提供一个MasterCard debit。

之前我一直懒,拖到6月初赶上最后一波可以用内地护照申请,注册时app强制识别手机所在地址,也就是不能随便填写地址。我刚申请完几个小时就停止受理了。刚开始我还还处于兴奋期,没意识到什么,结果没多久就开始恶化了:

  • 被内地版微信支付拒收、
  • 主动停止了从法币账户买加密货币的业务、
  • 主动停止了给法币账户充值的业务、
  • 主动停止了内部多个货币相互兑换的业务……

最后只剩下支付宝、美团、tenpayGo+Apple Pay等少数渠道可以消费、SEPA通道可以充值。

我本来觉得这个Fiat24设计挺别致的,它其实是在Arbitrum One链上单独发行了4种代币作为银行余额的表达方式,所有账户余额和交易都上链,并且私钥由客户自己控制,消费的时候由经授权的智能合约代客户扣除代币余额。充值的时候,用户使用Arbitrum One链上的USDC兑换银行代币,目前仅允许持有Fiat24 NFT的用户操作这些代币。传说:古时候这个代币可以随意转出,但我开始用的时候在群里看到,如果转给外部用户则会因为接收方未持有NFT导致代币被锁死,并且我发现智能合约的当前版本其实已经运行了六百多天了。用户等级之类的可以购买F24代币或者累计用量来实现。

除了功能逐渐被封杀之外,我还发现一个问题:中国人消费用CNH24、USDC充值充到USD24可以实现一比一(SafePal补贴充值手续费),但是SEPA通道却只支持EUR24和CHF24两个币种,显得有些割裂。消费币种或者default币种必须足够支付单笔交易,它不支持多个币种余额组合支付同一笔交易。在已经不支持余额币种兑换的当下,这个割裂的情况愈发凸显。

后来既然无法继续充值,只好在余额消费完毕之后就暂停使用了。不过其实如果搭配Neverless/Kraken的卖币SEPA提现业务,也是可以继续用的吧?

Ether.fi

7月28日还是29日左右开放了几个小时,但我注册晚了,选Hong Kong地址,结果支持的护照签发地列表里面直接不包含内地了,也不许倒回去重新选居住地区。所以也没实际开始用。很多评价都说是最好的,但我也不知道为什么好。粗看了一下app大概也是DeFi那一套(质押之类的),但是不知道钱包记账的具体原理。

COCA

注册时选了香港地址、内地手机号、内地护照,结果说在香港地区不给提供SEPA接口;app不太稳定,大部分时候在展示arbitrum地址的时候就会崩溃,但是展示其它汇入地址则没问题,就好奇怪……

这里不提供私钥,是个托管账户,和上面SafePal不同。

在Banking页面选Add money,从多个币种和网络里选择,把前面SafePal的Arbitrum One链USDC转过来,这里app刷新一下就显示了法币余额了,但是在活动日志里显示是从Base链收到的;从Banking转出余额也只能选Base链USDC。所以可能这个卡的记账网络其实是在BASE链上的USDC?这样说的话,除了不提供私钥,其它方面和上述Fiat24是挺相似的,而且还省了自付手续费做跨链的麻烦。

Posted in 默认分类 | Tagged , , | Leave a comment

列举一下MySQL的身份验证历史发展的几个链接,供参考

4.1之前:mysql_old_password
https://mariadb.com/docs/server/reference/plugins/authentication-plugins/authentication-plugin-mysql_old_password

https://dev.mysql.com/doc/mysql-security-excerpt/5.7/en/account-upgrades.html


4.1开始:mysql_native_password
https://mariadb.com/docs/server/reference/plugins/authentication-plugins/authentication-plugin-mysql_native_password

5.7开始:sha2
https://dev.mysql.com/doc/mysql-security-excerpt/5.7/en/caching-sha2-pluggable-authentication.html

还有验证协议:https://mariadb.org/history-of-mysql-mariadb-authentication-protocols/

Posted in 默认分类 | Tagged , | Leave a comment

昨天另装了个VPS,迁移系统,遇到了很多问题

感叹,确实啊不能总是本地升级,还是要经常进行全新安装和数据转移的,否则总是停留在兼容模式,见识不到行业的发展。


昨天遇到几个问题,先说自己的责任事故
用rsync传输home目录,把普通用户的home目录搞成owned uid=0了,这用户登录之后进不去自己的目录,就去了根目录。然后我sudo chown了当前目录,把系统bin目录里的文件都chown了,此时应该已经丢失了很多setuid。赶紧用另一个已经sudo的窗口去chown回来,并给su和sudo增加setuid。然后再执行dpkg –verify,却发现没检查出多少结果来,不知道是不是dpkg根本没记录原始的mode信息?我记得以前rpm –verify应该是可以的。折腾了一会总感觉哪里不对劲,比如用vi编辑文件无法存盘,用nano却可以存盘等现象。最后决定重装。

再说几件兼容性问题:
我妄图把wordpress的数据库换成Postgres,于是给wordpress安装了pg4wp插件。检查这插件发现它是运行时劫持驱动的,类似于pyMySQL monkey patch的做法,并进行了SQL方言翻译。我用pgloader转移数据的时候发现几个问题:
一、字段名字的大小写问题,有些id有些ID
二、Ubuntu里pgloader版本太文物了,包版本虽然叫3.6.10但实际上是3.6.4,不支持某些字段类型。搜到2020年的issue说让升级……
三、MySQL从8版本开始改用类似于Postgres和Microsoft SQL server的验证方式,从unix domain socket连接的时候直接验证“对方”的用户名;但是pgloader既不支持unix domain socket也不支持mysql新版本的caching sha2 password。于是我又现场学习了30年前学习过一遍的关于mysql身份验证的知识,并成功的把MySQL 8降级到pgloader能够支持的mysql native password……

然后手工修改大小写,勉强整理到能够显示博客内容,但是登录后直接跳转回登录页,也没错误信息也没错误日志……于是切换回MySQL做了一下WordPress数据库升级,再pgloader到Postgres,这下彻底显示不了了。放弃……

Posted in 默认分类 | Tagged , , , | Leave a comment

期权交易心得

之前因为造就了上市公司美团、以及在大肠腾讯上班的缘故,手里是有不少员工股的。但是那时候不开窍,对难以引入内地的资产也一直不算太在乎。2024秋季,NVDA突发暴跌到90几美元。当时恰好在旅游,心情不错就买了一点。但这笔操作是以美团为抵押,找券商融资而得的。2025年4月特朗普股灾,美港双跌,我的美团股票被强制平仓,我自己把剩余部分也清了,避免了进一步损失。

灾后重建阶段,我开始学习期权的知识,并尝试使用covered call加速手里剩下的NVDA股票的回升,但因为目光短浅,终止在155左右,没吃到155到180这段涨幅。

不过这段经历倒是重塑了我对证券市场的认知,从此开始,我很少交易股票,转向交易期权,并略有心得。

Long or short

如果对后面的趋势有清晰坚定的看法,可以作为买方。在AMD和OpenAI宣布合作的那天,盘前AMD暴涨。我考虑到之前已经有好几家上市公司利用和OpenAI的合作来炒作“市值管理”,觉得开盘之后应该会跌回去。虽然我错了,但是我买的PUT还是在市场情绪波动的半小时内给我带来了(扣除费用之后)3976美元的利润。

但在平常,没有什么八卦消息的时候,我只是一个卖低价PUT、卖宽跨式的卖方而已。

Call or put

除了常见的“买方风险有限、卖方风险无限”之外,期权本身的方向也是影响风险的因素。

买期权如果买反了,权利金报废是最大风险。但是还有一种情况就是方向正确,但你没能力行权而且还忘记提前平仓,冤死。我不知道这种情况会不会真的发生,希望只是停留在我的恐怖幻想中吧。在小红书上看到有人遇到了,下边评价说在最后几个小时,券商会试算行权,如果发现买方没能力行权,会强制卖出平仓,算是为买方回收一点残值吧?然而小红书上还看到,东方财富的内地ETF期权是按照弃权来处理的!

卖put,要准备好被指派,自己被迫买下股票,所以提前就得做好思想建设,要选择自己喜欢的股票和能捏着鼻子忍了的价格。在期权价格上升(股票从高价跌向行权价)过程中,券商会提高保证金要求。如果补不足,可能会被强制平仓。这种交易的极端风险是股价不但跌破期权的行权价,甚至跌到0,最后你花了行权价的钱买了一堆废纸回来,虽然也挺惨,但毕竟不是无限。

卖call,我在卖宽跨式的时候遇到过涨超范围的情况。想象一下,股价有可能涨上天,而对手还是按原来约定的行权价来找你行权,这个风险理论上是无限的。

Strike price

我喜欢选择at the money附近,偏向out的那边一点点。

作为买方,我不想花钱买内在价值。如果一开始花钱买了,我没把握将来卖掉的时候这个内在价值还依然存在。换个角度,即使还存在,这部分花费也白白占用我的资金那么长时间,也不好。

作为卖方,卖出in the money的期权,无异于刀尖舔血,这说明从一开始你就处于被对方指派的价格区间了,你卖得的这部分内在价值,其实只是借来的,很可能还要还回去。

Expiration date

因为日常卖PUT,我喜欢一个月之内的,就当每月发工资。短线操作,我一般是作为买方,可以选更短期的,提高资金使用效率。

买方真的会行权吗?

之前卖PUT的日常,其实见过挺多次跌破行权价的情况。不过我大都选择死扛了,也有8月底美团财报这种极少数割肉平仓的情况。

换位思考:如果自己是买方,距离到期日还很远的情况下,提前行权虽然可以拿到内在价值,但是需要花费行权成本,且浪费了剩余的外在(时间+波动)价值,倒不如直接卖掉,我只吃差价就好了。如果临近到期,期权的流动性比较差,这时候还愿意花钱买的估计只有卖方忍痛买回去平仓了,买方可以选择卖回,也可以选择行权吧。

到底什么人会买二手期权呢?有看法相同、但是进场比上一手更晚的人;有意见相左的人;还有卖方买回去平仓的情况。

我的日常配置

所有资金全都买券商自动申赎的货币基金,赚每天几十块钱的生存费。另外卖低价PUT,依靠卖方的高胜率搏一个中等收入。有八卦新闻时,猜方向,买期权爽一把。

Posted in 默认分类 | Tagged , , | Leave a comment

开发Telegram bot的几点心得

Telegram bot 可以是一个HTTPS服务(Webhook模式:被动push Update)或者daemon(getUpdates模式:主动pull Update),不断的从bot API server获得Update,进行处理,做出动作,而实现一些功能。

Update解析

Telegram bot收到的Update对象有个不好的地方,就是它没有自带type属性。为了判断该Update到底是什么类型,需要先试探性的检查几个特定属性是否在Update内,然后再dispatch给各个type特定的处理函数。

输入控制

考虑到bot需要处理用户私聊和群聊两种场景,在获得update之后马上进行分类是必须的。

chat id == from id的情况下,bot收到的消息是私聊的。这种情况下一般消息上不会带有at bot的标志,不过偶尔也有人手贱非要输入,建议先无脑过滤掉,再进一步处理。此类bot大部分都是查询/控制类功能的bot,需要判断:发消息的人是否有权限对我发消息进行查询和控制

chat id != from id的情况下,bot收到的消息是群聊,chat id是群的ID号,from id是发消息人的ID号。如果bot被设计为处理群里的消息,在此时需要进行进一步判断:

  • 消息是来自我乐意管理的群吗?
  • 是普通消息还是botcommand消息?(对于一些群管机器人的发言积分、自动回复功能,一般是不需要特地at bot的,也不需要使用botcommand格式)
  • 如果是botcommand消息,发消息的人有权限吗?

响应

虽然Telegram在webhook模式提供了
Making requests when getting updates 功能,但似乎一次只能返回一个动作。考虑到业务的复杂性,有时候需要做出多个动作,比如群管机器人的删除/封禁动作,既要做出删除封禁动作,还要给管理员回复一个消息,甚至有时候需要延迟一段时间再做动作等等……还是建议直接调用bot API实现功能,而不要使用在返回体中调用API的方法。这玩意,也就聊胜于无吧。

限制:forwardMessage处理多图

一个经常遇到的需求就是:监听群,收到某种消息之后就自动转发到另一处。但是一旦遇到这个消息含有多个图,情况就会变得糟糕,人类看来是一个消息带了多个图,但在bot API里会被分拆成多个Update,其中第一个图和文字组成第一个Update,其它的每个图分别一个Update,它们拥有相同的media_group_id号码,似乎可以相互关联;

单从Updates的角度来看,你需要等到下一个不带media_group_id号码的消息,或者拥有不同的media_group_id号码的消息,才能确定上次的一组media已经收集齐全了,然后才能用forwardMessages一次性转发把这堆内容转发过去,这样它在目标chat里才会正确的粘连起来;如果还没有凑齐就调用forwardMessages,则多次forwardMessages转发过去的消息之间并不会自动粘连。

但问题是:并没有一个字段声明这个media group里到底有多少个内容,也没有任何情报指出Telegram保证把这一堆Update放在同一个Updates里,尤其是:Webhook模式一次只能收到一个Update、getUpdates模式你指定的limit说不定会小于拆分的瓣数,甚至拆出来的好多瓣有可能是bot的不同实例分别处理的。

再加上绝大多数bot的写作方式都是循环一圈仅处理一条Update,导致:凑齐media group里所有内容这个需求存在工程上的困难

Posted in 默认分类 | Tagged , | Leave a comment

在Python里抑制requests库的日志消息

我自己经常在自己的脚本开头使用logging.basicConfig(level=logging.DEBUG)初始化logging库,但是随之而来的就是requests会输出大量日志,甚至盖过了我自己的内容。所以我打算抑制requests的日志。

综合搜索的资料,和探索源代码,我发现:

  • 其实requests代码里根本就没有调用任何logging/logger,甚至它还在__init__.py里给“自己的”logger设置了NullHandler
  • docs/api.rst 文档里其实讲了怎么“配置”日志,只是没有“supress”这个词,以至于我没搜到
  • 通过在Format里加上%(name)s,可以发现写日志的其实是urllib3.connectionpool

所以只需要在basicConfig后面加一句

logging.getLogger(“urllib3”).setLevel(logging.WARNING)

就可以抑制这部分日志了。

另外,logging.Logger.manager.loggerDict是个好东西,可以检查当前到底存在哪些logger。通过检查loggerDict[‘urllib3.connectionpool’].propagate发现其为True,其上层也是True,因此,虽然这两层logger一个没handler,一个NullHandler,但是该logger记录的日志消息仍会逐层上传,最终被basicConfig的root logger处理。

Posted in 默认分类 | Tagged , , | Leave a comment

GNU和BSD版本的xargs 分隔符不同

例子:
list="a b c d e"; echo $list |xargs -n1 -I{} echo begin {} end

在Mac上执行结果:
begin a end
begin b end
begin c end
begin d end
begin e end

在Linux上执行结果:
begin a b c d e end

我这里的需求是有一堆输入,要分别以其为参数,执行一些命令,无论是否成功都要对所
有目标执行,所以
1 “一些命令”我选用shell function来实现,在其中读了$1作为本次处理的目标
2 “所有目标”我选用xargs;如果选Parallel还得额外安装

结果发现xargs在切分“以空格为分隔符”的字符串的时候,GNU版本默认不切分,结果把
整个“含空格分隔符的字符串”传给函数,执行了一次,而函数里又选了$1作为本次执行
目标,其综合结果就是只对列表中第一个目标执行了一遍

更惨的是我对比的时候是在Mac上做的对比,怎么看怎么顺眼……

最后请教同事,用xargs的-d参数解决的

   This  manual page documents the GNU version of xargs.  xargs reads items from the standard input, delimited by blanks (which can be protected with double or single quotes or a backslash) or newlines

GNU xargs的manpage说支持blanks 按说空格也应该可以啊……
xargs.c的read_line函数里:

  893           /* POSIX: In the POSIX locale, the separators are <SPC> and 
  894            * <TAB>, but not <FF> or <VT>. 
  895            */ 
  896           if (!bc_ctl.replace_pat && ISBLANK (c)) 
  897             { 
  898               *p++ = '\0'; 
  899               len = p - linebuf; 
  900               if (EOF_STR (linebuf)) 
  901                 { 
  902                   eof = true; 
  903                   return first ? -1 : len; 
  904                 } 
  905               bc_push_arg (&bc_ctl, &bc_state, 
  906                            linebuf, len, 
  907                            NULL, 0, 
  908                            initial_args); 
  909               p = linebuf; 
  910               state = SPACE; 
  911               first = false; 
  912               continue; 
  913             } 

这一段状态机代码应该是“之前读到了普通字符,这次读到了空格”的处理,这时候应该把已经读到的这一段作为一个参数加到列表里去 

看它的判断条件if (!bc_ctl.replace_pat && ISBLANK (c)) 
其实是要求没用-i/-I参数,且本次读到的字符为空白

验证一下,去掉-i之后:

echo a b c d e |xargs -n1  echo begin {} end 
运行结果就几乎正确了。虽然丧失了使用占位符的能力,但至少它确实按照空格进行分割了 
begin {} end a 
begin {} end b 
begin {} end c 
begin {} end d 
begin {} end e 
  
我觉得这个判断条件就是个bug。但是有网友指出:按照POSIX标准、GNU xargs的文档,开启-I就是强制一整行的,我的用法不清真。对此我只能说:满足标准但是不满足需求啊,为什么输出端的参数会影响输入端的行为呢?

Posted in 默认分类 | Tagged | Leave a comment

昨天遇到collectd exec插件的bug,顺便发现他们不按套路出牌啊

先说症状:

collectd exec插件调用的几个外部脚本,其中总会随机有一个缺少COLLECTD_HOSTNAME和COLLECTD_INTERVAL环境变量。

搜了一下是这个bug https://github.com/collectd/collectd/issues/3041

然后我好奇啊,就读了一下修改前后的代码,发现collectd不按套路出牌。

带有bug的版本:

先setenv()设置主进程自己的环境变量,然后尝试fork(),如果成功,在子进程里execvp();主进程重新unsetenv()恢复主进程自己的环境变量。在多个exec密集执行的时候,都会访问主进程的环境变量,会有race condition,偶尔会发生前一个exec插件刚unsetenv()然后后一个exec插件开始fork()的情况,丢失环境变量。

修复后的版本:

先fork(),在子进程里准备环境变量数组,尝试execvpe()带签署环境变量数组作为参数,执行新进程(execvpe()为GNU专有扩展),或者先设置extern char **environ指针指向准备好的数组,然后execvp()执行新进程直接继承。

别人家套路:

先准备环境变量数组,然后fork(),在子进程里execve()并使用前述环境变量数组作为参数。

Posted in 默认分类 | Tagged , | Leave a comment

Tencent tlinux的$releasever比Redhat原版更反动

前几年我写过一篇批判$releasever的博客之后,现在工作中又用到了tlinux,而且发现它的$releasevar居然是2.2,是带小数的。但是看了看tlinux-release的主版本号确实是2,不带小数;它provide的centos-release的主版本号也是7,也不带小数。

折腾了一番,发现yum的config.py里,class VersionGroupConf下边的_read_yumvars(yumvars, root) 函数,直接提供了用 /etc/yum/vars/ 下边的文件来设置release属性的方法。

Posted in 默认分类 | Leave a comment

supervisor泄漏进程案例分析

起因

前几天使用 salt ‘*’ test.ping 的时候发现响应内容中有一些“某某minion was already deleted from tracker, probably a duplicate key“的提示信息。刚开始误以为是salt-key管理有问题,尝试删除再重新accept,但是依然会出错。到该minion上检查,发现上面运行了两套salt-minion*三层进程树,一共6个进程,其中一套的PPID为1,另一套的Parent是supervisord。

然后就开始研究这种情况是怎么产生的,发现有两种可能:

第一种可能

supervisor本身不被systemd监管,被SIGKILL信号杀死时,因为SIGKILL由内核直接处理,所以并没有机会关闭下属的进程,导致下属salt-minion进程树泄漏。而且不但salt-minion进程树泄漏,连同样被supervisor监管的另一个服务也一并泄漏,二者的PPID都变成了1号。

不过,如果supervisor本身被systemd监管,在其主进程被杀死时,systemd会给整个service slice cgroup里所有进程补刀,所以并不会泄漏进程;如果supervisor是被SIGTERM信号杀死,它也会给下属子进程发信号,一般也不会泄漏进程。

第二种可能

supervisor没有受到影响,正常运行;supervisor监管的salt-minion三层进程树的其中最高层进程(也就是supervisord的直属子进程)被SIGKILL信号杀死,随即,第二层进程exit(1) (不明原因,可能需要看一下salt-minion源码),导致第三层进程变成孤儿。经检查源代码的_spawn_as_child()函数,supervisor针对其监管下的每一个服务,都是采用 fork() + setpgid() +execve() 的方式来启动的,在调用setpgid()改变了process group id之后,第三层进程的孤儿收养关系就不再归属于supervisord进程,而是归属于1号进程。

随后supervisor会重启salt-minion服务,产生新的3个进程,加上之前剩下的,一共4个。

结论

  • 考虑到观察到6个进程而不是4个,实际发生的大概是前一种情况
  • supervisor虽然有“能力”处理进程退出之后马上重启的工作,但是因为使用了setpgid()把下属服务与自己隔离,没使用cgroup机制把下属服务单独圈起来,又不具备1号的神圣地位,其实它并不知道到底下属了多少、哪些进程,从机制原理上就根本无法保证所有下属的孤儿进程都被其reap。还是建议不要在严肃场合使用
  • 1号进程神圣,所有的服务进程监管工作都应该交给1号进程来处理
Posted in 默认分类 | Tagged , , | Leave a comment