31
1月

区块链支付系统与传统支付系统有哪些不同?

在这里不讨论区块链支付有没有未来、加密币是不是庞氏骗局、是不是割韭菜这样形而上的问题,只从技术角度探讨区块链支付相对与传统第三方支付的一些差异。

按照以上对比,似乎区块链支付从各方面都碾压传统第三方支付平台。

其实不然,相对于成熟的基于法币支付体系的第三方支付,区块链支付在实际应用中还存在诸多致命性问题,例如:

1、法币-加密币出金、入金难题

区块链支付只是解决了区块链及加密币体系内的支付问题,但没有解决“用户持有的加密币从什么地方来?用户持有的加密币怎样变现为法币?”。

可以说:没有通畅的法币-加密币的出金、入金解决方案,极大程度制约了区块链及加密币的发展。

受制于各国政府KYC、AML合规性要求,目前市场上的OTC在用户体验、市场流动性、交易速度、交易成本等方面都存在较大问题。

2、币价波动导致的资金安全风险

现实消费场景大部分商家只愿意接受法币结算,因此目前大部分应用场景下,都由支付服务商帮助商家收取加密币,然后以法币形式结算给商户。虽然可以通过币币交易、OTC交易等方式将收取的加密币通过量化交易算法转为稳定币或法币,但市场流动性、币价波动性、交易成本等因素导致支付服务商仍存在较大资金安全风险。

3、持有加密币、加密币钱包的受众人群及范围还是太少太少,还局限在小众圈子里,尤其是2017、2018火热的ICO割了一大堆韭菜后。

4、实际落地应用场景极少

这些致命问题什么时候能够解决,有什么好的解决办法呢?业内也在探索中。

一点体会:

最近一段时间,由于行业监管趋严,市场上出现了诸多新玩法,核心思路与区块链“去中心化”的核心理念不谋而合,还有一些创新玩法也尝试借鉴“去信任化”的思路,利用智能合约解决保证金缴纳去中心化担保问题。市场刚性需求倒逼金融服务“创新”,在此大变革进程中,丢掉各种噱头,回归到区块链最核心的理念和价值,与互联网成熟的应用场景结合,或许是寒冬中区块链项目最好的出路。

26
1月

拼多多优惠券事故思考

备注:在知乎关于拼多多优惠券事故邀请的回答。

回答很多了,大家从法律角度、运营角度、风控反欺诈等角度阐述得比较详细了。我只谈对问题原因的看法。

与大部分回答看法不同,个人认为拼多多的风控反欺诈体系及风控意识应该不至于像大家说的那么弱智。拼多多在这几年发展过程中,一直是薅羊毛人群的重点对象,因此类似的挑战肯定都遇到过,也出现过大大小小的各种风控相关问题,要是能力差,早就死在路上。虽然这一次事故确实低级,但不至于冠上风控意识薄弱、风控能力不足帽子。

拼多多短短三年发展到现在规模,可以说互联网企业高速发展的典范,上述回答提到的反薅羊毛风控反欺诈体系、产品运营变更上线流程、风控实时告警体系、系统风控检测体系,我相信拼多多都有对应的体系、规章流程制度。像这样规模的企业,都有对应的专业人才,但问题是:为何这些体系、规章制度在简单的优惠券设置上线事件中没有一个环节及时发挥止损点作用?

对大部分互联网公司,风控反欺诈体系建设都有专属的部门来负责,其他业务都不能自造轮子,必须遵循统一的风控架构,也就是说:反薅羊毛、账户资金异常监控、业务流量异常监控等都会纳入到风控反欺诈体系中。在假定拼多多风控反欺诈能力不弱智的前提下,为何风控反欺诈体系未及时起到风控告警作用?

按照自己经验,这样的问题,不可能是单一原因,可能的原因:

a、业务部门基于业务高速发展、快速上线需要,并未真正按照风控规范体系要求将各种细化指标及业务数据上报风控平台,有些业务平台可能根本就接入风控平台;

b、风控部门、审计部门不愿意深入了解熟悉业务及产品,从而无法发现各种业务深层风险、业务交叉风险,风控部门只是按照想当然的经验、业务部门反馈,设置对应的风控规则,而这完全将风控交付给业务部门风险意识、经验、业务发展等诸多因素上;

c、企业在高速发展过程中,做取舍时候有意、无意忽视了各种警示信息

以拼多多事故为例,技术产品在做优惠券运营管理平台时候为了通用,可能不考虑金额上限及流程审核问题。但对一家公司而言,任何涉及资金投入的活动,不管是现金还是像优惠券这样的虚拟账户资金,都是公司财务资产,按照财务管理制度,针对不同金额肯定都有分级审批流程。拼多多这样上市企业,在上市前、上市后的各种IT审计、财务审计、管理审计等过程中,肯定都是知名审计所审计过的,那么问题来了,是哪个环节问题呢?

按照自我经验理解,应该是为了高速发展,主动延迟了一些“非高优先级”问题的解决时间点,于是一拖再拖,最终没有下文,成为惯例。

还能列举出一堆可能原因,这里要表达的核心观点为:拼多多事故表现为风控系统问题,但可能最本质的根源还是企业高速发展与管理精细化矛盾。

作为旁观者,提出问题总是最容易的。怎样解决以上一堆问题,尤其是对拼多多这样的高速发展企业?

这么多年的创业经历,让我越来越理解作为高速发展与管理精细化间矛盾和难处。对于通过模式创新起家的高速发展企业,意味着支撑模式创新的文化体系、管理体系、运营体系、技术体系等至少与已有的成功经验有所差异,各种体系建立只能参照别人成功经验但无法直接拿来就用。

有很多方法论体系可供参考,我这几年最受益匪浅的是RCA(Root Cause Analysis)制度,也就是在运营过程中,不管大事、小事都真正追根溯源,将每一次运营、运维事故都作为一次企业及团队成长、制度完善机会。

声明:与拼多多无任何利益相关关系。

02
4月

Ethereum调用机制总结call/delegatecall/callcode/send/transfer

Ethereum中涉及各种类型的方法调用。一个编程中常用的call概念,可能为Ethereum Vitrual Machine(EVM)内的call,又可能为EVM外的call,又涉及诸如Message/Transaction等基本概念;同时还包括call/delegatecall/callcode/send/transfer/sendTransaction等等方法。

对这些调用机制及基本概念的说明散落在 solidity文档黄皮书白皮书 中,初学者容易搞晕。这里对这些基本概念、调用机制及用法做一下整体梳理。

Ethereum调用机制总结

PDF版本:Ethereum调用机制总结

PNG版本:Ethereum调用机制总结

示例代码:测试代码下载

pragma solidity ^ 0.4.18;

contract A {
uint public var2;
uint public var1;
B b1;
address addrB;

function A() public {
b1 = new B();
addrB=address(b1);
}

function callSet(address addr, uint param) public returns(uint) {
//addr.call(bytes4(keccak256(“set(uint)”)), param); //使用了uint,调用不生效。应当使用uint256; uint是uint256的昵称,但在ABI调用时候,只能使用长类型。

//使用address.call
addr.call(bytes4(keccak256(“set(uint256)”)), param); //call 使用的是被调用者的存储,因此改变的是B.var1值

//使用实例方式,可以直接采用callee.method形式调用,从而直接获取函数返回值
/*
B b2 = B(addr);
uint result = b2.set(param);
return result;
*/

//使用 new B()方式方式调用
// uint  b1=b1.set(param);
// addrB.call(bytes4(keccak256(“set(uint256)”)), param);

return 1;
}

function delegatecallSet(address addr, uint param) {
//addr.delegatecall(bytes4(keccak256(“set(uint)”)), param); //使用了uint,调用不生效。应当使用uint256; uint是uint256的昵称,但在ABI调用时候,只能使用长类型。
addr.delegatecall(bytes4(keccak256(“set(uint256)”)), param); //delegatecall和callcode都是使用调用者的存储,特别要注意调用者和被调用者合约变量定义顺序,目前改变的是A.var2的值。
B b2 = B(addr);  //使用实例方式,可以直接采用callee.method形式调用,从而直接获取函数返回值
uint result = b2.set(param);
// uint  b1=b1.set(param);
// addrB.call(bytes4(keccak256(“set(uint256)”)), param);
}

function callcodeSet(address addr, uint param) {
addr.callcode(bytes4(keccak256(“set(uint256)”)), param);
}

function getVar1() public view returns(uint) {
return var1;
}

function getVar2() public view returns(uint) {
return var2;
}

function getAddr() public view returns(address) {
return addrB;
}
}

contract B {
uint public var1;
event MyEvent(uint param, uint result);

function set(uint param) public returns(uint) {
var1 = param;
MyEvent(param, 1);
return param;
}
}

23
12月

R语言常用统计分布的蒙特卡洛模拟

为加深对各种常用统计分布的理解,更好掌握R语言对应的各类分布的概率函数(d、p、q、r族)以及广义线性模型的使用,研究了一下常用统计分布数据的模拟生成方法,收获颇多。

各类常用统计分布蒙特卡洛模拟数据生成的大致思路:

1、构造自变量x的均匀分布
2、根据对应分布的均值函数,构造x变量对应的均值。(广义线性模型的link 函数参考
https://en.wikipedia.org/wiki/Generalized_linear_model#Link_function
3、将均值代入,R中对应分布的随机变量生成函数,得到因变量y(例如正态分布为rnorm、泊松分布为rpois)

 

代码:

#norm distribution simulation
set.seed(1234)
num=100
beta0=1
beta1=0.2
x=beta0+beta1*runif(n=num,-1,1)
y=rnorm(num,mean=x,sd=1)
model=glm(y~x,,family=gaussian(link=’identity’))

 

#possion distribution simulation
set.seed(1234)
num=100
beta0=1
beta1=0.2
x=beta0 + beta1*runif(n=num, min=0, max=5)
lambda=exp(x)
y=rpois(n=num, lambda=lambda)
model = glm(y~x, family=poisson(link = log))

 

#Exponential/Gamma distribution simulation
set.seed(1234)
num=100
beta0=1
beta1=0.2
x=beta0 + beta1*runif(n=num, min=0, max=5)
y=rexp(num,rate=exp(-x))
model=glm(y~x,,family=Gamma(link=’log’))
#使用nls模拟
df=data.frame(x,y)
model=nls(y~exp(a+b*x),data=df,start = list(a=0,b=0))

 

#logistic/probit distribution simulation
set.seed(1234)
num=100
beta0=1
beta1=0.2
x=beta0 + beta1*runif(n=num, min=0, max=5)
#logistic distribution logit=log(odds)=log(p/(1-p))
odds=exp(x)
probs=odds/(1+odds)
#probit distribution probit=Cumulative normal pdf
#probs=pnorm(x)

 

y=rbinom(n=num,size=1,prob=probs)
model=glm(y~x1+x2,family = binomial(link=”logit”))

 

#bionimal/Categorical/Multinomial distribution simulation
library(nnet)
y=rbinom(n=num,size=3,prob=probs)
model <- multinom(y ~ x1 + x2)

代码下载


参考资料:《 Monte Carlo Simulation and Resampling Methods for Social Science》

https://www.sagepub.com/sites/default/files/upm-binaries/57233_Chapter_6.pdf

17
12月

R中fitted()与predict()函数关系以及predict type参数解析

R文档对fitted()、predict()函数以及predict函数type参数取值的具体含义说得很不清楚,网上也没有清晰的解释。总结一下。

一、说明

目的一、R中fitted和predict的关系
目的二、以logistic为例,解析predict中type参数不同取值的关系

1、R中fitted和predict的关系
fitted对无link函数模型(例如线性回归),fitted值和predict的值相同
对有link函数的模型(例如logistic、Binomial),
fitted是link函数作用前(或者link逆函数inverse of the link function作用后)的预测值
predict是link函数作用后(或link逆函数inverse of the link function作用前)的预测值
link function  https://en.wikipedia.org/wiki/Generalized_linear_modelLink_function

2、logistic中predict中type参数不同取值的关系
predict中type参数的可选项有:default, type="link",type="response",type="terms"

假设log(odds)=log(p/(1-p))=alpha+beta1*x1+beta2*x2+…+betan*xn
a.、type="link",type为缺省值
  type="link"为缺省值,给出logit线性函数预测值,link=alpha+beta1*x1+beta2*x2+…+betan*xn

b、 type="response"
  type="response" 给出概率预测值。
  response=predict(model,type = "response"),则
  log(response/(1-response))=alpha+beta1*x1+beta2*x2+…+betan*xn=link,或:link=exp(response)/(1+response)

c、type="terms"
  type="terms"表示各个变量的预测值(包括前面参数在模型中的值)。
  terms=predict(model, newdata = traindata,type = "terms")的结果为各个变量term的值
  term1=beta1*x1,term2=beta2*x2, …
  sum(term1,…,termn)+attr(terms,"constant")=log(odds)=log(response/(1-response))
  attr(terms,"constant")的含义 http://blog.minitab.com/blog/adventures-in-statistics-2/regression-analysis-how-to-interpret-the-constant-y-intercept

二、代码

代码下载

#fitted和predict的关系演示
#linear regression
#no link function
num=100
x1=rnorm(num)
x2=rnorm(num)
lr=1+2*x1+3*x2
model=lm(y~x+x2)

fit=fitted(model)
pred=predict(model)
print(all.equal(fit, pred))
#对无link函数的模型,fitted值与predict值相同

#possion
#link=log
num=100
x=rnorm(num)
y=rpois(num,lambda = exp(x))
model = glm(y~x, family="poisson")
fit=fitted(model)
pred=predict(model)
print(all.equal(log(fit),pred))
print(all.equal(fit,exp(pred)))
#对有link函数的模型,fitted值为link函数作用前的值,predict为link作用后的值

#logistic
#link=logit=log(p/(1-p))
num=100
x1=rnorm(num)
x2=rnorm(num)
lo=1+2*x1+3*x2
odds=exp(lo)
probs=odds/(1+odds)
y=rbinom(n=num,size=1,prob=probs)
model=glm(y~x1+x2,family = binomial(link="logit"))

fit=fitted(model)
pred=predict(model)
print(all.equal(log(fit/(1-fit)),pred))
print(all.equal(fit,exp(pred)/(1+exp(pred))))
#对有link函数的模型,fitted值为link函数作用前的值,predict为link作用后的值

#以logistic模型为例子,验证predict中type为缺省值,type="link",type="response",type="terms"关系
#predict中type="link"时的fitted及predict
link=predict(model,type="link")
print(link)
print(all.equal(pred,link))
#type=link为缺省值

#predict中type="response"时的fitted及predict
response=predict(model,type="response")
print(response)
print(all.equal(fit,response))
print(all.equal(pred,log(response/(1-response))))

#type="terms"时的fitted及predict
terms<- predict(model,type="terms")
term=terms[,1]+terms[,2]+attr(terms,"constant")
print(term)
print(all.equal(link,term))
print(all.equal(response,exp(term)/(1+exp(term))))

参考资料:

https://stackoverflow.com/questions/12201439/is-there-a-difference-between-the-r-functions-fitted-and-predict

16
12月

资产端线上对接资金端系统?

我在知乎关于提问“资产端线上对接资金端系统?” https://www.zhihu.com/question/67426957/answer/277256804  的回答。

虽然最近现金贷整顿,但此问题涉及的内容的应用场景挺多的,简单说一下思路。

资产端平台接入资金端平台时候,需要重点考虑两大部分的内容:

1、交易结构设计

由于合作双方监管合规性问题,交易结构设计主要解决资金端资金怎样以合规、安全的形式流转到资产端。这里的合规性,不单纯只是资产端或资金端,双方都涉及合规性。

在交易结构设计时候,涉及很多因素,例如:

a、资产端的资质,例如是P2P平台、互联网小贷平台、消费金融公司、助贷机构等的资金合规来源渠道并不同。

b、资金端的资质,例如是金融机构(银行、保险公司、融资性担保公司等)、P2P、互联网小贷、消费金融公司、企业、股东等的资金。

c、资产方的资产质量

d、实际放款方,资产最终归属。涉及放款资质,是否需要债权转让、ABS等

e、担保方式

f、资金放款、还款流转流程,支付/代扣渠道

g、资产端的保证金、杠杆、资金成本等

交易结构设计是整个合作最核心的内容,也是最难的地方,需要双方一起就双方实际情况探讨合规的合作模式。

交易结构设计影响了资产端、资金端合作模式、金融产品设计、平台对接的业务流等内容。

2、平台化设计

低成本、稳定、多元化的资金来源是资产端平台的核心竞争力,因此一般情况下,资产端都会尽量接入更多的资金端平台,以保证资金的持续供给。因此从平台建设角度来说,需要在设计时候考虑平台的扩展性、可运营性等因素,以满足多个资金端接入的需求。

在合作之初以验证合作模式为主,因此平台功能平台功能没必要设计得过于复杂。但随着业务规模扩大,低成本的运营能力成为平台的核心能力之一,可以说每一个资产端的资金对接平台最终都会演变成资产端、资金端的撮合路由平台。

一张图供参考

产品架构图

13
12月

R语言信用评分卡模型(脑图、代码)

在消费金融中,风险模型组(申请评分卡、行为评分卡、催收评分卡、失联概率、违约概率PD模型等)、营销模型组(客户分层模型、客户响应模型、客户流失模型)、反欺诈模型组等都涉及了信用评分卡模型的应用。关于信用评分卡模型的使用及代码实现,网上公开的完整资料较少,且大都写得语焉不详。基于R语言做了一个完整例子,算是对最近一段时间工作总结。

分为两部分:

1、评分卡模型使用总结脑图

2、评分卡模型R语言代码

1、评分卡模型使用总结脑图(完整脑图点击下载)

2017-12-13_205830

《评分卡模型使用总结脑图》下载

2、R语言代码

scorecard.r

08
6月

使用docker来实现libreoffice并发转换docx文件为pdf

由于libreoffice的soffice不支持多进程,只允许同时有一个进程来转换docx文件为pdf文件,因此无法多线程并发执行转换操作。

为实现并发转换操作,有两种方案:

方案一:将转换代码部署到不同的服务器上,此种方案需要单独服务器,对资源要求较高,部署也麻烦;

方案二:利用docker对资源的隔离特性,把libreoffice 放到docker中,然后启用多个libreoffice容器,java代码调用不同容器的soffice命令来并发转换docx文件。

1、安装docker相关程序

CentOS7已经缺省支持docker

yum install docker

service docker start

chkconfig docker on

简单的安装使用可以参考 https://linux.cn/article-4340-1.html

2、把缺省的docker镜像仓库换成国内的网易镜像

echo "DOCKER_OPTS=\"\$DOCKER_OPTS –registry-mirror=http://hub-mirror.c.163.com\"" >> /etc/default/docker

service docker restart

3、下载centos docker镜像

docker pull centos

4、启动centos的docker镜像

docker run -it centos /bin/bash

5、进入docker容器

docker run -it centos -v /root/install:/root/install  /bin/bash

由于生产镜像时候,可能需要与宿主主机共享一些文件,例如中文ttf文件,因此在run 时候使用了-v参数,用于宿主主机与docker container之间的共享目录。

假设docker 容器与宿主机的共享目录为/root/install 。

Docker容器和主机之间共享数据,可以参考 https://www.howtoing.com/how-to-share-data-between-the-docker-container-and-the-host/

6、安装libreoffice、中文字体库等所需的软件

yum install libreoffice-writer.x86_64

yum groupinstall "Fonts"

yum groupinstall "Input Methods"

7、生成安装了libreoffice、中文字体等软件的docker 镜像

docker commit CONTAINER_ID liang/libreoffice1

其中:

CONTAINER_ID是docker ps -a得到了容器id docker ps -a|grep "centos"|awk ‘{print $1}’

liang/libreoffice1 是我们生成的docker镜像

8、停掉刚才启动的centos容器

docker stop CONTAINER_ID

9、启动我们新生成的docker 镜像

根据并发转换docx为pdf文件的需要,基于我们自己生成的docker镜像,启动多个docker的容器,利用docker容器对资源的隔离,调用不同容器中的soffice来并发处理不同的pdf文件

docker run –name office1 -it -d -v /root/pdf1:/root/pdf1  liang/libreoffice1

docker run –name office2 -it -d -v /root/pdf1:/root/pdf1  liang/libreoffice1

镜像文件与宿主机的共享目录为/root/pdf1和/root/pdf2,在共享目录中放入要转换的docx文件

docker exec -it office1 /bin/bash

 

10、调用soffice命令行,将docx转为pdf

docker exec -it office1 /bin/soffice –headless  –invisible –norestore –nodefault –nolockcheck  –nofirststartwizard –convert-to pdf:writer_pdf_Export –outdir /root/pdf1 /root/pdf1/liang1.docx

docker exec -it office2 /bin/soffice –headless  –invisible –norestore –nodefault –nolockcheck  –nofirststartwizard –convert-to pdf:writer_pdf_Export –outdir /root/pdf2 /root/pdf2/liang2.docx

以上命令行放到java代码中,同时可以在多个线程中并发调用。

06
11月

IPv6的那些事

背景:

1、生产服务器采用了dnspod-sr作为内网DNS服务器,以方便应用的部署。

2、所有服务器都采用CentOS,IP采用IPv4,未禁用IPv6,但操作系统路由、路由器及防火墙皆未启用IPv6地址。

虽然以前也遇到过因为IPv6导致类似curl、wget之类的系统命令执行太慢的问题,但一直未对IPv6引起足够的重视。

最近接连出现了一堆与IPv6相关的问题。

1、先是APP上APP Store审核,被苹果因为IPv6的问题接连打回来几次。最终直接把服务器的IPv6禁用了事。

2、有一台Redis服务器作为缓存服务器,其他Spring Boot应用通过内部DNS连接Redia。比较诡异的是,每一次重启Spring Boot应用,初始化Redis连接池时候,至少需要15秒。

最终查明了是DNS解析惹的祸,直接采用IP连接就不存在问题。原因是应用通过内部域名连接Redis时,做域名解析时候,发送了两条AAAA(IPv6)、A(IPv4)域名解析请求,其中AAAA > A记录请求 。

而且CentOS下即便禁用了IPv6,采用TCPDUMP抓包发现似乎仍然也会发送AAAA域名解析请求,导致先AAAA请求超时后,再处理IPv4。

网上大部分资料都建议直接禁用掉IPv6,只不IPv6是趋势,IPv6 Ready还是有必要的,尤其是外部一些IPv6环境的访问服务器时候,服务器端获取IPv6相关信息还是挺有价值的。

有没有在不禁用IPv6的情况下,让操作系统优先使用IPv4而非IPv6呢?

按照RFC3484 的说法,在IPv6、IPv4共存的环境下,解析顺序为
1. IPv6
2. IPv4
3. 6to4-Traffic

如果能够采用 Prefer IPv4 over IPv6,则可以实现IPv4 和IPv6并存。

办法一:修改/etc/gai.conf

/etc/gai.conf是socket getaddrinfo(3)函数的配置文件,因此修改此配置文件,可以影响各种socket程序对IPv4、IPv6地址的处理顺序。

配置样例:

label       ::1/128        0
label       ::/0           1
label       2002::/16      2
label       ::/96          3
label       ::ffff:0:0/96  4
precedence  ::1/128        50
precedence  ::/0           40
precedence  2002::/16      30
precedence  ::/96          20
precedence  ::ffff:0:0/96  100

配置文件说明可以参考Linux MAN

http://man7.org/linux/man-pages/man5/gai.conf.5.html

http://man7.org/linux/man-pages/man3/getaddrinfo.3.html

方案2、在Java应用中,指定-Djava.net.preferIPv4Stack=true

可以参考官方文档 Networking Properties

24
2月

服务器部署Session Draining支持方案

 

    RSS订阅

    近期文章

    近期评论

    文章归档

    分类目录