作者归档:jimmy

flutter应用在三星手机上运行时,屏幕左下角有游戏工具的浮动图标。flutter应用在三星android手机上被识别为游戏?

今日,我用flutter编写一个ios/android应用,并在三星note8上运行时,突然发现应用启动后,屏幕上显示的不是

Flutter Demo Home Page

而是

Welcome to Game Tools

我点击屏幕右下角的 < 后,屏幕倒是切换到flutter应用了,然而,屏幕左下角还是比其他应用多了一个图标。即 ||| 的左侧图标,在其他应用中不会出现,而且,点击该图标后,手机就会启动游戏工具(Game Tools)

我搜索Stackoverflow后,发现,React Native的应用也有类似情况

React Native app is recognized as a game on Samsung Note8

https://stackoverflow.com/questions/49287871/react-native-app-is-recognized-as-a-game-on-samsung-note8

I think there are few possibilities.
you (or one of your dependencies) have included the google play service API which inside of play service API has a module named games that samsung will automatically treat it as game.
You could find which of your dependency is loading google play service API and create a exclude like:
compile (project ('your.dependency')){ exclude group: 'com.google.android.gms', module:'play-services-game' }
Your application id (can see on build.gradle) is registered on samsung game database. You could check by going into playstore and search for your application id

不过,我检查了flutter的依赖(dependencies),并没有发现play-services-game。难道,真的是application id被三星识别为游戏的id了吗。

我检查了flutter默认的app id,该id为com.example.app,我将该值改为com.example.app,并重新编译运行应用。结果,我发现,当app id为com.example.app时,三星的游戏工具就会出现,而app id为com.example.app111时,三星的游戏工具就不会出现。

看来,com.example.app的确已经注册到三星的游戏数据库中( is registered on samsung game database. )了。还好,我们正常发布的应用不会使用com.example.app,所以,我们无需担心flutter编写的应用会触发三星的游戏工具(Game Tool)。

iOS 13的新Info.plist的key NSBluetoothAlwaysUsageDescription,相关应用需要及时升级

iOS 13在隐私方面有了新的变化,其中一个变化对于视频录制类应用会有较大影响。那就是Privacy – Bluetooth Always Usage Description(NSBluetoothAlwaysUsageDescription)。
如果视频类应用支持手持稳定器,如大疆Osmo Mobile,而目前手持稳定器与手机之间都是通过蓝牙连接的,那么,App的Info.plist中需要添加该Key,否则,App在尝试连接手持稳定器的时候,会crash。

Crash的错误信息如下
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.

使用CoreMotion时遇到Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]

2019年8月1日更新:根据本人测试,在iOS 13 Developer Beta 5,iPhone XS Max上,本bug已经不再出现,App调用CoreMotion不会再出现Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState] 错误。


我近期以ProMovie为原型,开发一款iPhone的录像app,在开发的过程中,考虑到用户可能会锁定屏幕旋转,所以,我使用了CoreMotion判断设备当前的orientation,以此确定拍摄的视频的orientation。然而,在实际调试中,我发现,每次在我的iPhone XS Max iOS 12.3.1上运行,Xcode都会检测到以下错误。该错误不会导致应用崩溃,但是一直存在。最终,我只能像忽略warning一样忽略它,然而,与warning不一样之处在于,我可以想办法解决warning,却没办法解决此问题。

Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 11696, TID: 3583674, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x000000022fc476f4 + 56
5 CoreMotion 0x00000002363c1d9c CoreMotion + 294300
6 CoreMotion 0x00000002363c22cc CoreMotion + 295628
7 CoreMotion 0x00000002363c21dc CoreMotion + 295388
8 CoreMotion 0x00000002363f001c CoreMotion + 483356
9 CoreMotion 0x00000002363f0060 CoreMotion + 483424
10 CoreFoundation 0x00000002309d627c + 28
11 CoreFoundation 0x00000002309d5b64 + 276
12 CoreFoundation 0x00000002309d0e58 + 2276
13 CoreFoundation 0x00000002309d0254 CFRunLoopRunSpecific + 452
14 CoreFoundation 0x00000002309d0f88 CFRunLoopRun + 84
15 CoreMotion 0x00000002363ef9f4 CoreMotion + 481780
16 libclang_rt.asan_ios_dynamic.dylib 0x00000001053e1ef0 _ZN6__asan10AsanThread11ThreadStartEyPN11__sanitizer16atomic_uintptr_tE + 192
17 libsystem_pthread.dylib 0x000000023064e908 + 132
18 libsystem_pthread.dylib 0x000000023064e864 _pthread_start + 48
19 libsystem_pthread.dylib 0x0000000230656dcc thread_start + 4
2019-07-22 16:56:00.955489+0800 VideoCamera[11696:3583674] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 11696, TID: 3583674, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x000000022fc476f4 + 56
5 CoreMotion 0x00000002363c1d9c CoreMotion + 294300
6 CoreMotion 0x00000002363c22cc CoreMotion + 295628
7 CoreMotion 0x00000002363c21dc CoreMotion + 295388
8 CoreMotion 0x00000002363f001c CoreMotion + 483356
9 CoreMotion 0x00000002363f0060 CoreMotion + 483424
10 CoreFoundation 0x00000002309d627c + 28
11 CoreFoundation 0x00000002309d5b64 + 276
12 CoreFoundation 0x00000002309d0e58 + 2276
13 CoreFoundation 0x00000002309d0254 CFRunLoopRunSpecific + 452
14 CoreFoundation 0x00000002309d0f88 CFRunLoopRun + 84
15 CoreMotion 0x00000002363ef9f4 CoreMotion + 481780
16 libclang_rt.asan_ios_dynamic.dylib 0x00000001053e1ef0 _ZN6__asan10AsanThread11ThreadStartEyPN11__sanitizer16atomic_uintptr_tE + 192
17 libsystem_pthread.dylib 0x000000023064e908 + 132
18 libsystem_pthread.dylib 0x000000023064e864 _pthread_start + 48
19 libsystem_pthread.dylib 0x0000000230656dcc thread_start + 4

然而,令我感到困惑的是,Stack Trace给出的信息表明这应该是CoreMotion自己的问题。

我通过搜索,发现Stackoverflow上有相应的问题和回答
https://stackoverflow.com/questions/54607856/main-thread-checker-warning-with-coremotion-only-appearing-on-2018-model-iphone
根据该问答,2018款的iPhone都可以复现此问题。这样看来,这是一个iPhone的bug了。

我于是拿来了我的iPhone 6s进行测试,果然,在我的iPhone 6s iOS 12.3.1上,我的app并不会复现此问题。

然而,经过搜寻,我又发现了如下的bug report
https://openradar.appspot.com/46210367

有人报告,在使用UIInterpolatingMotionEffect时,也会出现UI API called on a background thread: -[UIApplication applicationState] Thread name: com.apple.CoreMotion.MotionThread 错误,且该错误仅在iPhone XS上出现,在iPhone X,6s和6s Plus上则不能复现此错误。因此,该report认为,bug与新的硬件相关,具体什么原因,未知。需要注意的是,该bug report提到的iOS版本为12.1,而截止到本文写作之时,iOS已经升级到了12.3.1,很快就要一年了,该bug仍然存在。

A new instance of CMMotionManager results in a Thread Checker warning
Originator: futuretap
Number: rdar://46210367 Date Originated: 22-Nov-2018 10:46 AM
Status: Duplicate/31658500/Closed Resolved:
Product: iOS + SDK Product Version: 12.1
Classification: Serious Bug Reproducible: Always

Summary:
This is a duplicate of radar #45003816. We encounter the same issue when using UIInterpolatingMotionEffect. The Main Thread Checker kicks in on an iPhone XS@12.1.
iPhone X@12.1.1, iPhone 6s@11.4.1, iPhone 6s Plus@11.4.1 all work fine so it seems to depend on the new hardware.

Creating a new instance of CMMotionManager always results in a Thread Checker exception

Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 9123, TID: 1958556, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x00000002079d7894 + 56
5 CoreMotion 0x000000020e2387a4 CoreMotion + 305060
6 CoreMotion 0x000000020e238cd8 CoreMotion + 306392
7 CoreMotion 0x000000020e238be8 CoreMotion + 306152
8 CoreMotion 0x000000020e26a3cc CoreMotion + 508876
9 CoreMotion 0x000000020e26a42c CoreMotion + 508972
10 CoreFoundation 0x0000000208770888 + 28
11 CoreFoundation 0x000000020877016c + 276
12 CoreFoundation 0x000000020876af54 + 1016
13 CoreFoundation 0x000000020876a844 CFRunLoopRunSpecific + 452
14 CoreFoundation 0x000000020876b5a8 CFRunLoopRun + 84
15 CoreMotion 0x000000020e269d64 CoreMotion + 507236
16 libsystem_pthread.dylib 0x00000002083e5a04 + 132
17 libsystem_pthread.dylib 0x00000002083e5960 _pthread_start + 52
18 libsystem_pthread.dylib 0x00000002083eddf4 thread_start + 4
2018-10-04 12:09:35.737994+0200 test[9123:1958556] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 9123, TID: 1958556, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0

Comments
Happening for me as well, lot of things getting impacted, and yeah only happens on XS, XS Max, and XR

By jchaudhry at May 6, 2019, 11:36 p.m. (reply…)
This issue still happens. Should I file yet another bug report with Apple?

By aruslan at April 2, 2019, 6:34 p.m. (reply…)
Any update on this issue, seems like this is massively impacting a lot of projects

By xuehao.hu at March 29, 2019, 6:11 a.m. (reply…)

Swift语言的随机数:运行效率和安全性(cryptographically good)

Apple推出Swift语言已经很多年了,如今,Swift语言已经进化到了5.1版本,很多细节也越来越完善了,不过,总的来说,Swift语言的overhead仍然较大,具体到随机数上,Swift内置的运行时生成随机数效率低,不适用于模拟,测试和游戏等对于cryptographically good要求不高的场景。

如果读者需要大量生成随机数的话,建议用arc4random或者arc4random_uniform,不要使用arc4random_buf或者Swift语言的random方法。对于测试,模拟和游戏等不需要cryptographically good/secure的情况,可以直接引入C语言类库的rand方法,运行效率更高。

Swift对于内置的Double, Int等类型提供了random方法,用以生成指定的开闭区间内的随机数。然而,经过实际测试,我发现,一方面,Swift语言本身的overhead很大,另一方面,Swift内置的SystemRandomNumberGenerator使用的是arc4random_buf,该方法比arc4random和arc4random_buf慢很多。所以,如果效率非常重要,那么,请不要使用Swift类库的随机数,应该在Swift中调用C/C++语言实现的随机数库。


       var i = 0;
        while(i<100000000){
 //1亿个随机数,每个随机数在闭区间[0,11]内取值,随机数独立同分布
            let _ = arc4random_uniform(11)
            i += 1;
        }
       var i = 0;
        while(i<100000000){
//1亿个随机数,每个随机数在闭区间[0,2^32-1]内取值,随机数独立同分布
            let _ = arc4random()
            i += 1;
        }
       var i = 0;
        while(i<100000000){
            var b:UInt32 = 0;
//1亿个随机数,每个随机数在闭区间[0,2^32-1]内取值
            arc4random_buf(&b, 4)
            i += 1;
        }
        var i = 0;
        while(i<100000000){
            var b:UInt32 = 0;
            b = UInt32.random(in: 0...10)
            i += 1;
        }

以下为一个特别简单的模n同余的Linear Congruent伪随机数生成器,实际测试发现,生成1亿个32位随机数的话,Debug模式下需要耗时29.31秒, Release模式下需要5.06秒。

struct LCG: RandomNumberGenerator {
    var seed:UInt64
    
    init() {
        self.seed = mach_absolute_time()
    }
    
    init(seed:UInt64) {
        self.seed = seed;
    }
    
    mutating func next() -> UInt64 {
        self.seed = 32479 &+ self.seed
        return self.seed
    }
    
}

var i = 0;
while(i<100000000){
    var b:UInt32 = 0;
    b = UInt32.random(in: 0...10, using: &lcg)
    i += 1;
}

经过实际测试,结果如下
1亿个随机数,

随机数生成方法 debug release
rand 11.21秒 0.52秒
arc4random 3.09秒 2.34秒
arc4random_uniform 3.41秒 2.67秒
arc4random_buf 19.69秒 18.79秒
UInt32.random 47.98秒 19.27秒
自定义LCG 29.31秒 5.06秒

UInt32.random实际上会使用Swift内置的SystemRandomNumberGenerator,实际上最终调用了arc4random_buf,然而,与直接调用arc4random_buf相比,UInt32.random在Debug模式下慢了近30秒,Release模式下则慢了16秒,因此,我们可以得出结论,即便是Release模式下,Swift的overhead还是很大的。因此,如果需要高效率生成很多随机数的话,不建议直接使用Swift的Double, Int的random方法。不过很特别的是,rand函数在Debug模式下竟然比arc4random慢很多,甚至和arc4random_buf差不多了,但是,在Release模式下,rand还是最快的,0.52秒就可以生成1亿个伪随机数。

接下来,我们来看看为什么arc4random_buf比arc4random和arc4random_uniform慢这么多。

经过查看苹果的具体实现 https://opensource.apple.com/source/Libc/Libc-1272.200.26/gen/FreeBSD/arc4random.c.auto.html

我们不难发现,arch4random方法的具体代码如下,其中CACHE_LENGTH的值为64

uint32_t
arc4random(void)
{
	int ret;
	os_unfair_lock_lock(&arc4_lock);
	arc4_init();
	if (arc4_count <= 0) {
	    arc4_stir();
	}
	if (cache_pos >= CACHE_LENGTH) {
		ret = ccdrbg_generate(&rng_info, rng_state, sizeof(rand_buffer), rand_buffer, 0, NULL);
		os_assert_zero(ret);
		cache_pos = 0;
	}
	uint32_t rand = rand_buffer[cache_pos];
	// Delete the current random number from buffer
	memset_s(rand_buffer+cache_pos, sizeof(rand_buffer[cache_pos]), 0, sizeof(rand_buffer[cache_pos]));
	arc4_count--;
	cache_pos++;
	os_unfair_lock_unlock(&arc4_lock);
	return rand;
}

而arc4random_uniform实际调用arc4random方法,并且通过多次循环避免modulo bias

/*
 * Calculate a uniformly distributed random number less than upper_bound
 * avoiding "modulo bias".
 *
 * Uniformity is achieved by trying successive ranges of bits from the random
 * value, each large enough to hold the desired upper bound, until a range
 * holding a value less than the bound is found.
 */
uint32_t
arc4random_uniform(uint32_t upper_bound)
{
	if (upper_bound < 2)
		return 0;

	// find smallest 2**n -1 >= upper_bound
	int zeros = __builtin_clz(upper_bound);
	int bits = CHAR_BIT * sizeof(uint32_t) - zeros;
	uint32_t mask = 0xFFFFFFFFU >> zeros;

	do {
		uint32_t value = arc4random();

		// If low 2**n-1 bits satisfy the requested condition, return result
		uint32_t result = value & mask;
		if (result < upper_bound) {
			return result;
		}

		// otherwise consume remaining bits of randomness looking for a satisfactory result.
		int bits_left = zeros;
		while (bits_left >= bits) {
			value >>= bits;
			result = value & mask;
			if (result < upper_bound) {
				return result;
			}
			bits_left -= bits;
		}
	} while (1);
}

也就是说,arc4random和arc4random_uniform由于rand_buffer[64]的存在,每生成64个32位随机数,才需要调用一次ccdrbg_generate,而arc4random_buf每一次调用,都需要调用一次ccdrbg_generate。所以,如果读者需要大量生成随机数的话,建议用arc4random或者arc4random_uniform,不要使用arc4random_buf或者Swift语言的random方法。

对于测试,模拟和游戏等不需要cryptographically good/secure的情况,可以直接引入C语言类库的rand方法,运行效率更高。

说说退休父母投靠在京独生子女落户北京的流程

本人落户在北京朝阳区,前往户口所在派出所咨询了父母投靠的详细细节,于2018年12月底正式向派出所提交材料,2019年5月14日拿到准迁证。准迁证的签发日期是2019年5月9日。

提交材料后,其实没别的事,就是一个“等”字。但是,提交材料以前,花了大量时间理解政策,收集材料。

需要准备的材料中,首先要准备的是我的档案部分,到存档部门直接索要就可以了,现在回想起来,当初我还是犯了一些错误。1. 没有事先准备一个文字的清单,导致第一次去的时候没有拿到查档证明,这个查档证明的内容就是XXX为我处存档人员,根据XX档案,父母为XXX,无兄弟姐妹。我为此不得不第二次去人才办理查档证明,多跑了一次。2. 自己的档案管理不及时,等到要办事用到档案了,才发现档案中缺少高中的毕业生登记表,最后不得不抽空回了老家到了当年上学的高中翻了好多档案,才最终找到。据说当年应该是高考后,到大学时就应该转移的。

在准备材料的过程中,问题最大的就是我父母在广西,父亲退休的时候广西已经不再对企业退休人员发放退休证了,而北京的文件仍然要求退休证,当初我也是感到很麻烦,也到本版进行了咨询。这里要说明的是,在广西的国家干部和事业单位退休人员,还是有退休证的,但是企业职工,不管你是高管还是工人,通通不发退休证,只发待遇资格证了。

最后,我采用的解决办法如下
1 复印了我父亲的“享受养老保险待遇资格证”
2 通过广西省一级的政府信息公开,要到了盖章的红头文件,该文件表
明广西确实不对2008年以后退休的企业职工发放退休证了。该文件为“广西壮族自治区劳动和社会保障厅关于全区统一使用享受基本养老保险待遇资格证的通知(桂劳社发[2008]3号)”

为了政府信息公开,我请人专门去南宁了一趟。之所以专门去南宁一趟,主要是之前我的父母和政府打交道的时候发现,大量政府人员认为“你们要这个文件有什么用,有资格证就行了”,当政府认为一个东西没有必要的时候,根本就不会给你办理,如果你不亲自出面,工作人员感受不到必要性。具体办理地点为 广西壮族自治区政务服务中心 南宁市青秀区怡宾路6号。请各位读者注意,在本案例中,该文件的发文单位为省级单位,所以要去自治区政府服务中心,而不是南宁市政府服务中心。

还有一个就是独生子女证,这个证件的主要问题就是子女姓名和我的姓名不一致,最后采取的办法是父母到了当地街道的计生办,去了数次,体现了自己的诚意,表明了重新办证的必要性和自己确实只有一个子女的事实,最后,街道计生办颁发了新证件。

以上就是我在办理父母投靠进京落户的时候遇到的一些难点和问题。其余的材料,在我看来,都是按照派出所的文件按部就班就好了,唯独以上几个问题是一开始很困扰我的。

最后说点题外话,由于这次父母投靠事情,我看到了自己和父母的大量原始档案,对于中国的历史问题有了更深刻的理解。

我终于明白身份证的生日和实际生日不一样是怎么回事了。新闻中说的档案中农历和公历生日混为一谈,生日填错的情形,真实发生在我父母的档案中,最后,这些问题都通过手写一份材料解决了。

第三调解室3月19日晚的节目给我们的警示

第三调解室昨晚(3月19日)的节目有很大的警示意义。这一期节目的家人还上过2018-12-21和2018-12-22两期节目,年夜饭后起风波(1)和(2)。

北京市丰台区石榴庄村的一位老父亲指望女儿养老,做主自己百年之后送一套房子给女儿,女儿觉得没有后顾之忧就卖了自家的房,没想到女儿先去世,老父亲反悔不愿意送房子了,现在女婿和外孙女即将无家可归。

某老父母有两个儿子一个女儿,女儿女婿别处有房,后来父母和儿子在农村拆迁得了好几套房,女儿由于有房就没有拆迁得房。父母指望女儿和女婿养老,所以2011年和儿子女儿一起签了一个协议,拆迁房子送一套给女儿女婿,以后女儿女婿负责养老送终,女儿觉得自己公公婆婆已经先走了,以后爸妈也指望自己养老,所以就把自己家里的房子卖了,想要以后带着老公和女儿与父母一起住。大姐曾经有一套唯一住房,2011年签了分家协议以后没多久,2012年就卖掉了自己的唯一住房,大姐的原来的房子是1990年亚运会拆迁的安置房。她的原话是,我就没有后顾之忧了,所以我就把自己的房子卖了。

2016年母亲去世,随后不久女儿因为脑溢血也去世了。老父亲觉得女儿死了,以后养老就不能指望女婿和外孙女了,所以想要推翻原来的养老协议,也不送房子了,现在女婿和外孙女即将无家可归。老父亲说,他们没房子,搬走后去哪里住,我管不着了。

最后结局,由于老母亲先死,女儿后亡,外孙女可以代位继承姥姥的资产,最后舅舅和姥爷给了外孙女100万作为遗产的对价。

想着这家人都是北京人,女儿当年觉得没有后顾之忧卖房的时候北京房价比现在低多了,女婿和外孙女就算有这100万,在北京也买不了什么房,女儿当年觉得自己没有后顾之忧的时候,她肯定没有没有想到自己的丈夫和女儿会面临这种惨痛的结局。

背景介绍
郑家八口人分了五套房,父母二人,二哥三口,小弟三口,大姐的户口不在娘家,所以没有得到拆迁安置房。
八口人分成了两户按照,父母和二哥分了两套两居室90, 75,一套三居室115,小弟分了两套两居室。

2011年12月12日一家人签了分家协议,小弟的一套75平米的房子给姐姐,父母百年以后,小弟的75平米房子首先过户给姐姐,最后,父母的115平米的三居室过户给小弟。
2013年拆迁房得到了,大姐认为这套房子就是自己的房子了。

[php-fpm slow log应用]wordpress的Captcha Booster插件导致我的个人blog因为最近有一段时间无法访问

我的blog最近有一段时间不能访问了,总是报504 Gateway Timeout。
具体原因如下。

我用php-fpm作为fastcgi server,nginx通过reverse proxy连接到php-fpm,然后php-fpm执行wordpress代码并将html数据返回给用户。

我的wordpress的编辑后台添加了Captcha Booster插件,这样登录界面就有captcha了。然而,没想到就是这个插件,导致了我的blog无法访问。

我通过启用了php-fpm的slow log,发现问题出在plugins/wp-captcha-booster/wp-captcha-booster.php:552

[14-Sep-2018 16:55:58.866159]  [pool www] pid 31454
script_filename = /usr/share/nginx/wordpress/wp-admin/index.php
[0x00007f5ea7fe2398] file_get_contents() /usr/share/nginx/wordpress/wp-content/plugins/wp-captcha-booster/wp-captcha-booster.php:552
[0x00007f5ea7fe21b8] get_ip_location_captcha_booster() /usr/share/nginx/wordpress/wp-content/plugins/wp-captcha-booster/wp-captcha-booster.php:576
[0x00007f5ea7fe2060] blocking_visitors_captcha_booster() /usr/share/nginx/wordpress/wp-content/plugins/wp-captcha-booster/wp-captcha-booster.php:901
[0x00007fffb287fcb0] user_functions_for_captcha_booster() unknown:0
[0x00007f5ea7fe1ef8] call_user_func_array() /usr/share/nginx/wordpress/wp-includes/class-wp-hook.php:286
[0x00007f5ea7fe1dd0] apply_filters() /usr/share/nginx/wordpress/wp-includes/class-wp-hook.php:310
[0x00007f5ea7fe1c70] do_action() /usr/share/nginx/wordpress/wp-includes/plugin.php:453
[0x00007f5ea7fe1ab8] do_action() /usr/share/nginx/wordpress/wp-settings.php:450
[0x00007f5ea7fe1990] +++ dump failed

在Captcha Booster的代码中

wordpress/wp-content/plugins/wp-captcha-booster/wp-captcha-booster.php 550到559行

                        $api_call  = TECH_BANKER_SERVICES_URL . '/api/getipaddress.php?ip_address=' . $ip_address;
                        if ( ! function_exists( 'curl_init' ) ) {
                                $json_data = @file_get_contents( $api_call );  // @codingStandardsIgnoreLine.
                        } else {
                                $ch = curl_init();  // @codingStandardsIgnoreLine.
                                curl_setopt( $ch, CURLOPT_URL, $api_call );  // @codingStandardsIgnoreLine.
                                curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Accept: application/json' ) );  // @codingStandardsIgnoreLine.
                                curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 );  // @codingStandardsIgnoreLine.
                                curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );  // @codingStandardsIgnoreLine.
                                curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );  // @codingStandardsIgnoreLine.

上述代码中TECH_BANKER_SERVICES_URL的常量的值为https://tech-banker-services.org

我发现,不知什么原因https://tech-banker-services.org已经不可用了,这导致这几行代码执行将不会成功。
而一开始我没有安装php-curl,这样,wordpress就会执行

$json_data = @file_get_contents( $api_call );  // @codingStandardsIgnoreLine.

所以这就导致wordpress会卡在这一行代码非常久,nginx也就无法及时获得数据,从而超时报504 Gateway Timeout了。

我现在临时的解决办法如下
首先安装php5-curl

> apt-get install php5-curl

接下来将

curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 );

改为

curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 1 );

这样子,wordpress执行的时候,只会被卡1秒了。

由于golang.org不能正常访问导致的glide install错误Cannot detect VCS

Go语言是Google推出的一种程序语言,Glide是go语言的包管理器,可以解决项目的依赖关系。目前,Glide建议我们尽快迁移到dep上。

然而,由于许多常用的包都在golang.org上,而由于众所周知的原因,golang.org一直处于中国大陆无法正常访问的情况,所以,glide update总会出现诸如Cannot detect VCS的错误。

Mac和Linux下,可以用如下命令添加golang的镜像

rm -rf ~/.glide
mkdir -p ~/.glide
glide mirror set https://golang.org/x/mobile https://github.com/golang/mobile --vcs git
glide mirror set https://golang.org/x/crypto https://github.com/golang/crypto --vcs git
glide mirror set https://golang.org/x/net https://github.com/golang/net --vcs git
glide mirror set https://golang.org/x/tools https://github.com/golang/tools --vcs git
glide mirror set https://golang.org/x/text https://github.com/golang/text --vcs git
glide mirror set https://golang.org/x/image https://github.com/golang/image --vcs git
glide mirror set https://golang.org/x/sys https://github.com/golang/sys --vcs git
glide mirror set https://google.golang.org/grpc https://github.com/grpc/grpc-go --vcs git

比特币的UTXO和以太坊的Account Nonce

比特币和以太坊都是目前知名的数字货币,然而,两者在实现上采用了截然不同的模型。

比特币采用的是未消费输出(Unspent Transaction Output,简写UTXO),而以太坊则采用账户余额体系。

比特币的交易模型如下

一笔交易 TX 包含多个输入(input)和多个输出(output,一个输出包含两个内容,一个是比特币数量,单位为satoshi,另一个就是接受比特币的收款人的public key),当TX 1,2,3,4,5,6都还没有发生的时候,TX 0的output0和output1都没有被别的交易的输入而引用,此时,TX 0的output0和output1就被称之为未消费输出(UTXO),当TX 0的output0被TX 1的input0引用之后,TX 0的output0就被消费了。

不难看出,比特币为了防止双花(double spend),一个输出不能同时被两个输入而引用。如果A的一个UTXO有100个比特币,而我们希望转20个给B,那么,我们需要将这100个比特币的UTXO消费掉,将其中80个发送到一个新的收款人为A的UTXO中,20个发送收款人为B的UTXO中,交易完成以后,之前的有100个比特币的输出就从UTXO变成了Spent Transaction Output了。

对于比特币来说,输出的源头就是挖矿而产生的coinbase交易,一个coinbase交易的输入是一个coinbase,输出为区块奖励。挖矿奖励的coinbase交易为一个区块的第一个交易。

我们不难看到,对于比特币来说,如果我们需要计算一个地址有多少比特币余额,我们必须将该地址对应的所有的UTXO余额累加起来,才能得到账户的余额,然而,如果我们的账户拥有10个UTXO,那么,理论上,我们可以同时发送10笔交易,这笔交易同时打包到一个区块之中,只要每一笔交易引用的UTXO是不同的就好。

总结,比特币的UTXO模型,使得单个账户可同时发送多笔交易,且多笔交易之间可以不互相产生影响,这使得比特币的交易天生有着很好的并发友好特性。

而对于以太坊来说,由于采用了账户余额体系,因此,以太坊在防止双花(Double Spend)上就没有办法采用比特币的方法了。为此,以太坊规定,每一个账户有一个nonce值,这个nonce值等于该账户的累计发起的交易数量,如果该账户发起一笔交易,那么,交易的数据中必须包含一个nonce值,该值必须大于账户的nonce值,否则,该交易属于非法交易。如果交易的nonce值减去账户的nonce大于1,那么,该笔交易暂时不能被包含到区块中,必须等到nonce值为账户的nonce值加一的交易被打包以后,该笔交易才能被打包到区块中。如果有两笔交易都是同样的nonce值,那么,只有其中一笔能够成功。

以太坊采取了账户体系,并通过状态变化(State Transition)记录了交易对账户的影响。

我们不难发现以太坊的nonce值是必要的。

首先,如果没有nonce值的话,黑客可以通过重放攻击来偷盗数字货币,假设A向B转行了1 ETH,而且以太坊没有nonce机制,那么,B可以将之前成功的转账交易数据在以太坊网络上重复发送,这样,B就可以源源不断的获取ETH,而这显然不是A期待的。

其次,nonce值的设计确保了交易一定是顺序执行的,避免了双花(Double Spend)问题。

    然而,以太坊的设计也造成了一些天然的缺点。

  1. 如果用户发送了一笔交易,并且选择了很低的矿工费(如gasPrice设置为0),那么,该笔交易可能数日甚至几个月都无法得到确认,这会导致用户的账号在很长的时间内都无法发起转出交易。而比特币就没有这个问题。
  2. 官方的Ethereum Wallet和Mist都没有提供取消交易的功能,实际上,我们可以通过发送和上一笔交易相同的nonce的新的交易,且新的交易为自己转账给自己,并选择高矿工费(如gasPrice为50或者100 Gwei),那么上一笔交易将会被以太坊网络抛弃。然而,由于Ethereum Wallet和Mist既不支持用户自定义nonce,也不支持自己转账给自己,所以,事实上客户没有好的办法取消自己的交易。
  3. 尽管以太坊的出块速度比比特币高很多,平均15秒出一个快,且理论上以太坊没有设置block limit,实际上以太坊的TPS仍然很低,只能达到10左右。这是因为有很多复杂交易,例如创建新的合约,一笔交易就上百万的gas消耗,而以太坊的区块gas limit通常也就800万左右,一个区块能够容纳的交易个数就有限了。尽管矿工可以提升gas limit,由于单个区块交易越多,矿工计算量就越大,所以,矿工不会轻易提升区块的gas limit,毕竟小区块更容易挖出来,从而挣到block reward。基于此考虑,以太坊的TPS很难提升。

然而,不论是比特币还是以太坊,亦或是其他的公有链的区块链平台,由于他们在设计上都考虑到了全球节点的同步,这就决定了出块速度不能太快,否则必然大量节点无法及时同步最新的状态,这就导致共识无法及时形成,很容易造成区块的分叉。

以太坊 Ethereum ERC20代币批量转账接口

以太坊(Ethereum)作为一个知名的区块链平台,大量的代币发行(Initial Coin Offering)通过以太坊进行,而代币通常为以太坊上一个遵循了ERC20规范的智能合约。

如果一个以太坊智能合约实现了以下接口,那么,这个智能合约即为一个ERC20代币。

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// ----------------------------------------------------------------------------
contract ERC20Interface {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

然而,我们不难看到,ERC20规范中只有一对一转账的transfer和transferFrom,如果我们要一次实现向成千上万个地址转账,那么,我们就需要产生上万个transfer交易,这未免太低效了。

所以,不少ERC20代币都实现了批量转账的接口。
如近期爆出漏洞的BEC(https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d#code)实现了batchTransfer函数。

SMT(https://etherscan.io/address/0x55f93985431fc9304077687a35a1ba103dc1e081#code)也实现了allocateTokens函数。

他们都可以实现一笔以太坊交易(Transaction)完成对多个账户的代币转账或初始化。

本文提出了一种实现一对多转账的方法,该方法名称为transferMultiple

首先,本文默认已用了SafeMath库

library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

接下来,transferMultiple

transferMultiple实现了从msg.sender向count个_tos地址转账,且_tos[i]获得_values[i]的代币。

首先,第一个for循环进行了前置检查,确保了每一个_tos地址都是非0地址,同时,计算了转账的总额,并将总额记录到total变量中。在计算过程中,为了防止溢出,我们采用了SafeMath库,并且每一次都要比较当前计算出来的总额total和上一笔总额total_prev,确保total大于等于total_prev,双重保证不会整数溢出导致转账故障。

其次,第二个for循环不直接调用transfer方法,而是直接修改内部变量,这是因为前置检查已经做了,如果再次调用transfer函数的话,会再次执行额外的不必要的前置检查,会增加消耗的gas。

function transferMultiple(address[] _tos, uint256[] _values, uint count)  payable public returns (bool success) {
        uint256 total = 0;
        uint256 total_prev = 0;
        uint i = 0;

        for(i=0;i= total_prev);
        }

        require(total <= balanceOf(msg.sender);

        for(i=0;i<=count-1;i++){
            balances[msg.sender] = SafeMath.sub(balances[msg.sender], _values[i]);
            balances[_tos[i]] = SafeMath.add(balances[_tos[i]], _values[i]);
            Transfer(msg.sender, _tos[i], _values[i]);
            //以上三行也可以替换为下一行,好处是不需要假设客户的余额保存在类型为mapping的balances变量中,坏处是会额外增加很多不必要的前置检查,额外消耗gas
            //transfer(_tos[i], _values[i]);
        }

        return true;
    }

大家一定很关心,那么,我用transferMultiple一次实现一对一万转账行不行呢。
实际测试表明,一对四十转账的时候,大约消耗的gas在130万左右,而截止目前本文写作之时,以太坊一个区块的gas上限大约为800万,所以,大家不难看出,一次实现一对二百四十转账就差不多将区块的gas上限占满了。如果一次转账的收款对象数量太多,完全会因为超出区块gas上限而导致交易无法成功。